Node-RED MCUのOTA updateノードでデバイスのファームウェアを更新する

　Node-RED MCUに「OTA update」ノードが提供されて、WiFiが接続されていればデバイスにケーブルを接続しなくてもOTA（On The Air）でファームウェア（デバイス上のソフトウェア）を更新できるようになりました。「ota update」ノードはファームウェアを更新したあとにデバイスをリスタートするための「mcu restart」ノードとペアで動作します。

:::note info
ここではコマンド等はUbuntuの事例として紹介します。
異なる環境でお試しの際は環境に合わせて読み替えてください。
:::

#### 試した環境
Node-RED Version : v3.1.3
Node.js Version : v18.17.1
Operating System : Ubuntu 22.04.3 LTS
Moddable SDK Version : v4.3.8
Node-RED MCU Plugin Version : v1.4.1-beta.1
ota-update node Version : 0.8.8

#### 関連の情報

これに使用した、[Node-RED MCU Editionについてはこちら](https://github.com/phoddie/node-red-mcu/blob/main/readme.md)をご覧ください。

https://github.com/phoddie/node-red-mcu/

また、この内容は@kitazakiさんの記事で紹介されている書籍、補足書に掲載した内容のリライト版です。図や解説を追加しています。

https://qiita.com/kitazaki/items/0768eb7484ee3c27f6e1


### ノードの追加
使用する「ota update」ノードと「mcu restart」ノードは、Node-ERD MCUをセットアップしたデフォルトの状態では含まれていません。ターミナルを起動して、下記のように.node-redフォルダに移動して２つのノードをインストールします。インストールができると、パレットのMCUタブにノードが表示されます。
```
$ cd ~/.node-red
$ npm install @moddable-node-red/ota-update
$ npm install @moddable-node-red/mcu_restart
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/8472c369-4740-bd52-d211-cff33dd5aa8b.png)
「ota update」ノードと「mcu restart」ノード

:::note warn
ノードが更新されました。
ota updateノードは2023/1/10のv0.8.8以降のものを使用してください。

もし上手くいかない場合はNode-RED MCU プラグインの再インストールや、Moddable SDKの最新版を取得するなどお試しください。
:::

### ２種類のOTA Update
OTA Updateの方法はPullとPushの２種類あります。
利用する目的や環境に応じて使い分けます。
#### 1) OTA Update - Pull
更新するための新しいファームウェアをHTTPサーバ上に配置し、デバイス側の操作でソフトウェアを引いてきて(Pull)更新します。クラウドサーバ上にファームウェアを置くことで、WiFiに接続できれば場所を問わず更新することができますので、配布したデバイスのファームウェアをユーザーに更新してもらうといった使い方ができます。ユーザーの操作で更新が開始されますので、セキュリティ的にも安全です。
#### 2) OTA Update - Push
更新するための新しいファームウェアを、デバイスのIPアドレスを指定して提供側から押し込んで(Push)更新します。IPアドレス指定でファームウェアの書き換えができますので、セキュアなローカルネットワーク内などで利用するには便利な機能ですが、パブリックなネットワークに公開すると意図せずファームウェアが書き換えられるリスクもあります。もしもそういうリスクが想定される場合はPullを使用してください。

### テスト用ハードウェア
　ファームウェアの更新を確認するためにLEDをふたつ使用します。それぞれGPIO26と27に接続します。また、pullモードでファームウェア更新を行うためにGPIO12にボタンを接続しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/f593dc9e-6f94-bbdc-dc14-772e5cc70aa4.png)

### フローファイル
この記事で紹介しているサンプルフローはGithubで公開しています。
[こちらからダウンロード](https://github.com/mshioji/examples_node-red-mcu/tree/main/Qiita-articles/ota-update)できます。

## ■　OTA Update - Pull　を使う
PullモードではHTTPサーバに更新用ファームウェアを置き、デバイス側のアクションでファームウェアの更新を開始します。

#### ファームウェア更新のテストを行う流れ
今回はOTAでファームウェアが更新できることを確認するため、２種類のファームウェアを作ってWebサーバに置いておき、デバイス側からPullで更新する事を試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/d02d76ca-8736-28cd-5432-ff87afa8314b.png)

##### ファームウェアA : blink One
ファームウェアAでは、LEDをひとつ点滅させるフローをメインの処理にしておき、このファームウェアの中にはファームウェアBにOTAで更新するフローを含めておきます。
##### ファームウェアB : blink Two
ファームウェアBでは、LEDをふたつ点滅させるフローをメインの処理にしておき、このファームウェアの中にはファームウェアAにOTAで更新するフローを含めておきます。


このように、LEDをひとつだけ点滅させるファームウェアAが動作している時にOTA Updateを動かすと、ファームウェアBに更新されてLEDの点滅がふたつになる、というように動作の変化を見ることができます。


#### 1. HTTPサーバの準備
この例ではNode-REDを動かしているのと同じUbuntuマシンにHTTPサーバとしてApacheをインストールして、公開ディレクトリの/var/www/html/内にfirmwareフォルダを作成して更新用ファームウェアを置きます。稼働しているサーバをお持ちの場合はそちらを利用すると良いでしょう。

・Apacheのインストール
```
$ sudo apt update
$ sudo apt install apache2
```

インストールができたらWebブラウザからテストページを開いて動作を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/e6a32799-63ca-c3de-678a-47579f41d78d.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/bb64f61a-87ec-3159-9f7c-092f21cce3e6.png)
　　　　　Apacheの動作確認用ページ

Apacheの公開用フォルダは /var/www/html ですので、今回はこの中に新たにfirmwareフォルダを作成して更新用ファームウェアを置く場所にします。
```
$ sudo mkdir /var/www/html/firmware
```
#### 2. blinkOne フローの作成とファームウェアファイルの取り出し

PullモードでOTA Updateを行うフローを作成します。例として作成するフローの上段はメイン処理としてBlink(Lチカ)1灯、下段がファームウェア更新のフローです。ESP32を接続してビルドすると得られるファームウェアファイルを「blinkOne.bin」としてWebサーバにも保存します。

![Screenshot from 2024-01-08 13-27-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/a53be41c-a980-3b49-ded7-74898e45c7f4.png)
　　　　　Blink１灯のフロー
     
OTA Updateの手前にある「change」ノードを使用してmsg.urlにファームウェア公開用のHTTPサーバと、更新するファームウェアを指定します。

・HTTPサーバのアドレスはご利用になるファームウェア公開用サーバのアドレスをセットします。先ほどの手順でローカルマシンにApacheをインストールしてそれを使用する場合はそのPCのIPアドレスとなります。

・HTTPサーバのアドレスに加えてファームウェアを指定します。ここで作成するBlink1灯のフローでは、後で作成するBlink2灯のファームウェア「blinkTwo.bin」に更新するように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/99725836-f1d6-9cd5-c4df-96b7e8925776.png)
　　　　　firmware設定の「change」ノード

更新用のファームウェア（バイナリファイル）は、ESP32を接続してフローをビルドすることで作成されます。ターゲットデバイスなどのビルド設定によって作成される場所が変わりますので、ビルドののち、下記に沿って目的のファイルを見つけます。

以下の例は、ESP32 Devkitモジュール(node mcu)を使用してビルドした場合です。
適用するターゲットデバイスやビルド設定に従って読み替えてください。

　・Node-RED MCU プラグインのMCU Build Configurations 設定の例
　　ESP32 | Espressif		(1)
　　ESP32/nodemcu			(2)
　　　Build Target: all
　　　Instrumentation: Debug	(3)
　　Plug-inで作成するBuildフォルダ（フォルダ名は変わります）
　	　（例）zf1o8otuo8o		(4)

　更新用ファームウェアの場所
　	ビルドしたファイルができる場所は $MODDABLE/build/bin内の、上記1~4
　	のビルド設定内容に沿って示された場所となります。
```
　	$MODDABLE/build/bin/esp32/nodemcu/debug/zf1o8otuo8o/  
```
このフォルダに移動し、内容を確認します。
```
$ cd $MODDABLE/build/bin/esp32/nodemcu/debug/zf1o8otuo8o/
$ ls -l
-rw-r--r-- 1 mshioji mshioji    17520  bootloader.bin
-rw-r--r-- 1 mshioji mshioji     8192  ota_data_initial.bin
-rw-r--r-- 1 mshioji mshioji     3072  partition-table.bin
-rw-r--r-- 1 mshioji mshioji 16657098  xs_esp32.a
-rw-r--r-- 1 mshioji mshioji  1500944  xs_esp32.bin
-rwxr-xr-x 1 mshioji mshioji 14302204  xs_esp32.elf
-rw-r--r-- 1 mshioji mshioji  8266409  xs_esp32.map
-rw-r--r-- 1 mshioji mshioji   455868  xs_esp32.sym
$
```
上記の中で「xs_esp32.bin」が目的のファームウェアファイルです。

この「xs_esp32.bin」を「blinkOne.bin」と名称を変更して先に用意したHTTPサーバのファームウェア用のフォルダにコピーします。

```
$ cd $MODDABLE/build/bin/esp32/nodemcu/debug/<build folder>/
$ sudo cp ./xs_esp32.bin /var/www/html/firmware/blinkOne.bin
```

#### 3. blinkTwoのファームウェアを準備
もうひとつBlink２灯のフローを作成し、ビルドします。このとき得られるバイナリファイルも「blinkTwo.bin」としてHTTPサーバのファームウェア用のフォルダに保存します。

![Screenshot from 2024-01-08 13-27-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/395efde9-28ed-3950-2a02-c91c1d283c06.png)
　　　　　　　　　　2灯Blinkのフロー

blink２灯のフローから更新するfirmware設定ではblink１灯の「blinkOne.bin」を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/6f2a9846-5c47-3c9d-91f5-6431d3a55299.png)
　　　　　　　　　firmware設定の「change」ノード

以下のコマンドで「xs_esp32.bin」を「blinkTwo.bin」と名称を変更して先に用意したHTTPサーバのファームウェア用のフォルダに保存します。
```
$ cd $MODDABLE/build/bin/esp32/nodemcu/debug/<build folder>/
$ sudo cp ./xs_esp32.bin /var/www/html/firmware/blinkTwo.bin
```

#### 4. Pullモードでのファームウェア更新
ネットワークに接続してHTTPサーバにアクセスできる状態で、デバイスのボタン(GPIO 12)を押すと、LED２灯点滅が動作している場合は１灯点滅の「blinkOne.bin」への更新が行われて点滅動作が変更されます。LED１灯点滅が動作している場合は２灯点滅の「blinkTwo.bin」への更新が行われて点滅動作が変更されるのを見ることができます。

:::note info
ファームウェアの更新には時間が掛かりますので、しばらくお待ちください。
:::
MCUデバイスをUSBでPCに接続し、プラグインでビルドして書き込んだ状態では「OTA update」ノードで更新の状況をモニタすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/df6288b8-0b03-0608-df08-79a82933ea29.png)
ファームウェア更新中
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/6a8b54ba-9d75-c078-8b08-ab45e7ca84ec.png)
ファームウェア更新完了

:::note info
WiFi経由での更新ですので、USBケーブルで接続せずにMCUデバイスをバッテリーで動かしているような場合でもアップデートができます。
:::

## ■　OTA Update - Push　を使う

PushモードではローカルのPCやローカルネットワーク上のサーバに置いた更新用ファームウェアをPC/サーバ側の操作でデバイスに送信して更新します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/0fd15a41-eaa3-ec39-5c8a-ca6d31cba69f.png)

こちらもデバイス用のフローとして「push-blinkOne」と「push-blinkTwo」の２種類、そしてPC/サーバ上で動作するファームウェア更新を実行するフローを作成して使用します。

#### 1. デバイス用ファームウェア「push-blinkOne」の作成
![Screenshot from 2024-01-08 13-27-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/9b0681c5-8be3-19e5-94cd-a75fc3f3cb5f.png)
　　　　　「push-blinkOne」　のフロー（blink１灯）

Pushモードで使用する「OTA update」ノードは入力端子には何も接続する必要がありません。ノード設定を開いてPush Endpointに「/ota/firmware」を設定します。

:::note info
このPush Endpointを設定しない場合、Pushモードは無効になっています。
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/e0948783-d7aa-9097-a60b-cdc19f6cbbcd.png)
　　　　　Push モードの「OTA update」ノード設定

ビルドして得られたファームウェアを適当なフォルダを作成してpush-blinkOne.binの名称で保存します。この例では/home/firmware を作成して保存します。
```
$ cd $MODDABLE/build/bin/esp32/nodemcu/debug/<build folder>/
$ sudo cp ./xs_esp32.bin /home/<user>/firmware/push-blinkOne.bin
```
#### 2. デバイス用ファームウェア「push-blinkTwo」の作成
もうひとつ、同じようにLED２灯点滅のファームウェア「push-blinkTwo」も作成します。
![Screenshot from 2024-01-08 13-28-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/d6fc5e3f-b4ce-fcbf-168a-c936c2f7741c.png)
　　　　　「push-blinkTwo」　のフロー（blink２灯）
     
こちらも先ほどと同じディレクトリにpush-blinkTwo.binの名称で保存します。
```
$ cd $MODDABLE/build/bin/esp32/nodemcu/debug/<build folder>/
$ sudo cp ./xs_esp32.bin /home/<user>/firmware/push-blinkTwo.bin
```

#### 3. PC/サーバ用フローの作成
![Screenshot from 2024-01-08 13-37-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/2fd4f7b5-2153-93ce-35bd-8829011198d5.png)
　　　　　　Pushモード　PC/サーバ用フロー
      
動作としては、インジェクトノードで「read file」ノードを動かしてファームウェアファイルを読み出し、それに続く「HTTP request」ノードを使用してデバイスに送信します。

上側の「read file」ノードの設定では、さきほどPullモードの時に作成したファームウェアファイルのpush-blinkOne.binを読み出すようにします。
下側の「read file」ノードの設定ではpush-blinkTwo.binを読み出すようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/33515877-ab2b-546f-d4ba-723e7cb4fee2.png)
　　　　　　「read file」設定内容

「HTTP request」ノードは、下記のとおり設定します。
メソッド: PUT
URL: http://<更新するデバイスのIPアドレス>/ota/firmware
出力形式：バイナリバッファ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/58e18288-d976-2b4f-3fcc-4eba6679b9f1.png)
　　　　　「HTTP request」ノードの設定

#### 4. Pushモードでのファームウェア更新
デバイスがネットワークに接続してPC/サーバからアクセスできる状態で、どちらかの「inject」ノードのボタンを押すと、設定に従ってファームウェアの更新が行われ、デバイスの動作が変更されます。

:::note info
ファームウェアの更新には時間が掛かりますので、しばらくお待ちください。
:::
MCUデバイスをUSBでPCに接続し、プラグインでビルドして書き込んだ状態では「OTA update」ノードで更新の状況をモニタすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/e2c04dda-6b3f-db8c-5672-2142160db3c5.png)
ファームウェア更新中
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012948/632f7c98-7575-0674-69b2-4ada0d423d61.png)
ファームウェア更新完了

### 最後に
　これまではベランダなど家の外に設置していたセンサデバイスなどのソフトウェアを更新するにはいったん取り外して家の中に持ってきてPCに接続して更新していましたが、「ota Update」のフローをメインの処理に追加しておくとWiFiがつながる環境であれば家の中から更新できるようになりました。離れたところでMCUデバイスを動かしている所には便利だと思います。
　また、何かの製品に使用するような場合でもファームウェア公開サーバを用意しておけば、Pullでユーザーに更新してもらえるようにもできるので可能性が広がると思いました。