

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

# 使用 AWS IoT 裝置SDKs建置解決方案
<a name="iot-tutorials-sdk-intro"></a>

本節中的教學課程將逐步引導您開發可使用 部署到生產環境的 IoT 解決方案 AWS IoT。

這些教學課程可能需要比 章節中更多的時間來完成，[使用 AWS IoT 裝置用戶端建置示範](iot-tutorials-dc-intro.md)因為它們使用 AWS IoT 裝置SDKs並更詳細地解釋正在套用的概念，以協助您建立安全可靠的解決方案。

## 使用 AWS IoT 裝置SDKs開始建置解決方案
<a name="iot-sdk-tutorial-overview"></a>

這些教學課程會逐步解說不同的 AWS IoT 案例。在適當的情況下，教學課程會使用 AWS IoT 裝置 SDKs。

**Topics**
+ [使用 AWS IoT 裝置SDKs開始建置解決方案](#iot-sdk-tutorial-overview)
+ [教學課程： AWS IoT Core 使用 裝置 SDK 將 AWS IoT 裝置連線至](sdk-tutorials.md)
+ [建立 AWS IoT 規則將裝置資料路由到其他 服務](iot-rules-tutorial.md)
+ [在裝置離線時保留裝置狀態](iot-shadows-tutorial.md)
+ [教學課程：為 建立自訂授權方 AWS IoT Core](custom-auth-tutorial.md)
+ [教學課程：使用 AWS IoT 和 Raspberry Pi 監控土壤濕度](iot-moisture-tutorial.md)

# 教學課程： AWS IoT Core 使用 裝置 SDK 將 AWS IoT 裝置連線至
<a name="sdk-tutorials"></a>

本教學課程示範如何將裝置連線至 ， AWS IoT Core 以便傳送和接收進出的資料 AWS IoT。完成本教學課程後，您的裝置將設定為連線至 ， AWS IoT Core 而且您將了解裝置與 的通訊方式 AWS IoT。

**Topics**
+ [先決條件](#sdk-tutorials-prereq)
+ [為 準備您的裝置 AWS IoT](#sdk-tutorials-prepare)
+ [檢閱 MQTT 通訊協定](#sdk-tutorials-mqtt-review)
+ [檢閱 pubsub.py 裝置 SDK 範例應用程式](#sdk-tutorials-explore-sample)
+ [連接您的裝置並與 通訊 AWS IoT Core](#sdk-tutorials-experiment)
+ [檢視結果](#sdk-tutorials-conclusion)
+ [教學課程：使用 適用於 Embedded C 的 AWS IoT Device SDK](iot-embedded-c-sdk.md)

## 先決條件
<a name="sdk-tutorials-prereq"></a>

開始本教學課程之前，請確定您有：
+ 

**已完成 [AWS IoT Core 教學課程入門](iot-gs.md)**  
在該教學課程中您必須進行 [設定您的裝置](configure-device.md) 的部分中，請為您的裝置選取 [連接 Raspberry Pi 或其他裝置](connecting-to-existing-device.md) 選項，並使用 Python 語言選項來配置您的裝置。
**注意**  
讓該教學課程中的終端機視窗處於開啟狀態，因為您還會在本教學課程中用到它。
+ 

**可執行適用於 Python 的 AWS IoT Device SDK v2 的裝置。**  
本教學課程說明如何使用 Python AWS IoT Core 程式碼範例將裝置連接至 ，這需要相對強大的裝置。若您使用是資源受限的裝置，這些程式碼範例可能無法在這些裝置上運作。在這種情況下，您可能會在[教學課程：使用 適用於 Embedded C 的 AWS IoT Device SDK](iot-embedded-c-sdk.md)教學課程中獲得更多成功。
+ 

**取得連線至裝置所需的資訊**  
若要將裝置連線至 AWS IoT，您必須擁有物件名稱、主機名稱和連接埠號碼的相關資訊。
**注意**  
您也可以使用自訂身分驗證來連接裝置 AWS IoT Core。您傳遞給授權方 Lambda 函數的連線資料取決於您使用的通訊協定。
  + **物件名稱**：您要連線的 AWS IoT 物件名稱。您必須已將 註冊為裝置做為 AWS IoT 物件。如需詳細資訊，請參閱[使用 管理裝置 AWS IoT](iot-thing-management.md)。
  + **主機名稱**：帳戶特定 IoT 端點的主機名稱。
  + **連接埠號碼**：要連線的連接埠號碼。

  您可以使用 AWS IoT Python SDK 中的 `configureEndpoint`方法來設定主機名稱和連接埠號碼。

  ```
  myAWSIoTMQTTClient.configureEndpoint("random.iot.region.amazonaws.com", 8883)
  ```

## 為 準備您的裝置 AWS IoT
<a name="sdk-tutorials-prepare"></a>

在 [AWS IoT Core 教學課程入門](iot-gs.md) 中，您準備好裝置和 AWS 帳戶，使其可進行通訊。本節會檢閱適用於任何裝置連線之準備工作的各個層面 AWS IoT Core。

若為要連線至 AWS IoT Core的裝置：

1. 您必須具有 **AWS 帳戶**。

    AWS 帳戶 如果您還沒有 ， 中的程序會[設定 AWS 帳戶](setting-up.md)說明如何建立 。

1. 在該帳戶中，您必須為 AWS 帳戶 和 區域中的裝置定義下列**AWS IoT 資源**。

   程序[建立 AWS IoT 資源](create-iot-resources.md) 中的程序說明如何為您在 AWS 帳戶 和區域中的裝置建立這些資源。
   + 以 AWS IoT 註冊並啟用以驗證裝置的**裝置憑證**。

     憑證通常是使用 **AWS IoT 物件**建立並加以連接。雖然裝置不需要物件才能連線 AWS IoT，但它可讓裝置使用其他 AWS IoT 功能。
   + 連接到裝置憑證**的政策**，授權它連接到 AWS IoT Core 並執行您想要它的所有動作。

1. 可存取您 AWS 帳戶之裝置端點的**網際網路連線**。

   裝置端點會在 中說明[AWS IoT 裝置資料和服務端點](iot-connect-devices.md#iot-connect-device-endpoints)，並可在 [AWS IoT 主控台的設定頁面](https://console.aws.amazon.com/iot/home#/settings)中查看。

1. **通訊軟體**，例如 AWS IoT 裝置 SDK 提供。本教學課程使用[適用於 Python 的AWS IoT 裝置 SDK v2](https://github.com/aws/aws-iot-device-sdk-python-v2#aws-iot-device-sdk-v2-for-python)。

## 檢閱 MQTT 通訊協定
<a name="sdk-tutorials-mqtt-review"></a>

在我們談論範例應用程式之前，其有助於了解 MQTT 通訊協定。MQTT 通訊協定提供了一些優於其他網路通訊協定 (如 HTTP) 的優勢，這使其成為 IoT 裝置的熱門選擇。本節將會檢視 MQTT 適用於本教學課程的主要層面。如需 MQTT 與 HTTP 比較的資訊，請參閱 [選擇裝置通訊的應用程式通訊協定](protocols.md#protocol-selection)。

**MQTT 使用一個發佈/訂閱通訊模型。**  
MQTT 通訊協定會與其主機一起使用發佈/訂閱通訊模型。此模型與 HTTP 使用的請求/回應模型不同。使用 MQTT，裝置會與由唯一用戶端 ID 識別的主機建立工作階段。如要傳送資料，裝置會將主題識別的訊息發佈至主機中的訊息代理程式。為了收到訊息代理程式的訊息，裝置會在訂閱請求中傳送主題篩選條件給訊息代理程式，以訂閱其會收到的主題。

**MQTT 支援持久性工作階段**  
訊息代理程式會收到來自裝置的訊息，並將訊息發佈至已加以訂閱的裝置。利用[持久性工作階段](mqtt.md#mqtt-persistent-sessions) (即使啟動裝置以中斷連線，工作階段仍保持活動狀態)，裝置可擷取中斷連線時所發佈的訊息。在裝置端，MQTT 支援服務品質層級 ([QoS](mqtt.md#mqtt-qos))，以確保主機接收裝置所傳送的訊息。

## 檢閱 pubsub.py 裝置 SDK 範例應用程式
<a name="sdk-tutorials-explore-sample"></a>

本節會從用於本教學課程中之**適用於 Python 的AWS IoT 裝置 SDK v2** 檢閱 `pubsub.py` 範例應用程式。在這裡，我們將檢閱其如何連線至 AWS IoT Core 以發佈和訂閱 MQTT 訊息。下一節提供一些練習，協助您探索裝置連線和通訊的方式 AWS IoT Core。

**`pubsub.py` 範例應用程式示範 MQTT 連線的這些層面 AWS IoT Core：**
+ [通訊協定](#sdk-tutorials-explore-protocols)
+ [持久性工作階段](#sdk-tutorials-explore-persistent)
+ [服務品質](#sdk-tutorials-explore-qos)
+ [訊息發佈](#sdk-tutorials-explore-publish)
+ [訊息訂閱](#sdk-tutorials-explore-subscribe)
+ [裝置中斷連線及重新連線](#sdk-tutorials-explore-connect)

### 通訊協定
<a name="sdk-tutorials-explore-protocols"></a>

`pubsub.py` 範例會示範使用 MQTT 和透過 WSS 的 MQTT 通訊協定的 MQTT 連線。[AWS 通用執行時間 (AWS CRT)](https://github.com/awslabs/aws-crt-python#aws-crt-python) 程式庫提供低階通訊協定支援，並包含在適用於 Python 的 AWS IoT Device SDK v2 中。

#### MQTT
<a name="sdk-tutorials-explore-mqtt"></a>

中使用 AWS IoT Core MQTT 通訊協定[https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py](https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py)與 建立連線`pubsub.py`的範例呼叫 `mtls_from_path`（如下所示）。 `mtls_from_path`使用 X.509 憑證和 TLS v1.2 來驗證裝置。 AWS CRT 程式庫會處理該連線的較低層級詳細資訊。

```
mqtt_connection = mqtt_connection_builder.mtls_from_path(
    endpoint=args.endpoint,
    cert_filepath=args.cert,
    pri_key_filepath=args.key,
    ca_filepath=args.ca_file,
    client_bootstrap=client_bootstrap,
    on_connection_interrupted=on_connection_interrupted,
    on_connection_resumed=on_connection_resumed,
    client_id=args.client_id,
    clean_session=False,
    keep_alive_secs=6
)
```

`endpoint`  
 AWS 帳戶您的 IoT 裝置端點  
於範例應用程式中，此值會從命令列傳入。

`cert_filepath`  
裝置憑證檔案的路徑。  
於範例應用程式中，此值會從命令列傳入。

`pri_key_filepath`  
使用其憑證檔案建立的裝置私密金鑰檔案路徑  
於範例應用程式中，此值會從命令列傳入。

`ca_filepath`  
根 CA 檔案的路徑。僅當 MQTT 伺服器使用不在您信任庫中的憑證時才需要。  
於範例應用程式中，此值會從命令列傳入。

`client_bootstrap`  
處理通訊端通訊活動的通用執行時間物件  
在範例應用程式中，此物件就在呼叫 `mqtt_connection_builder.mtls_from_path` 之前進行實例化。

`on_connection_interrupted``on_connection_resumed`  
裝置連線遭到中斷並回復時呼叫的回呼函數。

`client_id`  
唯一在 AWS 區域中識別此裝置的 ID。  
於範例應用程式中，此值會從命令列傳入。

`clean_session`  
是否啟動新的持續性工作階段，或者，若存在，則重新連接到現有的工作階段

`keep_alive_secs`  
在 `CONNECT` 請求中傳送的保持活動值 (以秒為單位)。Ping 會在此時間間隔自動傳送。若在此值的 1.5 倍之後未收到 ping，則伺服器會假設連線中斷。

#### 透過 WSS 的 MQTT
<a name="sdk-tutorials-explore-mqtt-wss"></a>

`pubsub.py` 範例呼叫 [https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py](https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py) 中的 `websockets_with_default_aws_signing` (如圖所示)，使用透過 WSS 的 MQTT 通訊協定來建立與 AWS IoT Core 的連線。`websockets_with_default_aws_signing` 會使用 [Signature 第 4 版](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)透過 WSS 建立 MQTT 連線，來驗證裝置。

```
mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing(
    endpoint=args.endpoint,
    client_bootstrap=client_bootstrap,
    region=args.signing_region,
    credentials_provider=credentials_provider,
    websocket_proxy_options=proxy_options,
    ca_filepath=args.ca_file,
    on_connection_interrupted=on_connection_interrupted,
    on_connection_resumed=on_connection_resumed,
    client_id=args.client_id,
    clean_session=False,
    keep_alive_secs=6
)
```

`endpoint`  
 AWS 帳戶您的 IoT 裝置端點  
於範例應用程式中，此值會從命令列傳入。

`client_bootstrap`  
處理通訊端通訊活動的通用執行時間物件  
在範例應用程式中，此物件就在呼叫 `mqtt_connection_builder.websockets_with_default_aws_signing` 之前進行實例化。

`region`  
Signature V4 身分驗證所使用的 AWS 簽署區域。於 `pubsub.py` 中，其會傳遞在命令列中輸入的參數。  
於範例應用程式中，此值會從命令列傳入。

`credentials_provider`  
提供用於身分驗證的 AWS 登入資料  
在範例應用程式中，此物件就在呼叫 `mqtt_connection_builder.websockets_with_default_aws_signing` 之前進行實例化。

`websocket_proxy_options`  
HTTP 代理選項 (若使用代理主機)  
在範例應用程式中，此值就在呼叫 `mqtt_connection_builder.websockets_with_default_aws_signing` 之前進行初始化。

`ca_filepath`  
根 CA 檔案的路徑。僅當 MQTT 伺服器使用不在您信任庫中的憑證時才需要。  
於範例應用程式中，此值會從命令列傳入。

`on_connection_interrupted``on_connection_resumed`  
裝置連線遭到中斷並回復時呼叫的回呼函數。

`client_id`  
在 AWS 區域中唯一識別此裝置的 ID。  
於範例應用程式中，此值會從命令列傳入。

`clean_session`  
是否啟動新的持續性工作階段，或者，若存在，則重新連接到現有的工作階段

`keep_alive_secs`  
在 `CONNECT` 請求中傳送的保持活動值 (以秒為單位)。Ping 會在此時間間隔自動傳送。若在此值的 1.5 倍之後未收到 ping，則伺服器會假設連線中斷。

#### HTTPS
<a name="sdk-tutorials-explore-https"></a>

HTTPS 如何？ AWS IoT Core 支援發佈 HTTPS 請求的裝置。從程式設計的角度來看，裝置會將 HTTPS 請求傳送至 AWS IoT Core ，就像任何其他應用程式一樣。有關從裝置傳送 HTTP 訊息的 Python 程式範例，請參閱使用 Python `requests` 程式庫的 [HTTPS 程式碼範例](http.md#codeexample)。此範例 AWS IoT Core 使用 HTTPS 傳送訊息至 ， AWS IoT Core 讓 將其解譯為 MQTT 訊息。

雖然 AWS IoT Core 支援來自裝置的 HTTPS 請求，但請務必檢閱 的相關資訊，[選擇裝置通訊的應用程式通訊協定](protocols.md#protocol-selection)以便您可以針對裝置通訊所使用的通訊協定做出明智的決策。

### 持久性工作階段
<a name="sdk-tutorials-explore-persistent"></a>

於範例應用程式中，將 `clean_session` 參數設定為 `False` 表示連線應該是持續的。於實踐中，這意味著透過此呼叫開啟的連線重新連接至現有的持久性工作階段 (如若存在)。否則，其會建立並連線至新的持續工作階段。

使用持續性工作階段，在裝置未連線時，訊息代理程式會儲存傳送至裝置的訊息。當裝置重新連線至持續性工作階段時，訊息代理程式會將所有已訂閱的儲存訊息傳送至裝置。

若無持續性工作階段，裝置將不會收到裝置未連線時傳送的訊息。要使用哪個選項取決於您的應用程式，及是否必須對裝置未連接時產生訊息進行通訊。如需詳細資訊，請參閱[MQTT 持久性工作階段](mqtt.md#mqtt-persistent-sessions)。

### 服務品質
<a name="sdk-tutorials-explore-qos"></a>

當裝置發佈和訂閱訊息時，可以設定偏好的服務品質 (QoS)。 AWS IoT 支援 QoS 層級 0 和 1 以進行發佈和訂閱操作。如需 中 QoS 層級的詳細資訊 AWS IoT，請參閱 [MQTT 服務品質 (QoS) 選項](mqtt.md#mqtt-qos)。

Python 的 AWS CRT 執行期會為其支援的 QoS 層級定義這些常數：


**Python 服務品質層級**  

| MQTT QoS 層級 | SDK 所使用的 Python 符號值 | Description | 
| --- | --- | --- | 
| QoS 層級 0 | mqtt.QoS.AT\$1MOST\$1ONCE | 無論是否收到訊息，都只會嘗試傳送一次訊息。該訊息可能根本不會傳送，例如，若裝置未連線或網路發生錯誤。 | 
| QoS 層級 1 | mqtt.QoS.AT\$1LEAST\$1ONCE | 訊息會重複傳送，直到收到 PUBACK 確認為止。 | 

於範例應用程式中，發佈和訂閱請求的 QoS 層級為 1 (`mqtt.QoS.AT_LEAST_ONCE`)。
+ 

**發佈時的 QoS**  
當裝置發佈 QoS 層級 1 的訊息時，其會重複傳送訊息，直到收到來自訊息代理程式的 `PUBACK` 回應為止。若裝置未連線，訊息會在重新連線後佇列等待傳送。
+ 

**訂閱時的 QoS**  
當裝置訂閱 QoS 層級 1 的訊息時，訊息代理程式會儲存裝置訂閱的訊息，直至其可傳送至裝置為止。訊息代理程式會重新傳送訊息，直至收到來自裝置的 `PUBACK` 回應。

### 訊息發佈
<a name="sdk-tutorials-explore-publish"></a>

成功建立與 的連線後 AWS IoT Core，裝置可以發佈訊息。`pubsub.py` 範例經由呼叫 `mqtt_connection` 物件的 `publish` 操作來完成此作業。

```
mqtt_connection.publish(
    topic=args.topic,
    payload=message,
    qos=mqtt.QoS.AT_LEAST_ONCE
)
```

`topic`  
識別訊息的訊息主題名稱  
於範例應用程式中，這會從命令列傳入。

`payload`  
將訊息承載格式化為字串 (例如，JSON 文件)  
於範例應用程式中，這會從命令列傳入。  
JSON 文件是一種常見的承載格式，並且由其他服務辨識 AWS IoT ；不過，訊息承載的資料格式可以是發佈者和訂閱者同意的任何內容。但在某些狀況下，對大多數的作業而言，其他 AWS IoT 服務僅識別 JSON 和 CBOR。

`qos`  
此訊息的 QoS 層級

### 訊息訂閱
<a name="sdk-tutorials-explore-subscribe"></a>

若要接收來自 AWS IoT 和其他 服務和裝置的訊息，裝置會依其主題名稱訂閱這些訊息。裝置可透過指定[主題名稱](topics.md#topicnames)訂閱個別訊息，及透過指定可包含萬用字元字的[主題篩選條件](topics.md#topicfilters)訂閱一組訊息。`pubsub.py` 範例會使用顯示於此處的程式碼來訂閱訊息，並註冊回呼函數，以於收到訊息之後進行處理。

```
subscribe_future, packet_id = mqtt_connection.subscribe(
    topic=args.topic,
    qos=mqtt.QoS.AT_LEAST_ONCE,
    callback=on_message_received
)
subscribe_result = subscribe_future.result()
```

`topic`  
要訂閱的主題。此可為主題名稱或主題篩選條件。  
於範例應用程式中，這會從命令列傳入。

`qos`  
訊息代理程式是否應該在裝置中斷連線斷時儲存這些訊息。  
`mqtt.QoS.AT_LEAST_ONCE` 的值 (QoS 層級 1) 需要在建立連線時指定持久性工作階段 (`clean_session=False`)。

`callback`  
呼叫來處理訂閱訊息的函數。

此 `mqtt_connection.subscribe` 函式會傳回一個未來和一個封包 ID。若訂閱請求已順利啟動，傳回的封包 ID 大於 0。如要確定訊息代理程式收到訂閱並已註冊，您必須等待非同步作業的結果傳回，如程式碼範例中所示。

**回呼函數**  
`pubsub.py` 範例中的回呼會在裝置收到訂閱訊息時進行處理。

```
def on_message_received(topic, payload, **kwargs):
    print("Received message from topic '{}': {}".format(topic, payload))
    global received_count
    received_count += 1
    if received_count == args.count:
        received_all_event.set()
```

`topic`  
訊息的主題  
即使您已訂閱了主題篩選條件，此為所接收訊息的特定主題名稱。

`payload`  
訊息承載  
此格式為應用程式專用。

`kwargs`  
可能的其他引數，如 [https://awslabs.github.io/aws-crt-python/api/mqtt.html#awscrt.mqtt.Connection.subscribe](https://awslabs.github.io/aws-crt-python/api/mqtt.html#awscrt.mqtt.Connection.subscribe) 中所述。

在 `pubsub.py` 範例中，`on_message_received` 只會顯示主題及其承載。其還會計算在達到限制後，所收到結束程式的訊息。

您的應用程式會評估主題和承載，以決定要執行的動作。

### 裝置中斷連線及重新連線
<a name="sdk-tutorials-explore-connect"></a>

`pubsub.py` 範例包含在裝置中斷連線及重新建立連線時呼叫的回呼函數。您的裝置對這些事件採取的動作為應用程式專用。

裝置首次連線時，必須訂閱要接收的主題。若裝置的工作階段在重新連線時存在，則會還原其訂閱，並在重新連線後將來自這些訂閱的任何儲存訊息傳送至裝置。

若裝置的工作階段在重新連線時不復存在，則必須重新訂閱其訂閱。持續性工作階段具有限的存留期，當裝置中斷連線過久時，可能會過期。

## 連接您的裝置並與 通訊 AWS IoT Core
<a name="sdk-tutorials-experiment"></a>

本節提供一些練習，協助您探索將裝置連線至 AWS IoT Core的不同層面。於這些練習中，您將會使用 AWS IoT 主控台中的 [MQTT 測試用戶端](https://console.aws.amazon.com/iot/home#/test)，查看裝置發佈的內容，及將訊息發佈至裝置。這些練習使用來自[適用於 Python 的AWS IoT 裝置 SDK v2](https://github.com/aws/aws-iot-device-sdk-python-v2/tree/master/samples#sample-apps-for-the-aws-iot-device-sdk-v2-for-python) 的 [https://github.com/aws/aws-iot-device-sdk-python-v2/blob/master/samples/pubsub.py](https://github.com/aws/aws-iot-device-sdk-python-v2/blob/master/samples/pubsub.py) 範例，並以您 [AWS IoT Core 教學課程入門](iot-gs.md) 教學課程經驗為基礎。

**Topics**
+ [訂閱萬用字元主題篩選條件](#sdk-tutorials-experiment-wild)
+ [處理主題篩選條件訂閱](#sdk-tutorials-experiment-process)
+ [從您的裝置發佈訊息](#sdk-tutorials-experiment-publish)

對於這些練習，您會從 `pubsub.py` 範例程式開始。

**注意**  
這些練習假設您已完成 [AWS IoT Core 教學課程入門](iot-gs.md) 教學課程，並使用該教學課程中的裝置終端機視窗。

### 訂閱萬用字元主題篩選條件
<a name="sdk-tutorials-experiment-wild"></a>

在本練習中，您會修改用來呼叫 `pubsub.py` 以訂閱萬用字元主題篩選條件並根據訊息主題處理所接收訊息的命令列。

#### 演練程序
<a name="sdk-tutorials-experiment-wild-steps"></a>

在此練習中，想像您的裝置包含溫度控制和光源控制。其會使用這些主題名稱來識別與其相關的訊息。

1. 開始練習之前，請試著從裝置上的 [AWS IoT Core 教學課程入門](iot-gs.md) 教學課程執行此命令，確保所有項目都準備好進行練習。

   ```
   cd ~/aws-iot-device-sdk-python-v2/samples
   python3 pubsub.py --topic topic_1 --ca_file ~/certs/Amazon-root-CA-1.pem --cert ~/certs/device.pem.crt --key ~/certs/private.pem.key --endpoint your-iot-endpoint
   ```

   您應該會看到與在[教學課程入門](connecting-to-existing-device.md#gs-device-node-app-run)中看到的相同輸出。

1. 在此練習中，請變更這些命令列參數。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/sdk-tutorials.html)

   對初始命令列進行這些變更會產生此命令列。在裝置的終端機視窗中輸入此命令。

   ```
   python3 pubsub.py --message "" --count 2 --topic device/+/details --ca_file ~/certs/Amazon-root-CA-1.pem --cert ~/certs/device.pem.crt --key ~/certs/private.pem.key --endpoint your-iot-endpoint
   ```

   程式應該會顯示如下所示的內容：

   ```
   Connecting to a3qexamplesffp-ats.iot.us-west-2.amazonaws.com with client ID 'test-24d7cdcc-cc01-458c-8488-2d05849691e1'...
   Connected!
   Subscribing to topic 'device/+/details'...
   Subscribed with QoS.AT_LEAST_ONCE
   Waiting for all messages to be received...
   ```

   若您在終端機上看到類似的內容，表示您的裝置已準備就緒，且會收聽主題名稱開頭為 `device`，結尾為 `/detail` 的訊息。所以，讓我們來進行測試。

1. 以下為您的裝置可能會收到的幾個訊息。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/sdk-tutorials.html)

1. 在 AWS IoT 主控台中使用 MQTT 測試用戶端，將上一個步驟中所述的訊息傳送到您的裝置。

   1. 在 AWS IoT 主控台中開啟 [MQTT 測試用戶端](https://console.aws.amazon.com/iot/home#/test)。

   1. 於 **Subscribe to a topic** (訂閱主題) 的 **Subscription topic** (訂閱主題) 欄位中，輸入主題篩選條件：**device/\$1/details**，再選擇 **Subscribe to topic** (訂閱主題)。

   1. 在 MQTT 測試用戶端的 **Subscriptions** (訂閱) 欄中，選擇 **device/\$1/details**。

   1. 若為上表中的每個主題，請於 MQTT 測試用戶端中執行下列動作：

      1. 於 **Publish** (發佈) 中，輸入來自表格中 **Topic name** (主題名稱) 欄的值。

      1. 在主題名稱下方的訊息承載欄位中，輸入來自表格中 **Message payload** (訊息承載) 欄的值。

      1. 觀看 `pubsub.py` 正在執行的終端機視窗，並在 MQTT 測試用戶端中，選擇 **Publish to topic** (發佈至主題)。

      您應該看到該訊息是由終端機視窗中的 `pubsub.py` 所收到。

#### 演練結果
<a name="sdk-tutorials-experiment-wild-result"></a>

有了此 `pubsub.py`，使用萬用字元主題篩選條件訂閱訊息，加以訊息，並將其顯示於終端機視窗中。請注意您如何訂閱單一主題篩選條件，並呼叫回呼函數來處理具有兩個不同主題的訊息。

### 處理主題篩選條件訂閱
<a name="sdk-tutorials-experiment-process"></a>

建置於上一個練習上，修改 `pubsub.py` 範例應用程式來評估訊息主題，並根據主題處理訂閱的訊息。

#### 演練程序
<a name="sdk-tutorials-experiment-process-steps"></a>

**如要評估訊息主題**

1. 將 `pubsub.py` 複製至 `pubsub2.py`。

1. 以您最愛的文字編輯器或 IDE 中開啟 `pubsub2.py` 檔案。

1. 在 `pubsub2.py` 中，尋找 `on_message_received` 函數。

1. 在 `on_message_received` 中，請在以 `print("Received message` 開頭的行之後及以 `global received_count` 開頭的行之前插入下列程式碼。

   ```
       topic_parsed = False
       if "/" in topic:
           parsed_topic = topic.split("/")
           if len(parsed_topic) == 3:
               # this topic has the correct format
               if (parsed_topic[0] == 'device') and (parsed_topic[2] == 'details'):
                   # this is a topic we care about, so check the 2nd element
                   if (parsed_topic[1] == 'temp'):
                       print("Received temperature request: {}".format(payload))
                       topic_parsed = True
                   if (parsed_topic[1] == 'light'):
                       print("Received light request: {}".format(payload))
                       topic_parsed = True
       if not topic_parsed:
           print("Unrecognized message topic.")
   ```

1. 儲存您的變更，並使用此命令列執行修改後的程式。

   ```
   python3 pubsub2.py --message "" --count 2 --topic device/+/details --ca_file ~/certs/Amazon-root-CA-1.pem --cert ~/certs/device.pem.crt --key ~/certs/private.pem.key --endpoint your-iot-endpoint
   ```

1. 在 AWS IoT 主控台中，開啟 [MQTT 測試用戶端](https://console.aws.amazon.com/iot/home#/test)。

1. 於 **Subscribe to a topic** (訂閱主題) 的 **Subscription topic** (訂閱主題) 欄位中，輸入主題篩選條件：**device/\$1/details**，再選擇 **Subscribe to topic** (訂閱主題)。

1. 在 MQTT 測試用戶端的 **Subscriptions** (訂閱) 欄中，選擇 **device/\$1/details**。

1. 若為此表格中的每個主題，請在 MQTT 測試用戶端中執行下列動作：    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/sdk-tutorials.html)

   1. 於 **Publish** (發佈) 中，輸入來自表格中 **Topic name** (主題名稱) 欄的值。

   1. 在主題名稱下方的訊息承載欄位中，輸入來自表格中 **Message payload** (訊息承載) 欄的值。

   1. 觀看 `pubsub.py` 正在執行的終端機視窗，並在 MQTT 測試用戶端中，選擇 **Publish to topic** (發佈至主題)。

   您應該看到該訊息是由終端機視窗中的 `pubsub.py` 所收到。

您應該會在終端機視窗中看到類似的內容。

```
Connecting to a3qexamplesffp-ats.iot.us-west-2.amazonaws.com with client ID 'test-af794be0-7542-45a0-b0af-0b0ea7474517'...
Connected!
Subscribing to topic 'device/+/details'...
Subscribed with QoS.AT_LEAST_ONCE
Waiting for all messages to be received...
Received message from topic 'device/light/details': b'{ "desiredLight": 100, "currentLight": 50 }'
Received light request: b'{ "desiredLight": 100, "currentLight": 50 }'
Received message from topic 'device/temp/details': b'{ "desiredTemp": 20, "currentTemp": 15 }'
Received temperature request: b'{ "desiredTemp": 20, "currentTemp": 15 }'
2 message(s) received.
Disconnecting...
Disconnected!
```

#### 演練結果
<a name="sdk-tutorials-experiment-process-result"></a>

在本練習中，您已新增程式碼，讓範例應用程式能辨識並處理回呼函數中的多個訊息。有了這個，您的裝置可以接收訊息並對其採取行動。

裝置接收和處理多則訊息的另一種方式是分別訂閱不同訊息，並將每個訂閱分配給各自的回呼函數。

### 從您的裝置發佈訊息
<a name="sdk-tutorials-experiment-publish"></a>

您可使用 pubsub.py 範例應用程式，以發佈來自您裝置的訊息。在其會發佈訊息時，這些訊息無法作為 JSON 文件加以讀取。本練習會修改範例應用程式，以便在可讀取的訊息承載中發佈 JSON 文件 AWS IoT Core。

#### 演練程序
<a name="sdk-tutorials-experiment-publish-steps"></a>

在本練習中，下列訊息將會與 `device/data` 主題一起傳送。

```
{
    "timestamp": 1601048303,
    "sensorId": 28,
    "sensorData": [
        {
        "sensorName": "Wind speed",
        "sensorValue": 34.2211224
        }
    ]
}
```

**如要準備 MQTT 測試用戶端監控本練習的訊息**

1. 於 **Subscribe to a topic** (訂閱主題) 的 **Subscription topic** (訂閱主題) 欄位中，輸入主題篩選條件：**device/data**，再選擇 **Subscribe to topic** (訂閱主題)。

1. 在 MQTT 測試用戶端的 **Subscriptions** (訂閱) 欄中，選擇 **device/data**。

1. 讓 MQTT 測試用戶端視窗保持開啟狀態，以等待來自您裝置的訊息。

**如要使用 pubsub.py 範例應用程式傳送 JSON 文件**

1. 在您的裝置上，將 `pubsub.py` 複製至 `pubsub3.py`。

1. Edit (編輯) `pubsub3.py`，變更其格式化所發佈訊息的方式。

   1. 在文字編輯器中開啟 `pubsub3.py`。

   1. 找出這行程式碼：

      `message = "{} [{}]".format(message_string, publish_count)`

   1. 將其變更為：

      `message = "{}".format(message_string)`

   1. 找出這行程式碼：

      `message_json = json.dumps(message)`

   1. 將其變更為：

      `message = "{}".json.dumps(json.loads(message))`

   1. 儲存您的變更。

1. 在您的裝置上，執行此命令以傳送訊息兩次。

   ```
   python3 pubsub3.py  --ca_file ~/certs/Amazon-root-CA-1.pem  --cert ~/certs/device.pem.crt  --key ~/certs/private.pem.key  --topic device/data  --count 2 --message '{"timestamp":1601048303,"sensorId":28,"sensorData":[{"sensorName":"Wind speed","sensorValue":34.2211224}]}'  --endpoint your-iot-endpoint
   ```

1. 於 MQTT 測試用戶端中，檢查是否已解譯並格式化訊息承載中的 JSON 文件，例如：  
![\[顯示如何在 AWS IoT 主控台的 MQTT 用戶端中顯示 JSON 訊息承載的影像。\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/images/mqtt-test-client-output.png)

依預設，`pubsub3.py` 也會訂閱其傳送的訊息。您您應會看到其在應用程式輸出中收到訊息。終端機視窗看起來與下列內容相似。

```
Connecting to a3qEXAMPLEsffp-ats.iot.us-west-2.amazonaws.com with client ID 'test-5cff18ae-1e92-4c38-a9d4-7b9771afc52f'...
Connected!
Subscribing to topic 'device/data'...
Subscribed with QoS.AT_LEAST_ONCE
Sending 2 message(s)
Publishing message to topic 'device/data': {"timestamp":1601048303,"sensorId":28,"sensorData":[{"sensorName":"Wind speed","sensorValue":34.2211224}]}
Received message from topic 'device/data': b'{"timestamp":1601048303,"sensorId":28,"sensorData":[{"sensorName":"Wind speed","sensorValue":34.2211224}]}'
Publishing message to topic 'device/data': {"timestamp":1601048303,"sensorId":28,"sensorData":[{"sensorName":"Wind speed","sensorValue":34.2211224}]}
Received message from topic 'device/data': b'{"timestamp":1601048303,"sensorId":28,"sensorData":[{"sensorName":"Wind speed","sensorValue":34.2211224}]}'
2 message(s) received.
Disconnecting...
Disconnected!
```

#### 演練結果
<a name="sdk-tutorials-experiment-publish-result"></a>

如此一來，您的裝置就可以產生要傳送至 的訊息 AWS IoT Core ，以測試基本連線，並提供裝置訊息 AWS IoT Core 讓 處理。例如，您可以使用此應用程式從您的裝置傳送測試資料，以測試 AWS IoT 規則動作。

## 檢視結果
<a name="sdk-tutorials-conclusion"></a>

本教學中的範例為您提供了有關裝置如何與 通訊的基本知識的實作體驗 AWS IoT Core，這是您 AWS IoT 解決方案的基本部分。當您的裝置能夠與 通訊時 AWS IoT Core，他們可以將訊息傳遞給 AWS 服務和其他可以採取行動的裝置。同樣地， AWS 服務和其他裝置可以處理導致訊息傳回裝置的資訊。

當您準備好 AWS IoT Core 進一步探索時，請嘗試這些教學課程：
+ [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)
+ [教學課程：將裝置資料儲存在 DynamoDB 表格中](iot-ddb-rule.md)
+ [教學課程：使用 AWS Lambda 函數格式化通知](iot-lambda-rule.md)

# 教學課程：使用 適用於 Embedded C 的 AWS IoT Device SDK
<a name="iot-embedded-c-sdk"></a>

本節說明如何執行 適用於 Embedded C 的 AWS IoT Device SDK。

**Topics**
+ [Step1：安裝 適用於 Embedded C 的 AWS IoT Device SDK](#install-embedded-c-sdk)
+ [步驟 2：設定範例應用程式](#iot-c-sdk-app-config)
+ [步驟 3：建置並執行範例應用程式](#iot-c-sdk-app-run)

## Step1：安裝 適用於 Embedded C 的 AWS IoT Device SDK
<a name="install-embedded-c-sdk"></a>

通常以需要最佳化 C 語言執行時間的資源限制裝置 適用於 Embedded C 的 AWS IoT Device SDK 為目標。您可以在任何作業系統上使用軟體開發套件，並將其裝載在任何處理器類型 (例如 MCU 和 MPU) 上。如果您有更多可用的記憶體和處理資源，建議您使用較高順序的裝置 AWS IoT 和行動 SDKs （例如 C\$1\$1、Java、JavaScript 和 Python)。

一般而言， 適用於 Embedded C 的 AWS IoT Device SDK 適用於使用 MCUs的系統或執行內嵌作業系統的低階 MPUs。在本節中的程式設計範例，我們假設您的裝置使用 Linux。

**Example**  

1. 從 [GitHub](https://github.com/aws/aws-iot-device-sdk-embedded-C) 將 下載 適用於 Embedded C 的 AWS IoT Device SDK 至您的裝置。

   ```
   git clone https://github.com/aws/aws-iot-device-sdk-embedded-c.git --recurse-submodules
   ```

   這會在目前目錄中建立名為 `aws-iot-device-sdk-embedded-c` 的目錄。

1. 導航至該目錄並查看最新版本。請參閱[ github.com/aws/aws-iot-device-sdk-embedded-C/tags](https://github.com/aws/aws-iot-device-sdk-embedded-C/tags)，取得最新版本標籤。

   ```
   cd aws-iot-device-sdk-embedded-c
   git checkout latest-release-tag
   ```

1. 安裝 OpenSSL 1.1.0 版或更新版本。當透過套件管理工具進行安裝時，OpenSSL 開發程式庫通常會稱為 "libssl-dev" 或 "openssl-devel"。

   ```
   sudo apt-get install libssl-dev
   ```

## 步驟 2：設定範例應用程式
<a name="iot-c-sdk-app-config"></a>

 適用於 Embedded C 的 AWS IoT Device SDK 包含供您嘗試的範例應用程式。為了簡化，本教學課程使用 `mqtt_demo_mutual_auth` 應用程式，說明如何連線至 AWS IoT Core 訊息中介裝置，以及訂閱和發佈至 MQTT 主題。

1. 將您建立於 [AWS IoT Core 教學課程入門](iot-gs.md) 中的憑證和私有金鑰複製至 `build/bin/certificates` 目錄中。
**注意**  
裝置和根憑證授權機構憑證會有過期或遭撤銷的可能。若這些憑證過期或遭到撤銷，您必須將新的憑證授權機構憑證或私有金鑰和裝置憑證複製到裝置上。

1. 您必須使用個人 AWS IoT Core 端點、私有金鑰、憑證和根 CA 憑證來設定範例。導覽至 `aws-iot-device-sdk-embedded-c/demos/mqtt/mqtt_demo_mutual_auth` 目錄。

   如果您 AWS CLI 已安裝 ，您可以使用此命令來尋找帳戶的端點 URL。

   ```
   aws iot describe-endpoint --endpoint-type iot:Data-ATS
   ```

   如果您沒有 AWS CLI 安裝 ，請開啟您的 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)。在導覽窗格中，選擇 **Manage (管理)**，然後選擇 **Things (物件)**。為您的裝置選擇 IoT 物件，然後選擇 **Interact** (互動)。您的端點會顯示在物件詳細資料頁面的 **HTTPS** 區段中。

1. 開啟 `demo_config.h` 檔案，並更新下列內容的值：  
AWS\$1IOT\$1ENDPOINT  
您的個人端點。  
CLIENT\$1CERT\$1PATH  
您的憑證檔案路徑，例如 `certificates/device.pem.crt"`。  
CLIENT\$1PRIVATE\$1KEY\$1PATH  
您的私有金鑰檔案名稱，例如`certificates/private.pem.key`。

   例如：

   ```
   // Get from demo_config.h
   // =================================================
   #define AWS_IOT_ENDPOINT               "my-endpoint-ats.iot.us-east-1.amazonaws.com"
   #define AWS_MQTT_PORT                  8883
   #define CLIENT_IDENTIFIER              "testclient"
   #define ROOT_CA_CERT_PATH              "certificates/AmazonRootCA1.crt"
   #define CLIENT_CERT_PATH               "certificates/my-device-cert.pem.crt"
   #define CLIENT_PRIVATE_KEY_PATH        "certificates/my-device-private-key.pem.key"
   // =================================================
   ```

1. 使用此命令來檢查您的裝置是否已安裝 CMake。

   ```
   cmake --version
   ```

   若您看見編譯器的版本資訊，則可繼續到下一節。

   若發生錯誤，或者沒看見資任何訊，您將需要使用此命令來安裝 cmake 套件。

   ```
   sudo apt-get install cmake
   ```

   再次執行 **cmake --version** 命令，確認已安裝 CMake，備妥繼續。

1. 使用此命令來檢查您的裝置是否已安裝開發工具。

   ```
   gcc --version
   ```

   若您看見編譯器的版本資訊，則可繼續到下一節。

   如果發生錯誤，或者沒看見編譯器資訊，您將需要使用此命令來安裝 `build-essential` 套件。

   ```
   sudo apt-get install build-essential
   ```

   再次執行 **gcc --version** 命令，確認已安裝建置工具，準備好繼續。

## 步驟 3：建置並執行範例應用程式
<a name="iot-c-sdk-app-run"></a>

此程序說明如何在您的裝置上產生`mqtt_demo_mutual_auth`應用程式，並使用 將其連接至[AWS IoT 主控台](https://console.aws.amazon.com/iot/home) 適用於 Embedded C 的 AWS IoT Device SDK。

**執行 適用於 Embedded C 的 AWS IoT Device SDK 範例應用程式**

1. 導覽至 `aws-iot-device-sdk-embedded-c`，建立一個建置目錄。

   ```
   mkdir build && cd build
   ```

1. 輸入下列 CMake 命令，產生需要建置的 Makefile。

   ```
   cmake ..  
   ```

1. 輸入下列命令來建置可執行的應用程式檔案。

   ```
   make
   ```

1. 使用此命令執行 `mqtt_demo_mutual_auth` 應用程式。

   ```
   cd bin
   ./mqtt_demo_mutual_auth
   ```

   您應該會看到類似下列的輸出：  
![\[執行 適用於 Embedded C 的 AWS IoT Device SDK 範例應用程式的命令列輸出。\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/images/successful-run2.png)

您的裝置現在已 AWS IoT 使用 連線至 適用於 Embedded C 的 AWS IoT Device SDK。

您也可以使用 AWS IoT 主控台來檢視範例應用程式正在發佈的 MQTT 訊息。如需關於如何在 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)中使用 MQTT 用戶端的資訊，請參閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)。

# 建立 AWS IoT 規則將裝置資料路由到其他 服務
<a name="iot-rules-tutorial"></a>

這些教學課程說明如何使用一些較常見的 AWS IoT 規則動作來建立和測試規則。

AWS IoT 規則會將資料從您的裝置傳送至其他 AWS 服務。其會聆聽特定的 MQTT 訊息、格式化訊息承載中的資料，並將結果傳送至其他 AWS 服務。

即使目標是要建立使用 Lambda 函數或更複雜的規則，我們仍建議您依照此處顯示的順序進行嘗試。教學課程會依照基本到複雜的順序顯示。其會逐步提供新概念，協助您學習可用來建立沒有特定教學課程的規則動作概念。

**注意**  
AWS IoT 規則可協助您將資料從 IoT 裝置傳送至其他 AWS 服務。不過，如要成功地達到這一點，您需具備要傳送資料的其他服務的相關工作知識。雖然這些教學課程提供了完成任務所需的資訊，但您可能會發現，在解決方案中使用這些服務之前，進一步了解將資料傳送至服務的相關資訊可能更有幫助。其他服務的詳細說明 AWS 超出這些教學課程的範圍。

**教學課程方案概觀**  
這些教學課程的方案為定期發佈其資料的天氣感應器裝置。在此虛構的系統中有很多這樣的感應器裝置。不過，本節中的教學課程會聚焦在單一裝置上，同時展現容納多個感應器的方法。

本節中的教學課程說明如何使用 AWS IoT 規則，透過這個假想的天氣感應器裝置系統來執行下列任務。
+ 

**[教學課程：重新發佈 MQTT 訊息](iot-repub-rule.md)**  
本教學課程將會顯示如何將氣象感應器所接收的 MQTT 訊息重新發佈為僅包含感應器 ID 和溫度值的訊息。其僅使用 AWS IoT Core 服務，並展示簡單的 SQL 查詢，及如何使用 MQTT 用戶端來測試您的規則。
+ 

**[教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)**  
本教學課程會顯示天氣感應器裝置的值超出特定值時，如何傳送 SNS 訊息。它以先前教學課程中呈現的概念為基礎，並新增了如何使用其他服務 AWS ：[Amazon Simple Notification Service](https://docs.aws.amazon.com//sns/latest/dg/welcome.html) (Amazon SNS)。

  若您初次使用 Amazon SNS，請檢閱其[入門](https://docs.aws.amazon.com//sns/latest/dg/sns-getting-started.html)練習，然後再開始本教學課程。
+ 

**[教學課程：將裝置資料儲存在 DynamoDB 表格中](iot-ddb-rule.md)**  
本教學課程會顯示如何從天氣感應器裝置存放資料庫表中的資料。其會使用規則查詢陳述式和替代範本來格式化目的地服務[Amazon DynamoDB](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/Introduction.html) 的訊息資料。

  若您初次使用 DynamoDB，請檢閱其[入門](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html)練習，然後再開始本教學課程。
+ 

**[教學課程：使用 AWS Lambda 函數格式化通知](iot-lambda-rule.md)**  
本教學課程顯示如何呼叫 Lambda 函數來重新格式化裝置資料，然後以文字訊息進行傳送。它會在 [AWS Lambda](https://docs.aws.amazon.com//lambda/latest/dg/welcome.html)函數中新增 Python 指令碼和 AWS SDK 函數，以格式化來自天氣感應器裝置的訊息承載資料，並傳送文字訊息。

  若您初次使用 Lambda，請檢閱其[入門](https://docs.aws.amazon.com//lambda/latest/dg/getting-started.html)練習，然後再開始本教學課程。

**AWS IoT 規則概觀**  
所有這些教學課程都會建立 AWS IoT 規則。

若要讓 AWS IoT 規則將資料從裝置傳送至其他服務 AWS ，它會使用：


+ 包含下列項目的規則查詢陳述式：
  + SQL SELECT 子句，用於選取並格式化訊息承載中的資料
  + 主題篩選條件 (規則查詢陳述式中的 FROM 物件)，用來識別要使用的訊息
  + 選用性的條件陳述式 (SQL WHERE 子句)，指定要採取行動的特定條件
+ 至少一個規則動作

裝置會發佈訊息至 MQTT 主題。SQL SELECT 陳述式中的主題篩選條件會識別要套用規則的 MQTT 主題。SQL SELECT 陳述式中指定的欄位會格式化傳入 MQTT 訊息承載的資料，以供規則動作使用。如需規則動作的完整清單，請參閱 [AWS IoT 規則動作](iot-rule-actions.md)。

**Topics**
+ [教學課程：重新發佈 MQTT 訊息](iot-repub-rule.md)
+ [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)
+ [教學課程：將裝置資料儲存在 DynamoDB 表格中](iot-ddb-rule.md)
+ [教學課程：使用 AWS Lambda 函數格式化通知](iot-lambda-rule.md)

# 教學課程：重新發佈 MQTT 訊息
<a name="iot-repub-rule"></a>

本教學課程示範如何建立 AWS IoT 規則，在接收到指定的 MQTT 訊息時發佈 MQTT 訊息。傳入的訊息承載可在發佈前由規則進行修改。這樣就可以建立專為特定應用程式量身訂製的訊息，而不需要更改裝置或其韌體。您還可使用規則的篩選條件面向，僅在符合特定條件時才發佈訊息。

規則重新發佈的訊息就像由任何其他 AWS IoT 裝置或用戶端傳送的訊息。裝置可訂閱重新發佈的訊息，就像裝置可訂閱任何其他 MQTT 訊息主題一樣。

**您會在本教學課程中學到什麼：**
+ 如何在規則查詢陳述式中使用簡單的 SQL 查詢和函數
+ 如何使用 MQTT 用戶端測試 AWS IoT 規則

此教學課程約需 30 分鐘方能完成。

**Topics**
+ [檢閱 MQTT 主題和 AWS IoT 規則](#iot-repub-rule-mqtt)
+ [步驟 1：建立 AWS IoT 規則以重新發佈 MQTT 訊息](#iot-repub-rule-define)
+ [步驟 2：測試新規則](#iot-repub-rule-test)
+ [步驟 3：檢閱結果及後續步驟](#iot-repub-rule-review)

**開始本教學課程之前，請確定您有：**
+ 

**[設定 AWS 帳戶](setting-up.md)**  
您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。
+ 

**檢閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)**  
請確定您可使用 MQTT 用戶端來訂閱並發佈至主題。您會使用 MQTT 用戶端，在此程序中測試您的新規則。

## 檢閱 MQTT 主題和 AWS IoT 規則
<a name="iot-repub-rule-mqtt"></a>

在討論 AWS IoT 規則之前，了解 MQTT 通訊協定會有幫助。在 IoT 解決方案中，MQTT 通訊協定提供了一些優於其他網路通訊協定 (如 HTTP) 的優勢，這使其成為 IoT 裝置使用的熱門選擇。本節將會檢視 MQTT 適用於本教學課程的主要層面。如需 MQTT 與 HTTP 比較的資訊，請參閱 [選擇裝置通訊的應用程式通訊協定](protocols.md#protocol-selection)。

**MQTT 通訊協定**  
MQTT 通訊協定會與其主機一起使用發佈/訂閱通訊模型。若要傳送資料，裝置會將主題識別的訊息發佈至 AWS IoT 訊息中介裝置。若要收到訊息代理程式的訊息，裝置會在訂閱請求中傳送主題篩選條件給訊息代理程式，以訂閱其會收到的主題。 AWS IoT 規則引擎會收到來自訊息代理程式的 MQTT 訊息。

**AWS IoT 規則**  
AWS IoT 規則包含規則查詢陳述式和一或多個規則動作。當 AWS IoT 規則引擎收到 MQTT 訊息時，這些元素對訊息產生的作用如下。
+ 

**規則查詢陳述式**  
規則的查詢陳述式說明要使用的 MQTT 主題、解譯來自訊息承載的資料，及依據 SQL 陳述式所描述的 SQL 陳述式來格式化資料，這些陳述式類似於常用 SQL 資料庫所使用的陳述式。查詢陳述式的結果為傳送至規則動作的資料。
+ 

**規則動作**  
規則中的每個規則動作都會作用於規則查詢陳述式所產生的資料。 AWS IoT 支援[許多規則動作](iot-rule-actions.md)。不過，在本教學課程中，您會專注於 [重新發佈](republish-rule-action.md) 規則動作，其會將查詢陳述式的結果發佈為具有特定主題的 MQTT 訊息。

## 步驟 1：建立 AWS IoT 規則以重新發佈 MQTT 訊息
<a name="iot-repub-rule-define"></a>

您將在本教學課程中建立的 AWS IoT 規則會訂閱 `device/device_id/data` MQTT 主題，其中 *device\$1id* 是傳送訊息的裝置 ID。[主題篩選條件](topics.md#topicfilters)會將這些主題描述為 `device/+/data`，其中`+` 為與兩個正斜線字元間之任何字串相符的萬用字元。

規則收到來自相符主題的訊息時，其會重新發佈 `device_id` 和 `temperature` 值，作為具 `device/data/temp` 主題的新 MQTT 訊息。

例如，具 `device/22/data` 主題的 MQTT 訊息承載如下所示：

```
{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}
```

該規則採用來自訊息承載的 `temperature` 值及來自主題的 `device_id`，並將其重新發佈為具 `device/data/temp` 主題的 MQTT 訊息，及如下所示的訊息承載：

```
{
  "device_id": "22",
  "temperature": 28
}
```

使用此規則，則只需要裝置 ID 和溫度資料的裝置會訂閱 `device/data/temp` 主題，以僅接收該資訊。

**如要建立一個重新發佈 MQTT 訊息的規則**

1. 開啟 [AWS IoT 主控台的**規則**中樞](https://console.aws.amazon.com//iot/home#/rulehub)。

1. 在 **Rules** (規則) 中，選擇**Create**(建立)，然後開始建立新規則。

1. 在 **Create a rule** (建立規則) 的頂部中：

   1. 在 **Name** (名稱) 中，輸入規則的名稱。在本教學課程中，請將其命名為 **republish\$1temp**。

      請記住，規則名稱在您的帳戶和區域內必須是唯一的，且不能有任何空格。我們在此名稱中使用底線字元來分隔規則名稱中的兩個單字。

   1.  在 **Description** (說明) 中，說明規則。

      有意義的說明可幫助您記住此規則的作用及建立規則的原因。說明可依所需而定，因此請盡可能詳細說明。

1. 在 **Create a rule** (建立規則) 的 **Rule query statement** (規則查詢陳述式) 中：

   1.  在**使用 SQL 版本**中，選取 **2016-03-23**。

   1. 在 **Rule query statement** (規則查詢陳述式) 編輯方塊中輸入陳述式：

      ```
      SELECT topic(2) as device_id, temperature FROM 'device/+/data'
      ```

      本陳述式：
      + 聆聽具與 `device/+/data` 主題篩選條件相符之主題的 MQTT 訊息。
      + 從主題字串中選取第二個元素，並將其指定給 `device_id` 欄位。
      + 從訊息承載選取值 `temperature` 欄位，並將其指派給 `temperature` 欄位。

1. 在 **Set one or more actions** (設定一個或多個動作) 中：

   1. 若要開啟此規則的規則動作清單，請選擇 **Add action** (新增動作)。

   1. 在**選取動作**中，選擇**重新發佈訊息至 AWS IoT 主題**。

   1. 在動作清單底部，選擇**設定動作**以開啟所選動作的組態頁面。

1. 在 **Configure action** (設定動作)：

   1.  在 **Topic** (主題) 中，輸入 **device/data/temp**。這是此規則將發佈訊息的 MQTT 主題。

   1.  在**Quality of Service** (服務品質) 中，選擇 **0 - The message is delivered zero or more times** (0 - 訊息傳遞零次或多次)。

   1.  在**選擇或建立角色以授予執行此動作的 AWS IoT 存取權**：

      1.  選擇 **Create Role** (建立角色)。**Create a new role** (建立新角色) 對話方塊隨即開啟。

      1. 輸入可說明新角色的名稱。在本教學課程中，使用 **republish\$1role**。

         當您建立新角色時，會建立執行規則動作的正確政策，並將其連接至新角色。如果您變更此規則動作的主題，或在其他規則動作中使用此角色，則必須更新該角色的政策，以授權新的主題或動作。若要更新現有角色，請選擇本節中的 **Update role** (更新角色)。

      1. 選擇**Create Role** (建立角色)，以建立角色並關閉對話方塊。

   1. 選擇**Add action** (新增動作)，將動作新增至規則，並返回**Create a rule** (建立規則) 頁面。

1. 將**訊息重新發佈至 AWS IoT 主題**動作現在列在**設定一或多個動作**中。

   在新動作的圖標中，**Republish a message to an AWS IoT topic (將訊息重新發佈至 IoT 主題)** 之下，您可看到重新發佈動作將發佈的主題。

   這是您將新增至此規則的唯一規則動作。

1. 在 **Create a rule** (建立規則) 中，向下捲動至底部，然後選擇 **Create rule** (建立規則)，建立規則並完成此步驟。

## 步驟 2：測試新規則
<a name="iot-repub-rule-test"></a>

若要測試新規則，您將使用 MQTT 用戶端來發佈和訂閱此規則所使用的 MQTT 訊息。

在新視窗的 [AWS IoT 主控台中開啟 MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test)。這可讓您編輯規則，而不會遺失 MQTT 用戶端的組態。如果您讓它轉到主控台中的另一個頁面，MQTT 用戶端不會保留任何訂閱或訊息記錄。

**如要使用 MQTT 用戶端來測試您的規則。**

1. 在 AWS IoT 主控台的 [MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test) 中，訂閱輸入主題，在此案例中為 `device/+/data`。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Subscribe to a topic** (訂閱主題)。

   1. 在 **Subscription topic** (訂閱主題) 中，輸入輸入主題篩選條件 **device/\$1/data** 的主題 。

   1. 將剩下的欄位保留為其預設設定。

   1. 請選擇 **Subscribe to topic** (訂閱主題)。

      在 **Subscriptions** (訂閱) 欄中，**Publish to a topic** (發佈到主題) 之下，**device/\$1/data** 隨即顯示。

1. 訂閱規則將發佈的主題：`device/data/temp`。

   1. 在 **Subscriptions** (訂閱) 之下，選擇 **Subscribe to a topic** (訂閱主題)，並在 **Subscription topic** (訂閱主題) 中，輸入重新發佈訊息 **device/data/temp** 的主題。

   1. 將剩下的欄位保留為其預設設定。

   1. 請選擇 **Subscribe to topic** (訂閱主題)。

      在 **Subscriptions** (訂閱) 欄中，**device/\$1/data** 之下，**device/data/temp** 隨即顯示。

1. 使用特定裝置 ID **device/22/data**，將訊息發佈至輸入主題。您無法發佈至包含萬用字元的 MQTT 主題。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Publish to topic** (發佈至主題)。

   1. 在 **Publish** (發佈) 欄位中輸入輸入主題名稱 **device/22/data**。

   1. 複製此處顯示的範例資料，並在主題名稱下方的編輯方塊中貼上範例資料。

      ```
      {
        "temperature": 28,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 若要傳送 MQTT 訊息，請選擇 **Publish to topic** (發佈至主題)。

1. 檢閱傳送的訊息。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下，您先前訂閱的兩個主題旁會有一個綠點。

      該綠點表示自上次查看後，已收到一個或多個新訊息。

   1. 於 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，來檢查訊息承載是否與您剛剛發佈的內容相符，如下所示：

      ```
      {
        "temperature": 28,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 於 **Subscriptions** (訂閱) 下，選擇 **device/data/temp**，來檢查您重新發佈的訊息承載，如下所示：

      ```
      {
        "device_id": "22",  
        "temperature": 28
      }
      ```

      請注意，`device_id` 值是個帶有引號的字串，而 `temperature` 值是個數字。這是因為 [https://docs.aws.amazon.com//iot/latest/developerguide/iot-sql-functions.html#iot-function-topic](https://docs.aws.amazon.com//iot/latest/developerguide/iot-sql-functions.html#iot-function-topic) 函數從輸入訊息的主題名稱中提取字串，而 `temperature` 值會使用輸入訊息承載的數值。

      若要將 `device_id` 值設為數值，請將規則查詢陳述式中的 `topic(2)` 替換為：

      ```
      cast(topic(2) AS DECIMAL)
      ```

      請注意，將 `topic(2)` 值轉換為數值，僅適用於主題的該部分僅包含數字字元時。

1. 若您看到正確的訊息已發佈至 **device/data/temp** 主題，則您的規則有效。請參閱下一節，了解更多 Republish (重新發佈) 規則動作的相關資訊。

   若您看不到正確的訊息已發佈至 **device/\$1/data** 或 **device/data/temp** 主題中，請查看疑難排解提示。

### 疑難排解重新發佈訊息規則
<a name="iot-repub-rule-trouble"></a>

若您並未看到預期的結果，請查看以下事項。
+ 

**您收到錯誤的橫幅**  
若在您發佈輸入訊息時出現錯誤，請先更正該錯誤。下列步驟可協助您修正該錯誤。
+ 

**您並未在 MQTT 用戶端中看到輸入訊息**  
每次您將輸入訊息發佈至 `device/22/data` 主題時，若您依程序中所述訂閱了 `device/+/data` 主題篩選條件，則該訊息應會顯示於 MQTT 用戶端中。

**要檢查的事項**
  + 

**檢查您訂閱的主題篩選條件**  
若您依程序中所述訂閱了輸入訊息主題，則每次發佈輸入訊息時都應該會看到其複本。

    若您並未訊息，請檢查您訂閱的主題名稱，並將其與所發佈的主題進行比較。主題名稱區分大小寫，且您訂閱的主題必須與所發佈訊息承載的主題相同。
  + 

**檢查訊息發佈功能**  
在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，檢查發佈訊息的主題，然後選擇 **Publish to topic** (發佈至主題)。您應該會在訊息清單中出現主題下方的編輯方塊中看到訊息承載。
+ 

**您在 MQTT 用戶端中並未看到已重新發佈的訊息**  
若要讓您的規則運作，其必須具有授權其接收和重新發佈訊息的正確政策，且必須接收訊息。

**要檢查的事項**
  + 

**檢查 MQTT 用戶端 AWS 區域 的 和您建立的規則**  
您正在執行 MQTT 用戶端的主控台必須與您建立的規則處於相同的 AWS 區域。
  + 

**檢查規則查詢陳述式中的輸入訊息主題**  
若要讓規則運作，其必須收到一則訊息，其主題名稱與規則查詢陳述式之 FROM 子句中的主題篩選條件相符。

    檢查規則查詢陳述式中主題篩選條件的拼字與 MQTT 用戶端中主題的拼字。主題名稱區分大小寫，且郵件的主題必須與規則查詢陳述式中的主題篩選條件相符。
  + 

**檢查輸入訊息承載的內容**  
若要讓規則運作，其必須在 SELECT 陳述式中宣告的訊息承載中尋找資料欄位。

    檢查規則查詢陳述式中 `temperature` 欄位的拼字與 MQTT 用戶端中訊息承載的拼字。欄位名稱區分大小寫，規則查詢陳述式中的 `temperature` 欄位必須與訊息承載中的 `temperature` 欄位相符。

    請確定訊息承載中的 JSON 文件格式正確。若 JSON 有任何錯誤，例如缺少逗號，則規則將無法進行讀取。
  + 

**檢查規則動作中重新發佈的訊息主題**  
Republish (重新發佈) 規則動作發佈新訊息的主題必須與您在 MQTT 用戶端中訂閱的主題相符。

    開啟您建立於主控台中的規則，並檢查規則動作重新發佈訊息的主題。
  + 

**檢查規則所使用的角色**  
規則動作必須具有接收原始主題及發佈新主題的權限。

    授權規則接收訊息資料的政策並加以重新發佈為所使用的主題所特定的。若變更用於重新發佈訊息資料的主題，則必須更新規則動作的角色，來更新其政策以與目前主題相符。

    若您懷疑這會是問題，請編輯 Republish (重新發佈) 規則動作並建立新角色。規則動作建立的新角色會收到執行這些動作所需的授權。

## 步驟 3：檢閱結果及後續步驟
<a name="iot-repub-rule-review"></a>

**於本教學課程中**
+ 您使用了簡單的 SQL 查詢和規則查詢陳述式中的幾個函數來產生一個新的 MQTT 訊息。
+ 您已建立一個重新發佈該新訊息的規則。
+ 您使用 MQTT 用戶端來測試 AWS IoT 規則。

**後續步驟**  
使用此規則重新發佈一些訊息之後，請嘗試使用其來查看教學課程的某些層面如何影響重新發佈的訊息。此處有幾種簡單的入門方式。
+ 變更輸入訊息主題中的 *device\$1id*，並觀察重新發佈之訊息承載中的影響。
+ 變更規則查詢陳述式中所選取的欄位，並觀察重新發佈之訊息承載中的影響。
+ 請嘗試本系列中的下一個教學課程，並了解如何進行 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)。

此教學課程中所使用的 Republish (重新發佈) 規則動作亦可協助您對規則查詢陳述式進行偵錯。例如，您可將此動作新增至規則，以查看其規則查詢陳述式如何格式化用於其規則動作的資料。

# 教學課程：傳送 Amazon SNS 通知
<a name="iot-sns-rule"></a>

本教學課程示範如何建立 AWS IoT 規則，將 MQTT 訊息資料傳送至 Amazon SNS 主題，以做為 SMS 文字訊息傳送。

在本教學課程中，您會建立一個規則，在溫度超過規則中所設定的值時，將訊息資料從天氣感應器傳送至 Amazon SNS 主題的所有訂閱者。該規則會在回報的溫度超過規則設定值時進行偵測，然後建立新的訊息承載資料，其中僅包含裝置 ID、回報的溫度及超過的溫度限制。該規則會將新訊息承載作為 JSON 文件傳送至 SNS 主題，其會通知 SNS 主題的所有訂閱者。

**您會在本教學課程中學到什麼：**
+ 如何建立及測試 Amazon SNS 通知
+ 如何從 AWS IoT 規則呼叫 Amazon SNS 通知
+ 如何在規則查詢陳述式中使用簡單的 SQL 查詢和函數
+ 如何使用 MQTT 用戶端測試 AWS IoT 規則

此教學課程約需 30 分鐘方能完成。

**Topics**
+ [步驟 1：建立傳送簡訊的 Amazon SNS 主題](#iot-sns-rule-create-sns-topic)
+ [步驟 2：建立 AWS IoT 規則以傳送文字訊息](#iot-sns-rule-create-rule)
+ [步驟 3：測試 AWS IoT 規則和 Amazon SNS 通知](#iot-sns-rule-test-rule)
+ [步驟 4：檢閱結果及後續步驟](#iot-sns-rule-review-results)

**開始本教學課程之前，請確定您有：**
+ 

**[設定 AWS 帳戶](setting-up.md)**  
您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。
+ 

**檢閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)**  
請確定您可使用 MQTT 用戶端來訂閱並發佈至主題。您會使用 MQTT 用戶端，在此程序中測試您的新規則。
+ 

**檢閱 [Amazon Simple Notification Service](https://docs.aws.amazon.com//sns/latest/dg/welcome.html)**  
若您未曾使用過 Amazon SNS，請查閱[設定 Amazon SNS 的存取](https://docs.aws.amazon.com//sns/latest/dg/sns-setting-up.html)。如果您已完成其他 AWS IoT 教學課程，則應該已正確設定您的 AWS 帳戶 。

## 步驟 1：建立傳送簡訊的 Amazon SNS 主題
<a name="iot-sns-rule-create-sns-topic"></a>

此程序說明如何建立天氣感應器可以傳送訊息資料的 Amazon SNS 主題。然後，Amazon SNS 主題會透過簡訊通知所有訂閱者超過溫度限制。

**若要建立一個傳送 SMS 簡訊的 Amazon SNS 主題**

1. **建立一個 Amazon SNS 主題。**

   1. 登入 [Amazon SNS 主控台](https://console.aws.amazon.com//sns/home)。

   1. 在左側導覽窗格中，選擇 **Topics** (主題)。

   1. 在 **Topics** (主題) 頁面上，選擇 **Create new topic** (建立新主題)。

   1. 於 **Details** (詳細資訊) 中，選擇 **Standard** (標準) 類型。依預設，主控台會建立一個 FIFO 主題。

   1. 於 **Name** (名稱) 中，輸入 SNS 主題名稱。針對本教學，輸入 **high\$1temp\$1notice**。

   1. 向下捲動到頁面底部，並選擇 **Create topic** (建立主題)。

      主控台會開啟新主題的 **Details** (詳細資訊) 頁面。

1. **建立 Amazon SNS 訂閱。**
**注意**  
您在此訂閱中使用的電話號碼可能會因您將在本教學課程中傳送訊息而產生簡訊費用。

   1. 於 **high\$1temp\$1notice** 主題詳細資訊頁面中，選擇 **Create subscription** (建立訂閱)。

   1. 於**Create subscription** (建立訂閱) 的 **Details** (詳細資訊) 區段下，在 **Protocol** (通訊協定) 清單中選擇 **SMS**。

   1. 於 **Endpoint** (端點) 中，輸入可接收簡訊的電話號碼。請務必將其輸入，使其以 `+` 開頭，包含國碼和地區碼，且不包含任何其他標點符號字元。

   1. 選擇**建立訂閱**。

1. **測試 Amazon SNS 通知。**

   1. 於 [Amazon SNS 主控台](https://console.aws.amazon.com//sns/home)的左側導覽窗格中，選擇 **Topics** (主題)。

   1. 若要開啟主題的詳細資料頁面，請於 **Topics** (主題) 的主題清單中，選擇 **high\$1temp\$1notice**。

   1. 如要開啟 **Publish message to topic** (將訊息發佈至主題) 頁面，請於 **high\$1temp\$1notice** 詳細資訊頁面中，選擇 **Publish message** (發佈訊息)。

   1. 在 **Publish message to topic** (將訊息發佈至主題) 的 **Message body** (訊息內文) 區段下，在 **Message body to send to the endpoint** (要傳送至端點的訊息內文) 中，輸入簡短訊息。

   1. 捲動到頁面底部，並選擇 **Publish message** (發佈訊息)。

   1. 在您先前建立訂閱時所使用的手機號碼上，確認已收到訊息。

   若您並未收到測試訊息，請再次檢查電話號碼及手機設定。

   請確保您可從 [Amazon SNS 主控台](https://console.aws.amazon.com//sns/home) 發佈測試訊息，然後再繼續教學課程。

## 步驟 2：建立 AWS IoT 規則以傳送文字訊息
<a name="iot-sns-rule-create-rule"></a>

您將在本教學課程中建立的 AWS IoT 規則會訂閱 `device/device_id/data` MQTT 主題，其中 `device_id`是傳送訊息的裝置 ID。主題篩選條件會將這些主題描述為 `device/+/data`，其中 `+` 為與兩個正斜線字元間之任何字串相符的萬用字元。此規則也會測試訊息裝載中的 `temperature` 欄位值。

當規則收到來自相符主題的訊息時，其會採用來自主題名稱的 `device_id`、來自訊息承載的 `temperature` 值、及為其測試的限制新增一個常數值，並將這些值做為 JSON 文件傳送至 Amazon SNS 通知主題。

 例如，來自天氣感應器裝置編號 32 的 MQTT 訊息會使用 `device/32/data` 主題，並具有看起來如下的訊息承載：

```
{
  "temperature": 38,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}
```

規則的規則查詢陳述式採用訊息承載的 `temperature` 值、主題名稱的 `device_id`，並新增常數 `max_temperature` 值，以將看起來像這樣的訊息承載傳送至 Amazon SNS 主題：

```
{
  "device_id": "32",
  "reported_temperature": 38,
  "max_temperature": 30
}
```

**建立 AWS IoT 規則以偵測超額溫度值，並建立要傳送至 Amazon SNS 主題的資料**

1. 開啟 [AWS IoT 主控台的**規則**中樞](https://console.aws.amazon.com//iot/home#/rulehub)。

1. 若此為您的第一個規則，請選擇 **Create** (建立) 或 **Create a rule** (建立規則)。

1. 在 **Create a rule** (建立規則) 中：

   1. 在 **Name** (名稱) 中，輸入 **temp\$1limit\$1notify**。

      請記住，規則名稱在您的 AWS 帳戶 和 區域中必須是唯一的，而且不能有任何空格。我們在此名稱中使用底線字元來分隔規則名稱中的字詞。

   1. 在 **Description** (說明) 中，說明規則。

      有意義的說明可讓您更容易記住此規則的作用及您建立規則的原因。說明可依所需而定，因此請盡可能詳細說明。

1. 在 **Create a rule** (建立規則) 的 **Rule query statement** (規則查詢陳述式) 中：

   1.  在**使用 SQL 版本**中，選取 **2016-03-23**。

   1. 在 **Rule query statement** (規則查詢陳述式) 編輯方塊中輸入陳述式：

      ```
      SELECT topic(2) as device_id, 
          temperature as reported_temperature, 
          30 as max_temperature 
        FROM 'device/+/data' 
        WHERE temperature > 30
      ```

      本陳述式：
      + 聆聽主題與 `device/+/data` 主題篩選條件相符及 `temperature` 值大於 30 的 MQTT 訊息。
      + 從主題字串中選取第二個元素，並將其指定給 `device_id` 欄位。
      + 從訊息承載選取值 `temperature` 欄位，並將其指派給 `reported_temperature` 欄位。
      + 建立常數值 `30` 來表示限制值，並將其指定給 `max_temperature` 欄位。

1. 若要開啟此規則的規則動作清單，請於 **Set one or more actions** (設定一個或多個動作) 中，選擇 **Add action** (新增動作)。

1. 在 **Select an action** (選取動作) 中，選擇 **Send a message as an SNS push notification** (傳送 SNS 推送通知形式的訊息)。

1. 若要開啟所選取動作的組態頁面，請在動作清單底部選擇 **Configure action** (設定動作)。

1. 於 **Configure action** (設定動作)：

   1. 於 **SNS target** (SNS 目標) 中，選擇 **Select** (選取)，找出您名為 **high\$1temp\$1notice** 的 SNS 主題，然後選擇 **Select** (選取)。

   1. 在 **Message format** (訊息格式) 中，選擇 **RAW**。

   1. 在**選擇或建立角色以授予執行此動作的 AWS IoT 存取權**中，選擇**建立角色**。

   1. 在 **Create a new role** (建立新角色) 的 **Name** (名稱) 中，輸入新角色的唯一名稱。在本教學課程中，使用 **sns\$1rule\$1role**。

   1. 選擇 **Create Role** (建立角色)。

   若您要重複此教學課程或重複使用現有的角色，請先選擇 **Update role** (更新角色)，再繼續進行。此會更新角色的政策文件，以使用 SNS 目標。

1. 選擇 **Add action** (新增動作)，並返回 **Create a rule** (建立規則) 頁面。

   在新動作的圖標中，**Send a message as an SNS push notification** (以 SNS 推送通知形式傳送訊息)，您可看到規則呼叫的 SNS 主題。

   這是您將新增至此規則的唯一規則動作。

1. 如要建立並完成此步驟，請於 **Create a rule** (建立規則) 中，向下捲動至底部，然後選擇 **Create rule** (建立規則)。

## 步驟 3：測試 AWS IoT 規則和 Amazon SNS 通知
<a name="iot-sns-rule-test-rule"></a>

若要測試新規則，您將使用 MQTT 用戶端來發佈和訂閱此規則所使用的 MQTT 訊息。

在新視窗的 [AWS IoT 主控台中開啟 MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test)。這可讓您編輯規則，而不會遺失 MQTT 用戶端的組態。如果您讓 MQTT 用戶端前往主控台中的另一個頁面，其不會保留任何訂閱或訊息記錄。

**如要使用 MQTT 用戶端來測試您的規則。**

1. 在 AWS IoT 主控台的 [MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test) 中，訂閱輸入主題，在此案例中為 `device/+/data`。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Subscribe to a topic** (訂閱主題)。

   1. 在 **Subscription topic** (訂閱主題) 中，輸入輸入主題篩選條件 **device/\$1/data** 的主題 。

   1. 將剩下的欄位保留為其預設設定。

   1. 請選擇 **Subscribe to topic** (訂閱主題)。

      在 **Subscriptions** (訂閱) 欄中，**Publish to a topic** (發佈到主題) 之下，**device/\$1/data** 隨即顯示。

1. 使用特定裝置 ID **device/32/data**，將訊息發佈至輸入主題。您無法發佈至包含萬用字元的 MQTT 主題。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Publish to topic** (發佈至主題)。

   1. 在 **Publish** (發佈) 欄位中輸入輸入主題名稱 **device/32/data**。

   1. 複製此處顯示的範例資料，並在主題名稱下方的編輯方塊中貼上範例資料。

      ```
      {
        "temperature": 38,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 選擇 **Publish to topic** (發佈至主題)，發佈您的 MQTT 訊息。

1. 確認簡訊已傳送。

   1. 在 MQTT 用戶端中 **Subscriptions** (訂閱) 下，您先前訂閱的主題旁會有一個綠點。

      該綠點表示自上次查看後，已收到一個或多個新訊息。

   1. 於 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，來檢查訊息承載是否與您剛剛發佈的內容相符，如下所示：

      ```
      {
        "temperature": 38,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 檢查您用來訂閱 SNS 主題的手機，並確認訊息承載內容如下所示：

      ```
      {"device_id":"32","reported_temperature":38,"max_temperature":30}
      ```

      請注意，`device_id` 值是個帶有引號的字串，而 `temperature` 值是個數字。這是因為 [https://docs.aws.amazon.com//iot/latest/developerguide/iot-sql-functions.html#iot-function-topic](https://docs.aws.amazon.com//iot/latest/developerguide/iot-sql-functions.html#iot-function-topic) 函數從輸入訊息的主題名稱中提取字串，而 `temperature` 值會使用輸入訊息承載的數值。

      若要將 `device_id` 值設為數值，請將規則查詢陳述式中的 `topic(2)` 替換為：

      ```
      cast(topic(2) AS DECIMAL)
      ```

      請注意，將 `topic(2)` 值轉換為數值，`DECIMAL` 值僅適用於該部分主題僅包含數字字元時。

1. 請嘗試傳送溫度不超過限制的 MQTT 訊息。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Publish to topic** (發佈至主題)。

   1. 在 **Publish** (發佈) 欄位中輸入輸入主題名稱 **device/33/data**。

   1. 複製此處顯示的範例資料，並在主題名稱下方的編輯方塊中貼上範例資料。

      ```
      {
        "temperature": 28,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 若要傳送 MQTT 訊息，請選擇 **Publish to topic** (發佈至主題)。

   您應該能看到您在 **device/\$1/data** 訂閱中傳送的訊息。不過，因溫度值低於規則查詢陳述式中的最高溫度，所以您不應收到簡訊。

   若您並未看到正確的行為，請查看疑難排解提示。

### 疑難排解 SNS 訊息規則
<a name="iot-sns-rule-trouble"></a>

若您並未看到預期的結果，請查看以下事項。
+ 

**您收到錯誤的橫幅**  
若在您發佈輸入訊息時出現錯誤，請先更正該錯誤。下列步驟可協助您修正該錯誤。
+ 

**您並未在 MQTT 用戶端中看到輸入訊息**  
每次您將輸入訊息發佈至 `device/22/data` 主題中，若您依程序中所述訂閱了 `device/+/data` 主題篩選條件，則該訊息應會顯示於 MQTT 用戶端中。

**要檢查的事項**
  + 

**檢查您訂閱的主題篩選條件**  
若您依程序中所述訂閱了輸入訊息主題，則每次發佈輸入訊息時都應該會看到其複本。

    若您並未訊息，請檢查您訂閱的主題名稱，並將其與所發佈的主題進行比較。主題名稱區分大小寫，且您訂閱的主題必須與所發佈訊息承載的主題相同。
  + 

**檢查訊息發佈功能**  
在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，檢查發佈訊息的主題，然後選擇 **Publish to topic** (發佈至主題)。您應該會在訊息清單中出現主題下方的編輯方塊中看到訊息承載。
+ 

**您並未收到 SMS 訊息：**  
若要讓您的規則運作，其必須具有授權其接收訊息和傳送 SNS 通知的正確政策，且必須接收訊息。

**要檢查的事項**
  + 

**檢查 MQTT 用戶端 AWS 區域 的 和您建立的規則**  
您正在執行 MQTT 用戶端的主控台必須與您建立的規則處於相同的 AWS 區域。
  + 

**檢查訊息承載中的溫度值是否超過測試閾值**  
若溫度值小於或等於 30 (如規則查詢陳述式中所定義)，則規則將不會執行其任何動作。
  + 

**檢查規則查詢陳述式中的輸入訊息主題**  
若要讓規則運作，其必須收到一則訊息，其主題名稱與規則查詢陳述式之 FROM 子句中的主題篩選條件相符。

    檢查規則查詢陳述式中主題篩選條件的拼字與 MQTT 用戶端中主題的拼字。主題名稱區分大小寫，且郵件的主題必須與規則查詢陳述式中的主題篩選條件相符。
  + 

**檢查輸入訊息承載的內容**  
若要讓規則運作，其必須在 SELECT 陳述式中宣告的訊息承載中尋找資料欄位。

    檢查規則查詢陳述式中 `temperature` 欄位的拼字與 MQTT 用戶端中訊息承載的拼字。欄位名稱區分大小寫，規則查詢陳述式中的 `temperature` 欄位必須與訊息承載中的 `temperature` 欄位相符。

    請確定訊息承載中的 JSON 文件格式正確。若 JSON 有任何錯誤，例如缺少逗號，則規則將無法進行讀取。
  + 

**檢查規則動作中重新發佈的訊息主題**  
Republish (重新發佈) 規則動作發佈新訊息的主題必須與您在 MQTT 用戶端中訂閱的主題相符。

    開啟您建立於主控台中的規則，並檢查規則動作重新發佈訊息的主題。
  + 

**檢查規則所使用的角色**  
規則動作必須具有接收原始主題及發佈新主題的權限。

    授權規則接收訊息資料的政策並加以重新發佈為所使用的主題所特定的。若變更用於重新發佈訊息資料的主題，則必須更新規則動作的角色，來更新其政策以與目前主題相符。

    若您懷疑這會是問題，請編輯 Republish (重新發佈) 規則動作並建立新角色。規則動作建立的新角色會收到執行這些動作所需的授權。

## 步驟 4：檢閱結果及後續步驟
<a name="iot-sns-rule-review-results"></a>

**於本教學課程中：**
+ 您已建立並測試了 Amazon SNS 通知主題和訂閱。
+ 您在規則查詢陳述式中使用了一個簡單的 SQL 查詢和函數，可為您的通知建立一個新訊息。
+ 您已建立 AWS IoT 規則來傳送使用自訂訊息承載的 Amazon SNS 通知。
+ 您使用 MQTT 用戶端來測試 AWS IoT 規則。

**後續步驟**  
使用此規則傳送一些簡訊之後，請嘗試使用其來查看教學課程的某些層面如何影響訊息，及訊息傳送的時間。此處有幾種簡單的入門方式。
+ 變更輸入訊息主題中的 *device\$1id*，並觀察簡訊內容中的影響。
+ 變更規則查詢陳述式中所選取的欄位，並觀察簡訊內容中的影響。
+ 變更規則查詢陳述式中的測試，以測試最低溫度，而非最高溫度。請記得變更 `max_temperature` 的名稱！
+ 新增重新發佈規則動作，以於傳送 SNS 通知時傳送 MQTT 訊息。
+ 請嘗試本系列中的下一個教學課程，並了解如何進行 [教學課程：將裝置資料儲存在 DynamoDB 表格中](iot-ddb-rule.md)。

# 教學課程：將裝置資料儲存在 DynamoDB 表格中
<a name="iot-ddb-rule"></a>

本教學課程示範如何建立將訊息資料傳送至 DynamoDB 資料表的 AWS IoT 規則。

在此教學課程中，您會建立一個規則，將訊息資料從虛構天氣感應器裝置傳送至 DynamoDB 表格。該規則會格式化來自多個天氣感應器的資料，由此可將其新增至單個資料庫表格中。

**您會在本教學課程中學到什麼**
+ 如何建立 DynamoDB 表格
+ 如何從 AWS IoT 規則傳送訊息資料至 DynamoDB 資料表
+ 如何在 AWS IoT 規則中使用替代範本
+ 如何在規則查詢陳述式中使用簡單的 SQL 查詢和函數
+ 如何使用 MQTT 用戶端測試 AWS IoT 規則

此教學課程約需 30 分鐘方能完成。

**Topics**
+ [步驟 1：為本教學課程建立 DynamoDB 表格](#iot-ddb-rule-ddb-table)
+ [步驟 2：建立 AWS IoT 規則以將資料傳送至 DynamoDB 資料表](#iot-ddb-rule-topic-rule)
+ [步驟 3：測試 AWS IoT 規則和 DynamoDB 資料表](#iot-ddb-rule-test)
+ [步驟 4：檢閱結果及後續步驟](#iot-ddb-rule-review)

**開始本教學課程之前，請確定您有：**
+ 

**[設定 AWS 帳戶](setting-up.md)**  
您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。
+ 

**檢閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)**  
請確定您可使用 MQTT 用戶端來訂閱並發佈至主題。您會使用 MQTT 用戶端，在此程序中測試您的新規則。
+ 

**檢閱了 [Amazon DynamoDB](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/Introduction.html) 概觀**  
若您未曾使用過 DynamoDB，請查閱 [DynamoDB 入門](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/GettingStartedDynamoDB.html)，以熟悉 DynamoDB 的基本概念和作業。

## 步驟 1：為本教學課程建立 DynamoDB 表格
<a name="iot-ddb-rule-ddb-table"></a>

於本教學課程中，您會建立一個具下列屬性的 DynamoDB 表格，以記錄來自虛構氣候感應器裝置的資料：
+ `sample_time` 為主索引鍵，並說明記錄範例的時間。
+ `device_id` 為排序索引鍵，並說明提供範例的裝置 
+ `device_data` 為從裝置接收並由規則查詢陳述式格式化的資料

**如要建立本教學課程的 DynamoDB 表格**

1. 開啟 [DynamoDB 主控台](https://console.aws.amazon.com//dynamodb/home)，然後選擇 **Create table** (建立表格)。

1. 在 **Create table** (建立資料表) 中：

   1.  於 **Table name** (表格名稱) 中，輸入表格名稱：**wx\$1data**。

   1. 在 **Primary key** (主索引鍵) 中，輸入 **sample\$1time**，然後在欄位旁的選項清單中，選擇 **Number**。

   1. 在 **Sort key** (排序索引鍵) 中，輸入 **device\$1id**，然後在欄位旁的選項清單中，選擇 **Number**。

   1. 請在頁面底部，選擇 **Create** (建立)。

您稍後將在設定 DynamoDB 規則動作時定義 `device_data`。

## 步驟 2：建立 AWS IoT 規則以將資料傳送至 DynamoDB 資料表
<a name="iot-ddb-rule-topic-rule"></a>

於此步驟中，您會使用規則查詢陳述式，格式化虛構天氣感應器裝置的資料，以寫入資料庫表格。

從天氣感應器裝置接收的訊息承載範例如下所示：

```
{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}
```

對於資料庫項目，您會使用規則查詢陳述式將訊息承載的結構平面化，如下所示：

```
{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind_velocity": 22,
  "wind_bearing": 255
}
```

於此規則中，您還會使用一些 [替代範本](iot-substitution-templates.md)。替代範本是可讓您從函數和訊息資料插入動態值的運算式。

**建立 AWS IoT 規則以將資料傳送至 DynamoDB 資料表**

1. 開啟[AWS IoT 主控台的 Rules (規則) 中樞](https://console.aws.amazon.com//iot/home#/rulehub)。或者，您可以在 AWS 管理主控台 中開啟 AWS IoT 首頁，並導覽至**訊息路由＞規則**。

1. 如要在 **Rules** (規則) 中開始建立新規則，請選擇 **Create rule** (建立規則)。

1. 在 **Rule properties** (規則屬性) 中：

   1. 在 **Rule name** (規則名稱) 中，輸入 **wx\$1data\$1ddb**。

      請記住，規則名稱在您的 AWS 帳戶 和 區域中必須是唯一的，而且不能有任何空格。我們在此名稱中使用底線字元來分隔規則名稱中的兩個單字。

   1. 在 **Rule description** (規則說明) 中，說明規則。

      有意義的說明可讓您更容易記住此規則的作用及您建立規則的原因。說明可依所需而定，因此請盡可能詳細說明。

1. 選擇 **Next** (下一步) 繼續。

1. 在 **SQL statement** (SQL 陳述式) 中：

   1. 在 **SQL version** (SQL 版本) 中，選取 **2016-03-23**。

   1. 在 **SQL statement** (SQL 陳述式) 編輯方塊中，輸入陳述式：

      ```
      SELECT temperature, humidity, barometer,
        wind.velocity as wind_velocity,
        wind.bearing as wind_bearing,
      FROM 'device/+/data'
      ```

      本陳述式：
      + 聆聽具與 `device/+/data` 主題篩選條件相符之主題的 MQTT 訊息。
      + 將 `wind` 屬性的元素格式化為個別屬性。
      + 傳遞 `temperature`、`humidity` 和 `barometer` 屬性不變。

1. 選擇 **Next** (下一步) 繼續。

1. 在 **Rule actions** (規則動作) 中：

   1. 若要開啟此規則的規則動作清單，請於 **Action 1** (動作 1) 中，選擇 **DynamoDB**。
**注意**  
請務必選擇 DynamoDB 而非 DynamoDBv2 作為規則動作。

   1. 於 **Table name** (表格名稱) 中，選擇您在先前步驟中建立的 DynamoDB 表格名稱：**wx\$1data**。

      **Partition key type** (分割區索引鍵類型) 和 **Sort key type** (排序索引鍵類型) 欄位，都會填入 DynamoDB 表格中的值。

   1. 在 **Partition key (分區索引鍵)**，輸入 **sample\$1time**。

   1. 在 **Partition key value (分割區索引鍵值)** 中，輸入 **\$1\$1timestamp()\$1**。

      這是您將用於此規則中 [替代範本](iot-substitution-templates.md) 的首項。而非使用訊息承載中的值，其會使用 timestamp 函數傳回的值。如需進一步了解，請參閱*AWS IoT Core 開發人員指南中*的[時間戳記](iot-sql-functions.md#iot-function-timestamp)。

   1. 在 **Sort key** (排序索引鍵) 中，輸入 **device\$1id**。

   1. 在 **Sort key value (排序索引鍵值)** 中，輸入 **\$1\$1cast(topic(2) AS DECIMAL)\$1**。

      這是您將用於此規則中 [替代範本](iot-substitution-templates.md) 第二項。其會在主題名稱中插入第二個元素值，即裝置的 ID，然後將其轉換為 DECIMAL 值，以與索引鍵的數值格式相符。若要進一步了解主題，請參閱*AWS IoT Core 開發人員指南*中的[主題](iot-sql-functions.md#iot-function-topic)。或者，若要了解將值轉換為數值的詳細資訊，請參閱*AWS IoT Core 開發人員指南*中的[將值轉換為數值](iot-sql-functions.md#iot-sql-function-cast)。

   1. 在 **Write message data to this column (寫入訊息資料至此欄)** 中輸入 **device\$1data**。

      這會建立 DynamoDB 表格中的 `device_data` 欄。

   1. 將 **Operation (操作)** 保持空白。

   1. 在 **IAM Role** (IAM 角色) 中，選擇 **Create a new Role** (建立新角色)。

   1. 在 **Create role** (建立角色) 對話方塊中，請為 **Role name** (角色名稱) 輸入 **wx\$1ddb\$1role**。這個新角色會自動包含以 "aws-iot-rule" 為字首的政策，其允許 **wx\$1data\$1ddb** 規則將資料傳送到您建立的 **wx\$1data** DynamoDB 資料表。

   1. 在 **IAM role** (IAM 角色) 中，選擇 **wx\$1ddb\$1role**。

   1. 請選擇頁面最下方的 **Next** (下一頁)。

1. 請在 **Review and create** (檢閱和建立) 頁面底部，選擇 **Create** (建立) 來建立規則。

## 步驟 3：測試 AWS IoT 規則和 DynamoDB 資料表
<a name="iot-ddb-rule-test"></a>

若要測試新規則，您會使用 MQTT 用戶端來發佈和訂閱用於此測試的 MQTT 訊息。

在新視窗的 [AWS IoT 主控台中開啟 MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test)。這可讓您編輯規則，而不會遺失 MQTT 用戶端的組態。如果您讓它轉到主控台中的另一個頁面，MQTT 用戶端不會保留任何訂閱或訊息記錄。您也需要在主控台中開啟單獨的[AWS IoT 主控台視窗至 DynamoDB Tables 中樞](https://console.aws.amazon.com//dynamodb/home#tables:)，以檢視規則傳送的新項目。

**如要使用 MQTT 用戶端來測試您的規則。**

1. 於 [AWS IoT 主控台的 MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test) 中，訂閱輸入主題 `device/+/data`。

   1. 於 MQTT 用戶端中，選擇 **Subscribe to a topic** (訂閱主題)。

   1. 若為 **Topic filter** (主題篩選條件)，請輸入輸入主題篩選條件的主題 **device/\$1/data**。

   1. 選擇 **Subscribe (訂閱)**。

1. 現在，使用特定裝置 ID **device/22/data** 將訊息發佈至輸入主題。您無法發佈至包含萬用字元的 MQTT 主題。

   1. 於 MQTT 用戶端中，請選擇 **Publish to a topic** (發佈至主題)。

   1. 若為 **Topic name** (主題名稱)，請輸入輸入主題名稱 **device/22/data**。

   1. 若為 **Message payload** (訊息承載)，請輸入下列範例資料。

      ```
      {
        "temperature": 28,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 如要發佈 MQTT 訊息，請選擇 **Publish** (發佈)。

   1. 現在，於 MQTT 用戶端中，選擇 **Subscribe to a topic** (訂閱主題)。於 **Subscribe** (訂閱) 欄中，選擇 **device/\$1/data** 訂閱。確認上一步的範例資料會顯示於此處。

1. 查看您規則所建立 DynamoDB 表格中的列。

   1. 在[AWS IoT 主控台的 DynamoDB Tables 中樞](https://console.aws.amazon.com//dynamodb/home#tables:)中，選擇 **wx\$1data**，然後選擇**項目**索引標籤。

      若您已在 **Items** (項目) 索引標籤上，您可能需要選擇表格標題右上角的重新整理圖示來重新整理該顯示。

   1. 請注意，表格中的 **sample\$1time** 值為連結並開啟一個。若您剛傳送了第一則訊息，則其將是清單中唯一的訊息。

      此連結會顯示表格該列中的所有資料。

   1. 展開 **device\$1data** 項目，查看規則查詢陳述式所產生的資料。

   1. 探索此顯示中可用資料的不同表示法。您亦可於此顯示中編輯資料。

   1. 完成查閱此資料列之後，如要儲存所做的任何變更，請選擇 **Save** (儲存)，或者，如要結束而不儲存任何變更，請選擇 **Cancel** (取消)。

若您並未看到正確的行為，請查看疑難排解提示。

### 對您的 DynamoDB 規則進行疑難排解
<a name="iot-ddb-rule-trouble"></a>

若您並未看到預期的結果，請查看以下事項。
+ 

**您收到錯誤的橫幅**  
若在您發佈輸入訊息時出現錯誤，請先更正該錯誤。下列步驟可協助您修正該錯誤。
+ 

**您並未在 MQTT 用戶端中看到輸入訊息**  
每次您將輸入訊息發佈至 `device/22/data` 主題時，若您依程序中所述訂閱了 `device/+/data` 主題篩選條件，則該訊息應會顯示於 MQTT 用戶端中。

**要檢查的事項**
  + 

**檢查您訂閱的主題篩選條件**  
若您依程序中所述訂閱了輸入訊息主題，則每次發佈輸入訊息時都應該會看到其複本。

    若您並未訊息，請檢查您訂閱的主題名稱，並將其與所發佈的主題進行比較。主題名稱區分大小寫，且您訂閱的主題必須與所發佈訊息承載的主題相同。
  + 

**檢查訊息發佈功能**  
在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，檢查發佈訊息的主題，然後選擇 **Publish to topic** (發佈至主題)。您應該會在訊息清單中出現主題下方的編輯方塊中看到訊息承載。
+ 

**您在 DynamoDB 表格中看不到您的資料**  
首先要做的是選擇表格標題右上角的重新整理圖示，以重新整理顯示。若未顯示您正在尋找的資料，請檢查下列內容。

**要檢查的事項**
  + 

**檢查 MQTT 用戶端 AWS 區域 的 和您建立的規則**  
您正在執行 MQTT 用戶端的主控台必須與您建立的規則處於相同的 AWS 區域。
  + 

**檢查規則查詢陳述式中的輸入訊息主題**  
若要讓規則運作，其必須收到一則訊息，其主題名稱與規則查詢陳述式之 FROM 子句中的主題篩選條件相符。

    檢查規則查詢陳述式中主題篩選條件的拼字與 MQTT 用戶端中主題的拼字。主題名稱區分大小寫，且郵件的主題必須與規則查詢陳述式中的主題篩選條件相符。
  + 

**檢查輸入訊息承載的內容**  
若要讓規則運作，其必須在 SELECT 陳述式中宣告的訊息承載中尋找資料欄位。

    檢查規則查詢陳述式中 `temperature` 欄位的拼字與 MQTT 用戶端中訊息承載的拼字。欄位名稱區分大小寫，規則查詢陳述式中的 `temperature` 欄位必須與訊息承載中的 `temperature` 欄位相符。

    請確定訊息承載中的 JSON 文件格式正確。若 JSON 有任何錯誤，例如缺少逗號，則規則將無法進行讀取。
  + 

**檢查用於規則動作中的索引鍵和欄位名稱**  
用於主題規則中的欄位名稱必須與已發佈訊息之 JSON 訊息承載中找到的欄位名稱相符。

    開啟您建立於主控台中的規則，並檢查規則動作組態中的欄位名稱與用於 MQTT 用戶端中的欄位名稱。
  + 

**檢查規則所使用的角色**  
規則動作必須具有接收原始主題及發佈新主題的權限。

    授權規則以接收訊息資料及更新 DynamoDB 表格的政策是所使用主題特有的。若您變更規則使用的主題或 DynamoDB 表格名稱，則必須更新規則動作的角色，以更新其相符的政策。

    若您懷疑這會是問題，請編輯規則動作並建立新角色。規則動作建立的新角色會收到執行這些動作所需的授權。

## 步驟 4：檢閱結果及後續步驟
<a name="iot-ddb-rule-review"></a>

在您使用此規則將數則訊息傳送至 DynamoDB 表格後，請試著對其進行試驗，以查看變更教學課程中的某些層面如何影響寫入表格的資料。此處有幾種簡單的入門方式。
+ 變更輸入訊息主題中的 *device\$1id*，並觀察對資料的影響。您可使用此來模擬從多個天氣感應器接收資料。
+ 變更規則查詢陳述式中所選取的欄位，並觀察對資料的影響。您可以此來篩選儲存於表格中的資料。
+ 新增重新發佈規則動作，為新增至表格中的每一列傳送 MQTT 訊息。您可以此來進行除錯。

完成本教學課程後，請查看 [教學課程：使用 AWS Lambda 函數格式化通知](iot-lambda-rule.md)。

# 教學課程：使用 AWS Lambda 函數格式化通知
<a name="iot-lambda-rule"></a>

本教學課程示範如何將 MQTT 訊息資料傳送至 AWS Lambda 動作，以格式化和傳送至其他 AWS 服務。在本教學課程中， AWS Lambda 動作會使用 AWS 開發套件，將格式化的訊息傳送至您在教學課程中建立的 Amazon SNS 主題，了解如何 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)。

於有關如何進行 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md) 的教學課程中，由規則查詢陳述式產生的 JSON 文件 作為簡訊的主文傳送。結果為一則看似此範例的簡訊：

```
{"device_id":"32","reported_temperature":38,"max_temperature":30}
```

在本教學課程中，您將使用 AWS Lambda 規則動作來呼叫 AWS Lambda 函數，將規則查詢陳述式中的資料格式化為友善格式，例如此範例：

```
Device 32 reports a temperature of 38, which exceeds the limit of 30.
```

您將在本教學課程中建立的 AWS Lambda 函數會使用規則查詢陳述式中的資料來格式化訊息字串，並呼叫 AWS SDK 的 [SNS 發佈](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html#SNS.Client.publish)函數來建立通知。

**您會在本教學課程中學到什麼**
+ 如何建立和測試 AWS Lambda 函數
+ 如何在 AWS Lambda 函數中使用 AWS SDK 發佈 Amazon SNS 通知
+ 如何在規則查詢陳述式中使用簡單的 SQL 查詢和函數
+ 如何使用 MQTT 用戶端測試 AWS IoT 規則

此教學課程約需 45 分鐘方能完成。

**Topics**
+ [步驟 1：建立傳送文字訊息的 AWS Lambda 函數](#iot-lambda-rule-create-lambda)
+ [步驟 2：使用 AWS IoT 規則動作建立 AWS Lambda 規則](#iot-lambda-rule-create-rule)
+ [步驟 3：測試 AWS IoT 規則和 AWS Lambda 規則動作](#iot-lambda-rule-test-rule)
+ [步驟 4：檢閱結果及後續步驟](#iot-lambda-rule-next-steps)

**開始本教學課程之前，請確定您有：**
+ 

**[設定 AWS 帳戶](setting-up.md)**  
您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。
+ 

**檢閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)**  
請確定您可使用 MQTT 用戶端來訂閱並發佈至主題。您會使用 MQTT 用戶端，在此程序中測試您的新規則。
+ 

**已完成本節中的其他規則教學課程**  
本教學課程需要您於教學課程中建立有關如何進行 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md) 的 SNS 通知主題。其亦假設您已完成本節中其他與規則相關的教學課程。
+ 

**檢閱了 [AWS Lambda](https://docs.aws.amazon.com//lambda/latest/dg/welcome.html) 概觀**  
如果您 AWS Lambda 未曾使用過 ，請檢閱[AWS Lambda](https://docs.aws.amazon.com//lambda/latest/dg/welcome.html)和[開始使用 Lambda](https://docs.aws.amazon.com//lambda/latest/dg/getting-started.html) 以了解其術語和概念。

## 步驟 1：建立傳送文字訊息的 AWS Lambda 函數
<a name="iot-lambda-rule-create-lambda"></a>

本教學課程中的 AWS Lambda 函數會收到規則查詢陳述式的結果、將元素插入文字字串，並將產生的字串作為通知中的訊息傳送至 Amazon SNS。

與[教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md)如何使用規則 AWS IoT 動作傳送通知的 教學課程不同，本教學課程會使用 AWS SDK 的 函數，從 Lambda 函數傳送通知。然而，用於本教學課程中的實際 Amazon SNS 通知主題與您在教學課程中有關如何進行 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md) 相同。

**建立傳送文字訊息的 AWS Lambda 函數**

1. 建立新的 AWS Lambda 函數。

   1. 於 [AWS Lambda 主控台](https://console.aws.amazon.com//lambda/home)中，選擇 **Create function** (建立函數)。

   1. 於 **Create function** (建立函數) 中，選取 **Use a blueprint** (使用藍圖)。

      搜尋並選取 **hello-world-python** 藍圖，然後選擇 **Configure** (設定)。

   1. 於**基本資訊**中：

      1. 在 **Function name** (函數名稱) 中，輸入此函數的名稱 **format-high-temp-notification**。

      1. 在**執行角色**中，選擇**從 AWS 政策範本建立新角色**。

      1. 於 Role name (角色名稱) 中，輸入新角色 **format-high-temp-notification-role** 的名稱。

      1. 在 **Policy templates - *optional*** (政策範本：選用) 中，搜尋並選取 **Amazon SNS publish policy** (Amazon SNS 發佈政策)。

      1. 選擇**建立函數**。

1. 修改藍圖程式碼以進行格式化並傳送 Amazon SNS 通知。

   1. 在您建立函數之後，您應該會看到 **format-high-temp-notification** 詳細資訊頁面。若您未看到，請從 [Lambda **函數**](https://console.aws.amazon.com//lambda/home#/functions) 頁面上加以開啟。

   1. 在**format-high-temp-notification** 詳細資訊頁面中，選擇**Configuration** (組態) 索引標籤，然後捲動至 **Function code** (函數程式碼) 面板。

   1. 於 **Function code** (函數程式碼) 視窗的 **Environment** (環境) 窗格中，選擇 Python 檔案 `lambda_function.py`。

   1. 於 **Function code** (函數程式碼) 視窗中，刪除藍圖中的所有原始程式程式碼，並以此程式碼進行取代。

      ```
      import boto3
      #
      #   expects event parameter to contain:
      #   {
      #       "device_id": "32",
      #       "reported_temperature": 38,
      #       "max_temperature": 30,
      #       "notify_topic_arn": "arn:aws:sns:us-east-1:57EXAMPLE833:high_temp_notice"
      #   }
      # 
      #   sends a plain text string to be used in a text message
      #
      #      "Device {0} reports a temperature of {1}, which exceeds the limit of {2}."
      #   
      #   where:
      #       {0} is the device_id value
      #       {1} is the reported_temperature value
      #       {2} is the max_temperature value
      #
      def lambda_handler(event, context):
      
          # Create an SNS client to send notification
          sns = boto3.client('sns')
      
          # Format text message from data
          message_text = "Device {0} reports a temperature of {1}, which exceeds the limit of {2}.".format(
                  str(event['device_id']),
                  str(event['reported_temperature']),
                  str(event['max_temperature'])
              )
      
          # Publish the formatted message
          response = sns.publish(
                  TopicArn = event['notify_topic_arn'],
                  Message = message_text
              )
      
          return response
      ```

   1. 選擇**部署**。

1. 在新視窗中，從教學課程有關如何進行 [教學課程：傳送 Amazon SNS 通知](iot-sns-rule.md) 中查詢您 Amazon SNS 主題的 Amazon 資源名稱 (ARN)。

   1. 於新視窗中，開啟 [Amazon SNS 主控台的主題頁面](https://console.aws.amazon.com//sns/v3/home#/topics)。

   1. 於 **Topics** (主題) 頁面中，於 Amazon SNS 主題清單中尋找 **high\$1temp\$1notice** 通知主題。

   1. 尋找 **high\$1temp\$1notice** 通知主題的 **ARN**，以用於下一個步驟中。

1. 建立您 Lambda 函數的測試案例。

   1. 於主控台的 [Lambda **函數**](https://console.aws.amazon.com//lambda/home#/functions)頁面的 **format-high-temp-notification** 詳細資訊頁面上，選擇該頁面右上角中的 **Select a test event** (選擇測試事件) (即使看似已停用)，然後選擇 **Configure test events** (設定測試事件)。

   1. 於 **Configure test event** (設定測試事件) 中，選擇 **Create new test event** (建立新的測試事件)。

   1. 於 **Event name** (事件名稱) 中，輸入 **SampleRuleOutput**。

   1. 在 **Event name** (事件名稱) 下的 JSON 編輯器，請貼上此範例 JSON 文件。這是您的 AWS IoT 規則將傳送至 Lambda 函數的範例。

      ```
      {
        "device_id": "32",
        "reported_temperature": 38,
        "max_temperature": 30,
        "notify_topic_arn": "arn:aws:sns:us-east-1:57EXAMPLE833:high_temp_notice"
      }
      ```

   1. 請參閱具有 **high\$1temp\$1notice** 通知主題之 **ARN** 的視窗，然後複製 ARN 值。

   1. 以您通知主題的 ARN 取代 JSON 編輯器中的 `notify_topic_arn` 值。

      保持此視窗的開啟狀態，您可於建立 AWS IoT 規則時再次使用此 ARN 值。

   1. 選擇**建立**。

1. 以範例資料測試函數。

   1. 於 **format-high-temp-notification** 詳細資料頁面的右上角中，確認 **SampleRuleOutput** 會顯示於 **Test** (測試) 按鈕旁。若未顯示，請從可用的測試事件清單中進行選擇。

   1. 如要將範例規則輸出訊息傳送至您的函數，請選擇 **Test** (測試)。

若函數和通知皆有效，您會在訂閱通知的手機上收到一則簡訊。

若手機上並未收到簡訊，請檢查作業結果。於 **Function code** (函數程式碼) 面板中的 **Execution result** (執行結果) 索引標籤中，查閱回應以找出發生的任何錯誤。在您的函數可將通知傳送至手機之前，請勿繼續進行下一個步驟。

## 步驟 2：使用 AWS IoT 規則動作建立 AWS Lambda 規則
<a name="iot-lambda-rule-create-rule"></a>

於此步驟中，您會使用規則查詢陳述式，格式化虛構天氣感應器裝置的資料，以傳送至 Lambda 函數，該函數將會格式化並傳送簡訊。

從天氣裝置接收到的訊息承載裝置的範例如下所示：

```
{
  "temperature": 28,
  "humidity": 80,
  "barometer": 1013,
  "wind": {
    "velocity": 22,
    "bearing": 255
  }
}
```

於此規則中，您會使用規則查詢陳述式來建立 Lambda 函數的訊息承載，如下所示：

```
{
  "device_id": "32",
  "reported_temperature": 38,
  "max_temperature": 30,
  "notify_topic_arn": "arn:aws:sns:us-east-1:57EXAMPLE833:high_temp_notice"
}
```

此包含 Lambda 函數格式化及傳送正確簡訊所需的所有資訊。

**建立 AWS IoT 規則以呼叫 Lambda 函數**

1. 開啟 [AWS IoT 主控台的**規則**中樞](https://console.aws.amazon.com//iot/home#/rulehub)。

1. 如要於 **Rules** (規則) 中開始建立新規則，請選擇 **Create** (建立)。

1. 在 **Create a rule** (建立規則) 的頂部中：

   1. 於 **Name** (名稱) 中，輸入規則名稱 **wx\$1friendly\$1text**。

      請記住，規則名稱在您的 AWS 帳戶 和 區域中必須是唯一的，而且不能有任何空格。我們在此名稱中使用底線字元來分隔規則名稱中的兩個單字。

   1.  在 **Description** (說明) 中，說明規則。

      有意義的說明可讓您更容易記住此規則的作用及您建立規則的原因。說明可依所需而定，因此請盡可能詳細說明。

1. 在 **Create a rule** (建立規則) 的 **Rule query statement** (規則查詢陳述式) 中：

   1.  在**使用 SQL 版本**中，選取 **2016-03-23**。

   1. 在 **Rule query statement** (規則查詢陳述式) 編輯方塊中輸入陳述式：

      ```
      SELECT 
        cast(topic(2) AS DECIMAL) as device_id, 
        temperature as reported_temperature,
        30 as max_temperature,
        'arn:aws:sns:us-east-1:57EXAMPLE833:high_temp_notice' as notify_topic_arn
      FROM 'device/+/data' WHERE temperature > 30
      ```

      本陳述式：
      + 聆聽主題與 `device/+/data` 主題篩選條件相符及 `temperature` 值大於 30 的 MQTT 訊息。
      + 從主題字串選取第二個元素，將其轉換為十進位數字，進而指派給 `device_id` 欄位。
      + 從訊息承載選取值 `temperature` 欄位的值，然後將其指派給 `reported_temperature` 欄位。
      + 建立常數值 `30` 來表示限制值，並將其指定給 `max_temperature` 欄位。
      + 建立 `notify_topic_arn` 欄位的常數值。

   1. 請參閱具有 **high\$1temp\$1notice** 通知主題之 **ARN** 的視窗，然後複製 ARN 值。

   1. 以您通知主題的 ARN 取代規則查詢陳述式編輯器中的 ARN 值 (*arn:aws:sns:us-east-1:57EXAMPLE833:high\$1temp\$1notice*)。

1. 於 **Set one or more actions** (設定一個或多個動作) 中：

   1. 若要開啟此規則的規則動作清單，請選擇 **Add action** (新增動作)。

   1. 於 **Select an action** (選取動作) 中，選擇 **Send a message to a Lambda function** (將訊息傳送至 Lambda 函數)。

   1. 若要開啟所選取動作的組態頁面，請在動作清單底部選擇 **Configure action** (設定動作)。

1. 於 **Configure action** (設定動作)：

   1. 於 **Function name** (函數名稱) 中，選擇 **Select** (選取)。

   1. 選擇 **format-high-temp-notification**。

   1. 在 **Configure action** (設定動作) 的底部，選擇 **Add action** (新增動作)。

   1. 如要建立規則，請於 **Create a rule** (建立規則) 中，選擇 **Create rule** (建立規則)。

## 步驟 3：測試 AWS IoT 規則和 AWS Lambda 規則動作
<a name="iot-lambda-rule-test-rule"></a>

若要測試新規則，您將使用 MQTT 用戶端來發佈和訂閱此規則所使用的 MQTT 訊息。

在新視窗的 [AWS IoT 主控台中開啟 MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test)。您現在可以編輯規則，而不會遺失 MQTT 用戶端的設定。如果您讓 MQTT 用戶端前往主控台中的另一個頁面，您將會遺失您的訂閱或訊息記錄。

**如要使用 MQTT 用戶端來測試您的規則。**

1. 在 AWS IoT 主控台的 [MQTT 用戶端](https://console.aws.amazon.com//iot/home#/test) 中，訂閱輸入主題，在此案例中為 `device/+/data`。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Subscribe to a topic** (訂閱主題)。

   1. 在 **Subscription topic** (訂閱主題) 中，輸入輸入主題篩選條件 **device/\$1/data** 的主題 。

   1. 將剩下的欄位保留為其預設設定。

   1. 請選擇 **Subscribe to topic** (訂閱主題)。

      在 **Subscriptions** (訂閱) 欄中，**Publish to a topic** (發佈到主題) 之下，**device/\$1/data** 隨即顯示。

1. 使用特定裝置 ID **device/32/data**，將訊息發佈至輸入主題。您無法發佈至包含萬用字元的 MQTT 主題。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Publish to topic** (發佈至主題)。

   1. 在 **Publish** (發佈) 欄位中輸入輸入主題名稱 **device/32/data**。

   1. 複製此處顯示的範例資料，並在主題名稱下方的編輯方塊中貼上範例資料。

      ```
      {
        "temperature": 38,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 如要發佈您的 MQTT 訊息，請選擇 **Publish to topic** (發佈至主題)。

1. 確認簡訊已傳送。

   1. 在 MQTT 用戶端中 **Subscriptions** (訂閱) 下，您先前訂閱的主題旁會有一個綠點。

      該綠點表示自上次查看後，已收到一個或多個新訊息。

   1. 於 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，來檢查訊息承載是否與您剛剛發佈的內容相符，如下所示：

      ```
      {
        "temperature": 38,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 檢查您用來訂閱 SNS 主題的手機，並確認訊息承載內容如下所示：

      ```
      Device 32 reports a temperature of 38, which exceeds the limit of 30.
      ```

      若您變更訊息主題中的主題 ID 元素，請記得，僅當訊息主題中的該元素僅包含數字字元時，將 `topic(2)` 值轉換為數值才會有效。

1. 請嘗試傳送溫度不超過限制的 MQTT 訊息。

   1. 在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下選擇 **Publish to topic** (發佈至主題)。

   1. 在 **Publish** (發佈) 欄位中輸入輸入主題名稱 **device/33/data**。

   1. 複製此處顯示的範例資料，並在主題名稱下方的編輯方塊中貼上範例資料。

      ```
      {
        "temperature": 28,
        "humidity": 80,
        "barometer": 1013,
        "wind": {
          "velocity": 22,
          "bearing": 255
        }
      }
      ```

   1. 若要傳送 MQTT 訊息，請選擇 **Publish to topic** (發佈至主題)。

   您應該會看到您在 **device/\$1/data** 訂閱中傳送的訊息；但因溫度值低於規則查詢陳述式中的最高溫度，您不應收到簡訊。

   若您並未看到正確的行為，請查看疑難排解提示。

### 對 AWS Lambda 規則和通知進行故障診斷
<a name="iot-lambda-rule-troubleshoot"></a>

若您並未看到預期的結果，請查看以下事項。
+ 

**您收到錯誤的橫幅**  
若在您發佈輸入訊息時出現錯誤，請先更正該錯誤。下列步驟可協助您修正該錯誤。
+ 

**您並未在 MQTT 用戶端中看到輸入訊息**  
每次您將輸入訊息發佈至 `device/32/data` 主題中，若您依程序中所述訂閱了 `device/+/data` 主題篩選條件，則該訊息應會顯示於 MQTT 用戶端中。

**要檢查的事項**
  + 

**檢查您訂閱的主題篩選條件**  
若您依程序中所述訂閱了輸入訊息主題，則每次發佈輸入訊息時都應該會看到其複本。

    若您並未訊息，請檢查您訂閱的主題名稱，並將其與所發佈的主題進行比較。主題名稱區分大小寫，且您訂閱的主題必須與所發佈訊息承載的主題相同。
  + 

**檢查訊息發佈功能**  
在 MQTT 用戶端中的 **Subscriptions** (訂閱) 下，選擇 **device/\$1/data**，檢查發佈訊息的主題，然後選擇 **Publish to topic** (發佈至主題)。您應該會在訊息清單中出現主題下方的編輯方塊中看到訊息承載。
+ 

**您並未收到 SMS 訊息：**  
若要讓您的規則運作，其必須具有授權其接收訊息和傳送 SNS 通知的正確政策，且必須接收訊息。

**要檢查的事項**
  + 

**檢查 MQTT 用戶端 AWS 區域 的 和您建立的規則**  
您正在執行 MQTT 用戶端的主控台必須與您建立的規則處於相同的 AWS 區域。
  + 

**檢查訊息承載中的溫度值是否超過測試閾值**  
若溫度值小於或等於 30 (如規則查詢陳述式中所定義)，則規則將不會執行其任何動作。
  + 

**檢查規則查詢陳述式中的輸入訊息主題**  
若要讓規則運作，其必須收到一則訊息，其主題名稱與規則查詢陳述式之 FROM 子句中的主題篩選條件相符。

    檢查規則查詢陳述式中主題篩選條件的拼字與 MQTT 用戶端中主題的拼字。主題名稱區分大小寫，且郵件的主題必須與規則查詢陳述式中的主題篩選條件相符。
  + 

**檢查輸入訊息承載的內容**  
若要讓規則運作，其必須在 SELECT 陳述式中宣告的訊息承載中尋找資料欄位。

    檢查規則查詢陳述式中 `temperature` 欄位的拼字與 MQTT 用戶端中訊息承載的拼字。欄位名稱區分大小寫，規則查詢陳述式中的 `temperature` 欄位必須與訊息承載中的 `temperature` 欄位相符。

    請確定訊息承載中的 JSON 文件格式正確。若 JSON 有任何錯誤，例如缺少逗號，則規則將無法進行讀取。
  + 

**檢查 Amazon SNS 通知**  
於 [步驟 1：建立傳送簡訊的 Amazon SNS 主題](iot-sns-rule.md#iot-sns-rule-create-sns-topic) 中，請參閱步驟 3，說明如何測試 Amazon SNS 通知並測試通知以確保通知可正常運作。
  + 

**檢查 Lambda 函數**  
於 [步驟 1：建立傳送文字訊息的 AWS Lambda 函數](#iot-lambda-rule-create-lambda) 中，請參閱步驟 5，說明如何使用測試資料來測試 Lambda 函數，並測試 Lambda 函數。
  + 

**檢查規則所使用的角色**  
規則動作必須具有接收原始主題及發佈新主題的權限。

    授權規則接收訊息資料的政策並加以重新發佈為所使用的主題所特定的。若變更用於重新發佈訊息資料的主題，則必須更新規則動作的角色，來更新其政策以與目前主題相符。

    若您懷疑這會是問題，請編輯 Republish (重新發佈) 規則動作並建立新角色。規則動作建立的新角色會收到執行這些動作所需的授權。

## 步驟 4：檢閱結果及後續步驟
<a name="iot-lambda-rule-next-steps"></a>

**於本教學課程中：**
+ 您已建立 AWS IoT 規則來呼叫 Lambda 函數，該函數會傳送使用自訂訊息承載的 Amazon SNS 通知。
+ 您於規則查詢陳述式中使用了一個簡單的 SQL 查詢和函數，可為您的 Lambda 函數建立一個新訊息承載。
+ 您使用 MQTT 用戶端來測試 AWS IoT 規則。

**後續步驟**  
使用此規則傳送一些簡訊之後，請嘗試使用其來查看教學課程的某些層面如何影響訊息，及訊息傳送的時間。此處有幾種簡單的入門方式。
+ 變更輸入訊息主題中的 *device\$1id*，並觀察簡訊內容中的影響。
+ 變更規則查詢陳述式中所選取的欄位、更新 Lambda 函數以於新訊息中加以使用，並觀察簡訊內容中的影響。
+ 變更規則查詢陳述式中的測試，以測試最低溫度，而非最高溫度。更新 Lambda 函數以格式化新的訊息，並記得變更 `max_temperature` 的名稱。
+ 若要進一步了解如何尋找在您開發和使用 AWS IoT 規則時可能發生的錯誤，請參閱 [監控 AWS IoT](monitoring_overview.md)。

# 在裝置離線時保留裝置狀態
<a name="iot-shadows-tutorial"></a>

這些教學課程說明如何使用 AWS IoT Device Shadow 服務來存放和更新裝置的狀態資訊。Shadow 文件是一個 JSON 文件，其根據裝置、本機應用程式或服務發佈的訊息，顯示裝置狀態的變化。於本教學課程中，Shadow 文件會顯示燈泡顏色的變化。這些教學課程還會顯示影子如何在裝置與網際網路中斷連線時，儲存此資訊，並在裝置回復連線並請求此資訊時，將最新狀態資訊傳回裝置。

建議您依此處顯示的順序試試這些教學課程，從您需要建立的 AWS IoT 資源和必要的硬體設定開始，這亦可協助您逐步學習概念。這些教學課程示範如何設定和連接 Raspberry Pi 裝置以搭配 使用 AWS IoT。若您並無所需的硬體，您可依照這些教學課程進行調整，以適應您選擇的裝置或[使用 Amazon EC2 建立虛擬裝置](creating-a-virtual-thing.md)。

**教學課程案例概觀**  
這些教學課程的案例為本機應用程式或服務，可變更燈泡的顏色，及將其資料發佈置預留的影子主題。這些教學課程類似於[互動式入門教學課程](interactive-demo.md)中說明的 Device Shadow 功能，並在 Raspberry Pi 裝置實作。本節中的教學課程側重於單一經典影子裝置，同時展現如何容納已命名影子或多個裝置的方法。

下列教學課程將協助您了解如何使用 AWS IoT Device Shadow 服務。
+ 

**[教學課程：準備好 Raspberry Pi 來執行影子應用程式](create-resources-shadow.md)**  
本教學課程說明如何設定 Raspberry Pi 裝置以進行連線 AWS IoT。您也將建立 AWS IoT 政策文件和物件資源、下載憑證，然後將政策連接到該物件資源。此教學課程約需 30 分鐘方能完成。
+ 

**[教學課程：安裝裝置 SDK 並執行 Device Shadows 的範例應用程式](lightbulb-shadow-application.md)**  
本教學課程說明如何安裝必要的工具、軟體和適用於 Python 的 AWS IoT 裝置 SDK，然後執行範例陰影應用程式。本教學課程以 [連接 Raspberry Pi 或其他裝置](connecting-to-existing-device.md) 中提出的概念為基礎，需 20 分鐘才能完成。
+ 

**[教學課程：使用範例應用程式和 MQTT 測試用戶端，與 Device Shadow 互動](interact-lights-device-shadows.md)**  
本教學課程示範如何使用`shadow.py`範例應用程式和**AWS IoT 主控台**來觀察 AWS IoT Device Shadows 與燈泡狀態變更之間的互動。本教學課程也會展示如何將 MQTT 訊息傳送至 Device Shadow 的預留主題。此教學課程約需 45 分鐘方能完成。

**AWS IoT Device Shadow 概觀**  
Device Shadow 是由您在 AWS IoT 登錄檔中建立的[物件資源](iot-thing-management.md)管理之裝置的持久性虛擬表示法。Shadow 文件是個 JSON 或 JavaScript 標記法文件，用來存儲和檢索裝置的目前狀態資訊。您可透過 MQTT 或 HTTP REST API，使用影子來取得及設定裝置的狀態 (無論該裝置是否連線至網際網路)。

Shadow 的文件包含 `state` 屬性，說明裝置狀態的下列層面：
+ `desired`：應用程式會透過更新 `desired` 物件來指定裝置屬性的所需狀態。
+ `reported`：裝置會報告其在 `reported` 物件中的目前狀態。
+ `delta`： AWS IoT 報告 `delta` 物件中所需狀態與報告狀態之間的差異。

以下為 Shadow 狀態文件的範例：

```
{
  "state": {
    "desired": {
      "color": "green"
      },
    "reported": {
      "color": "blue"
      },
    "delta": {
      "color": "green"
      }
   }
}
```

如要更新裝置的 Shadow 文件，您可使用[預留的 MQTT 主題](reserved-topics.md#reserved-topics-shadow)、以 HTTP 支援 `GET`、`UPDATE` 和 `DELETE` 操作的 [Device Shadow REST API](device-shadow-rest-api.md)，以及 [AWS IoT CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iot-data/index.html)。

在上一個範例中，假設您想將 `desired` 顏色變更為 `yellow`。如要執行此動作，請傳送請求至 [UpdateThingShadow](device-shadow-rest-api.md#API_UpdateThingShadow) API 或將訊息發佈至[更新](device-shadow-mqtt.md#update-pub-sub-topic)主題 `$aws/things/THING_NAME/shadow/update`。

```
{
  "state": {
    "desired": {
      "color": yellow
    }
  }
}
```

更新只會影響請求中所指定的欄位。成功更新 Device Shadow 後， 會將新`desired`狀態 AWS IoT 發佈至`delta`主題 `$aws/things/THING_NAME/shadow/delta`。於此狀況下，Shadow 文件看起來像這樣：

```
{
  "state": {
    "desired": {
      "color": yellow
    },
    "reported": {
      "color": green
    },
    "delta": {
      "color": yellow
      }
  }
}
```

接著會使用`$aws/things/THING_NAME/shadow/update`具有下列 JSON 訊息`Update`的主題，向 AWS IoT Device Shadow 報告新狀態：

```
{
  "state": {
    "reported": {
      "color": yellow
    }
  }
}
```

若您想要取得目前的狀態資訊，請將請求傳送至 [GetThingShadow](device-shadow-rest-api.md#API_GetThingShadow) API 或將 MQTT 訊息發佈至[取得](device-shadow-mqtt.md#get-pub-sub-topic)主題 `$aws/things/THING_NAME/shadow/get`。

如需使用 Device Shadow 服務的相關資訊，請參閱 [AWS IoT Device Shadow 服務](iot-device-shadows.md)。

如需在裝置、應用程式和服務中使用 Device Shadows 的詳細資訊，請參閱 [在裝置中使用影子](device-shadow-comms-device.md) 和 [在應用程式和服務中使用影子](device-shadow-comms-app.md)。

如需與 AWS IoT 陰影互動的資訊，請參閱 [與影子互動](device-shadow-data-flow.md)。

如需 MQTT 預留主題和 HTTP REST API 的相關資訊，請參閱 [Device Shadow MQTT 主題](device-shadow-mqtt.md) 和 [Device Shadow REST API](device-shadow-rest-api.md)。

# 教學課程：準備好 Raspberry Pi 來執行影子應用程式
<a name="create-resources-shadow"></a>

本教學課程示範如何設定 Raspberry Pi 裝置，以及建立裝置連線和交換 MQTT 訊息所需的 AWS IoT 資源。

**注意**  
若您打算 [使用 Amazon EC2 建立虛擬裝置](creating-a-virtual-thing.md)，則可跳過本頁，並繼續 [設定您的裝置](configure-device.md)。當您建立虛擬物件時，您將會建立這些資源。若您想使用不同的裝置，而非 Raspberry Pi，您可嘗試依照這些教學課程進行調整，使其適應您選擇的裝置。

**於本教學課程中，您會了解如何：**
+ 設定 Raspberry Pi 裝置並將其設定為搭配 使用 AWS IoT。
+ 建立 AWS IoT 政策文件，授權您的裝置與服務 AWS IoT 互動。
+  AWS IoT 在 X.509 裝置憑證中建立物件資源，然後連接政策文件。

  問題是您的裝置在 AWS IoT 登錄檔中的虛擬表示。憑證會將您的裝置驗證為 AWS IoT Core，而政策文件會授權您的裝置與之互動 AWS IoT。

**如何執行本教學課程**  
如要執行 Device Shadows 的 `shadow.py` 範例應用程式，您需要一個連接至 AWS IoT的 Raspberry Pi 裝置。我們建議您依照此處顯示的順序學習本教學課程，從設定 Raspberry Pi 及其配件開始，然後建立政策，並將政策連接至您建立的物件資源。然後，您可以使用 Raspberry Pi 支援的圖形使用者介面 (GUI) 來遵循本教學課程，在裝置的 Web 瀏覽器上開啟 AWS IoT 主控台，這也可以讓您更輕鬆地直接將憑證下載到 Raspberry Pi 以進行連線 AWS IoT。

**開始本教學課程之前，請確定您有：**
+  AWS 帳戶。若您沒有帳戶，請完成 [設定 AWS 帳戶](setting-up.md) 所述的步驟，然後再繼續。您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。
+ Raspberry Pi 及其必要的配件。您會需要：
  + [Raspberry Pi 3 代 B 型](https://www.raspberrypi.com/products/)或更新的型號。本教學課程可能適用於較早版本的 Raspberry Pi，但我們尚未對其進行測試。
  + [Raspberry Pi OS (32 位元)](https://www.raspberrypi.com/software/operating-systems/) 或更新版本。我們建議您使用最新版本的 Raspberry Pi 作業系統。較早版本的作業系統可能適用，但我們尚未對其進行測試。
  + 乙太網路或 Wi-Fi 連線。
  + 鍵盤、滑鼠、顯示器、纜線和電源供應器。

此教學課程約需 30 分鐘方能完成。

## 步驟 1：設定及配置 Raspberry Pi 裝置
<a name="setup-device-shadow"></a>

在本節中，我們將設定 Raspberry Pi 裝置以搭配 使用 AWS IoT。

**重要**  
調整這些指示以適用其他裝置和作業系統可能是一項艱鉅的挑戰。您必須充分了解您的裝置，才能解譯這些指示並將其套用至您的裝置。若遇到困難，您可嘗試使用其他裝置選項的一項作為替代選項，例如 [使用 Amazon EC2 建立虛擬裝置](creating-a-virtual-thing.md) 或 [使用您的 Windows 或 Linux PC 或 Mac 做為 AWS IoT 裝置](using-laptop-as-device.md)。

您需要配置 Raspberry Pi，使其可以啟動作業系統 (OS)，連接至網際網路，並可讓您在命令列介面與其互動。您也可以使用 Raspberry Pi 支援的圖形使用者介面 (GUI) 來開啟 AWS IoT 主控台並執行本教學課程的其餘部分。

**設定 Raspberry Pi**

1. 將 SD 卡插入 Raspberry Pi 上的 MicroSD 記憶卡插槽。有些 SD 卡會預先載入安裝管理員，在啟動主機板後提示您安裝作業系統的選單。您也可使用 Raspberry Pi 成像器，在卡上安裝作業系統。

1. 將 HDMI 電視或顯示器連接至連接到 Raspberry Pi 之 HDMI 連接埠的 HDMI 纜線。

1. 將鍵盤和滑鼠連接至 Raspberry Pi 的 USB 連接埠，然後插入電源整流器以啟動機板。

Raspberry Pi 啟動後，若 SD 卡預先載入安裝管理員，會出現一個安裝作業系統的選單。若於安裝作業系統時發生問題，您可試試下列步驟。如需有關 Raspberry Pi 的設定資訊，請參閱[設定 Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up/)。

**若您在設定 Raspberry Pi 時發生問題：**
+ 在啟動機板之前，請檢查是否已插入 SD 卡。若您在啟動機板後插入 SD 卡，則安裝選單可能不會顯示。
+ 確認電視或顯示器已開啟，且已選取正確的輸入。
+ 確保您正在使用 Raspberry Pi 相容的軟體。

安裝並設定 Raspberry Pi 作業系統之後，請開啟 Raspberry Pi 的 Web 瀏覽器，然後導覽至 AWS IoT Core 主控台以繼續本教學課程中的其餘步驟。

如果您可以開啟 AWS IoT Core 主控台，表示 Raspberry Pi 已準備就緒，您可以繼續 [教學課程：在 中佈建您的裝置 AWS IoT](shadow-provision-cloud.md)。

若您仍然無法解決問題或需要其他協助，請參閱[獲取有關 Raspberry Pi 的協助](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up/5)。

# 教學課程：在 中佈建您的裝置 AWS IoT
<a name="shadow-provision-cloud"></a>

本節會建立教學課程將使用 AWS IoT Core 的資源。

**Topics**
+ [步驟 1：建立 Device Shadow AWS IoT 的政策](#create-policy-shadow)
+ [步驟 2：建立物件資源並將政策連接至該物件](#create-thing-shadow)
+ [步驟 3：檢閱結果及後續步驟](#resources-shadow-review)

## 步驟 1：建立 Device Shadow AWS IoT 的政策
<a name="create-policy-shadow"></a>

X.509 憑證透過 AWS IoT Core. AWS IoT policies 驗證您的裝置，會連接到允許裝置執行 AWS IoT 操作的憑證，例如訂閱或發佈至 Device Shadow 服務所使用的 MQTT 預留主題。您的裝置會在連線並傳送訊息時提供其憑證 AWS IoT Core。

在此過程中，您將會建立一個政策，可讓您的裝置執行 AWS IoT 執行範例程式所需的作業。建議您建立政策，該政策僅授予執行任務所需的許可權。首先建立 AWS IoT 政策，然後將其連接到稍後建立的裝置憑證。

**建立 AWS IoT 政策**

1. 請在左側選單上選擇 **Secure** (安全)，然後選擇 **Policies** (政策)。若您的帳戶具有現有政策，請選擇 **Create** (建立)，否則，在 **You don’t have a policy yet** (您尚未設定政策) 頁面上，選擇 **Create a policy** (建立政策)。

1. 在 **Create policy** (建立政策) 頁面上：

   1. 在 **Name** (名稱) 欄位中，輸入政策的名稱 (例如，**My\$1Device\$1Shadow\$1policy**)。請勿在政策名稱中使用個人識別資訊。

   1. 在政策文件中，您說明了連線、訂閱、接收和發佈動作，這些動作授予裝置發佈和訂閱 MQTT 預留主題的許可權限。

      複製下列範例政策，並將其貼入您的政策文件中：`thingname` 將 取代為您建立的物件名稱 （例如 `My_light_bulb`)、`region`將 取代為您使用 服務的 AWS IoT 區域，並將 `account`取代為您的 AWS 帳戶 號碼。如需 AWS IoT 政策的詳細資訊，請參閱 [AWS IoT Core 政策](iot-policies.md)。  
****  

      ```
      {
          "Version":"2012-10-17",		 	 	 
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Publish"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/get",
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/update"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Receive"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/get/accepted",
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/get/rejected",
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/update/accepted",
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/update/rejected",
                      "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingname/shadow/update/delta"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Subscribe"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingname/shadow/get/accepted",
                      "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingname/shadow/get/rejected",
                      "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingname/shadow/update/accepted",
                      "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingname/shadow/update/rejected",
                      "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingname/shadow/update/delta"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": "iot:Connect",
                  "Resource": "arn:aws:iot:us-east-1:123456789012:client/test-*"
              }
          ]
      }
      ```

## 步驟 2：建立物件資源並將政策連接至該物件
<a name="create-thing-shadow"></a>

連線至 的裝置 AWS IoT 可由 AWS IoT 登錄檔中的*物件資源*表示。*物件資源*意指特定裝置或邏輯實體，例如本教學課程中的燈泡。

若要了解如何在 中建立物件 AWS IoT，請遵循中所述的步驟[建立物件](create-iot-resources.md#create-aws-thing)。當您依照該教學課程中的步驟時，請注意以下一些關鍵事項：

1. 選擇 **Create a single thing** (建立單一物件)，在 **Name** (名稱) 欄位中，輸入與您先前建立政策時指定之 `thingname` 相同的物件名稱 (例如，`My_light_bulb`)。

   物件類型建立之後，您就無法變更其名稱。若您給它一個不同於 `thingname` 的名字，請建立名為 `thingname` 的新物件並刪除舊物件。
**注意**  
請勿在物件名稱中使用個人識別資訊。物件名稱可以出現在未加密的通訊和報告中。

1. 我們建議您將**憑證已建立！**頁面上的每個憑證檔案下載至您可輕鬆找到的位置。您必須安裝這些檔案，才能執行範例應用程式。

   建議您將檔案下載至 Raspberry Pi `home` 目錄中的 `certs` 子目錄上，並使用如下表中所建議更簡單名稱對其進行命名。  
**憑證檔案名稱**    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/shadow-provision-cloud.html)

1. 啟用憑證以啟用連線後 AWS IoT，請選擇**連接政策**，並確保您將先前建立的政策 （例如，**My\$1Device\$1Shadow\$1policy**) 連接到物件。

   建立物件之後，您可以在 AWS IoT 主控台的物件清單中看到您的物件資源。

## 步驟 3：檢閱結果及後續步驟
<a name="resources-shadow-review"></a>

**在本教學課程中，您會了解如何：**
+ 設定及配置 Raspberry Pi 裝置。
+ 建立 AWS IoT 政策文件，授權您的裝置與 AWS IoT 服務互動。
+ 建立物件資源和相關聯的 X.509 裝置憑證，並將政策文件與其進行連接。

**後續步驟**  
您現在可以安裝適用於 Python AWS IoT 的裝置 SDK、執行`shadow.py`範例應用程式，並使用 Device Shadows 控制狀態。如需如何執行本教學課程的詳細資訊，請參閱 [教學課程：安裝裝置 SDK 並執行 Device Shadows 的範例應用程式](lightbulb-shadow-application.md)。

# 教學課程：安裝裝置 SDK 並執行 Device Shadows 的範例應用程式
<a name="lightbulb-shadow-application"></a>

本節說明如何安裝必要的軟體和適用於 Python 的 AWS IoT Device SDK，並執行`shadow.py`範例應用程式來編輯 Shadow 文件並控制影子的狀態。

**於本教學課程中，您會了解如何：**
+ 使用已安裝的軟體和適用於 Python 的 AWS IoT Device SDK 來執行範例應用程式。
+ 了解如何使用範例應用程式輸入值，以於 AWS IoT 主控台中發佈所需的值。
+ 檢閱 `shadow.py` 範例應用程式，及其如何使用 MQTT 通訊協定來更新影子的狀態。

**在您執行此教學課程之前：**  
您必須設定好 AWS 帳戶、設定 Raspberry Pi 裝置，並建立 AWS IoT 物件和政策，授予裝置發佈和訂閱 Device Shadow 服務 MQTT 預留主題的許可。如需詳細資訊，請參閱[教學課程：準備好 Raspberry Pi 來執行影子應用程式](create-resources-shadow.md)。

您還必須安裝 Git、Python 和適用於 Python 的 AWS IoT 裝置 SDK。本教學課程是以教學課程 [連接 Raspberry Pi 或其他裝置](connecting-to-existing-device.md) 中提出的概念為基礎。若您尚未嘗試該教學課程，建議您依照該教學課程中說明的步驟，安裝憑證檔案和裝置 SDK，然後回到本教學課程，執行 `shadow.py` 範例應用程式。

**Topics**
+ [步驟 1：執行 shadow.py 範例應用程式](#run-sample-application-shadows)
+ [步驟 2：檢閱 shadow.py 裝置 SDK 範例應用程式](#review-shadow-sample-code)
+ [步驟 3：使用 `shadow.py` 範例應用程式進行疑難排解](#shadow-sample-app-troubleshoot)
+ [步驟 4：檢閱結果及後續步驟](#sample-app-shadow-review)

此教學課程約需 20 分鐘方能完成。

## 步驟 1：執行 shadow.py 範例應用程式
<a name="run-sample-application-shadows"></a>

執行 `shadow.py` 範例應用程式之前，除了您所安裝之憑證檔案的名稱和位置之外，您還需要下列資訊。


**應用程式參數值**  

|  參數  |  可在哪裡找到值  | 
| --- | --- | 
| your-iot-thing-name |  您先前在 中建立的 AWS IoT 物件名稱[步驟 2：建立物件資源並將政策連接至該物件](shadow-provision-cloud.md#create-thing-shadow)。 如要尋找此值，請於 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)中，依序選擇 **Manage** (管理) 和 **Things** (物件)。  | 
| your-iot-endpoint |   *your-iot-endpoint* 值的格式為：`endpoint_id-ats.iot.region.amazonaws.com`，例如 `a3qj468EXAMPLE-ats.iot.us-west-2.amazonaws.com`。如要尋找此值： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/lightbulb-shadow-application.html)  | 

**安裝並執行範例應用程式**

1. 導覽至範例應用程式目錄。

   ```
   cd ~/aws-iot-device-sdk-python-v2/samples/service-clients
   ```

1. 在命令列視窗中，按照指示替換 *your-iot-endpoint* 和 *your-iot-thing-name* 並執行此命令。

   ```
   python3 shadow.py --ca_file ~/certs/Amazon-root-CA-1.pem --cert ~/certs/device.pem.crt --key ~/certs/private.pem.key --endpoint your-iot-endpoint --thing_name your-iot-thing-name
   ```

1. 觀察範例應用程式：

   1. 連線至您帳戶的 AWS IoT 服務。

   1. 訂閱 `Delta` 事件和 `Update` 與 `Get` 回應。

   1. 提示您在終端機終輸入所需的值。

   1. 顯示類似下列內容的輸出：

   ```
   Connecting to a3qEXAMPLEffp-ats.iot.us-west-2.amazonaws.com with client ID 'test-0c8ae2ff-cc87-49d2-a82a-ae7ba1d0ca5a'...
   Connected!
   Subscribing to Delta events...
   Subscribing to Update responses...
   Subscribing to Get responses...
   Requesting current shadow state...
   Launching thread to read user input...
   Finished getting initial shadow state.
   Shadow contains reported value 'off'.
   Enter desired value:
   ```

**注意**  
若在執行 `shadow.py` 範例應用程式時發生問題，請檢閱 [步驟 3：使用 `shadow.py` 範例應用程式進行疑難排解](#shadow-sample-app-troubleshoot)。如要取得可協助您修正問題的其他資訊，請將 `--verbosity debug` 參數新增至命令列，以便範例應用程式會顯示有關其正在執行之動作的詳細訊息。

**輸入值並觀察 Shadow 文件中的更新**  
您可於終端機中輸入值，以指定 `desired` 值，其還會更新 `reported` 值。假設您在終端機中輸入顏色 `yellow`。`reported` 值也會更新為顏色 `yellow`。下列展示顯示於終端機中的訊息：

```
Enter desired value:
yellow
Changed local shadow value to 'yellow'.
Updating reported shadow value to 'yellow'...
Update request published.
Finished updating reported shadow value to 'yellow'.
```

當您發佈此更新請求時， 會為物件資源 AWS IoT 建立預設、傳統陰影。您可以查看您建立之物件資源的 Shadow 文件 （例如，)，以觀察您發佈至 AWS IoT 主控台中 `reported`和 `desired`值的更新請求`My_light_bulb`。如要在 Shadow 文件中查看更新：

1. 在 AWS IoT 主控台中，選擇**管理**，然後選擇**實物**。

1. 在顯示的物件清單中，選取您所建立的物件，然後依序選擇 **Shadows** (影子) 和 **Classic Shadow** (經典影子)。

Shadow 文件應看似如下列內容，顯示設定為顏色 `yellow` 的 `reported` 和 `desired` 值。您會在文件的 **Shadow state** (影子狀態) 區段中看到這些值。

```
{
"desired": {
  "welcome": "aws-iot",
  "color": "yellow"
},
"reported": {
  "welcome": "aws-iot",
  "color": "yellow"
}
}
```

您還會看到 **Metadata** (中繼資料) 區段，其包含請求之時間戳記資訊和版本號碼。

您可以使用狀態文件版本，確認您正在更新的裝置影子文件為最新版本。若您傳送另一個更新請求，版本編號會遞增 1。當您為更新請求提供版本，若狀態文件的目前版本與提供的版本不符，則服務會拒絕請求並顯示 HTTP 409 衝突回應代碼。

```
{
"metadata": {
  "desired": {
    "welcome": {
      "timestamp": 1620156892
    },
    "color": {
      "timestamp": 1620156893
    }
  },
  "reported": {
    "welcome": {
      "timestamp": 1620156892
    },
    "color": {
      "timestamp": 1620156893
    }
  }
},
"version": 10
}
```

如要了解 Shadow 文件的更多資訊並觀察狀態資訊的變更，請繼續參閱說明於本教學課程 [步驟 4：檢閱結果及後續步驟](#sample-app-shadow-review) 部分中的下一個教學課程 [教學課程：使用範例應用程式和 MQTT 測試用戶端，與 Device Shadow 互動](interact-lights-device-shadows.md)。或者，您還可於下一節中了解 `shadow.py` 範本程式碼及其如何使用 MQTT 通訊協定。

## 步驟 2：檢閱 shadow.py 裝置 SDK 範例應用程式
<a name="review-shadow-sample-code"></a>

本節會從用於本教學課程中之**適用於 Python 的AWS IoT 裝置 SDK v2** 檢閱 `shadow.py` 範例應用程式。在這裡，我們將 AWS IoT Core 使用 MQTT 和透過 WSS 通訊協定的 MQTT 來檢閱它如何連接到 。[AWS 通用執行時間 (AWS-CRT)](https://github.com/awslabs/aws-crt-python#aws-crt-python) 程式庫提供低階通訊協定支援，並包含在適用於 Python 的 AWS IoT Device SDK v2 中。

雖然本教學課程透過 WSS 使用 MQTT 和 MQTT， AWS IoT 支援發佈 HTTPS 請求的裝置。有關從裝置傳送 HTTP 訊息的 Python 程式範例，請參閱使用 Python `requests` 程式庫的 [HTTPS 程式碼範例](http.md#codeexample)。

如需有關如何決定裝置通訊所使用之通訊協定的詳細資訊，請參閱 [選擇裝置通訊的應用程式通訊協定](protocols.md#protocol-selection)。

**MQTT**  
`shadow.py` 範例呼叫 [https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py](https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py) 中的 `mtls_from_path` (如圖所示)，使用 MQTT 通訊協定來建立與 AWS IoT Core 的連線。`mtls_from_path` 會使用 X.509 憑證和 TLS v1.2 來驗證裝置。 AWS CRT 程式庫會處理該連線的較低層級詳細資訊。

```
mqtt_connection = mqtt_connection_builder.mtls_from_path(
  endpoint=args.endpoint,
  cert_filepath=args.cert,
  pri_key_filepath=args.key,
  ca_filepath=args.ca_file,
  client_bootstrap=client_bootstrap,
  on_connection_interrupted=on_connection_interrupted,
  on_connection_resumed=on_connection_resumed,
  client_id=args.client_id,
  clean_session=False,
  keep_alive_secs=6
)
```
+ `endpoint` 是您從命令列傳入的 AWS IoT 端點，`client_id`也是 中唯一識別此裝置的 ID AWS 區域。
+ `cert_filepath`、`pri_key_filepath` 和 `ca_filepath` 是裝置憑證和私密金鑰檔案及根 CA 檔案的路徑。
+ `client_bootstrap` 是處理通訊端通訊活動的通用執行時間物件，並在呼叫 `mqtt_connection_builder.mtls_from_path` 之前實例化。
+ `on_connection_interrupted` 和 `on_connection_resumed` 是在裝置連線遭到中斷並回復時呼叫的回呼函數。
+ `clean_session` 為是否啟動一個新的、持續的工作階段，或若存在，則進行重新連線。`keep_alive_secs` 為 `CONNECT` 請求中傳送的保持活動值，以秒為單位。Ping 會在此時間間隔自動傳送。若在此值的 1.5 倍之後未收到 ping，則伺服器會假設連線中斷。

`shadow.py` 範例還會呼叫 [https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py](https://github.com/awslabs/aws-crt-python/blob/89207bcf1387177034e02fe29e8e469ca45e39b7/awscrt/awsiot_mqtt_connection_builder.py) 中的 `websockets_with_default_aws_signing`，使用透過 WSS 的 MQTT 通訊協定建立與 AWS IoT Core 的連線。透過 WSS 的 MQTT 還會使用與 MQTT 相同的參數，並採用下列其他參數：
+ `region` 是 Signature V4 身分驗證所使用的 AWS 簽署區域，`credentials_provider`也是提供用於身分驗證的 AWS 登入資料。從命令列傳入 Region (區域)，並在呼叫 `mqtt_connection_builder.websockets_with_default_aws_signing` 之前將 `credentials_provider` 物件實例化。
+ 若使用代理主機，`websocket_proxy_options` 是 HTTP 代理選項。在 `shadow.py` 範例應用程式中，此值在呼叫 `mqtt_connection_builder.websockets_with_default_aws_signing` 之前進行實例化。

**訂閱 Shadow 主題和事件**  
`shadow.py` 範例嘗試建立連線，並等待完整連線。若未連線，則指令會排入佇列中。連線後，範例會訂閱差異事件及更新和取得訊息，並發佈服務品質 (QoS) 層級為 1 (`mqtt.QoS.AT_LEAST_ONCE`) 的訊息。

當裝置訂閱 QoS 層級 1 的訊息時，訊息代理程式會儲存裝置訂閱的訊息，直至其可傳送至裝置為止。訊息代理程式會重新傳送訊息，直至收到來自裝置的 `PUBACK` 回應。

如需 MQTT 通訊協定的詳細資訊，請參閱 [檢閱 MQTT 通訊協定](sdk-tutorials.md#sdk-tutorials-mqtt-review) 和 [MQTT](mqtt.md)。

如需用於本教學課程中的 MQTT、透過 WSS 的 MQTT、持續性工作階段及 QoS 層級的詳細資訊，請參閱 [檢閱 pubsub.py 裝置 SDK 範例應用程式](sdk-tutorials.md#sdk-tutorials-explore-sample)。

## 步驟 3：使用 `shadow.py` 範例應用程式進行疑難排解
<a name="shadow-sample-app-troubleshoot"></a>

當您執行 `shadow.py` 範例應用程式時，您應會看到一些顯示於終端機中的訊息，並提示輸入 `desired` 值。若程式拋出錯誤，則如要偵錯，您可先檢查是否為您的系統執行正確的命令。

在某些狀況下，錯誤訊息可能表示連線問題，且看似如下：`Host name was invalid for dns resolution` 或 `Connection was closed unexpectedly`。於此狀況下，您可檢查下列事項：
+ 

**檢查命令中的端點的地址**  
請檢閱您為執行範例應用程式 (例如 `a3qEXAMPLEffp-ats.iot.us-west-2.amazonaws.com`) 輸入命令中的 `endpoint` 引數，並在 **AWS IoT 主控台** 中檢查此值。

  如要檢查您是否使用了正確的值：

  1. 於 **AWS IoT 主控台**中，依序選擇 **Manage** (管理) 和 **Things** (物件)。

  1. 選擇您為範例應用程式建立的物件 (例如，**My\$1light\$1bulb**)，然後選擇 **Interact** (互動)。

  在物件詳細資料頁面上，您的端點會顯示於 **HTTPS** 區段中。您應該也會看到訊息：`This thing already appears to be connected.`
+ 

**檢查憑證啟用**  
憑證會使用 驗證您的裝置 AWS IoT Core。

  如要檢查您的憑證是否為作用中：

  1. 於 **AWS IoT 主控台**中，依序選擇 **Manage (管理)** 和 **Things (物件)**。

  1. 選擇您為範例應用程式建立的物件 (例如，**My\$1light\$1bulb**)，然後選擇 **Security** (安全性)。

  1. 選取憑證，然後從憑證的詳細資料頁面選擇 Select the certificate (選取憑證)，然後從憑證的詳細資料頁面選擇 **Actions** (動作)。

  若於下拉式清單中 **Activate** (啟用) 無法使用，則您只能選擇 **Deactivate**(停用)，表示您的憑證處於作用中。若無，請選擇 **Activate** (啟用)，然後重新執行範例程式。

  若程式仍然無法執行，請檢查 `certs` 資料夾中的憑證檔案名稱。
+ 

**檢查連接至該物件資源的政策**  
當憑證驗證您的裝置時， AWS IoT 政策會允許裝置執行 AWS IoT 操作，例如訂閱或發佈至 MQTT 預留主題。

  如要檢查是否已連接正確的政策：

  1. 如先前所述尋找憑證，然後選擇 **Policies** (政策)。

  1. 選擇顯示的政策，並檢查其是否說明了授予裝置發佈和訂閱 MQTT 預留主題之許可權限的 `connect`、`subscribe`、`receive` 和 `publish` 動作。

     若是範例政策，請參閱 [步驟 1：建立 Device Shadow AWS IoT 的政策](shadow-provision-cloud.md#create-policy-shadow)。

  如果您看到錯誤訊息指出無法連線至 AWS IoT，這可能是因為您用於政策的許可。如果是這種情況，我們建議您從提供 AWS IoT 資源完整存取權的政策開始，然後重新執行範例程式。您可編輯目前的政策，或選擇目前的政策，選擇 **Detach** (分離)，然後建立另一個提供完整存取權的政策，並將其連接至您的物件資源。您可於稍後將政策限制為僅執行程式所需的動作和政策。  
****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:*"
            ],
            "Resource": "*"
        }
    ]
  }
  ```
+ 

**檢查您的裝置 SDK 安裝**  
若程式仍然無法執行，您可重新安裝裝置 SDK，以確定您的 SDK 安裝完整且正確。

## 步驟 4：檢閱結果及後續步驟
<a name="sample-app-shadow-review"></a>

**在本教學課程中，您會了解如何：**
+ 安裝必要的軟體、工具和適用於 Python 的 AWS IoT 裝置 SDK。
+ 了解範例應用程式 `shadow.py` 會如何使用 MQTT 通訊協定來擷取和更新影子的目前狀態。
+ 執行 Device Shadows 的範例應用程式，並在 AWS IoT 主控台中觀察 Shadow 文件的更新。您還會了解在執行程式時如何疑難排解任何問題並修復錯誤。

**後續步驟**  
您現在可以執行 `shadow.py` 範例應用程式，並使用 Device Shadows 來控制狀態。您可以觀察 AWS IoT 主控台中 Shadow 文件的更新，並觀察範例應用程式回應的差異事件。使用 MQTT 測試用戶端，您可以訂閱預留的影子主題，並在執行範例程式時觀察主題所收到的訊息。如需如何執行本教學課程的詳細資訊，請參閱 [教學課程：使用範例應用程式和 MQTT 測試用戶端，與 Device Shadow 互動](interact-lights-device-shadows.md)。

# 教學課程：使用範例應用程式和 MQTT 測試用戶端，與 Device Shadow 互動
<a name="interact-lights-device-shadows"></a>

如要與 `shadow.py` 範例應用程式產生互動，請為 `desired` 值在終端機中輸入一個值。例如，您可以指定類似流量燈的顏色，並 AWS IoT 回應請求並更新報告的值。

**於本教學課程中，您會了解如何：**
+ 使用 `shadow.py` 範例應用程式來指定所需的狀態並更新影子的目前狀態。
+ 編輯 Shadow 文件，以觀察差異事件，及 `shadow.py` 範例應用程序如何加以回應。
+ 使用 MQTT 測試用戶端訂閱影子主題，並在執行範例程式時觀察更新。

**執行本教學課程之前，您必須具備：**  
設定您的 AWS 帳戶、設定您的 Raspberry Pi 裝置，並建立 AWS IoT 物件和政策。您也必須安裝了必要的軟體、裝置 SDK、憑證檔案，並在終端機中執行範例程式。如需詳細資訊，請參閱先前的教學課程 [教學課程：準備好 Raspberry Pi 來執行影子應用程式](create-resources-shadow.md) 和 [步驟 1：執行 shadow.py 範例應用程式](lightbulb-shadow-application.md#run-sample-application-shadows)。若您尚未完成，您必須完成這些教學課程。

**Topics**
+ [步驟 1：使用 `shadow.py` 範例應用程式更新所需和回報的值](#update-desired-shadow-sample)
+ [步驟 2：檢視 MQTT 測試用戶端中 `shadow.py` 範例應用程式的訊息](#shadow-sample-view-msg)
+ [步驟 3：疑難排解 Device Shadow 互動的錯誤](#shadow-observe-messages-troubleshoot)
+ [步驟 4：檢閱結果及後續步驟](#sample-shadow-review)

此教學課程約需 45 分鐘方能完成。

## 步驟 1：使用 `shadow.py` 範例應用程式更新所需和回報的值
<a name="update-desired-shadow-sample"></a>

在先前的教學課程中[步驟 1：執行 shadow.py 範例應用程式](lightbulb-shadow-application.md#run-sample-application-shadows)，您已了解如何在輸入所需的值時，觀察在 AWS IoT 主控台中發佈至 Shadow 文件的訊息，如 一節所述[教學課程：安裝裝置 SDK 並執行 Device Shadows 的範例應用程式](lightbulb-shadow-application.md)。

在上一個範例中，我們將所需的顏色設定為 `yellow`。輸入每個值後，終端機會提示您輸入另一個 `desired` 值。若您再次輸入相同的值 (`yellow`)，應用程序會辨識這一點，並提示您輸入新的 `desired` 值。

```
Enter desired value:
yellow
Local value is already 'yellow'.
Enter desired value:
```

現在，假設您輸入顏色 `green`。 AWS IoT 會回應請求，並將`reported`值更新為 `green`。當 `desired` 狀態與 `reported` 狀態不同而造成差異時，這就是更新發生了。

**`shadow.py` 範例應用程式如何模擬 Device Shadow 互動：**

1. 於終端機中輸入 `desired` 值 (例如 `yellow`)以發布所需的狀態。

1. 由於 `desired` 狀態與 `reported` 狀態 (例如，顏色 `green`) 不同，就會發生差異，而訂閱差異的應用程式會收到這個訊息。

1. 應用程式會回應訊息，並將其狀態更新為 `desired` 值，`yellow`。

1. 然後，應用程式會發佈更新訊息，其包含裝置狀態 `yellow` 的新回報值。

下列展示了終端機中顯示的訊息，其中顯示如何發佈更新請求。

```
Enter desired value:
green
Changed local shadow value to 'green'.
Updating reported shadow value to 'green'...
Update request published.
Finished updating reported shadow value to 'green'.
```

在 AWS IoT 主控台中，Shadow 文件會將 `reported`和 `desired` 欄位`green`的更新值反映至 ，而版本編號會遞增 1。例如，若先前的版本編號顯示為 10，則目前的版本編號會顯示為 11。

**注意**  
刪除影子不會將版本編號重置為 0。當您發佈更新請求或建立另一個具相同名稱的陰子時，您會看到陰子版本會遞增 1。

**編輯 Shadow 文件以觀察差異事件**  
`shadow.py` 範例應用程式也會訂閱 `delta` 事件，並在 `desired` 值發生變更時加以回應。例如，您可將 `desired` 值變更為顏色 `red`。若要這樣做，請在 AWS IoT 主控台中，按一下編輯來**編輯** Shadow 文件，然後在 JSON `red` 中將`desired`值設定為 ，同時將`reported`值保留為 `green`。儲存變更之前，請讓 Raspberry Pi 上的終端機保持開啟狀態，因為您會在變更發生時看到顯示於終端機中的訊息。

```
{
"desired": {
  "welcome": "aws-iot",
  "color": "red"
},
"reported": {
  "welcome": "aws-iot",
  "color": "green"
}
}
```

儲存新值之後，`shadow.py` 範例應用程式會回應此變更，並在終端機中顯示指出差異的訊息。接著，您應該會在輸入 `desired` 值的提示下方看到下列訊息。

```
Enter desired value:
Received shadow delta event.
Delta reports that desired value is 'red'. Changing local value...
Changed local shadow value to 'red'.
Updating reported shadow value to 'red'...
Finished updating reported shadow value to 'red'.
Enter desired value:
Update request published.
Finished updating reported shadow value to 'red'.
```

## 步驟 2：檢視 MQTT 測試用戶端中 `shadow.py` 範例應用程式的訊息
<a name="shadow-sample-view-msg"></a>

您可使用 **AWS IoT 主控台**中的 **MQTT 測試用戶端**，監控傳入您 AWS 帳戶中的 MQTT 訊息。訂閱 Device Shadow 服務使用的預留 MQTT 主題，您可在執行範例應用程式時觀察主題收到的訊息。

若您尚未使用 MQTT 測試用戶端，您可檢閱 [使用 MQTT 用戶端檢視 AWS IoT MQTT 訊息](view-mqtt-messages.md)。這有助於您了解如何使用 **AWS IoT 主控台**中的 **MQTT 測試用戶端**，在 MQTT 訊息通過訊息代理程式時，檢視訊息。

1. 

**開啟 MQTT 測試用戶端**

   在新視窗開啟 [AWS IoT 主控台中的 MQTT 測試用戶端](https://console.aws.amazon.com//iot/home#/test)，如此您便可觀察 MQTT 主題所收到的訊息，不會遺失 MQTT 測試用戶端的組態。若您讓 MQTT 用戶端前往主控台中的另一個頁面，其不會保留任何訂閱或訊息記錄。在教學課程的本節中，您可以讓 AWS IoT 物件的 Shadow 文件和 MQTT 測試用戶端在不同的視窗中開啟，以更輕鬆地觀察與 Device Shadows 的互動。

1. 

**訂閱 MQTT 預留的 Shadow 主題**

   您可使用 MQTT 測試用戶端輸入 Device Shadow 之 MQTT 預留主題的名稱，並加以訂閱，以於執行 `shadow.py` 範例應用程式時收到更新。如要訂閱主題：

   1. 於 **AWS IoT 主控台**的 **MQTT 測試用戶端**中，選擇 **Subscribe to a topic** (訂閱主題)。

   1.  於 **主題篩選條件**區段中，輸入：**\$1aws/things/*thingname*/shadow/update/\$1**。在此，`thingname` 是您先前所建立物件資源的名稱 (例如，`My_light_bulb`)。

   1. 保留其他組態設定的預設值，然後選擇 **Subscribe** (訂閱)。

   在主題訂閱中使用 **\$1** 萬用字元，您可同時訂閱多個 MQTT 主題，並在單一視窗中觀察裝置及其 Shadow 間交換的所有訊息。如需萬用字元及其用法的詳細資訊，請參閱 [MQTT 主題](topics.md)。

1. 

**執行 `shadow.py` 範例程式並觀察訊息**

   在 Raspberry Pi 的命令列視窗中，若您已中斷程式的連線，請再次執行範例應用程式，並於 **AWS IoT 主控台**中查看 **MQTT 測試用戶端**中的訊息。

   1. 執行下列命令以重新啟動範例程式。將 *your-iot-thing-name* 和 *your-iot-endpoint* 取代為您先前建立的 AWS IoT 物件名稱 （例如 `My_light_bulb`)，以及要與裝置互動的端點。

      ```
      cd ~/aws-iot-device-sdk-python-v2/samples/service-clients
      python3 shadow.py --ca_file ~/certs/Amazon-root-CA-1.pem --cert ~/certs/device.pem.crt --key ~/certs/private.pem.key --endpoint your-iot-endpoint --thing_name your-iot-thing-name
      ```

      `shadow.py` 範例應用程式接著會執行並擷取目前的影子狀態。若您已刪除影子或清除目前狀態，該程式會將目前值設定為 `off`，然後提示您輸入 `desired` 值。

      ```
      Connecting to a3qEXAMPLEffp-ats.iot.us-west-2.amazonaws.com with client ID 'test-0c8ae2ff-cc87-49d2-a82a-ae7ba1d0ca5a'...
      Connected!
      Subscribing to Delta events...
      Subscribing to Update responses...
      Subscribing to Get responses...
      Requesting current shadow state...
      Launching thread to read user input...
      Finished getting initial shadow state.
      Shadow document lacks 'color' property. Setting defaults...
      Changed local shadow value to 'off'.
      Updating reported shadow value to 'off'...
      Update request published.
      Finished updating reported shadow value to 'off'...
      Enter desired value:
      ```

      另一方面，若程式正在執行，而您將其重新啟動，則會在終端機中看到回報的最新顏色值。於 MQTT 測試用戶端中，您會看到主題更新 **\$1aws/things/*thingname*/shadow/get** 和 **\$1aws/things/*thingname*/shadow/get/accepted**。

      假設回報的最新顏色是 `green`。下列會顯示 **\$1aws/things/*thingname*/shadow/get/accepted** JSON 檔案的內容。

      ```
      {
      "state": {
        "desired": {
          "welcome": "aws-iot",
          "color": "green"
        },
        "reported": {
          "welcome": "aws-iot",
          "color": "green"
        }
      },
      "metadata": {
        "desired": {
          "welcome": {
            "timestamp": 1620156892
          },
          "color": {
            "timestamp": 1620161643
          }
        },
        "reported": {
          "welcome": {
            "timestamp": 1620156892
          },
          "color": {
            "timestamp": 1620161643
          }
        }
      },
      "version": 10,
      "timestamp": 1620173908
      }
      ```

   1. 於終端機中輸入 `desired` 值，例如 `yellow`。`shadow.py` 範例應用程式會在終端機中回應並顯示下列訊息，顯示 `reported` 值變更為 `yellow`。

      ```
      Enter desired value:
      yellow
      Changed local shadow value to 'yellow'.
      Updating reported shadow value to 'yellow'...
      Update request published.
      Finished updating reported shadow value to 'yellow'.
      ```

      於 **AWS IoT console (主控台)** **MQTT test client (MQTT 測試用戶端)** 的 **Subscriptions (訂閱)** 之下，您會看到下列主題收到訊息：
      + **\$1aws/things/*thingname*/shadow/update**：顯示 `desired` 和 `updated` 值會變更為顏色 `yellow`。
      + **\$1aws/things/*thingname*/shadow/update/accepted**：顯示 `desired` 和 `reported` 狀態的目前值及其中繼資料和版本資訊。
      + **\$1aws/things/*thingname*/shadow/update/documents**：顯示 `desired` 和 `reported` 狀態的先前的值和目前值及其中繼資料和版本資訊。

      作為文件 **\$1aws/things/*thingname*/shadow/update/documents** 也包含其他兩個主題中所包含的資訊，我們可以加以檢閱以查看狀態資訊。先前狀態會顯示設定為 `green` 的回報值、其中繼資料和版本資訊，目前狀態會顯示已更新為 `yellow` 的回報值。

      ```
      {
      "previous": {
        "state": {
          "desired": {
            "welcome": "aws-iot",
            "color": "green"
          },
          "reported": {
            "welcome": "aws-iot",
            "color": "green"
          }
        },
        "metadata": {
          "desired": {
            "welcome": {
              "timestamp": 1617297888
            },
            "color": {
              "timestamp": 1617297898
            }
          },
          "reported": {
            "welcome": {
              "timestamp": 1617297888
            },
            "color": {
              "timestamp": 1617297898
            }
          }
        },
        "version": 10
      },
      "current": {
        "state": {
          "desired": {
            "welcome": "aws-iot",
            "color": "yellow"
          },
          "reported": {
            "welcome": "aws-iot",
            "color": "yellow"
          }
        },
        "metadata": {
          "desired": {
            "welcome": {
              "timestamp": 1617297888
            },
            "color": {
              "timestamp": 1617297904
            }
          },
          "reported": {
            "welcome": {
              "timestamp": 1617297888
            },
            "color": {
              "timestamp": 1617297904
            }
          }
        },
        "version": 11
      },
      "timestamp": 1617297904
      }
      ```

   1. 若您現在輸入另一個 `desired` 值，您會看到這些主題所收到 `reported` 值和訊息更新的進一步變更。版本編號也會遞增 1。例如，若您輸入值 `green` 時，先前的狀態會報告值 `yellow`，而目前狀態會報告值 `green`。

1. 

**編輯 Shadow 文件以觀察差異事件**

   如要觀察差異主題的變更，請編輯 AWS IoT 主控台中的 Shadow 文件。例如，您可將 `desired` 值變更為顏色 `red`。若要這樣做，請在 AWS IoT 主控台中選擇**編輯**，然後在 JSON 中將`desired`值設定為紅色，同時將`reported`值設定為 `green`。儲存變更之前，請保持終端開啟狀態，因為您會在終端機中看到回報的差異訊息。

   ```
   {
   "desired": {
     "welcome": "aws-iot",
     "color": "red"
   },
   "reported": {
     "welcome": "aws-iot",
     "color": "green"
   }
   }
   ```

   `shadow.py` 範例應用程式會回應此變更，並在終端機中顯示指出差異的訊息。於 MQTT 測試用戶端中，`update` 主題將會收到一則訊息，顯示 `desired` 和 `reported` 值的變更。

   您也會看到主題 **\$1aws/things/*thingname*/shadow/update/delta** 收到訊息。如要查看訊息，請選擇此列於 **Subscriptions** (訂閱) 之下的主題。

   ```
   {
   "version": 13,
   "timestamp": 1617318480,
   "state": {
     "color": "red"
   },
   "metadata": {
     "color": {
       "timestamp": 1617318480
     }
   }
   }
   ```

## 步驟 3：疑難排解 Device Shadow 互動的錯誤
<a name="shadow-observe-messages-troubleshoot"></a>

當您執行 Shadow 範例應用程式時，您可能會遇到觀察與 Device Shadow 服務互動的問題。

若程式執行成功，並提示您輸入 `desired` 值，您應可使用如前所述的 Shadow 文件和 MQTT 測試用戶端，觀察 Device Shadow 互動。不過，若您無法看到互動，您可檢查下列事項：
+ 

**在 AWS IoT 主控台中檢查物件名稱及其影子**  
若您看不到 Shadow 文件中的訊息，請檢閱命令，並確定其符合 **AWS IoT 主控台**中的物件名稱。您還可依序選擇您的物件名稱和 **Shadows** (影子)，來檢查您是否有經典影子。本教學課程主要著重於與經典影子的互動。

   您也可以確認您使用的裝置已連線至網際網路。於 **AWS IoT 主控台**中，選擇您先前建立的物件，然後選擇 **Interact** (互動)。在物件詳細資訊頁面上，您應會在此處看到訊息：`This thing already appears to be connected.`
+ 

**查看您訂閱的 MQTT 預留主題**  
若您在 MQTT 測試用戶端中看不到訊息，請檢查您訂閱的主題是否已正確格式化。MQTT Device Shadow 主題具有格式 **\$1aws/things/*thingname*/shadow/**，且可能具有 `update`、`get` 或 `delete`，根據您希望對影子執行的動作而定。本教學課程使用主題 **\$1aws/things/*thingname*/shadow/\$1**，因此，在訂閱測試用戶端之**主題篩選條件**部分中的主題時，請確定您已正確輸入。

  當您輸入主題名稱時，請確定 *thingname* 與您先前建立的 AWS IoT 物件名稱相同。您還可訂閱其他 MQTT 主題，查看是否已成功執行更新。例如，您可訂閱主題 **\$1aws/things/*thingname*/shadow/update/rejected**，在更新請求失敗時收到訊息，您便可對連線問題進行偵錯。如需有關預留主題的更多資訊，請參閱 [影子主題](reserved-topics.md#reserved-topics-shadow) 和 [Device Shadow MQTT 主題](device-shadow-mqtt.md)。

## 步驟 4：檢閱結果及後續步驟
<a name="sample-shadow-review"></a>

**在本教學課程中，您會了解如何：**
+ 使用 `shadow.py` 範例應用程式來指定所需的狀態並更新影子的目前狀態。
+ 編輯 Shadow 文件，以觀察差異事件，及 `shadow.py` 範例應用程序如何加以回應。
+ 使用 MQTT 測試用戶端訂閱影子主題，並在執行範例程式時觀察更新。

**後續步驟**  
您可訂閱其他 MQTT 預留主題，以觀察影子應用程式的更新。例如，若您僅訂閱主題 **\$1aws/things/*thingname*/shadow/update/accepted**，則更新成功執行時，您只會看到目前的狀態資訊。

您也可訂閱其他影子主題來偵錯問題，或深入了解 Device Shadow 互動，還可對 Device Shadow 互動的任何問題進行偵錯。如需詳細資訊，請參閱 [影子主題](reserved-topics.md#reserved-topics-shadow) 及 [Device Shadow MQTT 主題](device-shadow-mqtt.md)。

您也可選擇使用命名影子或使用為 LED 與 Raspberry Pi 連接其他的硬體擴展您的應用程式，並使用從終端傳送訊息來觀察其狀態的變化。

如需有關 Device Shadows 服務及在裝置、應用程式和服務中使用服務的詳細資訊，請參閱 [AWS IoT Device Shadow 服務](iot-device-shadows.md)、[在裝置中使用影子](device-shadow-comms-device.md) and [在應用程式和服務中使用影子](device-shadow-comms-app.md)。

# 教學課程：為 建立自訂授權方 AWS IoT Core
<a name="custom-auth-tutorial"></a>

本教學課程示範使用 AWS CLI來建立、驗證和使用自訂身分驗證的步驟。使用本教學課程，您可以選擇性地使用 Postman，藉由使用 HTTP 發佈 API 將資料傳送到 AWS IoT Core 。

本教學課程會向您展示如何建立範例 Lambda 函數，搭配使用 **create-authorizer** 呼叫與啟用的字符簽署，以實作授權和身分驗證邏輯，以及自訂授權方。然後，使用 驗證授權方**test-invoke-authorizer**，最後您可以使用 HTTP 發佈 API AWS IoT Core 將資料傳送至測試 MQTT 主題。範例請求將使用 `x-amz-customauthorizer-name` 標頭指定要叫用的授權方，並以請求標頭傳遞 token-key-name 和 `x-amz-customauthorizer-signature`。

**您會在本教學課程中學到什麼：**
+ 如何將 Lambda 函數建立為自訂授權方處理常式
+ 如何使用啟用字符簽署 AWS CLI 的 建立自訂授權方
+ 如何使用 **test-invoke-authorizer** 命令測試您的自訂授權方
+ 如何使用[Postman](https://www.postman.com/) 發佈 MQTT 主題，以及如何利用您的自訂授權方驗證請求

此教學課程約需 60 分鐘方能完成。

**Topics**
+ [步驟 1：為自訂授權方建立 Lambda 函數](#custom-auth-tutorial-define)
+ [步驟 2：為自訂授權方建立公有和私有金鑰對](#custom-auth-tutorial-keys)
+ [步驟 3：建立自訂授權方資源及其授權](#custom-auth-tutorial-authorizer)
+ [步驟 4：呼叫 test-invoke-authorizer 來測試授權方](#custom-auth-tutorial-test)
+ [步驟 5：使用 Postman 測試發佈 MQTT 郵件](#custom-auth-tutorial-postman)
+ [步驟 6：在 MQTT 測試用戶端中檢視訊息](#custom-auth-tutorial-testclient)
+ [步驟 7：檢閱結果及後續步驟](#custom-auth-tutorial-review)
+ [步驟 8：清理](#custom-auth-tutorial-cleanup)

**開始本教學課程之前，請確定您有：**
+ 

**[設定 AWS 帳戶](setting-up.md)**  
您需要 AWS 帳戶 和 AWS IoT 主控台才能完成本教學課程。

  當您用於本教學課程的帳戶至少包含這些 AWS 受管政策時，此帳戶最適用：
  + [https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/IAMFullAccess$jsonEditor](https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/IAMFullAccess$jsonEditor)
  + [https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/AWSIoTFullAccess$jsonEditor](https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/AWSIoTFullAccess$jsonEditor)
  + [https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/AWSLambda_FullAccess$jsonEditor](https://console.aws.amazon.com//iam/home#/policies/arn:aws:iam::aws:policy/AWSLambda_FullAccess$jsonEditor)
**重要**  
本教學課程中使用的 IAM 政策比您應該在生產實作中遵循的 IAM 政策更寬鬆。在生產環境中，請確定您的帳戶和資源策略僅授予必要的許可。  
當您建立 IAM 政策進行生產時，請判斷使用者和角色需要哪些存取權，然後設計政策，讓使用者只能執行這些任務。  
如需詳細資訊，請參閱 [IAM 中的安全最佳實務](https://docs.aws.amazon.com//IAM/latest/UserGuide/best-practices.html)。
+ 

**已安裝 AWS CLI**  
如需如何安裝 的資訊 AWS CLI，請參閱[安裝 AWS CLI](https://docs.aws.amazon.com//cli/latest/userguide/cli-chap-install.html)。本教學課程需要 AWS CLI 版本 `aws-cli/2.1.3 Python/3.7.4 Darwin/18.7.0 exe/x86_64` 或更新版本。
+ 

**OpenSSL 工具**  
本教學課程中的範例使用 [LibreSSL 2.6.5](https://www.libressl.org/)。您也可以針對本教學課程使用 [OpenSSL v1.1.1i](https://www.openssl.org/) 工具。
+ 

**檢閱了 [AWS Lambda](https://docs.aws.amazon.com//lambda/latest/dg/welcome.html) 概觀**  
如果您 AWS Lambda 未曾使用過 ，請檢閱[AWS Lambda](https://docs.aws.amazon.com//lambda/latest/dg/welcome.html)和[開始使用 Lambda](https://docs.aws.amazon.com//lambda/latest/dg/getting-started.html) 以了解其術語和概念。
+ 

**已檢閱如何在 Postman 中建置請求**  
如需詳細資訊，請參閱[建置請求](https://learning.postman.com/docs/sending-requests/requests/)。
+ 

**已從前一個教學課程中移除自訂授權方**  
您的 一次 AWS 帳戶 只能設定有限數量的自訂授權方。如需如何移除自訂授權方的相關資訊，請參閱 [步驟 8：清理](#custom-auth-tutorial-cleanup)。

## 步驟 1：為自訂授權方建立 Lambda 函數
<a name="custom-auth-tutorial-define"></a>

中的自訂身分驗證 AWS IoT Core 會使用您建立的授權[方資源](https://docs.aws.amazon.com//iot/latest/apireference/API_AuthorizerDescription.html)來驗證和授權用戶端。您將在本節中建立的 函數會在用戶端連線至 和存取 AWS IoT 資源時，進行身分驗證 AWS IoT Core 和授權。

Lambda 函數會執行下列動作：
+ 如果請求來自 **test-invoke-authorizer**，它會傳回一個具有 `Deny` 動作的 IAM 政策。
+ 如果請求來自使用 HTTP 的 Postman，且 `actionToken` 參數的值為 `allow`，則會傳回具有 `Allow`動作的 IAM 政策。否則，它會傳回一個具有 `Deny` 動作的 IAM 政策。

**若要為自訂授權方建立 Lambda 函數**

1. 在 [Lambda](https://console.aws.amazon.com//lambda/home#) 主控台中，開啟 [Functions](https://console.aws.amazon.com//lambda/home#/functions) (函數)。

1. 選擇**建立函數**。

1. 確認已選取 **Author from scratch** (從頭開始撰寫)。

1. 在 **Basic information** (基本資訊) 下：

   1. 在**函數名稱** 中，輸入 **custom-auth-function**。

   1. 在**執行期**中，確認 **Node.js 18.x** 

1. 選擇**建立函數**。

   Lambda 會建立 Node.js 函數和許可函數上傳記錄的[執行角色](https://docs.aws.amazon.com//lambda/latest/dg/lambda-intro-execution-role.html)。當您叫用函數時，Lambda 函數會擔任執行角色，並使用執行角色來建立 AWS SDK 的登入資料，以及從事件來源讀取資料。

1. 若要在 [AWS Cloud9](https://docs.aws.amazon.com/cloud9/latest/user-guide/welcome.html) 編輯器中查看函數的程式碼和組態，請在設計工具視窗中選擇 **custom-auth-function**，然後在編輯器的導覽窗格中選擇 **index.js**。

   對於指令碼語言 (例如 Node.js)，Lambda 包含可傳回成功回應的基本函數。您可以使用 [AWS Cloud9](https://docs.aws.amazon.com/cloud9/latest/user-guide/welcome.html) 編輯器來編輯函數，只要原始碼不超過 3 MB。

1. 將編輯器中的 **index.js** 程式碼取代為下列程式碼：

   ```
   // A simple Lambda function for an authorizer. It demonstrates
   // How to parse a CLI and Http password to generate a response.
   
   export const handler = async (event, context, callback) => {
   
       //Http parameter to initiate allow/deny request
       const HTTP_PARAM_NAME='actionToken';
       const ALLOW_ACTION = 'Allow';
       const DENY_ACTION = 'Deny';
   
       //Event data passed to Lambda function
       var event_str = JSON.stringify(event);
       console.log('Complete event :'+ event_str);
   
       //Read protocolData from the event json passed to Lambda function
       var protocolData = event.protocolData;
       console.log('protocolData value---> ' + protocolData);
   
       //Get the dynamic account ID from function's ARN to be used
       // as full resource for IAM policy
       var ACCOUNT_ID = context.invokedFunctionArn.split(":")[4];
       console.log("ACCOUNT_ID---"+ACCOUNT_ID);
   
       //Get the dynamic region from function's ARN to be used
       // as full resource for IAM policy
       var REGION = context.invokedFunctionArn.split(":")[3];
       console.log("REGION---"+REGION);
   
       //protocolData data will be undefined if testing is done via CLI.
       // This will help to test the set up.
       if (protocolData === undefined) {
   
           //If CLI testing, pass deny action as this is for testing purpose only.
           console.log('Using the test-invoke-authorizer cli for testing only');
           callback(null, generateAuthResponse(DENY_ACTION,ACCOUNT_ID,REGION));
   
       } else{
   
           //Http Testing from Postman
           //Get the query string from the request
           var queryString = event.protocolData.http.queryString;
           console.log('queryString values -- ' + queryString);
           /*         global URLSearchParams       */
           const params = new URLSearchParams(queryString);
           var action = params.get(HTTP_PARAM_NAME);
   
           if(action!=null && action.toLowerCase() === 'allow'){
   
               callback(null, generateAuthResponse(ALLOW_ACTION,ACCOUNT_ID,REGION));
   
           }else{
   
               callback(null, generateAuthResponse(DENY_ACTION,ACCOUNT_ID,REGION));
   
           }
   
       }
   
   };
   
   // Helper function to generate the authorization IAM response.
   var generateAuthResponse = function(effect,ACCOUNT_ID,REGION) {
   
       var full_resource = "arn:aws:iot:"+ REGION + ":" + ACCOUNT_ID + ":*";
       console.log("full_resource---"+full_resource);
   
       var authResponse = {};
       authResponse.isAuthenticated = true;
       authResponse.principalId = 'principalId';
   
       var policyDocument = {};
       policyDocument.Version = '2012-10-17';		 	 	 
       policyDocument.Statement = [];
       var statement = {};
       statement.Action = 'iot:*';
       statement.Effect = effect;
       statement.Resource = full_resource;
       policyDocument.Statement[0] = statement;
       authResponse.policyDocuments = [policyDocument];
       authResponse.disconnectAfterInSeconds = 3600;
       authResponse.refreshAfterInSeconds = 600;
   
       console.log('custom auth policy function called from http');
       console.log('authResponse --> ' + JSON.stringify(authResponse));
       console.log(authResponse.policyDocuments[0]);
   
       return authResponse;
   }
   ```

1. 選擇**部署**。

1. 在 **Changes deployed** (已部署變更) 出現在編輯器上方之後：

   1. 捲動至編輯器上方的 **Function overview** (函數概觀) 區段。

   1. 複製 **Function ARN** (函數 ARN) 並加以儲存，以便稍後可在本教學課程中使用。

1. 測試您的函數

   1. 選擇 **Test** (測試) 標籤。

   1. 使用預設測試設定，選擇 **Invoke** (叫用)。

   1. 如果測試成功，請在 **Execution results** (執行結果) 下，開啟 **Details** (詳細資訊) 檢視。您應該會看到函數傳回的政策文件。

      如果測試失敗或看不到政策文件，請檢閱程式碼以尋找並更正錯誤。

## 步驟 2：為自訂授權方建立公有和私有金鑰對
<a name="custom-auth-tutorial-keys"></a>

您的自訂授權方需要公有和私有金鑰來驗證它。本節中的命令會使用 OpenSSL 工具來建立此金鑰對。

**若要為自訂授權方建立公有和私有金鑰對**

1. 建立私有金鑰檔案。

   ```
   openssl genrsa -out private-key.pem 4096
   ```

1. 驗證您剛建立的私有金鑰檔案。

   ```
   openssl rsa -check -in private-key.pem -noout
   ```

   如果命令未顯示任何錯誤，則私有金鑰檔案是有效的。

1. 建立公有金鑰檔案。

   ```
   openssl rsa -in private-key.pem -pubout -out public-key.pem
   ```

1. 驗證公有金鑰檔案。

   ```
   openssl pkey -inform PEM -pubin -in public-key.pem -noout
   ```

   如果命令未顯示任何錯誤，則公有金鑰檔案是有效的。

## 步驟 3：建立自訂授權方資源及其授權
<a name="custom-auth-tutorial-authorizer"></a>

 AWS IoT 自訂授權方是將先前步驟中建立的所有元素連結在一起的資源。在本節中，您將建立自訂授權方資源，並許可其執行您先前建立的 Lambda 函數。您可以使用 AWS IoT 主控台 AWS CLI、 或 AWS API 來建立自訂授權方資源。

在本教學課程中，您只需建立一個自訂授權方即可。本節說明如何使用 AWS IoT 主控台和 建立 AWS CLI，讓您可以使用最方便的方法。由任一種方法建立的自訂授權方資源之間沒有任何差異。

### 建立自訂授權方資源
<a name="custom-auth-tutorial-authorizer-resource"></a>

**選擇其中一個選項，來建立自訂授權方資源**
+ [使用 AWS IoT 主控台建立自訂授權方](#create-custom-auth-in-console)
+ [使用 AWS CLI建立自訂授權方](#create-custom-auth-in-cli)

**若要建立自訂授權方 (主控台)**

1. 開啟[AWS IoT 主控台的自訂授權方頁面](https://console.aws.amazon.com//iot/home#/authorizerhub)，然後選擇**建立授權方**。

1. 在**建立授權方**中：

   1. 在**授權方名稱**中，輸入 **my-new-authorizer**。

   1. 在**授權方狀態**中，勾選**作用中**。

   1. 在 **Authorizer function** (授權方函數) 中，選擇您先前建立的 Lambda 函數。

   1. 在 **Token validation - optional** (字符驗證 - 選用) 中：

      1. 切換**字符驗證**。

      1. 在**符記金鑰名稱**中，輸入 **tokenKeyName**。

      1. 選擇 **Add key (新增金鑰)**。

      1. 在**金鑰名稱**中，輸入 **FirstKey**。

      1. 在**公有金鑰**中，輸入 `public-key.pem` 檔案的內容。務必包括檔案中具有 `-----BEGIN PUBLIC KEY-----` 和 `-----END PUBLIC KEY-----` 的字行，並且不得從檔案內容中新增或刪除任何換行字元、歸位字元或其他字元。您輸入的字串應該看起來與這個範例相似。

         ```
         -----BEGIN PUBLIC KEY-----
         MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvEBzOk4vhN+3LgslvEWt
         sLCqNmt5Damas3bmiTRvq2gjRJ6KXGTGQChqArAJwL1a9dkS9+maaXC3vc6xzx9z
         QPu/vQOe5tyzz1MsKdmtFGxMqQ3qjEXAMPLEOmqyUKPP5mff58k6ePSfXAnzBH0q
         lg2HioefrpU5OSAnpuRAjYKofKjbc2Vrn6N2G7hV+IfTBvCElf0csalS/Rk4phD5
         oa4Y0GHISRnevypg5C8n9Rrz91PWGqP6M/q5DNJJXjMyleG92hQgu1N696bn5Dw8
         FhedszFa6b2x6xrItZFzewNQkPMLMFhNrQIIyvshtT/F1LVCS5+v8AQ8UGGDfZmv
         QeqAMAF7WgagDMXcfgKSVU8yid2sIm56qsCLMvD2Sq8Lgzpey9N5ON1o1Cvldwvc
         KrJJtgwW6hVqRGuShnownLpgG86M6neZ5sRMbVNZO8OzcobLngJ0Ibw9KkcUdklW
         gvZ6HEJqBY2XE70iEXAMPLETPHzhqvK6Ei1HGxpHsXx6BNft582J1VpgYjXha8oa
         /NN7l7Zbj/euAb41IVtmX8JrD9z613d1iM5L8HluJlUzn62Q+VeNV2tdA7MfPfMC
         8btGYladFAnitThaz6+F0VSBJPu7pZQoLnqyEp5zLMtF+kFl2yOBmGAP0RBivRd9
         JWBUCG0bqcLQPeQyjbXSOfUCAwEAAQ==
         -----END PUBLIC KEY-----
         ```

1. 選擇 **Create Authorizer** (建立授權方)。

1. 如果已建立自訂授權方資源，您會看到自訂授權方的清單，而且您的新自訂授權方應該會出現在清單中，接著您可以繼續進行下一節來測試它。

   如果您看到錯誤，請檢閱錯誤並嘗試重新建立您的自訂授權方，然後再次檢查這些項目。請注意，每個自訂授權方資源都必須具有唯一名稱。

**建立自訂授權方 (AWS CLI)**

1. 將您的值替代為 `authorizer-function-arn` 和 `token-signing-public-keys`，然後執行下列命令：

   ```
   aws iot create-authorizer \
   --authorizer-name "my-new-authorizer" \
   --token-key-name "tokenKeyName" \
   --status ACTIVE \
   --no-signing-disabled \
   --authorizer-function-arn "arn:aws:lambda:Region:57EXAMPLE833:function:custom-auth-function" \
   --token-signing-public-keys FirstKey="-----BEGIN PUBLIC KEY-----
   MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvEBzOk4vhN+3LgslvEWt
   sLCqNmt5Damas3bmiTRvq2gjRJ6KXGTGQChqArAJwL1a9dkS9+maaXC3vc6xzx9z
   QPu/vQOe5tyzz1MsKdmtFGxMqQ3qjEXAMPLEOmqyUKPP5mff58k6ePSfXAnzBH0q
   lg2HioefrpU5OSAnpuRAjYKofKjbc2Vrn6N2G7hV+IfTBvCElf0csalS/Rk4phD5
   oa4Y0GHISRnevypg5C8n9Rrz91PWGqP6M/q5DNJJXjMyleG92hQgu1N696bn5Dw8
   FhedszFa6b2x6xrItZFzewNQkPMLMFhNrQIIyvshtT/F1LVCS5+v8AQ8UGGDfZmv
   QeqAMAF7WgagDMXcfgKSVU8yid2sIm56qsCLMvD2Sq8Lgzpey9N5ON1o1Cvldwvc
   KrJJtgwW6hVqRGuShnownLpgG86M6neZ5sRMbVNZO8OzcobLngJ0Ibw9KkcUdklW
   gvZ6HEJqBY2XE70iEXAMPLETPHzhqvK6Ei1HGxpHsXx6BNft582J1VpgYjXha8oa
   /NN7l7Zbj/euAb41IVtmX8JrD9z613d1iM5L8HluJlUzn62Q+VeNV2tdA7MfPfMC
   8btGYladFAnitThaz6+F0VSBJPu7pZQoLnqyEp5zLMtF+kFl2yOBmGAP0RBivRd9
   JWBUCG0bqcLQPeQyjbXSOfUCAwEAAQ==
   -----END PUBLIC KEY-----"
   ```

**其中：**
   + `authorizer-function-arn` 值是您為自訂授權方建立之 Lambda 函數的 Amazon 資源名稱 (ARN)。
   +  `token-signing-public-keys` 值包含金鑰的名稱 **FirstKey** 以及 `public-key.pem` 檔案的內容 務必包括檔案中具有 `-----BEGIN PUBLIC KEY-----` 和 `-----END PUBLIC KEY-----` 的字行，並且不得從檔案內容中新增或刪除任何換行字元、歸位字元或其他字元。

     注意：輸入公有金鑰時要小心，因為公有金鑰值若有任何更改，都會使其無法使用。

1. 如果已建立自訂授權方，則命令會傳回新資源的名稱和 ARN，如下所示。

   ```
   {
       "authorizerName": "my-new-authorizer",
       "authorizerArn": "arn:aws:iot:Region:57EXAMPLE833:authorizer/my-new-authorizer"
   }
   ```

   儲存 `authorizerArn` 值，以便用於下一個步驟。

   記住，每個自訂授權方資源都必須具有唯一名稱。

### 授權自訂授權方資源
<a name="custom-auth-tutorial-authorizer-permission"></a>

在本節中，您將許可您剛建立的自訂授權方資源執行 Lambda 函數。若要授與許可，您可以使用 [add-permission](https://docs.aws.amazon.com//cli/latest/reference/lambda/add-permission.html) CLI 命令。

**使用 將許可授予 Lambda 函數 AWS CLI**

1. 在插入您的值之後，輸入以下命令。請注意，`statement-id` 值必須是唯一的。如果您之前已執行本教學課程，或者如果您得到 `ResourceConflictException` 錯誤，請將 `Id-1234` 取代為另一個值，。

   ```
   aws lambda add-permission  \
   --function-name "custom-auth-function" \
   --principal "iot.amazonaws.com" \
   --action "lambda:InvokeFunction" \
   --statement-id "Id-1234" \
   --source-arn authorizerArn
   ```

1. 如果命令成功，它會傳回許可陳述式，例如此範例。您可以繼續下一節來測試自訂授權方。

   ```
   {
       "Statement": "{\"Sid\":\"Id-1234\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"iot.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:Region:57EXAMPLE833:function:custom-auth-function\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:lambda:Region:57EXAMPLE833:function:custom-auth-function\"}}}"
   }
   ```

   如果命令未成功，它會傳回錯誤，例如此範例。您必須先檢閱並更正錯誤，然後才能繼續進行。

   ```
   An error occurred (AccessDeniedException) when calling the AddPermission operation: User: arn:aws:iam::57EXAMPLE833:user/EXAMPLE-1 is not authorized to perform: lambda:AddPer
   mission on resource: arn:aws:lambda:Region:57EXAMPLE833:function:custom-auth-function
   ```

## 步驟 4：呼叫 test-invoke-authorizer 來測試授權方
<a name="custom-auth-tutorial-test"></a>

定義所有資源後，在本節中，您會從命令列呼叫 test-invoke-authorizer 來測試授權是否通過。

請注意，從命令列叫用授權方時，未定義 `protocolData`，因此授權方將一律傳回 DENY 文件。不過，此測試會確認您的自訂授權方和 Lambda 函數已正確設定，即使它未完全測試 Lambda 函數也一樣。

**使用 測試您的自訂授權方及其 Lambda 函數 AWS CLI**

1. 在具有您在前一個步驟中建立之 `private-key.pem` 檔案的目錄中，執行下列命令。

   ```
   echo -n "tokenKeyValue" | openssl dgst -sha256 -sign private-key.pem | openssl base64 -A
   ```

   此命令會建立要在下一個步驟中使用的簽章字串。簽章字串看起來像這樣：

   ```
   dBwykzlb+fo+JmSGdwoGr8dyC2qB/IyLefJJr+rbCvmu9Jl4KHAA9DG+V+MMWu09YSA86+64Y3Gt4tOykpZqn9mn
   VB1wyxp+0bDZh8hmqUAUH3fwi3fPjBvCa4cwNuLQNqBZzbCvsluv7i2IMjEg+CPY0zrWt1jr9BikgGPDxWkjaeeh
   bQHHTo357TegKs9pP30Uf4TrxypNmFswA5k7QIc01n4bIyRTm90OyZ94R4bdJsHNig1JePgnuOBvMGCEFE09jGjj
   szEHfgAUAQIWXiVGQj16BU1xKpTGSiTAwheLKUjITOEXAMPLECK3aHKYKY+d1vTvdthKtYHBq8MjhzJ0kggbt29V
   QJCb8RilN/P5+vcVniSXWPplyB5jkYs9UvG08REoy64AtizfUhvSul/r/F3VV8ITtQp3aXiUtcspACi6ca+tsDuX
   f3LzCwQQF/YSUy02u5XkWn+sto6KCkpNlkD0wU8gl3+kOzxrthnQ8gEajd5Iylx230iqcXo3osjPha7JDyWM5o+K
   EWckTe91I1mokDr5sJ4JXixvnJTVSx1li49IalW4en1DAkc1a0s2U2UNm236EXAMPLELotyh7h+flFeloZlAWQFH
   xRlXsPqiVKS1ZIUClaZWprh/orDJplpiWfBgBIOgokJIDGP9gwhXIIk7zWrGmWpMK9o=
   ```

   複製此簽章字串以在下一個步驟中使用。請小心不要包含任何額外的字元或留下任何額外的字元。

1. 在此命令中，會將 `token-signature` 值取代為來自前一個步驟的簽章字串，並執行此命令來測試您的授權方。

   ```
   aws iot test-invoke-authorizer \
   --authorizer-name my-new-authorizer \
   --token tokenKeyValue \
   --token-signature dBwykzlb+fo+JmSGdwoGr8dyC2qB/IyLefJJr+rbCvmu9Jl4KHAA9DG+V+MMWu09YSA86+64Y3Gt4tOykpZqn9mnVB1wyxp+0bDZh8hmqUAUH3fwi3fPjBvCa4cwNuLQNqBZzbCvsluv7i2IMjEg+CPY0zrWt1jr9BikgGPDxWkjaeehbQHHTo357TegKs9pP30Uf4TrxypNmFswA5k7QIc01n4bIyRTm90OyZ94R4bdJsHNig1JePgnuOBvMGCEFE09jGjjszEHfgAUAQIWXiVGQj16BU1xKpTGSiTAwheLKUjITOEXAMPLECK3aHKYKY+d1vTvdthKtYHBq8MjhzJ0kggbt29VQJCb8RilN/P5+vcVniSXWPplyB5jkYs9UvG08REoy64AtizfUhvSul/r/F3VV8ITtQp3aXiUtcspACi6ca+tsDuXf3LzCwQQF/YSUy02u5XkWn+sto6KCkpNlkD0wU8gl3+kOzxrthnQ8gEajd5Iylx230iqcXo3osjPha7JDyWM5o+KEWckTe91I1mokDr5sJ4JXixvnJTVSx1li49IalW4en1DAkc1a0s2U2UNm236EXAMPLELotyh7h+flFeloZlAWQFHxRlXsPqiVKS1ZIUClaZWprh/orDJplpiWfBgBIOgokJIDGP9gwhXIIk7zWrGmWpMK9o=
   ```

   如果命令成功，它會傳回自訂授權方函數產生的資訊，例如此範例。

   ```
   {
       "isAuthenticated": true,
       "principalId": "principalId",
       "policyDocuments": [
           "{\"Version\":\"2012-10-17\",		 	 	 \"Statement\":[{\"Action\":\"iot:*\",\"Effect\":\"Deny\",\"Resource\":\"arn:aws:iot:Region:57EXAMPLE833:*\"}]}"
       ],
       "refreshAfterInSeconds": 600,
       "disconnectAfterInSeconds": 3600
   }
   ```

   如果指令傳回錯誤，請檢閱錯誤並再次檢查您在本節中使用的命令。

## 步驟 5：使用 Postman 測試發佈 MQTT 郵件
<a name="custom-auth-tutorial-postman"></a>

1. 若要從命令列取得您的裝置資料端點，請呼叫 [describe-endpoint](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iot/describe-endpoint.html)，如這裡所示

   ```
   aws iot describe-endpoint --output text --endpoint-type iot:Data-ATS
   ```

   儲存此地址，以在稍後步驟中用作 *device\$1data\$1endpoint\$1address*。

1. 開啟新的 Postman 視窗並建立新的 HTTP POST 請求。

   1. 從您的電腦中，開啟 Postman 應用程式。

   1. 在 Postman 的 **File** (檔案) 選單中，選擇 **New** (新增)。

   1. 在 **New** (新增) 對話方塊中，選擇 **Request** (請求)。

   1. 在儲存請求中，

      1. 在 **Request name** (請求名稱) 中，輸入 **Custom authorizer test request**。

      1. 在 **Select a collection or folder to save to:** (選取要儲存到哪個集合或資料夾：) 中，選擇或建立要儲存此請求的集合。

      1. 選擇 ** Save to *collection\$1name*** (儲存至 collection\$1name)。

1. 建立 POST 請求來測試您的自訂授權方。

   1. 在 URL 欄位旁邊的請求方法選取器中，選擇 **POST**。

   1. 在 URL 欄位中，為您的請求建立 URL，方法為搭配使用下列 URL 與來自前一個步驟中 [describe-endpoint](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iot/describe-endpoint.html) 命令的 *device\$1data\$1endpoint\$1address*。

      ```
      https://device_data_endpoint_address:443/topics/test/cust-auth/topic?qos=0&actionToken=allow
      ```

      請注意，此 URL 包含 `actionToken=allow` 查詢參數，它會告訴您的 Lambda 函數傳回允許存取 AWS IoT的政策文件。輸入 URL 之後，查詢參數也會出現在 Postman 的 **Params** (參數) 標籤中。

   1. 在 **Auth** (身分驗證) 標籤的 **Type** (類型) 欄位中，選擇 **No Auth** (無需身分驗證)。

   1. 在標頭標籤中：

      1. 如果有已核取的 **Host** (主機) 金鑰，請取消核取此金鑰。

      1. 在標頭清單的底部，加入這些新標題並確認已核取它們。將 **Host** 值取代為您的 *device\$1data\$1endpoint\$1address*，並將 **x-amz-customauthorizer-signature** 值取代為在上節中與 **test-invoke-authorize** 命令搭配使用的簽章字串。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/custom-auth-tutorial.html)

   1. 在主體標籤中：

      1. 在資料格式選項方塊中，選擇 **Raw** (原始)。

      1. 在資料類型清單中，選擇 **JavaScript**。

      1. 在文字欄位中，為您的測試訊息輸入此 JSON 訊息承載：

         ```
         {
             "data_mode": "test",
             "vibration": 200,
             "temperature": 40
         }
         ```

1. 選擇 **Send** (傳送) 以傳送請求。

   如果請求成功，它會傳回：

   ```
   {
       "message": "OK",
       "traceId": "ff35c33f-409a-ea90-b06f-fbEXAMPLE25c"
   }
   ```

   成功回應表示您的自訂授權方允許與 的連線， AWS IoT 且測試訊息已交付至 中的代理程式 AWS IoT Core。

   如果傳回錯誤，請檢閱錯誤訊息、*device\$1data\$1endpoint\$1address*、簽章字串，以及其他標頭值。

請將此請求保留在 Postman，以供下一節使用。

## 步驟 6：在 MQTT 測試用戶端中檢視訊息
<a name="custom-auth-tutorial-testclient"></a>

在上一個步驟中，您使用 Postman AWS IoT 將模擬裝置訊息傳送到 。成功回應指出您的自訂授權方允許連線到 AWS IoT ，並指出測試訊息已傳遞至 AWS IoT Core中的代理程式。在本節中，您將使用 AWS IoT 主控台中的 MQTT 測試用戶端，以其他裝置和服務可能的方式查看該訊息的訊息內容。

**若要查看自訂授權方所授權的測試訊息**

1. 在 AWS IoT 主控台中，開啟 [MQTT 測試用戶端](https://console.aws.amazon.com//iot/home#/test)。

1. 在 **Subscribe to topic** (訂閱主題) 標籤的 **Topic filter** (主題篩選條件) 中，輸入 **test/cust-auth/topic**，這是前一節的 Postman 範例中使用的訊息主題。

1. 選擇 **Subscribe (訂閱)**。

   保留此視窗讓後續步驟可以看到它。

1. 在 Postman 中，於您為前一節建立的請求中，選擇 **Send** (傳送)。

   檢閱回應以確定回應成功。如果未成功，請依照前一節所述疑難排解錯誤。

1. 在 **MQTT test client** (MQTT 測試用戶端) 中，您應該會看到顯示訊息主題的新項目，而且如果展開的話，則會看到您從 Postman 傳送之請求中的訊息承載。

   如果未在 **MQTT test client** (MQTT 測試用戶端) 中看到您的訊息，以下是一些需要檢查的事項：
   + 確定您的 Postman 請求成功傳回。如果 AWS IoT 拒絕連線並傳回錯誤，則請求中的訊息不會傳遞給訊息中介裝置。
   + 確定 AWS 區域 用來開啟 AWS IoT 主控台的 AWS 帳戶 和 與您在 Postman URL 中使用的 相同。
   + 請確定您使用自訂授權方的適當端點。預設 IoT 端點可能不支援搭配 Lambda 函數使用自訂授權方。反之，您可以使用網域組態來定義新的端點，然後為自訂授權方指定該端點。
   + 確定您已在 **MQTT test client** (MQTT 測試用戶端) 中正確地輸入主題。主題篩選條件會區分大小寫。如有疑問，您也可以訂閱 主題，該**\$1**主題會訂閱傳遞訊息代理程式 AWS 帳戶 並 AWS 區域 用來開啟 AWS IoT 主控台的所有 MQTT 訊息。

## 步驟 7：檢閱結果及後續步驟
<a name="custom-auth-tutorial-review"></a>

**在本教學課程中：**
+ 已將 Lambda 函數建立為自訂授權方處理常式
+ 已在啟用字符簽署的情況下建立自訂授權方
+ 已使用 **test-invoke-authorizer** 命令測試您的自訂授權方
+ 已使用 [Postman](https://www.postman.com/) 發佈 MQTT 主題，並利用您的自訂授權方驗證請求
+ 已使用 **MQTT test client** (MQTT 測試用戶端) 來檢視從 Postman 測試傳送的訊息

**後續步驟**  
從 Postman 傳送一些訊息以驗證自訂授權方是否正在運作之後，請試驗看看變更本教學課程的不同層面如何影響結果。以下是幾個入門範例。
+ 變更簽章字串，以便查看未經授權之連線嘗試的處理方式不再有效。您應該會得到錯誤回應 (例如這個錯誤回應)，且訊息應該不會出現在 **MQTT test client** (MQTT 測試用戶端) 中。

  ```
  {
      "message": "Forbidden",
      "traceId": "15969756-a4a4-917c-b47a-5433e25b1356"
  }
  ```
+ 若要進一步了解如何尋找在您開發和使用 AWS IoT 規則時可能發生的錯誤，請參閱 [監控 AWS IoT](monitoring_overview.md)。

## 步驟 8：清理
<a name="custom-auth-tutorial-cleanup"></a>

如果想要重複本教學課程，您可能需要移除某些自訂授權方。您的 AWS 帳戶 一次只能設定有限數量的自訂授權方，而且當您嘗試新增新的授權方而不移除現有的自訂授權方`LimitExceededException`時，您可以取得 。

**移除自訂授權方 (主控台)**

1. 開啟[AWS IoT 主控台的自訂授權方頁面](https://console.aws.amazon.com//iot/home#/authorizerhub)，然後在自訂授權方清單中，尋找要移除的自訂授權方。

1. 開啟自訂授權方詳細資訊頁面，然後從 **Actions** (動作) 選單中，選擇 **Edit** (編輯)。

1. 取消核取 **Activate authorizer** (啟用授權方)，然後選擇 **Update** (更新)。

   在自訂授權方作用中時，您無法將其刪除。

1. 從自訂授權方詳細資訊頁面中，開啟 **Actions** (動作) 選單，然後選擇 **Delete** (刪除)。

**移除自訂授權方 (AWS CLI)**

1. 列出您已安裝的自訂授權方，並找出要刪除的自訂授權方名稱。

   ```
   aws iot list-authorizers 
   ```

1. 在將 `Custom_Auth_Name` 取代為要刪除之自訂授權方的 `authorizerName` 之後，執行此命令將自訂授權方設定為 `inactive`。

   ```
   aws iot update-authorizer --status INACTIVE --authorizer-name Custom_Auth_Name
   ```

1. 在將 `Custom_Auth_Name` 取代為要刪除之自訂授權方的 `authorizerName` 之後，執行此命令來刪除自訂授權方。

   ```
   aws iot delete-authorizer --authorizer-name Custom_Auth_Name
   ```

# 教學課程：使用 AWS IoT 和 Raspberry Pi 監控土壤濕度
<a name="iot-moisture-tutorial"></a>

本教學課程說明如何使用 [Raspberry Pi](https://www.raspberrypi.org/)、濕度感應器，以及 AWS IoT 監控房屋植物或花園的土壤濕度等級。Raspberry Pi 會執行程式碼，從感應器讀取濕度等級和溫度，然後將資料傳送至其中 AWS IoT。您可以在 中建立規則 AWS IoT ，當濕度等級低於閾值時，將電子郵件傳送至訂閱 Amazon SNS 主題的地址。

**注意**  
本教學課程可能不是最新版本。自本主題最初發佈後，某些參考文件可能已被取代。

**Contents**
+ [先決條件](#iot-moisture-prereqs)
+ [設定 AWS IoT](iot-moisture-setup.md)
  + [步驟 1：建立 AWS IoT 政策](iot-moisture-policy.md)
  + [步驟 2：建立 AWS IoT 物件、憑證和私有金鑰](iot-moisture-create-thing.md)
  + [步驟 3：建立 Amazon SNS 主題與訂閱。](iot-moisture-create-sns-topic.md)
  + [步驟 4：建立 AWS IoT 規則來傳送電子郵件](iot-moisture-create-rule.md)
+ [設定您的 Raspberry Pi 和濕度感應器](iot-moisture-raspi-setup.md)

## 先決條件
<a name="iot-moisture-prereqs"></a>

為完成此教學課程您需要：
+  AWS 帳戶。
+ 擁有管理員許可的 IAM 使用者。
+ 執行 Windows、macOS、Linux 或 Unix 的開發電腦，用來存取 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)。
+ 執行最新 [Raspberry Pi 作業系統的 Raspberry Pi 3B 或 4B](https://www.raspberrypi.com/products/)。 [https://www.raspberrypi.com/software/operating-systems/](https://www.raspberrypi.com/software/operating-systems/)如需安裝說明，請參閱 Raspberry Pi 網站上的[安裝作業系統](https://www.raspberrypi.com/documentation/computers/getting-started.html#installing-the-operating-system)。
+ 用於 Raspberry Pi 的顯示器、鍵盤、滑鼠和 Wi-Fi 網路或乙太網路連線。
+ 與 Raspberry Pi 相容的濕度感應器。本教學課程中使用的感應器是 [Adafure STEMMA I2C 電容式濕度感應器](https://www.adafruit.com/product/4026)搭配 [JST 4 針腳轉母頭插槽纜線接頭](https://www.adafruit.com/product/3950)。

# 設定 AWS IoT
<a name="iot-moisture-setup"></a>

若要完成此教學課程，您需要建立以下資源。若要將裝置連線至 AWS IoT，您可以建立 IoT 物件、裝置憑證和 AWS IoT 政策。
+ 實 AWS IoT 物。

  代表實體裝置 (在此案例中為 Rasberry Pi)，包含有關裝置的靜態中繼資料的物件。
+ 裝置憑證。

  所有裝置都必須擁有裝置憑證，才能連接至並向 AWS IoT驗證。
+  AWS IoT 政策。

  每個裝置憑證都有一或多個與其相關聯的 AWS IoT 政策。這些政策會決定裝置可存取 AWS IoT 的資源。
+  AWS IoT 根 CA 憑證。

  裝置和其他用戶端使用 AWS IoT 根 CA 憑證來驗證與其通訊的 AWS IoT 伺服器。如需詳細資訊，請參閱[伺服器驗證](server-authentication.md)。
+  AWS IoT 規則。

  規則包含查詢和一或多個規則動作。查詢會從裝置訊息擷取資料，以判斷是否應處理訊息資料。規則動作會指定當資料符合查詢時要採取的動作。
+ Amazon SNS 主題和主題訂閱。

  此規則會監聽來自 Raspberry Pi 的濕度資料。如果值低於閾值，它會將訊息傳送到 Amazon SNS 主題。Amazon SNS 會將該訊息傳送到訂閱該主題的所有電子郵件地址。

 



# 步驟 1：建立 AWS IoT 政策
<a name="iot-moisture-policy"></a>

建立允許 Raspberry Pi 連線和傳送訊息 AWS IoT 的政策 AWS IoT。

1. 在 [AWS IoT 主控台](https://console.aws.amazon.com/iot)中，如果 **Get started (開始)** 按鈕出現，請選擇它。否則，請在導覽窗格中展開 **Security** (安全性)，然後選擇 **Policies (政策)**。

1. 如果 **You don’t have any policies yet (您尚未有任何政策)** 對話方塊出現，請選擇 **Create a policy (建立政策)**。否則，請選擇 **Create** (建立)。

1. 輸入 AWS IoT 政策的名稱 （例如 **MoistureSensorPolicy**)。

1. 在 **Add statements (新增陳述式)** 區段中，將現有政策取代為下列 JSON。以您的 和 AWS 帳戶 號碼取代*區域* AWS 區域 和*帳戶*。  
****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": "iot:Connect",
               "Resource": "arn:aws:iot:us-east-1:123456789012:client/RaspberryPi"
           },
           {
               "Effect": "Allow",
               "Action": "iot:Publish",
               "Resource": [
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/update",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/delete",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/get"
               ]
           },
           {
               "Effect": "Allow",
               "Action": "iot:Receive",
               "Resource": [
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/update/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/delete/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/get/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/update/rejected",
                   "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/RaspberryPi/shadow/delete/rejected"
               ]
           },
           {
               "Effect": "Allow",
               "Action": "iot:Subscribe",
               "Resource": [
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/RaspberryPi/shadow/update/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/RaspberryPi/shadow/delete/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/RaspberryPi/shadow/get/accepted",
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/RaspberryPi/shadow/update/rejected",
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/RaspberryPi/shadow/delete/rejected"
               ]
           },
           {
               "Effect": "Allow",
               "Action": [
                   "iot:GetThingShadow",
                   "iot:UpdateThingShadow",
                   "iot:DeleteThingShadow"
               ],
               "Resource": "arn:aws:iot:us-east-1:123456789012:thing/RaspberryPi"
           }
       ]
   }
   ```

1. 選擇**建立**。

# 步驟 2：建立 AWS IoT 物件、憑證和私有金鑰
<a name="iot-moisture-create-thing"></a>

在 AWS IoT 登錄檔中建立物件以代表您的 Raspberry Pi。

1. 在 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)的導覽窗格中，依序選擇 **Manage** (管理) 和 **Things** (物件)。

1. 如果顯示 **You don’t have any things yet (尚無任何物件)** 對話方塊，請選擇 **Register a thing (註冊物件)**。否則，請選擇 **Create** (建立)。

1. 在**建立 AWS IoT 物件**頁面上，選擇**建立單一物件**。

1. 在 **Add your device to the device registry (將裝置新增至裝置登錄檔)** 頁面上，輸入您 IoT 物件的名稱 (例如 **RaspberryPi**)，然後選擇 **Next (下一步)**。您無法在建立之後變更物件的名稱。要變更物件的名稱，您必須建立一個新的物件並為它命名，然後刪除舊的物件。

1. 在 **Add a certificate for your thing (新增物件的憑證)** 頁面上，選擇 **Create certificate (建立憑證)**。

1. 選擇 **Download (下載)** 連結來下載憑證、私有金鑰和根憑證授權機構憑證。
**重要**  
這是您可以下載憑證和私有金鑰唯一機會。

1. 若要啟用憑證，請選擇 **Activate** (啟用)。憑證必須作用中，裝置才能連接到 AWS IoT。

1. 選擇 **Attach a policy (連接政策)**。

1. 針對 **Add a policy for your thing (新增您的物件的政策)**，選擇 **MoistureSensorPolicy**，然後選擇 **Register Thing (註冊物件)**。

# 步驟 3：建立 Amazon SNS 主題與訂閱。
<a name="iot-moisture-create-sns-topic"></a>

建立 Amazon SNS 主題與訂閱。

1. 在 [AWS SNS 主控台](https://console.aws.amazon.com/sns/home)的導覽窗格中，選擇 **Topics** (主題)，然後選擇 **Create topic** (建立主題)。

1. 選擇類型為**標準**，然後輸入主題的名稱 （例如 **MoistureSensorTopic**)。

1. 輸入主題的顯示名稱 (例如，**Moisture Sensor Topic**)。這是在 Amazon SNS 主控台中針對您的主題顯示的名稱。

1. 請選擇**建立主題**。

1. 在 Amazon SNS 主題詳細資訊頁面中，選擇 **Create subscription** (建立訂閱)。

1. 對於**通訊協定**，選擇**電子郵件**。

1. 針對 **Endpoint (端點)**，輸入電子郵件地址。

1. 選擇**建立訂閱**。

1. 開啟您的電子郵件用戶端，並尋找主旨為 **MoistureSensorTopic** 的訊息。開啟電子郵件，然後按一下 **Confirm subscription (確認訂閱)** 連結。
**重要**  
在您確認訂閱之前，不會收到來自此 Amazon SNS 主題的任何電子郵件提醒。

您應該會收到含有您輸入文字的電子郵件訊息。

# 步驟 4：建立 AWS IoT 規則來傳送電子郵件
<a name="iot-moisture-create-rule"></a>

 AWS IoT 規則會定義查詢，以及從裝置接收訊息時要採取的一或多個動作。 AWS IoT 規則引擎會監聽裝置傳送的訊息，並使用訊息中的資料來判斷是否應採取一些動作。如需詳細資訊，請參閱[的規則 AWS IoT](iot-rules.md)。

在此教學中，您的 Raspberry Pi 會將訊息發佈至 `aws/things/RaspberryPi/shadow/update`。這是內部的 MQTT 主題，供裝置和物件影子服務使用。Raspberry Pi 發佈的訊息會具備以下格式：

```
{
    "reported": {
        "moisture" : moisture-reading,
        "temp" : temperature-reading
    }
}
```

您會建立一個查詢，從傳入的訊息擷取濕度和溫度資料。您也可以建立 Amazon SNS 動作，在濕度讀數低於閾值時，取得資料並將其傳送給 Amazon SNS 主題訂閱者。

**建立 Amazon SNS 規則**

1. 在 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)中，選擇**訊息路由**，然後選擇**規則**。如果 **You don’t have any rules yet (您尚未有任何規則)** 對話方塊出現，請選擇 **Create a rule (建立規則)**。否則，請選擇**建立規則**。

1. 在**規則屬性**頁面中，輸入**規則名稱**，例如 **MoistureSensorRule**，並提供簡短的**規則描述**，例如 **Sends an alert when soil moisture level readings are too low**。

1. 選擇**下一步**並設定 SQL 陳述式。選擇 **SQL 版本**為 **2016-03-23**，然後輸入下列 AWS IoT SQL 查詢陳述式：

   ```
   SELECT * FROM '$aws/things/RaspberryPi/shadow/update/accepted' WHERE state.reported.moisture < 400
   ```

   此陳述式會在 `moisture` 讀數小於 `400` 時觸發規則動作。
**注意**  
您可能需要使用不同的值。在 Raspberry Pi 上執行程式碼之後，您可以碰觸感應器、將其放入水中或將其放入花盆中，以查看從感應器取得的值。

1. 選擇**下一步**並連接規則動作。針對**動作 1**，選擇**簡易通知服務**。此規則動作的描述是**傳送訊息做為 SNS 推送通知**。

1. 針對 **SNS 主題**，選擇您在 [步驟 3：建立 Amazon SNS 主題與訂閱。](iot-moisture-create-sns-topic.md)、**MoistureSensorTopic** 中建立的主題，並將**訊息格式**保留為 **RAW**。對於 **IAM 角色**，選擇**建立新角色**。輸入角色的名稱，例如 **LowMoistureTopicRole**，然後選擇**建立角色**。

1. 選擇**下一步**以檢閱，然後選擇**建立**以建立規則。

# 設定您的 Raspberry Pi 和濕度感應器
<a name="iot-moisture-raspi-setup"></a>



將 microSD 卡插入 Raspberry Pi，連接顯示器、鍵盤、滑鼠，以及乙太網路纜線 (如果未使用 Wi-Fi)。還不要連接電源線。

將 JST 跳線纜線連接至濕度感應器。跳線的另一端有四條配線：
+ 綠色：I2C SCL
+ 白色：I2C SDA
+ 紅色：功率 (3.5 V)
+ 黑色：接地

按住 Raspberry Pi 與右側的乙太網路插孔。在此方向中，上方有兩列 GPIO 接腳。按照以下順序，將濕度感應器的配線連接到接腳的下方排。從最左側接腳開始，連接紅色 (電源)、白色 (SDA) 和綠色 (SCL)。略過一個接腳，然後連接黑色 (接地) 配線。如需詳細資訊，請參閱 [Python Computer Wiring](https://learn.adafruit.com/adafruit-stemma-soil-sensor-i2c-capacitive-moisture-sensor/python-circuitpython-test)。

將電源線連接至 Raspberry Pi，並將另一端插入牆上插座，以將其開啟。

**設定您的 Raspberry Pi**

1. 在 **Welcome to Raspberry Pi (歡迎使用 Raspberry Pi)** 上，選擇 **Next (下一步)**。

1. 選擇您的國家/地區、語言、時區及鍵盤配置。選擇 **Next** (下一步)。

1. 輸入您的 Raspberry Pi 的密碼，然後選擇 **Next (下一步)**。

1. 選擇您的 Wi-Fi 網路，然後選擇 **Next (下一步)**。如果您不是使用 Wi-Fi 網路，請選擇 **Skip (略過)**。

1. 選擇 **Next (下一步)** 以檢查軟體更新。更新完成時，選擇 **Restart (重新啟動)** 以重新啟動您的 Raspberry Pi。

在 Raspberry Pi 啟動後，啟用 I2C 介面。

1. 在 Raspbian 桌面的左上角，按一下 Raspberry 圖示，選擇 **Preferences (偏好設定)**，然後選擇 **Raspberry Pi Configuration (Raspberry Pi 組態)**。

1. 在 **Interfaces (介面)** 標籤上，針對 **I2C**，選擇 **Enable (啟用)**。

1. 選擇**確定**。

Adafruit STEMMA 濕度感應器的程式庫是針對 CircuitPython 所撰寫。若要在 Raspberry Pi 上執行它們，您需要安裝最新版本的 Python 3。

1. 從命令提示執行下列命令，以更新您的 Raspberry Pi 軟體：

   `sudo apt-get update`

   `sudo apt-get upgrade`

1. 執行以下命令來更新您的 Python 3 安裝：

   `sudo pip3 install --upgrade setuptools`

1. 執行以下命令來安裝 Raspberry Pi GPIO 程式庫：

   `pip3 install RPI.GPIO`

1. 執行以下命令來安裝 Adafruit Blinka 程式庫：

   `pip3 install adafruit-blinka`

   如需詳細資訊，請參閱 [在 Raspberry Pi 上安裝 CircuitPython 程式庫](https://learn.adafruit.com/circuitpython-on-raspberrypi-linux/installing-circuitpython-on-raspberry-pi)。

1. 執行以下命令來安裝 Adafruit Seesaw 程式庫：

   `sudo pip3 install adafruit-circuitpython-seesaw`

1. 執行下列命令來安裝適用於 Python 的 AWS IoT 裝置 SDK：

   `pip3 install AWSIoTPythonSDK`

您的 Raspberry Pi 現在擁有所有必要的程式庫。建立名為 **moistureSensor.py** 的檔案，並將下列 Python 程式碼複製到檔案：

```
from adafruit_seesaw.seesaw import Seesaw
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
from board import SCL, SDA

import logging
import time
import json
import argparse
import busio

# Shadow JSON schema:
#
# {
#   "state": {
#       "desired":{
#           "moisture":<INT VALUE>,
#           "temp":<INT VALUE>            
#       }
#   }
# }

# Function called when a shadow is updated
def customShadowCallback_Update(payload, responseStatus, token):

    # Display status and data from update request
    if responseStatus == "timeout":
        print("Update request " + token + " time out!")

    if responseStatus == "accepted":
        payloadDict = json.loads(payload)
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Update request with token: " + token + " accepted!")
        print("moisture: " + str(payloadDict["state"]["reported"]["moisture"]))
        print("temperature: " + str(payloadDict["state"]["reported"]["temp"]))
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
        print("Update request " + token + " rejected!")

# Function called when a shadow is deleted
def customShadowCallback_Delete(payload, responseStatus, token):

     # Display status and data from delete request
    if responseStatus == "timeout":
        print("Delete request " + token + " time out!")

    if responseStatus == "accepted":
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Delete request with token: " + token + " accepted!")
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
        print("Delete request " + token + " rejected!")


# Read in command-line parameters
def parseArgs():

    parser = argparse.ArgumentParser()
    parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your device data endpoint")
    parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path")
    parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path")
    parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path")
    parser.add_argument("-p", "--port", action="store", dest="port", type=int, help="Port number override")
    parser.add_argument("-n", "--thingName", action="store", dest="thingName", default="Bot", help="Targeted thing name")
    parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicShadowUpdater", help="Targeted client id")

    args = parser.parse_args()
    return args


# Configure logging
# AWSIoTMQTTShadowClient writes data to the log
def configureLogging():

    logger = logging.getLogger("AWSIoTPythonSDK.core")
    logger.setLevel(logging.DEBUG)
    streamHandler = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    streamHandler.setFormatter(formatter)
    logger.addHandler(streamHandler)


# Parse command line arguments
args = parseArgs()

if not args.certificatePath or not args.privateKeyPath:
    parser.error("Missing credentials for authentication.")
    exit(2)

# If no --port argument is passed, default to 8883
if not args.port: 
    args.port = 8883


# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = None
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(args.clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(args.host, args.port)
myAWSIoTMQTTShadowClient.configureCredentials(args.rootCAPath, args.privateKeyPath, args.certificatePath)

# AWSIoTMQTTShadowClient connection configuration
myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Initialize Raspberry Pi's I2C interface
i2c_bus = busio.I2C(SCL, SDA)

# Intialize SeeSaw, Adafruit's Circuit Python library
ss = Seesaw(i2c_bus, addr=0x36)

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(args.thingName, True)

# Delete current shadow JSON doc
deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)

# Read data from moisture sensor and update shadow
while True:

    # read moisture level through capacitive touch pad
    moistureLevel = ss.moisture_read()

    # read temperature from the temperature sensor
    temp = ss.get_temp()

    # Display moisture and temp readings
    print("Moisture Level: {}".format(moistureLevel))
    print("Temperature: {}".format(temp))
    
    # Create message payload
    payload = {"state":{"reported":{"moisture":str(moistureLevel),"temp":str(temp)}}}

    # Update shadow
    deviceShadowHandler.shadowUpdate(json.dumps(payload), customShadowCallback_Update, 5)
    time.sleep(1)
```

將檔案儲存到您可以找到的位置。從命令列搭配下列參數執行 `moistureSensor.py`：

端點  
您的自訂 AWS IoT 端點。如需詳細資訊，請參閱[Device Shadow REST API](device-shadow-rest-api.md)。

rootCA  
 AWS IoT 根 CA 憑證的完整路徑。

cert  
裝置 AWS IoT 憑證的完整路徑。

金鑰  
裝置 AWS IoT 憑證私有金鑰的完整路徑。

thingName  
您的物件名稱 (在此案例中為 `RaspberryPi`)。

clientId  
MQTT 用戶端 ID。請使用 `RaspberryPi`。

命令列看起來應該如下：

`python3 moistureSensor.py --endpoint your-endpoint --rootCA ~/certs/AmazonRootCA1.pem --cert ~/certs/raspberrypi-certificate.pem.crt --key ~/certs/raspberrypi-private.pem.key --thingName RaspberryPi --clientId RaspberryPi`

嘗試碰觸感應器、將其放入花盆中，或將其放入杯水中，以查看感應器如何回應各種濕度。如有需要，您可以在 `MoistureSensorRule` 中變更閾值。當濕度感應器讀數低於規則 SQL 查詢陳述式中指定的值時， 會將訊息 AWS IoT 發佈至 Amazon SNS 主題。您應該會收到包含濕度和溫度資料的電子郵件訊息。

在您驗證收到來自 Amazon SNS 的電子郵件訊息後，請按 **CTRL\$1C** 來停止 Python 程式。Python 程式傳送的訊息量不足以產生費用，但最佳實務是在完成時停止程式。