一、关键概念
动态注册用于产品量产过程中,在控制台批量创建设备后,设备根据 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 密钥长度要求)。 |
回调 tc_iot_dynamic_register_cb 参数说明:
| 参数 | 说明 |
| error_code | TC_IOT_ERR_SUCCESS 表示成功;其余为错误码。 |
| error_message | 失败时的错误描述,成功时为 NULL。 |
| device_key | 注册成功时返回的设备密钥,失败时为 NULL。 |
注意:说明:
发起动态注册:
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",};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, }; 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, }; 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", }; 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 与控制台不一致(解密失败),或响应被中间代理改写。 |
十三、联系我们
如果你在接入或使用过程中有任何疑问或者建议,欢迎 联系我们 提交反馈。