ESP32〜AWS IoTでMQTT通信して詰んだ話【ClientID】

#IoTに関するAWS
- AWS IoT
- AWS IoT 1-click
- AWS Greengrass Core
- AWS FreeRTOS

いくつかある...
非常に関心があるのでいずれ使ってみようと思います。

#AWS IoT
IoTデバイスをバックエンドやクラウドサービスと連携することができる。
通信はHTTPSやMQTT通信を用いることができる。

#使用マイコン・環境
- ESP-WROOM-32
- Arduino IDE

<img src="https://qiita-image-store.s3.amazonaws.com/0/253129/e5f33e17-5c8d-a1aa-e946-3578445d9ba0.jpeg" width=50%>


#MQTTプロトコルについて
1990年代後半に開発されたプロトコル！
TCP/IPベースに規定されたプロトコル！
MQTTは軽量で、しかも柔軟性がとても高いプロトコル！
M2M(Machine-to-Machine)を重要視するIoTにおいて、とてもぴったりなプロトコル！

利点
IoTではハードウェアやネットワークの面において、非常に厳しい環境で開発する場合がある。
- 待ち時間や帯域幅、デバイス面での条件が厳しい場合
- [双方向通信](https://www.slideshare.net/ma2shita/aws-iot-69558775)-Publish/Subscribe
- 1対多通信
- プロトコルヘッダサイズが小さく軽量(HTTP:50バイト,MQTT:2バイト)
- 電力消費量
- 非同期通信
- 負荷
- 通信量
- 頻繁な通信=>大量に送れる

欠点は信頼性ですかね。同期通信ではないですし...
サーバークライアント間がHTTPSやHTTPでなくMQTTだとやはり向いてないですよね。

#クライアント証明
AWS IoTでは「モノ」「ポリシー」の登録。
最初に証明書を発行してもらい、RooTCA、Private_Key、Certificateをダウンロード！

#Arduinoでのスケッチ

WiFiClientSecure.h - Wifi接続がよりSecureにできるようになるライブラリ
PubSubClient.h - Port:8883でMQTT通信ができるようになるライブラリ
これらを使っていきます。

```c++:AWS_IoT_ESP32.ino
#include <WiFiClientSecure.h>
#include <PubSubClient.h>

const char* ssid     = "[SSID]";
const char* password = "[Password]";

const char* server = "[end point]";
const int port = 8883;

const char* Root_CA = \
"-----BEGIN CERTIFICATE-----\n" \
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" \
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" \
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" \
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" \
"-----END CERTIFICATE-----\n";

const char* Client_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" \
"-----END CERTIFICATE-----\n";

const char* Client_private = \
"-----BEGIN RSA PRIVATE KEY-----\n" \
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n" \
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n" \
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n" \
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n" \
"-----END RSA PRIVATE KEY-----\n";
  
WiFiClientSecure client;
PubSubClient mqttClient(client);

void connectAWSIoT() {
    while (!mqttClient.connected()) {
        if (mqttClient.connect("ESP32")) {
            Serial.println("Connected.");
        } else {
            Serial.print("Failed. Error state=");
            Serial.print(mqttClient.state());
            // Wait 5 seconds before retrying
            delay(5000);
        }
    }
}

void setup()
{
  Serial.begin(115200);

  delay(10);

  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  delay(1000);

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

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  client.setCACert(Root_CA);
  client.setCertificate(Client_cert);
  client.setPrivateKey(Client_private);
  mqttClient.setServer(server, port);  
  mqttClient.setCallback(mqttCallback);
  
  connectAWSIoT();
}

  
long messageSentAt = 0;
int Value = 0;
char pubMessage[128];
  
void mqttCallback (char* topic, byte* payload, unsigned int length) {
    Serial.print("Received. topic=");
    Serial.println(topic);
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.print("\n");
}
  
void mqttLoop() {
    if (!mqttClient.connected()) {
        connectAWSIoT();
    }
    mqttClient.loop();
}
  
void loop() {
  mqttLoop();
}
```

#こりゃダメだ

エラーコードは-1

<img width="170" alt="スクリーンショット 2018-07-01 18.59.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/253129/247d4b56-6cbd-7dd9-eca1-e70156590dd3.png">

エラーの原因

```
MQTT_DISCONNECTED - the client is disconnected cleanly
```

MQTTでAWS IoTのモノまで到達できていないそう...

#考えられる原因
- 証明書が有効になってない -> アタッチ済み
- 証明書や鍵の割り当て -> 何回か行なったが心配 ←怪しい
- ポリシー -> 全権限許可
- ポート -> 8883(MQTTの場合)
- エンドポイント -> 間違いなさそう
- AWS IoTはTLS.1.2のみ対応 -> ESP32:WifiClientSecureもTLS.1.2対応

- PubSubClient::connectの引数　←　怪しい
  - プログラム上では失敗してるが？


#証明書をOpensslコマンドで確認

rootca2.pem - RootCA
[AWS_name]-certificate.pem - Certificate
[AWS_name]-private.pem - Private Key

```:Terminal
$ openssl s_client -connect [AWS_name].iot.ap-northeast-1.amazonaws.com:8443 -CAfile rootca2.pem -cert [AWS_name]-certificate.pem -key [AWS_name]-private.pem
```

```
CONNECTED(00000003)
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 Secure Server CA - G4
verify return:1
depth=0 C = US, ST = Washington, L = Seattle, O = "Amazon.com, Inc.", CN = *.iot.ap-northeast-1.amazonaws.com
verify return:1
```

接続できてるので証明書の問題ではない。
(あるとしてもコピーミス)

#不可解

ところでこの接続成功はなに

<img width="1142" alt="スクリーンショット 2018-07-01 19.50.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/253129/f50793d8-2e95-4849-5283-0348a3832edd.png">

ということはArduinoのプログラムがやはり怪しい...

#追記-接続完了！

<img width="301" alt="スクリーンショット 2018-07-02 22.12.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/253129/ad6d740f-59af-203c-f4e3-cbc67c10638a.png">

犯人はこのコード

```c:AWS_IoT_ESP32.ino
if (mqttClient.connect("ESP32")) {
            Serial.println("Connected.");
        } else {
            Serial.print("Failed. Error state=");
            Serial.print(mqttClient.state());
            // Wait 5 seconds before retrying
            delay(5000);
 }

mqttClient.connect("ESP32")
```

"ESP32"というのがDocumentとにらめっこした挙句、ClientIDということがわかりました。
ClientIDはAWSのポリシーで設定しておく必要があります。
デフォルトのTopicのままだとアクセスできない状態なので、以下のようにClientに変更して設定したClientIDにアクセスできるようになる。

今回ならこんな感じ
[ESP32] -> ClientID

```:Policy
{
    "Version": "2018-7-1",
    "Statement": [{
        "Effect": "Allow",
        "Action": ["iot:Connect"],
        "Resource": [
            "arn:aws:iot:[us-east-1:123456789012]:client/ESP32"
        ]
    }]
```

mqttClient.connect("ESP32"）が失敗してただけで、AWS IoTに接続はできていた。
Arduinoのコード上でdisconnectedが表記されていたのはそのため。
つまり上記は何も不可解でもなかった。

#展望
ここから　AWS lambdaやCognitoと連携して、サービスをつくっていく

#参考文献
MQTT
- https://kfep.jp/solution/iot-mqtt/mqtt
- https://www.ibm.com/developerworks/jp/iot/library/iot-mqtt-why-good-for-iot/index.html
AWS IoT
- https://www.1ft-seabass.jp/memo/2017/07/31/esp32-developer-aws-iot-tips/
- https://blog.maripo.org/2017/07/esp32-aws-iot-troubleshooting/
- https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/protocols.html
- http://ken5owata.hatenablog.com/entry/2017/10/22/102207
エラー
- https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/basic-policy-variables.html