物模型是阿里云物联网平台为产品定义的数据模型,通过属性、事件、服务的方式对产品支持的能力进行描述,在设备开发时也需要以物模型的方式进行编程。
获取Link SDK
不同版本的Link SDK下载,请参见SDK获取,本文以Link SDK v3.2.0版本为例,介绍设备属性、服务、事件的开发。
注意
- 在Linux环境下,可通过修改wrappers/os/ubuntu/HAL_OS_linux.c文件中的默认设备认证信息,从而使用您在物联网控制台创建设备的身份信息。
- 在src/dev_model/examples目录下提供了名为model_for_example.json的物模型描述文件,您可以用已创建产品的ProductKey替换掉该文件中的ProductKey值后,将该物模型文件导入到物联网平台上的产品定义中,这样可以快速的参照示例代码来体验基于物模型的编程方式。
设备属性
- 属性上报说明
您可以调用IOT_Linkkit_Report()函数来上报属性,属性上报时需要按照物联网平台定义的属性格式,使用JSON编码后进行上报。示例中函数user_post_property展示了如何使用IOT_Linkkit_Report进行属性上报(对于异常情况的上报,详见./src/dev_model/examples/linkkit_example_solo.c)。说明
property_payload = “{\”Counter\”:1}”
即是将属性编码为JSON对象。- 当上报属性或者事件需要物理网平台应答时,可以通过
IOT_Ioctl
对IOTX_IOCTL_RECV_EVENT_REPLY
选项进行设置。
void user_post_property(void) { static int cnt = 0; int res = 0; char property_payload[30] = {0}; HAL_Snprintf(property_payload, sizeof(property_payload), "{\"Counter\": %d}", cnt++); res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *)property_payload, strlen(property_payload)); EXAMPLE_TRACE("Post Property Message ID: %d", res); }
- 属性设置说明
示例代码在回调函数user_property_set_event_handler中获取物联网平台设置的属性值,并原样将收到的数据发回给物联网平台,这样可以更新在物联网平台的设备属性值,在此处对收到的属性值进行处理。说明 该回调函数是在example初始化时使用IOT_RegisterCallback注册的ITE_PROPERTY_SET事件对应的回调函数。
static int user_property_set_event_handler(const int devid, const char *request, const int request_len) { int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); EXAMPLE_TRACE("Property Set Received, Devid: %d, Request: %s", devid, request); res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY, (unsigned char *)request, request_len); EXAMPLE_TRACE("Post Property Message ID: %d", res); return 0; }
设备服务
在示例程序中,当收到服务调用请求时(包括同步服务和异步服务),会进入下面的回调函数:
说明
- 您需要动态分配内存空间用于存放服务应答数据,并通过response参数返回给SDK,SDK在完成应答数据发送后会负责response指向内存的释放。
- 对服务输出参数为空的情况,
*response
应指向存放JSON对象{}
的内存,不能让*response
指向空指针。
static int user_service_request_event_handler(const int devid, const char *serviceid, const int serviceid_len,
const char *request, const int request_len,
char **response, int *response_len){ int add_result = 0;
cJSON *root = NULL, *item_number_a = NULL, *item_number_b = NULL;
const char *response_fmt = "{\"Result\": %d}";
EXAMPLE_TRACE("Service Request Received, Service ID: %.*s, Payload: %s", serviceid_len, serviceid, request);
/* Parse Root */
root = cJSON_Parse(request);
if (root == NULL || !cJSON_IsObject(root)) {
EXAMPLE_TRACE("JSON Parse Error");
return -1;
}
if (strlen("Operation_Service") == serviceid_len && memcmp("Operation_Service", serviceid, serviceid_len) == 0) {
/* Parse NumberA */
item_number_a = cJSON_GetObjectItem(root, "NumberA");
if (item_number_a == NULL || !cJSON_IsNumber(item_number_a)) {
cJSON_Delete(root);
return -1;
}
EXAMPLE_TRACE("NumberA = %d", item_number_a->valueint);
/* Parse NumberB */
item_number_b = cJSON_GetObjectItem(root, "NumberB");
if (item_number_b == NULL || !cJSON_IsNumber(item_number_b)) {
cJSON_Delete(root);
return -1;
}
EXAMPLE_TRACE("NumberB = %d", item_number_b->valueint);
add_result = item_number_a->valueint + item_number_b->valueint;
/* 服务应答数据,数据长度通过response,response_len参数传给SDK */
*response_len = strlen(response_fmt) + 10 + 1;
*response = (char *)HAL_Malloc(*response_len);
if (*response == NULL) {
EXAMPLE_TRACE("Memory Not Enough");
return -1;
}
memset(*response, 0, *response_len);
HAL_Snprintf(*response, *response_len, response_fmt, add_result);
*response_len = strlen(*response);
}
cJSON_Delete(root);
return 0;
}
设备事件
示例中使用IOT_Linkkit_TriggerEvent上报事件。其中展示了如何使用IOT_Linkkit_Report进行事件上报(对于异常情况的上报,详细信息,请参见./src/dev_model/examples/linkkit_example_solo.c)。
void user_post_event(void){
int res = 0;
char *event_id = "HardwareError";
char *event_payload = "{\"ErrorCode\": 0}";
res = IOT_Linkkit_TriggerEvent(EXAMPLE_MASTER_DEVID, event_id, strlen(event_id),
event_payload, strlen(event_payload));
EXAMPLE_TRACE("Post Event Message ID: %d", res);
}
关于上报消息的格式说明及示例
上报属性时,属性ID和值以JSON格式的形式放在IOT_Linkkit_Report()的payload
中,不同数据类型以及多个属性的格式示例如下:
/* 整型数据 */
char *payload = "{\"Brightness\":50}";
/* 浮点型数据上报 */
char *payload = "{\"Temperature\":11.11}";
/* 枚举型数据上报 */
char *payload = "{\"WorkMode\":2}";
/* 布尔型数据上报, 在物模型定义中, 布尔型为整型, 取值为0或1, 与JSON格式的整型不同 */
char *payload = "{\"LightSwitch\":1}";
/* 字符串数据上报 */
char *payload = "{\"Description\":\"Amazing Example\"}";
/* 时间型数据上报, 在物模型定义中, 时间型为字符串 */
char *payload = "{\"Timestamp\":\"1252512000\"}";
/* 复合类型属性上报, 在物模型定义中, 符合类型属性为JSON对象 */
char *payload = "{\"RGBColor\":{\"Red\":11,\"Green\":22,\"Blue\":33}}";
/* 多属性上报, 如果需要上报以上各种数据类型的所有属性, 将它们放在一个JSON对象中即可 */
char *payload = "{\"Brightness\":50,\"Temperature\":11.11,\"WorkMode\":2,\"LightSwitch\":1,\"Description\":\"Amazing Example\",\"Timestamp\":\"1252512000\",\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}";
/* 属性payload准备好以后, 就可以使用如下接口进行上报了 */
IOT_Linkkit_Report(devid, ITM_MSG_POST_PROPERTY, payload, strlen(payload));
上报事件与上报属性的区别是,事件ID需要单独拿出来,放在IOT_Linkkit_TriggerEvent()的eventid
中,而事件的上报内容,也就是物模型定义中事件的输出参数,则使用与上报属性相同的格式进行上报,示例如下:
/* 事件ID为Error, 其输出参数ID为ErrorCode, 数据类型为枚举型 */
char *eventid = "Error";
char *payload = "{\"ErrorCode\":0}";
/* 事件ID为HeartbeatNotification, 其输出参数有2个. 第一个是布尔型参数ParkingState, 第二个是浮点型参数VoltageValue */
char *eventid = "HeartbeatNotification";
char *payload = "{\"ParkingState\":1,\"VoltageValue\":3.0}";
/* 事件payload准备好以后, 就可以使用如下接口进行上报了 */
IOT_Linkkit_TriggerEvent(devid, event_id, strlen(event_id), payload, strlen(payload));
/* 从上面的示例可以看出, 当事件的输出参数有多个时, payload的格式与多属性上报是相同的 */
基于MQTT Topic进行数据收发
虽然物模型编程的API并未返回MQTT编程接口IOT_MQTT_XXX()所需要的pClient参数,但基于MQTT Topic进行数据收发仍可和物模型编程混用。下文介绍了MQTT数据收发的接口和部分示例。- MQTT数据收发接口:
说明 以下接口的第1个参数都可使用参数
0
作为输入,表示使用当前唯一的MQTT通道进行数据收发等操作。- IOT_MQTT_Construct
- IOT_MQTT_Destroy
- IOT_MQTT_Yield
- IOT_MQTT_CheckStateNormal
- IOT_MQTT_Subscribe
- IOT_MQTT_Unsubscribe
- IOT_MQTT_Publish
- IOT_MQTT_Subscribe_Sync
- IOT_MQTT_Publish_Simple
- 使用示例:
- Topic订阅
如果要在使用物模型编程API的程序代码段落中表示对某个Topic进行订阅,可以通过如下接口:
IOT_MQTT_Subscribe(0, topic_request, IOTX_MQTT_QOS0, topic_callback, topic_context);
- 数据上报
如果要在使用物模型编程API的程序代码段落中表示在某个Topic进行发布(即数据上报),可以通过如下接口:
IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, payload_len);
- Topic订阅
与物模型功能相关的API列表
函数名 | 说明 |
---|---|
IOT_Linkkit_Open | 创建本地资源,在进行网络报文交互之前,必须先调用此接口,得到一个会话的句柄。 |
IOT_Linkkit_Connect | 对主设备或网关来说,将会建立设备与物联网平台的通信。对于子设备来说,将向物联网平台注册该子设备,并添加主子设备拓扑关系。
说明 如果是已经注册的子设备,则直接添加主子设备拓扑关系。
|
IOT_Linkkit_Yield | 若SDK占有独立线程,该函数只将接收到的网络报文分发到您的回调函数中,否则表示将CPU交给SDK让其接收网络报文并将消息分发到您的回调函数中。 |
IOT_Linkkit_Close | 若入参中的会话句柄为主设备或者网关,则关闭网络连接并释放SDK为该会话所占用的所有资源。 |
IOT_Linkkit_TriggerEvent | 向物联网平台发送事件报文、错误码、异常告警等。 |
IOT_Linkkit_Report | 向物联网平台发送没有业务数据下发的上行报文,包括属性值、设备标签、二进制透传数据和子设备管理等各种报文。 |
IOT_Linkkit_Query | 向物联网平台发送存在业务数据下发的查询报文,包括OTA状态查询、OTA固件下载、子设备拓扑查询和NTP时间查询等各种报文。 |
IOT_RegisterCallback | 对SDK注册事件回调函数,如物联网平台连接成功或失败,有属性设置或服务请求到达,子设备管理报文答复等。 |
IOT_Ioctl | 对SDK进行各种参数运行时设置和获取,以及运行状态的信息获取等,实参可以是任何数据类型。 |