物模型是阿里云物聯(lián)網(wǎng)平臺(tái)為產(chǎn)品定義的數(shù)據(jù)模型,通過屬性、事件、服務(wù)的方式對(duì)產(chǎn)品支持的能力進(jìn)行描述,在設(shè)備開發(fā)時(shí)也需要以物模型的方式進(jìn)行編程。

獲取Link SDK

不同版本的Link SDK下載,請(qǐng)參見SDK獲取,本文以Link SDK v3.2.0版本為例,介紹設(shè)備屬性、服務(wù)、事件的開發(fā)。

注意
  • 在Linux環(huán)境下,可通過修改wrappers/os/ubuntu/HAL_OS_linux.c文件中的默認(rèn)設(shè)備認(rèn)證信息,從而使用您在物聯(lián)網(wǎng)控制臺(tái)創(chuàng)建設(shè)備的身份信息。
  • src/dev_model/examples目錄下提供了名為model_for_example.json的物模型描述文件,您可以用已創(chuàng)建產(chǎn)品的ProductKey替換掉該文件中的ProductKey值后,將該物模型文件導(dǎo)入到物聯(lián)網(wǎng)平臺(tái)上的產(chǎn)品定義中,這樣可以快速的參照示例代碼來體驗(yàn)基于物模型的編程方式。

設(shè)備屬性

  • 屬性上報(bào)說明
    您可以調(diào)用IOT_Linkkit_Report()函數(shù)來上報(bào)屬性,屬性上報(bào)時(shí)需要按照物聯(lián)網(wǎng)平臺(tái)定義的屬性格式,使用JSON編碼后進(jìn)行上報(bào)。示例中函數(shù)user_post_property展示了如何使用IOT_Linkkit_Report進(jìn)行屬性上報(bào)(對(duì)于異常情況的上報(bào),詳見./src/dev_model/examples/linkkit_example_solo.c)。
    說明
    • property_payload = “{\”Counter\”:1}” 即是將屬性編碼為JSON對(duì)象。
    • 當(dāng)上報(bào)屬性或者事件需要物理網(wǎng)平臺(tái)應(yīng)答時(shí),可以通過IOT_Ioctl對(duì)IOTX_IOCTL_RECV_EVENT_REPLY選項(xiàng)進(jìn)行設(shè)置。
    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);
    }
  • 屬性設(shè)置說明
    示例代碼在回調(diào)函數(shù)user_property_set_event_handler中獲取物聯(lián)網(wǎng)平臺(tái)設(shè)置的屬性值,并原樣將收到的數(shù)據(jù)發(fā)回給物聯(lián)網(wǎng)平臺(tái),這樣可以更新在物聯(lián)網(wǎng)平臺(tái)的設(shè)備屬性值,在此處對(duì)收到的屬性值進(jìn)行處理。
    說明 該回調(diào)函數(shù)是在example初始化時(shí)使用IOT_RegisterCallback注冊(cè)的ITE_PROPERTY_SET事件對(duì)應(yīng)的回調(diào)函數(shù)。
    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;
    }

設(shè)備服務(wù)

在示例程序中,當(dāng)收到服務(wù)調(diào)用請(qǐng)求時(shí)(包括同步服務(wù)和異步服務(wù)),會(huì)進(jìn)入下面的回調(diào)函數(shù):

說明
  • 您需要?jiǎng)討B(tài)分配內(nèi)存空間用于存放服務(wù)應(yīng)答數(shù)據(jù),并通過response參數(shù)返回給SDK,SDK在完成應(yīng)答數(shù)據(jù)發(fā)送后會(huì)負(fù)責(zé)response指向內(nèi)存的釋放。
  • 對(duì)服務(wù)輸出參數(shù)為空的情況,*response應(yīng)指向存放JSON對(duì)象{}的內(nèi)存,不能讓*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;

        /* 服務(wù)應(yīng)答數(shù)據(jù),數(shù)據(jù)長(zhǎng)度通過response,response_len參數(shù)傳給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;
}

設(shè)備事件

示例中使用IOT_Linkkit_TriggerEvent上報(bào)事件。其中展示了如何使用IOT_Linkkit_Report進(jìn)行事件上報(bào)(對(duì)于異常情況的上報(bào),詳細(xì)信息,請(qǐng)參見./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);
}

關(guān)于上報(bào)消息的格式說明及示例

上報(bào)屬性時(shí),屬性ID和值以JSON格式的形式放在IOT_Linkkit_Report()payload中,不同數(shù)據(jù)類型以及多個(gè)屬性的格式示例如下:

/* 整型數(shù)據(jù) */
char *payload = "{\"Brightness\":50}";

/* 浮點(diǎn)型數(shù)據(jù)上報(bào) */
char *payload = "{\"Temperature\":11.11}";

/* 枚舉型數(shù)據(jù)上報(bào) */
char *payload = "{\"WorkMode\":2}";

/* 布爾型數(shù)據(jù)上報(bào), 在物模型定義中, 布爾型為整型, 取值為0或1, 與JSON格式的整型不同 */
char  *payload = "{\"LightSwitch\":1}";

/* 字符串?dāng)?shù)據(jù)上報(bào) */
char *payload = "{\"Description\":\"Amazing Example\"}";

/* 時(shí)間型數(shù)據(jù)上報(bào), 在物模型定義中, 時(shí)間型為字符串 */
char *payload = "{\"Timestamp\":\"1252512000\"}";

/* 復(fù)合類型屬性上報(bào), 在物模型定義中, 符合類型屬性為JSON對(duì)象 */
char *payload = "{\"RGBColor\":{\"Red\":11,\"Green\":22,\"Blue\":33}}";

/* 多屬性上報(bào), 如果需要上報(bào)以上各種數(shù)據(jù)類型的所有屬性, 將它們放在一個(gè)JSON對(duì)象中即可 */
char *payload = "{\"Brightness\":50,\"Temperature\":11.11,\"WorkMode\":2,\"LightSwitch\":1,\"Description\":\"Amazing Example\",\"Timestamp\":\"1252512000\",\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}";

/* 屬性payload準(zhǔn)備好以后, 就可以使用如下接口進(jìn)行上報(bào)了 */
IOT_Linkkit_Report(devid, ITM_MSG_POST_PROPERTY, payload, strlen(payload));           

上報(bào)事件與上報(bào)屬性的區(qū)別是,事件ID需要單獨(dú)拿出來,放在IOT_Linkkit_TriggerEvent()eventid中,而事件的上報(bào)內(nèi)容,也就是物模型定義中事件的輸出參數(shù),則使用與上報(bào)屬性相同的格式進(jìn)行上報(bào),示例如下:

/* 事件ID為Error, 其輸出參數(shù)ID為ErrorCode, 數(shù)據(jù)類型為枚舉型 */
char *eventid = "Error";
char *payload = "{\"ErrorCode\":0}";

/* 事件ID為HeartbeatNotification, 其輸出參數(shù)有2個(gè). 第一個(gè)是布爾型參數(shù)ParkingState, 第二個(gè)是浮點(diǎn)型參數(shù)VoltageValue */
char *eventid = "HeartbeatNotification";
char *payload = "{\"ParkingState\":1,\"VoltageValue\":3.0}";

/* 事件payload準(zhǔn)備好以后, 就可以使用如下接口進(jìn)行上報(bào)了 */
IOT_Linkkit_TriggerEvent(devid, event_id, strlen(event_id), payload, strlen(payload));

/* 從上面的示例可以看出, 當(dāng)事件的輸出參數(shù)有多個(gè)時(shí), payload的格式與多屬性上報(bào)是相同的 */

基于MQTT Topic進(jìn)行數(shù)據(jù)收發(fā)

雖然物模型編程的API并未返回MQTT編程接口IOT_MQTT_XXX()所需要的pClient參數(shù),但基于MQTT Topic進(jìn)行數(shù)據(jù)收發(fā)仍可和物模型編程混用。下文介紹了MQTT數(shù)據(jù)收發(fā)的接口和部分示例。
  • MQTT數(shù)據(jù)收發(fā)接口:
    說明 以下接口的第1個(gè)參數(shù)都可使用參數(shù)0作為輸入,表示使用當(dāng)前唯一的MQTT通道進(jìn)行數(shù)據(jù)收發(fā)等操作。
    • 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的程序代碼段落中表示對(duì)某個(gè)Topic進(jìn)行訂閱,可以通過如下接口:

      IOT_MQTT_Subscribe(0, topic_request, IOTX_MQTT_QOS0, topic_callback, topic_context);
    • 數(shù)據(jù)上報(bào)

      如果要在使用物模型編程API的程序代碼段落中表示在某個(gè)Topic進(jìn)行發(fā)布(即數(shù)據(jù)上報(bào)),可以通過如下接口:

      IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, payload_len);

與物模型功能相關(guān)的API列表

函數(shù)名 說明
IOT_Linkkit_Open 創(chuàng)建本地資源,在進(jìn)行網(wǎng)絡(luò)報(bào)文交互之前,必須先調(diào)用此接口,得到一個(gè)會(huì)話的句柄。
IOT_Linkkit_Connect 對(duì)主設(shè)備或網(wǎng)關(guān)來說,將會(huì)建立設(shè)備與物聯(lián)網(wǎng)平臺(tái)的通信。對(duì)于子設(shè)備來說,將向物聯(lián)網(wǎng)平臺(tái)注冊(cè)該子設(shè)備,并添加主子設(shè)備拓?fù)潢P(guān)系。
說明 如果是已經(jīng)注冊(cè)的子設(shè)備,則直接添加主子設(shè)備拓?fù)潢P(guān)系。
IOT_Linkkit_Yield 若SDK占有獨(dú)立線程,該函數(shù)只將接收到的網(wǎng)絡(luò)報(bào)文分發(fā)到您的回調(diào)函數(shù)中,否則表示將CPU交給SDK讓其接收網(wǎng)絡(luò)報(bào)文并將消息分發(fā)到您的回調(diào)函數(shù)中。
IOT_Linkkit_Close 若入?yún)⒅械臅?huì)話句柄為主設(shè)備或者網(wǎng)關(guān),則關(guān)閉網(wǎng)絡(luò)連接并釋放SDK為該會(huì)話所占用的所有資源。
IOT_Linkkit_TriggerEvent 向物聯(lián)網(wǎng)平臺(tái)發(fā)送事件報(bào)文、錯(cuò)誤碼、異常告警等。
IOT_Linkkit_Report 向物聯(lián)網(wǎng)平臺(tái)發(fā)送沒有業(yè)務(wù)數(shù)據(jù)下發(fā)的上行報(bào)文,包括屬性值、設(shè)備標(biāo)簽、二進(jìn)制透?jìng)鲾?shù)據(jù)和子設(shè)備管理等各種報(bào)文。
IOT_Linkkit_Query 向物聯(lián)網(wǎng)平臺(tái)發(fā)送存在業(yè)務(wù)數(shù)據(jù)下發(fā)的查詢報(bào)文,包括OTA狀態(tài)查詢、OTA固件下載、子設(shè)備拓?fù)洳樵兒蚇TP時(shí)間查詢等各種報(bào)文。
IOT_RegisterCallback 對(duì)SDK注冊(cè)事件回調(diào)函數(shù),如物聯(lián)網(wǎng)平臺(tái)連接成功或失敗,有屬性設(shè)置或服務(wù)請(qǐng)求到達(dá),子設(shè)備管理報(bào)文答復(fù)等。
IOT_Ioctl 對(duì)SDK進(jìn)行各種參數(shù)運(yùn)行時(shí)設(shè)置和獲取,以及運(yùn)行狀態(tài)的信息獲取等,實(shí)參可以是任何數(shù)據(jù)類型。