mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-15 00:02:49 +03:00
A new approach for erasing WiFi Settings (#8828)
* A new approach for erasing WiFi Settings Add support for hardware reset function call - simulates EXT_RST via HWDT. Add reset selection to `ESP.eraseConfig()` for calling hardware reset after erasing the WiFi Settings. Update ArduinoOTA to use `ESP.eraseConfig(true)` Externalized `ArduinoOTA.eraseConfigAndReset()` Add OTA examples to illustrate using erase config changes. * style fixed confused example * improve wording * Add new state to retry eraseConfigAndReset * Removed unreachable error test from examples. Removed continuous retry of "eraseConfig" and allow the script to assign an error handling option for "eraseConfig" failure. Update example to use error handling option. * In eboot for function ets_wdt_enable() added missing arguments * Update comments and example * Wording * Rebuilt eboot.elf with current tools from ./get.py * Requested changes. * cleanup comments * Update hardware_reset Avoid using "[[noreturn]]" - not accepted for a .c file function Updated to use __attribute__((noreturn)) to handle both .cpp and .c file functions.
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
#endif
|
||||
#include <functional>
|
||||
#include <WiFiUdp.h>
|
||||
#include <eboot_command.h>
|
||||
#include "ArduinoOTA.h"
|
||||
#include "MD5Builder.h"
|
||||
#include "StreamString.h"
|
||||
@ -24,10 +25,11 @@ extern "C" {
|
||||
#include <ESP8266mDNS.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ESP_OTA
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#if defined(DEBUG_ESP_OTA) && defined(DEBUG_ESP_PORT)
|
||||
#define OTA_DEBUG DEBUG_ESP_PORT
|
||||
#endif
|
||||
#define OTA_DEBUG_PRINTF(fmt, ...) OTA_DEBUG.printf_P(PSTR(fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
#define OTA_DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
ArduinoOTAClass::ArduinoOTAClass()
|
||||
@ -93,6 +95,10 @@ void ArduinoOTAClass::setRebootOnSuccess(bool reboot){
|
||||
_rebootOnSuccess = reboot;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::setEraseConfig(ota_erase_cfg_t eraseConfig){
|
||||
_eraseConfig = eraseConfig;
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
if (_initialized)
|
||||
return;
|
||||
@ -119,7 +125,7 @@ void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
if(!_udp_ota->listen(IP_ADDR_ANY, _port))
|
||||
return;
|
||||
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
|
||||
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
if(_useMDNS) {
|
||||
MDNS.begin(_hostname.c_str());
|
||||
@ -133,9 +139,7 @@ void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
#endif
|
||||
_initialized = true;
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
|
||||
}
|
||||
|
||||
int ArduinoOTAClass::parseInt(){
|
||||
@ -243,13 +247,11 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
IPAddress ota_ip = _ota_ip;
|
||||
|
||||
if (!Update.begin(_size, _cmd)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.println("Update Begin Error");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("Update Begin Error\n");
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_BEGIN_ERROR);
|
||||
}
|
||||
|
||||
|
||||
StreamString ss;
|
||||
Update.printError(ss);
|
||||
_udp_ota->append("ERR: ", 5);
|
||||
@ -275,9 +277,7 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
|
||||
WiFiClient client;
|
||||
if (!client.connect(_ota_ip, _ota_port)) {
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Connect Failed\n");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("Connect Failed\n");
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_CONNECT_ERROR);
|
||||
@ -293,9 +293,7 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
while (!client.available() && waited--)
|
||||
delay(1);
|
||||
if (!waited){
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Receive Failed\n");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("Receive Failed\n");
|
||||
_udp_ota->listen(IP_ADDR_ANY, _port);
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_RECEIVE_ERROR);
|
||||
@ -320,18 +318,31 @@ void ArduinoOTAClass::_runUpdate() {
|
||||
client.flush();
|
||||
delay(1000);
|
||||
client.stop();
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Update Success\n");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("Update Success\n");
|
||||
if (_end_callback) {
|
||||
_end_callback();
|
||||
}
|
||||
if(_rebootOnSuccess){
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("Rebooting...\n");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("Rebooting...\n");
|
||||
//let serial/network finish tasks that might be given in _end_callback
|
||||
delay(100);
|
||||
if (OTA_ERASE_CFG_NO != _eraseConfig) {
|
||||
eraseConfigAndReset(); // returns on failure
|
||||
if (_error_callback) {
|
||||
_error_callback(OTA_ERASE_SETTINGS_ERROR);
|
||||
}
|
||||
if (OTA_ERASE_CFG_ABORT_ON_ERROR == _eraseConfig) {
|
||||
eboot_command_clear();
|
||||
return;
|
||||
}
|
||||
#ifdef OTA_DEBUG
|
||||
else if (OTA_ERASE_CFG_IGNORE_ERROR == _eraseConfig) {
|
||||
// Fallthrough and restart
|
||||
} else {
|
||||
panic();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ESP.restart();
|
||||
}
|
||||
} else {
|
||||
@ -357,10 +368,19 @@ void ArduinoOTAClass::end() {
|
||||
}
|
||||
#endif
|
||||
_state = OTA_IDLE;
|
||||
#ifdef OTA_DEBUG
|
||||
OTA_DEBUG.printf("OTA server stopped.\n");
|
||||
#endif
|
||||
OTA_DEBUG_PRINTF("OTA server stopped.\n");
|
||||
}
|
||||
|
||||
void ArduinoOTAClass::eraseConfigAndReset() {
|
||||
OTA_DEBUG_PRINTF("Erase Config and Hard Reset ...\n");
|
||||
if (WiFi.mode(WIFI_OFF)) {
|
||||
ESP.eraseConfigAndReset(); // No return testing - Only returns on failure
|
||||
OTA_DEBUG_PRINTF(" ESP.eraseConfigAndReset() failed!\n");
|
||||
} else {
|
||||
OTA_DEBUG_PRINTF(" WiFi.mode(WIFI_OFF) Timeout!\n");
|
||||
}
|
||||
}
|
||||
|
||||
//this needs to be called in the loop()
|
||||
void ArduinoOTAClass::handle() {
|
||||
if (_state == OTA_RUNUPDATE) {
|
||||
|
@ -18,9 +18,16 @@ typedef enum {
|
||||
OTA_BEGIN_ERROR,
|
||||
OTA_CONNECT_ERROR,
|
||||
OTA_RECEIVE_ERROR,
|
||||
OTA_END_ERROR
|
||||
OTA_END_ERROR,
|
||||
OTA_ERASE_SETTINGS_ERROR
|
||||
} ota_error_t;
|
||||
|
||||
typedef enum {
|
||||
OTA_ERASE_CFG_NO = 0,
|
||||
OTA_ERASE_CFG_IGNORE_ERROR,
|
||||
OTA_ERASE_CFG_ABORT_ON_ERROR
|
||||
} ota_erase_cfg_t;
|
||||
|
||||
class ArduinoOTAClass
|
||||
{
|
||||
public:
|
||||
@ -47,6 +54,10 @@ class ArduinoOTAClass
|
||||
//Sets if the device should be rebooted after successful update. Default true
|
||||
void setRebootOnSuccess(bool reboot);
|
||||
|
||||
//Sets flag to erase WiFi Settings at reboot/reset. "eraseConfig" selects to
|
||||
//abort erase on failure or ignore error and erase.
|
||||
void setEraseConfig(ota_erase_cfg_t eraseConfig = OTA_ERASE_CFG_ABORT_ON_ERROR);
|
||||
|
||||
//This callback will be called when OTA connection has begun
|
||||
void onStart(THandlerFunction fn);
|
||||
|
||||
@ -64,6 +75,11 @@ class ArduinoOTAClass
|
||||
|
||||
//Ends the ArduinoOTA service
|
||||
void end();
|
||||
|
||||
//Has the effect of the "+ WiFi Settings" in the Arduino IDE Tools "Erase
|
||||
//Flash" selection. Only returns on erase flash failure.
|
||||
void eraseConfigAndReset();
|
||||
|
||||
//Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
|
||||
void handle();
|
||||
|
||||
@ -84,6 +100,7 @@ class ArduinoOTAClass
|
||||
bool _initialized = false;
|
||||
bool _rebootOnSuccess = true;
|
||||
bool _useMDNS = true;
|
||||
ota_erase_cfg_t _eraseConfig = OTA_ERASE_CFG_NO;
|
||||
ota_state_t _state = OTA_IDLE;
|
||||
int _size = 0;
|
||||
int _cmd = 0;
|
||||
|
106
libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino
Normal file
106
libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
This example is a variation on BasicOTA.
|
||||
|
||||
As is, this example will "always" erase WiFi Settings and reset after a
|
||||
successful update. You can make this conditional.
|
||||
*/
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
Serial.println(String("Reset Reason: ") + ESP.getResetReason());
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
/*
|
||||
By calling "ArduinoOTA.setEraseConfig(ArduinoOTA::OTA_ERASE_CFG_ABORT_ON_ERROR),"
|
||||
this example will erase the "WiFi Settings" as part of an OTA update. When
|
||||
erasing WiFi Settings fails, the OTA Update aborts, and eboot will not
|
||||
copy the new ".bin" in place.
|
||||
|
||||
Without the call to "ArduinoOTA.setEraseConfig" legacy behavior, the
|
||||
system restarts without touching the WiFi Settings.
|
||||
|
||||
Options for "setEraseConfig" to handle eraseConfig failures:
|
||||
OTA_ERASE_CFG_NO - Do not erase WiFi Settings
|
||||
OTA_ERASE_CFG_IGNORE_ERROR - Ignore the error and continue with update ".bin" copy
|
||||
OTA_ERASE_CFG_ABORT_ON_ERROR - Cancel flash update copy at reboot
|
||||
|
||||
To meet unique requirements, you can make the call below conditional.
|
||||
Also, this call could be enabled before ArduinoOTA.onEnd() and canceled
|
||||
here with "ArduinoOTA.setEraseConfig(OTA_ERASE_CFG_NO)."
|
||||
*/
|
||||
ArduinoOTA.setEraseConfig(OTA_ERASE_CFG_ABORT_ON_ERROR);
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
} else if (error == OTA_ERASE_SETTINGS_ERROR) {
|
||||
Serial.println("Erase WiFi Settings Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
162
libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino
Normal file
162
libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
This example is a variation on BasicOTA.
|
||||
|
||||
Logic added to look for a change in SDK Version. If changed, erase the WiFi
|
||||
Settings and Reset the system.
|
||||
|
||||
Added extra debug printing to aid in cutting through the confusion of the
|
||||
multiple reboots.
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
// You can control the extra debug printing here. To turn off, change 1 to 0.
|
||||
#if 1
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define CONSOLE DEBUG_ESP_PORT
|
||||
#else
|
||||
#define CONSOLE Serial
|
||||
#endif
|
||||
#define DEBUG_PRINTF(fmt, ...) CONSOLE.printf_P(PSTR(fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
struct YourEEPROMData {
|
||||
// list of parameters you need to keep
|
||||
// ...
|
||||
|
||||
// To efficiently save and compare SDK version strings, we use their computed
|
||||
// CRC32 value.
|
||||
uint32_t sdkCrc;
|
||||
};
|
||||
|
||||
bool checkSdkCrc() {
|
||||
auto reason = ESP.getResetInfoPtr()->reason;
|
||||
// In this example, the OTA update does a software restart. As coded, SDK
|
||||
// version checks are only performed after a hard reset. Change the lines
|
||||
// below at your discretion.
|
||||
//
|
||||
// Boot loop guard
|
||||
// Limit crash loops erasing flash. Only run at Power On or Hardware Reset.
|
||||
if (REASON_DEFAULT_RST != reason && REASON_EXT_SYS_RST != reason) {
|
||||
DEBUG_PRINTF(" Boot loop guard - SDK version not checked. To perform check, do a hardware reset.\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* sdkVerStr = ESP.getSdkVersion();
|
||||
uint32_t sdkVersionCrc = crc32(sdkVerStr, strlen(sdkVerStr));
|
||||
|
||||
uint32_t savedSdkVersionCrc;
|
||||
EEPROM.begin((sizeof(struct YourEEPROMData) + 3) & ~3);
|
||||
EEPROM.get(offsetof(struct YourEEPROMData, sdkCrc), savedSdkVersionCrc);
|
||||
|
||||
DEBUG_PRINTF(" Current SDK Verison: %s CRC(0x%08X)\r\n", sdkVerStr, sdkVersionCrc);
|
||||
DEBUG_PRINTF(" Previous saved SDK CRC(0x%08X)\r\n", savedSdkVersionCrc);
|
||||
if (sdkVersionCrc == savedSdkVersionCrc) {
|
||||
return EEPROM.end();
|
||||
}
|
||||
|
||||
DEBUG_PRINTF(" Handle wew SDK Version\r\n");
|
||||
// Remember new SDK CRC
|
||||
EEPROM.put(offsetof(struct YourEEPROMData, sdkCrc), sdkVersionCrc);
|
||||
if (EEPROM.commit() && EEPROM.end()) {
|
||||
// Erase WiFi Settings and Reset
|
||||
DEBUG_PRINTF(" EEPROM update successful. New SDK CRC saved.\r\n");
|
||||
DEBUG_PRINTF(" Erase config and reset: ...\r\n");
|
||||
ArduinoOTA.eraseConfigAndReset(); // Only returns on fail
|
||||
DEBUG_PRINTF(" ArduinoOTA.eraseConfigAndReset() failed!\r\n");
|
||||
|
||||
} else {
|
||||
DEBUG_PRINTF(" EEPROM.commit() or EEPROM.end() failed!\r\n");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting");
|
||||
// It is normal for resets generated by "ArduinoOTA.eraseConfigAndReset()"
|
||||
// to be reported as "External System".
|
||||
Serial.println(String("Reset Reason: ") + ESP.getResetReason());
|
||||
Serial.println("Check for changes in SDK Version:");
|
||||
if (checkSdkCrc()) {
|
||||
Serial.println(" SDK version has not changed.");
|
||||
} else {
|
||||
Serial.println(" SDK version changed and update to saved details failed.");
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Connection Failed! Rebooting...");
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Port defaults to 8266
|
||||
// ArduinoOTA.setPort(8266);
|
||||
|
||||
// Hostname defaults to esp8266-[ChipID]
|
||||
// ArduinoOTA.setHostname("myesp8266");
|
||||
|
||||
// No authentication by default
|
||||
// ArduinoOTA.setPassword("admin");
|
||||
|
||||
// Password can be set with it's md5 value as well
|
||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
||||
ArduinoOTA.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_FS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
||||
Serial.println("Start updating " + type);
|
||||
});
|
||||
ArduinoOTA.onEnd([]() {
|
||||
Serial.println("\nEnd");
|
||||
});
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
Serial.println("Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
Serial.println("Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
Serial.println("Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
Serial.println("Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
Serial.println("End Failed");
|
||||
}
|
||||
});
|
||||
ArduinoOTA.begin();
|
||||
Serial.println("Ready");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
Reference in New Issue
Block a user