AWSを使ってESP32-C3をOTAアップデートしよう
はじめに
IoTデバイスを継続的に運用するためには、不具合の修正や機能追加に合わせて、ファームウェアを更新する仕組みが必要です。しかし、設置済みのデバイスを一台ずつ回収し、ケーブルを接続して書き換える方法では、台数が増えるほど作業の負担も大きくなります。
OTAアップデートとは
OTA(Over The Air)アップデートとは、Wi-Fiなどの無線を通じて、デバイスのファームウェアを遠隔から更新する仕組みです。
デバイスを回収せずに更新できるため、不具合修正や機能追加、セキュリティ対策を効率よく行えます。特に、多数のIoTデバイスを設置して運用する場合に有効です。
また、今回紹介する方法は、ファームウェアの署名検証もするため、セキュアにアップデートすることができます。
FreeRTOSがGitHubで公開しているESP32向けのリファレンス実装「iot-reference-esp32」を使い、Seed StudioのXiao ESP32C3でOTAアップデートを試しました。
本記事では、動作環境、AWS側とESP32-C3側の設定、OTAアップデートの実行手順、実際に試した際につまずいた点を紹介します。
今回はMQTT経由でファームウェアを配信します。MQTTやOTAアップデートの原理についての解説は行いません。
手順
基本的にはiot-reference-esp32のドキュメントに従って行います。
1. 動作環境
- 使用したESP32-C3ボード:Seeed Studio XIAO ESP32C3
- OS:Windows 11
- ESP-IDFのバージョン:v5.5.5
- IDE:VS Code (ESP-IDFの拡張機能)
2. AWSのセットアップ
ここではIAMの設定は省略します。
https://github.com/FreeRTOS/iot-reference-esp32/blob/main/AWSSetup.md
ここをもとに設定してください。
以下の設定もドキュメント通りにやるのが分かりやすいかもしれません。
2.1. AWS IoTでポリシーを作成
https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/policy/policy
AWS IoTのダッシュボードでポリシーを追加します。

適当な名前を付けて、以上のようなポリシーを作成します。
本来はセキュリティのためにポリシーリソースはリージョンとアカウントIDの範囲を指定するべきですが、今回はワイルドカードを使用します。(良い子はドキュメント通りにやってね^_^)
2.2. 「モノ」の追加
https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/thinghub
ダッシュボードでモノを作成します。
適当な名前を付けて次へ、「新しい証明書を自動生成 (推奨)」で、先ほど作成したポリシーをアタッチし、作成します。今回は「esp32c3」としました。

モノを作成すると証明書のダウンロードページが出るので以下をダウンロードします。(全部ダウンロードしてもいいですが)
ダウンロードしたものはリポジトリのmain/certs内にいれておくと便利です。

2.3. エンドポイントの取得
最後にAWSのエンドポイントを取得します。ドキュメントでは設定から取得するようになっていますが、現在は場所が移動して「ドメイン設定」にあります。
エンドポイントをどこかに控えておいてください。

2.4. S3バケットの作成
ここで、アップデートするファームウェアを保存するためのバケットも作成します。
https://ap-northeast-1.console.aws.amazon.com/s3/home?region=ap-northeast-1#
ダッシュボードでバケットを作成します。
適当な名前をいれ「バケットのバージョニング」有効にしてください。あとはそのままでいいです。
2.5. ロールとポリシーの設定
OTAに必要なロールやポリシーを設定します。IAMの設定は以下に従ってください。
https://github.com/FreeRTOS/iot-reference-esp32/blob/main/GettingStartedGuide.md 5.1 Setup pre-requisites for OTA cloud resources
https://docs.aws.amazon.com/freertos/latest/userguide/create-service-role.html
https://docs.aws.amazon.com/freertos/latest/userguide/create-ota-user-policy.html
2.6. 証明書の作成
http://docs.aws.amazon.com/freertos/latest/userguide/ota-code-sign-cert-win.html
上記のURLをもとにファームウェア用の証明書を作成します。(OpenSSLが必要です。)
作成した証明書はダッシュボードから追加することもできます。ただ追加しなくてもあとで追加できるのでこれはどっちでもいいです。
作成した公開鍵証明書の内容を「main/certs/aws_codesign.crt」にコピーしてください。
3. ESP32-C3の設定
3.1. VS Codeの設定
今回はVS Codeを開発環境として利用します。
ESP-IDFの拡張機能を入れていない場合、入れてください。
Ctrl + Shift + Pで「ESP-IDF」といれ、「ESP-IDF:Welcome」をクリックし、以下のページを開きます。
ESP-IDFをダウンロードしていない場合は「Configure Extension」からv5.5.5をダウンロードしておいてください。

その後、「Import project」からiot-reference-esp32を選択し、インポートしてください。ディレクトリを選べとでますが、Use Current...でOKです。
3.2. ESP32のコンフィグ
VSCodeの下部の歯車マークでコンフィグを開きます。(ついでにチップがesp32c3、IDFがv5.5.5になっているか確認してください)
VSCode以外ではidf.py menuconfigで開けます。

VSCodeのコンフィグページだと少し分かりづらいのですが、「OTA buffer number.」の下の項目3つを入力します。エンドポイントは2.3.で取得したもの、Thing nameは2.2. で作成した際の名前です。上のSaveで保存します。

3.3. 証明書類のプロビジョニング
デバイス証明書・秘密鍵・CA証明書をプロビジョニングします。
下部のターミナルのアイコンから、ESP-IDFのターミナルを開きます。

PCにESP32を接続し、証明書を書き込みます。
ドキュメントのコマンドだとエラーになってしまうので以下のコマンドで実行します。
PORTとファイルのパスは自分の環境に合わせてください。一旦書き込まずファイル生成します。
python managed_components/espressif__esp_secure_cert_mgr/tools/configure_esp_secure_cert.py -p PORT --keep_ds_data_on_host --ca-cert ./main/certs/root_cert_auth.crt --device-cert DEVICE_CERT_FILEPATH --private-key PRIVATE_KEY_FILEPATH --target_chip esp32c3 --secure_cert_type cust_flash_tlv --priv_key_algo RSA 2048 --skip_flash以下のコマンドで書き込みます。
python -m esptool --chip esp32c3 -p PORT write_flash 0xD000 ./esp_secure_cert_data/esp_secure_cert.bin3.4. ファームウェアのビルド・書き込み
下部の「Build, Flash and Monitor」でビルドし、書き込みます。
最初のビルドはそれなりに時間がかかります。
書き込めると、モニターモードに入りコンソール出力で二次元コードが表示されると思います。次へ進んでください。

3.5. Wi-Fiの接続
モニターが開かれると、以下のような二次元コードが表示されます。

このコードを「ESP BLE Provisioning」というアプリで読み取ります。(ESP BLE Provisioningはモバイルアプリです。お持ちのスマホにアプリをインストールしてください。)
アプリで読み取ると、Wi-Fiの設定画面になるので、ここでWi-Fiに設定してください。
BLE以外の設定でSoftAPで行う方法もコンフィグから設定可能です。(その際はESP SoftAP Provisioningが必要です。)
Wi-Fi設定の削除方法
一度登録するとWi-Fiの設定が残り続けるので消したい場合以下のコマンドで削除します。
parttool.py -p PORT --partition-table-file partitions.csv erase_partition --partition-name=nvs4. MQTTの確認
Wi-Fiに接続すると正しく設定できていればAWSに接続しMQTTでメッセージを送るようになります。
https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/test
AWSのテストクライアントで「トピックをサブスクライブする」のトピックに「#」を入力し、サブスクライブします。
サブスクライブすると、いくつかのメッセージが確認できると思います。デモでは温度が送られるようになっていますが、センサーがないため0が送られます。

5. OTAアップデートの実行
いよいよOTAアップデートをしていきます。
5.1. アップデートするファームウェアの作成
まずは更新するファームウェアを作成します。今回は同じファームウェアのバージョンをインクリメントしたものを使用します。
コンフィグを開いて(開き方は3.2.)バージョンを上げます。
「Application version build」を1にして保存し、ビルドしてください。

5.2. OTAアップデートのジョブを作成
AWS IoTのコンソールの「リモートアクション」、「ジョブ」から新しいジョブを作成します。
https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/jobhub
「FreeRTOS OTA 更新ジョブを作成」を選択し次へ、適当な名前をつけ次へ、「OTA ファイル設定」ページの「デバイス」でアップデートするモノを選択し、「ファイル」で「新しいファイルに署名します。」を選択。
ここが重要です。
「既存のコード署名プロファイル」を最初はまだないので作成します。二回目以降は、作成したものを選択します。
適当な名前をつけ、「デバイスのハードウェアプラットフォーム」は「ESP32-DevKitC」を選択します。
証明書は2.6. でAWSにアップロードした場合はそれを選択、していない場合は、ここでアップロードしてください。
「デバイスのコード署名証明書のパス名」は「/」を入れて作成します。

「ファイル」は「新しいファイルをアップロードします。」を選択し、アップデートするファームウェアを選択してください。ファームウェアはプロジェクトディレクトリのbuild/FeaturedFreeRTOSIoTIntegration.binを選択します。
「S3でのファイルのアップロード先」は2.4.で作成したバケットを選択します。
「デバイス上のファイルのパス名」には「NA」といれます。

「IAMロール」で2.5.で作成したロールを選択し、次へ。
そのまま作成します。
5.3. OTAアップデート
ジョブを作成するとファームウェアがチャンク単位でダウンロードされるログを確認できます。
ダウンロードが完了し、署名検証に成功するとデバイスが再起動し、新しいバージョンが起動します。
ログを見ているとバージョンが0.0.1に上がっていることが確認できると思います。
I (1020) main: Application version number: 0.0.1AWSを確認するとジョブのステータスが「完了」になっていると思います。
以上でOTAアップデートは完了です。
6. 試していてつまずいたところ
- AWS IoTのエンドポイントの場所が公式ドキュメントと異なった。
configure_esp_secure_cert.pyの公式掲載コマンドでは動かなかった。- ESP32-C3ではsecure certのオプションを変更した。
- IAMがシンプルに面倒くさい。
おわりに
AWS公式サンプルを使い、ESP32-C3へAWS経由でOTAアップデートを行う一連の流れを確認できました。
AWS側では、IoT Core、IoT Jobs、S3、IAMなど複数のサービスを設定する必要がありますが、一度仕組みを構築すれば、遠隔地にあるデバイスへファームウェアを配信できるようになります。
また、公式サンプルを利用することで、OTA処理を一から実装する場合と比べて、全体の構成や処理の流れを把握しやすいと感じました。一方で、ESP32-C3向けの設定や権限まわりでは確認が必要な箇所もあり、実際に動かしてみることで理解が深まりました。
ただ、公式サンプルではThing Nameがファームウェアに組み込まれてしまっているため、複数台を同じファームウェアでアップデートする場合はThing NameをNVSに保存するような変更が必要になるかとは思います。
今回はIAMの設定のやり方の説明が面倒だったので割愛しましたが、時間があるときに追加できればと思います。