LoginSignup
7
6

More than 3 years have passed since last update.

SDカードを使ってESP32のファームウェアをアップデートしてみた

Last updated at Posted at 2019-10-16

概要

ESP32のファームウェアをSDカードに入れて、ESP32のアプリケーション上でファームウェアをアップデートします。※ArduinoIDEを使用しました

espressif公式のサンプルコードをベースにして以下の変更を加えました。
・変更1:SDカードの制御(SD_MMC.hではなくSD.hのライブラリを使用する)
・変更2:動作しているファームのパーティションアドレスをシリアル出力する

使用したもの

結線

以下の通りに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.新しいファームウェアがない場合、再起動する

simple_sdcard_firmware_update.ino
/*

 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を(プルアップ抵抗から)切り離す

プルアップ説明2.png

3.USBを繋いでスケッチをESP32に書き込む(スケッチはgithubに置きました)
※Arduinoの設定は以下の通りにしました
スケッチ設定.png

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μ_〆(・ω・。)

参考記事

更新履歴

  • 2019-10-16:新規作成
7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6