概要
ESP32のファームウェアをSDカードに入れて、ESP32のアプリケーション上でファームウェアをアップデートします。※ArduinoIDEを使用しました
espressif公式のサンプルコードをベースにして以下の変更を加えました。
・変更1:SDカードの制御(SD_MMC.hではなくSD.hのライブラリを使用する)
・変更2:動作しているファームのパーティションアドレスをシリアル出力する
使用したもの
- ESPr® Developer 32 ※これ以降は「ESP32]と呼称
- HiLetgo® 2.8 "TFT LCDディスプレイ タッチパネル(SDカードスロットとして使用)
- Arduino IDE(バージョン1.8.5を使用)
- SDカード
結線
以下の通りにESP32とSDカードを結線します
SD
ESP32 | SD | 備考 |
---|---|---|
VOUT | VCC | |
GND | GND | |
IO14 | SD_SCK | 10kの抵抗を使用して3.3Vでプルアップする(参考記事) |
IO2 | SD_MISO | 10kの抵抗を使用して3.3Vでプルアップする(参考記事) |
IO15 | SD_MOSI | 10kの抵抗を使用して3.3Vでプルアップする(参考記事) |
IO13 | SD_CS | 10kの抵抗を使用して3.3Vでプルアップする(参考記事) |
動作手順
スケッチ
スケッチの動作は以下の流れです。
1.現在動作中のファームウェアの領域アドレスをシリアル出力する
2-1.新しいファームウェアがある場合、ファームウェアを書き込んで、再起動する
2-2.新しいファームウェアがない場合、再起動する
/*
Arduino board manager:
https://dl.espressif.com/dl/package_esp32_index.json
Arduino Settings(use Arduino 1.8.5):
Board ESP32 Dev Module
Flash Mode QIO
Flash Frequency 40MHz
CPU Frequency 240MHz
Flash Size 4M (32Mb)
Partition Scheme Default 4MB with spiffs(1.2MB APP / 1.5MB SPIFFS)
Upload Speed 115200
Core Debug Level non
The circuit:
* SD card attached to SPI bus as follows:
** CLK - pin 14
** MISO - pin 2
** MOSI - pin 15
** SS - pin 13
*/
#include <Update.h>
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
SPIClass spiSD(HSPI);
#define SD_CLK 14
#define SD_MISO 2
#define SD_MOSI 15
#define SD_SS 13
#define SDSPEED 27000000
#define UPDATE_FILE_NAME "/update.bin"
const esp_partition_t *running;
// perform the update from a given stream
void performUpdate(Stream &updateSource, size_t updateSize) {
if (Update.begin(updateSize)) {
size_t written = Update.writeStream(updateSource);
if (written == updateSize) {
Serial.println("Written : " + String(written) + " successfully");
}
else {
Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
}
if (Update.end()) {
Serial.println("done!");
if (Update.isFinished()) {
Serial.println("Update successfully completed. Rebooting.");
}
else {
Serial.println("Update not finished? Something went wrong!");
}
}
else {
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
}
}
else
{
Serial.println("Not enough space to begin OTA");
}
}
// check update file is available
boolean updateFileIsAvailable(fs::FS &fs) {
boolean ret = false;
File updateBin = fs.open(UPDATE_FILE_NAME);
if (updateBin) {
if(updateBin.isDirectory()){
Serial.println("Error, new firmware is not a file");
}
else {
size_t updateSize = updateBin.size();
if (updateSize > 0) {
ret = true;
}
else {
Serial.println("Error, file is empty");
}
}
updateBin.close();
}
else {
Serial.println("Could not load new firmware file from sd root");
}
return ret;
}
// check given FS for valid update.bin and perform update if available
void updateFromFS(fs::FS &fs) {
File updateBin = fs.open(UPDATE_FILE_NAME);
size_t updateSize = updateBin.size();
performUpdate(updateBin, updateSize);
updateBin.close();
// remove the update file from sd card to indicate end of the process
fs.remove(UPDATE_FILE_NAME);
reboot();
}
void setup() {
Serial.begin(115200);
running = esp_ota_get_running_partition();
Serial.printf("running partition address:0x%08x", running->address);
Serial.println("");
Serial.print("Checking new firmware...");
spiSD.begin(SD_CLK, SD_MISO, SD_MOSI, SD_SS);
boolean newFirmIsOK;
if(!SD.begin( SD_SS, spiSD, SDSPEED)){
newFirmIsOK = false;
}
else {
if (updateFileIsAvailable(SD)) {
newFirmIsOK = true;
}
else {
newFirmIsOK = false;
}
}
if (newFirmIsOK) {
Serial.println("OK");
Serial.println("Start firm update");
updateFromFS(SD);
}
else {
Serial.println("FAILED");
reboot();
}
}
void reboot(){
Serial.println("Reboot");
delay(3000);
ESP.restart();
}
void loop() {
; //will not be reached
}
動作手順
1. ファームウェアを入れたSDカードを挿入する
※ファームウェアはArduinoIDEの「スケッチ」→「コンパイルしたバイナリを出力」で作成する(スケッチと同じフォルダに生成される)
※生成されたファームウェアのファイル名をupdate.binに変更する
2.PCとESP32を接続しているUSBケーブルを抜いてIO2を(プルアップ抵抗から)切り離す
3.USBを繋いでスケッチをESP32に書き込む(スケッチはgithubに置きました)
※Arduinoの設定は以下の通りにしました
4.USBケーブルを抜いてIO2を(プルアップ抵抗と)接続する
5.USBケーブルを接続する
6.ESP32のアプリが立ち上がり、アップデートが行われる
7.アップデートが終わると再起動してアップデートされたファームウェアが立ち上がる
※SDカード上のファームウェアは削除されているので再びアップデートを行わない
アップデートについて
今回アップデートではUpdate.hというespressif公式のライブラリを使用しています。これはOTA(Wi-Fiを使用するファームアップデート)にも使用されるライブラリです。
そして、このライブラリでは以下のメモリマップ上のapp0かapp1の使用していない方のファームウェア用の領域に新しいファームウェアを書き込みます。
そして、アップデートした時に起動する領域を切り替えることで、再起動時に新しいファームウェアで動作する、ということのようです。(参考記事)
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x140000,
app1, app, ota_1, 0x150000, 0x140000,
eeprom, data, 0x99, 0x290000, 0x1000,
spiffs, data, spiffs, 0x291000, 0x16F000,
実際に動作中の領域のアドレスをシリアル出力させたところ、以下のように0x010000→0x150000と変わっていることが分かります。
running partition address:0x00010000
Checking new firmware...OK
Start firm update
Written : 302896 successfully
done!
Update successfully completed. Rebooting.
Reboot
:
running partition address:0x00150000
終わりに
最初はOTAのサンプルコードの一部を流用するつもりでしたが、SDカードをアップデートに使用するピンポイントなサンプルコードが見つかって幸運でした。
アップデートに要する時間はそこまで長くないので、LCDなどの表示でその待ち時間を「上手く」扱えれば、ゲーム機のソフトのようにSDカードを使うことができて、面白そうです。
見ていただいてありがとうございました。
тнайк чoμ_〆(・ω・。)
参考記事
-
espressif公式のデータシート
-
M5StickCのPartition Tablesを調べる
-
ESP32 でやってみたいこと(すzのAVR研究)
-
BLE環境センサー・ゲートウェイ(ESP32編)
更新履歴
- 2019-10-16:新規作成