esp8266 aggiornamenti del firmware e del filesystem con client FTP – 2

Spread the love

In questa serie di articoli sul firmware e su come aggiornarlo, vorrei aggiungere una serie di metodi alternativi di aggiornamento molto comodi.

esp8266 firmware and filesystem update with FTP client
esp8266 firmware and filesystem update with FTP client

In questo articolo impareremo come aggiungere un server FTP al nostro dispositivo e usarlo per caricare gli aggiornamenti del firmware e del filesystem.

Aggiornare il firmware da una memoria con la classe Update

I metodi di base per aggiornare un flusso di dati sono contenuti nella classe Update.

Un metodo, in particolare, può gestire gli aggiornamenti via stream

Update.begin(firmwareSizeInBytes);
Update.writeStream(streamVar);
Update.end();

Caricare il firmware tramite FTP e aggiornamento automatico

Ho creato alcune applicazioni per la memorizzazione di logs e statistiche e scarico poi i dati tramite un client FTP. Per fare ciò, utilizzo una semplice libreria che puoi vedere in questo articolo “Server FTP su esp8266 ed esp32“. Ho creato quella libreria e l’ho personalizzata con alcune callback che possono essere usate per il nostro scopo.

Possiamo usare SPIFFS, LittleFS o SD. Controlla su questi articoli:

Aggiorna il firmware con server FTP su LittleFS

Per questo test ricorda di modificare (o controllare) che FtpServerKey.h sia così

#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266
	#define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 	NETWORK_ESP8266
	#define DEFAULT_STORAGE_TYPE_ESP8266 STORAGE_LITTLEFS
#endif

Per capire come generare il firmware compilato, leggi questo articolo “esp8266: flash del firmware binario(.bin) compilato e firmato”, che è abbastanza semplice ma è obbligatorio per comprendere tutti i passaggi.

Qui scrivo un esempio che utilizza la libreria SimpleFTPServer per caricare il file firmware.bin su LittleFS e quando il sistema completa la memorizzazione di quel file, avvia l’aggiornamento del firmware.

/*
 * Upload firmware with FtpServer (esp8266 with LittleFS)
 * when uploaded start automatic Update and reboot
 *
 * AUTHOR:  Renzo Mischianti
 *
 * https://mischianti.org/
 *
 */

#include <ESP8266WiFi.h>
#include <LittleFS.h>

#include <SimpleFTPServer.h>

const char* ssid = "<YOUR-SSID>";
const char* password = "<YOUR-PASSWD>";


FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial

bool isFirmwareUploaded = false;

void progressCallBack(size_t currSize, size_t totalSize) {
    Serial.printf("CALLBACK:  Update process at %d of %d bytes...\n", currSize, totalSize);
}

#define FIRMWARE_VERSION 0.2

void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
  switch (ftpOperation) {
    case FTP_CONNECT:
      Serial.println(F("FTP: Connected!"));
      break;
    case FTP_DISCONNECT:
      Serial.println(F("FTP: Disconnected!"));
      break;
    case FTP_FREE_SPACE_CHANGE:
      if (isFirmwareUploaded){
          Serial.println(F("The uploaded firmware now stored in FS!"));
		  Serial.print(F("\nSearch for firmware in FS.."));
		  String name = "firmware.bin";
		  File firmware =  LittleFS.open(name, FTP_FILE_READ);
		  if (firmware) {
			Serial.println(F("found!"));
			Serial.println(F("Try to update!"));

			Update.onProgress(progressCallBack);

			Update.begin(firmware.size(), U_FLASH);
			Update.writeStream(firmware);
			if (Update.end()){
			Serial.println(F("Update finished!"));
			}else{
			Serial.println(F("Update error!"));
			Serial.println(Update.getError());
			}

			firmware.close();

			String renamed = name;
			renamed.replace(".bin", ".bak");
			if (LittleFS.rename(name, renamed.c_str())){
			Serial.println(F("Firmware rename succesfully!"));
			}else{
			Serial.println(F("Firmware rename error!"));
			}
			delay(2000);

			ESP.reset();
		  }else{
			Serial.println(F("not found!"));
		  }
		  // isFirmwareUploaded = false; // not need by reset
      }
      Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace);
      break;
    default:
      break;
  }
};
void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
  switch (ftpOperation) {
    case FTP_UPLOAD_START:
      Serial.println(F("FTP: Upload start!"));
      break;
    case FTP_UPLOAD:
      Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize);
      break;
    case FTP_TRANSFER_STOP:
      Serial.println(F("FTP: Finish transfer!"));
      break;
    case FTP_TRANSFER_ERROR:
      Serial.println(F("FTP: Transfer error!"));
      break;
    default:
      break;
  }

  /* FTP_UPLOAD_START = 0,
   * FTP_UPLOAD = 1,
   *
   * FTP_DOWNLOAD_START = 2,
   * FTP_DOWNLOAD = 3,
   *
   * FTP_TRANSFER_STOP = 4,
   * FTP_DOWNLOAD_STOP = 4,
   * FTP_UPLOAD_STOP = 4,
   *
   * FTP_TRANSFER_ERROR = 5,
   * FTP_DOWNLOAD_ERROR = 5,
   * FTP_UPLOAD_ERROR = 5
   */

  if (ftpOperation == FTP_UPLOAD_STOP && String(name).indexOf("firmware.bin")>=0){
    isFirmwareUploaded = true;
  }
};

void setup(void){
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  ftpSrv.setCallback(_callback);
  ftpSrv.setTransferCallback(_transferCallback);

  /////FTP Setup, ensure LittleFS is started before ftp;  /////////
  if (LittleFS.begin()) {
      Serial.println("LittleFS opened!");
      Serial.print(F("\nCurrent firmware version: "));
      Serial.println(FIRMWARE_VERSION);

      ftpSrv.begin("esp8266","esp8266");    //username, password for ftp.   (default 21, 50009 for PASV)
  }
}
void loop(void){
  ftpSrv.handleFTP();        //make sure in loop you call handleFTP()!!
 // server.handleClient();   //example if running a webserver you still need to call .handleClient();

}

Il codice non è così complicato, ma devi capire alcuni passaggi.

Quando l’FTP carica il file nella callback, puoi distinguere tre fasi: FTP_UPLOAD_START, FTP_UPLOAD e FTP_TRANSFER_STOP.

Quando il trasferimento si interrompe, il file viene trasferito completamente

  if (ftpOperation == FTP_UPLOAD_STOP && String(name).indexOf("firmware.bin")>=0){
    isFirmwareUploaded = true;
  }

ma non ancora archiviato, e devi controllare l’evento FTP_FREE_SPACE_CHANGE per assicurarti che il file sia finalmente archiviato.

  switch (ftpOperation) {
    case FTP_CONNECT:
      Serial.println(F("FTP: Connected!"));
      break;
    case FTP_DISCONNECT:
      Serial.println(F("FTP: Disconnected!"));
      break;
    case FTP_FREE_SPACE_CHANGE:
      if (isFirmwareUploaded){
          Serial.println(F("The uploaded firmware now stored in FS!"));
		  Serial.print(F("\nSearch for firmware in FS.."));
		  String name = "firmware.bin";
		  File firmware =  LittleFS.open(name, FTP_FILE_READ);
		  if (firmware) {
			Serial.println(F("found!"));
			Serial.println(F("Try to update!"));

			Update.onProgress(progressCallBack);

			Update.begin(firmware.size(), U_FLASH);
			Update.writeStream(firmware);
			if (Update.end()){
			Serial.println(F("Update finished!"));
			}else{
			Serial.println(F("Update error!"));
			Serial.println(Update.getError());
			}

			firmware.close();

			String renamed = name;
			renamed.replace(".bin", ".bak");
			if (LittleFS.rename(name, renamed.c_str())){
			Serial.println(F("Firmware rename succesfully!"));
			}else{
			Serial.println(F("Firmware rename error!"));
			}
			delay(2000);

			ESP.reset();
		  }else{
			Serial.println(F("not found!"));
		  }
		  // isFirmwareUploaded = false; // not need by reset
      }
      Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace);
      break;
    default:
      break;
  }

Ora controllo se il file è presente e se sì, l’aggiornamento avvia lo stream del file; dopo il caricamento, viene rinominato e si avvia il riavvio che fa la fase finale dell’aggiornamento.

.......
Connected to reef-casa-sopra
IP address: 192.168.1.127
LittleFS opened!

Current firmware version: 0.10
FTP: Connected!
FTP: Upload start!
FTP: Upload of file firmware.bin byte 2048
FTP: Upload of file firmware.bin byte 3216
FTP: Upload of file firmware.bin byte 3752
FTP: Upload of file firmware.bin byte 5360
FTP: Upload of file firmware.bin byte 7408
FTP: Upload of file firmware.bin byte 8040
FTP: Upload of file firmware.bin byte 9112
FTP: Upload of file firmware.bin byte 11160
FTP: Upload of file firmware.bin byte 11792
FTP: Upload of file firmware.bin byte 12864
FTP: Upload of file firmware.bin byte 14912
FTP: Upload of file firmware.bin byte 15544
FTP: Upload of file firmware.bin byte 16080
FTP: Upload of file firmware.bin byte 17688

[...]


FTP: Upload of file firmware.bin byte 327400
FTP: Upload of file firmware.bin byte 329104
FTP: Upload of file firmware.bin byte 329640
FTP: Upload of file firmware.bin byte 330176
FTP: Upload of file firmware.bin byte 331784
FTP: Upload of file firmware.bin byte 333832
FTP: Upload of file firmware.bin byte 335536
FTP: Upload of file firmware.bin byte 336072
FTP: Upload of file firmware.bin byte 338120
FTP: Upload of file firmware.bin byte 338752
FTP: Upload of file firmware.bin byte 340360
FTP: Upload of file firmware.bin byte 341600
FTP: Finish transfer!
The uploaded firmware now stored in FS!

Search for firmware in FS..found!
Try to update!
CALLBACK:  Update process at 0 of 341600 bytes...
CALLBACK:  Update process at 4096 of 341600 bytes...
CALLBACK:  Update process at 8192 of 341600 bytes...
CALLBACK:  Update process at 12288 of 341600 bytes...
CALLBACK:  Update process at 16384 of 341600 bytes...
CALLBACK:  Update process at 20480 of 341600 bytes...
CALLBACK:  Update process at 24576 of 341600 bytes...
CALLBACK:  Update process at 28672 of 341600 bytes...
CALLBACK:  Update process at 32768 of 341600 bytes...
CALLBACK:  Update process at 36864 of 341600 bytes...

[...]

CALLBACK:  Update process at 319488 of 341600 bytes...
CALLBACK:  Update process at 323584 of 341600 bytes...
CALLBACK:  Update process at 327680 of 341600 bytes...
CALLBACK:  Update process at 331776 of 341600 bytes...
CALLBACK:  Update process at 335872 of 341600 bytes...
CALLBACK:  Update process at 339968 of 341600 bytes...
CALLBACK:  Update process at 341600 of 341600 bytes...
CALLBACK:  Update process at 341600 of 341600 bytes...
Update finished!
Firmware rename succesfully!

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v00053100
@cp:B0
ld

.......
Connected to reef-casa-sopra
IP address: 192.168.1.127
LittleFS opened!

Current firmware version: 0.20

Aggiornare il filesystem via FTP tramite SD

Per questo test, ricorda di modificare (o controllare) che FtpServerKey.h sia così

#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266
	#define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 	NETWORK_ESP8266
	#define DEFAULT_STORAGE_TYPE_ESP8266 STORAGE_SD
#endif

Questo sketch non è più complesso, riutilizzo una parte del codice precedente e aggiungo la gestione del file filesystem.bin.

Creiamo un file version.txt nella cartella dei dati dello sketch e scrivo 0.1 all’interno, e lo uso come versione del FileSystem, quindi carico la cartella dei dati sul filesystem.

Ora modificheremo il file version.txt con la versione 0.2, quindi lo caricheremo sul dispositivo

Modifica la versione 0.2 in version.txt, rigenera senza caricare, rinomina il file ArduinoOTAesp8266_fs_update.mklittlefs.bin in filesystem.bin e carica il file sulla SD.

Ecco lo sketch completo.

/*
 * Upload firmware or filesystem (LittleFS) with FtpServer (esp8266 with SD)
 * when uploaded start automatic Update and reboot
 *
 * AUTHOR:  Renzo Mischianti
 *
 * https://mischianti.org/
 *
 */

#include <ESP8266WiFi.h>
#include <SD.h>
#include <LittleFS.h>

#include <SimpleFTPServer.h>

const char* ssid = "<YOUR-SSID>";
const char* password = "<YOUR-PASSWD>";


FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial

bool isFirmwareUploaded = false;
bool isFilesystemUploaded = false;

void progressCallBack(size_t currSize, size_t totalSize) {
    Serial.printf("CALLBACK:  Update process at %d of %d bytes...\n", currSize, totalSize);
}

#define FIRMWARE_VERSION 0.2
String FILESYSTEM_VERSION = "0.0";

void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
  switch (ftpOperation) {
    case FTP_CONNECT:
      Serial.println(F("FTP: Connected!"));
      break;
    case FTP_DISCONNECT:
      Serial.println(F("FTP: Disconnected!"));
      break;
    case FTP_FREE_SPACE_CHANGE:
      if (isFirmwareUploaded){
          Serial.println(F("The uploaded firmware now stored in FS!"));
		  Serial.print(F("\nSearch for firmware in FS.."));
		  String name = "firmware.bin";
		  File firmware =  SD.open(name, FTP_FILE_READ);
		  if (firmware) {
			Serial.println(F("found!"));
			Serial.println(F("Try to update!"));

			Update.onProgress(progressCallBack);

			Update.begin(firmware.size(), U_FLASH);
			Update.writeStream(firmware);
			if (Update.end()){
			Serial.println(F("Update finished!"));
			}else{
			Serial.println(F("Update error!"));
			Serial.println(Update.getError());
			}

			firmware.close();

			String renamed = name;
			renamed.replace(".bin", ".bak");
			if (SD.rename(name, renamed.c_str())){
			Serial.println(F("Firmware rename succesfully!"));
			}else{
			Serial.println(F("Firmware rename error!"));
			}
			delay(2000);

			ESP.reset();
		  }else{
			Serial.println(F("not found!"));
		  }
		  // isFirmwareUploaded = false; // not need by reset
      }
      if (isFilesystemUploaded){
          Serial.println(F("The uploaded Filesystem now stored in FS!"));
		  Serial.print(F("\nSearch for Filesystem in FS.."));
		  String name = "filesystem.bin";
		  File filesystem =  SD.open(name, FTP_FILE_READ);
		  if (filesystem) {
			Serial.println(F("found!"));
			Serial.println(F("Try to update!"));

			Update.onProgress(progressCallBack);

			Update.begin(filesystem.size(), U_FS);
			Update.writeStream(filesystem);
			if (Update.end()){
			Serial.println(F("Update finished!"));
			}else{
			Serial.println(F("Update error!"));
			Serial.println(Update.getError());
			}

			filesystem.close();

			String renamed = name;
			renamed.replace(".bin", ".bak");
			if (SD.rename(name, renamed.c_str())){
			Serial.println(F("Filesystem rename succesfully!"));
			}else{
			Serial.println(F("Filesystem rename error!"));
			}
			delay(2000);

			ESP.reset();
		  }else{
			Serial.println(F("not found!"));
		  }
		  // isFilesystemUploaded = false; // not need by reset
      }
      Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace);
      break;
    default:
      break;
  }
};
void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
  switch (ftpOperation) {
    case FTP_UPLOAD_START:
      Serial.println(F("FTP: Upload start!"));
      break;
    case FTP_UPLOAD:
      Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize);
      break;
    case FTP_TRANSFER_STOP:
      Serial.println(F("FTP: Finish transfer!"));
      break;
    case FTP_TRANSFER_ERROR:
      Serial.println(F("FTP: Transfer error!"));
      break;
    default:
      break;
  }

  /* FTP_UPLOAD_START = 0,
   * FTP_UPLOAD = 1,
   *
   * FTP_DOWNLOAD_START = 2,
   * FTP_DOWNLOAD = 3,
   *
   * FTP_TRANSFER_STOP = 4,
   * FTP_DOWNLOAD_STOP = 4,
   * FTP_UPLOAD_STOP = 4,
   *
   * FTP_TRANSFER_ERROR = 5,
   * FTP_DOWNLOAD_ERROR = 5,
   * FTP_UPLOAD_ERROR = 5
   */

  if (ftpOperation == FTP_UPLOAD_STOP && String(name).indexOf("firmware.bin")>=0){
    isFirmwareUploaded = true;
  }
  if (ftpOperation == FTP_UPLOAD_STOP && String(name).indexOf("filesystem.bin")>=0){
    isFilesystemUploaded = true;
  }
};

void setup(void){
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  ftpSrv.setCallback(_callback);
  ftpSrv.setTransferCallback(_transferCallback);

  Serial.print(F("Inizializing FS..."));
  if (LittleFS.begin()){
      Serial.println(F("done."));
  }else{
      Serial.println(F("fail."));
  }

  Serial.print(F("FileSystem version "));
  File versionFile = LittleFS.open(F("/version.txt"), "r");
  if (versionFile) {
	  FILESYSTEM_VERSION = versionFile.readString();
	  versionFile.close();
  }
  Serial.println(FILESYSTEM_VERSION);

  /////FTP Setup, ensure SD is started before ftp;  /////////
  if (SD.begin(SS)) {
      Serial.println("SD opened!");
      Serial.print(F("\nCurrent firmware version: "));
      Serial.println(FIRMWARE_VERSION);

      ftpSrv.begin("esp8266","esp8266");    //username, password for ftp.   (default 21, 50009 for PASV)
  }
}
void loop(void){
  ftpSrv.handleFTP();        //make sure in loop you call handleFTP()!!
 // server.handleClient();   //example if running a webserver you still need to call .handleClient();

}

L’output seriale quando carico il file filesystem.bin

...
Connected to reef-casa-sopra 
IP address: 192.168.1.127
Inizializing FS...done.
FileSystem version 0.1
SD opened!

Current firmware version: 0.20
FTP: Connected!
FTP: Free space change, free 1 of 1!
FTP: Free space change, free 1 of 1!
FTP: Free space change, free 1 of 1!
FTP: Upload start!
FTP: Upload of file filesystem.bin byte 1608
FTP: Upload of file filesystem.bin byte 2680
FTP: Upload of file filesystem.bin byte 3216
FTP: Upload of file filesystem.bin byte 4288
FTP: Upload of file filesystem.bin byte 4824
FTP: Upload of file filesystem.bin byte 5360
FTP: Upload of file filesystem.bin byte 6968
FTP: Upload of file filesystem.bin byte 7504
FTP: Upload of file filesystem.bin byte 8040
FTP: Upload of file filesystem.bin byte 9112
FTP: Upload of file filesystem.bin byte 9648
FTP: Upload of file filesystem.bin byte 10184

[...]

FTP: Upload of file filesystem.bin byte 2068960
FTP: Upload of file filesystem.bin byte 2069496
FTP: Upload of file filesystem.bin byte 2070568
FTP: Upload of file filesystem.bin byte 2071104
FTP: Upload of file filesystem.bin byte 2071640
FTP: Upload of file filesystem.bin byte 2072576
FTP: Finish transfer!
The uploaded Filesystem now stored in FS!

Search for Filesystem in FS..found!
Try to update!
CALLBACK:  Update process at 0 of 2072576 bytes...
CALLBACK:  Update process at 4096 of 2072576 bytes...
CALLBACK:  Update process at 8192 of 2072576 bytes...
CALLBACK:  Update process at 12288 of 2072576 bytes...
CALLBACK:  Update process at 16384 of 2072576 bytes...
CALLBACK:  Update process at 20480 of 2072576 bytes...
CALLBACK:  Update process at 24576 of 2072576 bytes...

[...]

CALLBACK:  Update process at 2052096 of 2072576 bytes...
CALLBACK:  Update process at 2056192 of 2072576 bytes...
CALLBACK:  Update process at 2060288 of 2072576 bytes...
CALLBACK:  Update process at 2064384 of 2072576 bytes...
CALLBACK:  Update process at 2068480 of 2072576 bytes...
CALLBACK:  Update process at 2072576 of 2072576 bytes...
CALLBACK:  Update process at 2072576 of 2072576 bytes...
Update finished!
Filesystem rename succesfully!

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v00070bd0
~ld
 
.......
Connected to reef-casa-sopra 
IP address: 192.168.1.127
Inizializing FS...done.
FileSystem version 0.2
SD opened!

Current firmware version: 0.20

Grazie

  1. Firmware and OTA update

Spread the love

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *