ThingsBoard / ESP32のFirmwareをOver-The-Airで更新する
Overview
IoT機器を開発するのに、FirmwareのOTA(遠隔での更新)は重要な機能のひとつです。ThingsBoardにもOTAの機能がありますのでこれを紹介します。後述する通り、Device側(本記事ではESP32)でOTAできるような実装も必要になります。ThingsBoardを使わずとももちろんOTAはできますので、ESP32の実装の参考にもなればと思います。
使うもの
- Arduino
- PlatformIO
- ESP32
作るものの全体像
OTAの機能がある、といってもThingBoardが担う範囲は期待するより小さく、大別すると以下2つです。
- BuildしたFirmwareのBinaryを保管する
- Shared AttributesとしてDeviceのOTAのメタ情報を管理する
前者は単純にストレージとしての役割です。認証付きのストレージと大差ありません。
後者に関しては以下を見てください。こちらの記事でShared Attributeの扱い方について触れましたが、これとほぼ同じで、Shared AttributeでFirmwareのメタ情報(更新したFirmware名など)をDeviceと授受します。更新対象のFirmwareをDeviceに把握させ、そのBinaryをDeviceに渡すことでFirmwareを更新します。
本記事ではESP32からThingsBoardに対してHTTPリクエストを行うことでFirmwareのBinaryをダウンロードしてOTAするようなFirmwareを例示しますが、MQTTで受け取るなど方法は様々です。いずれにしてもESP32でFirmwareを受け取る仕組みを実装しなければならず、ThingsBoardさえあればうまいことOTAしてくれるものではありません。
Firmwareの実装
以下にFirmwareの例を書きました。
OTA前後の2つのFirmwareが必要なので、platformio.iniで [env:beforeOTA] と [env:afterOTA] の2つを用意しましたが、いずれも ota.cpp
を参照しています。
重要な部分だけピックアップしてみます。
Attributeの変更の検知
ThingsBoardのMQTT Brokerに接続するようにした上で、callback関数を定義して、pubsubClient.setCallback(callback);
に設定しています。これでThingsBoardでShared Attributesが変更をESP32が検知したら、callback
が実行されます。
callback
の中身
callback
では、まずThingsBoardからのメッセージを受信します。ThingsBoardの設定完了後、実際にFirmwareを更新してみると以下のようなメッセージを受け取ります。
{"fw_title":"TEST","fw_version":"1.1.0","fw_tag":"TEST 1.1.0","fw_size":818512,"fw_checksum_algorithm":"SHA256","fw_checksum":"68c371163883760963fc1d8ffba80d3bcccaaf57dbb1b363b4eeab40fff3a99c"}
とりあえずはDeviceのAccess Tokenに加えて、Firmwareのタイトルとバージョン(それぞれ fw_title
、fw_version
)さえあれば、Firmware APIのendpointにリクエストし、Firmwareをダウンロードすることができます。
HTTPUpdateを使ってOTAする
更新する対象のFirmwareのURLがわかっている場合には、HTTPUpdate を使うのが便利です。以下のような記述のみでOTAできるようになります。
#include <WiFi.h>
#include <HTTPUpdate.h>
WiFiClient OTAclient;
httpUpdate.update(OTAclient, char *<FirmwareのURL>);
メッセージ受け取る -> FirmwareのTitleとVersionを取得し、URLを構成する -> HTTPUpdateを実行する、これだけでOTAできてしまいます。
Firmwareを書き込む
実装できたらESP32にOTA前のFirmware( env:beforeOTA
)を書き込みます。簡単のためDevice Profile: default
に紐づけたDeviceのAccess Tokenを設定して書き込みます。
補足
ota.cpp中に以下のような記述をしました。SETUP_MODE
が定義されていれば、Preferenceを使ってWiFiのSSID、パスワード、ThingsBoardのAccess Tokenをフラッシュメモリに書き込み、それ以降はフラッシュメモリの値を読み込んで使うようにしています。
SETUP_MODE
はenv:beforeOTA
でのみ定義されていて、env:afterOTA
では定義していません。初回のFirmwareの書き込みでは、WiFiやAccess TokenなどDevice個別の情報が必要になりますが、env:afterOTA
はDeviceに帰属する情報は入れずに汎用的に使えるFirmwareにすべきなのでこのような実装にしています。
FirmwareのBinaryを取得する
このあとThingsBoardの設定に移りますが、FirmwareのソースコードではなくBinaryをアップロードする必要があります。この記事ではPlatformIOを使うので、PlatformIOの使い方の解説になってしまいますがご了承ください。
env:afterOTA
をBuildします。GUI使ってもいいですし、CLIだと platformio.ini
が存在するディクトリで以下のコマンドを実行することになります。
$ pio run -e afterOTA
その後、同じディレクトリで以下のコマンドを実行します。build
は以下はbuildコマンドを実行すると生成されます。この中に firmware.bin
というファイルがありますが、これがFirmwareのBinaryです。これをこのあとThingsBoardにアップロードします。
$ ls -l .pio/build/afterOTA/
total 59584
drwxr-xr-x@ 91 kazrin staff 2912 2 25 17:11 FrameworkArduino
-rw-r--r--@ 1 kazrin staff 17536 2 25 17:11 bootloader.bin
-rw-r--r--@ 1 kazrin staff 818704 2 25 17:11 firmware.bin
-rwxr-xr-x@ 1 kazrin staff 13224344 2 25 17:11 firmware.elf
-rw-r--r--@ 1 kazrin staff 11831438 2 25 17:11 firmware.map
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 lib04a
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 lib239
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 lib6a3
-rw-r--r--@ 1 kazrin staff 3299502 2 25 17:11 libFrameworkArduino.a
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 libd2e
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 libdc4
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 libf0b
drwxr-xr-x@ 4 kazrin staff 128 2 25 17:11 libfe5
-rw-r--r--@ 1 kazrin staff 3072 2 25 17:11 partitions.bin
drwxr-xr-x@ 7 kazrin staff 224 2 25 17:11 src
ThingsBoardの設定
Firmwareのアップロード
ThingsBoardの設定に移ります。以下の画面からFirmwareをアップロードすることができます。
Advanced features > OTA updates から「+」をクリックです。
以下を設定した上でAdd
でFirmwareをアップロードします。
- Title:
TEST
を入力 - Version:
1.1.0
を入力。1.0.0
以外ならなんでも可。 - Device profile:
default
- Package type:
Firmware
- Upload binary fileを選択して、さきほど取得した
firmware.bin
をアップロード
Firmwareの実装で、TitleがTEST
で、Versionが1.0.0
ではない場合のみOTAを実行するように記述しました。ref
そのため、ここではTitleに TEST
、Versionに1.0.0
以外を指定する必要があります。
Firmwareを更新
それではFirmwareの更新を行ってみます。さきほど実装したFirmwareがESP32で動作していて、かつインターネットに接続していることを確認した上で行います。Serial Monitorで出力を見ながらの方がわかりやすいです。
ThingsBoardの対象Deviceを開き、"Assigned firmware"にアップロードしたFirmwareを指定します。
すると、Serial Monitorに以下のような出力が見られます。
21:17:29:093 -> Message arrived [v1/devices/me/attributes] {"fw_title":"TEST","fw_version":"1.1.0","fw_tag":"TEST 1.1.0","fw_size":818688,"fw_checksum_algorithm":"SHA256","fw_checksum":"fb0c844fe39f714c3c1ab2444e87a4d00adf45ea6ba09729d3e225b422544d4f"}
21:17:29:112 -> Update firmware
21:17:31:057 -> Callback: OTA process started
21:17:31:160 -> Callback: OTA process at 0 of 818688 bytes (0.0%)
21:17:31:174 -> Callback: OTA process at 0 of 818688 bytes (0.0%)
21:17:31:459 -> Callback: OTA process at 4096 of 818688 bytes (0.5%)
(中略)
21:18:19:108 -> Callback: OTA process at 818688 of 818688 bytes (100.0%)
21:18:19:111 -> Callback: OTA process at 818688 of 818688 bytes (100.0%)
21:18:19:383 -> Callback: OTA process finished
21:18:19:443 -> ets Jul 29 2019 12:21:46
21:18:19:443 ->
21:18:19:443 -> rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
21:18:19:448 -> configsip: 0, SPIWP:0xee
21:18:19:451 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
21:18:19:457 -> mode:DIO, clock div:2
21:18:19:459 -> load:0x3fff0030,len:1184
21:18:19:463 -> load:0x40078000,len:13232
21:18:19:465 -> load:0x40080400,len:3028
21:18:19:465 -> entry 0x400805e4
重要な部分をピックアップすると以下の流れで、無事OTAが完了しました。
- 21:17:29:093 -> Shared Attributeの更新を検知
- 21:17:29:112 -> Firmwareのダウンロード開始
- 21:18:19:383 -> ダウンロード完了
- 21:18:19:443 -> 再起動
補足
より実践的なFirmwareの実装
本記事で例示したFirmwareのOTAは「ESP32がインターネットに接続している間に、ThingsBoardでAssignするFirmwareを更新」しなければならない実装になっています。しかし、ESP32がいつでもインターネットに接続しているとは限らないので、「起動時に1度だけ」もしくは「定期的に」Attributeを確認して、今のFirmwareとAttributeに設定されているFirmwareのバージョンに違いがないかを検証した上でOTAを実行する、というのが実践的なFirmwareの実装になると思います。
Device ProfileにFirmwareをAssignする
上の手順ではDeviceにFirmwareをアサインしましたが、Device ProfileにAssignすることで、そのDevice Profileに属するすべてのFirmwareを対象に一括でShared Attributeを更新することもできます。とすると、なおさらすべてのDeviceがインターネットに接続しているとは限らないので、先述のような実装が好ましいです。
まとめ
ThingsBoardを使ったFirmwareのOTAについて書きました。とりあえずOTAを試す目的で不要な箇所は削ぎ落としたFirmwareになっているのでかなりシンプルですが、OTAが失敗して装置が動かなくなるリスクをはらんでいるので、エラーハンドリングなど考慮すべき事項は多々あると思います。OTA難しい。
Discussion