SPRESENSE の Arduino で FWUpdate を試してみた

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


SDKの FW Update チュートリアルはここにあります。


SDK_FWUpdate.png


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”を選択して、コンパイルイメージを出力します。



ArduinoExport.png



すると、スケッチのあるフォルダに”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

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware




SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)

SPRESENSEではじめるローパワーエッジAI (Make: PROJECTS)

  • 出版社/メーカー: オライリージャパン
  • 発売日: 2022/02/28
  • メディア: 単行本(ソフトカバー)

この記事へのコメント