1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +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:
M Hightower 2023-08-29 08:24:07 -07:00 committed by GitHub
parent 1a4663fbe8
commit a348833a81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 531 additions and 34 deletions

View File

@ -17,8 +17,39 @@
#define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0); #define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0);
extern void ets_wdt_enable(void); /*
extern void ets_wdt_disable(void); After Power Enable Pin, EXT_RST, or HWDT event, at "main()" in eboot, WDT is
disabled. Key WDT hardware registers are zero.
After "ESP.restart()" and other soft restarts, at "main()" in eboot, WDT is enabled.
References for the under-documented ets_wdt_* API
https://mongoose-os.com/blog/esp8266-watchdog-timer/
http://cholla.mmto.org/esp8266/bootrom/boot.txt
After looking at esp8266-watchdog-timer some more, `ets_wdt_enable(4, 12, 12)`
is good for eboot's needs. From a ".map" the NON-OS SDK does not use the
ets_wdt_* APIs, so our choices are not too critical.
The SDK will set up the WDT as it wants it.
A rationale for keeping the "ets_wdt_enable()" line, if the system is not
stable during a "soft restart," the HWDT would provide a recovery reboot.
*/
extern void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2);
/*
"ets_wdt_disable"
Diables WDT, then feeds the dog.
For current modes other than 1 or 2, returns the current mode.
For current mode 1, calls ets_timer_disarm, then return the current mode.
For current mode 2, calls ets_isr_mask, then return the current mode.
I always see a return value of 0xFFFFFFFF.
The return value would normally be used with ets_wdt_restore; however, that is
not an option since a valid prior call to ets_wdt_enable() may not have been done.
*/
extern uint32_t ets_wdt_disable(void);
int print_version(const uint32_t flash_addr) int print_version(const uint32_t flash_addr)
{ {
@ -241,12 +272,12 @@ int main()
ets_wdt_disable(); ets_wdt_disable();
res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false);
ets_wdt_enable(); ets_wdt_enable(4, 12, 12); // WDT about 13 secs.
ets_printf("%d\n", res); ets_printf("%d\n", res);
#if 0 #if 0
//devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the
//beginning of the image in the empty area, see #7458. Disabling for now. //beginning of the image in the empty area, see #7458. Disabling for now.
//TODO: replace the below verify with hash type, crc, or similar. //TODO: replace the below verify with hash type, crc, or similar.
// Verify the copy // Verify the copy
ets_printf("cmp:"); ets_printf("cmp:");
@ -257,7 +288,7 @@ int main()
} }
ets_printf("%d\n", res); ets_printf("%d\n", res);
#endif #endif
if (res == 0) { if (res == 0) {
cmd.action = ACTION_LOAD_APP; cmd.action = ACTION_LOAD_APP;
cmd.args[0] = cmd.args[1]; cmd.args[0] = cmd.args[1];

Binary file not shown.

View File

@ -31,6 +31,7 @@
#include "umm_malloc/umm_malloc.h" #include "umm_malloc/umm_malloc.h"
#include <pgmspace.h> #include <pgmspace.h>
#include "reboot_uart_dwnld.h" #include "reboot_uart_dwnld.h"
#include "hardware_reset.h"
extern "C" { extern "C" {
#include "user_interface.h" #include "user_interface.h"
@ -519,7 +520,7 @@ struct rst_info * EspClass::getResetInfoPtr(void) {
} }
bool EspClass::eraseConfig(void) { bool EspClass::eraseConfig(void) {
const size_t cfgSize = 0x4000; const size_t cfgSize = 0x4000; // Sectors: RF_CAL + SYSTEMPARAM[3]
size_t cfgAddr = ESP.getFlashChipSize() - cfgSize; size_t cfgAddr = ESP.getFlashChipSize() - cfgSize;
for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) { for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) {
@ -531,6 +532,17 @@ bool EspClass::eraseConfig(void) {
return true; return true;
} }
bool EspClass::eraseConfigAndReset(void) {
// Before calling, ensure the WiFi state is equivalent to
// "WiFi.mode(WIFI_OFF)." This will reduce the likelihood of the SDK
// performing WiFi data writes to Flash between erasing and resetting.
bool reset = eraseConfig();
if (reset) {
hardware_reset();
}
return reset;
}
uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes)
{ {
/** /**

View File

@ -212,6 +212,22 @@ class EspClass {
static bool eraseConfig(); static bool eraseConfig();
/**
* @brief Erases 4 sectors at the end of flash, 1 - RF_CAL and 3 - SYSTEMPARM.
* These are the same additional sectors that are erase when you select
* Erase Flash: "Sketch + WiFi Settings" from the Arduino IDE Tools menu.
*
* This operation erases the running SDK's flash configuration space.
* As a precaution before calling, first call "WiFi.mode(WIFI_OFF)."
*
* If you need to erase "WiFi Settings" and reboot consider using
* "ArduinoOTA.eraseConfigAndReset()" it handles shutting down WiFi
* before the erase.
* @return bool result of operation. Always False on return.
* Function does not return on success.
*/
static bool eraseConfigAndReset();
static uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes); static uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes);
static uint32_t random(); static uint32_t random();

View File

@ -0,0 +1,119 @@
/*
Make the reset look like an EXT_RST reset by:
* Set INTLEVEL to 15 blocking NMI Software WDT interference
* set "restart reason" to REASON_EXT_SYS_RST
* Config Hardware WDT for 1.6ms
* Disable Hardware WDT Level-1 interrupt option
* wait, ...
Inspired by RTOS SDK hardware_restart in panic.c
*/
#include "Arduino.h"
#include <user_interface.h>
#include <ets_sys.h>
#include "hardware_reset.h"
// Extracted from RTOS_SDK eagle_soc.h
/*
* ESPRSSIF MIT License
*
* Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#define REG_WRITE(_r, _v) (*(volatile uint32_t *)(_r)) = (_v)
#define REG_READ(_r) (*(volatile uint32_t *)(_r))
//Watchdog reg {{
#define PERIPHS_WDT_BASEADDR 0x60000900
#define WDT_CTL_ADDRESS 0
#define WDT_OP_ADDRESS 0x4
#define WDT_OP_ND_ADDRESS 0x8
#define WDT_RST_ADDRESS 0x14
#define WDT_CTL_RSTLEN_MASK 0x38
#define WDT_CTL_RSPMOD_MASK 0x6
#define WDT_CTL_EN_MASK 0x1
#define WDT_CTL_RSTLEN_LSB 0x3
#define WDT_CTL_RSPMOD_LSB 0x1
#define WDT_CTL_EN_LSB 0
#define WDT_FEED_VALUE 0x73
#define WDT_REG_READ(_reg) REG_READ(PERIPHS_WDT_BASEADDR + _reg)
#define WDT_REG_WRITE(_reg, _val) REG_WRITE(PERIPHS_WDT_BASEADDR + _reg, _val)
#define CLEAR_WDT_REG_MASK(_reg, _mask) WDT_REG_WRITE(_reg, WDT_REG_READ(_reg) & (~_mask))
#define SET_WDT_REG_MASK(_reg, _mask, _val) SET_PERI_REG_BITS((PERIPHS_WDT_BASEADDR + _reg), _mask, _val, 0)
#undef WDT_FEED
#define WDT_FEED() WDT_REG_WRITE(WDT_RST_ADDRESS, WDT_FEED_VALUE)
//}}
// Inspired by RTOS SDK task_wdt.c and hardware_restart in panic.c
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern "C" {
void hardware_reset(void) {
volatile uint32_t* const rtc_mem = (volatile uint32_t *)0x60001100u;
// Block NMI or Software WDT from disturbing out restart reason
xt_rsil(15);
// An HWDT reason would imply a fault or bug, but this reset was requested.
// Set hint reason to EXT_RST. From empirical evidence, an HWDT looks a lot
// like an EXT_RST. The WDT registers are reset to zero like an EXT_RST;
// however, the PLL initialization is still set. We can still read the Boot
// ROM serial output messages.
// SDK restart reason/hint location
rtc_mem[0] = REASON_EXT_SYS_RST;
// Disable WDT
CLEAR_WDT_REG_MASK(WDT_CTL_ADDRESS, WDT_CTL_EN_MASK);
// Set Reset pulse to maximum
// Select Reset only - no level-1 interrupt
SET_WDT_REG_MASK(WDT_CTL_ADDRESS,
WDT_CTL_RSTLEN_MASK | WDT_CTL_RSPMOD_MASK,
(7 << WDT_CTL_RSTLEN_LSB) | (2 << WDT_CTL_RSPMOD_LSB));
// Set WDT Reset timer to 1.6 ms.
WDT_REG_WRITE(WDT_OP_ADDRESS, 1); // 2^n * 0.8ms, mask 0xf, n = 1 -> (2^1 = 2) * 0.8 * 0.001 = 0.0016
// Enable WDT
SET_WDT_REG_MASK(WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 1 << WDT_CTL_EN_LSB);
while (true);
}
};

View File

@ -0,0 +1,14 @@
#ifndef HARDWARE_RESET_H
#define HARDWARE_RESET_H
#ifdef __cplusplus
extern "C" {
#endif
void hardware_reset(void) __attribute__((noreturn));
#ifdef __cplusplus
}
#endif
#endif

View File

@ -3,6 +3,7 @@
#endif #endif
#include <functional> #include <functional>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <eboot_command.h>
#include "ArduinoOTA.h" #include "ArduinoOTA.h"
#include "MD5Builder.h" #include "MD5Builder.h"
#include "StreamString.h" #include "StreamString.h"
@ -24,10 +25,11 @@ extern "C" {
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#endif #endif
#ifdef DEBUG_ESP_OTA #if defined(DEBUG_ESP_OTA) && defined(DEBUG_ESP_PORT)
#ifdef DEBUG_ESP_PORT
#define OTA_DEBUG 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 #endif
ArduinoOTAClass::ArduinoOTAClass() ArduinoOTAClass::ArduinoOTAClass()
@ -93,6 +95,10 @@ void ArduinoOTAClass::setRebootOnSuccess(bool reboot){
_rebootOnSuccess = reboot; _rebootOnSuccess = reboot;
} }
void ArduinoOTAClass::setEraseConfig(ota_erase_cfg_t eraseConfig){
_eraseConfig = eraseConfig;
}
void ArduinoOTAClass::begin(bool useMDNS) { void ArduinoOTAClass::begin(bool useMDNS) {
if (_initialized) if (_initialized)
return; return;
@ -119,7 +125,7 @@ void ArduinoOTAClass::begin(bool useMDNS) {
if(!_udp_ota->listen(IP_ADDR_ANY, _port)) if(!_udp_ota->listen(IP_ADDR_ANY, _port))
return; return;
_udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this));
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
if(_useMDNS) { if(_useMDNS) {
MDNS.begin(_hostname.c_str()); MDNS.begin(_hostname.c_str());
@ -133,9 +139,7 @@ void ArduinoOTAClass::begin(bool useMDNS) {
#endif #endif
_initialized = true; _initialized = true;
_state = OTA_IDLE; _state = OTA_IDLE;
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port);
#endif
} }
int ArduinoOTAClass::parseInt(){ int ArduinoOTAClass::parseInt(){
@ -243,13 +247,11 @@ void ArduinoOTAClass::_runUpdate() {
IPAddress ota_ip = _ota_ip; IPAddress ota_ip = _ota_ip;
if (!Update.begin(_size, _cmd)) { if (!Update.begin(_size, _cmd)) {
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("Update Begin Error\n");
OTA_DEBUG.println("Update Begin Error");
#endif
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_BEGIN_ERROR); _error_callback(OTA_BEGIN_ERROR);
} }
StreamString ss; StreamString ss;
Update.printError(ss); Update.printError(ss);
_udp_ota->append("ERR: ", 5); _udp_ota->append("ERR: ", 5);
@ -275,9 +277,7 @@ void ArduinoOTAClass::_runUpdate() {
WiFiClient client; WiFiClient client;
if (!client.connect(_ota_ip, _ota_port)) { if (!client.connect(_ota_ip, _ota_port)) {
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("Connect Failed\n");
OTA_DEBUG.printf("Connect Failed\n");
#endif
_udp_ota->listen(IP_ADDR_ANY, _port); _udp_ota->listen(IP_ADDR_ANY, _port);
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_CONNECT_ERROR); _error_callback(OTA_CONNECT_ERROR);
@ -293,9 +293,7 @@ void ArduinoOTAClass::_runUpdate() {
while (!client.available() && waited--) while (!client.available() && waited--)
delay(1); delay(1);
if (!waited){ if (!waited){
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("Receive Failed\n");
OTA_DEBUG.printf("Receive Failed\n");
#endif
_udp_ota->listen(IP_ADDR_ANY, _port); _udp_ota->listen(IP_ADDR_ANY, _port);
if (_error_callback) { if (_error_callback) {
_error_callback(OTA_RECEIVE_ERROR); _error_callback(OTA_RECEIVE_ERROR);
@ -320,18 +318,31 @@ void ArduinoOTAClass::_runUpdate() {
client.flush(); client.flush();
delay(1000); delay(1000);
client.stop(); client.stop();
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("Update Success\n");
OTA_DEBUG.printf("Update Success\n");
#endif
if (_end_callback) { if (_end_callback) {
_end_callback(); _end_callback();
} }
if(_rebootOnSuccess){ if(_rebootOnSuccess){
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("Rebooting...\n");
OTA_DEBUG.printf("Rebooting...\n");
#endif
//let serial/network finish tasks that might be given in _end_callback //let serial/network finish tasks that might be given in _end_callback
delay(100); 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(); ESP.restart();
} }
} else { } else {
@ -357,10 +368,19 @@ void ArduinoOTAClass::end() {
} }
#endif #endif
_state = OTA_IDLE; _state = OTA_IDLE;
#ifdef OTA_DEBUG OTA_DEBUG_PRINTF("OTA server stopped.\n");
OTA_DEBUG.printf("OTA server stopped.\n");
#endif
} }
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() //this needs to be called in the loop()
void ArduinoOTAClass::handle() { void ArduinoOTAClass::handle() {
if (_state == OTA_RUNUPDATE) { if (_state == OTA_RUNUPDATE) {

View File

@ -18,9 +18,16 @@ typedef enum {
OTA_BEGIN_ERROR, OTA_BEGIN_ERROR,
OTA_CONNECT_ERROR, OTA_CONNECT_ERROR,
OTA_RECEIVE_ERROR, OTA_RECEIVE_ERROR,
OTA_END_ERROR OTA_END_ERROR,
OTA_ERASE_SETTINGS_ERROR
} ota_error_t; } 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 class ArduinoOTAClass
{ {
public: public:
@ -47,6 +54,10 @@ class ArduinoOTAClass
//Sets if the device should be rebooted after successful update. Default true //Sets if the device should be rebooted after successful update. Default true
void setRebootOnSuccess(bool reboot); 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 //This callback will be called when OTA connection has begun
void onStart(THandlerFunction fn); void onStart(THandlerFunction fn);
@ -64,6 +75,11 @@ class ArduinoOTAClass
//Ends the ArduinoOTA service //Ends the ArduinoOTA service
void end(); 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. //Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used.
void handle(); void handle();
@ -84,6 +100,7 @@ class ArduinoOTAClass
bool _initialized = false; bool _initialized = false;
bool _rebootOnSuccess = true; bool _rebootOnSuccess = true;
bool _useMDNS = true; bool _useMDNS = true;
ota_erase_cfg_t _eraseConfig = OTA_ERASE_CFG_NO;
ota_state_t _state = OTA_IDLE; ota_state_t _state = OTA_IDLE;
int _size = 0; int _size = 0;
int _cmd = 0; int _cmd = 0;

View 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();
}

View 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();
}