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

物模型通信

最近更新时间:2026-07-30 12:00:37

我的收藏
本文将介绍如何基于腾讯云物联网设备端 SDK,接入物模型(Data Model)能力,演示属性的上报与控制、事件的上报、行为的下发与回复等核心流程。

关键概念

物模型(Data Model)是物理世界中的设备在云端的数字化抽象。它将设备能力拆解为三个维度:属性(Property)、事件(Event)、行为(Action),让云端、应用端可以用统一的语言理解设备。

属性(Property)

设备运行时的状态或配置参数,既可被云端读取,也可被云端设置,是双向的。例如灯的开关、当前温度、亮度档位。

事件(Event)

设备主动上报到云端的信息,常用于告警、故障、运行状态变化等不可预期的场景,由设备单向推送。

行为(Action)

由云端下发给设备执行的命令,设备接收后需执行并回复结果。行为通常带有输入参数(云端下传)和输出参数(设备回传)。

data_id

属性、事件参数、行为参数的标识符,在同一物模型下唯一,调用 SDK 时必须与云端定义保持一致。

前提条件

在接入物模型通信功能前,请您先完成 快速接入 文档中跑通 tc_iot_login 登录功能。如果您已经完成,可以跳过该操作。

接入步骤

本节介绍如何调用 SDK 完成物模型的核心调用:上报属性、上报事件、接收云端对属性的设置、接收云端下发的行为并回复结果。

步骤 1:控制台定义物模型

控制台必须定义物模型,设备才可以上报物模型数据到服务器,上报未定义的物模型数据,服务器会拒绝。
1. 登录 物联网开发平台控制台,在产品列表中单击创建的产品名称进入详情。此处以“智能灯”产品为例。

2. 选择功能定义,单击新增功能导入物模型,成功后会自动生成对应功能类型。
新增功能:在当前物模型基础上新增一条物模型属性,事件,或行为。
导入物模型:将同类产品的物模型配置文件导入当前产品。

新增功能可以选择新增属性、事件或行为。
新增属性
新增事件
新增行为
界面如下图:

界面如下图:

界面如下图:


步骤 2:设备初始化物模型模块

登录成功后,调用 tc_iot_data_model_init 注册 tc_iot_data_model_callback_t 回调函数,物模型的回调如下:
回调
说明
on_report_property_result_cb
属性上报后,收到服务器响应或设备发送超时后触发,属性上报结果通过 data_model_report_result_t 告知用户。
on_receive_property_changed_cb
云端修改属性值时,触发设备回调,云端修改后的属性值通过回调参数 data_model_property 告知用户。
on_report_event_result_cb
事件上报后,收到服务器响应或网络超时后触发,事件上报结果通过 data_model_report_result_t 告知用户。
on_receive_new_action_cb
云端调用行为时,触发设备回调,云端下发的行为输入参数通过回调参数 data_model_action 告知用户。
调用 tc_iot_data_model_init 设置回调,初始化物模型模块。
static void on_report_property_result_cb(char *property_id, void *user_data,
data_model_report_result_t result_code);
static void on_receive_property_changed_cb(char *property_id,
tc_iot_data_model_property_t *data_model_property);
static void on_report_event_result_cb(char *event_id, void *user_data,
data_model_report_result_t result_code);
static int on_receive_new_action_cb(char *action_id,
tc_iot_data_model_action_t *data_model_action);


tc_iot_data_model_callback_t callback;
memset(&callback, 0, sizeof(callback));
callback.on_report_property_result_cb = on_report_property_result_cb;
callback.on_receive_property_changed_cb = on_receive_property_changed_cb;
callback.on_report_event_result_cb = on_report_event_result_cb;
callback.on_receive_new_action_cb = on_receive_new_action_cb;


tc_iot_error_e dm_rc = tc_iot_data_model_init(callback);
if (dm_rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_init failed: %d\\n", dm_rc);
}
属性和事件上报结果 data_model_report_result_t 取值和说明:
取值
说明
DATA_MODEL_REPORT_SUCCESS
上报成功,云端已确认。
DATA_MODEL_REPORT_REJECTED
上报被云端拒绝。
DATA_MODEL_REPORT_NO_RESPONSE
上报无响应(通常为超时)。
DATA_MODEL_REPORT_LOCAL_TIMEOUT
本地超时。

步骤 3:设备上报属性

调用 tc_iot_data_model_report_property 上报一条或多条属性到云端。属性支持以下数据类型(data_model_data_type_t):
取值
说明
DATA_MODEL_DATA_TYPE_BOOL
布尔值。
DATA_MODEL_DATA_TYPE_INT
32 位整数。
DATA_MODEL_DATA_TYPE_FLOAT
双精度浮点。
DATA_MODEL_DATA_TYPE_STRING
字符串。
DATA_MODEL_DATA_TYPE_TIME
32 位无符号时间戳(秒)。
DATA_MODEL_DATA_TYPE_ENUM
32 位无符号枚举。
上报一个布尔属性(灯的开关):
#include "tc_iot_data_model.h"

tc_iot_data_model_property_t property[1];
memset(property, 0, sizeof(property));

char power_switch_id[] = "power_switch";
property[0].property.data_id = power_switch_id;
property[0].property.data_type = DATA_MODEL_DATA_TYPE_BOOL;
property[0].property.data_value.value_bool = true;

rc = tc_iot_data_model_report_property(property, 1, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_property failed: %d\\n", rc);
}
上报多种不同类型的属性:
tc_iot_data_model_property_t property[4];
memset(property, 0, sizeof(property));

char id_power[] = "power_switch";
char id_bright[] = "brightness";
char id_color[] = "color";
char id_name[] = "device_name";

property[0].property.data_id = id_power;
property[0].property.data_type = DATA_MODEL_DATA_TYPE_BOOL;
property[0].property.data_value.value_bool = true;

property[1].property.data_id = id_bright;
property[1].property.data_type = DATA_MODEL_DATA_TYPE_INT;
property[1].property.data_value.value_int = 80;

property[2].property.data_id = id_color;
property[2].property.data_type = DATA_MODEL_DATA_TYPE_ENUM;
property[2].property.data_value.value_enum = 2;

property[3].property.data_id = id_name;
property[3].property.data_type = DATA_MODEL_DATA_TYPE_STRING;
property[3].property.data_value.value_string = "test_device_001";

rc = tc_iot_data_model_report_property(property, 4, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_property failed: %d\\n", rc);
}
说明:
tc_iot_data_model_report_property 是异步接口,返回值仅表示参数是否合法以及消息是否成功入队;真正的上报结果通过 on_report_property_result_cb 异步回调返回。
data_id 必须与控制台物模型中定义的属性标识符完全一致,否则云端会返回错误码。
data_type 与 data_value 的 union 字段必须一一对应(例如 data_type=INT 时只能写 value_int)。

步骤 4:设备上报事件

调用 tc_iot_data_model_report_event 上报一个或多个事件到云端。事件类型(data_model_event_type_t):
取值
说明
DATA_MODEL_EVENT_TYPE_INFO
普通信息事件,例如设备上线、心跳等。
DATA_MODEL_EVENT_TYPE_ALERT
告警事件,例如温度过高、门被打开。
DATA_MODEL_EVENT_TYPE_FAULT
故障事件,例如传感器异常。
上报一个告警事件(带参数):
#include "tc_iot_data_model.h"

tc_iot_data_model_event_t event[1];
memset(event, 0, sizeof(event));

char event_id[] = "temperature_alert";
event[0].event_id = event_id;
event[0].event_type = DATA_MODEL_EVENT_TYPE_ALERT;

data_model_data_item_t event_data[1];
memset(event_data, 0, sizeof(event_data));

char temp_id[] = "temperature";
event_data[0].data_id = temp_id;
event_data[0].data_type = DATA_MODEL_DATA_TYPE_INT;
event_data[0].data_value.value_int = 85;

event[0].event_data_list = event_data;
event[0].event_data_num = 1;

rc = tc_iot_data_model_report_event(event, 1, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_event failed: %d\\n", rc);
}
上报一个故障事件(不带参数):
tc_iot_data_model_event_t fault[1];
memset(fault, 0, sizeof(fault));

fault[0].event_id = "device_error";
fault[0].event_type = DATA_MODEL_EVENT_TYPE_FAULT;
fault[0].event_data_num = 0;
fault[0].event_data_list = NULL;

rc = tc_iot_data_model_report_event(fault, 1, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_event failed: %d\\n", rc);
}
说明:
事件的 event_data_list 中的 data_id 同样必须与云端物模型中该事件定义的参数标识符一致。
事件如果没有参数,可以将 event_data_num 置为 0、event_data_list 置为 NULL。

步骤 5:控制台查看设备上报的属性和事件

1. 登录 物联网开发平台控制台,在设备列表单击设备名称进入详情页面,选择物模型数据即可查看设备上报的属性和事件,

2. 选择需要查看的属性,单击查看

可以查看属性的变化曲线,以及每次上报的属性值。

3. 在设备详情页选择物模型数据后,单击事件,即可查看设备上报的所有事件数据。


步骤 6:控制台修改属性和调用行为

1. 登录 物联网开发平台控制台,在设备列表单击设备名称进入详情页面。
2. 选择在线调试 > 属性调试,勾选要修改的属性,填写修改后的数据,单击发送即可

3. 选择在线调试 > 行为调用,在左侧设置行为参数并发送,右侧会显示行为调用时间,以及设备对行为调用的响应报文和响应时间。


步骤 7:设备端接收属性变更

当云端修改了设备的某个属性时,SDK 会通过 on_receive_property_changed_cb 回调通知上层。
根据回调参数 tc_iot_data_model_property_t 确定属性名称,属性数据类型,属性数据值。
static void on_receive_property_changed_cb(char *property_id,
tc_iot_data_model_property_t *data_model_property) {
if (property_id == NULL || data_model_property == NULL) {
return;
}

data_model_data_item_t *item = &data_model_property->property;
printf("[demo] property changed: id=%s, type=%d\\n", item->data_id, (int)item->data_type);

switch (item->data_type) {
case DATA_MODEL_DATA_TYPE_BOOL:
// 根据 item->data_value.value_bool 调整设备状态
break;
case DATA_MODEL_DATA_TYPE_INT:
// 根据 item->data_value.value_int 调整设备状态
break;
case DATA_MODEL_DATA_TYPE_FLOAT:
// 根据 item->data_value.value_float 调整设备状态
break;
case DATA_MODEL_DATA_TYPE_STRING:
// 根据 item->data_value.value_string 调整设备状态
break;
case DATA_MODEL_DATA_TYPE_ENUM:
// 根据 item->data_value.value_enum 调整设备状态
break;
case DATA_MODEL_DATA_TYPE_TIME:
// 根据 item->data_value.value_time 调整设备状态
break;
default:
break;
}
}

说明:
on_receive_property_changed_cb 触发后,设备如果修改了属性值,可以调用 tc_iot_data_model_report_property 将最新的属性值上报云端。

步骤 8:设备端接收行为调用

当云端调用设备的行为时,SDK 会通过 on_receive_new_action_cb 回调通知上层。
回调返回 int:0 表示执行成功,非0表示执行失败。
如果行为定义了输出参数,回调里需要自行给 action_output_data_list / action_output_data_num 赋值,SDK 会自动把结果回传给云端。
static int on_receive_new_action_cb(char *action_id, tc_iot_data_model_action_t *data_model_action) {
if (action_id == NULL || data_model_action == NULL) {
return -1;
}

printf("[demo] receive new action: id=%s, token=%s, timestamp=%u, input_num=%d\\n",
action_id,
data_model_action->token ? data_model_action->token : "",
(unsigned)data_model_action->timestamp,
data_model_action->action_input_data_num);

// 1. 解析输入参数
for (int i = 0; i < data_model_action->action_input_data_num; i++) {
data_model_data_item_t *input = &data_model_action->action_input_data_list[i];
printf(" input[%d]: id=%s, type=%d\\n", i, input->data_id, (int)input->data_type);
// 根据业务需要读取 input->data_value ...
}

// 2. 执行行为逻辑(同步执行,避免在回调里做长时间阻塞)

// 3. 填充输出参数(按云端定义的输出参数顺序和类型)
int output_num = data_model_action->action_input_data_num; // 示例:与输入参数个数保持一致
data_model_action->action_output_data_num = output_num;
data_model_action->action_output_data_list = NULL;
if (output_num > 0) {
data_model_action->action_output_data_list =
(data_model_data_item_t *)calloc(output_num, sizeof(data_model_data_item_t));
if (data_model_action->action_output_data_list == NULL) {
return -2; // 内存不足,回复云端失败
}
for (int i = 0; i < output_num; i++) {
data_model_data_item_t *input = &data_model_action->action_input_data_list[i];
data_model_data_item_t *output = &data_model_action->action_output_data_list[i];

output->data_id = input->data_id; // 与云端约定好的输出参数标识
output->data_type = input->data_type; // 与云端约定好的输出参数类型
if (input->data_type == DATA_MODEL_DATA_TYPE_INT) {
output->data_value.value_int = input->data_value.value_int + 1;
} else {
output->data_value = input->data_value;
}
}
}

// 4. 返回 0:执行成功;返回非 0:执行失败,云端会收到对应错误码
return 0;
}

说明:
如果行为没有输出参数,把 action_output_data_num 置为 0、action_output_data_list 置为 NULL 即可。
返回 0 表示执行成功,云端收到的 action_reply 消息中 code=0、status=success;返回非 0 时 code=返回值、status=action execution failed。

完整示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "tc_iot.h"
#include "tc_iot_data_model.h"

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_report_property_result_cb(char *property_id, void *user_data,
data_model_report_result_t result_code) {
printf("[demo] report property result: id=%s, code=%d\\n",
property_id ? property_id : "(null)", (int)result_code);
}

// 事件上报结果回调
static void on_report_event_result_cb(char *event_id, void *user_data,
data_model_report_result_t result_code) {
printf("[demo] report event result: id=%s, code=%d\\n",
event_id ? event_id : "(null)", (int)result_code);
}

// 云端修改属性
static void on_receive_property_changed_cb(char *property_id,
tc_iot_data_model_property_t *data_model_property) {
if (!property_id || !data_model_property) {
return;
}
data_model_data_item_t *item = &data_model_property->property;
printf("[demo] property changed: id=%s, type=%d\\n", item->data_id, (int)item->data_type);
// 按 item->data_type 解析 item->data_value 并更新本地状态 ...
}

// 云端下发行为
static int on_receive_new_action_cb(char *action_id, tc_iot_data_model_action_t *data_model_action) {
if (!action_id || !data_model_action) {
return -1;
}
printf("[demo] receive new action: id=%s, input_num=%d\\n",
action_id, data_model_action->action_input_data_num);

// 解析输入参数并执行业务逻辑(示例:直接回传)
int n = data_model_action->action_input_data_num;
data_model_action->action_output_data_num = n;
data_model_action->action_output_data_list = NULL;
if (n > 0) {
data_model_action->action_output_data_list =
(data_model_data_item_t *)calloc(n, sizeof(data_model_data_item_t));
if (!data_model_action->action_output_data_list) {
return -2;
}
for (int i = 0; i < n; i++) {
data_model_data_item_t *in = &data_model_action->action_input_data_list[i];
data_model_data_item_t *out = &data_model_action->action_output_data_list[i];
out->data_id = in->data_id;
out->data_type = in->data_type;
out->data_value = in->data_value; // 示例:原样回传
}
}
return 0;
}

int main(int argc, char **argv) {
if (argc < 7) {
printf("Usage: %s -p <product_id> -d <device_id> -s <device_secret>\\n", argv[0]);
return 0;
}

const char *product_id = NULL;
const char *device_id = NULL;
const char *device_secret = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
product_id = argv[++i];
} else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
device_id = argv[++i];
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
device_secret = argv[++i];
}
}
if (!product_id || !device_id || !device_secret) {
printf("device info error\\n");
return 1;
}

// 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_device_info_s device_info;
device_info.product_id = product_id;
device_info.device_id = device_id;
device_info.device_secret = device_secret;
device_info.region = "ap-guangzhou";

rc = tc_iot_login(&device_info, on_login);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_login call failed: %d\\n", rc);
tc_iot_deinit();
return 1;
}

int waited_ms = 0;
while (!s_login_done && waited_ms < 5000) {
usleep(100 * 1000);
waited_ms += 100;
}
if (!s_login_done || s_login_rc != TC_IOT_ERR_SUCCESS) {
printf("login timeout or failed\\n");
tc_iot_deinit();
return 1;
}

// 3. 初始化物模型模块
tc_iot_data_model_callback_t callback = {
.on_report_property_result_cb = on_report_property_result_cb,
.on_receive_property_changed_cb = on_receive_property_changed_cb,
.on_report_event_result_cb = on_report_event_result_cb,
.on_receive_new_action_cb = on_receive_new_action_cb,
};
rc = tc_iot_data_model_init(callback);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_init failed: %d\\n", rc);
tc_iot_logout();
tc_iot_deinit();
return 1;
}

// 4. 上报一个布尔属性:power_switch = true
tc_iot_data_model_property_t property[1];
memset(property, 0, sizeof(property));
char id_power[] = "power_switch";
property[0].property.data_id = id_power;
property[0].property.data_type = DATA_MODEL_DATA_TYPE_BOOL;
property[0].property.data_value.value_bool = true;

rc = tc_iot_data_model_report_property(property, 1, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_property failed: %d\\n", rc);
}

// 5. 上报一个告警事件:temperature_alert,参数 temperature = 85
tc_iot_data_model_event_t event[1];
memset(event, 0, sizeof(event));
char id_event[] = "temperature_alert";
event[0].event_id = id_event;
event[0].event_type = DATA_MODEL_EVENT_TYPE_ALERT;

data_model_data_item_t event_data[1];
memset(event_data, 0, sizeof(event_data));
char id_temp[] = "temperature";
event_data[0].data_id = id_temp;
event_data[0].data_type = DATA_MODEL_DATA_TYPE_INT;
event_data[0].data_value.value_int = 85;

event[0].event_data_list = event_data;
event[0].event_data_num = 1;

rc = tc_iot_data_model_report_event(event, 1, NULL);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_data_model_report_event failed: %d\\n", rc);
}

// 6. 等待云端交互(属性修改、行为调用等),观察回调打印
sleep(10);

// 7. 释放资源
tc_iot_data_model_deinit();
tc_iot_logout();
tc_iot_deinit();
printf("[demo] main exit\\n");
return 0;
}

编译并运行

1. 在项目根目录执行 CMake 编译。
cmake -B build
cmake --build build -j
2. 运行可执行文件,传入设备三元组。
./build/iot_quickstart_demo -p YOUR_PRODUCT_ID -d YOUR_DEVICE_NAME -s 'YOUR_DEVICE_SECRET'
3. 观察日志输出,出现以下内容代表登录与信令发送均成功。
[demo] login success
[demo] report property result: id=power_switch, code=0
[demo] report event result: id=temperature_alert, code=0
云端在控制台对设备发起一次行为调用(如 set_brightness),设备端会打印:
[demo] receive new action: id=set_brightness, input_num=1 input[0]: id=brightness, type=2
云端在控制台修改一次属性(如把 brightness 改为 90),设备端会打印:
[demo] property changed: id=brightness, type=2

常见问题

现象
排查建议
tc_iot_data_model_init 返回 TC_IOT_ERR_NOT_INITIALIZED

确认在调用前已成功调用 tc_iot_init 和 tc_iot_login,且 on_login 回调返回 TC_IOT_ERR_SUCCESS。


tc_iot_data_model_init 返回 TC_IOT_ERR_ALREADY_INITIALIZED

物模型模块只允许初始化一次,确认没有重复调用,或先调用 tc_iot_data_model_deinit 再重新 init。

tc_iot_data_model_report_property 返回 TC_IOT_ERR_INVALID_ARGUMENT
检查 property 指针是否为空、property_num 是否 ≤ 0。
云端收不到上报的属性 / 事件
检查 data_id、event_id 是否与控制台物模型定义完全一致;data_type 与 data_value 的 union 字段是否对应正确。
on_report_property_result_cb 回调里 result_code ≠ DATA_MODEL_REPORT_SUCCESS
查看 SDK 日志中的错误码;常见原因有网络抖动、MQTT 断连、设备未登录。
云端行为调用,设备执行后并响应后,云端没有收到结果 / 一直 pending
确认 on_receive_new_action_cb 在合理时间内返回(0 表示成功);回调中不要做阻塞或 sleep;输出参数按云端约定填写。
编译报错找不到头文件
确认 CMakeLists.txt 中 target_include_directories 正确指向 tc_iot_sdk/include。

联系我们

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