ESP-WROOM-02をAWS IoTにつないでみた

# はじめに
EC2インスタンスの電源ボタンを、ESP-WROOM-02開発ボードで作ってみた。

EC2インスタンスの電源状態のモニタリングをAPI Gateway+Lambdaでやろうとしたが、ポーリングでリクエストが多くなるとお財布事情が、、、と思い、AWS IoTからプッシュ通知ができないか調べてみたところ、ドンピシャなライブラリ[aws-mqtt-websockets](https://github.com/odelot/aws-mqtt-websockets)があったのでこれを使ってみる。

# 事前準備

## IAMユーザの作成

サンプルスケッチを見ればわかるが、AWSの認証情報（アクセスキー,シークレットキー）を書かないといけないので、専用IAMユーザを作って、認証情報を保存しておく。

アタッチするポリシーは、インラインポリシーで以下のものを（ここまでいらないかもしれない。コメント求む。）。

```json:policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Connect",
                "iot:Publish",
                "iot:Subscribe",
                "iot:Receive",
                "iot:GetThingShadow",
                "iot:UpdateThingShadow"
            ],
            "Resource": "*"
        }
    ]
}
```

## Thingの作成
AWS IoTでThingを`aws iot create-thing --thing-name ${thing_name}`で作成する。以下は`ec2power-switch`という名前のthingを作る例。

```shell-session

$ aws iot create-thing --thing-name ec2power-switch
{
    "thingArn": "arn:aws:iot:${region}:${account}:thing/ec2power-switch",
    "thingName": "ec2power-switch"
}
```
\# \${region}や${account}は、それぞれ実行環境で変わる。

※登録済みのthingのリストは、`aws iot list-things`で確認できる。

```shell-session
$ aws iot list-things
{
    "things": [
        {
            "attributes": {},
            "version": 1,
            "thingName": "ec2power-switch"
        }
    ]
}
```

## エンドポイントアドレスの確認
AWS IoTのエンドポイントアドレスを確認する。

```shell-session
$ aws iot describe-endpoint
{
    "endpointAddress": "xxxxxxxxxxxxxx.iot.${region}.amazonaws.com"
}
```

ここまでで、

* AWSの認証情報
  * アクセスキー
  * シークレットキー
* Thingの名前
* エンドポイントアドレス

がそろっているはず。

また

* AWSのリージョン名（ex. ap-northeast-1）
* 実行環境の下記無線LAN情報
  * ssid
  * password

も確認しておく。

# Arduino用ライブラリの取得
既にArudino IDEの構築や、ESP-WROOM-02用の設定は済ませているものとして、AWS IoTにESP-WROOM-02をつなぐのに必要な部分だけ書く。

以下の4つの追加ライブラリを入れる。

 1. aws-mqtt-websockets
 1. aws-sdk-arduino
 1. WebSockets for Arduino
 1. Paho MQTT for Arduino

## aws-mqtt-websockets
[aws-mqtt-websockets](https://github.com/odelot/aws-mqtt-websockets)
githubから取得して、librariesディレクトリ[^aruduino_libraries_windows][^aruduino_libraries_macos]に展開して保存する。

## aws-sdk-arduino
[aws-sdk-arduino](https://github.com/svdgraaf/aws-sdk-arduino)
githubから取得して、librariesディレクトリ[^aruduino_libraries_windows][^aruduino_libraries_macos]に展開して保存した後、src/common以下をこのライブラリのルートにコピー、さらにsrc/esp8266以下を同じ場所に上書きコピーする。

## WebSockets for Arduino
Arduino IDEのメニューから、[スケッチ]→[ライブラリのインクルード]→[ライブラリの管理...]と辿って検索フィルタに`WebSockets for Arduino (Server + Client)`と入れて出てくるライブラリの最新バージョンをインストールする。

## Paho MQTT for Arduino
[Paho MQTT for Arduino](https://projects.eclipse.org/projects/technology.paho/downloads)
公式サイトからArduino Client Library 1.0.0(2017/02/12現在)を取得して、librariesディレクトリ[^aruduino_libraries_windows][^aruduino_libraries_macos]に展開して保存する。

# サンプルスケッチの実行
Arduino IDEのメニューから[ファイル]→[スケッチ例]→[aws-mqtt-websocket]→[aws-mqtt-websocket-example]を選択してサンプルスケッチを開き、30行目付近にある下記設定値を変更する。

```c++:aws-mqtt-websocket-example
 :
//AWS IOT config, change these:
char wifi_ssid[]       = "your-ssid";
char wifi_password[]   = "your-password";
char aws_endpoint[]    = "your-endpoint.iot.eu-west-1.amazonaws.com";
char aws_key[]         = "your-iam-key";
char aws_secret[]      = "your-iam-secret-key";
char aws_region[]      = "eu-west-1";
const char* aws_topic  = "$aws/things/your-device/shadow/update";
 :
```
Thingの名前は、`aws_topic`の`your-device`の部分に書く。

このスケッチをコンパイルして書き込めば、AWS IoTに接続して待ち受けして、シリアルコンソールに以下のようなログが出る。

```shell-session
connected with {SSID}, channel 11
dhcp client start...
..........ip:192.168.0.16,mask:255.255.255.0,gw:192.168.0.1
7682 - conn: 1 - (34528)
please start sntp first !
websocket layer connected
MQTT connecting
MQTT connected
MQTT subscribed
```
ここまでくると接続成功。

またこのサンプルスケッチでは、接続成功時に自分のThingにメッセージを送っていて、その結果も併せて出力されている（下記2行）。

```shell-session
Message 1 arrived: qos 0, retained 0, dup 0, packetid 0
Payload {"state":{"reported":{"on": false}, "desired":{"on": false}}}
```


### 余談
自分の環境では、`setup()`内の

```C++
  WiFiMulti.addAP(wifi_ssid, wifi_password);
  Serial.println ("connecting to wifi");
  while(WiFiMulti.run() != WL_CONNECTED) {
    delay(100);
    Serial.print (".");
  }
```
の部分（175行目付近）を、

```C++
  WiFi.begin(wifi_ssid, wifi_password);
  Serial.println ("connecting to wifi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
```
のように書き換えないとWiFiにつながらなかった。
これはAWS IoTとかmqttとかwebsocketは関係なく、自分の無線環境の問題か。

# おまけ
冒頭の「EC2インスタンスの電源ボタンを～」というのをやるには、CloudWatchのルールでEC2インスタンスの状態を監視して、変化時に起動されるLambda関数でAWS IoTに通知するようにした。

CloudWatchのルールは、以下のような設定。
![ec2monitoring_cloudwatch.png](https://qiita-image-store.s3.amazonaws.com/0/90361/8e1dab14-c05c-a2d4-f521-0a912f7febd4.png)

状態に変化があった時に実行されるLambda関数の、AWS IoTへの通知部分は以下のようなコード。

```js:index.js
var IoTData = new AWS.IotData({
  endpoint: 'xxxxxxxxxxxxxx.iot.${region}.amazonaws.com',
  region: '${region}'
});

var params = {
  topic: '$aws/things/${thingName}/shadow/update',
  payload: 
};

IoTData.publish(params).promise()
.then((data) => {
  :
});
```
あと電源のOn/Offは結局API Gateway+Lambdaでやった。

----
[^aruduino_libraries_windows]: Windowsの場合: %USERPROFILE%\Documents\Arduino\libraries
[^aruduino_libraries_macos]: macOSの場合：~/Documents/Arduino/libraries