本文将介绍如何基于腾讯云物联网设备端 SDK,使用动态注册(Dynamic Register)能力,在产品量产阶段由设备自动从云端获取设备密钥,减少客户手动逐个烧录设备密钥的工作量,提升量产效率。
关键概念
动态注册用于产品量产过程中,在控制台批量创建设备后,设备根据
productId、product_secret、device_id,从后台获取设备密钥(device_secret)。该机制减少客户手动逐个烧录设备密钥,提升量产效率。适用场景
量产阶段需要批量激活大量设备,逐一烧录设备密钥成本过高。
设备出厂时只烧录产品级别的
product_secret,由设备首次联网时自行换取设备密钥。设备端希望密钥不固化在产线,降低密钥泄漏风险。
名词解释
名词 | 说明 |
productId | 产品标识,在控制台创建产品时生成。 |
product_secret | 产品密钥,产品级别,用于动态注册时对请求进行 HMAC-SHA256 签名与对响应进行 AES-128-CBC 解密。 |
device_id | 设备名称,在控制台批量创建设备时由平台生成或自定义,需与控制台保持一致。 |
device_secret | 设备密钥,动态注册成功后由云端下发,后续登录时使用。 |
前提条件
在接入动态注册功能前,请您先完成以下准备工作:
1. 登录 物联网开发平台控制台,创建产品并记录
productId 与 product_secret。2. 在控制台批量创建设备,记录每台设备的
device_id。动态注册要求设备名必须先在控制台预创建,否则会返回 Pre create mode: device not exists。3. 在产品详情页开启动态注册功能。如果未开启,设备端会收到
Not support dynamic device register, please check setting first。4. 已完成 登录与注册 文档中
tc_iot_init 的初始化流程(动态注册仅需 SDK 初始化,不依赖 tc_iot_login 登录成功)。接入步骤
本节介绍如何调用 SDK 完成动态注册的核心调用:发起注册请求、在回调中获取设备密钥、使用密钥完成登录。
步骤 1:控制台预创建设备并开启动态注册
1. 登录 物联网开发平台控制台,在产品列表中单击创建的产品名称进入详情。
2. 在设备页面单击批量新建设备,生成若干
device_id。动态注册时传入的 device_id 必须在此列表中存在。
3. 在产品详情页的动态注册开关处,确认功能已开启。关闭状态下注册请求会被云端拒绝。

步骤 2:初始化 SDK
调用
tc_iot_init 完成 SDK 初始化,动态注册依赖 SDK 内部的 HTTP/TLS 能力。#include "tc_iot.h"tc_iot_config_s config;memset(&config, 0, sizeof(config));config.storage_path = "./";config.log_level = TC_IOT_LOG_LEVEL_INFO;tc_iot_error_e rc = tc_iot_init(&config);if (rc != TC_IOT_ERR_SUCCESS) {printf("tc_iot_init failed: %d\\n", rc);return -1;}
步骤 3:发起动态注册请求
调用
tc_iot_dynamic_register 发起注册,入参 tc_iot_dynamic_register_param_s:字段 | 说明 |
product_id | 产品标识,必填。 |
device_id | 设备名称,必填,需在控制台预创建。 |
product_secret | 产品密钥,必填,长度需 ≥ 16字节(AES-128 密钥长度要求)。 |
region | 保留字段,当前不使用,可置 NULL。 |
回调
tc_iot_dynamic_register_cb 参数说明:参数 | 说明 |
error_code | TC_IOT_ERR_SUCCESS 表示成功;其余为错误码。 |
error_message | 失败时的错误描述,成功时为 NULL。 |
device_key | 注册成功时返回的设备密钥,失败时为 NULL。 |
说明:
tc_iot_dynamic_register 是异步接口,返回值仅表示参数是否合法以及请求是否成功入队;真正的注册结果通过回调异步返回。同一时刻只允许有一个注册请求在途,重复调用会返回
TC_IOT_ERR_DYNAMIC_REGISTER_BUSY。发起动态注册:
static void on_dynamic_register(tc_iot_error_e error_code, const char *error_message,const char *device_key) {if (error_code != TC_IOT_ERR_SUCCESS || !device_key) {printf("dynamic register failed|error=%d|message=%s\\n", error_code,error_message ? error_message : "");return;}printf("dynamic register device key:%s, err_msg:%s\\n", device_key, error_message);}tc_iot_dynamic_register_param_s param = {.product_id = "YOUR_PRODUCT_ID",.device_id = "YOUR_DEVICE_ID",.product_secret = "YOUR_PRODUCT_SECRET",.region = "ap-guangzhou",};tc_iot_error_e rc = tc_iot_dynamic_register(¶m, on_dynamic_register);if (rc != TC_IOT_ERR_SUCCESS) {printf("tc_iot_dynamic_register failed: %d\\n", rc);}
步骤 4:使用获取的密钥登录
动态注册成功后,在回调中拿到
device_key,将其作为 device_secret 调用 tc_iot_login 完成登录。建议将密钥持久化到本地存储,避免每次开机都重新注册。static volatile int s_login_done = 0;static tc_iot_error_e s_login_rc = TC_IOT_ERR_SUCCESS;static void on_login(tc_iot_error_e error_code, const char *error_message) {s_login_rc = error_code;s_login_done = 1;if (error_code == TC_IOT_ERR_SUCCESS) {printf("[demo] login success\\n");} else {printf("[demo] login failed: %d, msg=%s\\n", error_code,error_message ? error_message : "");}}static void on_dynamic_register(tc_iot_error_e error_code, const char *error_message,const char *device_key) {if (error_code != TC_IOT_ERR_SUCCESS || !device_key) {printf("dynamic register failed|error=%d|message=%s\\n", error_code,error_message ? error_message : "");return;}printf("dynamic register success, device_key:%s\\n", device_key);// 建议持久化 device_key,下次开机直接复用,无需再次动态注册// save_device_secret_to_storage(device_key);tc_iot_device_info_s dev = {.product_id = "YOUR_PRODUCT_ID",.device_id = "YOUR_DEVICE_ID",.device_secret = device_key,.region = "ap-guangzhou",};tc_iot_error_e rc = tc_iot_login(&dev, on_login);if (rc != TC_IOT_ERR_SUCCESS) {printf("tc_iot_login failed: %d\\n", rc);}}
完整示例代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include "tc_iot.h"static volatile int s_reg_done = 0;static volatile int s_login_done = 0;static tc_iot_error_e s_login_rc = TC_IOT_ERR_SUCCESS;static void on_login(tc_iot_error_e error_code, const char *error_message) {s_login_rc = error_code;s_login_done = 1;if (error_code == TC_IOT_ERR_SUCCESS) {printf("[demo] login success\\n");} else {printf("[demo] login failed: %d, msg=%s\\n", error_code,error_message ? error_message : "");}}static void on_dynamic_register(tc_iot_error_e error_code, const char *error_message,const char *device_key) {s_reg_done = 1;if (error_code != TC_IOT_ERR_SUCCESS || !device_key) {printf("[demo] dynamic register failed|error=%d|message=%s\\n", error_code,error_message ? error_message : "");return;}printf("[demo] dynamic register success, device_key:%s\\n", device_key);// 用动态注册拿到的密钥登录tc_iot_device_info_s dev = {.product_id = "YOUR_PRODUCT_ID",.device_id = "YOUR_DEVICE_ID",.device_secret = device_key,.region = "ap-guangzhou",};tc_iot_error_e rc = tc_iot_login(&dev, on_login);if (rc != TC_IOT_ERR_SUCCESS) {printf("[demo] tc_iot_login failed: %d\\n", rc);}}int main(int argc, char **argv) {(void)argc;(void)argv;// 1. 初始化 SDKtc_iot_config_s config;memset(&config, 0, sizeof(config));config.storage_path = "./";config.log_level = TC_IOT_LOG_LEVEL_INFO;tc_iot_error_e rc = tc_iot_init(&config);if (rc != TC_IOT_ERR_SUCCESS) {printf("tc_iot_init failed: %d\\n", rc);return 1;}// 2. 发起动态注册tc_iot_dynamic_register_param_s param = {.product_id = "YOUR_PRODUCT_ID",.device_id = "YOUR_DEVICE_ID",.product_secret = "YOUR_PRODUCT_SECRET",.region = "ap-guangzhou",};rc = tc_iot_dynamic_register(¶m, on_dynamic_register);if (rc != TC_IOT_ERR_SUCCESS) {printf("tc_iot_dynamic_register failed: %d\\n", rc);tc_iot_deinit();return 1;}// 3. 等待动态注册 + 登录完成int waited_ms = 0;while (!s_reg_done && waited_ms < 15000) {usleep(100 * 1000);waited_ms += 100;}if (!s_reg_done) {printf("[demo] dynamic register timeout\\n");tc_iot_deinit();return 1;}waited_ms = 0;while (!s_login_done && waited_ms < 10000) {usleep(100 * 1000);waited_ms += 100;}// 4. 业务完成后释放资源if (s_login_done && s_login_rc == TC_IOT_ERR_SUCCESS) {tc_iot_logout();}tc_iot_deinit();printf("[demo] main exit\\n");return 0;}
编译并运行
1. 在项目根目录执行 CMake 编译。编译前请将示例程序中
"YOUR_PRODUCT_ID", "YOUR_DEVICE_ID", "YOUR_PRODUCT_SECRET" 替换为控制台获取的有效数据。cmake -B buildcmake --build build -j
2. 运行可执行文件。
./build/iot_video_demo
3. 观察日志输出,出现以下内容代表动态注册与登录均成功。
[demo] dynamic register success, device_key:xxxxxxxxxxxxx[demo] login success
常见问题
现象 | 排查建议 |
动态注册回调提示错误 Not support dynamic device register, please check setting first。 | 动态注册功能未在控制台开启。请在产品详情页打开动态注册开关后再试。 |
动态注册回调提示错误 "Pre create mode: device not exists"。 | 传入的 device_id 在控制台未预创建。请先在控制台批量创建对应设备,确保 device_id 与控制台一致。 |
动态注册回调提示错误 "Device has been activated"。 | 该设备已动态注册过且密钥已激活。已注册的设备名再次动态注册会被拒绝,请使用上次获取的密钥登录,或参考下方说明。 |
动态注册获取的密钥未登录,再次注册拿到新密钥。 | 如果动态注册获取的密钥未用于登录,下次仍可执行动态注册,但会获取到新的密钥,之前的密钥同时失效,只能使用最新获取的密钥。 |
tc_iot_dynamic_register 返回 TC_IOT_ERR_DYNAMIC_REGISTER_BUSY。 | 上一次注册请求仍在途。SDK 同一时刻只允许一个注册请求,请等待回调返回后再发起新请求。 |
tc_iot_dynamic_register 返回 TC_IOT_ERR_INVALID_ARGUMENT。 | 检查 product_id、device_id 是否为空或过长(需 < 64字节);product_secret 长度是否 ≥ 16字节(AES-128 密钥要求)。 |
tc_iot_dynamic_register 返回 TC_IOT_ERR_NOT_INITIALIZED。 | 确认在调用前已成功调用 tc_iot_init。 |
TC_IOT_ERR_DYNAMIC_REGISTER_REQUEST_FAILED | HTTP 请求失败或云端返回错误。请检查网络、 product_secret 是否正确、签名是否匹配;查看 SDK 日志中的 error_message 获取具体原因。 |
TC_IOT_ERR_DYNAMIC_REGISTER_RESPONSE_INVALID | 云端响应格式异常或解密失败。常见原因为 product_secret 与控制台不一致(解密失败),或响应被中间代理改写。 |