物模型是阿里云物聯網平臺為產品定義的數據模型,通過屬性、事件、服務的方式對產品支持的能力進行描述,在設備開發時也需要以物模型的方式進行編程。
獲取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進行各種參數運行時設置和獲取,以及運行狀態的信息獲取等,實參可以是任何數據類型。 |