SPRESENSE の Arduino で FWUpdate を試してみた
SPRESENSE の Arduino でファームウェアアップデートを試してみました。ファームウェアアップデートはSDKのExampleで用意されています。これをArduinoでも利用できないかなと思い試してみることにしました。
SDKの FW Update チュートリアルはここにあります。

SDKのサンプルコードは次の github にあります。
ファームウェアアップデートを行うスケッチ
SDKのサンプルを参考に Arduino から呼び出してみました。ファイルは二つに分かれています。Maincore用のスケッチとFWUpdate用のスケッチです。Maincore用のスケッチと同じフォルダに入れてコンパイルしてください。
Maincore 用のスケッチ
#include <LowPower.h>
#include <Flash.h>
#define VERSION (1.0)
#define UPDATE_FILE "/mnt/spif/package.bin"
uint16_t counter = 0;
extern int fwupdate_package(char* pathname);
void setup() {
Serial.begin(115200);
Serial.println("FWUPDATER test start.");
digitalWrite(LED0, HIGH);
LowPower.begin();
}
void loop() {
if (counter == 20) {
int ret = fwupdate_package(UPDATE_FILE);
if (ret != OK) {
Serial.println("Update filed");
while(1) {
digitalWrite(LED0, LOW); delay(100);
digitalWrite(LED0, HIGH); delay(100);
}
} else {
Serial.println("FW Updated reboot");
LowPower.reboot();
}
}
Serial.println(String(VERSION) + ": " + String(counter++));
sleep(1);
}
fwuputils/fwup_client.h
こちらは、1秒ごとにカウントアップするカウンターが”20"に達したらファームウェアアップデートを行うように作られています。この場合lファームウェアのパッケージは フラッシュ上に"package.bin”という名前で格納されている必要があります。もしパッケージがなかったらエラーが帰ってきてLED点滅をさせます。もしあった場合はファームウェアアップデートがなされリブートします。
FWUpdate を行うスケッチ
SDKのサンプルから流用してきたものです。一点だけ、Arduinoでコンパイルする場合、fwuputils/fwup_client.h のインクルードをダブルクォーテーション’”’からアングルブラケット’<>’に変更してください。コンパイラの読み出し順序の関係で、enum fw_type_e でエラーが出てしまいます。
#include <nuttx/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <debug.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <fwuputils/fwup_client.h>
#ifndef CONFIG_EXAMPLES_FWUPDATE_DOWNLOAD_WORK_SIZE
# define CONFIG_EXAMPLES_FWUPDATE_DOWNLOAD_WORK_SIZE (0x10000) /* 64KB */
#endif
#ifndef MIN
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************
static uint32_t get_file_size(const char *pathname);
static const char *get_file_type_string(enum fw_type_e fwtype);
static int do_partial_download(struct fwup_client_s *fwup, FILE *fp, enum fw_type_e fwtype, uint32_t fwsize);
static int do_package_download(struct fwup_client_s *fwup, char* pathname);
int fwupdate_package(char* pathname);
/****************************************************************************
* Private Data
****************************************************************************/
/*
* A example of package byte stream format (little endian)
* +------+------+------- ... -+------+------+--------------------+
* |fwtype|fwsize|fwdata |fwtype|fwsize|fwdata |
* +------+------+------- ... -+------+------+--------------------+
* ->|4 byte|<-
*/
struct header_s {
enum fw_type_e fwtype;
uint32_t fwsize;
};
/****************************************************************************
* Private Functions
****************************************************************************/
static uint32_t get_file_size(const char *pathname) {
struct stat stbuf;
if (stat(pathname, &stbuf) < 0) return 0;
return stbuf.st_size;
}
static const char *get_file_type_string(enum fw_type_e fwtype) {
switch (fwtype) {
case FW_APP: return "FW_APP";
case FW_SYS: return "FW_SYS";
case FW_UPDATER: return "FW_UPDATER";
case FW_SBL: return "FW_SBL";
default: return "";
}
}
static int do_partial_download(struct fwup_client_s *fwup, FILE *fp, enum fw_type_e fwtype, uint32_t fwsize) {
int ret = OK;
uint32_t *buf = NULL;
uint32_t size;
uint32_t remain = fwsize;
do {
size = MIN(remain, CONFIG_EXAMPLES_FWUPDATE_DOWNLOAD_WORK_SIZE);
buf = (uint32_t*)malloc(size); /* buf must be 4byte alignment */
if (buf == NULL) {
ret = -ENOMEM;
break;
}
ret = fread(buf, 1, size, fp);
if (size != ret) { /* sanity check */
free(buf);
ret = -ENODATA;
break;
}
ret = fwup->download(fwtype, fwsize, buf, size);
remain -= size;
printf("->dl(0x%08lx, %ld / %ld): ret=%d\n", (uint32_t)buf, fwsize - remain, fwsize, ret);
fwup->msgsync();
free(buf);
} while (0 < remain);
return ret;
}
static int do_package_download(struct fwup_client_s *fwup, char* pathname) {
int ret = OK;
enum fw_type_e fwtype;
uint32_t fwsize;
FILE *fp;
int fwnum = 0;
struct header_s header;
fp = fopen(pathname, "rb");
while (1) {
/* get package header */
ret = fread(&header, 1, sizeof(struct header_s), fp);
if (sizeof(struct header_s) != ret) break; /* end of byte stream */
fwtype = header.fwtype;
fwsize = header.fwsize;
/* debug information */
printf("File: %s(%d)\n", pathname, fwnum++);
printf("Size: %ld\n", fwsize);
printf("Type: %s\n", get_file_type_string(fwtype));
if (fwsize <= 0) {
ret = -ENOENT;
break;
}
ret = do_partial_download(fwup, fp, fwtype, fwsize);
if (ret) break;
}
fclose(fp);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int fwupdate_package(char* pathname) {
int ret = OK;
/* Setup for FW Update */
struct fwup_client_s *fwup = fwup_client_setup();
/* FW Update Sequence Initialization */
fwup->init();
/* debug information */
printf("File: %s\n", pathname);
printf("Size: %ld\n", get_file_size(pathname));
/* FW Download into SPI-Flash */
/* - support the partial download depending on the size of work memory */
ret = do_package_download(fwup, pathname);
if (ret < 0) return ret;
/* FW Update Sequence Start after reboot */
ret = fwup->update();
printf("->update: ret=%d\n", ret);
return ret;
}
FWUpdate用のパッケージの作り方
まずアップデート用のパッケージを作ります。Maicore用のパッケージのVERIONを”2.0"に変更します。そのあとにArduinoIDEメニューの”Sketch"から”Export Compiled Binary”を選択して、コンパイルイメージを出力します。

すると、スケッチのあるフォルダに”build”フォルダができて、”SPRESENSE.spresense.spresense”というフォルダの下に ”%スケッチ名%.spk”、”%スケッチ名%.elf” というファイルが出力されます。アップデート用のバイナリは”%スケッチ名%.spk”です。ファイル名は”nuttx.spk”に変更します。
次にFWバイナリをパッケージに変更します。パッケージを作成するためのスクリプトはSDKのサンプルにあります。
こちらのスクリプトを次のコマンドを使ってパッケージを作成します。パッケージは”package.bin”というファイル名で生成されます。
$ bash package.sh nuttx.spk
パッケージを内蔵フラッシュに転送
できあがった”package.bin”をフラッシュに転送します。転送にはSDKのtoolフォルダにある xmodem_writer を使います。
Windows
Linux
MacOS
Windowsの場合は下記のようにコマンドを打てば、内蔵フラッシュにファイルを転送してくれます。
$ xmodem_writer.exe -c COM11 package.bin
Spresenseをリセットすると、ファイルがアップデートされVERSIONがアップデートされているのが確認できます。
作ってみた感想
FWUpdate用のファイルを毎回コピーするのが面倒なので、次はライブラリにしてみたいと思います。今回のプログラムだと毎回アップデートしてしまうので、アップデートが完了したらパッケージを削除するしくみもライブラリに追加したいと思います。あとせっかくなのでWiFiからFWUpdateしたいですね。

SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware

SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2022/02/28
- メディア: 単行本(ソフトカバー)
この記事へのコメント