From 1a4663fbe8dca5ee3992aa7b0fc98794d956ac30 Mon Sep 17 00:00:00 2001 From: Luciano Martorella Date: Tue, 15 Aug 2023 18:25:58 +0200 Subject: [PATCH 1/8] Add virtual beginMulticast(...) stub to UDP class (#8969) * - Same UDP API of ESP32 core * - PR review --- cores/esp8266/Udp.h | 1 + doc/esp8266wifi/udp-class.rst | 4 ++-- libraries/ESP8266WiFi/src/WiFiUdp.cpp | 5 +++++ libraries/ESP8266WiFi/src/WiFiUdp.h | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/Udp.h b/cores/esp8266/Udp.h index 6c8ee4b385..37f2fb922b 100644 --- a/cores/esp8266/Udp.h +++ b/cores/esp8266/Udp.h @@ -43,6 +43,7 @@ class UDP: public Stream { public: virtual ~UDP() {}; virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t) { return 0; } // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure virtual void stop() =0; // Finish with the UDP socket // Sending UDP packets diff --git a/doc/esp8266wifi/udp-class.rst b/doc/esp8266wifi/udp-class.rst index 5e02f7b3da..73a9086021 100644 --- a/doc/esp8266wifi/udp-class.rst +++ b/doc/esp8266wifi/udp-class.rst @@ -26,11 +26,11 @@ Multicast UDP .. code:: cpp - uint8_t beginMulticast (IPAddress interfaceAddr, IPAddress multicast, uint16_t port) + uint8_t beginMulticast (IPAddress multicast, uint16_t port) virtual int beginPacketMulticast (IPAddress multicastAddress, uint16_t port, IPAddress interfaceAddress, int ttl=1) IPAddress destinationIP () uint16_t localPort () -The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. +The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. For code samples please refer to separate section with `examples `__ dedicated specifically to the UDP Class. diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.cpp b/libraries/ESP8266WiFi/src/WiFiUdp.cpp index a45659d844..cf22e3cd2a 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.cpp +++ b/libraries/ESP8266WiFi/src/WiFiUdp.cpp @@ -84,6 +84,11 @@ uint8_t WiFiUDP::begin(uint16_t port) return (_ctx->listen(IPAddress(), port)) ? 1 : 0; } +uint8_t WiFiUDP::beginMulticast(IPAddress multicast, uint16_t port) +{ + return beginMulticast(IP_ADDR_ANY, multicast, port); +} + uint8_t WiFiUDP::beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port) { if (_ctx) { diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.h b/libraries/ESP8266WiFi/src/WiFiUdp.h index d8fbcbe221..fdf82217bc 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -47,6 +47,8 @@ class WiFiUDP : public UDP, public SList { // Finish with the UDP connection void stop() override; // join a multicast group and listen on the given port + virtual uint8_t beginMulticast(IPAddress interfaceAddr, uint16_t port); + // join a multicast group and listen on the given port, using a specific interface address uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port); // Sending UDP packets From a348833a817244ab7e3c65c78ad5fb3276bb6f35 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:24:07 -0700 Subject: [PATCH 2/8] 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. --- bootloaders/eboot/eboot.c | 43 ++++- bootloaders/eboot/eboot.elf | Bin 89152 -> 89264 bytes cores/esp8266/Esp.cpp | 14 +- cores/esp8266/Esp.h | 16 ++ cores/esp8266/hardware_reset.cpp | 119 +++++++++++++ cores/esp8266/hardware_reset.h | 14 ++ libraries/ArduinoOTA/ArduinoOTA.cpp | 72 +++++--- libraries/ArduinoOTA/ArduinoOTA.h | 19 +- .../OTAEraseConfig/OTAEraseConfig.ino | 106 ++++++++++++ .../examples/OTASdkCheck/OTASdkCheck.ino | 162 ++++++++++++++++++ 10 files changed, 531 insertions(+), 34 deletions(-) create mode 100644 cores/esp8266/hardware_reset.cpp create mode 100644 cores/esp8266/hardware_reset.h create mode 100644 libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino create mode 100644 libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 6e15d137b2..c3d0c278f7 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -17,8 +17,39 @@ #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) { @@ -241,12 +272,12 @@ int main() ets_wdt_disable(); 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); #if 0 - //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. + //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. //TODO: replace the below verify with hash type, crc, or similar. // Verify the copy ets_printf("cmp:"); @@ -257,7 +288,7 @@ int main() } ets_printf("%d\n", res); -#endif +#endif if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index c036951ef899ec484a553dd8bbcb6cd1e56b73a7..0e679ff46c495e768ca7af2e0c983f8af1962462 100755 GIT binary patch delta 11349 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 diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 20da390008..67719dcfe7 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -31,6 +31,7 @@ #include "umm_malloc/umm_malloc.h" #include #include "reboot_uart_dwnld.h" +#include "hardware_reset.h" extern "C" { #include "user_interface.h" @@ -519,7 +520,7 @@ struct rst_info * EspClass::getResetInfoPtr(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; for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) { @@ -531,6 +532,17 @@ bool EspClass::eraseConfig(void) { 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) { /** diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 9f97175c29..9cb4141292 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -212,6 +212,22 @@ class EspClass { 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 uint32_t random(); diff --git a/cores/esp8266/hardware_reset.cpp b/cores/esp8266/hardware_reset.cpp new file mode 100644 index 0000000000..827b402a5a --- /dev/null +++ b/cores/esp8266/hardware_reset.cpp @@ -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 +#include +#include "hardware_reset.h" + + +// Extracted from RTOS_SDK eagle_soc.h +/* + * ESPRSSIF MIT License + * + * Copyright (c) 2015 + * + * 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); + } +}; diff --git a/cores/esp8266/hardware_reset.h b/cores/esp8266/hardware_reset.h new file mode 100644 index 0000000000..38d798b815 --- /dev/null +++ b/cores/esp8266/hardware_reset.h @@ -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 diff --git a/libraries/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index 109bbb4959..1ef83b895f 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/ArduinoOTA.cpp @@ -3,6 +3,7 @@ #endif #include #include +#include #include "ArduinoOTA.h" #include "MD5Builder.h" #include "StreamString.h" @@ -24,10 +25,11 @@ extern "C" { #include #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) { diff --git a/libraries/ArduinoOTA/ArduinoOTA.h b/libraries/ArduinoOTA/ArduinoOTA.h index d1a81a316e..d3d93b9b36 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.h +++ b/libraries/ArduinoOTA/ArduinoOTA.h @@ -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; diff --git a/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino b/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino new file mode 100644 index 0000000000..85a0797d47 --- /dev/null +++ b/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino @@ -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 +#include +#include +#include + +#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(); +} diff --git a/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino b/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino new file mode 100644 index 0000000000..1965321bda --- /dev/null +++ b/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino @@ -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 +#include +#include +#include +#include + +// 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(); +} From 7f2deb14a254d0d1b0903e0bb15a87815833f579 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:48:53 +0300 Subject: [PATCH 3/8] Bump actions/checkout from 3 to 4 (#8984) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-host.yml | 4 ++-- .github/workflows/build-ide.yml | 8 ++++---- .github/workflows/build-platformio.yml | 2 +- .github/workflows/check-autogenerated.yml | 6 +++--- .github/workflows/documentation.yml | 2 +- .github/workflows/release-to-publish.yml | 2 +- .github/workflows/style-check.yml | 4 ++-- .github/workflows/tag-to-draft-release.yml | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-host.yml b/.github/workflows/build-host.yml index 079f8885cd..ae0353068d 100644 --- a/.github/workflows/build-host.yml +++ b/.github/workflows/build-host.yml @@ -17,7 +17,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - run: | @@ -32,7 +32,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - run: | diff --git a/.github/workflows/build-ide.yml b/.github/workflows/build-ide.yml index 75e0e0583e..b97874e9d1 100644 --- a/.github/workflows/build-ide.yml +++ b/.github/workflows/build-ide.yml @@ -17,7 +17,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - uses: actions/cache@v3 @@ -39,7 +39,7 @@ jobs: lwip: ["default", "IPv6"] chunk: [0, 1, 2, 3, 4, 5, 6, 7] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - uses: actions/setup-python@v4 @@ -63,7 +63,7 @@ jobs: name: Windows runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - uses: actions/setup-python@v4 @@ -88,7 +88,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - uses: actions/setup-python@v4 diff --git a/.github/workflows/build-platformio.yml b/.github/workflows/build-platformio.yml index db2f5187a6..04d8b53d9e 100644 --- a/.github/workflows/build-platformio.yml +++ b/.github/workflows/build-platformio.yml @@ -18,7 +18,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - uses: actions/setup-python@v4 diff --git a/.github/workflows/check-autogenerated.yml b/.github/workflows/check-autogenerated.yml index dc0e624e35..099875f253 100644 --- a/.github/workflows/check-autogenerated.yml +++ b/.github/workflows/check-autogenerated.yml @@ -16,7 +16,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - run: | @@ -29,7 +29,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - uses: actions/setup-python@v4 @@ -51,7 +51,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - uses: actions/setup-python@v4 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 1120851742..a2d2402f34 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -16,7 +16,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - uses: actions/setup-python@v4 diff --git a/.github/workflows/release-to-publish.yml b/.github/workflows/release-to-publish.yml index e8dc9ffa96..3a80412551 100644 --- a/.github/workflows/release-to-publish.yml +++ b/.github/workflows/release-to-publish.yml @@ -36,7 +36,7 @@ jobs: name: Update master JSON file runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 0 diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index 6511ccf355..8d6ef3ed51 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -17,7 +17,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - uses: actions/setup-python@v4 @@ -46,7 +46,7 @@ jobs: name: codespell runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false - name: Run codespell diff --git a/.github/workflows/tag-to-draft-release.yml b/.github/workflows/tag-to-draft-release.yml index 855421b691..d4b3762cac 100644 --- a/.github/workflows/tag-to-draft-release.yml +++ b/.github/workflows/tag-to-draft-release.yml @@ -18,7 +18,7 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 From 1662248b394740d82824f504a021d4dad3d314b6 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 8 Oct 2023 10:30:00 -0700 Subject: [PATCH 4/8] Add link to IDE 2.x LittleFS upload tool (#8998) --- doc/filesystem.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/filesystem.rst b/doc/filesystem.rst index 86375ad8e1..1dca7dc707 100644 --- a/doc/filesystem.rst +++ b/doc/filesystem.rst @@ -228,10 +228,16 @@ use esptool.py. *ESP8266LittleFS* is the equivalent tool for LittleFS. -- Download the 2.6.0 or later version of the tool: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases +For Arduino IDE 1.x: +- Download the latest plugin from: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases - Install as above - To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload +For Arduino IDE 2.x: +- Download the latest plugin from: https://github.com/earlephilhower/arduino-littlefs-upload/releases +- Follow the manual installation instructions in: https://github.com/earlephilhower/arduino-littlefs-upload/blob/main/README.md +- To upload a LittleFS filesystem use `Ctrl`+`Shift`+`P` and then select the `Upload LittleFS to Pico/ESP8266` item + File system object (SPIFFS/LittleFS/SD/SDFS) -------------------------------------------- From 497dacc78fd6d4411f28f19ecc65a2363fc594e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Andr=C3=A1ssy?= <10706773+JAndrassy@users.noreply.github.com> Date: Sat, 4 Nov 2023 23:29:06 +0100 Subject: [PATCH 5/8] WiFi.BSSID and scan result BSSID with parameter as other WiFi libraries (#9008) --- libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp | 12 ++++++++++++ libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h | 1 + libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp | 15 +++++++++++++++ libraries/ESP8266WiFi/src/ESP8266WiFiScan.h | 1 + 4 files changed, 29 insertions(+) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index bfba96457d..c27000182c 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -607,6 +607,18 @@ uint8_t* ESP8266WiFiSTAClass::BSSID(void) { return reinterpret_cast(conf.bssid); } +/** + * Fill the current bssid / mac associated with the network if configured + * @param bssid pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + * @return bssid uint8_t * + */ +uint8_t* ESP8266WiFiSTAClass::BSSID(uint8_t* bssid) { + struct station_config conf; + wifi_station_get_config(&conf); + memcpy(bssid, conf.bssid, WL_MAC_ADDR_LENGTH); + return bssid; +} + /** * Return the current bssid / mac associated with the network if configured * @return String bssid mac diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h index 0c1150a8e4..11f188a6d3 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h @@ -79,6 +79,7 @@ class ESP8266WiFiSTAClass: public LwipIntf { String psk() const; uint8_t * BSSID(); + uint8_t * BSSID(uint8_t* bssid); String BSSIDstr(); int8_t RSSI(); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp index 5331c7bac8..1fdc6e04c7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp @@ -259,6 +259,21 @@ uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i) { return it->bssid; } +/** + * fill MAC / BSSID of scanned wifi + * @param i specify from which network item want to get the information + * @param bssid pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + * @return uint8_t * MAC / BSSID of scanned wifi + */ +uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i, uint8_t* bssid) { + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if(!it) { + return 0; + } + memcpy(bssid, it->bssid, WL_MAC_ADDR_LENGTH); + return bssid; +} + /** * return MAC / BSSID of scanned wifi * @param i specify from which network item want to get the information diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h index 36d159aca0..1c9c3a7408 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h @@ -48,6 +48,7 @@ class ESP8266WiFiScanClass { uint8_t encryptionType(uint8_t networkItem); int32_t RSSI(uint8_t networkItem); uint8_t * BSSID(uint8_t networkItem); + uint8_t * BSSID(uint8_t networkItem, uint8_t* bssid); String BSSIDstr(uint8_t networkItem); int32_t channel(uint8_t networkItem); bool isHidden(uint8_t networkItem); From 30c6df46399c4a72babaec59d7b976687c750849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Andr=C3=A1ssy?= <10706773+JAndrassy@users.noreply.github.com> Date: Sat, 4 Nov 2023 23:39:46 +0100 Subject: [PATCH 6/8] WiFiServer - operator bool() and method end() (#8995) --- libraries/ESP8266WiFi/src/WiFiServer.cpp | 8 ++++++++ libraries/ESP8266WiFi/src/WiFiServer.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 884065920c..462dbd7f74 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -173,6 +173,14 @@ void WiFiServer::stop() { close(); } +void WiFiServer::end() { + close(); +} + +WiFiServer::operator bool() { + return (status() != CLOSED); +} + err_t WiFiServer::_accept(tcp_pcb* apcb, err_t err) { (void) err; DEBUGV("WS:ac\r\n"); diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index ac6ad40aaf..049aa9f9d0 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -100,6 +100,8 @@ class WiFiServer { uint16_t port() const; void close(); void stop(); + void end(); + explicit operator bool(); using ClientType = WiFiClient; From fb8d6d668df95e138d6248e7239cbf98c77f8174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Sun, 5 Nov 2023 00:01:49 +0100 Subject: [PATCH 7/8] ESP8266HTTPUpdate: Get available firmware version from update server (#8968) * added getAvailableVersion(), moved _httpClientTimeout and _followRedirects to protected, added enum HTTPUpdateError * auto numbering of HTTPUpdateError enum * added getAvailableVersion(), debug output current current Sketch MD5 * updated advanced updater php script * switch case indention corrected * Revert "added getAvailableVersion(), debug output current current Sketch MD5" This reverts commit 60d2c7762e7fb1fed7fae37fa99be149e12f125c. * Revert "auto numbering of HTTPUpdateError enum" This reverts commit 61785b27da3f2d42f8f95316d78ce22e5b00103a. * Revert "added getAvailableVersion(), moved _httpClientTimeout and _followRedirects to protected, added enum HTTPUpdateError" This reverts commit cec84ed17ab149d3e48082293f9e2723246b7d0b. * corrected incorrect merge with master --- doc/ota_updates/readme.rst | 94 ++++++++++----- .../src/ESP8266httpUpdate.cpp | 110 ++++++++++++++++++ .../ESP8266httpUpdate/src/ESP8266httpUpdate.h | 29 ++--- 3 files changed, 188 insertions(+), 45 deletions(-) diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index 373327fe8c..ba68506cb0 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -592,34 +592,42 @@ With this information the script now can check if an update is needed. It is als "DOOR-7-g14f53a19", - "18:FE:AA:AA:AA:BB" => "TEMP-1.0.0" - ); - - if(!isset($db[$_SERVER['x-ESP8266-STA-MAC']])) { - header($_SERVER["SERVER_PROTOCOL"].' 500 ESP MAC not configured for updates', true, 500); + + $db_string = '{ + "18:FE:AA:AA:AA:AA": {"file": "DOOR-7-g14f53a19.bin", "version": 1}, + "18:FE:AA:AA:AA:BB": {"file": "TEMP-1.0.0".bin", "version": 1}}'; + // $db_string = file_get_contents("arduino-db.json"); + $db = json_decode($db_string, true); + $mode = $headers['x-ESP8266-mode']; + $mac = $headers['x-ESP8266-STA-MAC']; + + if (!isset($db[$mac])) { + header($_SERVER["SERVER_PROTOCOL"].' 404 ESP MAC not configured for updates', true, 404); + echo "MAC ".$mac." not configured for updates\n"; + exit(); } - - $localBinary = "./bin/".$db[$_SERVER['x-ESP8266-STA-MAC']].".bin"; - - // Check if version has been set and does not match, if not, check if - // MD5 hash between local binary and ESP8266 binary do not match if not. - // then no update has been found. - if((!check_header('x-ESP8266-sdk-version') && $db[$_SERVER['x-ESP8266-STA-MAC']] != $_SERVER['x-ESP8266-version']) - || $_SERVER["x-ESP8266-sketch-md5"] != md5_file($localBinary)) { - sendFile($localBinary); + + $localBinary = $db[$mac]['file']; + $localVersion = $db[$mac]['version']; + + if (!is_readable($localBinary)) { + header($_SERVER["SERVER_PROTOCOL"].' 404 File not found', true, 404); + echo "File ".$localBinary." not found\n"; + exit(); + } + + if ($mode == 'sketch') { + // Check if version has been set and does not match, if not, check if + // MD5 hash between local binary and ESP8266 binary do not match if not. + // then no update has been found. + if ((check_header('x-ESP8266-version') && $headers['x-ESP8266-version'] != $localVersion)) { + // || $headers["x-ESP8266-sketch-md5"] != md5_file($localBinary)) { + sendFile($localBinary, $localVersion); + } else { + header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304); + echo "File ".$localBinary." not modified\n"; + } + } else if ($mode == 'version') { + header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200); + header('x-MD5: '.md5_file($localBinary), true); + header('x-version: '.$localVersion, true); } else { - header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304); + header($_SERVER["SERVER_PROTOCOL"].' 404 Mode not supported', true, 404); + echo "mode: ".$mode." not supported\n"; + exit(); } - - header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500); + ?> Stream Interface ---------------- diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 9cd5d62801..af45852e5f 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -235,6 +235,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str()); if(currentVersion && currentVersion[0] != 0x00) { DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() ); @@ -440,6 +441,115 @@ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, const String& md5, return true; } +/** + * @brief Get avialable firmware version from update server + * @author Holger Mueller + * @date 2023-08-03 + * + * @param client WiFiClient to use (see HTTPClient::begin) + * @param host Update host name or IP (see HTTPClient::begin) + * @param port Port on host (see HTTPClient::begin) + * @param uri Update URI on server (see HTTPClient::begin) + * @param current_version Current firmware version + * @param available_version Firmware version available on update server + * @return ESP8266HTTPUpdate::HTTPUpdateResult, HTTP_UPDATE_OK in case of success + */ +HTTPUpdateResult ESP8266HTTPUpdate::getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version) { + HTTPUpdateResult ret = HTTP_UPDATE_FAILED; + HTTPClient http; + http.begin(client, host, port, uri); + + // use HTTP/1.0 for update since the update handler not support any transfer Encoding + http.useHTTP10(true); + http.setTimeout(_httpClientTimeout); + http.setFollowRedirects(_followRedirects); + http.setUserAgent(F("ESP8266-http-Update")); + http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId())); + http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress()); + http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress()); + http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace())); + http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize())); + http.addHeader(F("x-ESP8266-sketch-md5"), String(ESP.getSketchMD5())); + http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize())); + http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion()); + + http.addHeader(F("x-ESP8266-mode"), F("version")); + + if (current_version && current_version[0] != 0x00) { + http.addHeader(F("x-ESP8266-version"), current_version); + } + + if (!_user.isEmpty() && !_password.isEmpty()) { + http.setAuthorization(_user.c_str(), _password.c_str()); + } + + if (!_auth.isEmpty()) { + http.setAuthorization(_auth.c_str()); + } + + const char* headerkeys[] = {"x-MD5", "x-version"}; + size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); + + // track these headers + http.collectHeaders(headerkeys, headerkeyssize); + + int code = http.GET(); + + if (code <= 0) { + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str()); + _setLastError(code); + http.end(); + return HTTP_UPDATE_FAILED; + } + + DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); + DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", http.getSize()); + if (code != HTTP_CODE_OK) { + DEBUG_HTTP_UPDATE("[httpUpdate] - payload: %s\n", http.getString().c_str()); + } + + switch (code) { + case HTTP_CODE_OK: ///< OK (check for version) + if (http.hasHeader("x-version")) { + available_version = http.header("x-version"); + ret = HTTP_UPDATE_OK; + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version.c_str()); + DEBUG_HTTP_UPDATE("[httpUpdate] - server version: %s\n", available_version.c_str()); + } else { + _setLastError(HTTP_UE_SERVER_NOT_REPORT_VERSION); + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Server did not respond with a firmware version\n"); + } + if (http.hasHeader("x-MD5")) { + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str()); + DEBUG_HTTP_UPDATE("[httpUpdate] - server Sketch MD5: %s\n", http.header("x-MD5").c_str()); + } + break; + case HTTP_CODE_NOT_FOUND: + _setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND); + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_FORBIDDEN: + _setLastError(HTTP_UE_SERVER_FORBIDDEN); + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_UNAUTHORIZED: + _setLastError(HTTP_UE_SERVER_UNAUTHORIZED); + ret = HTTP_UPDATE_FAILED; + break; + default: + _setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE); + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); + break; + } + + http.end(); + return ret; +} + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE) ESP8266HTTPUpdate ESPhttpUpdate; #endif diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 803988d3be..28e90bad23 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -43,16 +43,18 @@ #endif /// note we use HTTP client errors too so we start at 100 -//TODO - in v3.0.0 make this an enum -constexpr int HTTP_UE_TOO_LESS_SPACE = (-100); -constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101); -constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102); -constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103); -constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104); -constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105); -constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106); -constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107); -constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108); +enum HTTPUpdateError { + HTTP_UE_SERVER_NOT_REPORT_VERSION = -109, // server did not respond with a firmware version + HTTP_UE_SERVER_UNAUTHORIZED, // -108 + HTTP_UE_BIN_FOR_WRONG_FLASH, // -107 + HTTP_UE_BIN_VERIFY_HEADER_FAILED, // -106 + HTTP_UE_SERVER_FAULTY_MD5, // -105 + HTTP_UE_SERVER_WRONG_HTTP_CODE, // -104 + HTTP_UE_SERVER_FORBIDDEN, // -103 + HTTP_UE_SERVER_FILE_NOT_FOUND, // -102 + HTTP_UE_SERVER_NOT_REPORT_SIZE, // -101 + HTTP_UE_TOO_LESS_SPACE // -100 +}; enum HTTPUpdateResult { HTTP_UPDATE_FAILED, @@ -122,7 +124,9 @@ class ESP8266HTTPUpdate t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = ""); t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = ""); t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = ""); - + + t_httpUpdate_return getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version); + // Notification callbacks void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; } void onEnd(HTTPUpdateEndCB cbOnEnd) { _cbEnd = cbOnEnd; } @@ -153,10 +157,9 @@ class ESP8266HTTPUpdate String _password; String _auth; String _md5Sum; -private: int _httpClientTimeout; followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; - +private: // Callbacks HTTPUpdateStartCB _cbStart; HTTPUpdateEndCB _cbEnd; From 31c1592ad6e1153dfaa9957f5b64e0f1920d4d5a Mon Sep 17 00:00:00 2001 From: AriaN Date: Tue, 7 Nov 2023 16:49:31 +0330 Subject: [PATCH 8/8] add Stream::readStringUntil function that uses string terminator (#9011) * add readStringUntil function with string terminator * rename count parameter to untilTotalNumberOfOccurrences --- cores/esp8266/Stream.cpp | 26 ++++++++++++++++++++++++++ cores/esp8266/Stream.h | 1 + 2 files changed, 27 insertions(+) diff --git a/cores/esp8266/Stream.cpp b/cores/esp8266/Stream.cpp index a901c8d437..b9b5b95f65 100644 --- a/cores/esp8266/Stream.cpp +++ b/cores/esp8266/Stream.cpp @@ -262,6 +262,32 @@ String Stream::readStringUntil(char terminator) { return ret; } +String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences) { + String ret; + int c; + uint32_t occurrences = 0; + size_t termLen = strlen(terminator); + size_t termIndex = 0; + size_t index = 0; + + while ((c = timedRead()) > 0) { + ret += (char) c; + index++; + + if (terminator[termIndex] == c) { + if (++termIndex == termLen && ++occurrences == untilTotalNumberOfOccurrences) { + // don't include terminator in returned string + ret.remove(index - termIndex, termLen); + break; + } + } else { + termIndex = 0; + } + } + + return ret; +} + // read what can be read, immediate exit on unavailable data // prototype similar to Arduino's `int Client::read(buf, len)` int Stream::read (uint8_t* buffer, size_t maxLen) diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index f39bb423f2..21f319ee6b 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -115,6 +115,7 @@ class Stream: public Print { // Arduino String functions to be added here virtual String readString(); String readStringUntil(char terminator); + String readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences = 1); virtual int read (uint8_t* buffer, size_t len); int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); }