This article describes how to call the API operations of Link SDK for C to configure device shadows. In this example, the ./demos/shadow_basic_demo.c sample code file is used.

Background information

  • For more information about device shadows, see Overview.

  • You must establish an MQTT connection. For more information, see Overview.

Step 1: Initialize a client

  1. Add header files.
    ...
    ...
    
    #include "aiot_shadow_api.h"
  2. Add the underlying dependency and configure the log output feature.
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call the aiot_shadow_init operation to create a shadow client instance and initialize the default parameters.
        shadow_handle = aiot_shadow_init();
        if (shadow_handle == NULL) {
            printf("aiot_shadow_init failed\n");
            return -1;
        }

Step 2: Configure required features

Call the aiot_shadow_setopt operation to configure the following items.

  1. Associate with an MQTT connection handle.
    Notice Before you set device shadow-specific parameters,
    • Sample code
          aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_MQTT_HANDLE, mqtt_handle);
    • Parameters:
      Parameter Example Description
      AIOT_SHADOWOPT_MQTT_HANDLE mqtt_handle You must establish an MQTT connection. This parameter is used to associate with the MQTT connection handle.
  2. Configure a message callback
    • Sample code
          aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);
    • Parameters:
      Parameter Example Description
      AIOT_SHADOWOPT_RECV_HANDLER demo_shadow_recv_handler This function is called when the device shadow message is received.

Step 3: Submit the device status

If the device is online, the device can submit the status to its shadow. Applications obtain the status from the shadow.

  1. The device calls the aiot_shadow_send operation to submit the latest status to the device shadow in IoT Platform.
    When you submit the status, take note of the following items:
    • aiot_shadow_msg_t indicates the data format. This parameter is an input parameter of the aiot_shadow_send() callback.
    • AIOT_SHADOWMSG_UPDATE indicates the message type.
    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. You specify the message content.
    • Sample code
              res = demo_update_shadow(shadow_handle, "{\"LightSwitch\":1}", 0);
              if (res < 0) {
                  printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
              }
    • Sample message:
      Sample message Alink format Description
      {
          "LightSwitch": 1
      }
      {
        "method": "update", 
        "state": {
          "reported": {
            "LightSwitch": 1
          }
        }, 
        "version": 0
      }
      The message content is in the JSON format. The content is indicated by the state parameter in the Alink data. For more information, see The device submits the status.

      The following items describe the message content in this example:

      • Set the LightSwitch property to 1.
      • Set the version number to 0.
        Note
        • The version numbers of subsequent operations must be incremented. Otherwise, IoT Platform returns an error.
        • If you set the version number to -1, IoT Platform clears the device shadow data and updates the version number to 0.
  3. After IoT Platform receives the message, IoT Platform updates the shadow file. Then, IoT Platform returns a response to the device.
  4. After the device receives the response, the demo_shadow_recv_handler callback is called.

    When you specify the processing logic of the callback, take note of the following items:

    • aiot_shadow_recv_t indicates the data format. This parameter is an input parameter of the callback.
    • AIOT_SHADOWRECV_GENERIC_REPLY indicates the message type.
    • The following table describes a sample response and the Alink format of the response.
      Example Alink format Description
      {
          "status":"success",
          "version":0
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success", 
          "version": 0
        }, 
        "timestamp": 1626317187
      }
      The response is indicated by the payload parameter in the Alink data.

      This sample response indicates that the status is submitted.

    • In this example, the message is printed.
    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;
        }
    }

Step 4: Change the device status by using desired properties

To change the device status, you can use an application to send desired properties to the device shadow. You can also log on to the IoT Platform console and modify desired properties in the device shadow.

  1. You update desired properties in the device shadow. The following methods are available:
    • Develop a cloud application and call the API operation to send desired properties to the device shadow. For more information, see UpdateDeviceShadow.
    • On the Device Details page of IoT Platform, modify desired properties in the device shadow. For more information, see View and update a device shadow.
  2. IoT Platform updates the shadow file and then sends the file to the device.
  3. After the device receives the shadow file, the demo_shadow_recv_handler callback is called.
    Notice If the device is offline, you can request the device shadow after the device goes online. For more information, see Step 5: Request the device shadow.
    When you specify the processing logic of the callback, take note of the following items:
    • aiot_shadow_recv_t indicates the data format. This parameter is an input parameter of the callback.
    • AIOT_SHADOWRECV_CONTROL indicates the message type.
    • The following table describes a sample message and the Alink format of the message.
      Example Alink format Description
      {
          "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
      }
      The response is indicated by the payload parameter in the Alink data.

      In this example, the LightSwitch desired property is set to 0.

    • In this example, the message is printed.
    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. After the device updates the local status, the device submits the latest status to the device shadow and processes the response.
    For more information, see Step 3: Submit the device status.
  5. The device calls the aiot_shadow_send operation to delete the desired properties.
    When you delete desired properties, take note of the following items:
    • aiot_shadow_msg_t indicates the data format. This parameter is an input parameter of the aiot_shadow_send() callback.
    • AIOT_SHADOWMSG_CLEAN_DESIRED indicates the message type.
    • In this example, all desired properties are deleted and the version number is set to 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. After IoT platform receives the request to delete desired properties, IoT Platform returns a response. In this case, the demo_shadow_recv_handler callback is called.
    For more information, see Configure a callback to process responses.

Step 5: Request the device shadow

When an application sends a command, the device may be offline. After the device goes online, the device can request the shadow.

  1. The device calls the aiot_shadow_send operation to request the device shadow content from IoT Platform.
    AIOT_SHADOWMSG_GET indicates the message type.
    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. After IoT Platform receives the request, IoT Platform returns a response. After the device receives the response, the demo_shadow_recv_handler callback is called.
    When you specify the processing logic of the callback, take note of the following items:
    • aiot_shadow_recv_t indicates the data format. This parameter is an input parameter of the callback.
    • AIOT_SHADOWRECV_GET_REPLY indicates the message type.
    • The following table describes a sample response and the Alink format of the response.
      Example Alink format Description
      {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }, 
        "version": 5, 
        "timestamp": 1626320690
      }
      The response is indicated by the payload parameter in the Alink data.

      This sample response indicates that the request is successful. The reported parameter contains no data, which indicates that no properties are submitted.

    • In this example, the message is printed.
    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;
        }
    }

Step 6: Delete properties in the device shadow

If the device is in the latest status, the device can send a command to delete properties in the device shadow.

  1. The device calls the aiot_shadow_send operation to delete specified properties in the device shadow.
    When you send a request, take note of the following items:
    • aiot_shadow_msg_t indicates the data format. This parameter is an input parameter of the aiot_shadow_send() callback.
    • AIOT_SHADOWMSG_DELETE_REPORTED indicates the message type.
    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. You specify the properties to be deleted.
    • Sample code
              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);
              }
    • Sample message:
      Example Alink format Description
      "{\"LightSwitch\":\"null\"}", 2
      {
        "method": "delete", 
        "state": {
          "reported": {
            "LightSwitch": "null", 
          }
        }, 
        "version": 2
      }
      The message content is in the JSON format. The content is indicated by the state parameter in the Alink data. For more information, see The device deletes shadow properties.

      The following items describe the message content in this example:

      • Set the LightSwitch property to null. This value indicates that all data in the device shadow is cleared.
      • Set the version number to 2.
  3. After IoT Platform receives the request, IoT Platform returns a response. After the device receives the response, the demo_shadow_recv_handler callback is called.
    For more information, see Configure a callback to process responses.

Step 7: Exit the program

Call the aiot_shadow_deinit operation to destroy the shadow client instance and release resources.

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

What to do next

  • After you configure the sample code file, compile the file to generate an executable file In this example, the ./demos/shadow-basic-demo executable file is generated.

    For more information, see Compilation and running.

    Notice
    • When you configure the sample code file, delete the comment symbols (/* and */) on both sides of the code and change version numbers based on actual scenarios.
    • When you change version numbers, take note of the following items:
      • The version numbers of subsequent operations must be incremented. Otherwise, IoT Platform returns an error.
      • If you set the version number to -1, IoT Platform clears the device shadow data and updates the version number to 0.
  • For more information about running results, see View logs.