From 0420f45508be60c17d342c9f94bc88430bb0b663 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sun, 31 Jul 2022 00:39:27 +0300 Subject: [PATCH 1/5] Updater lifetime callbacks --- cores/esp8266/Updater.cpp | 35 +++++++++++++++++++++++++--------- cores/esp8266/Updater.h | 40 +++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index c194c1d23f..59cf80e04e 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -41,21 +41,22 @@ UpdaterClass::~UpdaterClass() #endif } -UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) { - _progress_callback = fn; - return *this; -} - -void UpdaterClass::_reset() { - if (_buffer) +void UpdaterClass::_reset(bool callback) { + if (_buffer) { delete[] _buffer; - _buffer = 0; + } + + _buffer = nullptr; _bufferLen = 0; _startAddress = 0; _currentAddress = 0; _size = 0; _command = U_FLASH; + if (callback && _end_callback) { + _end_callback(); + } + if(_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // off } @@ -170,7 +171,15 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { } else { _bufferSize = 256; } - _buffer = new uint8_t[_bufferSize]; + _buffer = new (std::nothrow) uint8_t[_bufferSize]; + if (!_buffer) { +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.println(F("[begin] Unable to allocate a temporary buffer.")); +#endif + _reset(false); + return false; + } + _command = command; #ifdef DEBUG_UPDATER @@ -182,6 +191,11 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { if (!_verify) { _md5.begin(); } + + if (_start_callback) { + _start_callback(); + } + return true; } @@ -555,6 +569,9 @@ size_t UpdaterClass::writeStream(Stream &data, uint16_t streamTimeout) { void UpdaterClass::_setError(int error){ _error = error; + if (_error_callback) { + _error_callback(error); + } #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 911ddf7784..905a45cb9d 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -51,7 +51,9 @@ class UpdaterVerifyClass { class UpdaterClass { public: - typedef std::function THandlerFunction_Progress; + using THandlerFunction_Progress = std::function; + using THandlerFunction_Error = std::function; + using THandlerFunction = std::function; UpdaterClass(); ~UpdaterClass(); @@ -120,7 +122,33 @@ class UpdaterClass { /* This callback will be called when Updater is receiving data */ - UpdaterClass& onProgress(THandlerFunction_Progress fn); + UpdaterClass& onProgress(THandlerFunction_Progress fn) { + _progress_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onError(THandlerFunction_Error fn) { + _error_callback = std::move(fn); + return *this; + + /* + This callback will be called when Updater begins + */ + UpdaterClass& onStart(THandlerFunction fn) { + _start_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onEnd(THandlerFunction fn) { + _end_callback = std::move(fn); + return *this; + } //Helpers uint8_t getError(){ return _error; } @@ -175,7 +203,7 @@ class UpdaterClass { } private: - void _reset(); + void _reset(bool callback = true); bool _writeBuffer(); bool _verifyHeader(uint8_t data); @@ -202,8 +230,12 @@ class UpdaterClass { // Optional signed binary verification UpdaterHashClass *_hash = nullptr; UpdaterVerifyClass *_verify = nullptr; - // Optional progress callback function + + // Optional lifetime callback functions THandlerFunction_Progress _progress_callback = nullptr; + THandlerFunction_Error _error_callback = nullptr; + THandlerFunction _start_callback = nullptr; + THandlerFunction _end_callback = nullptr; }; extern UpdaterClass Update; From ad271fec99b7620b0fc2dd3ef0991efbc7d45723 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sun, 31 Jul 2022 01:05:03 +0300 Subject: [PATCH 2/5] stringify error --- cores/esp8266/Updater.cpp | 45 ++++++++++++++++++++++++--------------- cores/esp8266/Updater.h | 6 ++++++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 59cf80e04e..28454b78b4 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -578,39 +578,50 @@ void UpdaterClass::_setError(int error){ _reset(); // Any error condition invalidates the entire update, so clear partial status } -void UpdaterClass::printError(Print &out){ - out.printf_P(PSTR("ERROR[%u]: "), _error); +String UpdaterClass::getErrorString() const { + String out; + if(_error == UPDATE_ERROR_OK){ - out.println(F("No Error")); + out = F("No Error"); } else if(_error == UPDATE_ERROR_WRITE){ - out.println(F("Flash Write Failed")); + out = F("Flash Write Failed"); } else if(_error == UPDATE_ERROR_ERASE){ - out.println(F("Flash Erase Failed")); + out = F("Flash Erase Failed"); } else if(_error == UPDATE_ERROR_READ){ - out.println(F("Flash Read Failed")); + out = F("Flash Read Failed"); } else if(_error == UPDATE_ERROR_SPACE){ - out.println(F("Not Enough Space")); + out = F("Not Enough Space"); } else if(_error == UPDATE_ERROR_SIZE){ - out.println(F("Bad Size Given")); + out = F("Bad Size Given"); } else if(_error == UPDATE_ERROR_STREAM){ - out.println(F("Stream Read Timeout")); + out = F("Stream Read Timeout"); } else if(_error == UPDATE_ERROR_NO_DATA){ - out.println(F("No data supplied")); + out = F("No data supplied"); } else if(_error == UPDATE_ERROR_MD5){ - out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str()); + out += F("MD5 verification failed: "); + out += F("expected: ") + _target_md5; + out += F(", calculated: ") + _md5.toString(); } else if(_error == UPDATE_ERROR_SIGN){ - out.println(F("Signature verification failed")); + out = F("Signature verification failed"); } else if(_error == UPDATE_ERROR_FLASH_CONFIG){ - out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize()); + out += F("Flash config wrong: "); + out += F("real: ") + String(ESP.getFlashChipRealSize(), 10); + out += F(", SDK: ") + String(ESP.getFlashChipSize(), 10); } else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){ - out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize()); + out += F("new Flash config wrong, real size: ") + String(ESP.getFlashChipRealSize(), 10); } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ - out.println(F("Magic byte is wrong, not 0xE9")); + out = F("Magic byte is not 0xE9"); } else if (_error == UPDATE_ERROR_BOOTSTRAP){ - out.println(F("Invalid bootstrapping state, reset ESP8266 before updating")); + out = F("Invalid bootstrapping state, reset ESP8266 before updating"); } else { - out.println(F("UNKNOWN")); + out = F("UNKNOWN"); } + + return out; +} + +void UpdaterClass::printError(Print &out){ + out.printf_P(PSTR("ERROR[%u]: %s\n"), _error, getErrorString().c_str()); } UpdaterClass Update; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 905a45cb9d..9e376c8336 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -99,6 +99,11 @@ class UpdaterClass { */ bool end(bool evenIfRemaining = false); + /* + Gets the last error description as string + */ + String getErrorString() const; + /* Prints the last error to an output stream */ @@ -133,6 +138,7 @@ class UpdaterClass { UpdaterClass& onError(THandlerFunction_Error fn) { _error_callback = std::move(fn); return *this; + } /* This callback will be called when Updater begins From 6649e66a063ba91ea42a97e7a2e0d4582c759cd3 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Tue, 1 Nov 2022 20:44:45 +0300 Subject: [PATCH 3/5] busy --- cores/esp8266/Updater.cpp | 49 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index ee809e4226..05f8f20dea 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -600,40 +600,57 @@ void UpdaterClass::_setError(int error){ String UpdaterClass::getErrorString() const { String out; - if(_error == UPDATE_ERROR_OK){ + switch (_error) { + case UPDATE_ERROR_OK: out = F("No Error"); - } else if(_error == UPDATE_ERROR_WRITE){ + break; + case UPDATE_ERROR_WRITE: out = F("Flash Write Failed"); - } else if(_error == UPDATE_ERROR_ERASE){ + break; + case UPDATE_ERROR_ERASE: out = F("Flash Erase Failed"); - } else if(_error == UPDATE_ERROR_READ){ + break; + case UPDATE_ERROR_READ: out = F("Flash Read Failed"); - } else if(_error == UPDATE_ERROR_SPACE){ + break; + case UPDATE_ERROR_SPACE: out = F("Not Enough Space"); - } else if(_error == UPDATE_ERROR_SIZE){ + break; + case UPDATE_ERROR_SIZE: out = F("Bad Size Given"); - } else if(_error == UPDATE_ERROR_STREAM){ + break; + case UPDATE_ERROR_STREAM: out = F("Stream Read Timeout"); - } else if(_error == UPDATE_ERROR_NO_DATA){ + break; + case UPDATE_ERROR_NO_DATA: out = F("No data supplied"); - } else if(_error == UPDATE_ERROR_MD5){ + break; + case UPDATE_ERROR_MD5: out += F("MD5 verification failed: "); out += F("expected: ") + _target_md5; out += F(", calculated: ") + _md5.toString(); - } else if(_error == UPDATE_ERROR_SIGN){ + break; + case UPDATE_ERROR_SIGN: out = F("Signature verification failed"); - } else if(_error == UPDATE_ERROR_FLASH_CONFIG){ + break; + case UPDATE_ERROR_FLASH_CONFIG: out += F("Flash config wrong: "); out += F("real: ") + String(ESP.getFlashChipRealSize(), 10); out += F(", SDK: ") + String(ESP.getFlashChipSize(), 10); - } else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){ - out += F("new Flash config wrong, real size: ") + String(ESP.getFlashChipRealSize(), 10); - } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ + break; + case UPDATE_ERROR_NEW_FLASH_CONFIG: + out += F("new Flash config wrong, real size: "); + out += String(ESP.getFlashChipRealSize(), 10); + break; + case UPDATE_ERROR_MAGIC_BYTE: out = F("Magic byte is not 0xE9"); - } else if (_error == UPDATE_ERROR_BOOTSTRAP){ + break; + case UPDATE_ERROR_BOOTSTRAP: out = F("Invalid bootstrapping state, reset ESP8266 before updating"); - } else { + break; + default: out = F("UNKNOWN"); + break; } return out; From 3d7161225356b2f4cf61cd9bb1b0047719da23bf Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Tue, 1 Nov 2022 20:47:49 +0300 Subject: [PATCH 4/5] type --- cores/esp8266/Updater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 05f8f20dea..6600a2b9e0 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -657,7 +657,7 @@ String UpdaterClass::getErrorString() const { } void UpdaterClass::printError(Print &out){ - out.printf_P(PSTR("ERROR[%u]: %s\n"), _error, getErrorString().c_str()); + out.printf_P(PSTR("ERROR[%hhu]: %s\n"), _error, getErrorString().c_str()); } UpdaterClass Update; From 6cda80688b8921bbfd45d864d75f8236310d7a8f Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Tue, 1 Nov 2022 21:09:32 +0300 Subject: [PATCH 5/5] alloc error --- cores/esp8266/Updater.cpp | 21 +++++++++++---------- cores/esp8266/Updater.h | 1 + doc/ota_updates/readme.rst | 24 ++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 6600a2b9e0..7ec9ca6926 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -176,9 +176,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { } _buffer = new (std::nothrow) uint8_t[_bufferSize]; if (!_buffer) { -#ifdef DEBUG_UPDATER - DEBUG_UPDATER.println(F("[begin] Unable to allocate a temporary buffer.")); -#endif + _setError(UPDATE_ERROR_OOM); _reset(false); return false; } @@ -307,7 +305,7 @@ bool UpdaterClass::end(bool evenIfRemaining){ const uint32_t sigAddr = _startAddress + binSize; sig.reset(new (std::nothrow) uint8_t[sigLen]); if (!sig) { - _setError(UPDATE_ERROR_SIGN); + _setError(UPDATE_ERROR_OOM); _reset(); return false; } @@ -622,17 +620,11 @@ String UpdaterClass::getErrorString() const { case UPDATE_ERROR_STREAM: out = F("Stream Read Timeout"); break; - case UPDATE_ERROR_NO_DATA: - out = F("No data supplied"); - break; case UPDATE_ERROR_MD5: out += F("MD5 verification failed: "); out += F("expected: ") + _target_md5; out += F(", calculated: ") + _md5.toString(); break; - case UPDATE_ERROR_SIGN: - out = F("Signature verification failed"); - break; case UPDATE_ERROR_FLASH_CONFIG: out += F("Flash config wrong: "); out += F("real: ") + String(ESP.getFlashChipRealSize(), 10); @@ -648,6 +640,15 @@ String UpdaterClass::getErrorString() const { case UPDATE_ERROR_BOOTSTRAP: out = F("Invalid bootstrapping state, reset ESP8266 before updating"); break; + case UPDATE_ERROR_SIGN: + out = F("Signature verification failed"); + break; + case UPDATE_ERROR_NO_DATA: + out = F("No data supplied"); + break; + case UPDATE_ERROR_OOM: + out = F("Out of memory"); + break; default: out = F("UNKNOWN"); break; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 9e376c8336..ad652d3806 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -20,6 +20,7 @@ #define UPDATE_ERROR_BOOTSTRAP (11) #define UPDATE_ERROR_SIGN (12) #define UPDATE_ERROR_NO_DATA (13) +#define UPDATE_ERROR_OOM (14) #define U_FLASH 0 #define U_FS 100 diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index 53ce8c2707..6fe2a06935 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -668,9 +668,29 @@ Updater class Updater is in the Core and deals with writing the firmware to the flash, checking its integrity and telling the bootloader (eboot) to load the new firmware on the next boot. -**Note:** The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. +The following `Updater `__. +.. code:: cpp + + using THandlerFunction_Progress = std::function; + void onProgress(THandlerFunction_Progress); // current and total number of bytes + + using THandlerFunction_Error = std::function; + void onStart(THandlerFunction_Error); // error code + + using THandlerFunction = std::function; + void onEnd(THandlerFunction); + void onError(THandlerFunction); + +Using RTC memory +~~~~~~~~~~~~~~~~ + +The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. + +Flash mode and size +~~~~~~~~~~~~~~~~~~~ + +For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. Update process - memory view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~