

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 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)。

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

此演示展示了如何使用 Dev AWS IoT ice Shadow 库连接到 Dev [AWS ice Shadow 服务](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html)。它使用 TLS（相互身份验证）与 MQTT 代理建立 M AWS IoT QTT 连接，并使用 CoreJson 库解析器来解析从 Shadow 服务收到的影子文档。[coreMQTT 库](coremqtt.md) AWS 该演示展示了基本的影子操作，例如如何更新影子文档和如何删除影子文档。该演示还展示了如何在 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. 使用 `shadow_demo_helpers.c` 中的帮助程序函数建立 MQTT 连接。

1. 使用 AWS IoT Device Shadow 库定义的宏汇编用于设备影子操作的 MQTT 主题字符串。

1. 发布到用于删除设备影子的 MQTT 主题以删除任何现有设备影子。

1. 使用 `shadow_demo_helpers.c` 中的帮助程序函数为 `/update/delta`、`/update/accepted` 和 `/update/rejected` 订阅 MQTT 主题。

1. 使用 `shadow_demo_helpers.c` 中的帮助程序函数发布 `powerOn` 的所需状态。这将导致向设备发送 `/update/delta` 消息。

1. 使用 Device Shadow 库定义的函数（`Shadow_MatchTopic`）处理传入的 MQTT 消息，并确定该消息是否与 AWS IoT 设备影子相关。`prvEventCallback`如果该消息是设备影子 `/update/delta` 消息，则主演示函数将发布第二条消息，将报告的状态更新为 `powerOn`。如果收到一条 `/update/accepted` 消息，请确认该消息与之前在更新消息中发布的消息具有相同的 `clientToken`。这标志演示的结束。

![\[影子演示终端输出\]](http://docs.aws.amazon.com/zh_cn/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)。

以下屏幕截图显示了演示成功时的预期输出。

![\[显示成功的影子演示终端输出\]](http://docs.aws.amazon.com/zh_cn/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>

要删除影子文档，请使用 D `xPublishToTopic` evice Shadow 库定义的 AWS IoT 宏调用一条空消息。这会使用 `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 库定义的宏，通过一条 JSON 格式的消息调用 `xPublishToTopic`。这会使用 `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>

使用 `MQTT_Init` 函数注册到 [coreMQTT 客户端库](https://www.freertos.org/iot-device-shadow/device-shadow-demo.html#handle-shadow-messages)的用户回调函数将通知我们传入数据包的事件。请参阅[ prvEventCallback](https://github.com/aws/amazon-freertos/blob/main/demos/device_shadow_for_aws/shadow_demo_main.c#L671-L753)上的回调函数 GitHub。

回调函数确认传入的数据包的类型为 `MQTT_PACKET_TYPE_PUBLISH`，并使用 Device Shadow 库 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) 来处理此消息。处理程序 `prvUpdateAcceptedHandler` 使用 coreJson 库解析消息，以便从消息获取 `clientToken`。此处理函数会检查 JSON 消息中的客户端令牌是否与应用程序使用的客户端令牌匹配。如果不匹配，则此函数会记录警告消息。