nRF52でBLEデバイスを開発する（２）OTAアップデート Buttonless DFUとSecure DFU

　この記事は[nRF52でBLEデバイスを開発する（１）Adafruit FeatherとSEGGER Embeded Studio](https://qiita.com/jiro-aqua/items/63d3c04ba33eac57ebd0)の続きです。

# OTAアップデート
　BLEデバイスを開発するので、何はなくともOTAアップデートが必要です。(断言

# 概要
　まず、公式ドキュメント。
　https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.3.0/lib_bootloader_modules.html

![bootloader_memory_nrf52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89529/b44193fe-fb6e-8fff-0103-5e1215bf05a2.png)

https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v15.3.0/lib_bootloader.html よりメモリマップを引用
　この図は、nRF52内蔵のフラッシュのメモリマップです。
　プロセッサが起動すると、まずMBRが実行されます。MBR内にBootloaderのアドレスが記録されていると、Bootloaderを起動します。
　SDKのサンプルアプリのほとんどはBootloaderなしで直接起動するように作られていますが、DFU(Device Firmware Update - nRF52でのOTAのこと)を実装する際は、Bootloaderを準備する必要があります。

まず、DFUの動作をざっくりと解説します。

1. アプリがDFU開始のシグナルを受け取る
- アプリはDFU開始ステータスをFLASHに書き込み、再起動
- Bootloaderが起動する
- BootloaderはDFU開始ステータスをチェックし(*1)、DFUモードで起動する(アプリは起動しない）
- Bootloaderはアドバタイズを開始する(*2)
- DFUを行うCentralがBootloaderに接続する
- ダウンロード開始。受信したデータはフラッシュのFree領域に格納される
- 全てのデータを受信したら、エラーのチェック後、受信データをアプリ領域に転送。
- 再起動
- 転送されたアプリが起動する

*1 DFU開始ステータスをチェックする方法が複数用意されています。

- 基板上のボタンを押されている
- リセットボタンを押されている
- 電源管理レジスタの特定のビットが立っている
- ボタンレスDFUモードが設定されている

*2 DFUの通信手段もANT,UART,BLE,USBと色々用意されていますが、本稿ではBLEのみ解説します。必要な人はサンプルソースを読んでください。

# Buttonless DFU
　ボタンレスDFUでは、アプリケーション側で特定のCharacteristicを開けておき、そこへのアクセスに対応してDFUシーケンスが開始されることになります。スマートホンのアプリ主導でファームウェアアップデートが行えることになります。

# Secure DFU
　nRF52 SDKでは偽のファームウェアによる攻撃を防ぐために、Secure DFUという手段が用意されています。
　ダウンロードするファイルを秘密鍵で署名－Bootloader内の公開鍵で署名を検証、という形で、正規のファームかどうかを識別する仕組みです。
　これによって、偽装ファームによる攻撃を防ぐことが可能となります。
（本記事では割愛しますが、デバッガ接続時のリードライトを禁止する手段も用意されています）

# Bootloaderの実装
　まず、こちらの記事を参照してください。
　nrf52でOTAする(Secure Bootloader+DFU)
　https://qiita.com/chabose/items/dab0b12f460f85918c8d

　上記記事と重複する内容はスキップして補足を書いていきます。
### nrfutil
　公式ページはこちら
　https://github.com/NordicSemiconductor/pc-nrfutil
　Mac/Linuxではpythonベースでインストールします。(公式ページに記載)
　Windows向けにはビルド済みバイナリが提供されています。以下からダウンロード
　https://github.com/NordicSemiconductor/pc-nrfutil/releases
### μECC
　μECCはmake環境のみ入っています。Windowsでのビルド手順は以下の通り。

　以下をインストールします。
- Git for Windows   ※gitを使っていれば入ってるはず
- make for Windows (http://gnuwin32.sourceforge.net/packages/make.htm)    ※CygwinでもOK
- GNU Arm Embedded Toolchain Version 7-2018-q2-update (https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/7-2018-q2-update)

※gitとmakeはパスを通しておきます。
※gccのバージョンは (SDK)/components/toolchain/gcc/Makefile.windows)に書いてあるので、それでググれば辿り着きます。

```
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/7 2018-q2-update/bin/
GNU_VERSION := 7.3.1
GNU_PREFIX := arm-none-eabi
```
　準備が出来たら、(SDK)/external/micro-ecc/ でコマンドプロンプトを開き、build_all.batを実行します。
　(SDK)/external/micro-ecc/nrf52hf_armgcc/armgcc/micro_ecc_lib_nrf52.a が生成されていたらOK。

### Bootloader
　前回の記事で書いたように、サンプルプロジェクト(/examples/dfu/secure_bootloader)を作業ディレクトリにコピーします。プロジェクトファイルは、pca10056_ble/ses を使用します。
### 秘密鍵／公開鍵のファイル
　private.key/dfu_public_key.c はプロジェクト外で管理し、公開リポジトリにはアップロードしないようにします。SES上で適宜参照先を変更しておきます。

# Applicationの実装
　同様にして、サンプルプロジェクトからコピーします。(/examples/ble_peripheral/ble_app_buttonless_dfu)

# 出来ているものがこちらに
　Githubに、今回使ったすぐ焼けるプロジェクトファイル一式を載せておきました。
　https://github.com/jiro-aqua/nrf52-ota-example

- プロジェクト外の ../dfu/にprivate.keyとdfu_public_key.cをコピーしてください。
- sesでアプリケーションとブートローダーを開き、Releaseビルドしておきます。
- package/配下でコマンドプロンプトを開き、package.batを実行すると、ROM焼き用イメージファイルとOTA用のイメージファイルが作成できます。

## デバッグする時のための補足
　このApplicationとBootloaderにはちょっとした仕組みが入っています。

- bootloaderはapplicationのCRCチェックに成功しないとapplicationを起動しない。
- applicationはbootloaderの存在を確認しないとエラーで止まる。

　このままだと、SESでのデバッグに支障が出るので、bootloaderのmain.cに細工をいれます。

```main.c
int main(void)
{
    uint32_t ret_val;

    // Protect MBR and bootloader code from being overwritten.
    ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);
    APP_ERROR_CHECK(ret_val);
    ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE, false);
    APP_ERROR_CHECK(ret_val);

    (void) NRF_LOG_INIT(nrf_bootloader_dfu_timer_counter_get);
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("Inside main");
#if ( DEBUG_BOOTLOADER == 0 )
    ret_val = nrf_bootloader_init(dfu_observer);
    APP_ERROR_CHECK(ret_val);
#endif
    // Either there was no DFU functionality enabled in this project or the DFU module detected
    // no ongoing DFU operation and found a valid main application.
    // Boot the main application.
    nrf_bootloader_app_start();

    // Should never be reached.
    NRF_LOG_INFO("After main");
}
```
　BootloaderのデバッグビルドではDEBUG_BOOTLOADER==1となるように設定しておき、デバッグビルドを実行した時はCRCチェックなしでApplicationが実行されるようにしておきます。(このため、デバッグビルドではOTAは動作しません)
　また、sesでアプリケーションを実行する時にソフトデバイスが書き込まれるのですが、この時にMBR上のBootloaderのアドレスがクリアされてしまうため、ソフトデバイスがロードされないように設定しておきます。

# ROM焼き
　ROM焼き用のイメージは以下の手順で作成します。

```
nrfutil settings generate --family %FAMILY% --application %APP% --application-version 1 --bootloader-version 1 --bl-settings-version 1 Output\settings.hex
mergehex --merge %SOFTDEVICE% %BOOTLOADER% --output Output\SD_BL.hex
mergehex --merge Output\SD_BL.hex %APP% --output Output\SD_BL_APP.hex
mergehex --merge Output\SD_BL_APP.hex Output\settings.hex --output Output\all.hex
```
　nrfutilでアプリの設定ファイルを作り、Bootloader,SoftDevice,ApplicationのHEXとマージします。これを、以下のバッチファイルで流し込みます。

```program.bat
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program Output/all.hex
nrfjprog --family NRF52 --reset
```
　※エラーが出る場合は、Command Line Toolsの最新版をチェックしてください。
　　https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools/Download#infotabs
　　nRFgoと一緒に入るファイルはバージョンがえらく古いです。

　起動したら Nordic_Buttonless というデバイス名が見えるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89529/e5314eb0-b477-f05c-3485-e0b6546db32c.png)


# OTA

　nRF-Toolboxを使います。Nordicがアプリストアに置いてあるnRF-ConnectというアプリでもDFUできますが、独自デバイスを開発する以上スマートホンアプリも作ることになるので、nRF Toolboxを自前ビルドして使用することをお勧めします。

　IOS-nRF-Toolbox
　https://github.com/NordicSemiconductor/IOS-nRF-Toolbox
　Android-nRF-Toolbox
　https://github.com/NordicSemiconductor/Android-nRF-Toolbox

- Output/ota_all.zip　BL,SD,APP全部入りのイメージ
- Output/ota_app.zip　APPのみのイメージ(通常はこちらで充分)

　自分で自分の上書をして、再起動することを確認します。

## Tips
 AndroidのnRF-Toolboxでは、GoogleDriveに置いたzipファイルを認識しません。MIMETYPEを"\*/*"にすると認識します。

```DfuActivity.java
	private void openFileChooser() {
		final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
		intent.setType(mFileTypeTmp == DfuService.TYPE_AUTO ? /*DfuService.MIME_TYPE_ZIP*/ "*/*" : DfuService.MIME_TYPE_OCTET_STREAM);
```

# さいごに

　駆け足ですが、OTA／ROM焼きのための環境構築について説明しました。ここら辺は量産・運用には欠かせないポイントだと思います。次回以降は、このOTAできる環境をベースに構築していきます。