AWS IoTとESP32とMongooseOSでクラウドLチカしてみた

はじめに

最近どういうわけか組み込みにもちょっと興味が湧いてきたので、試しにESP32の開発ボードを購入してみました。

ESP32はWiFiとBluetoothを内蔵したマイコンであり、技適もあって開発ボードでもかなり安価(Amazon.co.jpで2個入2,000円未満で買える)とあり、かなり人気のマイコンのようです。

さて、試しにプログラムを書いてみようと思ったのですが、組み込み界でのソフトウェア界で言う「Hello world」に該当するものとして、「Lチカ」と呼ばれるプログラムがあります。その名の通り、LEDをチカチカ点滅させるだけの簡単なプログラムのことを指します。

ESP32で単純なLチカをするプログラムならいくらでもありますが、AWS IoTと紐付けてだとちょっと見当たらなかったので、勉強がてら書いてみたのを共有させていただきます。

悩ましい開発環境

C言語といえば組み込み…というわけではないかもですが、組み込みといえばC言語でしょう。

ESP32の公式開発環境はESP-IDFというFreeRTOSベースのC/C++での開発となっています。Amazon FreeRTOSもESP-IDFを内包しており、追加でAWS IoTからのOTAやシャドウのAPIが用意されているなど、AWS IoTを使うならAmazon FreeRTOSが普通はベストなのかなと思います。

ただ、正直に白状しますが、可能であれば私はCを書きたくないです。

Cが組み込みにおいて最良の言語であることは明らかだと思います。少ないフットプリントや、組み込みで利用される多くのライブラリ(例えばI2C LCDの表示ライブラリ)がCで書かれてる以上、やはりCで書くのがベストでしょう。組み込みという資源が限られた環境のなかでは、やはり人間が機械に合わせるというのがいい方法だとは思います。

しかしそういった環境だとしても、私はなるべく人間が読み書きしやすい言語でプログラム書きたいです。

Arduinoは悪くない選択肢です、Cを色々書きやすいよう工夫されています。knolleary/pubsubclientという比較的活発でクライアント/サーバー認証に対応可能なMQTTクライアントもあります。ただこのMQTTクライアントは現時点ではPublishする際にQoS1に対応しておらず、この点で問題が生じるかもしれません。

MicroPythonというPython版mrubyのような組み込み用Pythonも良さそうに見えます。コンソールからREPLを叩けるのは本当に素晴らしいです。ただ、MQTTクライアントを使う際のsslソケットの実装において、サーバー認証を行わない場合がありちょっと今時点ではまだ未成熟だと思いました。

EspruinoというESP32に対応した組み込み用JavaScriptも良さげです。ただ、MQTTクライアントは暗号化に対応していません

Mongoose OS

しょうがない、Amazon FreeRTOSで頑張ってCで書くか…という時に私を救ってくれたのは、Mongoose OSでした。

Mongoose OSは、CとmJSと呼ばれるJavaScriptのサブセットで開発することができます。

はじめに言いますが、Mongoose OSはかなり素晴らしいです。

mJS自体がなかなか良いです、Cの関数呼び出しにも対応しており、ライブラリがCしかないという場合でも対応できます。コードサイズはCに比べて増えてしまうのですが、使用メモリは大きくは変わらないようです。

また、各クラウド(AWS, Azure, GCP等)に標準対応しており、MQTT(Publish時のQoS1にもクライアント/サーバー認証にも対応しています)といった各種クライアントや、ESP32のフラッシュメモリ暗号化にまで対応しています。

mos toolと呼ばれるローカルコンソールとマイコンのシリアルコンソールが2画面で表示出来るツールも付属しており、なかなか便利です。

オープンソース(Apache 2.0)の無料版と商用のエンタープライズ版があり、無料版はOTAのソース制限とcron(tab)が3つまでという制限があります。商用版でも料金は1,000ライセンス以上であれば1ライセンスあたり1ドルしないので、OTA周りを別途実装する工数を考えると、個数次第ではありますがエンタープライズ版を購入するのはかなりアリだと思いました。

クラウドLチカ

まず、Mongoose OSのクイックスタート通りにapp1というサンプルを実行してみてください。ステップ8以降はmDashというAWS IoTのようなサービスへの登録なのでしてもしなくても大丈夫ですが、10台までは無料のようなのでお試しするのは良さそうです。

クイックスタート通りにビルドすると、クラウド上でビルドされますが、ローカルでもビルドが出来ます。最初のビルドはかなり遅いですが、mos.ymlという構成ファイルを変えないでソースコードだけを変えると、2回目以降は高速にビルドされます。

AWS IoTのチュートリアルもあり、この通りにすればすぐ出来るのですが、今回は手でリソースを作って設定してみましょう。

はじめに、AWS IoTの公式のガイドどおりに、AWS IoTのThingと証明書とポリシーを設定してください。

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/register-device.html

証明書(XXXX-certificate.pem.crt)とプライベートキー(XXXX-private.pem.key)とルートCA証明書(AmazonRootCA1.pem)がMQTT通信時に必要なので、ESP32に転送するようにします。サンプルアプリ(app1)のmos.ymlのfilesystemに書いてあるとおり、fsディレクトリをESP32へ転送するようになっているので、このディレクトリ内に上記3ファイルを置くと、mos flash時に転送してくれます。

MQTTの設定もmos.yml内で行います。config_schemaを以下の通りに設定しました。

config_schema:
- ["mqtt.client_id", "CHANGEME"]
- ["mqtt.enable", true]
- ["mqtt.clean_session", false]
- ["mqtt.server", "CHANGEME-ats.iot.ap-northeast-1.amazonaws.com:8883"]
- ["mqtt.ssl_ca_cert", "AmazonRootCA1.pem"]
- ["mqtt.ssl_cert", "XXXX-certificate.pem.crt"]
- ["mqtt.ssl_key", "XXXX-private.pem.key"]
- ["wifi.sta.enable", true]
- ["wifi.sta.ssid", "CHANGEME"]
- ["wifi.sta.pass", "CHANGEME"]

mqtt.client_idは、AWSのオススメ通りThing名を使うことをおすすめします。

mqtt.serverはAWS IoTコンソールから確認出来るエンドポイントを設定してください。

各証明書は正しい証明書名を設定してください。

wifiのSSIDとパスワードも正しいものを設定してください。Mongoose OSのクイックスタート通りコマンドラインで設定するのでも大丈夫だと思います。

ソースコードであるinit.jsは次のとおりにしました。

load('api_gpio.js');
load('api_mqtt.js');

let LED_GPIO_PIN = CHANGEME;
GPIO.setup_output(LED_GPIO_PIN, 0);

MQTT.sub('$aws/things/CHANGE_ME_THING_NAME/shadow/update/delta', function(conn, topic, msg) {
  let delta_json = JSON.parse(msg);
  let delta_json_led = delta_json.state.led;

  GPIO.write(LED_GPIO_PIN, delta_json_led);
  
  let new_state_json = {
    state: {
      reported: {
        led: delta_json_led
      }
    }
  };
  MQTT.pub('$aws/things/CHANGE_ME_THING_NAME/shadow/update', JSON.stringify(new_state_json));
}, null);

LEDのGPIOピンは自分で刺したところにしてください。

MQTTのトピック名のThing名は作成したときと同じものにしてください。

プログラム自体は単純明快です、シャドウのdelta(desiredとreportedの差分)が発生したら、desired通りにLEDを設定して、reportedに変えたLEDの状態を書き込んでいるだけです。

あとはビルドして書き込んで以下のようにシャドウ内のdesiredのLEDの状態をtrueに書き換えれば、

{
  "desired": {
    "led": true
  }
}

LEDが点灯し、falseにすれば消えます。

おわりに

とても簡単にクラウドLチカが出来ました。もうLEDランプを大きいものにすれば、スマートLED家電と言い張ってもいいんじゃないんでしょうか。

もちろん、これを製品化するとなると、例えばアプリを作るだとか、電源ONにしたときにはdesiredを見てその状態するとかありますが、ひょっとしたら私にもあんなことやこんなことが出来るかも、という気持ちになりませんか?

また、私はESP32は単にArduinoに無線が付いただけと思ってたのですが、フラッシュメモリ暗号化等、さまざまな機能があり、実際に製品に組み込んで使える代物だと気付きました。

Cを書けなくても、あるいは書きたくなくても色々作れそうなので、皆さんもぜひ組み込みにチャレンジしてみてください。