

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# AWS IoT Device Shadow 示範應用程式
<a name="shadow-demo"></a>

**重要**  <a name="deprecation-message-demo"></a>
此示範託管在已棄用的 Amazon-FreeRTOS 儲存庫上。我們建議您在建立新專案時從[這裡開始](freertos-getting-started-modular.md)。如果您已經有以現在已棄用之 Amazon-FreeRTOS 儲存庫為基礎的現有 FreeRTOS 專案，請參閱 [Amazon-FreeRTOS Github 儲存庫遷移指南](github-repo-migration.md)。 FreeRTOS 

## 簡介
<a name="shadow-demo-introduction"></a>

此示範示範如何使用 AWS IoT Device Shadow 程式庫連線至 [AWS Device Shadow 服務](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html)。它使用 [coreMQTT 程式庫](coremqtt.md) 與 MQTT 中介裝置和 coreJSON 程式庫剖析器建立與 TLS （相互身分驗證） 的 AWS IoT MQTT 連線，以剖析從 AWS Shadow 服務收到的陰影文件。示範顯示基本陰影操作，例如如何更新陰影文件，以及如何刪除陰影文件。此示範也會示範如何向 coreMQTT 程式庫註冊回呼函數，以處理陰影等訊息，`/update`以及從 AWS IoT Device Shadow 服務傳送`/update/delta`的訊息。

此示範僅用於學習練習，因為更新影子文件 （狀態） 的請求和更新回應是由相同的應用程式完成。在逼真的生產案例中，外部應用程式會請求遠端更新裝置的狀態，即使裝置目前未連線。裝置會在連線時確認更新請求。

**注意**  
若要設定和執行 FreeRTOS 示範，請遵循中的步驟[FreeRTOS 入門](freertos-getting-started.md)。

## 功能
<a name="shadow-demo-functionality"></a>

示範會建立單一應用程式任務，逐一查看一組範例，示範陰影`/update`和回`/update/delta`呼，以模擬切換遠端裝置的狀態。它會傳送具有新`desired`狀態的陰影更新，並等待裝置變更其`reported`狀態以回應新`desired`狀態。此外，陰影回`/update`呼用於列印不斷變化的陰影狀態。此示範也會使用與 MQTT 中介裝置的安全 AWS IoT MQTT 連線，並假設裝置影子中有一個`powerOn`狀態。

示範會執行下列操作：

1. 使用 中的協助程式函數建立 MQTT 連線`shadow_demo_helpers.c`。

1. 使用 AWS IoT Device Shadow 程式庫定義的巨集，為裝置影子操作組合 MQTT 主題字串。

1. 發佈至用於刪除裝置影子的 MQTT 主題，以刪除任何現有的裝置影子。

1. 訂閱 的 MQTT 主題`/update/delta`，`/update/accepted`並在 `/update/rejected`中使用協助程式函數`shadow_demo_helpers.c`。

1. 在 中使用`powerOn`協助程式函數發佈所需的狀態`shadow_demo_helpers.c`。這會導致`/update/delta`訊息傳送到裝置。

1. 在 中處理傳入 MQTT 訊息`prvEventCallback`，並使用 Device Shadow 程式庫 () 定義的函數，判斷訊息是否與 AWS IoT 裝置影子相關`Shadow_MatchTopic`。如果訊息是裝置影子`/update/delta`訊息，則主要示範函數會發佈第二則訊息，將報告狀態更新為 `powerOn`。如果收到`/update/accepted`訊息，請確認訊息與先前在更新訊息中發佈的訊息`clientToken`相同。這會標記示範的結尾。

![\[shadow 示範終端機輸出\]](http://docs.aws.amazon.com/zh_tw/freertos/latest/userguide/images/shadow-demo-output.png)


您可以在 檔案`freertos/demos/device_shadow_for_aws/shadow_demo_main.c`或 [ GitHub](https://github.com/aws/amazon-freertos/blob/main/demos/device_shadow_for_aws/shadow_demo_main.c) 中找到示範。

下列螢幕擷取畫面顯示示範成功時的預期輸出。

![\[shadow demo 終端機輸出顯示成功\]](http://docs.aws.amazon.com/zh_tw/freertos/latest/userguide/images/shadow-demo-screenshot.png)


## 連接至 AWS IoT MQTT 代理程式
<a name="shadow-demo-connect-mqtt"></a>

若要連線至 AWS IoT MQTT 代理程式，我們使用與 `MQTT_Connect()` 中相同的方法[coreMQTT 交互身分驗證示範](mqtt-demo-ma.md)。

## 刪除影子文件
<a name="shadow-demo-delete-document"></a>

若要刪除影子文件，請使用 AWS IoT Device Shadow 程式庫定義的巨集，`xPublishToTopic`以空白訊息呼叫 。這會使用 `MQTT_Publish`發佈至 `/delete`主題。下列程式碼區段顯示如何在函數 中完成此操作`prvShadowDemoTask`。

```
/* First of all, try to delete any Shadow document in the cloud. */
returnStatus = PublishToTopic( SHADOW_TOPIC_STRING_DELETE( THING_NAME ),
                               SHADOW_TOPIC_LENGTH_DELETE( THING_NAME_LENGTH ),
                               pcUpdateDocument,
                               0U );
```

## 訂閱影子主題
<a name="shadow-demo-subscribe"></a>

訂閱 Device Shadow 主題，以接收來自 AWS IoT 中介裝置的陰影變更通知。Device Shadow 主題是由 Device Shadow 程式庫中定義的巨集組合而成。下列程式碼區段顯示如何在 `prvShadowDemoTask`函數中完成此操作。

```
/* Then try to subscribe shadow topics. */
if( returnStatus == EXIT_SUCCESS )
{
    returnStatus = SubscribeToTopic( 
                     SHADOW_TOPIC_STRING_UPDATE_DELTA( THING_NAME ),
                     SHADOW_TOPIC_LENGTH_UPDATE_DELTA( THING_NAME_LENGTH ) );
}

if( returnStatus == EXIT_SUCCESS )
{
    returnStatus = SubscribeToTopic( 
                     SHADOW_TOPIC_STRING_UPDATE_ACCEPTED( THING_NAME ),
                     SHADOW_TOPIC_LENGTH_UPDATE_ACCEPTED( THING_NAME_LENGTH ) );
}

if( returnStatus == EXIT_SUCCESS )
{
    returnStatus = SubscribeToTopic( 
                     SHADOW_TOPIC_STRING_UPDATE_REJECTED( THING_NAME ),
                     SHADOW_TOPIC_LENGTH_UPDATE_REJECTED( THING_NAME_LENGTH ) );
}
```

## 傳送陰影更新
<a name="shadow-demo-send-updates"></a>

若要傳送陰影更新，示範會使用 Device Shadow 程式庫定義的巨集，`xPublishToTopic`呼叫 JSON 格式的訊息。這會使用 `MQTT_Publish`來發佈至 `/delete`主題。下列程式碼區段顯示如何在 `prvShadowDemoTask`函數中完成此操作。

```
#define SHADOW_REPORTED_JSON    \
    "{"                         \
    "\"state\":{"               \
    "\"reported\":{"            \
    "\"powerOn\":%01d"          \
    "}"                         \
    "},"                        \
    "\"clientToken\":\"%06lu\"" \
    "}"
snprintf( pcUpdateDocument,
          SHADOW_REPORTED_JSON_LENGTH + 1,
          SHADOW_REPORTED_JSON,
           ( int ) ulCurrentPowerOnState,
           ( long unsigned ) ulClientToken );

xPublishToTopic( SHADOW_TOPIC_STRING_UPDATE( THING_NAME ),
                 SHADOW_TOPIC_LENGTH_UPDATE( THING_NAME_LENGTH ),
                 pcUpdateDocument,
                 ( SHADOW_DESIRED_JSON_LENGTH + 1 ) );
```

## 處理陰影差異訊息和陰影更新訊息
<a name="shadow-demo-delta-and-update"></a>

使用 函數向 [coreMQTT Client Library](https://www.freertos.org/iot-device-shadow/device-shadow-demo.html#handle-shadow-messages) 註冊的使用者回呼`MQTT_Init`函數將通知我們傳入的封包事件。請參閱 GitHub 上的回呼函數 [ prvEventCallback](https://github.com/aws/amazon-freertos/blob/main/demos/device_shadow_for_aws/shadow_demo_main.c#L671-L753)。

回呼函數會確認傳入封包類型為 `MQTT_PACKET_TYPE_PUBLISH`，並使用 Device Shadow Library API `Shadow_MatchTopic`來確認傳入訊息是影子訊息。

如果傳入訊息是類型為 的陰影訊息`ShadowMessageTypeUpdateDelta`，則我們呼叫 [ prvUpdateDeltaHandler](https://github.com/aws/amazon-freertos/blob/main/demos/device_shadow_for_aws/shadow_demo_main.c#L464-L580) 來處理此訊息。處理常式`prvUpdateDeltaHandler`使用 coreJSON 程式庫剖析訊息以取得 `powerOn` 狀態的差異值，並將其與本機維護的目前裝置狀態進行比較。如果這些不同，則會更新本機裝置狀態，以反映陰影文件中`powerOn`狀態的新值。

如果傳入訊息是類型為 的陰影訊息`ShadowMessageTypeUpdateAccepted`，則我們呼叫 [ prvUpdateAcceptedHandler](https://github.com/aws/amazon-freertos/blob/main/demos/device_shadow_for_aws/shadow_demo_main.c#L584-L667) 來處理此訊息。處理常式會使用 coreJSON `prvUpdateAcceptedHandler` 程式庫剖析訊息，`clientToken`從訊息取得 。此處理常式函數會檢查來自 JSON 訊息的用戶端字符是否符合應用程式使用的用戶端字符。如果不相符，函數會記錄警告訊息。