室内の温度と湿度をDynamoDBに書き込む

[HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード](https://qiita.com/tosihr/items/3068ce10a635fc63aed2)の記事で ESP32ボードの動作確認ができました。今度は、室内の温度と湿度をAWS IoTを使って、DynamoDBに書き込んでみました。温度と湿度の取得にはDHT22センサを使用しました。

<img width="700" alt="MySokuteiESP32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/6f356fb3-fa90-cffc-6f34-4fb02bbe6d05.png">
IoTルールの作成は、別記事「[MQTTでAWS DynamoDBに保存](https://qiita.com/tosihr/items/e42eaa57377a50a13f3d)」にしました。

なお、AWSアカウントは開設済みで、AWSコンソールには AdministratorAccess のポリシーがアタッチされたIAMユーザでログインしていることが前提です。

#IoTデバイスの作成

##クライアント証明書の作成

用意したデバイスのみからアクセスできるようにクライアント証明書を作成します。クライアント証明書は、プログラムに埋め込むためにダウンロードしておきます。クライアント証明書は作成とともにAWS IoT Coreに登録されます。

1. AWSコンソールの **AWS IoT**で **Manage** > **Things** を開き、**Create** > **Create a single thing** を選ぶ。
1. MySokuteiESP32 と **Name** フィールドに入力し、他はデフォルトのままにして **Next**を選ぶ。
1. **Create certificate**を選ぶ。ESP32での接続に a certificate for this thing、a private key、Amazon Root CA1 のダウンロードが必要。ダウンロードしたらセキュアな場所に保存しておく。
1. **Activate**、**Attach a policy** をクリックする。
1. ポリシーの追加はスキップして、**Register Thing** をクリックする。

##ポリシーの作成

MQTTトピック esp32/pub にパブリッシュする許可を、デバイスに与えるためのポリシーを作成します。今回作成するポリシーでは、次の2つのMQTTポリシーアクション：
    <dl>
      <dt>&nbsp;&nbsp;&nbsp;iot:Connect</dt><dd>AWS IoT Core メッセージブローカーに接続するアクセス許可</dd>
      <dt>&nbsp;&nbsp;&nbsp;iot:Publish</dt><dd>MQTT トピックにパブリッシュするアクセス許可</dd>
    </dl>
を許可します。

1. **AWS IoT** のサイドメニューから **Secure** > **Policies** を選び、**Create** をクリックする。
1. `SokuteiPolicy` と **Name** フィールドに入力し、**Advanced mode** を選ぶ。

    ```json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "iot:Connect",
          "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:client/MySokuteiESP32"
        },
        {
          "Effect": "Allow",
          "Action": "iot:Publish",
          "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:topic/esp32/pub"
        }
      ]
    }
    ```
  
1. **REGION** には使っているリージョン名を入れる(東京リージョンなら、`ap-northeast-1`)。リージョン名は、AWSコンソールの右上で確認できる。
1. **ACCOUNT_ID** を自分のものに変更する。[Account Settings](https://console.aws.amazon.com/billing/home?#/account) で確認できる。
1. **Create** をクリックする。

##クライアント証明書へのポリシーのアタッチ

デバイスからのアクセス許可は、クライアント証明書にポリシーをアタッチすることで行います。

1. **AWS IoT** のサイドメニューから **Secure** > **Certificates** を選ぶ。そして、いま作成した証明書を選び、**Actions** > **Attach policy** を選ぶ。
1. `SokuteiPolicy` にチェックを入れて、**Attatch** をクリックする。

ここまででESP32をAWS IoTに接続するためのAWSコンソールでの設定が完了。トピック `esp32/pub` にパブリッシュが可能である。 

#Arduino IDEの設定
##Arduinoライブラリのインストール
MQTTライブラリ、および、ArduinoJsonライブラリをインストールします。Arduino IDEのライブラリマネージャから行うことができます。

1. Arduino IDEを起動する。
1. **スケッチ** > **ライブラリをインクルード** > **ライブラリを管理** を選ぶ。
1. `MQTT` で検索し、Joel Gaehwiler の最新バージョンをインストールする。![MQTT.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/27ad778c-df3c-7c4f-30cf-286bae9d525e.png)
1. 同様に `ArduinoJson` もインストールする。 
![ArduinoJson.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/4744c0cd-3a35-4caa-da44-d1d4a6c2c26f.png)
1. **閉じる** をクリックする。

#ESP32の設定とプログラムの書き込み
次のようなプログラム：

- ローカルネットワークのWiFiに接続して、先ほどダウンロードしたクライアント証明書を使って、AWSエンドポイントのMQTTブローカーに接続
- JSON形式のメッセージをトピック `esp32/pub` に、繰り返しパブリッシュ

を作成して、ESP32に書き込みます。

1. Arduino IDE で　**ファイル** > **新規ファイル** を選ぶ。
1. **新規タブ** を選び、**新規ファイルの名前** に `secrets.h` を入力し、**OK**をクリックする。
1. 以下の secrets ファイルをペーストする。

    ```c
    #include <pgmspace.h>
    #define SECRETS
    #define THINGNAME ""
    
    const char WIFI_SSID[] = "";
    const char WIFI_PASSWORD[] = "";
    const char AWS_IOT_ENDPOINT[] = "xxxxx.amazonaws.com";

    // 証明書
    static const char AWS_CERT_CRT[] PROGMEM = R"CRT(
    -----BEGIN CERTIFICATE-----
    -----END CERTIFICATE-----
    )CRT";
    
    // プライベートキー
    static const char AWS_CERT_PRIVATE[] PROGMEM = R"PRK(
    -----BEGIN RSA PRIVATE KEY-----
    -----END RSA PRIVATE KEY-----
    )PRK";
    
    // ルートCA証明書
    static const char AWS_CERT_CA[] PROGMEM = R"RCA(
    -----BEGIN CERTIFICATE-----
    -----END CERTIFICATE-----
    )RCA";
    ```

1. **THINGNAME** に `MySokuteiESP32` を設定する。
1. Wifiに接続するため、**SSID** と **PASSWORD** を設定する。
1. **AWS_IOT_ENDPOINT** は、**AWS IoT** のサイドメニューから **Settings** のページで確認できる。
1. 証明書、プライベートキー、ルートCA証明書をそれぞれ `secrets.h` ファイルに書き込む。
1. メインスケッチのタブを開き、以下のコードをペーストする。**ファイル** > **保存** を選んで、プロジェクト名を設定して保存する。

```
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"

// ESP32からパブリッシュするMQTTトピック
#define AWS_IOT_PUBLISH_TOPIC   "esp32/pub"

WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);

void connectAWS()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  Serial.println("Wi-Fiに接続中..");

  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }

  // AWS IoTの認証情報を使用するようにWiFiClientSecureを設定
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);

  // AWSエンドポイントのMQTTブローカーに接続
  client.begin(AWS_IOT_ENDPOINT, 8883, net);

  Serial.print("AWS IoTに接続中..");

  while (!client.connect(THINGNAME)) {
    Serial.print(".");
    delay(100);
  }

  if(!client.connected()){
    Serial.println("AWS IoTがタイムアウトしました!");
    return;
  }

  Serial.println("AWS IoTに接続しました!");
}

void publishMessage()
{
  StaticJsonDocument<200> doc;
  doc["time"] = millis();
  doc["temp"] = "20.1";
  doc["hum"] = "60.5";
  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer); // JSON形式のデータを生成

  client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
}

void setup() {
  Serial.begin(9600);
  connectAWS();
}

void loop() {
  publishMessage();
  client.loop();
  delay(10000);
}
```

プログラムの書き込みは、[こちらの記事を参照](https://qiita.com/tosihr/itEnvems/3068ce10a635fc63aed2)。


#デバイスからパブリッシュされたデータの確認

1. AWSコンソールの **Services** から DynamoDB を開く。
1. サイドメニューの **Tables** から作成されたテーブルを選ぶ。
1. Itemsタブを選ぶと、デバイスから送信されたデータが記録されていることを確認できる。

# 室内の気温と湿度の記録
[温湿度センサ DHT11 on arduino-esp32](https://qiita.com/nanbuwks/items/74e5896071c57b0670c3)を参考に温度センサーとESP32を接続し、温度と湿度が取得できることを確認しておきます。ここでは DHT11の代わりにDHT22を使用し、DHT22のOUTは16番ピンに接続しました。

1. 先頭の #include 行の後ろに次のコードを追加する。

    ```c
    #include "DHTesp.h"
    #define DHTPIN 16
    DHTesp dht;
    ```
1. `publishMessage()` 関数を次のコードに置き換える。

    ```
    void publishMessage()
    {
      TempAndHumidity newValues = dht.getTempAndHumidity();
      if (dht.getStatus() != 0) {
        Serial.println("DHT22 error status: " + String(dht.getStatusString()));
      }
      float temp = newValues.temperature;
      float hemi = newValues.humidity;
      Serial.print(temp);
      Serial.print(" ");
      Serial.println(hemi);

      StaticJsonDocument<200> doc;
      doc["time"] = millis();
      doc["temp"] = temp;
      doc["hum"] = hemi;
      char jsonBuffer[512];
      serializeJson(doc, jsonBuffer); // JSON形式のデータを生成

      client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
    }
    ```
1. `setup()` 関数を次のコードに置き換える。

    ```
    void setup() {
      Serial.begin(9600);
      dht.setup(DHTPIN,DHTesp::DHT22);
      connectAWS();
    }
    ```

1. **マイコンに書き込む** をクリックする。
1. 書き込みが完了すれば、DynamoDB の payload に現在の温度と湿度が10秒毎に書き込まれる。

<br>
以上のように、ESP32 を AWS IoT に接続して、DHT22を使って測定した温度・湿度を DynamoDB に書き込むということが、とても簡単にできることが分かりました。

#参考

- [Building an AWS IoT Core device using AWS Serverless and an ESP32](https://aws.amazon.com/jp/blogs/compute/building-an-aws-iot-core-device-using-aws-serverless-and-an-esp32/)