Skip to content

Commit fff12e3

Browse files
authoredNov 3, 2022
Updater lifetime callbacks (#8653)
follow-up of #8598 similar to ArduinoOTA API, execute certain callback in the Updater context.
1 parent 27c0591 commit fff12e3

File tree

3 files changed

+153
-48
lines changed

3 files changed

+153
-48
lines changed
 

‎cores/esp8266/Updater.cpp

+88-42
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,22 @@ UpdaterClass::~UpdaterClass()
4444
#endif
4545
}
4646

47-
UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) {
48-
_progress_callback = fn;
49-
return *this;
50-
}
51-
52-
void UpdaterClass::_reset() {
53-
if (_buffer)
47+
void UpdaterClass::_reset(bool callback) {
48+
if (_buffer) {
5449
delete[] _buffer;
55-
_buffer = 0;
50+
}
51+
52+
_buffer = nullptr;
5653
_bufferLen = 0;
5754
_startAddress = 0;
5855
_currentAddress = 0;
5956
_size = 0;
6057
_command = U_FLASH;
6158

59+
if (callback && _end_callback) {
60+
_end_callback();
61+
}
62+
6263
if(_ledPin != -1) {
6364
digitalWrite(_ledPin, !_ledOn); // off
6465
}
@@ -173,7 +174,13 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
173174
} else {
174175
_bufferSize = 256;
175176
}
176-
_buffer = new uint8_t[_bufferSize];
177+
_buffer = new (std::nothrow) uint8_t[_bufferSize];
178+
if (!_buffer) {
179+
_setError(UPDATE_ERROR_OOM);
180+
_reset(false);
181+
return false;
182+
}
183+
177184
_command = command;
178185

179186
#ifdef DEBUG_UPDATER
@@ -185,6 +192,11 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
185192
if (!_verify) {
186193
_md5.begin();
187194
}
195+
196+
if (_start_callback) {
197+
_start_callback();
198+
}
199+
188200
return true;
189201
}
190202

@@ -293,7 +305,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
293305
const uint32_t sigAddr = _startAddress + binSize;
294306
sig.reset(new (std::nothrow) uint8_t[sigLen]);
295307
if (!sig) {
296-
_setError(UPDATE_ERROR_SIGN);
308+
_setError(UPDATE_ERROR_OOM);
297309
_reset();
298310
return false;
299311
}
@@ -574,45 +586,79 @@ size_t UpdaterClass::writeStream(Stream &data, uint16_t streamTimeout) {
574586

575587
void UpdaterClass::_setError(int error){
576588
_error = error;
589+
if (_error_callback) {
590+
_error_callback(error);
591+
}
577592
#ifdef DEBUG_UPDATER
578593
printError(DEBUG_UPDATER);
579594
#endif
580595
_reset(); // Any error condition invalidates the entire update, so clear partial status
581596
}
582597

583-
void UpdaterClass::printError(Print &out){
584-
out.printf_P(PSTR("ERROR[%u]: "), _error);
585-
if(_error == UPDATE_ERROR_OK){
586-
out.println(F("No Error"));
587-
} else if(_error == UPDATE_ERROR_WRITE){
588-
out.println(F("Flash Write Failed"));
589-
} else if(_error == UPDATE_ERROR_ERASE){
590-
out.println(F("Flash Erase Failed"));
591-
} else if(_error == UPDATE_ERROR_READ){
592-
out.println(F("Flash Read Failed"));
593-
} else if(_error == UPDATE_ERROR_SPACE){
594-
out.println(F("Not Enough Space"));
595-
} else if(_error == UPDATE_ERROR_SIZE){
596-
out.println(F("Bad Size Given"));
597-
} else if(_error == UPDATE_ERROR_STREAM){
598-
out.println(F("Stream Read Timeout"));
599-
} else if(_error == UPDATE_ERROR_NO_DATA){
600-
out.println(F("No data supplied"));
601-
} else if(_error == UPDATE_ERROR_MD5){
602-
out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str());
603-
} else if(_error == UPDATE_ERROR_SIGN){
604-
out.println(F("Signature verification failed"));
605-
} else if(_error == UPDATE_ERROR_FLASH_CONFIG){
606-
out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize());
607-
} else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){
608-
out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize());
609-
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
610-
out.println(F("Magic byte is wrong, not 0xE9"));
611-
} else if (_error == UPDATE_ERROR_BOOTSTRAP){
612-
out.println(F("Invalid bootstrapping state, reset ESP8266 before updating"));
613-
} else {
614-
out.println(F("UNKNOWN"));
598+
String UpdaterClass::getErrorString() const {
599+
String out;
600+
601+
switch (_error) {
602+
case UPDATE_ERROR_OK:
603+
out = F("No Error");
604+
break;
605+
case UPDATE_ERROR_WRITE:
606+
out = F("Flash Write Failed");
607+
break;
608+
case UPDATE_ERROR_ERASE:
609+
out = F("Flash Erase Failed");
610+
break;
611+
case UPDATE_ERROR_READ:
612+
out = F("Flash Read Failed");
613+
break;
614+
case UPDATE_ERROR_SPACE:
615+
out = F("Not Enough Space");
616+
break;
617+
case UPDATE_ERROR_SIZE:
618+
out = F("Bad Size Given");
619+
break;
620+
case UPDATE_ERROR_STREAM:
621+
out = F("Stream Read Timeout");
622+
break;
623+
case UPDATE_ERROR_MD5:
624+
out += F("MD5 verification failed: ");
625+
out += F("expected: ") + _target_md5;
626+
out += F(", calculated: ") + _md5.toString();
627+
break;
628+
case UPDATE_ERROR_FLASH_CONFIG:
629+
out += F("Flash config wrong: ");
630+
out += F("real: ") + String(ESP.getFlashChipRealSize(), 10);
631+
out += F(", SDK: ") + String(ESP.getFlashChipSize(), 10);
632+
break;
633+
case UPDATE_ERROR_NEW_FLASH_CONFIG:
634+
out += F("new Flash config wrong, real size: ");
635+
out += String(ESP.getFlashChipRealSize(), 10);
636+
break;
637+
case UPDATE_ERROR_MAGIC_BYTE:
638+
out = F("Magic byte is not 0xE9");
639+
break;
640+
case UPDATE_ERROR_BOOTSTRAP:
641+
out = F("Invalid bootstrapping state, reset ESP8266 before updating");
642+
break;
643+
case UPDATE_ERROR_SIGN:
644+
out = F("Signature verification failed");
645+
break;
646+
case UPDATE_ERROR_NO_DATA:
647+
out = F("No data supplied");
648+
break;
649+
case UPDATE_ERROR_OOM:
650+
out = F("Out of memory");
651+
break;
652+
default:
653+
out = F("UNKNOWN");
654+
break;
615655
}
656+
657+
return out;
658+
}
659+
660+
void UpdaterClass::printError(Print &out){
661+
out.printf_P(PSTR("ERROR[%hhu]: %s\n"), _error, getErrorString().c_str());
616662
}
617663

618664
UpdaterClass Update;

‎cores/esp8266/Updater.h

+43-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define UPDATE_ERROR_BOOTSTRAP (11)
2121
#define UPDATE_ERROR_SIGN (12)
2222
#define UPDATE_ERROR_NO_DATA (13)
23+
#define UPDATE_ERROR_OOM (14)
2324

2425
#define U_FLASH 0
2526
#define U_FS 100
@@ -51,7 +52,9 @@ class UpdaterVerifyClass {
5152

5253
class UpdaterClass {
5354
public:
54-
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
55+
using THandlerFunction_Progress = std::function<void(size_t, size_t)>;
56+
using THandlerFunction_Error = std::function<void(uint8_t)>;
57+
using THandlerFunction = std::function<void()>;
5558

5659
UpdaterClass();
5760
~UpdaterClass();
@@ -97,6 +100,11 @@ class UpdaterClass {
97100
*/
98101
bool end(bool evenIfRemaining = false);
99102

103+
/*
104+
Gets the last error description as string
105+
*/
106+
String getErrorString() const;
107+
100108
/*
101109
Prints the last error to an output stream
102110
*/
@@ -120,7 +128,34 @@ class UpdaterClass {
120128
/*
121129
This callback will be called when Updater is receiving data
122130
*/
123-
UpdaterClass& onProgress(THandlerFunction_Progress fn);
131+
UpdaterClass& onProgress(THandlerFunction_Progress fn) {
132+
_progress_callback = std::move(fn);
133+
return *this;
134+
}
135+
136+
/*
137+
This callback will be called when Updater ends
138+
*/
139+
UpdaterClass& onError(THandlerFunction_Error fn) {
140+
_error_callback = std::move(fn);
141+
return *this;
142+
}
143+
144+
/*
145+
This callback will be called when Updater begins
146+
*/
147+
UpdaterClass& onStart(THandlerFunction fn) {
148+
_start_callback = std::move(fn);
149+
return *this;
150+
}
151+
152+
/*
153+
This callback will be called when Updater ends
154+
*/
155+
UpdaterClass& onEnd(THandlerFunction fn) {
156+
_end_callback = std::move(fn);
157+
return *this;
158+
}
124159

125160
//Helpers
126161
uint8_t getError(){ return _error; }
@@ -175,7 +210,7 @@ class UpdaterClass {
175210
}
176211

177212
private:
178-
void _reset();
213+
void _reset(bool callback = true);
179214
bool _writeBuffer();
180215

181216
bool _verifyHeader(uint8_t data);
@@ -202,8 +237,12 @@ class UpdaterClass {
202237
// Optional signed binary verification
203238
UpdaterHashClass *_hash = nullptr;
204239
UpdaterVerifyClass *_verify = nullptr;
205-
// Optional progress callback function
240+
241+
// Optional lifetime callback functions
206242
THandlerFunction_Progress _progress_callback = nullptr;
243+
THandlerFunction_Error _error_callback = nullptr;
244+
THandlerFunction _start_callback = nullptr;
245+
THandlerFunction _end_callback = nullptr;
207246
};
208247

209248
extern UpdaterClass Update;

‎doc/ota_updates/readme.rst

+22-2
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,29 @@ Updater class
668668

669669
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.
670670

671-
**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) <https://github.com/esp8266/Arduino/pull/5330#issuecomment-437803456>`__.
671+
The following `Updater <https://github.com/esp8266/Arduino/tree/master/cores/esp8266/Updater.h` methods could be used to be notified about OTA progress:
672672

673-
**Note:** 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) <https://github.com/esp8266/Arduino/issues/7307#issuecomment-631523053>`__.
673+
.. code:: cpp
674+
675+
using THandlerFunction_Progress = std::function<void(size_t, size_t)>;
676+
void onProgress(THandlerFunction_Progress); // current and total number of bytes
677+
678+
using THandlerFunction_Error = std::function<void(uint8_t)>;
679+
void onStart(THandlerFunction_Error); // error code
680+
681+
using THandlerFunction = std::function<void()>;
682+
void onEnd(THandlerFunction);
683+
void onError(THandlerFunction);
684+
685+
Using RTC memory
686+
~~~~~~~~~~~~~~~~
687+
688+
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) <https://github.com/esp8266/Arduino/pull/5330#issuecomment-437803456>`__.
689+
690+
Flash mode and size
691+
~~~~~~~~~~~~~~~~~~~
692+
693+
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) <https://github.com/esp8266/Arduino/issues/7307#issuecomment-631523053>`__.
674694

675695
Update process - memory view
676696
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

0 commit comments

Comments
 (0)