2023年4月17日月曜日

thingsboardでOTAを利用してESP32のプログラムを更新


背景

thingsboardとはオープンソースのIoT機器管理サーバープログラムです。
以前起動と情報送信と情報の閲覧を行いました。

ログ取りサーバーthingsboardをRaspberry Piで動かしてデータを表示

今回は機能一覧に並んでいたOTA(Over the air: 無線でのプログラム更新)の使い方が気になったので、ESP32のプログラム更新を試してみました。
備忘録を兼ねて使い方を共有します。

使ったもの

  • thingsboardを動かすサーバー
    Raspberry Piで動かしています。
  • ESP32開発ボード
    M5Stackでもesp32devkitcでも良いです。
  • platformioを使えるPC
    ESP32のプログラム作成に利用します。
    ビルド後のファイルを扱うので、Arduino IDEよりplatformioの方が便利だと思います。

ESP32のプログラム作成

thingsboardはarduino向けのsdkをライブラリとして公開しているので、そのライブラリとサンプルコードを利用します。

sdkのサンプルコードのシリアルの通信速度は115200bpsなので、monitor_speedをそれに合わせます。
platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps = ThingsBoard

上記の設定で一度ビルドを走らせてライブラリをダウンロードしてからコードの編集に取り組むとplatformioの静的解析が動いてくれて便利です。

OTAのサンプルコードはバージョンを購読しておいて切り替わったら更新処理を行うもの(0011-esp8266_esp32_subscribe_OTA_MQTT.ino)一度だけ更新処理を実施するもの(0009-esp8266_esp32_process_OTA_MQTT.ino)の2種類があります。

マイコンを動かしたままにする場合は、前者のバージョンを購読しておいて切り替わったら更新処理を行うものを使うのが良いです。
マイコンの休止管理を実装して定期的に起動と休止を繰り返す場合は後者の一度だけ更新処理を実施するものを利用すると、より効率の良い処理になると思います。

ということでバージョンを購読しておいて切り替わったら更新処理を行うもののコードをmain.cppにコピーして使います。
platformioのmain.cppはarduinoのヘッダの読み込みが必要なので、ファイルの先頭でArduino.hをincludeします。

src/main.cpp
#include <Arduino.h>
// バージョンを購読しておいて切り替わったら更新処理を行うコードを貼り付け
// paste 0011-esp8266_esp32_subscribe_OTA_MQTT.ino

情報を送信するthingsboardに合わせてコピーしたコードの下記の箇所を変更します。

WiFi情報
ESP32を動かすWiFi環境に合わせて設定してください。
src/main.cpp
constexpr char WIFI_SSID[] PROGMEM = "YOUR_WIFI_SSID";
constexpr char WIFI_PASSWORD[] PROGMEM = "YOUR_WIFI_PASSWORD";

アクセストークン
デバイスのアクセストークンを設定します。
アクセストークンの参照方法はthingsboardをRaspberry Piで動かした記事が参考になります。
この記事ではthermostat2のアクセストークンを利用しています。
src/main.cpp
constexpr char TOKEN[] PROGMEM = "YOUR_DEVICE_ACCESS_TOKEN";

サーバーのホスト名
thingsboardを動かしているPCのIPに変更してください。
thingsboardをRaspberry Piで動かした環境を引き続き利用しているため、自分は「192.168.171」に設定して動作確認しました。
src/main.cpp
constexpr char THINGSBOARD_SERVER[] PROGMEM = "demo.thingsboard.io";

これは任意の変更です。
変更情報購読のプログラムは自身のバージョン情報を確認しづらい(更新実施後に表示するものの、ログが流れて確認しづらい)ので、どのバージョンで動いているか知らせるためのシリアル出力を追加します。
変更関連のコードをまとめるためにupdateRequestSentのif文内に下記の行を追加しました。
src/main.cpp
  if (!updateRequestSent) {
// updateRequestSentのif文内に下記の行を追加
Serial.println("Current firmware version is " + String(CURRENT_FIRMWARE_VERSION));

上記の変更を行ったプログラムをESP32に書き込みます。

シリアルモニタを開いた状態でリセットして下記のようなログが表示されたら、wifiの接続とthingsboardへの接続が成功しています。
Connecting to AP ...
.....Connected to AP
Connecting to: (192.168.1.171) with token (T2_TEST_TOKEN)
Current firmware version is 1.0.0
Firwmare Update Subscription...

このプログラムをOTAで書き換えます。

OTAで更新したいプログラムのビルドとアップロード

platformioの標準の設定だとプログラムのビルド先は隠しフォルダ.pioの中になっていてブラウザによっては辿れないので、ビルド結果の配置先をbuildフォルダに変更します。

build_dirにフォルダのパスを指定すると、それがビルド先になります。
platformio.ini
[platformio]
build_dir = build

バージョンを変更します。
タイトルはサンプルコードのTESTを維持したまま、バージョンを1.0.1にします。
src/main.cpp
#include <Arduino.h>
constexpr char CURRENT_FIRMWARE_TITLE[] PROGMEM = "TEST";
constexpr char CURRENT_FIRMWARE_VERSION[] PROGMEM = "1.0.1";

上記の設定でビルドするとbuild/esp32dev/firmware.binが作られます。
ESP32に書き込んでも問題無いですが、今回は上記のbinファイルの扱いが主です。

このfirmware.binをthingsboardのthermostatのファームウェアとしてアップロードします。

thingsboardでOTA updatesを選び、画面の+マークを押します。


編集画面で下記の入力を行います。
title: TEST
version: 1.0.1
device profile: thermostat
Browse fileを押すとファイル選択画面が出るのでfirmware.binを選びます。



全ての項目を入力すると追加ボタンが有効になるので押します。


TEST-1.0.1をアップロードできました。


なお、入力項目は作成後の部分的な変更が出来ないので、情報を変更したい場合はアップロードした情報を削除して作り直してください。

動作確認に利用するので、同様の方法で1.0.2もビルドしてアップロードします。
src/main.cpp
#include <Arduino.h>
constexpr char CURRENT_FIRMWARE_TITLE[] PROGMEM = "TEST";
constexpr char CURRENT_FIRMWARE_VERSION[] PROGMEM = "1.0.2";


thermostatに割り当てるプログラムを準備できました。

profileが利用するファームウェアを設定

標準設定で定義されているthermostatはthingsboardのprofileという設定のひとつであり、そのprofileで利用するファームウェアを指定すると、そのprofileに属するデバイス(この記事の場合はthermostat2)のOTA関連の処理が動きます。

ということで、thermostatのファームウェアをTEST-1.0.1に指定してみます。
Profile -> Device profiles を選んで画面を切替え、thermostatを選びます。


thermostatの編集ボタンを押します。


編集画面でAssigned firmwareを選びます。


先程アップロードした1.0.1を選びます。


保存ボタンを押します。


2つのデバイス(thermostatの1と2)に対して変更が加わるという警告が出るのでProceedを押して処理を進めます。
Proceedを押すと、OTAを購読しているESP32で更新処理が始まるので、ログが見たい場合はシリアルモニタを開いておきます。


ESP32で下記のようなログが出力されOTAでのプログラム更新が進みます。
[TB] Callback onMQTTMessage from topic: (v1/devices/me/attributes)
[TB] Received shared attribute update
[TB] No keys that we subscribed too were changed, skipping callback
[TB] Callback onMQTTMessage from topic: (v1/devices/me/attributes)
[TB] Received shared attribute update
[TB] Shared attribute update key: (fw_checksum) is subscribed
[TB] Calling subscribed callback for updated shared attribute (fw_checksum)
[TB] {"fw_title":"TEST","fw_version":"1.0.1","fw_tag":"TEST 1.0.1","fw_size":999392,"fw_checksum_algorithm":"SHA256","fw_checksum":"5d7084816b22845a642e3ea520406bf0f7a5754a229ae2aa3e1b659639f9f9f8"}
[TB] =================================
[TB] A new Firmware is available:
[TB] (1.0.0) => (1.0.1)
[TB] Attempting to download over MQTT...
Progress 0.41%
[TB] Callback onMQTTMessage from topic: (v2/fw/response/0/chunk/0)
[TB] Receive chunk (0), with size (4096) bytes
[TB] Callback onMQTTMessage from topic: (v2/fw/response/0/chunk/1)
[TB] Receive chunk (1), with size (4096) bytes
Progress 0.82%
[TB] Callback onMQTTMessage from topic: (v2/fw/response/0/chunk/2)
[TB] Receive chunk (2), with size (4096) bytes
Progress 1.23%

プログラムで指定したパケットの大きさ単位でダウンロードされます。
src/main.cpp
constexpr uint16_t FIRMWARE_PACKET_SIZE PROGMEM = 4096U;

ダウンロードが終わるとchecksumが照合され、合致していれば再起動します。
[TB] Callback onMQTTMessage from topic: (v2/fw/response/0/chunk/242)
[TB] Receive chunk (242), with size (4096) bytes
Progress 99.59%
[TB] Callback onMQTTMessage from topic: (v2/fw/response/0/chunk/243)
[TB] Receive chunk (243), with size (4064) bytes
[TB] (SHA256) actual checksum: (5d7084816b22845a642e3ea520406bf0f7a5754a229ae2aa3e1b659639f9f9f8)
[TB] (SHA256) expected checksum: (5d7084816b22845a642e3ea520406bf0f7a5754a229ae2aa3e1b659639f9f9f8)
[TB] Checksum is the same as expected
[TB] Update success
Done, Reboot now
ets Jun 8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13104
load:0x40080400,len:3036
entry 0x400805e4
Connecting to AP ...
...Connected to AP
Connecting to: (192.168.1.171) with token (T2_TEST_TOKEN)
Current firmware version is 1.0.1
Firwmare Update Subscription...

再起動して期待通りに1.0.1のファームウェアが動きました。
Current firmware version is 1.0.1

動作確認のため、今度はthermostatのファームウェアを1.0.2にしてみます。


OTA処理が動いて再起動し、期待通りに1.0.2のファームウェアが動きました。
Current firmware version is 1.0.2

thingsboardは現在動いているファームウェアと異なるバージョンが指定されたらOTAが動く仕様なので、1.0.2から1.0.1にダウングレードも可能です。

おわり

thingsboardのOTA機能を利用してESP32のファームウェアを更新できました。

サンプルコードの利用だとデバイスのアクセストークンをプログラムに直書きしていてthermostat1と2の区別ができないため、デバイスによって異なる情報の管理はESP32のflashメモリなどを利用する必要があります。
このまま本運用とはいきませんが、thingsboardを介してプログラムを更新できるのは便利でありがたいです。

参考

arduino向けのthingsboardのsdk(ライブラリ)です。
https://github.com/thingsboard/thingsboard-arduino-sdk

arduino向けのsdkのサンプルコードです。
バージョンを購読しておいて切り替わったら更新処理を行うもの(0011-esp8266_esp32_subscribe_OTA_MQTT.ino)
一度だけ更新処理を実施するもの(0009-esp8266_esp32_process_OTA_MQTT.ino)

thingsboardを動作確認したときの記事です。
ログ取りサーバーthingsboardをRaspberry Piで動かしてデータを表示

公式のOTAの解説です。
しかしながら、この解説ではesp-idfが使われてarduino向けのsdkが出てこないので、あまり役には立ちませんでした。
https://thingsboard.io/docs/samples/esp32/ota/

変更履歴

2023.04.24 platformio版sdkの0.10.0は最新のarduino jsonに対応していないコードが配信される不具合が解消されたので、lib_depsの指定をgithubのタグのurl(https://github.com/thingsboard/thingsboard-arduino-sdk.git#v0.10.0)からライブラリ名に変更しました。

0 件のコメント :