本文以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
  • 关于运行结果的详细说明,请参见运行日志