AWS IoTにESP32からMQTT over WebSocketでつなぐ

ESP32から Amazon Web Service (AWS) IoT に MQTT over WebSocketでつなぐことを試した。Arduinoのライブラリの使い方でうまくいかない場合の原因調査に時間がかかった。分かってみればArduinoのプログラミングモデルを正しく理解していなかっただけであるが、備忘録として残しておく。

開発環境

開発につかった環境、ライブラリなどは以下の通り。

ハードウェア

  • MH-ET Live Minikit ESP32
    • 他の開発ボードでも同様だと思う。

プログラム開発環境

ライブラリ

AWS MQTT over WebSocket をライブラリとして使った。AWS SDK for Arduinoライブラリおよび arduinoWebSockets ライブラリが必要である。

また、MQTT ClientライブラリとしてPubSubClientライブラリもしくはEclipse Paho が必要である。

WiFiManagerも利用している。

AWS MQTT over WebSocket

Implementation of a middleware to use AWS MQTT service through websockets, aiming the ESP8266 plataform

https://github.com/odelot/aws-mqtt-websockets

AWS SDK for Arduino

An experimental SDK for working with AWS Services on Arduino-compatible devices. Currently has support for DynamoDB and Kinesis.

https://github.com/odelot/aws-sdk-arduino

(2018年8月18日追記)

ESP32で動作させるためには、一部コードの変更が必要である。

修正をしたコードをGitHubの次に置いた。

https://github.com/kunsen-an/aws-sdk-arduino

下記のライブラリの依存関係では aws-sdk-arduino-ESP32 となっている。PlatformIO のプロジェクトの下にあるlibの中にcloneして利用することができる。

WebSocket

arduinoWebSockets

https://github.com/Links2004/arduinoWebSockets

WifiManager

プログラムにWiFiの情報を埋め込むのを避けるためにWifiManager を使う。DNSServerやWebServerも必要になる。

(2018年8月18日追記)

ESP32のためには、development branch のコードを使う必要がある。

https://github.com/tzapu/WiFiManager/tree/development

もちろん、WiFiManagerを利用することは必須ではない。https://github.com/odelot/aws-mqtt-websockets/tree/master/examples のサンプルプログラムのように WifiManagerを使わなくてもプログラムを作成できる。

MQTT Clientライブラリ

PubSubClient

A client library for the Arduino Ethernet Shield that provides support for MQTT.

https://github.com/knolleary/pubsubclient

http://pubsubclient.knolleary.net/

Eclipse Paho

https://projects.eclipse.org/projects/technology.paho

https://www.eclipse.org/downloads/download.php?file=/paho/arduino_1.0.0.zip

PlatformIO IDE for VSCode で PIO Homeのライブラリマネージャからインストールできる。

ライブラリの依存関係

ライブラリの依存関係は以下の通り。PubSubClientもPahoも含んでいる。

単純なプログラムでのテスト

ESP32のプログラム

以下のプログラムを用いて動作確認を行った。WifiManagerを使ってWiFiに接続するようにしている。

40行目から45行目は利用するAWS IoTに応じて変更が必要である。MQTT でAWS IoT にアクセスするための設定 に簡単な設定方法について示した。

プログラムは起動直後に、$aws/things/your-device/shadow/update にメッセージを送信する。$aws/things/your-device/shadow/control にメッセージを受信するとそれをシリアルに表示すると共に、起動直後と同じメッセージを $aws/things/your-device/shadow/update に送信する。

AWS IoT Core のページから「テスト」を選び、「トピックへサブスクライブする」で $aws/things/your-device/shadow/update を subscribe する。

この後で、ESP32のプログラムを起動すると、メッセージがAWS IoTで受信されるはず。

また、「トピックへの発行」で$aws/things/your-device/shadow/control を指定し、「トピックに発行」ボタンを押すとメッセージが送られる。ESP32のプログラムが起動して正常に接続されていれば、以下のようにESP32の起動時と同じメッセージが送り返される。

AWS MQTT over WebSocket 利用の際の注意点

以下の部分の client.loop() もしくは client.yield() が頻繁に呼び出されるようにしておく必要がある。

また、aws-mqtt-websockets の内部で待ちがある場合に loop() 関数が呼び出されている箇所がある。

loop()に時間がかかり戻ってこないとそのためにタイムアウトになるなどして WebSocketの接続が切れるなど問題を生じる場合がある。たとえば、loop内で delayなどを使って時間がかかるようにしていると問題を生じる。

試していて、これら問題に気がつくまで、メッセージが送れなかったり、コネクションの切断が頻繁に起きたりしてうまく動作しなかった。

ライブラリに附属しているexampleではloopが短時間で終わるようになっているので問題は生じない。

MQTT接続の切断など

上記のプログラム例で LONG_DELAY と NO_LIBRARY_CALLBACK を定義すると、しばらく使っているとAWS IoT のMQTT接続が切断されることがある。

以下はAWS IoTのテストで、$aws/things/your-device/shadow/update をsubscribeしてあった際に出た通知。

また、 LONG_DELAY を定義して (NO_LIBRARY_CALLBACK は定義しないで) PubSubClientを使うとシリアルに以下のような出力がされ、切断と再接続が繰り返されていることがわかる。このような場合には、受信すべきメッセージが失われることになる。

まとめ

Arduino スタイルのプログラミングモデルでは当たり前といえば当たり前であるが、ライブラリはマルチスレッドなどで並行動作するわけではない。このために以下のことに気をつける必要がある。

  • PubSubClientライブラリの loop() もしくはPahoライブラリのyield() が頻繁に呼び出されるようにしておく
  • Arduinoプログラム本体の loop() は短時間で終了するようにしておく