nRF Connect SDK:DFU(V2.0+ 改版)

# 色々と分かっていなかった過去
実はSDK 1.x時代に一度DFU(FOTA)を調べています。

https://qiita.com/yoshihiro0709/items/cbe4a8d5463bcd622405

改めて読んでみると色々とおかしなところが満載です(笑)。ECDSA-256のことをRSA-2048と書いていたり、そもそもこのRSA-2048の秘密鍵なんてものは実際には使われていなかったり(後述)とボロボロです。

::: note
一応その時点での僕の理解度を元に書き留めた備忘録ということで・・・。
:::
::: note
逆に言えば以前の記事がおかしいと言えるということはそれだけ理解が進んでいるということでもあります。(ポジティブシンキング(笑))
:::
とどめに改めてコンパイルしてみると動きません・・・。SDK 2.xになったから動かないのかと思ってSDK 1.9.1でも試してみましたがやっぱり動きませんでした。僕はいったい何を作っていたのだろうか。
# とりあえずSDK 2.1.2へ移植してみよう
元々SDK 1.x時代に作成したプロジェクトを元に移植を進めていきます。なになに・・・
```
CONFIG_BT_L2CAP_TX_MTU=252
```
こりゃいったい何の設定だ・・・よく分からんのでパス(笑)、それ以外の数字系の設定も分からないのでパス！ということでできあがったコンフィギュレーションがこちらです。
``` proj.conf
CONFIG_SECURE_BOOT=y
CONFIG_SB_SIGNING_KEY_FILE="c:/ncs/samples/dfu1/priv.pem"
CONFIG_BT_DEVICE_NAME="dfu1"
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_DEBUG_THREAD_INFO=y
CONFIG_MCUMGR=y
CONFIG_MCUMGR_CMD_IMG_MGMT=y
CONFIG_MCUMGR_CMD_OS_MGMT=y
CONFIG_MCUMGR_SMP_BT=y
CONFIG_MCUMGR_SMP_BT_AUTHEN=n
CONFIG_IMG_ERASE_PROGRESSIVELY=y
CONFIG_DEBUG_OPTIMIZATIONS=y
```
MCUMGR周りの設定については元を辿ると前記事でも紹介したスレッドから拾ってきたものですが、こちらに詳しい説明を見つけました。

https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.0.0/nrf/ug_nrf52.html#fota-upgrades

上記の記事には出てこない
```
CONFIG_MCUMGR_SMP_BT_AUTHEN=n
```

については接続した時にボンディングを要求する設定(nなのでボンディングしない)

```
CONFIG_IMG_ERASE_PROGRESSIVELY=y
```

はイメージを段階的に消す設定ということですがぶっちゃけ不要な気がします。例えば途中で停止してしまった場合を考えても、半分くらい前のイメージが残っていたところでどうになるわけでもないですし(笑)。また、
```
CONFIG_SECURE_BOOT=y
CONFIG_SB_SIGNING_KEY_FILE="c:/ncs/samples/dfu1/priv.pem"
```
についてはこちらのページに出てくるUpgradable bootloaderの設定になります。なお、実際にはこの設定はなくても大丈夫ですし、なんなら秘密鍵は実際には機能していません(笑)。

https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/app_bootloaders.html

::: note warn
きっと不具合だと信じて・・・w
:::

![Screenshot_20221116-134313.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/ab6baa57-34fe-a273-ed23-ee12d3a38bef.png)
おお、コンパイルもちゃんと通りました。これはなんだか動きそうなふいんき(なぜか変換できない)です。期待を胸にDFUを動かしてみると・・・。
## 動かない！
あれ？DFUが拒否される・・・？
何度コンパイルしてみてもどうやってみてもDFUを受け付けてくれません。何がおかしいのか全く分かりませんでした。
# いったん諦めて情報収集
どうやっても動かない(DFUが拒否される)ので、いったん諦めて情報収集をすることにしました。最初は上記のBootloaderおよびそのサブページを全て読むことに。

https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/app_bootloaders.html

英語で読んでいるとちょっと辛い(微妙なニュアンスとかがくみ取れない)ので、DeepLで翻訳を表示しながら読んでいましたが、何度読んでもここに書いてあることはちゃんとやっている、別におかしなことは何もしていないという結論に。
仕方がないのでDevZoneで誰か同じような質問をしていないか調べていたところ・・・見つけました、これです。

https://devzone.nordicsemi.com/guides/nrf-connect-sdk-guides/b/software/posts/ncs-dfu

正確には質問というよりもNordicが公式にDFUのやり方をまとめたものです。読み進めていくと・・・あれ、よく分からないと言って飛ばした設定がやはりここでも出てきています。コメントを見るとBluetoothの大きなパケットサイズを許可すると書いています。
```
# Allow for large Bluetooth data packets.
CONFIG_BT_L2CAP_TX_MTU=252
CONFIG_BT_BUF_ACL_RX_SIZE=256
```
先ほど僕が作ったものとの差異はここだけなので、これを加えて動かしてみると・・・。
![Screenshot_20221116-095220.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/9350b67c-a829-d44c-e5c0-bda753019c92.png)
## 動いた！
やりました、ついに動きましたよ。この設定が必要だったんですね。ちなみに調べてみたところ、最低値は両方とも78でした。それ以下の値を設定していると動きませんでした。デフォルト値はそれぞれ23と27なので設定してあげる必要があるということです。
ただ、この数値はコメントにもあるようにパケットサイズに関係があるため、あまり小さい数字だとこんな感じで転送データレートが落ちてやたら時間がかかってしまいます。
![Screenshot_20221116-103827.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/1f6f8e67-439e-2a05-7406-fc7e997decb2.png)
だからと言って極端に大きくしても一定のところまでしか効果はありません。これはおそらくBLEの拡張パケットの最大サイズである252バイトが効率の上限だと思われます。それ以上の値を設定することもできますが、設定しても転送レートはこれ以上早くなりません。
# DFU詳細
nRF Connect SDK(以下NCS)ではデフォルトでデュアルバンク設定になっています。ROM容量が少ない機種でなければ原則このままでよいと思いますが、具体的にどのように動いているのかちょっと中身を覗いてみましょう。
## DFU実施前の状態
一度もDFUを実施していない場合、当然ですがデュアルバンクの片方にしか書き込みはありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/1d5def1e-6c90-10cd-b5e6-5da0f581d3b0.png)

::: note
これだけだとデュアルバンクの片方が空白なのか、シングルバンクでただ単にコードサイズが小さいだけなのか判別できませんがw
:::

![Screenshot_20221116-095242.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/e8511277-103a-4c5c-87de-86d5a758dc2b.png)

# DFU実施後の状態
DFUを実施してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/e15a1bd8-47bf-917f-9538-eeea27abba5a.png)
おお、たしかにデュアルバンクで別のスロットに新しいファームウェアが書き込まれているようです。
## ちょっといたずらを(笑)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/bd2825b0-1d7e-d3f3-56b0-0fa8aaee5b59.png)
新しいファームウェアは0x91200から書き込まれている・・・と思ったのですが、どうやら常に0x23000側に書き込まれるようです。つまり、0x91200側にあるのはどうも古いファームウェアのようです。DFUを実行する途中でコピーしているのでしょうか。
さて、本当にデュアルバンクが正常に機能するのかどうか、Segger Embedded Studioを使ってこれを破壊してみましょう(笑)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/04262669-2c62-eadf-fb8c-0ddd3cf1b27e.png)

::: note
ひっでぇことするなぁ・・・www
:::

・・・。
うんともすんとも言わなくなりました(笑)
これはどうもダメみたいです。何のためにデュアルバンク機能があるのかよく分からなくなってきましたが、詳細は後日調べてみようと思います。
# バンクに残っているファームウェアはアップロードしない
こんな感じでDFUはできるようになったのですが、再度、最初のファームウェアでDFUを試みるとデータ送信を一切しません。あれ？おかしいな・・・。
ですが、DFUの画面から切り替わってアドバタイズを見てみるとちゃんとアドバタイズ名がDFU1に変わっています。アップロードするファイルとバンクの中身を比較して一致している場合はアップロードしないようです。ですので、3つ目のDFUファイルを用意してDFUを実施するとちゃんとアップロードを始めます。
これがデュアルバンク特有の動作なのか、それともNCSのデフォルトの挙動なのかまでは調べきれませんでした。
# ここまでは序章
まだまだ続きます。最初のほうでチラッと出てきたのですが、今回のプロジェクトではImmutable Bootloaderに対して秘密鍵を指定していますが全く機能していません。何度も確認してみましたが、秘密鍵を変更したプロジェクトでDFUを実施するとあっさり書き換わってしまいます。
いくつか書き換えができないパターンというのも存在はしていて、
```
# Allow for large Bluetooth data packets.
CONFIG_BT_L2CAP_TX_MTU=252
CONFIG_BT_BUF_ACL_RX_SIZE=256
```
これらの値がDFU後のほうが大きい場合や、
```
CONFIG_SECURE_BOOT=y
```
の有無が異なっている場合、DFUのアップロード自体は実施されますがファームウェアは切り替わりません。ただ、これらのいずれもセキュリティによる書き換え防止ではないので、これで書き換えられないからと言って何の役にも立ちません。
## MCU Bootloader
いやいや、そもそも使っているのはMCU Bootloaderでしょ？だって
```
CONFIG_BOOTLOADER_MCUBOOT=y
```
って書いてますやん。はい、確かに使っていると思われるのはMCU Bootloaderのはずです。ですが、nRF ConnectでEnable MCUBootのスライドをONにしてみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/9fd91b4b-81a4-305f-c0da-efc6ff6f869c.png)
・・・？
どうもMCUBootと書いているにもかかわらずMCUBootではないっぽいです。Memory layoutと書いているのでもしかしたらPartition Manager関連かも知れませんが現時点では分かっていないです。
また、秘密鍵についても
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/84f33b59-6bb9-fbb1-510b-4610227064d1.png)
当然設定する項目があるのですが、いざこれを設定してコンパイルすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/5f4cf4ad-a901-c1c7-e1ee-3d465b5d2f8a.png)
いやいや、CONFIG_MCUBOOT_SIGNATURE_KEY_FILEじゃなくてCONFIG_BOOT_SIGNATURE_KEY_FILEざます、と言われるのでその通りに修正してみると・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/cfabb82c-39cc-7f8a-b28c-4727106c94e5.png)
エクスクラメーションマークが表示されて無効だよと注意を受けます。なにやらMCUBOOT_BUILD_STRATEGY_FROM_SOURCEがyになっているのが理由のようなので、今度はこれを変更してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/23842085-4436-aed7-6447-45f6f0eddd86.png)
しかし次から次へとうるさいやつだな・・・なんて思っていると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/62486490-9a11-3990-07e4-b25b11a33835.png)
エラーです。もう一つの選択肢に変更してみてもファイルを指定しろというエラーが出ますがそもそも何を指定すればいいのかすら分かりません。つまり・・・
## 詰んだオワタ
MCUBootに秘密鍵を持たせることは(少なくとも現時点で理解できている範囲では)できません。DFUができることに間違いはないのですが、いわゆるOpen Bootloaderのようなものになってしまっておりぶっちゃけ誰でも書き換えることができてしまいます。
個人的にはバグじゃないかと疑ってはいるのですが・・・とりあえずOpen Bootloaderでしばらく様子を見ようかと思っています。

::: note
nRF52 SDK時代にもVer 14ではWDT設定していると(DFU中にWDTが動いてしまって)DFUができないという不具合がありましたしね(笑)
:::