./demos/shadow_basic_demo.c展示裝置影子功能。
背景資訊
步驟一:初始化
- 添加標頭檔。
…… …… #include "aiot_shadow_api.h"
配置底層依賴和日誌輸出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
- 調用aiot_shadow_init,建立
Shadow
shadow_handle = aiot_shadow_init(); if (shadow_handle == NULL) { printf("aiot_shadow_init failed\n"); return -1; }
步驟二:配置功能
調用aiot_shadow_setopt,配置以下功能。
- 關聯MQTT串連的控制代碼。重要 配置裝置影子功能參數前,請確保已配置裝置認證資訊等相關參數。具體操作,請參見MQTT配置串連參數。
aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_MQTT_HANDLE, mqtt_handle);
配置項 樣本值 說明 AIOT_SHADOWOPT_MQTT_HANDLE mqtt_handle 裝置影子功能的請求基於MQTT串連,通過該配置項,關聯MQTT串連控制代碼。
- 配置訊息回調。
aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);
配置項 樣本值 說明 AIOT_SHADOWOPT_RECV_HANDLER demo_shadow_recv_handler 接收裝置影子相關訊息時,調用該函數。
步驟三:裝置主動上報狀態
裝置線上時,主動上報裝置狀態到影子,應用程式主動擷取裝置影子狀態。
- 裝置調用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); }
- 上報狀態訊息的資料結構類型為aiot_shadow_msg_t,是
- 設定上報狀態訊息的內容。
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
。
- 將屬性
- 物聯網平台接收到裝置影子的訊息後,更新影子檔案。然後,向裝置返回應答報文。
- 裝置接收應答報文後,觸發回呼函數
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; } }
步驟四:應用程式改變裝置狀態
您可以通過應用程式,或登入物聯網平台,向裝置影子發送期望屬性,最終實現改變裝置狀態。
- 下發期望狀態給裝置影子。
- 您可以開發物聯網平台雲端應用程式,調用API下發裝置影子期望屬性,詳細說明,請參見UpdateDeviceShadow。
- 您可以在物聯網平台的裝置詳情頁,直接下發裝置影子期望屬性,具體操作,請參見查看與更新裝置影子。
- 物聯網平台根據下發的期望狀態訊息,更新影子內容。然後,下發該影子內容至裝置。
- 裝置接收影子檔案後,觸發回呼函數
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; } }
- 裝置更新狀態後,上報最新的狀態至裝置影子,並處理應答報文。具體操作,請參見步驟三:裝置主動上報狀態。
- 上報最新的狀態後,調用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); }
- 刪除期望屬性訊息的資料結構類型為aiot_shadow_msg_t,是
- 刪除屬性的請求發送後,物聯網平台返回應答報文,觸發回呼函數
demo_shadow_recv_handler
。具體操作,請參見配置應答報文的回呼函數。
步驟五:裝置主動擷取裝置影子內容
若應用程式發送指令時,裝置離線。裝置再次上線後,將主動擷取裝置影子內容。
- 裝置調用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); }
- 物聯網平台收到查詢指令後,返回查詢結果。裝置接收返回結果後,觸發回呼函數
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; } }
步驟六:裝置主動刪除影子屬性
若裝置端已經是最新狀態,裝置端可以主動發送指令,刪除裝置影子中儲存的某條屬性狀態。
- 裝置調用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); }
- 刪除指令的資料結構類型為aiot_shadow_msg_t,是
- 設定刪除指令的內容。
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
。
- 將屬性
- 物聯網平台收到刪除指令後,返回應答報文。裝置接收報文後,觸發回呼函數
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
。
- 配置Demo檔案時,請根據測試情境,取消上報狀態、擷取屬性、刪除期望或上報屬性的相關代碼兩邊的注釋符號(
作業記錄。