(ESP32/MQTT/AWS)センサデータをAWSIoTCoreへ送信してTimestreamへ格納する 

# はじめに
MQTTとはIoTデバイスでよく使用される軽量なデータ配信プロトコルです。
IoTCoreを経由することでDBへの格納やトリガーにも利用できます。
今回は勉強がてらに、温度/湿度データをAWSIoTCoreを経由してTimestreamに格納することをゴールにします。

# Timestreamの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/d3ccd59d-0c80-5f2d-9691-77fe2a06aa37.png)

### Database/Tableの作成

先に格納先のDBとtableを作成しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/a5ed9afa-205c-bb69-5b78-622345becd93.png)

```
DB:esp32_test_database
table:esp32_test_table
```

# AWS IoT Coreの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/2d221a86-64a1-e2ba-4dc2-5d66540f0cd5.png)
## Things(モノ)を登録
デバイスを登録します。IoTCoreでは```Things(モノ)```と表現します。
サイドメニューより**Things**を選択して新規登録します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/df1c0149-35cb-fdc2-9263-7f1d8b01c30f.png" width="70%">

**デバイス名を入力**
今回は```ESP32_SAMPLE```というデバイス名にします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/6a7ae324-b8bf-a140-1657-aed8e73e4b68.png" width="70%">

一番上の```新しい証明書を自動生成```を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/7f3a0dd0-db66-3339-9767-a72d2a9fb2a8.png)

### Policyの作成
ポリシー名は```ESP32_Policy```として、ここでは送受信の権限を設定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/7d0c60d0-cf31-2517-cb99-8b24e77bf4c6.png)

```Name:Policy action
iot:Connect
iot:Publish
iot:Receive
iot:Subscribe
```
**Policy resourceは全て「*」** にします。

## 証明書とキーをダウンロードする
ポリシーまで作成すると証明書のダウンロード画面がでてきますので、**Public key**,**Private key**,**Root CA1 certificate**をダウンロードしておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/5a329d7c-8d99-13ce-9a05-6a7ffece8206.png)

## Rule(ルール)の設定
ルールを設定します。今回は予め設定したテーブルへデータを格納します。
**Message Routing→Rules**

**SQL Statement**がトピック名になります。

```json
SQL Statement: SELECT*FROM "esp32/pub" 
Action: "Timestream table"
DB:"esp32_test_database"
table:"esp32_test_table"
Dimensions name: "deviced"
Dimensions value: "my-esp32"
Timestamp: "${timestamp()}"
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/46128c43-4e08-6270-c96a-e60e54634ad3.png" width="70%">


パラメータの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/4dd8c2e6-d68c-7d5f-40f8-fdaf6f6624e6.png)




# ESP32側の設定

## 結線図
温度・湿度測定：DHT11
Vcc,GND,GPIO(4)をそれぞれ接続します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/f0d99b57-b336-f7fe-6f7d-2dc6a63ea691.png" width="50%">


## ソースコード

### 認証ファイルの記述


#### Keyの記述

ダウンロードした証明書3点からkeyをコピペします。
* **Public key.key**
* **Private key.key**
* **Root CA1 certificate.pem**

#### エンドポイント

Settings内の```Endpoint```をコピーして
下記行を変更します。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/6894bc88-5197-b5a3-2454-3c7d4b3afa65.png" width="70%">


```
const char AWS_IOT_ENDPOINT[] = "XXXXXXXXXXXX.amazonaws.com";       //change this
```

```c#Name:secrets.h
#include <pgmspace.h>
 
#define SECRET
#define THINGNAME "ESP32_DHT11"                         //change this
 
const char WIFI_SSID[] = "WIFI_SSID";               //change this
const char WIFI_PASSWORD[] = "WIFI_PASSWORD";           //change this
const char AWS_IOT_ENDPOINT[] = "XXXXXXXXXXXX.amazonaws.com";       //change this
 
// Amazon Root CA 1
static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
-----END CERTIFICATE-----
)EOF";
 
// Device Certificate                                               //change this
static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
-----END CERTIFICATE-----

)KEY";
 
// Device Private Key                                               //change this
static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
hogehogehogehogehogehogehogehogehogehogehogehogehogehoge
-----END RSA PRIVATE KEY-----
 
)KEY";

```

### メインコード

下記ライブラリはダウンロードして下さい。
* PubSubClient
* ArduinoJson
* WiFiClient


```cpp
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"
 
#include "DHT.h"
#define DHTPIN 4     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT11   // DHT 11
 
#define AWS_IOT_PUBLISH_TOPIC   "esp32/pub"
#define AWS_IOT_SUBSCRIBE_TOPIC "esp32/sub"
 
float h;
float t;
 
DHT dht(DHTPIN, DHTTYPE);
 
WiFiClientSecure net = WiFiClientSecure();
PubSubClient client(net);

void connectAWS()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 
  Serial.println("Connecting to Wi-Fi");
 
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
 
  // Configure WiFiClientSecure to use the AWS IoT device credentials
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);
 
  // Connect to the MQTT broker on the AWS endpoint we defined earlier
  client.setServer(AWS_IOT_ENDPOINT, 8883);
 
  // Create a message handler
  client.setCallback(messageHandler);
 
  Serial.println("Connecting to AWS IOT");
 
  while (!client.connect(THINGNAME))
  {
    Serial.print(".");
    delay(100);
  }
 
  if (!client.connected())
  {
    Serial.println("AWS IoT Timeout!");
    return;
  }
 
  // Subscribe to a topic
  client.subscribe(AWS_IOT_SUBSCRIBE_TOPIC);
 
  Serial.println("AWS IoT Connected!");
}

//publish
void publishMessage() 
{
  StaticJsonDocument<200> doc;
  doc["humidity"] = h;
  doc["temperature"] = t;
  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer); // print to client
  // Debug JSON in SerialConsole
  Serial.println(jsonBuffer);
//  client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
  client.publish("esp32/pub", jsonBuffer);

}
 
void messageHandler(char* topic, byte* payload, unsigned int length)
{
  Serial.print("incoming: ");
  Serial.println(topic);
 
  StaticJsonDocument<200> doc;
  deserializeJson(doc, payload);
  const char* message = doc["message"];
  Serial.println(message);
}
 
void setup()
{
  Serial.begin(115200);
  connectAWS();
  dht.begin();
}
 
void loop()
{
  h = dht.readHumidity();
  t = dht.readTemperature();
 
 
  if (isnan(h) || isnan(t) )  // Check if any reads failed and exit early (to try again).
  {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }
 
//  Serial.print(F("Humidity: "));
//  Serial.print(h);
//  Serial.print(F("%  Temperature: "));
//  Serial.print(t);
//  Serial.println(F("°C "));
 
  publishMessage();
  client.loop();
  delay(5000);
}

```


# 接続してみよう

## MQTT test clientでの確認

**Test→MQTT test client**で通信が成功している場合情報を受信できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/f2e2959f-fd16-1c0d-b6c5-3e3ce89cbb75.png)

レスポンス

```json
{
"humidity": "XX",
"temperature": "XX"
}
```


## queryでの確認

**Timestream→Query editor**にて確認することができます。

SQL文のサンプル

```
SELECT * FROM "esp32_test_database"."esp32_test_table" ORDER BY time DESC LIMIT 10
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/98290aec-cf0b-0dc7-ecf0-4245bf354404.png)

# (Tips)ワイルドカードによる設定
トピック名にワイルドカードを設定できます。それによりデバイスIDなどをPub時に合わせて送信することができます。

#### トピック名を変更する

**トピック名に「+」** を入れる

```Name:topic
esp32/+/pub
```

#### ディメンションを変更

```json
Dimensions name: "deviceID"
Dimensions value: "${topic(2)}"
```

**topicの2番目の位置にデバイスIDを入れる**　という設定になります。


## 参照記事
[IoT Coreに来たデータをAmazon Timestreamに保存してみる](https://dev.classmethod.jp/articles/iot-core-rule-action-to-amazon-timestream/)