Aggiornamento firmware e filesystem su ESP32 con un client FTP – 2
Benvenuti alla puntata finale della nostra serie completa sul firmware ESP32 e sulla gestione degli aggiornamenti Over-the-Air (OTA). Questa serie è stata una profonda immersione in vari metodi e strategie per l’aggiornamento del firmware e dei filesystem sul popolare microcontrollore ESP32. Oggi ci concentreremo sull’ultimo articolo, in cui esploreremo come utilizzare un client FTP per aggiornare senza problemi il firmware e il filesystem su un dispositivo ESP32.
In tutta la serie, abbiamo trattato un’ampia gamma di argomenti e metodologie di aggiornamento, come il flashing dei file del firmware compilato (.bin), gli aggiornamenti OTA utilizzando l’IDE di Arduino, gli aggiornamenti basati su browser web per firmware e filesystem, gli aggiornamenti self-OTA da HTTP server e persino aggiornamenti da schede SD. Ogni metodo ha i suoi vantaggi e casi d’uso specifici, ma ora ci concentreremo sul metodo del client FTP per completare la nostra esplorazione delle tecniche di aggiornamento di ESP32.
In questo articolo, ti guideremo attraverso il processo di utilizzo di un client FTP per aggiornare il firmware e il filesystem sui microcontrollori ESP32, discutendone i vantaggi e le applicazioni pratiche. Forniremo istruzioni dettagliate, suggerimenti e best practice per aiutarti a sfruttare appieno la potenza e la flessibilità dei dispositivi ESP32.
Here my device selection ESP32 Dev Kit v1 - TTGO T-Display 1.14 ESP32 - NodeMCU V3 V2 ESP8266 Lolin32 - NodeMCU ESP-32S - WeMos Lolin32 - WeMos Lolin32 mini - ESP32-CAM programmer - ESP32-CAM bundle - ESP32-WROOM-32 - ESP32-S
Ci auguriamo che questa serie completa abbia fornito preziose intuizioni e conoscenze pratiche che puoi applicare ai tuoi progetti ESP32. Immergiamoci e padroneggiamo l’ultima tecnica nella nostra vasta esplorazione dei metodi di aggiornamento di ESP32.
Aggiornare il firmware dalla memoria con la classe Update
Per mettere in funzione questo sistema, dobbiamo utilizzare una SD o un filesystem locale. E dobbiamo usare ilUpdate
classe.
Un metodo, in particolare, può gestire gli aggiornamenti tramite stream
Update.begin(firmwareSizeInBytes);
Update.writeStream(streamVar);
Update.end();
Caricare il firmware tramite FTP e aggiornamento automatico
Ho creato alcune applicazioni che memorizzano log e statistiche che possono essere scaricate tramite un client FTP. Per fare ciò, utilizzo una semplice libreria che puoi controllare in questo articolo, “Server FTP su esp8266 ed esp32“, ho creato quella libreria e la personalizzo con alcuni callback che possono essere utilizzati per il nostro scopo.
Possiamo usare SPIFFS, LittleFS, FFat o SD, controlla questi articoli per le nozioni di base:
- ESP32: fileSystem integrato SPIFFS
- ESP32: filesystem integrato LittleFS
- ESP32: filesystem integrato FFat (FAT/exFAT)
- Come usare la scheda SD con l’esp32
Aggiornare il firmware con il server FTP su SPIFFS
Per questo test, ricorda di modificare (o verificare) che FtpServerKey.h sia così
#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32
#define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 NETWORK_ESP32
#define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_SPIFFS
#endif
Per capire come generare il firmware compilato, leggi questo articolo “ESP32: flash del firmware binario compilato (.bin)”, è abbastanza semplice, ma è obbligatorio comprendere tutti i passaggi di questo articolo.
Qui vado a scrivere un esempio che utilizza la libreria SimpleFTPServer per caricare il file firmware.bin su SPIFFS, e quando il sistema memorizza quel file si avvia, l’aggiornamento del firmware.
/*
* Upload firmware with FtpServer (ESP32 with SPIFFS)
* when uploaded start automatic Update and reboot
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
*/
#include <WiFi.h>
#include <Update.h>
#include <SPIFFS.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 = SPIFFS.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_SPIFFS);
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 (SPIFFS.rename(name, renamed.c_str())){
Serial.println(F("Firmware rename succesfully!"));
}else{
Serial.println(F("Firmware rename error!"));
}
delay(2000);
ESP.restart();
}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 SPIFFS is started before ftp; /////////
if (SPIFFS.begin()) {
Serial.println("SPIFFS opened!");
Serial.print(F("\nCurrent firmware version: "));
Serial.println(FIRMWARE_VERSION);
ftpSrv.begin("esp32","esp32"); //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 solo pochi passaggi.
Quando FTP carica il file nel callback, puoi distinguere tre diverse fasi, FTP_UPLOAD_START, FTP_UPLOAD e FTP_TRANSFER_STOP.
Quando il trasferimento si interrompe, il file è completamente trasferito,
if (ftpOperation == FTP_UPLOAD_STOP && String(name).indexOf("firmware.bin")>=0){
isFirmwareUploaded = true;
}
ma non ancora memorizzato, è necessario controllare l’evento FTP_FREE_SPACE_CHANGE per essere sicuri che il file sia completamente memorizzato.
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 si aggiorna avvia lo stream del file che, dopo il caricamento, è stato rinominato, e avvia il riavvio che fa la fase finale dell’aggiornamento.
.....
Connected to reef-casa-sopra
IP address: 192.168.1.184
SPIFFS 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 4096
FTP: Upload of file /firmware.bin byte 5744
FTP: Upload of file /firmware.bin byte 7180
FTP: Upload of file /firmware.bin byte 9228
FTP: Upload of file /firmware.bin byte 11276
FTP: Upload of file /firmware.bin byte 13324
FTP: Upload of file /firmware.bin byte 15372
FTP: Upload of file /firmware.bin byte 17420
[...]
FTP: Upload of file /firmware.bin byte 805900
FTP: Upload of file /firmware.bin byte 807948
FTP: Upload of file /firmware.bin byte 809996
FTP: Upload of file /firmware.bin byte 812044
FTP: Upload of file /firmware.bin byte 814092
FTP: Upload of file /firmware.bin byte 816140
FTP: Upload of file /firmware.bin byte 817696
FTP: Finish transfer!
The uploaded firmware now stored in FS!
Search for firmware in FS..not found!
FTP: Free space change, free 276602 of 689120!
Aggiornare il filesystem con un server FTP su una SD
Per questo test, ricorda di modificare (o verificare) 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 data dello sketch, scrivo 0.1 all’interno e lo uso come versione del FileSystem, poi carico la cartella dati sul filesystem.
Ora andremo a modificare il file version.txt
con la versione 0.2 e poi lo caricheremo sul dispositivo, quindi
Cambia la versione in 0.2
in version.txt
, rigenera senza caricare e 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
- ESP32: piedinatura, specifiche e configurazione dell’Arduino IDE
- ESP32: fileSystem integrato SPIFFS
- ESP32: gestire più seriali e logging per il debug
- ESP32 risparmio energetico pratico
- ESP32 risparmio energetico pratico: gestire WiFi e CPU
- ESP32 risparmio energetico pratico: modem e light sleep
- ESP32 risparmio energetico pratico: deep sleep e ibernazione
- ESP32 risparmio energetico pratico: preservare dati al riavvio, sveglia a tempo e tramite tocco
- ESP32 risparmio energetico pratico: sveglia esterna e da ULP
- ESP32 risparmio energetico pratico: sveglia da UART e GPIO
- ESP32: filesystem integrato LittleFS
- ESP32: filesystem integrato FFat (Fat/exFAT)
- ESP32-wroom-32
- ESP32-CAM
- ESP32: ethernet w5500 con chiamate standard (HTTP) e SSL (HTTPS)
- ESP32: ethernet enc28j60 con chiamate standard (HTTP) e SSL (HTTPS)
- Come usare la scheda SD con l’esp32
- esp32 e esp8266: file system FAT su memoria SPI flash esterna
- Gestione aggiornamenti firmware e OTA
- Gestione del firmware
- Aggiornamento OTA con Arduino IDE
- Aggiornamento OTA con browser web
- Aggiornamenti automatici OTA da un server HTTP
- Aggiornamento del firmware non standard
- Integrare LAN8720 con ESP32 per la connettività Ethernet con plain (HTTP) e SSL (HTTPS)
- Collegare l’EByte E70 (CC1310) ai dispositivi ESP32 c3/s3 ed un semplice sketch di esempio
- ESP32-C3: piedinatura, specifiche e configurazione dell’IDE Arduino
- Integrazione del modulo W5500 su ESP32 con Core 3: supporto nativo ai protocolli Ethernet con SSL e altre funzionalità
- Integrazione del modulo LAN8720 su ESP32 con Core 3: supporto nativo del protocollo Ethernet con SSL e altre funzionalità.