Usage examples
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
- Add the header file.
…… …… #include "aiot_shadow_api.h" Configure underlying dependencies and log output.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);- Call aiot_shadow_init to create a
Shadowshadow_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.
- 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 item Example value Description AIOT_SHADOWOPT_MQTT_HANDLE mqtt_handle Feature requests are based on an MQTT connection. This configuration item associates the MQTT connection handle.
- Configure a message callback.
aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);Configuration item Example value Description AIOT_SHADOWOPT_RECV_HANDLER demo_shadow_recv_handler This 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.
- 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); } - The data structure is of type aiot_shadow_msg_t and is the input parameter for
- 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.
Example Alink format Description { "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
LightSwitchproperty to1. - 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 to0.
- Sets the
- After IoT Platform receives the device shadow message, it updates the device shadow. Then, IoT Platform returns an acknowledgement message to the device.
- After the device receives the acknowledgement message, the
demo_shadow_recv_handlercallback 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.
Example Alink format Description { "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.
- 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.
- IoT Platform updates the device shadow based on the desired state message. Then, it sends the updated device shadow to the device.
- After the device receives the device shadow, the
demo_shadow_recv_handlercallback function is triggered.Write the logic for the callback function based on the following information:Important If the device is offline, see Step 5: The device retrieves the device shadow content.- 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.
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 content of the desired state message is the value of the payload The sample message sets the desired property value of
LightSwitchto0. - 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; } } - 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.
- 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); } - The data structure type is aiot_shadow_msg_t, an input parameter for
- After the request to delete properties is sent, IoT Platform returns an acknowledgement message. The
demo_shadow_recv_handlercallback function is triggered.For more information, see Configure the callback for the acknowledgement message.
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.
- 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); } - After IoT Platform receives the query instruction, it returns the query result. After the device receives the result, the
demo_shadow_recv_handlercallback 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.
Example Alink format Description { "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.
- 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); } - The data structure type is aiot_shadow_msg_t, which is an input parameter for
- 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.
Example Alink format Description "{\"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
LightSwitchproperty tonull. This deletes the property from the device shadow. - Sets the version number to
2.
- Sets the
- After IoT Platform receives the delete instruction, it returns an acknowledgement message. After the device receives the message, the
demo_shadow_recv_handlercallback function is triggered.For more information, see Configure the callback for the acknowledgement message.
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 to0.
- 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