Development Guide to Low-Power Keepalive and Wake-Up 2.0

Last Updated on : 2026-06-29 08:39:15Copy for LLMView as MarkdownDownload PDF

This topic describes how to implement low-power keepalive and wake-up 2.0 for Internet Protocol cameras (IPCs). It is based on the sample code in tuya_ipc_low_power_api_v2.c and ty_sdk_lowpower.c and also covers data point (DP) reporting.

Overview

Low-power 2.0 uses a persistent Transmission Control Protocol (TCP) connection to communicate with the Tuya low-power keepalive server. While sleeping, the device keeps the connection alive and starts the full IPC SDK after it receives a wake-up command.

Item 1.0 2.0
Encryption None or simple AES-GCM (128/256)
Heartbeat Fixed heartbeat packet PINGREQ/PINGRESP
Data Raw bytes DATA frame and optional DATAACK
Client ID Device ID Encrypted Client ID (issued by the server)

Related files:

  • Protocol implementation: app_lowpower_sample_v2/src/tuya_ipc_low_power_api_v2.c
  • Header file: app_lowpower_sample_v2/include/tuya_ipc_low_power_api_v2.h
  • Integration example: app_main/src/ty_sdk_lowpower.c

Prerequisites

  • Configure the device type as LOW_POWER_DEV.
  • Initialize the main SDK and connect the device to the network to retrieve the keepalive server information.
  • Obtain a LocalKey (16-byte AES-128 or 32-byte AES-256).
  • Obtain an encrypted Client ID through the SDK instead of using the standard device ID.
// Get the keepalive server (domain name, IP address, and port).
tuya_ipc_get_low_power_server_v2(domain, domain_len, &ip, &port);

// Get the encrypted Client ID (2.0 only, used in the CONNECT frame).
tuya_ipc_get_lowpower_v2_encrypt_devid(encrypt_devid, buf_len);

// Get the LocalKey.
tuya_ipc_get_local_key(local_key, &key_len);

Protocol frame types

Type Value Direction Description
CONNECT 0x01 Device to server Connection and authentication
CONNACK 0x02 Server to device Connection acknowledgment
PINGREQ 0x03 Device to server Heartbeat request
PINGRESP 0x04 Server to device Heartbeat response
DATA 0x05 Bidirectional Business data (encrypted)
DATAACK 0x06 Bidirectional Data acknowledgment
DISCONNECT 0x07 Bidirectional Disconnect

Common frame header (4 bytes, big-endian):

| Type (2B) | Remaining Length (2B) | Payload ... |

Connection flow

Device                         Keepalive server
 |----------- TCP connect ----------->|
 |------- CONNECT (encrypted) ------->|
 |<------- CONNACK (encrypted) -------|
 |      Save Keepalive + Random       |
 |------- PINGREQ (periodic) -------->|
 |<------------ PINGRESP -------------|
 |<--------- DATA (wake-up) ----------|
 |------ DATAACK (if required) ------>|

CONNECT notes

  • Version = 0x02
  • Encryption type:
    • 0x00: AES-128-GCM
    • 0x01: AES-256-GCM
  • The payload contains the encrypted Client ID, a 12-byte nonce, and the encrypted keepalive interval.
  • Encrypt the keepalive interval with the LocalKey. Do not use additional authenticated data (AAD).

CONNACK notes

  • code == 0 indicates success.
  • After decryption, you receive negotiated keepalive (2 bytes) and a 10-byte random string.
  • Use this random string as the AAD for DATA and DATAACK encryption.

API

INT_T tuya_ipc_low_power_v2_server_connect(
    TUYA_IP_ADDR_T serverIp, INT_T port,
    CHAR_T *devId, INT_T devIdLen,      // Encrypted Client ID
    CHAR_T *pkey, INT_T keyLen,         // LocalKey, 16 or 32 bytes
    UINT16_T keepaliveSec               // Recommended: 60s
);

If tuya_ipc_low_power_v2_server_connect fails, do not reconnect immediately. Retry after a backoff interval. The demo uses an interval of 10s × fail_cnt.

Keepalive and data transmission

Heartbeat

  • Set the select timeout to the keepalive interval in seconds to trigger PINGREQ.
  • PINGREQ contains only a 4-byte frame header with Remaining Length = 0.
  • Ignore the received PINGRESP (00 04 00 00).
tuya_ipc_low_power_v2_pingreq_get(ping_buf, &ping_len);
send(socket, ping_buf, ping_len, 0);

Receive DATA

TUYA_LP_V2_RECV_DATA_T recv_data;
UINT32_T payload_len = sizeof(buf);

tuya_ipc_low_power_v2_recv_data(&recv_data, buf, &payload_len);
// recv_data.type: 0x00 = wake-up, 0x01 = device data
// recv_data.ack_required: Whether DATAACK is required
// recv_data.packet_id / data / data_len

Send DATA (such as reporting DPs)

tuya_ipc_low_power_v2_create_data_packet(pkt, &pkt_len, payload, payload_len, FALSE);
send(socket, pkt, pkt_len, 0);

DATA plaintext structure (6-byte header and payload):

| AckFlag (1B) | PacketId (4B) | Type (1B) | Payload ... |
  • Type = 0x01: Device data (such as DP JSON)
  • AckFlag & 0x01: Requests a DATAACK from the peer

DATAACK

tuya_ipc_low_power_v2_dataack_get(ack_buf, &ack_len, packet_id);

Thread safety

Lock the socket for send and receive operations. Do not call select while holding the lock.

// Call select outside the lock.
tuya_ipc_low_power_v2_socket_lock();
send(...) / recv(...) / tuya_ipc_low_power_v2_recv_data(...);
tuya_ipc_low_power_v2_socket_unlock();

Recommended integration steps

Start the keepalive thread

Run the keepalive logic in a dedicated thread as shown in ty_sdk_lowpower_start_demo() to avoid blocking the main flow.

Connect to the keepalive server

// Resolve the domain name to an IP first. If it fails, use the IP returned by the SDK. Retry up to three times with backoff.
tal_net_gethostbyname(domain, TY_AF_INET, &tmp_ip);
tuya_ipc_low_power_v2_server_connect(tmp_ip, port, encrypt_devid, len, local_key, key_len, 60);

Main loop

  • Use select to wait until the socket is readable or the timeout expires.
  • On timeout, send a PINGREQ.
  • On readable: peek to detect PINGRESP. Otherwise, call recv_data to parse DATA.
  • If type == TUYA_LP_V2_DATA_TYPE_WAKEUP, start the full IPC SDK to resume normal application.
  • If ack_required is true, send a DATAACK.

Report DPs in sleep mode (optional)

// JSON format: {"protocol":4,"data":{"dps":{"1":true}}}
ty_sdk_lowpower_v2_send_dps(dp_array, dp_cnt);

Exit and clean up

tuya_ipc_low_power_v2_disconnect_get(buf, &len, TUYA_LP_V2_DISCONNECT_NORMAL); // Optional
tuya_ipc_low_power_v2_server_close();

Key constants

Constant Value Description
TUYA_LP_V2_NONCE_LEN 12 GCM nonce
TUYA_LP_V2_GCM_TAG_LEN 16 GCM tag
TUYA_LP_V2_RANDOM_STRING_LEN 10 AAD from CONNACK
Maximum frame length 2,048 Implementation limit
Socket timeout 8s Send and receive timeout

Differences from 1.0 (migration notes)

1.0 API 2.0 API
tuya_ipc_low_power_server_connect tuya_ipc_low_power_v2_server_connect (adds the keepalive parameter)
tuya_ipc_low_power_heart_beat_get tuya_ipc_low_power_v2_pingreq_get
tuya_ipc_low_power_wakeup_data_get and byte comparison recv_data.type == TUYA_LP_V2_DATA_TYPE_WAKEUP
Standard device ID tuya_ipc_get_lowpower_v2_encrypt_devid
tuya_ipc_get_low_power_server tuya_ipc_get_low_power_server_v2 (adds domain name)

FAQ

Why does CONNECT fail?

Check the following:

  • The encrypted Client ID is correct (you must obtain it through the v2 API).
  • The LocalKey length is 16 or 32 bytes.
  • The network is reachable.

Why am I not receiving wake-up messages?

Verify that:

  • The keepalive thread is running.
  • PINGREQ packets are sent on schedule.
  • The random string in CONNACK has been stored successfully (handled automatically by the library).

Why does DATA decryption fail?

Confirm that CONNACK succeeded. The AAD for DATA frames is the 10-byte random string in the CONNACK payload.

Does the SDK support IPv4 and IPv6?

TUYA_IP_ADDR_T supports IPv4 and IPv6. The demo defaults to IPv4. Adapt the address family of tal_net_gethostbyname according to the platform.

API quick reference

Function Purpose
tuya_ipc_low_power_v2_server_connect Connect and authenticate
tuya_ipc_low_power_v2_server_close Close the connection
tuya_ipc_low_power_v2_socket_fd_get Get the socket file descriptor
tuya_ipc_low_power_v2_socket_lock/unlock Use the thread lock
tuya_ipc_low_power_v2_pingreq_get Build a heartbeat packet
tuya_ipc_low_power_v2_create_data_packet Build an uplink DATA packet
tuya_ipc_low_power_v2_recv_data Receive and decrypt DATA
tuya_ipc_low_power_v2_dataack_get Build a DATAACK
tuya_ipc_low_power_v2_disconnect_get Build a DISCONNECT