Usage examples

Updated at:

The ./demos/shadow_basic_demo.c file demonstrates the device shadow feature.

Background information

  • For more information about the device shadow feature, see Overview.

  • This feature is based on MQTT access. For more information about the code for MQTT access, see MQTT access.

Step 1: Initialization

  1. Add the header file.
    ……
    ……
    
    #include "aiot_shadow_api.h"

  2. Configure underlying dependencies and log output.

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call aiot_shadow_init to create a Shadow
        shadow_handle = aiot_shadow_init();
        if (shadow_handle == NULL) {
            printf("aiot_shadow_init failed\n");
            return -1;
        }

Step 2: Configure features

Call aiot_shadow_setopt to configure the following features.

  1. Associate the MQTT connection handle.
    Important Ensure that device authentication information and related parameters are configured. For more information, see MQTT configuration connection parameters.
    •     aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_MQTT_HANDLE, mqtt_handle);
    • Configuration itemExample valueDescription
      AIOT_SHADOWOPT_MQTT_HANDLEmqtt_handleFeature requests are based on an MQTT connection. This configuration item associates the MQTT connection handle.

  2. Configure a message callback.
    •     aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);
    • Configuration itemExample valueDescription
      AIOT_SHADOWOPT_RECV_HANDLERdemo_shadow_recv_handlerThis function is called when a device shadow message is received.

Step 3: The device reports its state

When a device is online, it reports its status to its shadow. An application can then retrieve the device status from the shadow.

  1. The device calls aiot_shadow_send to report its latest state to the device shadow in IoT Platform.
    When you report the state, note the following:
    • The data structure is of type aiot_shadow_msg_t and is the input parameter for aiot_shadow_send().
    • The message type for state reporting is 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. Set the content of the state reporting message.
    •         res = demo_update_shadow(shadow_handle, "{\"LightSwitch\":1}", 0);
              if (res < 0) {
                  printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
              }
    • The following table describes the sample message content.
      ExampleAlink formatDescription
      {
          "LightSwitch": 1
      }
      {
        "method": "update", 
        "state": {
          "reported": {
            "LightSwitch": 1
          }
        }, 
        "version": 0
      }
      stateThe device reports its state.

      The message content in the sample code is as follows:

      • Sets the LightSwitch property to 1.
      • Sets the version number to 0.
        Note
        • The version number for subsequent operations must be incremented. Otherwise, IoT Platform returns an error.
        • If you set the version number to -1, IoT Platform purges the device shadow data and updates the version number to 0.
  3. After IoT Platform receives the device shadow message, it updates the device shadow. Then, IoT Platform returns an acknowledgement message to the device.
  4. After the device receives the acknowledgement message, the demo_shadow_recv_handler callback function is triggered.

    Write the logic for the callback function based on the following information:

    • The data structure is aiot_shadow_recv_t, which is an input parameter for the callback function.
    • The message type is AIOT_SHADOWRECV_GENERIC_REPLY.
    • The following table shows a sample acknowledgement message and its Alink data format.
      ExampleAlink formatDescription
      {
          "status":"success",
          "version":0
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success", 
          "version": 0
        }, 
        "timestamp": 1626317187
      }
      The content of the acknowledgement message is the value of the payload

      The sample message indicates that the state was reported successfully.

    • The sample code only prints the information.
    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: The application changes the device state

You can change the device state by sending desired properties to the device shadow from an application or the IoT Platform console.

  1. Send the desired state to the device shadow.
    • Develop an IoT Platform cloud application to call the API and send the desired properties of the device shadow. For more information, see UpdateDeviceShadow.
    • Send the desired properties of the device shadow on the Device Details page in the IoT Platform console. For more information, see View and update a device shadow.
  2. IoT Platform updates the device shadow based on the desired state message. Then, it sends the updated device shadow to the device.
  3. After the device receives the device shadow, the demo_shadow_recv_handler callback function is triggered.
    Important If the device is offline, see Step 5: The device retrieves the device shadow content.
    Write the logic for the callback function based on the following information:
    • The data structure type is aiot_shadow_recv_t, an input parameter for the callback function.
    • The message type of the desired state is AIOT_SHADOWRECV_CONTROL.
    • The following table shows a sample desired state message and its Alink data format.
      ExampleAlink formatDescription
      {
          "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 content of the desired state message is the value of the payload

      The sample message sets the desired property value of LightSwitch to 0.

    • The sample code only prints the information.
    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 state is updated, report the latest state to the device shadow and process the acknowledgement message.
    For more information, see Step 3: The device reports its state.
  5. After you report the latest state, call aiot_shadow_send to delete the desired properties.
    When you request to delete desired properties, note the following:
    • The data structure type is aiot_shadow_msg_t, an input parameter for aiot_shadow_send().
    • The message type is AIOT_SHADOWMSG_CLEAN_DESIRED.
    • The sample code deletes all desired properties and sets the version number 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 the request to delete properties is sent, IoT Platform returns an acknowledgement message. The demo_shadow_recv_handler callback function is triggered.

Step 5: The device retrieves the device shadow content

If a device is offline when an application sends a command, the device can retrieve the shadow content after it comes back online.

  1. The device calls aiot_shadow_send to send a query instruction to IoT Platform to retrieve the device shadow content.
    The message type of the query instruction is 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. After IoT Platform receives the query instruction, it returns the query result. After the device receives the result, the demo_shadow_recv_handler callback function is triggered.
    Write the logic for the callback function based on the following information:
    • The data structure type is aiot_shadow_recv_t, which is an input parameter for the callback function.
    • The message type is AIOT_SHADOWRECV_GET_REPLY.
    • The following table shows a sample message returned after a query and its Alink data format.
      ExampleAlink formatDescription
      {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }
      {
        "method": "reply", 
        "payload": {
          "status": "success",
          "state": {
              "reported": {
      
              }
          },
          "metadata": {
              "reported": {
      
              }
          }
      }, 
        "version": 5, 
        "timestamp": 1626320690
      }
      The content of the property retrieval message is the value of the payload

      The sample message indicates that the request was successful. The retrieved message value is empty because no message has been reported.

    • The sample code only prints the information.
    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: The device deletes shadow properties

If the device already has the latest status, it can send a command to delete a specific property stored in the device shadow.

  1. The device calls aiot_shadow_send to send a delete instruction to IoT Platform to delete specified properties from the device shadow.
    When you send a delete instruction, note the following:
    • The data structure type is aiot_shadow_msg_t, which is an input parameter for aiot_shadow_send().
    • The message type of the delete instruction is 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_reported.reported = reported;
        message.data.delete_reported.version = version;
    
        return aiot_shadow_send(shadow_handle, &message);
    }
  2. Set the content of the delete instruction.
    •         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);
              }
    • The following table describes the sample message content.
      ExampleAlink formatDescription
      "{\"LightSwitch\":\"null\"}", 2
      {
        "method": "delete", 
        "state": {
          "reported": {
            "LightSwitch": "null", 
          }
        }, 
        "version": 2
      }
      stateThe device deletes shadow properties.

      The message content in the sample code is as follows:

      • Sets the LightSwitch property to null. This deletes the property from the device shadow.
      • Sets the version number to 2.
  3. After IoT Platform receives the delete instruction, it returns an acknowledgement message. After the device receives the message, the demo_shadow_recv_handler callback function is triggered.

Step 7: Exit the program

Call aiot_shadow_deinit to destroy the Shadow client instance.

    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 file, compile it to generate the executable file ./output/shadow-basic-demo.

    For more information, see Compile and run.

    Important
    • When you configure the demo file, uncomment the code for your test scenario, such as reporting state, retrieving properties, or deleting desired properties. You must also change the version number as needed. The comment symbols are /* and */.
    • When you update the version number, note the following:
      • The version number for subsequent operations must be incremented. Otherwise, IoT Platform returns an error.
      • If you set the version number to -1, IoT Platform purges the device shadow data and updates the version number to 0.
  • Operational log.