本文以C Link SDK中的Demo檔案./demos/data_model_basic_demo.c為例,介紹如何調用Link SDK的API,使裝置可使用物模型的功能。
背景資訊
步驟一:初始化
添加標頭檔。
…… …… #include "aiot_dm_api.h"
配置底層依賴和日誌輸出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
調用aiot_dm_init,建立
data-model
用戶端執行個體,並初始化預設參數。dm_handle = aiot_dm_init(); if (dm_handle == NULL) { printf("aiot_dm_init failed"); return -1;}
步驟二:配置功能
調用aiot_dm_setopt,配置以下功能。
關聯MQTT串連的控制代碼。
重要配置物模型功能參數前,請確保已配置裝置認證資訊等相關參數。具體操作,請參見MQTT配置串連參數。
範例程式碼:
aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
相關參數:
配置項
樣本值
說明
AIOT_DMOPT_MQTT_HANDLE
mqtt_handle
物模型功能的請求基於MQTT串連,通過該配置項,關聯MQTT串連控制代碼。
編寫物模型訊息的回呼函數。
定義物模型訊息的回呼函數。
static void demo_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_handler, type = %d\r\n", recv->type); switch (recv->type) { /* 對屬性上報、事件上報、擷取期望屬性值或者刪除期望屬性值指令的應答 */ case AIOT_DMRECV_GENERIC_REPLY: { demo_dm_recv_generic_reply(dm_handle, recv, userdata); } break; /* 屬性設定 */ case AIOT_DMRECV_PROPERTY_SET: { demo_dm_recv_property_set(dm_handle, recv, userdata); } break; /* 非同步呼叫服務 */ case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: { demo_dm_recv_async_service_invoke(dm_handle, recv, userdata); } break; /* 同步調用服務 */ case AIOT_DMRECV_SYNC_SERVICE_INVOKE: { demo_dm_recv_sync_service_invoke(dm_handle, recv, userdata); } break; /* 下行位元據 */ case AIOT_DMRECV_RAW_DATA: { demo_dm_recv_raw_data(dm_handle, recv, userdata); } break; /* 二進位格式的同步調用服務, 比單純的位元據訊息多了個rrpc_id */ case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: { demo_dm_recv_raw_sync_service_invoke(dm_handle, recv, userdata); } break; /* 上行位元據後, 物聯網平台的回複報文 */ case AIOT_DMRECV_RAW_DATA_REPLY: { demo_dm_recv_raw_data_reply(dm_handle, recv, userdata); } break; default: break; } }
配置物模型訊息的回呼函數。
範例程式碼:
aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);
相關參數:
配置項
樣本值
說明
AIOT_DMOPT_RECV_HANDLER
demo_dm_recv_handler
接收到物模型訊息時,調用該函數。
配置物聯網平台是否應答報文。
您可以通過以下設定,要求物聯網平台在接收到裝置訊息後,是否應答。
範例程式碼:
uint8_t post_reply = 1; …… …… aiot_dm_setopt(dm_handle, AIOT_DMOPT_POST_REPLY, (void *)&post_reply);
相關參數:
配置項/參數
樣本
說明
AIOT_DMOPT_POST_REPLY
1
取值:
1:應答報文。
0:不應答報文。
如果配置項AIOT_DMOPT_POST_REPLY的值為
1
,需為應答報文編寫處理邏輯。您可根據業務需要編寫處理邏輯,範例程式碼僅做列印處理。
ICA標準資料格式(Alink JSON)
應答報文的訊息在
recv->data.generic_reply
中,與Alink資料格式相同,更多資訊,請參見裝置屬性、事件、服務。static void demo_dm_recv_generic_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata){ printf("demo_dm_recv_generic_reply msg_id = %d, code = %d, data = %.*s, message = %.*s\r\n", recv->data.generic_reply.msg_id, recv->data.generic_reply.code, recv->data.generic_reply.data_len, recv->data.generic_reply.data, recv->data.generic_reply.message_len, recv->data.generic_reply.message); }
透傳/自訂格式
應答報文訊息在
recv->data.raw_data
中,訊息收發前,您需在物聯網平台上傳解析指令碼,更多資訊請參見物模型訊息解析。static void demo_dm_recv_raw_data_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_async_service_invoke receive reply for up_raw msg, data len = %d\r\n", recv->data.raw_data.data_len); }
步驟三:上報屬性
裝置建立MQTT串連後,調用aiot_dm_send,向物聯網平台發送屬性訊息。要發送的屬性訊息,通過aiot_dm_msg_t結構體,存放於指定位置,作為aiot_dm_send的入參。
根據以下不同的裝置資料格式類型,為上報屬性訊息,編寫代碼。
ICA標準資料格式(Alink JSON)
添加物模型。
設定屬性訊息的內容。
範例程式碼:
預設模組:
/* 單個上報屬性的內容 */ demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}"); /* 批量上報屬性的內容 */ demo_send_property_batch_post(dm_handle, "{\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518}],\"WF\": [{\"value\": 3,\"time\":1612684518}]}}");
自訂模組:
demo_send_property_post(dm_handle, "{\"demo_extra_block:NightLightSwitch\": 1}");
樣本訊息說明:
物模型上報訊息為JSON格式。以下為上報訊息的樣本及其對應的Alink資料格式,更多資訊,請參見裝置上報屬性。
說明為了方便使用,您只需關注裝置上報的資料(即Alink資料格式中params中的內容),Link SDK會對上報的訊息封裝處理。
訊息類型
上報訊息的樣本
對應的Alink格式
單個屬性
{\"LightSwitch\": 0}
{ "id": "123", "version": "1.0", "sys":{ "ack":0 }, "params": { "LightSwitch": 0 }, "method": "thing.event.property.post" }
批量屬性
{\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518000}],\"WF\": [{\"value\": 3,\"time\":1612684518000}]}}
{ "id": 123, "version": "1.0", "sys":{ "ack":0 }, "method": "thing.event.property.batch.post", "params": { "properties": { "Power": [{ "value": "on", "time": 1612684518000 }, ], "WF": [{ "value": 3, "time": 1612684518000 }, ] }, }
批量上報屬性訊息後,物聯網平台基於批量上報的訊息,發送應答報文。如果您需確認物聯網平台已接收到該上報的訊息,需訂閱Topic
/sys/a18wP******/LightSwitch/thing/event/property/batch/post_reply
。範例程式碼:
aiot_mqtt_sub(mqtt_handle, "/sys/a18wP******/LightSwitch/thing/event/property/batch/post_reply", NULL, 1, NULL);
其中:
a18wP******
為裝置的ProductKey。LightSwitch
為裝置的DeviceName。
定義上報屬性訊息的函數。
/* 單個屬性上報 */ int32_t demo_send_property_post(void *dm_handle, char *params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_POST; msg.data.property_post.params = params; return aiot_dm_send(dm_handle, &msg); } /* 批量屬性上報 */ int32_t demo_send_property_batch_post(void *dm_handle, char *params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_BATCH_POST; msg.data.property_post.params = params; return aiot_dm_send(dm_handle, &msg); }
透傳/自訂格式
在物聯網平台上傳物模型資料解析指令碼。
需更多資訊,請參見透傳/自訂格式的訊息。
編寫裝置上報二進位訊息的代碼。
範例程式碼:
{ aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_DATA; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle, &msg); }
步驟四:設定屬性
您可以調用物聯網平台的雲端API SetDeviceProperty或SetDevicesProperty,向裝置發送屬性設定指令。裝置端調用aiot_mqtt_recv接收該指令,根據回呼函數demo_dm_recv_handler
的設定,執行對應處理。
您可以參考以下內容,編寫回呼函數的處理邏輯:
注意事項:
接收的訊息作為類型aiot_dm_recv_handler_t的入參,通過aiot_dm_recv_t結構體存放於您指定的位置。
不同的裝置資料格式,其接收的設定屬性指令內容,所在的欄位如下表所示:
裝置資料格式
接收的訊息中的欄位
說明
ICA標準資料格式(Alink JSON)
recv->data.property_set.params
與Alink格式資料中params的值一致,更多資訊,請參見屬性訊息。
透傳/自訂格式
recv->data.raw_data
裝置端需解析資料,更多資訊,請參見透傳/自訂格式的訊息。
建議的處理邏輯:
更新裝置屬性。
上報更新後的屬性。
範例程式碼的處理邏輯:
範例程式碼僅列印接收的訊息,如需示範上報設定的屬性,請取消範例程式碼中
TODO
下代碼的注釋符號(/*
和*/
),查看效果。ICA標準資料格式(Alink JSON)
static void demo_dm_recv_property_set(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_property_set msg_id = %ld, params = %.*s\r\n", (unsigned long)recv->data.property_set.msg_id, recv->data.property_set.params_len, recv->data.property_set.params); /* TODO: 以下代碼示範如何對物聯網平台的屬性設定指令進行應答, 您可取消注釋查看示範效果 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY; msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id; msg.data.property_set_reply.code = 200; msg.data.property_set_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ }
透傳/自訂格式
static void demo_dm_recv_raw_data(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_raw_data raw data len = %d\r\n", recv->data.raw_data.data_len); /* TODO: 以下代碼示範如何發送二進位格式資料, 如需使用,需在物聯網平台部署對應的資料透傳指令碼。 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_DATA; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle, &msg); } */ }
步驟五:上報事件
裝置建立MQTT串連後,調用aiot_dm_send,向物聯網平台發送事件訊息。要發送的事件訊息,通過aiot_dm_msg_t結構體,存放於指定位置,作為aiot_dm_send的入參。
根據以下不同的裝置資料格式類型,為上報事件,編寫代碼。
如果您的裝置資料格式為透傳/自訂格式,裝置上報事件與上報屬性的介面一致,具體流程,請參見步驟三中的上報透傳/自訂格式的訊息。
如果您的裝置資料格式為ICA標準資料格式(Alink JSON),裝置端代碼編寫的流程,請參見下文。
添加物模型。
設定事件訊息內容。
範例程式碼:
demo_send_event_post(dm_handle, "Error", "{\"ErrorCode\": 0}");
樣本訊息說明:
物模型上報訊息為JSON格式。以下為上報訊息的樣本及其對應的Alink資料格式,更多資訊,請參見裝置上報事件。
說明為了方便使用,您只需關注裝置上報的資料(即Alink資料格式中params中的內容),Link SDK會對上報的訊息封裝處理。
上報事件的樣本
對應的Alink格式
{\"ErrorCode\": 0}
{ "id": "123", "version": "1.0", "params": { "ErrorCode": 0 }, "method": "thing.event.alarm.post" }
定義上報事件訊息的函數。
int32_t demo_send_event_post(void *dm_handle, char *event_id, char *params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_EVENT_POST; msg.data.event_post.event_id = event_id; msg.data.event_post.params = params; return aiot_dm_send(dm_handle, &msg); }
步驟六:調用服務
您可以調用物聯網平台的雲端API InvokeThingService或InvokeThingsService,向裝置發送調用服務的指令。裝置端調用aiot_mqtt_recv接收該指令,根據回呼函數demo_dm_recv_handler
的設定,執行對應處理。
您可以參考以下內容,編寫回呼函數的處理邏輯:
注意事項:
接收的訊息作為類型aiot_dm_recv_handler_t的入參,通過aiot_dm_recv_t結構體存放於您指定的位置。
不同的裝置資料格式,其接收的調用服務指令內容,所在的欄位如下表所示:
裝置資料格式
同步訊息的欄位
非同步訊息的欄位
說明
ICA標準資料格式(Alink JSON)
recv->data.sync_service_invoke
recv->data.async_service_invoke
欄位中資料的格式與Alink格式資料中params的值一致,更多資訊,請參見服務訊息。
透傳/自訂格式
recv->data.raw_data
recv->data.raw_service_invoke
需自行上傳解析指令碼,解析指令內容。更多資訊,請參見透傳/自訂格式的訊息。
如果您定義的回呼函數不對調用服務指令做應答,自行編寫應答處理邏輯函數時,請確保應答訊息中的以下資訊與物聯網平台請求中的該參數值一致。
同步:
rrpc_id
和msg_id
非同步:
msg_id
向物聯網平台返回服務的應答報文時,需注意逾時時間:
同步:接收同步服務訊息後,請在8秒內做出應答。否則,即使裝置端已收到訊息,也視其失敗。
非同步:您可根據業務需要,自訂非同步呼叫逾時的代碼邏輯。物聯網平台未對此做限制。
建議的處理邏輯:
執行服務指令。
向物聯網平台返回服務的應答報文。
範例程式碼的處理邏輯:
範例程式碼僅列印接收的訊息,如需示範上報設定的屬性,請取消範例程式碼中
TODO
下代碼的注釋符號(/*
和*/
),查看效果。ICA標準資料格式(Alink JSON)
同步:
static void demo_dm_recv_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_sync_service_invoke msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s\r\n", (unsigned long)recv->data.sync_service_invoke.msg_id, recv->data.sync_service_invoke.rrpc_id, recv->data.sync_service_invoke.service_id, recv->data.sync_service_invoke.params_len, recv->data.sync_service_invoke.params); /* TODO: 以下代碼示範如何對來自物聯網平台的同步調用服務進行應答。 */ { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY; msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id; msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id; msg.data.sync_service_reply.code = 200; msg.data.sync_service_reply.service_id = "SetLightSwitchTimer"; msg.data.sync_service_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } }
非同步:
static void demo_dm_recv_async_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_async_service_invoke msg_id = %ld, service_id = %s, params = %.*s\r\n", (unsigned long)recv->data.async_service_invoke.msg_id, recv->data.async_service_invoke.service_id, recv->data.async_service_invoke.params_len, recv->data.async_service_invoke.params); /* TODO: 以下代碼示範如何對來自物聯網平台的非同步呼叫服務進行應答, 您可取消注釋查看示範效果。 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY; msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id; msg.data.async_service_reply.code = 200; msg.data.async_service_reply.service_id = "ToggleLightSwitch"; msg.data.async_service_reply.data = "{\"dataA\": 20}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ }
透傳/自訂格式
二進位非同步服務調用的處理邏輯,與二進位設定屬性的介面一致。具體操作,請參見自訂Topic訊息解析。
二進位同步服務調用的處理邏輯,範例程式碼如下:
static void demo_dm_recv_raw_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata) { printf("demo_dm_recv_raw_sync_service_invoke raw sync service rrpc_id = %s, data_len = %d\r\n", recv->data.raw_service_invoke.rrpc_id, recv->data.raw_service_invoke.data_len); /* TODO: 以下代碼示範如何對來自物聯網平台的同步調用服務進行應答, 您可取消注釋查看示範效果 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_SERVICE_REPLY; msg.data.raw_service_reply.rrpc_id = recv->data.raw_service_invoke.rrpc_id; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle, &msg); } */ }
步驟七:中斷連線
MQTT接入常應用於長串連的裝置,程式通常不會運行至此。
常式的主線程任務為配置參數並成功建立串連。串連建立後,主線程可進入休眠。
調用aiot_mqtt_disconnect,向物聯網平台發送中斷連線的報文,然後斷開網路連接。
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
步驟八:退出程式
調用aiot_dm_deinit,銷毀data-model
用戶端執行個體,釋放資源。
res = aiot_dm_deinit(&dm_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dm_deinit failed: -0x%04X\n", -res);
return -1;
}