本文以C Link SDK中的Demo文件./demos/shadow_basic_demo.c為例,介紹如何調用Link SDK的API,展示設備影子功能。

背景信息

  • 設備影子功能的更多信息,請參見概述

  • 設備影子功能基于MQTT接入,開發過程中涉及MQTT接入的代碼說明,請參見MQTT接入

步驟一:初始化

  1. 添加頭文件。
    ……
    ……
    
    #include "aiot_shadow_api.h"
  2. 配置底層依賴和日志輸出。
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. 調用aiot_shadow_init,創建Shadow客戶端實例,并初始化默認參數。
        shadow_handle = aiot_shadow_init();
        if (shadow_handle == NULL) {
            printf("aiot_shadow_init failed\n");
            return -1;
        }

步驟二:配置功能

調用aiot_shadow_setopt,配置以下功能。

  1. 關聯MQTT連接的句柄。
    注意 配置設備影子功能參數前,請確保已配置設備認證信息等相關參數。具體操作,請參見MQTT配置連接參數
    • 示例代碼:
          aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_MQTT_HANDLE, mqtt_handle);
    • 相關參數:
      配置項 示例值 說明
      AIOT_SHADOWOPT_MQTT_HANDLE mqtt_handle 設備影子功能的請求基于MQTT連接,通過該配置項,關聯MQTT連接句柄。
  2. 配置消息回調
    • 示例代碼:
          aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);
    • 相關參數:
      配置項 示例值 說明
      AIOT_SHADOWOPT_RECV_HANDLER demo_shadow_recv_handler 接收設備影子相關消息時,調用該函數。

步驟三:設備主動上報狀態

設備在線時,主動上報設備狀態到影子,應用程序主動獲取設備影子狀態。

  1. 設備調用aiot_shadow_send,向物聯網平臺上報最新狀態到影子。
    上報狀態時,需注意:
    • 上報狀態消息的數據結構類型為aiot_shadow_msg_t,是aiot_shadow_send()的入參。
    • 上報狀態的消息類型為AIOT_SHADOWMSG_UPDATE
    int32_t demo_update_shadow(void *shadow_handle, char *reported_data, int64_t version)
    {
        aiot_shadow_msg_t message;
    
        memset(&message, 0, sizeof(aiot_shadow_msg_t));
        message.type = AIOT_SHADOWMSG_UPDATE;
        message.data.update.reported = reported_data;
        message.data.update.version = version;
    
        return aiot_shadow_send(shadow_handle, &message);
    }
  2. 設置上報狀態消息的內容。
    • 示例代碼:
              res = demo_update_shadow(shadow_handle, "{\"LightSwitch\":1}", 0);
              if (res < 0) {
                  printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
              }
    • 示例消息內容說明:
      示例 Alink格式 說明
      {
          "LightSwitch": 1
      }
      {
        "method": "update", 
        "state": {
          "reported": {
            "LightSwitch": 1
          }
        }, 
        "version": 0
      }
      上報的消息的內容為JSON格式,是Alink格式數據中state的值。詳細說明,請參見設備主動上報狀態

      示例代碼的消息內容為:

      • 將屬性LightSwitch設置為1
      • 將版本號設置為0
        說明
        • 后續操作的版本號遞增, 否則物聯網平臺將返回錯誤。
        • 如果將版本號設置為-1, 物聯網平臺則清空設備影子數據,并將版本號更新為0
  3. 物聯網平臺接收到設備影子的消息后,更新影子文件。然后,向設備返回應答報文。
  4. 設備接收應答報文后,觸發回調函數demo_shadow_recv_handler

    您可以參考以下內容,編寫回調函數的處理邏輯:

    • 應答報文的數據結構類型為aiot_shadow_recv_t,是回調函數的入參。
    • 應答報文消息的類型為AIOT_SHADOWRECV_GENERIC_REPLY
    • 以下是應答報文示例及其Alink數據格式:
      示例 Alink格式 說明
      {
          "status":"success",
          "version":0
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success", 
          "version": 0
        }, 
        "timestamp": 1626317187
      }
      應答報文的內容是Alink數據中payload的值。

      示例消息表示上報狀態成功。

    • 示例代碼僅做打印處理。
    void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
    {
        printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
                recv->type, recv->product_key, recv->device_name);
    
        switch (recv->type) {
    
            case AIOT_SHADOWRECV_GENERIC_REPLY: {
                const aiot_shadow_recv_generic_reply_t *generic_reply = &recv->data.generic_reply;
    
                printf("payload = \"%.*s\", status = %s, timestamp = %ld\r\n",
                       generic_reply->payload_len,
                       generic_reply->payload,
                       generic_reply->status,
                       (unsigned long)generic_reply->timestamp);
            }
    ……
    ……
            default:
                break;
        }
    }

步驟四:應用程序改變設備狀態

您可以通過應用程序,或登錄物聯網平臺,向設備影子發送期望屬性,最終實現改變設備狀態。

  1. 下發期望狀態給設備影子。
    • 您可以開發物聯網平臺云端應用程序,調用API下發設備影子期望屬性,詳細說明,請參見UpdateDeviceShadow
    • 您可以在物聯網平臺的設備詳情頁,直接下發設備影子期望屬性,具體操作,請參見查看與更新設備影子
  2. 物聯網平臺根據下發的期望狀態消息,更新影子內容。然后,下發該影子內容至設備。
  3. 設備接收影子文件后,觸發回調函數demo_shadow_recv_handler
    注意 如果設備不在線,具體操作,請參見步驟五:設備主動獲取設備影子內容
    您可以參考以下內容,編寫回調函數的處理邏輯:
    • 下發的期望狀態消息的數據結構類型為aiot_shadow_recv_t,是回調函數的入參。
    • 期望狀態的消息類型為AIOT_SHADOWRECV_CONTROL
    • 以下是期望狀態消息示例及其Alink數據格式:
      示例 Alink格式 說明
      {
          "state": {
              "desired": {
                  "LightSwitch": 0
              }
          },
          "metadata": {
              "desired": {
                  "LightSwitch": {
                      "timestamp": 1626319658
                  }
              }
          }
      }
      {
        "method": "control", 
        "payload": { 
         "state": {
              "desired": {
                  "LightSwitch": 0
              }
          },
          "metadata": {
              "desired": {
                  "LightSwitch": {
                      "timestamp": 1626319658 
             }
            }
          }
        }, 
        "version": 2, 
        "timestamp": 1469564576
      }
      期望狀態消息的的內容是Alink數據中payload的值。

      示例消息為設置LightSwitch的期望屬性值為0

    • 示例代碼僅做打印處理。
    void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
    {
        printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
                recv->type, recv->product_key, recv->device_name);
    
        switch (recv->type) {
    ……
    ……
            case AIOT_SHADOWRECV_CONTROL: {
                const aiot_shadow_recv_control_t *control = &recv->data.control;
    
                printf("payload = \"%.*s\", version = %ld\r\n",
                       control->payload_len,
                       control->payload,
                       (unsigned long)control->version);
            }
    ……
    ……
            default:
                break;
        }
    }
  4. 設備更新狀態后,上報最新的狀態至設備影子,并處理應答報文。
    具體操作,請參見步驟三:設備主動上報狀態
  5. 上報最新的狀態后,調用aiot_shadow_send,刪除期望屬性。
    請求刪除期望屬性時,需注意:
    • 刪除期望屬性消息的數據結構類型為aiot_shadow_msg_t,是aiot_shadow_send()的入參。
    • 消息的類型為AIOT_SHADOWMSG_CLEAN_DESIRED
    • 示例代碼刪除了期望屬性的全部內容,并設置版本號為1
    int32_t demo_clean_shadow_desired(void *shadow_handle, int64_t version)
    {
        aiot_shadow_msg_t message;
    
        memset(&message, 0, sizeof(aiot_shadow_msg_t));
        message.type = AIOT_SHADOWMSG_CLEAN_DESIRED;
        message.data.clean_desired.version = version;
    
        return aiot_shadow_send(shadow_handle, &message);
    }
    ……
    ……
            res = demo_clean_shadow_desired(shadow_handle, 1);
            if (res < 0) {
                printf("demo_clean_shadow_desired failed, res = -0x%04x\r\n", -res);
            }
  6. 刪除屬性的請求發送后,物聯網平臺返回應答報文,觸發回調函數demo_shadow_recv_handler
    具體操作,請參見配置應答報文的回調函數

步驟五:設備主動獲取設備影子內容

若應用程序發送指令時,設備離線。設備再次上線后,將主動獲取設備影子內容。

  1. 設備調用aiot_shadow_send,向物聯網平臺發送查詢指令,獲取設備影子內容。
    查詢指令的消息類型為AIOT_SHADOWMSG_GET
    int32_t demo_get_shadow(void *shadow_handle)
    {
        aiot_shadow_msg_t message;
    
        memset(&message, 0, sizeof(aiot_shadow_msg_t));
        message.type = AIOT_SHADOWMSG_GET;
    
        return aiot_shadow_send(shadow_handle, &message);
    }
    ……
    ……
            res = demo_get_shadow(shadow_handle);
            if (res < 0) {
                printf("demo_get_shadow failed, res = -0x%04x\r\n", -res);
            }
  2. 物聯網平臺收到查詢指令后,返回查詢結果。設備接收返回結果后,觸發回調函數demo_shadow_recv_handler
    您可以參考以下內容,編寫回調函數的處理邏輯:
    • 回調函數處理消息的數據結構類型為aiot_shadow_recv_t,是回調函數的入參。
    • 消息的類型為AIOT_SHADOWRECV_GET_REPLY
    • 以下是查詢后返回的消息示例及其Alink數據格式:
      示例 Alink格式 說明
      {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }, 
        "version": 5, 
        "timestamp": 1626320690
      }
      獲取屬性消息的內容是Alink數據中payload的值。

      示例消息表示請求發送成功,獲取的消息值未空,未上報過消息。

    • 示例代碼僅做打印處理。
    void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
    {
        printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
                recv->type, recv->product_key, recv->device_name);
    
        switch (recv->type) {
    ……
    ……
            case AIOT_SHADOWRECV_GET_REPLY: {
                const aiot_shadow_recv_get_reply_t *get_reply = &recv->data.get_reply;
    
                printf("payload = \"%.*s\", version = %ld\r\n",
                       get_reply->payload_len,
                       get_reply->payload,
                       (unsigned long)get_reply->version);
            }
            default:
                break;
        }
    }

步驟六:設備主動刪除影子屬性

若設備端已經是最新狀態,設備端可以主動發送指令,刪除設備影子中保存的某條屬性狀態。

  1. 設備調用aiot_shadow_send,向物聯網平臺發送刪除指令,刪除設備影子中的指定屬性。
    發送刪除指令時,需注意:
    • 刪除指令的數據結構類型為aiot_shadow_msg_t,是aiot_shadow_send()的入參。
    • 刪除指令的消息類型為AIOT_SHADOWMSG_DELETE_REPORTED
    int32_t demo_delete_shadow_report(void *shadow_handle, char *reported, int64_t version)
    {
        aiot_shadow_msg_t message;
    
        memset(&message, 0, sizeof(aiot_shadow_msg_t));
        message.type = AIOT_SHADOWMSG_DELETE_REPORTED;
        message.data.delete_reporte.reported = reported;
        message.data.delete_reporte.version = version;
    
        return aiot_shadow_send(shadow_handle, &message);
    }
  2. 設置刪除指令的內容。
    • 示例代碼:
              res = demo_delete_shadow_report(shadow_handle, "{\"LightSwitch\":\"null\"}", 2);
              if (res < 0) {
                  printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
              }
    • 示例消息內容說明:
      示例 Alink格式 說明
      "{\"LightSwitch\":\"null\"}", 2
      {
        "method": "delete", 
        "state": {
          "reported": {
            "LightSwitch": "null", 
          }
        }, 
        "version": 2
      }
      刪除指令消息的內容為JSON格式,是Alink格式數據中state的值。詳細說明,請參見設備主動刪除影子屬性

      示例代碼的消息內容為:

      • 將屬性LightSwitch設置為null,表示清除設備影子所有數據。
      • 將版本號設置為2
  3. 物聯網平臺收到刪除指令后,返回應答報文。設備接收報文后,觸發回調函數demo_shadow_recv_handler
    具體操作,請參見配置應答報文的回調函數

步驟七:退出程序

調用aiot_shadow_deinit,銷毀Shadow客戶端實例,釋放資源。

    res = aiot_shadow_deinit(&shadow_handle);
    if (res < STATE_SUCCESS) {
        printf("aiot_shadow_deinit failed: -0x%04X\n", -res);
        return -1;
    }

后續步驟

  • 例程文件配置完成后,需進行編譯,生成可執行文件./output/shadow-basic-demo

    更多信息,請參見編譯與運行

    注意
    • 配置Demo文件時,請根據測試場景,取消上報狀態、獲取屬性、刪除期望或上報屬性的相關代碼兩邊的注釋符號(/**/),并根據場景更換版本號。
    • 更新版本號時,需注意:
      • 后續操作的版本號遞增, 否則物聯網平臺將返回錯誤。
      • 如果將版本號設置為-1, 物聯網平臺則清空設備影子數據,并將版本號更新為0
  • 關于運行結果的詳細說明,請參見運行日志