全部產品
Search
文件中心

:使用樣本

更新時間:Jun 30, 2024

本文以C Link SDK中的Demo檔案./demos/data_model_basic_demo.c為例,介紹如何調用Link SDK的API,使裝置可使用物模型的功能。

背景資訊

  • 物模型功能的更多資訊,請參見使用說明

  • 物模型功能基於MQTT接入,開發過程中涉及MQTT接入的代碼說明,請參見MQTT接入

步驟一:初始化

  1. 添加標頭檔。

    ……
    ……
    
    #include "aiot_dm_api.h"
  2. 配置底層依賴和日誌輸出。

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. 調用aiot_dm_init,建立data-model用戶端執行個體,並初始化預設參數。

        dm_handle = aiot_dm_init();
        if (dm_handle == NULL) {
            printf("aiot_dm_init failed");
            return -1;}

步驟二:配置功能

調用aiot_dm_setopt,配置以下功能。

  1. 關聯MQTT串連的控制代碼。

    重要

    配置物模型功能參數前,請確保已配置裝置認證資訊等相關參數。具體操作,請參見MQTT配置串連參數

    • 範例程式碼:

          aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
    • 相關參數:

      配置項

      樣本值

      說明

      AIOT_DMOPT_MQTT_HANDLE

      mqtt_handle

      物模型功能的請求基於MQTT串連,通過該配置項,關聯MQTT串連控制代碼。

  2. 編寫物模型訊息的回呼函數。

    1. 定義物模型訊息的回呼函數。

      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;
          }
      }
    2. 配置物模型訊息的回呼函數。

      • 範例程式碼:

            aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);                   
      • 相關參數:

        配置項

        樣本值

        說明

        AIOT_DMOPT_RECV_HANDLER

        demo_dm_recv_handler

        接收到物模型訊息時,調用該函數。

  3. 配置物聯網平台是否應答報文。

    您可以通過以下設定,要求物聯網平台在接收到裝置訊息後,是否應答。

    • 範例程式碼:

          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)ICA標準資料格式

        應答報文的訊息在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)

  1. 添加物模型。

    • 上報屬性前,您需登入物聯網平台控制台,在產品詳情頁面的功能定義頁簽,為預設模組或建立的自訂模組(例如:demo_extra_block)的物模型,添加屬性。具體操作,請參見單個添加物模型

      本文範例程式碼對應添加的物模型屬性如下。

      物模型模組名稱

      功能名稱

      標識符

      資料類型

      資料定義

      讀寫類型

      預設模組

      夜燈開關1

      LightSwitch

      bool(布爾型)

      • 0:開

      • 1:關

      讀寫

      電源

      Power

      text(字串)

      資料長度:10240

      讀寫

      工作電流

      WF

      int32(整數型)

      • 取值範圍:0~10

      • 步長:1

      • 單位:安/A

      讀寫

      demo_extra_block

      夜燈開關2

      NightLightSwitch

      bool(布爾型)

      • 0:開

      • 1:關

      讀寫

    • 您也可以直接匯入本樣本的物模型TSL檔案,具體操作,請參見大量新增物模型

  2. 設定屬性訊息的內容。

    • 範例程式碼:

      • 預設模組:

        /* 單個上報屬性的內容 */
                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。

  3. 定義上報屬性訊息的函數。

    /* 單個屬性上報 */
    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);
    }

透傳/自訂格式

  1. 在物聯網平台上傳物模型資料解析指令碼。

    需更多資訊,請參見透傳/自訂格式的訊息

  2. 編寫裝置上報二進位訊息的代碼。

    範例程式碼:

        {
            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 SetDevicePropertySetDevicesProperty,向裝置發送屬性設定指令。裝置端調用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

      裝置端需解析資料,更多資訊,請參見透傳/自訂格式的訊息

  • 建議的處理邏輯:

    1. 更新裝置屬性。

    2. 上報更新後的屬性。

  • 範例程式碼的處理邏輯:

    範例程式碼僅列印接收的訊息,如需示範上報設定的屬性,請取消範例程式碼中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),裝置端代碼編寫的流程,請參見下文。

  1. 添加物模型。

    • 上報事件前,您需登入物聯網平台控制台,在產品詳情頁面的功能定義頁簽,為裝置的產品添加事件。具體操作,請參見單個添加物模型

      本文範例程式碼對應添加的物模型事件如下。

      功能名稱

      標識符

      事件類型

      輸出參數

      報錯

      Error

      故障

      • 參數名稱:錯誤碼

      • 參數標識符:ErrorCode

      • 資料類型:enum(枚舉型)

      • 枚舉專案:

        • 0:裝置硬體故障

        • 1:裝置軟體故障

        • 2:未知錯誤

    • 您也可以直接匯入本樣本的物模型TSL檔案,具體操作,請參見大量新增物模型

  2. 設定事件訊息內容。

    • 範例程式碼:

              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"
      }
  3. 定義上報事件訊息的函數。

    
    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 InvokeThingServiceInvokeThingsService,向裝置發送調用服務的指令。裝置端調用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_idmsg_id

      • 非同步:msg_id

    • 向物聯網平台返回服務的應答報文時,需注意逾時時間:

      • 同步:接收同步服務訊息後,請在8秒內做出應答。否則,即使裝置端已收到訊息,也視其失敗。

      • 非同步:您可根據業務需要,自訂非同步呼叫逾時的代碼邏輯。物聯網平台未對此做限制。

  • 建議的處理邏輯:

    1. 執行服務指令。

    2. 向物聯網平台返回服務的應答報文。

  • 範例程式碼的處理邏輯:

    範例程式碼僅列印接收的訊息,如需示範上報設定的屬性,請取消範例程式碼中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;
    }

後續步驟

  • 需進行編譯,產生可執行檔./output/data-model-basic-demo

    更多資訊,請參見編譯與運行

  • 關於運行結果的詳細說明,請參見作業記錄