帮你快速理解、总结文档立即下载

Linux & RTOS 设备

最近更新时间:2026-09-15 15:42:16
我的收藏
本文将介绍如何基于腾讯云物联网设备端 SDK,使用动态注册(Dynamic Register)能力,在产品量产阶段由设备自动从云端获取设备密钥,减少客户手动逐个烧录设备密钥的工作量,提升量产效率。

关键概念

动态注册用于产品量产过程中,在控制台批量创建设备后,设备根据 productIdproduct_secretdevice_id,从后台获取设备密钥(device_secret)。该机制减少客户手动逐个烧录设备密钥,提升量产效率。

适用场景

量产阶段需要批量激活大量设备,逐一烧录设备密钥成本过高。
设备出厂时只烧录产品级别的 product_secret,由设备首次联网时自行换取设备密钥。
设备端希望密钥不固化在产线,降低密钥泄漏风险。

名词解释

名词
说明
productId
产品标识,在控制台创建产品时生成。
product_secret
产品密钥,产品级别,用于动态注册时对请求进行 HMAC-SHA256 签名与对响应进行 AES-128-CBC 解密。
device_id
设备名称,在控制台批量创建设备时由平台生成或自定义,需与控制台保持一致。
device_secret
设备密钥,动态注册成功后由云端下发,后续登录时使用。

前提条件

在接入动态注册功能前,请您先完成以下准备工作:
1. 登录 物联网开发平台控制台,创建产品并记录 productIdproduct_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(&param, 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. 初始化 SDK
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;
}

// 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(&param, 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 build
cmake --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_iddevice_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 与控制台不一致(解密失败),或响应被中间代理改写。

联系我们

如果您在接入或使用过程中有任何疑问或者建议,欢迎 联系我们 提交反馈。