From 4dbfab247975530f90fe2238c77e38add1ef8f45 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Apr 2026 14:30:30 +0200 Subject: [PATCH 01/20] [Eth] Fix static IP for Ethernet --- .../net/DataStructs/NWPluginData_base.cpp | 15 +- .../net/DataStructs/NWPluginData_base.h | 11 + .../NWPluginData_static_runtime.cpp | 3 +- .../NWPluginData_static_runtime_ESP32.cpp | 4 +- .../NW001_data_struct_WiFi_STA.cpp | 30 ++- .../NW001_data_struct_WiFi_STA.h | 1 + .../NW002_data_struct_WiFi_AP.cpp | 40 ++- .../NW002_data_struct_WiFi_AP.h | 2 + .../NW003_data_struct_ETH_RMII.cpp | 102 ++++++-- .../NW003_data_struct_ETH_RMII.h | 2 + .../NW004_data_struct_ETH_SPI.cpp | 75 +++++- .../NW004_data_struct_ETH_SPI.h | 2 + .../NW005_data_struct_PPP_modem.cpp | 10 +- .../NW005_data_struct_PPP_modem.h | 2 + .../net/wifi/ESPEasyWiFi_STA_Event_ESP32.cpp | 3 +- src/src/DataStructs/NodesHandler.cpp | 3 +- .../SecurityStruct_deviceSpecific.cpp | 12 +- .../ESPEasy_key_value_store_StorageType.cpp | 1 + .../ESPEasy_key_value_store_StorageType.h | 1 + .../DataTypes/ESPEasy_plugin_functions.cpp | 3 +- src/src/Helpers/Networking.cpp | 20 +- src/src/Helpers/Networking.h | 2 + src/src/Helpers/_ESPEasy_key_value_store.cpp | 247 +++++++++++++----- src/src/Helpers/_ESPEasy_key_value_store.h | 65 +++-- .../ESPEasy_key_value_store_webform.cpp | 73 ++++-- src/src/WebServer/Markup_Forms.cpp | 41 +-- src/src/WebServer/Markup_Forms.h | 8 + 27 files changed, 591 insertions(+), 187 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp index 48ebab7eb..594372660 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp @@ -2,6 +2,8 @@ #include "../../../src/DataStructs/ESPEasy_EventStruct.h" #include "../../../src/Helpers/StringConverter.h" +#include "../../../src/Helpers/Misc.h" +#include "../../../src/Helpers/Networking.h" #if FEATURE_STORE_NETWORK_INTERFACE_SETTINGS # include "../../../src/Helpers/_ESPEasy_key_value_store.h" #include "../_NWPlugin_Helper.h" @@ -65,6 +67,15 @@ bool NWPluginData_base::isDefaultRoute() const { #endif +bool NWPluginData_base::getStaticIPAddresses(IPAddress & ip, IPAddress & gateway, IPAddress & subnetmask, IPAddress & dns ) const +{ + getStaticIPAddress(IPAddressType::IP, ip); + getStaticIPAddress(IPAddressType::Gateway, gateway); + getStaticIPAddress(IPAddressType::Subnetmask, subnetmask); + getStaticIPAddress(IPAddressType::DNS, dns); + + return IPAddressSet(ip) && IPAddressSet(gateway) && IPAddressSet(subnetmask); +} bool NWPluginData_base::hasPluginStats() const { #if FEATURE_NETWORK_STATS @@ -333,7 +344,7 @@ bool NWPluginData_base::handle_priority_route_changed() for (size_t i = 0; i < NR_ELEMENTS(cache->_dns_cache); ++i) { auto tmp = _netif->dnsIP(i); - if ((cache->_dns_cache[i] != INADDR_NONE) && (cache->_dns_cache[i] != tmp)) { + if (valid_DNS_address(cache->_dns_cache[i]) && (cache->_dns_cache[i] != tmp)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Restore cached DNS server %d from %s to %s"), _netif->desc(), @@ -409,7 +420,7 @@ bool NWPluginData_base::_restore_DNS_cache() for (size_t i = 0; i < NR_ELEMENTS(cache->_dns_cache); ++i) { auto tmp = _netif->dnsIP(i); - if ((cache->_dns_cache[i] != INADDR_NONE) && (cache->_dns_cache[i] != tmp)) { + if (valid_DNS_address(cache->_dns_cache[i]) && (cache->_dns_cache[i] != tmp)) { addLog(LOG_LEVEL_INFO, strformat( F("NW%03%d: Restore cached DNS server %d from %s to %s"), _nw_data_pluginID, diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.h b/src/ESPEasy/net/DataStructs/NWPluginData_base.h index dad8dfa36..44830f4ab 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.h @@ -33,6 +33,13 @@ struct NWPluginData_base { #endif // ifdef ESP32 ); + enum class IPAddressType { + IP, + Gateway, + Subnetmask, + DNS + }; + virtual ~NWPluginData_base(); virtual bool init(EventStruct *event) = 0; @@ -110,6 +117,10 @@ struct NWPluginData_base { virtual NWPluginData_static_runtime* getNWPluginData_static_runtime() = 0; + virtual bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const = 0; + + bool getStaticIPAddresses(IPAddress & ip, IPAddress & gateway, IPAddress & subnetmask, IPAddress & dns ) const; + #if FEATURE_NETWORK_STATS diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp index 9c3b11fb0..515d9b57b 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp @@ -3,6 +3,7 @@ #include "../../../src/Globals/EventQueue.h" #include "../../../src/Globals/Settings.h" +#include "../../../src/Helpers/Networking.h" #include "../../../src/Helpers/NetworkStatusLED.h" #include "../../../src/Helpers/StringConverter.h" @@ -171,7 +172,7 @@ void NWPluginData_static_runtime::processEvents() if (loglevelActiveFor(LOG_LEVEL_INFO) && _netif) { auto ip = _netif->localIP(); - if (ip != INADDR_NONE) { + if (IPAddressSet(ip)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Got IP: %s/%d GW: %s"), _eventInterfaceName.c_str(), diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp index 91423d209..63a4daa5a 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp @@ -5,6 +5,7 @@ # include "../ESPEasyNetwork.h" # include "../../../src/Helpers/StringConverter.h" +# include "../../../src/Helpers/Networking.h" # include "../../../src/Globals/Settings.h" # include "../Globals/NetworkState.h" @@ -147,11 +148,10 @@ void NWPluginData_static_runtime::mark_got_IP() for (size_t i = 0; i < NR_ELEMENTS(_dns_cache); ++i) { auto tmp = _netif->dnsIP(i); - _dns_cache[i] = tmp; // Also set the 'empty' ones so we won't set left-over DNS server from when another interface was active. # if NW_PLUGIN_LOG_EVENTS - if ((tmp != INADDR_NONE) && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (valid_DNS_address(tmp) && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: DNS Cache %d set to %s"), _eventInterfaceName.c_str(), diff --git a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp index 0167caea2..cda7f7ab4 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp @@ -5,6 +5,8 @@ # include "../../../src/DataTypes/ESPEasy_plugin_functions.h" # include "../../../src/WebServer/HTML_wrappers.h" # include "../../../src/WebServer/Markup.h" +# include "../../../src/Globals/Settings.h" +# include "../../../src/Helpers/Networking.h" # include "../wifi/ESPEasyWifi.h" @@ -42,9 +44,9 @@ void NW001_data_struct_WiFi_STA::webform_load(EventStruct *event) } -void NW001_data_struct_WiFi_STA::webform_save(EventStruct *event) {} +void NW001_data_struct_WiFi_STA::webform_save(EventStruct *event) {} -bool NW001_data_struct_WiFi_STA::webform_getPort(KeyValueWriter *writer) { return true; } +bool NW001_data_struct_WiFi_STA::webform_getPort(KeyValueWriter *writer) { return true; } bool NW001_data_struct_WiFi_STA::init(EventStruct *event) { @@ -108,6 +110,30 @@ NWPluginData_static_runtime * NW001_data_struct_WiFi_STA::getNWPluginData_static return _WiFiEventHandler.getNWPluginData_static_runtime(); } +bool NW001_data_struct_WiFi_STA::getStaticIPAddress(IPAddressType addressType, IPAddress& ip) const +{ + IPAddress res; + + switch (addressType) + { + case IPAddressType::IP: + res = IPAddress(Settings.IP); + break; + case IPAddressType::Gateway: + res = IPAddress(Settings.Gateway); + break; + case IPAddressType::Subnetmask: + res = IPAddress(Settings.Subnet); + break; + case IPAddressType::DNS: + res = IPAddress(Settings.DNS); + break; + } + ip = res; + + return IPAddressSet(ip); +} + const __FlashStringHelper * NW001_data_struct_WiFi_STA::getWiFi_encryptionType() const { return _WiFiEventHandler.getWiFi_encryptionType(); diff --git a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.h b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.h index 4ea3813f0..f3309b99d 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.h +++ b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.h @@ -41,6 +41,7 @@ struct NW001_data_struct_WiFi_STA : public NWPluginData_base { # endif // if FEATURE_NETWORK_STATS NWPluginData_static_runtime* getNWPluginData_static_runtime() override; + bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const override; const __FlashStringHelper* getWiFi_encryptionType() const; WiFiDisconnectReason getWiFi_disconnectReason() const; diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp index 19f6778db..90e4c3028 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp @@ -3,11 +3,13 @@ #ifdef USES_NW002 # ifdef ESP32 -# include "../../../src/Globals/Settings.h" -#endif +# include "../../../src/Globals/Settings.h" +# endif # include "../wifi/ESPEasyWifi.h" +# include "../../../src/Helpers/Networking.h" + # ifdef ESP32 # include # include @@ -97,13 +99,13 @@ bool NW002_data_struct_WiFi_AP::init(EventStruct *event) # ifdef ESP32 NW002_update_NAPT(); # endif - # if FEATURE_MDNS - # ifdef ESP8266 + # if FEATURE_MDNS + # ifdef ESP8266 // notifyAPChange() is not present in the ESP32 MDNSResponder MDNS.notifyAPChange(); - # endif // ifdef ESP8266 - # endif // if FEATURE_MDNS + # endif // ifdef ESP8266 + # endif // if FEATURE_MDNS return true; } @@ -132,6 +134,32 @@ NWPluginData_static_runtime * NW002_data_struct_WiFi_AP::getNWPluginData_static_ return nullptr; } +bool NW002_data_struct_WiFi_AP::getStaticIPAddress(IPAddressType addressType, IPAddress& ip) const +{ + // TODO TD-er: Implement for AP +/* + IPAddress res; + + switch (addressType) + { + case IPAddressType::IP: res = IPAddress(Settings.IP); + break; + case IPAddressType::Gateway: res = IPAddress(Settings.Gateway); + break; + case IPAddressType::Subnetmask: res = IPAddress(Settings.Subnet); + break; + case IPAddressType::DNS: res = IPAddress(Settings.DNS); + break; + } + + if (IPAddressSet(res)) { + ip = res; + return true; + } +*/ + return false; +} + # ifdef ESP32 bool NW002_data_struct_WiFi_AP::handle_priority_route_changed() { return NW002_update_NAPT(); } diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.h b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.h index fb32afe9d..d7e22f235 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.h +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.h @@ -35,6 +35,8 @@ struct NW002_data_struct_WiFi_AP : public NWPluginData_base { NWPluginData_static_runtime* getNWPluginData_static_runtime() override; + bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const override; + private: # ifdef ESP32 diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp index 91c22fc14..519d94f71 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp @@ -8,6 +8,7 @@ # include "../../../src/Globals/Settings.h" # include "../../../src/Helpers/Hardware_GPIO.h" +# include "../../../src/Helpers/Networking.h" # include "../../../src/Helpers/StringConverter.h" # include "../../../src/WebServer/Markup.h" @@ -55,16 +56,16 @@ const __FlashStringHelper * NW003_data_struct_ETH_RMII::getLabelString(uint32_t case NW003_KEY_ETH_PIN_POWER: return displayString ? F("Ethernet Power pin") : F("pwr"); case NW003_KEY_CLOCK_MODE: return displayString ? F("Ethernet Clock") : F("clock"); case NW003_KEY_IP: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return F("IP"); case NW003_KEY_GW: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return displayString ? F("Gateway") : F("gw"); case NW003_KEY_SN: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return displayString ? F("Subnetmask") : F("sn"); case NW003_KEY_DNS: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return F("DNS"); } return F(""); @@ -144,7 +145,18 @@ void NW003_data_struct_ETH_RMII::loadDefaults(ESPEasy_key_value_store *kvs, kvs->setValue(NW003_KEY_ETH_PIN_POWER, static_cast(Settings.ETH_Pin_power_rst)); kvs->setValue(NW003_KEY_CLOCK_MODE, static_cast(Settings.ETH_Clock_Mode)); - // TODO TD-er: Copy IP info + const IPAddress ip = Settings.ETH_IP; + const IPAddress gw = Settings.ETH_Gateway; + const IPAddress subnet = Settings.ETH_Subnet; + const IPAddress dns = Settings.ETH_DNS; + + // TODO TD-er: Must clear the previous ETH IP settings, once converted + + kvs->setValue(NW003_KEY_IP, ip); + kvs->setValue(NW003_KEY_GW, gw); + kvs->setValue(NW003_KEY_SN, subnet); + kvs->setValue(NW003_KEY_DNS, dns); + store_nwpluginTaskData_KVS(kvs, networkIndex, nwPluginID); } @@ -176,12 +188,18 @@ void NW003_data_struct_ETH_RMII::webform_load(EventStruct *event) NW003_data_struct_ETH_RMII::loadDefaults(_kvs, event->NetworkIndex, nwpluginID_t(3)); addFormSubHeader(F("Ethernet IP Settings")); + { + const int keys[] = { + NW003_KEY_IP, + NW003_KEY_GW, + NW003_KEY_SN, + NW003_KEY_DNS + }; - addFormIPBox(F("ESP Ethernet IP"), F("espethip"), Settings.ETH_IP); - addFormIPBox(F("ESP Ethernet Gateway"), F("espethgateway"), Settings.ETH_Gateway); - addFormIPBox(F("ESP Ethernet Subnetmask"), F("espethsubnet"), Settings.ETH_Subnet); - addFormIPBox(F("ESP Ethernet DNS"), F("espethdns"), Settings.ETH_DNS); - addFormNote(F("Leave empty for DHCP")); + for (uint32_t i = 0; i < NR_ELEMENTS(keys); ++i) { + showWebformItem(*_kvs, NW003_makeWebFormItemParams(keys[i])); + } + } addFormSubHeader(F("Ethernet")); @@ -276,19 +294,21 @@ void NW003_data_struct_ETH_RMII::webform_load(EventStruct *event) // toString(EthClockMode_t::Default), toString(EthClockMode_t::Ext_crystal), toString(EthClockMode_t::Int_50MHz) -// , toString(EthClockMode_t::Ext_crystal_GPIO_32) -// , toString(EthClockMode_t::Int_50MHz_GPIO_32) -// , toString(EthClockMode_t::Ext_crystal_GPIO_44) -// , toString(EthClockMode_t::Int_50MHz_GPIO_44) + + // , toString(EthClockMode_t::Ext_crystal_GPIO_32) + // , toString(EthClockMode_t::Int_50MHz_GPIO_32) + // , toString(EthClockMode_t::Ext_crystal_GPIO_44) + // , toString(EthClockMode_t::Int_50MHz_GPIO_44) }; const int indices[] = { // static_cast(EthClockMode_t::Default), static_cast(EthClockMode_t::Ext_crystal), static_cast(EthClockMode_t::Int_50MHz) -// ,static_cast(EthClockMode_t::Ext_crystal_GPIO_32) -// ,static_cast(EthClockMode_t::Int_50MHz_GPIO_32) -// ,static_cast(EthClockMode_t::Ext_crystal_GPIO_44) -// ,static_cast(EthClockMode_t::Int_50MHz_GPIO_44) + + // ,static_cast(EthClockMode_t::Ext_crystal_GPIO_32) + // ,static_cast(EthClockMode_t::Int_50MHz_GPIO_32) + // ,static_cast(EthClockMode_t::Ext_crystal_GPIO_44) + // ,static_cast(EthClockMode_t::Int_50MHz_GPIO_44) }; # endif // if CONFIG_IDF_TARGET_ESP32P4 const FormSelectorOptions selector( @@ -298,10 +318,10 @@ void NW003_data_struct_ETH_RMII::webform_load(EventStruct *event) auto params = NW003_makeWebFormItemParams(NW003_KEY_CLOCK_MODE); # if CONFIG_IDF_TARGET_ESP32 params._defaultIntValue = static_cast(EthClockMode_t::Ext_crystal_osc); -#endif +# endif # if CONFIG_IDF_TARGET_ESP32P4 params._defaultIntValue = static_cast(EthClockMode_t::Ext_crystal); -#endif +# endif showFormSelector(*_kvs, selector, params); @@ -338,6 +358,36 @@ NWPluginData_static_runtime * NW003_data_struct_ETH_RMII::getNWPluginData_static return ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getNWPluginData_static_runtime(_networkIndex); } +bool NW003_data_struct_ETH_RMII::getStaticIPAddress(IPAddressType addressType, IPAddress& ip) const +{ + ip = IPAddress(); + + if (!_kvs) { return false; } + uint32_t key = NW003_MAX_KEY; + + switch (addressType) + { + case IPAddressType::IP: + key = NW003_KEY_IP; + break; + case IPAddressType::Gateway: + key = NW003_KEY_GW; + break; + case IPAddressType::Subnetmask: + key = NW003_KEY_SN; + break; + case IPAddressType::DNS: + key = NW003_KEY_DNS; + break; + } + + if ((key == NW003_MAX_KEY) || !_kvs->getValue(key, ip)) { + return false; + } + + return IPAddressSet(ip); +} + bool NW003_data_struct_ETH_RMII::write_Eth_HW_Address(KeyValueWriter *writer) { if (writer == nullptr) { return false; } @@ -447,6 +497,7 @@ void NW003_data_struct_ETH_RMII::ethPower(bool enable) if (!enable) { const EthClockMode_t ETH_ClockMode = static_cast(_kvs->getValueAsInt(NW003_KEY_CLOCK_MODE)); + if (isExternalCrystal(ETH_ClockMode)) { delay(600); // Give some time to discharge any capacitors // Delay is needed to make sure no clock signal remains present which may cause the ESP to boot into flash mode. @@ -503,7 +554,7 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (!(data && iface)) { return false; } if (data->started() && data->connected()) { - if (EthLinkUp()) return true; + if (EthLinkUp()) { return true; } data->mark_connect_failed(); return false; } @@ -541,6 +592,12 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { # ifndef ESP32P4 ethResetGPIOpins(); # endif + + IPAddress ip, gateway, sn, dns; + if (getStaticIPAddresses(ip, gateway, sn, dns)) { + iface->config(ip, gateway, sn, dns); + } + success = iface->begin( to_ESP_phy_type(phyType), phy_addr, @@ -548,7 +605,8 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { mdioPin, powerPin, (eth_clock_mode_t)ETH_ClockMode); - // TODO TD-er: When we can set the clock GPIO pin on ESP32-P4, this should also be matched to the Espressif eth_clock_mode_t enum. + + // TODO TD-er: When we can set the clock GPIO pin on ESP32-P4, this should also be matched to the Espressif eth_clock_mode_t enum. } if (success) { diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.h b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.h index b14979381..fbb4d7639 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.h +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.h @@ -39,6 +39,8 @@ struct NW003_data_struct_ETH_RMII : public NWPluginData_base { NWPluginData_static_runtime* getNWPluginData_static_runtime() override; + bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const override; + bool write_Eth_HW_Address(KeyValueWriter *writer); bool write_Eth_port(KeyValueWriter *writer); diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index 26ed19296..31cb1c58f 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -6,6 +6,7 @@ # include "../../../src/Helpers/Hardware_GPIO.h" # include "../../../src/Helpers/Hardware_SPI.h" +# include "../../../src/Helpers/Networking.h" # include "../../../src/Helpers/SPI_Helper.h" # include "../../../src/Helpers/StringConverter.h" @@ -53,16 +54,16 @@ const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t k case NW004_KEY_ETH_PIN_RST: return displayString ? F("Ethernet RST pin") : F("RST"); case NW004_KEY_SPI_BUS: return displayString ? F("SPI Bus") : F("ethspibus"); case NW004_KEY_IP: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return F("IP"); case NW004_KEY_GW: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return displayString ? F("Gateway") : F("gw"); case NW004_KEY_SN: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return displayString ? F("Subnetmask") : F("sn"); case NW004_KEY_DNS: - storageType = KVS_StorageType::Enum::string_type; + storageType = KVS_StorageType::Enum::ip_type; return F("DNS"); } return F(""); @@ -141,7 +142,17 @@ void NW004_data_struct_ETH_SPI::loadDefaults(ESPEasy_key_value_store *kvs, kvs->setValue(NW004_KEY_ETH_PIN_IRQ, static_cast(Settings.ETH_Pin_mdio_irq)); kvs->setValue(NW004_KEY_ETH_PIN_RST, static_cast(Settings.ETH_Pin_power_rst)); - // TODO TD-er: Copy IP info + const IPAddress ip = Settings.ETH_IP; + const IPAddress gw = Settings.ETH_Gateway; + const IPAddress subnet = Settings.ETH_Subnet; + const IPAddress dns = Settings.ETH_DNS; + + // TODO TD-er: Must clear the previous ETH IP settings, once converted + + kvs->setValue(NW004_KEY_IP, ip); + kvs->setValue(NW004_KEY_GW, gw); + kvs->setValue(NW004_KEY_SN, subnet); + kvs->setValue(NW004_KEY_DNS, dns); store_nwpluginTaskData_KVS(kvs, networkIndex, nwPluginID); } @@ -157,8 +168,6 @@ void NW004_data_struct_ETH_SPI::loadDefaults(ESPEasy_key_value_store *kvs, kvs->setValue(NW004_KEY_ETH_PIN_IRQ, static_cast(DEFAULT_ETH_PIN_MDIO)); kvs->setValue(NW004_KEY_ETH_PIN_RST, static_cast(DEFAULT_ETH_PIN_POWER)); - // TODO TD-er: Copy IP info - store_nwpluginTaskData_KVS(kvs, networkIndex, nwPluginID); } # endif // if defined(DEFAULT_ETH_PHY_TYPE) && defined(DEFAULT_ETH_CLOCK_MODE) && defined(DEFAULT_ETH_PHY_ADDR) && @@ -171,11 +180,18 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) _load(); NW004_data_struct_ETH_SPI::loadDefaults(_kvs, event->NetworkIndex, nwpluginID_t(4)); addFormSubHeader(F("Ethernet IP Settings")); + { + const int keys[] = { + NW004_KEY_IP, + NW004_KEY_GW, + NW004_KEY_SN, + NW004_KEY_DNS + }; - addFormIPBox(F("ESP Ethernet IP"), F("espethip"), Settings.ETH_IP); - addFormIPBox(F("ESP Ethernet Gateway"), F("espethgateway"), Settings.ETH_Gateway); - addFormIPBox(F("ESP Ethernet Subnetmask"), F("espethsubnet"), Settings.ETH_Subnet); - addFormIPBox(F("ESP Ethernet DNS"), F("espethdns"), Settings.ETH_DNS); + for (uint32_t i = 0; i < NR_ELEMENTS(keys); ++i) { + showWebformItem(*_kvs, NW004_makeWebFormItemParams(keys[i])); + } + } addFormNote(F("Leave empty for DHCP")); addFormSubHeader(F("Ethernet")); @@ -288,6 +304,37 @@ NWPluginData_static_runtime * NW004_data_struct_ETH_SPI::getNWPluginData_static_ return ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getNWPluginData_static_runtime(_networkIndex); } +bool NW004_data_struct_ETH_SPI::getStaticIPAddress(IPAddressType addressType, IPAddress& ip) const +{ + ip = IPAddress(); + + if (!_kvs) { return false; } + uint32_t key = NW004_MAX_KEY; + + switch (addressType) + { + case IPAddressType::IP: + key = NW004_KEY_IP; + break; + case IPAddressType::Gateway: + key = NW004_KEY_GW; + break; + case IPAddressType::Subnetmask: + key = NW004_KEY_SN; + break; + case IPAddressType::DNS: + key = NW004_KEY_DNS; + break; + } + + if ((key == NW004_MAX_KEY) || !_kvs->getValue(key, ip)) { + return false; + } + + return IPAddressSet(ip); + +} + bool NW004_data_struct_ETH_SPI::write_Eth_HW_Address(KeyValueWriter *writer) { if (writer == nullptr) { return false; } @@ -448,6 +495,12 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { // else if (spi_instance) { + IPAddress ip, gateway, sn, dns; + + if (getStaticIPAddresses(ip, gateway, sn, dns)) { + iface->config(ip, gateway, sn, dns); + } + // TODO TD-er: Do we need to include the CLK, MISO, MOSI pins in the call or do we need to start the SPI bus first? # if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.h b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.h index 8e1c236e1..239954bf3 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.h +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.h @@ -40,6 +40,8 @@ struct NW004_data_struct_ETH_SPI : public NWPluginData_base { NWPluginData_static_runtime* getNWPluginData_static_runtime() override; + bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const override; + bool write_Eth_HW_Address(KeyValueWriter *writer); bool write_Eth_port(KeyValueWriter *writer); diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp index 091dc4757..61cb711b2 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp @@ -926,12 +926,12 @@ bool NW005_data_struct_PPP_modem::init(EventStruct *event) } { String apn; - _kvs->getValue(NW005_KEY_APN, apn); + _kvs->getValue(NW005_KEY_APN, apn, KVS_StorageType::Enum::string_type); NW_PLUGIN_INTERFACE.setApn(apn.c_str()); } { String pin; - _kvs->getValue(NW005_KEY_SIM_PIN, pin); + _kvs->getValue(NW005_KEY_SIM_PIN, pin, KVS_StorageType::Enum::string_type); if (pin.length() >= 4) { NW_PLUGIN_INTERFACE.setPin(pin.c_str()); @@ -1108,6 +1108,12 @@ bool NW005_data_struct_PPP_modem::record_stats() NWPluginData_static_runtime * NW005_data_struct_PPP_modem::getNWPluginData_static_runtime() { return &stats_and_cache; } +bool NW005_data_struct_PPP_modem::getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const +{ + // No static IP for PPP modem + return false; +} + void NW005_data_struct_PPP_modem::onEvent(arduino_event_id_t event, arduino_event_info_t info) { // TODO TD-er: Must store flags from events in static (or global) object to act on it later. switch (event) diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h index adb5a5b6e..a290d248e 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h @@ -80,6 +80,8 @@ static int32_t getNextKey_noCredentials(int32_t key); NWPluginData_static_runtime* getNWPluginData_static_runtime() override; + bool getStaticIPAddress(IPAddressType addressType, IPAddress & ip) const override; + private: static void onEvent(arduino_event_id_t event, diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP32.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP32.cpp index 8c7e46cb5..6b4c30035 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP32.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP32.cpp @@ -13,6 +13,7 @@ # include "../../../src/DataTypes/ESPEasyTimeSource.h" # include "../../../src/ESPEasyCore/ESPEasy_Log.h" +# include "../../../src/Helpers/Networking.h" # include "../../../src/Helpers/StringConverter.h" # include "../../../src/Helpers/StringGenerator_WiFi.h" # include "../DataStructs/NWPluginData_static_runtime.h" @@ -65,7 +66,7 @@ bool ESPEasyWiFi_STA_EventHandler::restore_dns_from_cac for (size_t i = 0; i < NR_ELEMENTS(stats_and_cache._dns_cache); ++i) { auto tmp = WiFi.STA.dnsIP(i); - if ((stats_and_cache._dns_cache[i] != INADDR_NONE) && (stats_and_cache._dns_cache[i] != tmp)) { + if (valid_DNS_address(stats_and_cache._dns_cache[i]) && (stats_and_cache._dns_cache[i] != tmp)) { addLog(LOG_LEVEL_INFO, strformat( F("WiFi STA: Restore cached DNS server %d from %s to %s"), i, diff --git a/src/src/DataStructs/NodesHandler.cpp b/src/src/DataStructs/NodesHandler.cpp index 127641171..0cabad130 100644 --- a/src/src/DataStructs/NodesHandler.cpp +++ b/src/src/DataStructs/NodesHandler.cpp @@ -24,6 +24,7 @@ #include "../Globals/Settings.h" #include "../Helpers/ESPEasy_time_calc.h" #include "../Helpers/Misc.h" +#include "../Helpers/Networking.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_System.h" @@ -503,7 +504,7 @@ void NodesHandler::updateThisNode() { thisNode.distance = _distance; #if FEATURE_USE_IPV6 - thisNode.hasIPv4 = thisNode.IP() != INADDR_NONE; + thisNode.hasIPv4 = IPAddressSet(thisNode.IP()) && thisNode.IP().type() == IPv4; thisNode.hasIPv6_mac_based_link_local = ESPEasy::net::is_IPv6_link_local_from_MAC(thisNode.sta_mac); thisNode.hasIPv6_mac_based_link_global = ESPEasy::net::is_IPv6_global_from_MAC(thisNode.sta_mac); #endif diff --git a/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp b/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp index 21fedf9e0..eb2e1c6a6 100644 --- a/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp +++ b/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp @@ -103,12 +103,12 @@ bool SecurityStruct_deviceSpecific::hasWiFiCredentials(uint8_t index) const bool SecurityStruct_deviceSpecific::getValue( KeyType keytype, uint8_t index, String& value) const { - return _kvs.getValue(createKey(keytype, index), value); + return _kvs.getValue(createKey(keytype, index), value, KVS_StorageType::Enum::string_type); } void SecurityStruct_deviceSpecific::setValue(KeyType keytype, uint8_t index, const String& value) { - _kvs.setValue(createKey(keytype, index), value); + _kvs.setValue(createKey(keytype, index), value, KVS_StorageType::Enum::string_type); } uint32_t SecurityStruct_deviceSpecific::createKey(KeyType keytype, uint16_t index) @@ -123,8 +123,8 @@ bool SecurityStruct_deviceSpecific::getCredentials(KeyType keytype_key, String& secret) const { return - _kvs.getValue(createKey(keytype_key, index), key) && - _kvs.getValue(createKey(keytype_secret, index), secret); + _kvs.getValue(createKey(keytype_key, index), key, KVS_StorageType::Enum::string_type) && + _kvs.getValue(createKey(keytype_secret, index), secret, KVS_StorageType::Enum::string_type); } void SecurityStruct_deviceSpecific::setCredentials(KeyType keytype_key, @@ -133,8 +133,8 @@ void SecurityStruct_deviceSpecific::setCredentials(KeyType keytype_key, const String& key, const String& secret) { - _kvs.setValue(createKey(keytype_key, index), key); - _kvs.setValue(createKey(keytype_secret, index), secret); + _kvs.setValue(createKey(keytype_key, index), key, KVS_StorageType::Enum::string_type); + _kvs.setValue(createKey(keytype_secret, index), secret, KVS_StorageType::Enum::string_type); } #endif // if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE diff --git a/src/src/DataTypes/ESPEasy_key_value_store_StorageType.cpp b/src/src/DataTypes/ESPEasy_key_value_store_StorageType.cpp index f3794be43..ac9ade8ea 100644 --- a/src/src/DataTypes/ESPEasy_key_value_store_StorageType.cpp +++ b/src/src/DataTypes/ESPEasy_key_value_store_StorageType.cpp @@ -29,6 +29,7 @@ size_t KVS_StorageType::getStorageSizePerType(KVS_StorageType::Enum storageType) { case KVS_StorageType::Enum::not_set: break; case KVS_StorageType::Enum::string_type: break; + case KVS_StorageType::Enum::ip_type: break; case KVS_StorageType::Enum::int8_type: return 4 + 1; case KVS_StorageType::Enum::uint8_type: return 4 + 1; case KVS_StorageType::Enum::int16_type: return 4 + 2; diff --git a/src/src/DataTypes/ESPEasy_key_value_store_StorageType.h b/src/src/DataTypes/ESPEasy_key_value_store_StorageType.h index 119e293de..79ff659d2 100644 --- a/src/src/DataTypes/ESPEasy_key_value_store_StorageType.h +++ b/src/src/DataTypes/ESPEasy_key_value_store_StorageType.h @@ -23,6 +23,7 @@ public: float_type = 10, double_type = 11, bool_type = 12, + ip_type = 13, MAX_Type, // Leave this one after the generic type and before 'special' types diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.cpp b/src/src/DataTypes/ESPEasy_plugin_functions.cpp index 58948aa81..55f381d35 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.cpp +++ b/src/src/DataTypes/ESPEasy_plugin_functions.cpp @@ -3,6 +3,7 @@ #ifdef ESP32 # include "../Helpers/StringConverter.h" +# include "../Helpers/Networking.h" # include @@ -154,7 +155,7 @@ bool NWPlugin::get_subnet(NWPlugin::IP_type ip_type, NetworkInterface*networkInt if (!networkInterface) { return false; } IPAddress ip = get_IP_address(ip_type, networkInterface); - if (ip == INADDR_NONE) { return false; } + if (!IPAddressSet(ip)) { return false; } # if CONFIG_LWIP_IPV6 diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index df94d90e1..d84c34269 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -1124,6 +1124,24 @@ void scrubDNS() { */ } +#ifdef ESP32 +bool IPAddressSet(const IPAddress& ip) +{ + return !(ip == INADDR_NONE + # if CONFIG_LWIP_IPV6 + || ip == IN6ADDR_ANY + # endif + ); +} +#endif +#ifdef ESP8266 +bool IPAddressSet(const IPAddress& ip) +{ + return !(ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0); +} +#endif + + bool valid_DNS_address(const IPAddress& dns) { return /*dns.v4() != (uint32_t)0x00000000 && */ dns != IPAddress((uint32_t)0xFD000000) && @@ -1133,7 +1151,7 @@ bool valid_DNS_address(const IPAddress& dns) { // Global IPv6 prefixes currently start with 2xxx:: (dns[0] & 0xF0) != 0x20 && #endif // ifdef ESP32 - dns != INADDR_NONE; + IPAddressSet(dns); } bool setDNS(int index, const IPAddress& dns) { diff --git a/src/src/Helpers/Networking.h b/src/src/Helpers/Networking.h index 007a8db06..2b44f3bae 100644 --- a/src/src/Helpers/Networking.h +++ b/src/src/Helpers/Networking.h @@ -196,6 +196,8 @@ bool connectClient(WiFiClient& client, IPAddress ip, uint16_t port, uint32_t tim void scrubDNS(); +bool IPAddressSet(const IPAddress& ip); + bool valid_DNS_address(const IPAddress& dns); bool setDNS(int index, const IPAddress& dns); diff --git a/src/src/Helpers/_ESPEasy_key_value_store.cpp b/src/src/Helpers/_ESPEasy_key_value_store.cpp index 2ce7638f6..01dfc6a81 100644 --- a/src/src/Helpers/_ESPEasy_key_value_store.cpp +++ b/src/src/Helpers/_ESPEasy_key_value_store.cpp @@ -7,7 +7,7 @@ # include "../Helpers/KeyValueWriter.h" # include "../Helpers/StringConverter.h" # include "../Helpers/StringConverter_Numerical.h" - +# include "../Helpers/Networking.h" bool ESPEasy_key_value_store::isEmpty() const { @@ -138,7 +138,7 @@ bool ESPEasy_key_value_store::load( sizePerType = KVS_StorageType::getStorageSizePerType(storageType); key = KVS_StorageType::getKey_from_combined_key(combined_key.getUint32()); - if ((sizePerType < 4) && (storageType != KVS_StorageType::Enum::string_type)) { + if ((sizePerType < 4) && ((storageType != KVS_StorageType::Enum::string_type) && (storageType != KVS_StorageType::Enum::ip_type))) { // Should not happen as those should not be stored in the file _state = State::ErrorOnLoad; # ifndef BUILD_NO_DEBUG @@ -165,6 +165,7 @@ bool ESPEasy_key_value_store::load( } case KVS_StorageType::Enum::string_type: + case KVS_StorageType::Enum::ip_type: { if (bytesLeftPartialString == 0) { // Found new string type @@ -211,7 +212,7 @@ bool ESPEasy_key_value_store::load( } if ((bytesLeftPartialString == 0) && (partialString.length() > 0)) { - setValue(key, std::move(partialString)); + setValue(key, std::move(partialString), storageType); partialString.clear(); // ++bufPos; // Skip over nul-termination char @@ -427,10 +428,10 @@ bool ESPEasy_key_value_store::store( } else if (it_4byte != _4byte_data.end()) { // Write 4-byte data - uint32_t combined_key = it_4byte->first; + uint32_t combined_key = it_4byte->first; KVS_StorageType::Enum storageType = KVS_StorageType::get_StorageType_from_combined_key(combined_key); - const uint32_t key = KVS_StorageType::getKey_from_combined_key(combined_key); - const size_t sizePerType = KVS_StorageType::getStorageSizePerType(storageType); + const uint32_t key = KVS_StorageType::getKey_from_combined_key(combined_key); + const size_t sizePerType = KVS_StorageType::getStorageSizePerType(storageType); if (sizePerType >= 4) { if ((buffer.size() - bufPos) < sizePerType) { @@ -552,7 +553,7 @@ bool ESPEasy_key_value_store::hasKey(KVS_StorageType::Enum storageType, uint32_t const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key(storageType, key); - if (storageType == KVS_StorageType::Enum::string_type) + if ((storageType == KVS_StorageType::Enum::string_type) || (storageType == KVS_StorageType::Enum::ip_type)) { return _string_data.find(combined_key) != _string_data.end(); } @@ -569,6 +570,46 @@ bool ESPEasy_key_value_store::hasKey(KVS_StorageType::Enum storageType, uint32_t return false; } +bool ESPEasy_key_value_store::clearKey(KVS_StorageType::Enum storageType, uint32_t key) +{ + if (!hasKey(storageType, key)) { return false; } + + const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key(storageType, key); + + if ((storageType == KVS_StorageType::Enum::string_type) || (storageType == KVS_StorageType::Enum::ip_type)) + { + auto it = _string_data.find(combined_key); + + if (it != _string_data.end()) { + _state = State::Changed; + _string_data.erase(it); + updateHasStorageType(storageType); + } + return true; + } + const size_t size = KVS_StorageType::getStorageSizePerType(storageType); + + if (size == 12) + { + auto it = _8byte_data.find(combined_key); + + if (it != _8byte_data.end()) { + _state = State::Changed; + _8byte_data.erase(it); + updateHasStorageType(storageType); + } + } else if (size >= 4) { + auto it = _4byte_data.find(combined_key); + + if (it != _4byte_data.end()) { + _state = State::Changed; + _4byte_data.erase(it); + updateHasStorageType(storageType); + } + } + return true; +} + KVS_StorageType::Enum ESPEasy_key_value_store::getStorageType(uint32_t key) const { uint32_t cached_bitmap = _storage_type_present_cache; @@ -586,21 +627,26 @@ KVS_StorageType::Enum ESPEasy_key_value_store::getStorageType(uint32_t key) cons } return KVS_StorageType::Enum::not_set; } -#define KVS_STRINGPAIR_SEPARATOR ((char)1) + +# define KVS_STRINGPAIR_SEPARATOR ((char)1) + bool ESPEasy_key_value_store::getValue(uint32_t key, StringPair& stringPair) const { String str; - if (!getValue(key, str)) { + + if (!getValue(key, str, KVS_StorageType::Enum::string_type)) { return false; } const int separatorPos = str.indexOf(KVS_STRINGPAIR_SEPARATOR); - if (separatorPos < 0) return false; - stringPair.first = str.substring(0, separatorPos); + + if (separatorPos < 0) { return false; } + stringPair.first = str.substring(0, separatorPos); stringPair.second = str.substring(separatorPos + 1); return true; } -void ESPEasy_key_value_store::setValue(uint32_t key, const StringPair& stringPair) +void ESPEasy_key_value_store::setValue( + uint32_t key, const StringPair& stringPair) { String str; str.reserve(stringPair.first.length() + 1 + stringPair.second.length()); @@ -609,12 +655,14 @@ void ESPEasy_key_value_store::setValue(uint32_t key, const StringPair& stringPai str += stringPair.second; setValue(key, str); } -#undef KVS_STRINGPAIR_SEPARATOR -bool ESPEasy_key_value_store::getValue(uint32_t key, String& value) const +# undef KVS_STRINGPAIR_SEPARATOR + +bool ESPEasy_key_value_store::getValue( + uint32_t key, String& value, KVS_StorageType::Enum storageType) const { const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( - KVS_StorageType::Enum::string_type, + storageType, key); auto it = _string_data.find(combined_key); @@ -624,15 +672,20 @@ bool ESPEasy_key_value_store::getValue(uint32_t key, String& value) const return true; } -void ESPEasy_key_value_store::setValue(uint32_t key, const String& value) { setValue(key, String(value)); } +void ESPEasy_key_value_store::setValue( + uint32_t key, const String& value, + KVS_StorageType::Enum storageType) { setValue(key, String(value), storageType); } -void ESPEasy_key_value_store::setValue(uint32_t key, - const __FlashStringHelper*value) { setValue(key, String(value)); } +void ESPEasy_key_value_store::setValue( + uint32_t key, + const __FlashStringHelper*value, + KVS_StorageType::Enum storageType) { setValue(key, String(value), storageType); } -void ESPEasy_key_value_store::setValue(uint32_t key, String&& value) +void ESPEasy_key_value_store::setValue( + uint32_t key, String&& value, KVS_StorageType::Enum storageType) { const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( - KVS_StorageType::Enum::string_type, + storageType, key); auto it = _string_data.find(combined_key); @@ -646,12 +699,26 @@ void ESPEasy_key_value_store::setValue(uint32_t key, String&& value) _state = State::Changed; } } - setHasStorageType(KVS_StorageType::Enum::string_type); + setHasStorageType(storageType); +} + +bool ESPEasy_key_value_store::getValue(uint32_t key, IPAddress& value) const +{ + String ip_str; + + if (!getValue(key, ip_str, KVS_StorageType::Enum::ip_type)) { return false; } + return value.fromString(ip_str); +} + +void ESPEasy_key_value_store::setValue(uint32_t key, const IPAddress& value) +{ + if (IPAddressSet(value)) { setValue(key, value.toString(), KVS_StorageType::Enum::ip_type); } + else { clearKey(KVS_StorageType::Enum::ip_type, key); } } ESPEasy_key_value_store::map_4byte_data::const_iterator ESPEasy_key_value_store::get4byteIterator( KVS_StorageType::Enum storageType, - uint32_t key) const + uint32_t key) const { if (!hasStorageType(storageType)) { return _4byte_data.end(); } const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key(storageType, key); @@ -660,37 +727,37 @@ ESPEasy_key_value_store::map_4byte_data::const_iterator ESPEasy_key_value_store: ESPEasy_key_value_store::map_8byte_data::const_iterator ESPEasy_key_value_store::get8byteIterator( KVS_StorageType::Enum storageType, - uint32_t key) const + uint32_t key) const { if (!hasStorageType(storageType)) { return _8byte_data.end(); } const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key(storageType, key); return _8byte_data.find(combined_key); } -# define GET_4BYTE_TYPE(T, GF) \ +# define GET_4BYTE_TYPE(T, GF) \ auto it = get4byteIterator(KVS_StorageType::Enum::T, key); \ - if (it == _4byte_data.end()) return false; \ - value = it->second.GF(); \ - return true; \ + if (it == _4byte_data.end()) return false; \ + value = it->second.GF(); \ + return true; \ -# define GET_8BYTE_TYPE(T, GF) \ +# define GET_8BYTE_TYPE(T, GF) \ auto it = get8byteIterator(KVS_StorageType::Enum::T, key); \ - if (it == _8byte_data.end()) return false; \ - value = it->second.GF(); \ - return true; \ + if (it == _8byte_data.end()) return false; \ + value = it->second.GF(); \ + return true; \ -# define SET_4BYTE_TYPE(T, SF) \ - const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( \ - KVS_StorageType::Enum::T, key); \ - if (_4byte_data[combined_key].SF(value)) _state = State::Changed; \ +# define SET_4BYTE_TYPE(T, SF) \ + const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( \ + KVS_StorageType::Enum::T, key); \ + if (_4byte_data[combined_key].SF(value)) _state = State::Changed; \ setHasStorageType(KVS_StorageType::Enum::T); -# define SET_8BYTE_TYPE(T, SF) \ - const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( \ - KVS_StorageType::Enum::T, key); \ - if (_8byte_data[combined_key].SF(value)) _state = State::Changed; \ +# define SET_8BYTE_TYPE(T, SF) \ + const uint32_t combined_key = KVS_StorageType::combine_StorageType_and_key( \ + KVS_StorageType::Enum::T, key); \ + if (_8byte_data[combined_key].SF(value)) _state = State::Changed; \ setHasStorageType(KVS_StorageType::Enum::T); bool ESPEasy_key_value_store::getValue(uint32_t key, int8_t& value) const @@ -788,7 +855,8 @@ bool ESPEasy_key_value_store::getValueAsString(const KVS_StorageType::Enum& stor switch (storageType) { case KVS_StorageType::Enum::string_type: - return getValue(key, value); + case KVS_StorageType::Enum::ip_type: + return getValue(key, value, storageType); case KVS_StorageType::Enum::bool_type: case KVS_StorageType::Enum::int8_type: @@ -856,15 +924,15 @@ bool ESPEasy_key_value_store::getValueAsString(uint32_t key, String& value) cons return getValueAsString(getStorageType(key), key, value); } -# define GET_TYPE_AS_INT64(T, CT) \ +# define GET_TYPE_AS_INT64(T, CT) \ case KVS_StorageType::Enum::T: \ - { \ - CT v{}; \ - if (getValue(key, v)) { \ - value = v; \ - return true; \ - } \ - break; \ + { \ + CT v{}; \ + if (getValue(key, v)) { \ + value = v; \ + return true; \ + } \ + break; \ } bool ESPEasy_key_value_store::getValueAsInt( @@ -920,12 +988,12 @@ int64_t ESPEasy_key_value_store::getValueAsInt(uint32_t key) const return getValueAsInt_or_default(key, 0); } -# define GET_4BYTE_INT_TYPE_FROM_STRING(T, CT) \ - case KVS_StorageType::Enum::T: \ - { \ - CT v(value.toInt()); \ - setValue(key, v); \ - break; \ +# define GET_4BYTE_INT_TYPE_FROM_STRING(T, CT) \ + case KVS_StorageType::Enum::T: \ + { \ + CT v(value.toInt()); \ + setValue(key, v); \ + break; \ } void ESPEasy_key_value_store::setValue(const KVS_StorageType::Enum& storageType, uint32_t key, const String& value) @@ -933,8 +1001,15 @@ void ESPEasy_key_value_store::setValue(const KVS_StorageType::Enum& storageType, switch (storageType) { case KVS_StorageType::Enum::string_type: - setValue(key, value); + setValue(key, value, storageType); break; + case KVS_StorageType::Enum::ip_type: + { + IPAddress ip; + ip.fromString(value); + setValue(key, ip); + break; + } GET_4BYTE_INT_TYPE_FROM_STRING(int8_type, int8_t) GET_4BYTE_INT_TYPE_FROM_STRING(uint8_type, uint8_t) GET_4BYTE_INT_TYPE_FROM_STRING(int16_type, int16_t) @@ -972,7 +1047,7 @@ void ESPEasy_key_value_store::setValue(const KVS_StorageType::Enum& storageType, } bool ESPEasy_key_value_store::getValue( - KVS_StorageType::Enum & storageType, + KVS_StorageType::Enum & storageType, uint32_t key, ESPEasy_key_value_store_4byte_data_t& value) const { @@ -984,7 +1059,7 @@ bool ESPEasy_key_value_store::getValue( } void ESPEasy_key_value_store::setValue( - KVS_StorageType::Enum & storageType, + KVS_StorageType::Enum & storageType, uint32_t key, const ESPEasy_key_value_store_4byte_data_t& value) { @@ -1002,7 +1077,7 @@ void ESPEasy_key_value_store::setValue( } bool ESPEasy_key_value_store::getValue( - KVS_StorageType::Enum & storageType, + KVS_StorageType::Enum & storageType, uint32_t key, ESPEasy_key_value_store_8byte_data_t& value) const { @@ -1014,7 +1089,7 @@ bool ESPEasy_key_value_store::getValue( } void ESPEasy_key_value_store::setValue( - KVS_StorageType::Enum & storageType, + KVS_StorageType::Enum & storageType, uint32_t key, const ESPEasy_key_value_store_8byte_data_t& value) { @@ -1065,6 +1140,59 @@ void ESPEasy_key_value_store::setHasStorageType(KVS_StorageType::Enum storageTyp // TODO TD-er: Whenever this is called, there has been a change, so invalidate checksum } +void ESPEasy_key_value_store::updateHasStorageType(KVS_StorageType::Enum storageType) +{ + if ((storageType == KVS_StorageType::Enum::bool_true) || + (storageType == KVS_StorageType::Enum::bool_false)) { + storageType = KVS_StorageType::Enum::bool_type; + } + const uint32_t bitnr = static_cast(storageType); + constexpr uint32_t max_bitnr = static_cast(KVS_StorageType::Enum::MAX_Type); + + if (bitnr >= max_bitnr) { return; } + + const uint32_t old_cache = _storage_type_present_cache; + bitClear(_storage_type_present_cache, bitnr); + + if ((storageType == KVS_StorageType::Enum::string_type) || (storageType == KVS_StorageType::Enum::ip_type)) + { + bool done = false; + + for (auto it = _string_data.begin(); !done && it != _string_data.end(); ++it) { + if (storageType == KVS_StorageType::get_StorageType_from_combined_key(it->first)) { + bitSet(_storage_type_present_cache, bitnr); + done = true; + } + } + } else { + const size_t size = KVS_StorageType::getStorageSizePerType(storageType); + + if (size == 12) { + bool done = false; + + for (auto it = _8byte_data.begin(); !done && it != _8byte_data.end(); ++it) { + if (storageType == KVS_StorageType::get_StorageType_from_combined_key(it->first)) { + bitSet(_storage_type_present_cache, bitnr); + done = true; + } + } + } else { + bool done = false; + + for (auto it = _4byte_data.begin(); !done && it != _4byte_data.end(); ++it) { + if (storageType == KVS_StorageType::get_StorageType_from_combined_key(it->first)) { + bitSet(_storage_type_present_cache, bitnr); + done = true; + } + } + } + } + + if (old_cache != _storage_type_present_cache) { + _state = State::Changed; + } +} + void ESPEasy_key_value_store::dump() const { addLog(LOG_LEVEL_INFO, strformat(F("KVS: Payload Storage size : %d"), getPayloadStorageSize())); @@ -1144,5 +1272,4 @@ void ESPEasy_key_value_store::dump() const } - #endif // if FEATURE_ESPEASY_KEY_VALUE_STORE diff --git a/src/src/Helpers/_ESPEasy_key_value_store.h b/src/src/Helpers/_ESPEasy_key_value_store.h index 5b4bec57e..dca61ce1f 100644 --- a/src/src/Helpers/_ESPEasy_key_value_store.h +++ b/src/src/Helpers/_ESPEasy_key_value_store.h @@ -43,17 +43,17 @@ public: }; - State getState() const { return _state; } + State getState() const { return _state; } - bool isEmpty() const; + bool isEmpty() const; - void clear(); + void clear(); - bool load(SettingsType::Enum settingsType, - int index, - uint32_t offset_in_block, - uint16_t id_to_match); + bool load(SettingsType::Enum settingsType, + int index, + uint32_t offset_in_block, + uint16_t id_to_match); bool store(SettingsType::Enum settingsType, int index, uint32_t offset_in_block, @@ -64,7 +64,10 @@ public: // Check to see if there is a key stored with given storage type bool hasKey(KVS_StorageType::Enum storageType, - uint32_t key) const; + uint32_t key) const; + + bool clearKey(KVS_StorageType::Enum storageType, + uint32_t key); // Try to find a key and get its storage type or 'not_set' if the key is not present KVS_StorageType::Enum getStorageType(uint32_t key) const; @@ -81,14 +84,24 @@ public: void setValue(uint32_t key, const StringPair& stringPair); - bool getValue(uint32_t key, - String & value) const; - void setValue(uint32_t key, - const String& value); + bool getValue(uint32_t key, + String & value, + KVS_StorageType::Enum storageType) const; + void setValue(uint32_t key, + const String & value, + KVS_StorageType::Enum storageType); void setValue(uint32_t key, - const __FlashStringHelper*value); - void setValue(uint32_t key, - String&& value); + const __FlashStringHelper*value, + KVS_StorageType::Enum storageType); + void setValue(uint32_t key, + String && value, + KVS_StorageType::Enum storageType); + + bool getValue(uint32_t key, + IPAddress& value) const; + void setValue(uint32_t key, + const IPAddress& value); + bool getValue(uint32_t key, int8_t & value) const; @@ -149,8 +162,8 @@ public: // Generic get function for any given storageType/key and represent its value as a string. // Return false when storageType/key is not present. bool getValueAsString(const KVS_StorageType::Enum& storageType, - uint32_t key, - String & value) const; + uint32_t key, + String & value) const; bool getValueAsString(uint32_t key, String & value) const; @@ -166,8 +179,8 @@ public: // Given value is a string representation of that storage type. // TODO TD-er: Implement void setValue(const KVS_StorageType::Enum& storageType, - uint32_t key, - const String & value); + uint32_t key, + const String & value); String getLastError() const { return _lastError; } @@ -175,19 +188,19 @@ public: private: - bool getValue(KVS_StorageType::Enum & storageType, + bool getValue(KVS_StorageType::Enum & storageType, uint32_t key, ESPEasy_key_value_store_4byte_data_t& value) const; - void setValue(KVS_StorageType::Enum & storageType, + void setValue(KVS_StorageType::Enum & storageType, uint32_t key, const ESPEasy_key_value_store_4byte_data_t& value); - bool getValue(KVS_StorageType::Enum & storageType, + bool getValue(KVS_StorageType::Enum & storageType, uint32_t key, ESPEasy_key_value_store_8byte_data_t& value) const; - void setValue(KVS_StorageType::Enum & storageType, + void setValue(KVS_StorageType::Enum & storageType, uint32_t key, const ESPEasy_key_value_store_8byte_data_t& value); @@ -198,15 +211,17 @@ private: // Update cache to indicate we have at least one of the given storage type void setHasStorageType(KVS_StorageType::Enum storageType); + void updateHasStorageType(KVS_StorageType::Enum storageType); + typedef std::map map_4byte_data; typedef std::map map_8byte_data; map_4byte_data::const_iterator get4byteIterator( KVS_StorageType::Enum storageType, - uint32_t key) const; + uint32_t key) const; map_8byte_data::const_iterator get8byteIterator( KVS_StorageType::Enum storageType, - uint32_t key) const; + uint32_t key) const; String _lastError; diff --git a/src/src/WebServer/ESPEasy_key_value_store_webform.cpp b/src/src/WebServer/ESPEasy_key_value_store_webform.cpp index 459eac70a..dbe18e245 100644 --- a/src/src/WebServer/ESPEasy_key_value_store_webform.cpp +++ b/src/src/WebServer/ESPEasy_key_value_store_webform.cpp @@ -6,21 +6,21 @@ # include "../WebServer/Markup_Forms.h" WebFormItemParams::WebFormItemParams( - const String & label, - const String & id, + const String & label, + const String & id, KVS_StorageType::Enum storageType, - uint32_t key) + uint32_t key) : _label(label), _id(id), _storageType(storageType), _key(key) {} WebFormItemParams::WebFormItemParams( - const __FlashStringHelper *label, - const __FlashStringHelper *id, - KVS_StorageType::Enum storageType, - uint32_t key) + const __FlashStringHelper *label, + const __FlashStringHelper *id, + KVS_StorageType::Enum storageType, + uint32_t key) : _label(label), _id(id), _storageType(storageType), _key(key) {} # define CORRECT_RANGE(T, CT) \ - case KVS_StorageType::Enum::T: \ + case KVS_StorageType::Enum::T: \ if (_max > std::numeric_limits::max()) _max = std::numeric_limits::max(); \ if (_min < std::numeric_limits::min()) _min = std::numeric_limits::min(); \ break; @@ -29,15 +29,15 @@ void WebFormItemParams::checkRanges() { switch (_storageType) { - CORRECT_RANGE(int8_type, int8_t) - CORRECT_RANGE(uint8_type, uint8_t) - CORRECT_RANGE(int16_type, int16_t) - CORRECT_RANGE(uint16_type, uint16_t) - CORRECT_RANGE(int32_type, int32_t) - CORRECT_RANGE(uint32_type, uint32_t) - CORRECT_RANGE(float_type, float) - CORRECT_RANGE(uint64_type, uint64_t) - CORRECT_RANGE(int64_type, int64_t) + CORRECT_RANGE(int8_type, int8_t) + CORRECT_RANGE(uint8_type, uint8_t) + CORRECT_RANGE(int16_type, int16_t) + CORRECT_RANGE(uint16_type, uint16_t) + CORRECT_RANGE(int32_type, int32_t) + CORRECT_RANGE(uint32_type, uint32_t) + CORRECT_RANGE(float_type, float) + CORRECT_RANGE(uint64_type, uint64_t) + CORRECT_RANGE(int64_type, int64_t) // CORRECT_RANGE(double_type, double) default: break; @@ -52,6 +52,21 @@ bool showWebformItem(const ESPEasy_key_value_store& store, switch (params._storageType) { + case KVS_StorageType::Enum::ip_type: + { + IPAddress value; + + if (!store.getValue(params._key, value) && !params._defaultStringValue.isEmpty()) + { + value.fromString(params._defaultStringValue); + } + addFormIPBox( + params._label, + id, + value); + return true; + } + case KVS_StorageType::Enum::string_type: { String value; @@ -175,15 +190,15 @@ void showFormSelector(const ESPEasy_key_value_store& store, selector.addFormSelector(params._label, params._id, value); } -void storeWebformItem(ESPEasy_key_value_store & store, - uint32_t key, - KVS_StorageType::Enum storageType, - const __FlashStringHelper *id) { storeWebformItem(store, key, storageType, String(id)); } +void storeWebformItem(ESPEasy_key_value_store & store, + uint32_t key, + KVS_StorageType::Enum storageType, + const __FlashStringHelper *id) { storeWebformItem(store, key, storageType, String(id)); } -void storeWebformItem(ESPEasy_key_value_store & store, - uint32_t key, - KVS_StorageType::Enum storageType, - const String & id) +void storeWebformItem(ESPEasy_key_value_store& store, + uint32_t key, + KVS_StorageType::Enum storageType, + const String & id) { String _id = id.isEmpty() ? concat(F("KVS_ID_"), key) : id; @@ -193,7 +208,13 @@ void storeWebformItem(ESPEasy_key_value_store & store, } if (hasArg(_id)) { - store.setValue(storageType, key, webArg(_id)); + if (storageType == KVS_StorageType::Enum::ip_type) { + IPAddress ip; + ip.fromString(webArg(_id)); + store.setValue(key, ip); + } else { + store.setValue(storageType, key, webArg(_id)); + } } } diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 524085b3d..14a1c5581 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -380,35 +380,40 @@ bool getFormPassword(const String& id, String& password) return !equals(password, F(MARKUP_FORMS_PASSWORD_MASK_ASTERISKS)); } -// ******************************************************************************** -// Add a IP Box form -// ******************************************************************************** -void addFormIPBox(const __FlashStringHelper *label, - const __FlashStringHelper *id, - const uint8_t ip[4]) -{ - addFormIPBox(String(label), String(id), ip); -} - void addFormTextBox(const String& label, const String& id, const String& value) { addRowLabel_tr_id(label, id); addHtml(strformat( - F(""), - id.c_str(), - id.c_str(), - value.c_str() - )); + F(""), + id.c_str(), + id.c_str(), + value.c_str() + )); } -void addFormIPBox(const String& label, const String& id, const uint8_t ip[4]) -{ - const bool empty_IP = (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0); +// ******************************************************************************** +// Add a IP Box form +// ******************************************************************************** +void addFormIPBox(const __FlashStringHelper *label, + const __FlashStringHelper *id, + const uint8_t ip[4]) { addFormIPBox(String(label), String(id), IPAddress(ip)); } +void addFormIPBox(const String& label, const String& id, const uint8_t ip[4]) { addFormIPBox(label, id, IPAddress(ip)); } + +void addFormIPBox(const __FlashStringHelper *label, + const __FlashStringHelper *id, + const IPAddress & ip) { addFormIPBox(String(label), String(id), ip); } + +void addFormIPBox(const String & label, + const String & id, + const IPAddress& ip) +{ + const bool empty_IP = !IPAddressSet(ip); addFormTextBox(label, id, (empty_IP) ? EMPTY_STRING : formatIP(ip)); } + // ******************************************************************************** // Add a MAC Box form // ******************************************************************************** diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 72e84f22d..85d5a2e0c 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -233,6 +233,14 @@ void addFormIPBox(const String& label, const String& id, const uint8_t ip[4]); +void addFormIPBox(const __FlashStringHelper *label, + const __FlashStringHelper *id, + const IPAddress& ip); + +void addFormIPBox(const String& label, + const String& id, + const IPAddress& ip); + // ******************************************************************************** // Add a MAC address Box form // ******************************************************************************** From c9648663bdc7ab43b4b703bc7f1eec99d92451a8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Apr 2026 15:07:57 +0200 Subject: [PATCH 02/20] [Build] Fix missing include on minimal_core_312_ESP8266_1M_OTA_Domoticz --- src/src/Commands/System.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/src/Commands/System.cpp b/src/src/Commands/System.cpp index 1e2084211..2ed3a9af8 100644 --- a/src/src/Commands/System.cpp +++ b/src/src/Commands/System.cpp @@ -6,6 +6,7 @@ #include "../Globals/Settings.h" #include "../Helpers/DeepSleep.h" +#include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Misc.h" const __FlashStringHelper * Command_System_NoSleep(struct EventStruct *event, const char* Line) From e6fe18c19afe2dcc4e566584ac82ff880caa2d0e Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Apr 2026 15:44:00 +0200 Subject: [PATCH 03/20] [Build] Fix missing include for collection_H_ESP32_IRExt_4M316k build --- src/src/WebServer/Markup_Forms.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 14a1c5581..f3a415e30 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -9,6 +9,7 @@ #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_device_info.h" +#include "../Helpers/Networking.h" #include "../Helpers/Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" From 5b4f4b176cec43ce165f06705891f4ef5918e638 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Apr 2026 11:33:17 +0200 Subject: [PATCH 04/20] [WiFi] Fix slowdown issues using Eth on ESP32-P4 with WiFi off --- src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp | 8 +++++++- src/src/Helpers/StringProvider.cpp | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp index 38af13605..45c51c750 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp @@ -47,7 +47,13 @@ bool WiFi_pre_STA_setup() void doWiFiDisconnect() { uint8_t retry = 3; - while (!WiFi.disconnect(Settings.WiFiRestart_connection_lost()) && retry) { + #ifdef BOARD_HAS_SDIO_ESP_HOSTED + bool wifioff = true; + #else + bool wifioff = Settings.WiFiRestart_connection_lost(); + #endif + + while (!WiFi.disconnect(wifioff) && retry) { --retry; delay(100); } diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index a934ef7e8..e7df36d7f 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -407,7 +407,7 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } case LabelType::ENABLE_SERIAL_PORT_CONSOLE: { - return KeyValueStruct(F("Enable Serial Port Console"), !!Settings.UseSerial); + return KeyValueStruct(F("Enable Serial Port Console"), !!Settings.UseSerial); // Cast to bool to make sure it is shown as a checkmark } case LabelType::CONSOLE_SERIAL_PORT: { @@ -416,7 +416,7 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) #if USES_ESPEASY_CONSOLE_FALLBACK_PORT case LabelType::CONSOLE_FALLBACK_TO_SERIAL0: { - return KeyValueStruct(F("Fallback to Serial 0"), Settings.console_serial0_fallback); + return KeyValueStruct(F("Fallback to Serial 0"), !!Settings.console_serial0_fallback); // Cast to bool to make sure it is shown as a checkmark } case LabelType::CONSOLE_FALLBACK_PORT: { @@ -752,7 +752,10 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } case LabelType::RESTART_WIFI_LOST_CONN: { + #ifndef BOARD_HAS_SDIO_ESP_HOSTED + // Disable option for ESP-hosted WiFi as this always needs to restart when forcing disconnect. return KeyValueStruct(F("Restart WiFi Lost Conn"), Settings.WiFiRestart_connection_lost()); + #endif } case LabelType::FORCE_WIFI_NOSLEEP: { From d00b793bf672c34cd9eaa3fe6b7bc92c48fcbd64 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Apr 2026 15:34:23 +0200 Subject: [PATCH 05/20] [WiFi] Make Captive Portal more intuitive and only act when connected to AP --- src/ESPEasy/net/_NW001_WiFi_STA.cpp | 2 +- .../net/wifi/ESPEasyWiFi_state_machine.cpp | 9 +++ .../net/wifi/ESPEasyWiFi_state_machine.h | 2 + src/ESPEasy/net/wifi/ESPEasyWifi.cpp | 4 ++ src/ESPEasy/net/wifi/ESPEasyWifi.h | 1 + src/src/WebServer/404.cpp | 3 +- src/src/WebServer/ESPEasy_WebServer.cpp | 69 +++++++++++++++++-- src/src/WebServer/ESPEasy_WebServer.h | 8 +++ src/src/WebServer/RootPage.cpp | 4 +- 9 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/ESPEasy/net/_NW001_WiFi_STA.cpp b/src/ESPEasy/net/_NW001_WiFi_STA.cpp index 8e8f7cdd1..3ff2a0f82 100644 --- a/src/ESPEasy/net/_NW001_WiFi_STA.cpp +++ b/src/ESPEasy/net/_NW001_WiFi_STA.cpp @@ -488,7 +488,7 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin addFormNumericBox(LabelType::WIFI_NR_RECONNECT_ATTEMPTS, 0, 255); { - LabelType::Enum labels[]{ + LabelType::Enum labels[]{ LabelType::RESTART_WIFI_LOST_CONN , LabelType::WIFI_USE_LAST_CONN_FROM_RTC # ifndef ESP32 diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index 399e7ca3b..4c73ef129 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -655,6 +655,15 @@ bool ESPEasyWiFi_t::shouldStartAP_fallback() const (_connect_attempt > Settings.ConnectFailRetryCount); } +bool ESPEasyWiFi_t::shouldRedirectTo_setup() const +{ + if (Settings.StartAPfallback_NoCredentials() && !SecuritySettings.hasWiFiCredentials()) { + return true; + } + + return !Settings.DoNotStartAPfallback_ConnectFail(); +} + } // namespace wifi } // namespace net } // namespace ESPEasy diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h index 8bdaedd5a..6e72577e3 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h @@ -100,6 +100,8 @@ public: bool shouldStartAP_fallback() const; + bool shouldRedirectTo_setup() const; + private: // WiFi_AP_Candidate _active_sta; diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index 3b4c33b25..522e69e2a 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -168,6 +168,10 @@ bool shouldStartAP_fallback() { return ESPEasyWiFi.shouldStartAP_fallback(); } +bool shouldRedirectTo_setup() { + return ESPEasyWiFi.shouldRedirectTo_setup(); +} + # ifdef BOARD_HAS_SDIO_ESP_HOSTED // ******************************************************************************** diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index da9ad64fc..246a95a67 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -52,6 +52,7 @@ void exitWiFi(); void loopWiFi(); bool shouldStartAP_fallback(); +bool shouldRedirectTo_setup(); # ifdef BOARD_HAS_SDIO_ESP_HOSTED diff --git a/src/src/WebServer/404.cpp b/src/src/WebServer/404.cpp index 96195bd5a..794fe30c3 100644 --- a/src/src/WebServer/404.cpp +++ b/src/src/WebServer/404.cpp @@ -27,7 +27,7 @@ void handleNotFound() { if (captivePortal()) { // If captive portal redirect instead of displaying the error page. return; } - +/* // if Wifi setup, launch setup wizard if AP_DONT_FORCE_SETUP is not set. if (//WiFiEventData.wifiSetup && Settings.ApCaptivePortal()) @@ -35,6 +35,7 @@ void handleNotFound() { web_server.send_P(200, (PGM_P)F("text/html"), (PGM_P)F("")); return; } +*/ #ifdef WEBSERVER_RULES diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 35224d804..b12f60033 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -169,14 +169,16 @@ void sendHeadandTail_stdtemplate(bool Tail, bool rebooting) { } bool captivePortal() { - if (!Settings.ApCaptivePortal()) return false; - const IPAddress client_localIP = web_server.client().localIP(); - const bool fromAP = client_localIP == apIP; - const bool hasWiFiCredentials = SecuritySettings.hasWiFiCredentials(); + // We only need to check if a client connected here via AP + // as currently we don't have any interface which allows forwarding + // packets and thus acting as a gateway for others. + if (!Settings.ApCaptivePortal() || !clientConnectedToAP()) return false; + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, concat(F("CaptivePortal: hostHeader: "), web_server.hostHeader())); #endif - if (hasWiFiCredentials || !fromAP) { + if (!ESPEasy::net::NetworkConnected()) + { return false; } @@ -185,10 +187,11 @@ bool captivePortal() { && !getValue(LabelType::M_DNS).equalsIgnoreCase(web_server.hostHeader()) #endif ) { + const IPAddress client_localIP = web_server.client().localIP(); String redirectURL = concat(F("http://"), formatIP(client_localIP)); #ifdef WEBSERVER_SETUP - if (fromAP && !hasWiFiCredentials) { + if (ESPEasy::net::wifi::shouldRedirectTo_setup()) { redirectURL += F("/setup"); } #endif // ifdef WEBSERVER_SETUP @@ -201,6 +204,60 @@ bool captivePortal() { return false; } +bool clientConnectedToAP() +{ + const IPAddress client_localIP = web_server.client().localIP(); + return IPAddressSet(client_localIP) && client_localIP == apIP; +} + +ESPEasy::net::networkIndex_t getNetworkIndex_ClientConnectsTo() +{ + const IPAddress client_localIP = web_server.client().localIP(); + if (!IPAddressSet(client_localIP)) + return ESPEasy::net::INVALID_NETWORK_INDEX; + if (client_localIP == apIP) { + // Easy to check as this is a global variable + return NETWORK_INDEX_WIFI_AP; + } + #ifdef ESP8266 + if (client_localIP == WiFi.IP()) { + return NETWORK_INDEX_WIFI_STA; + } + #endif + #ifdef ESP32 + for (ESPEasy::net::networkIndex_t x = 0; x < NETWORK_MAX; ++x) { + if (Settings.getNetworkEnabled(x)) { + struct EventStruct TempEvent; + TempEvent.NetworkIndex = x; + String str; + + if (ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_GET_INTERFACE, &TempEvent, str)) + { + const NWPlugin::IP_type ip_types[] = { + NWPlugin::IP_type::inet, + # if CONFIG_LWIP_IPV6 + NWPlugin::IP_type::ipv6_unknown, + NWPlugin::IP_type::ipv6_global, + NWPlugin::IP_type::ipv6_link_local, + NWPlugin::IP_type::ipv6_site_local, + NWPlugin::IP_type::ipv6_unique_local, + NWPlugin::IP_type::ipv4_mapped_ipv6, + # endif // if CONFIG_LWIP_IPV6 + + }; + + for (size_t i = 0; i < NR_ELEMENTS(ip_types); ++i) { + const IPAddress ip(NWPlugin::get_IP_address(ip_types[i], TempEvent.networkInterface)); + if (client_localIP == ip) return x; + } + } + } + } + + #endif + return ESPEasy::net::INVALID_NETWORK_INDEX; +} + // ******************************************************************************** // Web Interface init // ******************************************************************************** diff --git a/src/src/WebServer/ESPEasy_WebServer.h b/src/src/WebServer/ESPEasy_WebServer.h index 3db772df2..35a83016a 100644 --- a/src/src/WebServer/ESPEasy_WebServer.h +++ b/src/src/WebServer/ESPEasy_WebServer.h @@ -5,6 +5,7 @@ #include "../DataTypes/SettingsType.h" #include "../WebServer/WebTemplateParser.h" +#include "../../ESPEasy/net/DataTypes/NetworkIndex.h" // Uncrustify must not be used on macros, so turn it off. @@ -33,6 +34,13 @@ void WebServerInit(); // ******************************************************************************** bool captivePortal(); +bool clientConnectedToAP(); + +// Determine which network interface is being used by the client to access this ESPEasy node +ESPEasy::net::networkIndex_t getNetworkIndex_ClientConnectsTo(); + + + void setWebserverRunning(bool state); void getWebPageTemplateDefault(const String& tmplName, diff --git a/src/src/WebServer/RootPage.cpp b/src/src/WebServer/RootPage.cpp index f74818c77..d51308435 100644 --- a/src/src/WebServer/RootPage.cpp +++ b/src/src/WebServer/RootPage.cpp @@ -64,7 +64,7 @@ void handle_root() { if (captivePortal()) { // If captive portal redirect instead of displaying the page. return; } - +/* // if Wifi setup, launch setup wizard if AP_FORCE_SETUP is set. if (!ESPEasy::net::NetworkConnected() && Settings.ApCaptivePortal()) @@ -72,7 +72,7 @@ void handle_root() { web_server.send_P(200, (PGM_P)F("text/html"), (PGM_P)F("")); return; } - +*/ if (!MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN) { if (!isLoggedIn()) { return; } } From 91d952ab496a58dcbffbda9728eeaf770dcb0277 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Apr 2026 01:18:31 +0200 Subject: [PATCH 06/20] [WiFi] Fix continuous logs flood + fix check for no credentials --- .../net/DataStructs/WiFi_AP_Candidate.cpp | 12 +-- src/ESPEasy/net/ESPEasyNetwork.cpp | 4 +- src/ESPEasy/net/ESPEasyNetwork.h | 6 +- src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp | 31 ++++++- src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp | 6 ++ src/ESPEasy/net/Globals/NWPlugins.cpp | 2 +- .../net/wifi/ESPEasyWiFi_state_machine.cpp | 8 +- src/ESPEasy/net/wifi/ESPEasyWifi.cpp | 74 +++++++++++------ src/ESPEasy/net/wifi/ESPEasyWifi.h | 4 + .../net/wifi/ESPEasyWifi_abstracted.cpp | 10 ++- src/src/DataStructs/SecurityStruct.cpp | 8 +- .../SecurityStruct_deviceSpecific.cpp | 80 +++++++++++++++++-- .../SecurityStruct_deviceSpecific.h | 46 ++--------- src/src/Helpers/ESPEasy_Storage.cpp | 2 +- src/src/Helpers/Networking.cpp | 4 +- src/src/WebServer/ESPEasy_WebServer.cpp | 3 +- 16 files changed, 206 insertions(+), 94 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/WiFi_AP_Candidate.cpp b/src/ESPEasy/net/DataStructs/WiFi_AP_Candidate.cpp index f3356c4b2..66257e986 100644 --- a/src/ESPEasy/net/DataStructs/WiFi_AP_Candidate.cpp +++ b/src/ESPEasy/net/DataStructs/WiFi_AP_Candidate.cpp @@ -7,6 +7,8 @@ #include "../../../src/Helpers/StringConverter.h" #include "../../../src/Helpers/StringGenerator_WiFi.h" +#include "../wifi/ESPEasyWifi.h" + #if defined(ESP8266) # include #endif // if defined(ESP8266) @@ -73,15 +75,9 @@ WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t index_c, const String& ssid_c) : { _allBits = 0u; - const size_t ssid_length = ssid_c.length(); - - if ((ssid_length == 0) || equals(ssid_c, F("ssid"))) { - return; + if (validWiFiSSID(ssid_c)) { + ssid = ssid_c; } - - if (ssid_length > 32) { return; } - - ssid = ssid_c; } WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t networkItem) : index(0) { diff --git a/src/ESPEasy/net/ESPEasyNetwork.cpp b/src/ESPEasy/net/ESPEasyNetwork.cpp index e84e2efa3..6ef3654ed 100644 --- a/src/ESPEasy/net/ESPEasyNetwork.cpp +++ b/src/ESPEasy/net/ESPEasyNetwork.cpp @@ -79,14 +79,14 @@ String makeRFCCompliantName(const String& name, const char replaceChar, const ch return hostname; } -void CheckRunningServices() { +void CheckRunningServices(bool force) { // First try to get the time, since that may be used in logs if (Settings.UseNTP() && (node_time.getTimeSource() > timeSource_t::NTP_time_source)) { node_time.lastNTPSyncTime_ms = 0; node_time.initTime(); } #if FEATURE_ESPEASY_P2P - updateUDPport(true); + updateUDPport(force); #endif #if FEATURE_WIFI diff --git a/src/ESPEasy/net/ESPEasyNetwork.h b/src/ESPEasy/net/ESPEasyNetwork.h index 22534cf74..3e3490ec2 100644 --- a/src/ESPEasy/net/ESPEasyNetwork.h +++ b/src/ESPEasy/net/ESPEasyNetwork.h @@ -3,6 +3,7 @@ #include "../../ESPEasy_common.h" #include "../net/DataStructs/MAC_address.h" +#include "../net/DataTypes/NetworkIndex.h" #include @@ -23,6 +24,9 @@ namespace net { void setNetworkMedium(NetworkMedium_t medium); + +ESPEasy::net::networkIndex_t getNetworkIndex_defaultNetwork(); + // void NetworkConnectRelaxed(); bool NetworkConnected(bool force = false); IPAddress NetworkLocalIP(); @@ -63,7 +67,7 @@ MAC_address WifiSoftAPmacAddress(); MAC_address WifiSTAmacAddress(); #endif // if FEATURE_WIFI -void CheckRunningServices(); +void CheckRunningServices(bool force = false); #if FEATURE_ETHERNET bool EthFullDuplex(); diff --git a/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp b/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp index bff09d5f1..7e8eea062 100644 --- a/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp +++ b/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp @@ -2,12 +2,14 @@ #include "../net/ESPEasyNetwork.h" -#include "../../ESPEasy/net/Globals/NetworkState.h" +#include "../net/Globals/NetworkState.h" +#include "../net/eth/ESPEasyEth.h" +#include "../net/Globals/NWPlugins.h" #include "../../src/ESPEasyCore/ESPEasy_Log.h" #include "../../src/Helpers/NetworkStatusLED.h" #include "../../src/Helpers/StringConverter.h" -#include "../net/eth/ESPEasyEth.h" -#include "../net/Globals/NWPlugins.h" +#include "../../src/Globals/Settings.h" + #include @@ -116,6 +118,29 @@ NetworkInterface* getDefaultNonAP_interface() return network_if; } +ESPEasy::net::networkIndex_t getNetworkIndex_defaultNetwork() +{ + auto network_if = getDefaultNonAP_interface(); + if (network_if == nullptr) { + return ESPEasy::net::INVALID_NETWORK_INDEX; + } + for (ESPEasy::net::networkIndex_t x = 0; x < NETWORK_MAX; ++x) { + if (Settings.getNetworkEnabled(x)) { + struct EventStruct TempEvent; + TempEvent.NetworkIndex = x; + String str; + + if (ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_GET_INTERFACE, &TempEvent, str)) + { + if (TempEvent.networkInterface->netif() == network_if->netif()) { + return x; + } + } + } + } + return ESPEasy::net::INVALID_NETWORK_INDEX; +} + bool NetworkConnected(bool force) { static bool last_result = false; static uint32_t last_check_millis = 0; diff --git a/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp b/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp index 131c37ba9..4b8480f78 100644 --- a/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp +++ b/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp @@ -56,6 +56,12 @@ void setNetworkMedium(NetworkMedium_t new_medium) { // ESPEasy::net::wifi::WiFiConnectRelaxed(); // } +ESPEasy::net::networkIndex_t getNetworkIndex_defaultNetwork() +{ + if (NetworkConnected()) return NETWORK_INDEX_WIFI_STA; + return ESPEasy::net::INVALID_NETWORK_INDEX; +} + bool NetworkConnected(bool force) { static bool last_result = false; static uint32_t last_check_millis = 0; diff --git a/src/ESPEasy/net/Globals/NWPlugins.cpp b/src/ESPEasy/net/Globals/NWPlugins.cpp index a4d53bcd4..a5aa8f672 100644 --- a/src/ESPEasy/net/Globals/NWPlugins.cpp +++ b/src/ESPEasy/net/Globals/NWPlugins.cpp @@ -181,7 +181,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) } if (Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) { - CheckRunningServices(); + CheckRunningServices(success); } return success; diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index 4c73ef129..c46c64dc8 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -153,7 +153,12 @@ void ESPEasyWiFi_t::loop() // && !WiFiEventData.warnedNoValidWiFiSettings ) { - addLog(LOG_LEVEL_ERROR, F("WIFI : No valid wifi settings")); + // Check for whether No Valid WiFi settings was already logged + static uint32_t lastTimeLoggedNoWiFiCredentials = 0; + if (timePassedSince(lastTimeLoggedNoWiFiCredentials) > 5000) { + addLog(LOG_LEVEL_ERROR, F("WIFI : No valid wifi settings")); + lastTimeLoggedNoWiFiCredentials = millis(); + } // WiFiEventData.warnedNoValidWiFiSettings = true; } @@ -657,6 +662,7 @@ bool ESPEasyWiFi_t::shouldStartAP_fallback() const bool ESPEasyWiFi_t::shouldRedirectTo_setup() const { + if (!Settings.ApCaptivePortal()) return false; if (Settings.StartAPfallback_NoCredentials() && !SecuritySettings.hasWiFiCredentials()) { return true; } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index 522e69e2a..989543efa 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -11,15 +11,15 @@ # include "../../../src/Helpers/ESPEasy_time_calc.h" # include "../../../src/Helpers/StringConverter.h" -#if FEATURE_TASKVALUE_UNIT_OF_MEASURE -# include "../../../src/Helpers/ESPEasy_UnitOfMeasure.h" -#endif +# if FEATURE_TASKVALUE_UNIT_OF_MEASURE +# include "../../../src/Helpers/ESPEasy_UnitOfMeasure.h" +# endif -#ifdef ESP8266 -# ifndef BUILD_NO_DEBUG -#include "../../../src/Helpers/StringGenerator_WiFi.h" -#endif -#endif +# ifdef ESP8266 +# ifndef BUILD_NO_DEBUG +# include "../../../src/Helpers/StringGenerator_WiFi.h" +# endif +# endif // ifdef ESP8266 # ifdef ESP32 # include @@ -101,6 +101,34 @@ namespace wifi { - Connection stable (connected for > 5 minutes) */ +bool validWiFiSSID(const String& ssid) { + // Can be any char upto 32 characters not being a null-char. + // Some implementations, like Cisco, suggest some invalid characters: + // - '+' + // - ']' + // - '/' + // - '*' + // - TAB + // - Trailing spaces + // And the first character cannot contain: + // - '!' + // - '#' + // - ';' + // Source: https://community.cisco.com/t5/networking-knowledge-base/characteristics-of-ssids/ta-p/3131765 + // + // However this seems to be Cisco specific and not a limitation of the standard + if (ssid.length() > 32) { return false; } + + // "ssid" is an internal ESPEasy limitation, to signal an 'unset' value. + return !ssid.isEmpty() && !ssid.equalsIgnoreCase(F("ssid")); +} + +bool validWiFiCredentials(const String& ssid, const String& pass) { + if (!validWiFiSSID(ssid)) { return false; } + const auto pass_length = pass.length(); + return (pass_length >= 8 && pass_length <= 63) + || pass_length == 0; // Empty password is possible +} // ******************************************************************************** // Check WiFi connected status @@ -158,19 +186,15 @@ void resetWiFi() { */ } -void initWiFi() { ESPEasyWiFi.setup(); } +void initWiFi() { ESPEasyWiFi.setup(); } -void exitWiFi() { ESPEasyWiFi.disable(); } +void exitWiFi() { ESPEasyWiFi.disable(); } -void loopWiFi() { ESPEasyWiFi.loop(); } +void loopWiFi() { ESPEasyWiFi.loop(); } -bool shouldStartAP_fallback() { - return ESPEasyWiFi.shouldStartAP_fallback(); -} +bool shouldStartAP_fallback() { return ESPEasyWiFi.shouldStartAP_fallback(); } -bool shouldRedirectTo_setup() { - return ESPEasyWiFi.shouldRedirectTo_setup(); -} +bool shouldRedirectTo_setup() { return ESPEasyWiFi.shouldRedirectTo_setup(); } # ifdef BOARD_HAS_SDIO_ESP_HOSTED @@ -229,13 +253,14 @@ String GetHostedFwVersion(EspHostTypes hostType) String GetHostedMCU() { // Function is not yet implemented in Arduino Core so emulate it here -#if defined(CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6) && CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6==1 +# if defined(CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6) && CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6 == 1 return String("ESP32-C6"); -#else +# else + if (equals(F(CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET), F("esp32c6"))) { return String("ESP32-C6"); } -#endif +# endif // if defined(CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6) && CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6 == 1 return String("Unknown"); } @@ -313,9 +338,9 @@ bool write_WiFi_Hosted_MCU_pins(KeyValueWriter*writer) writer->write(kv); } else { KeyValueStruct freq(F("SDIO Freq"), psdio_config->clock_freq_khz / 1000); -#if FEATURE_TASKVALUE_UNIT_OF_MEASURE +# if FEATURE_TASKVALUE_UNIT_OF_MEASURE freq.setUnit(UOM_MHz); -#endif +# endif writer->write(freq); writer->write({ F("SDIO D0"), psdio_config->pin_d0.pin }); writer->write({ F("SDIO D1"), psdio_config->pin_d1.pin }); @@ -324,9 +349,10 @@ bool write_WiFi_Hosted_MCU_pins(KeyValueWriter*writer) writer->write({ F("SDIO CLK"), psdio_config->pin_clk.pin }); writer->write({ F("SDIO CMD"), psdio_config->pin_cmd.pin }); writer->write({ F("SDIO RST"), psdio_config->pin_reset.pin }); + // Hide TX/RX queue size for now as it is unclear what these mean - //writer->write({ F("SDIO TX queue"), psdio_config->tx_queue_size }); - //writer->write({ F("SDIO RX queue"), psdio_config->rx_queue_size }); + // writer->write({ F("SDIO TX queue"), psdio_config->tx_queue_size }); + // writer->write({ F("SDIO RX queue"), psdio_config->rx_queue_size }); } return true; } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index 246a95a67..e31509600 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -40,6 +40,10 @@ namespace wifi { # define WIFI_SCAN_INTERVAL_AP_USED 125000 // in milliSeconds # define WIFI_SCAN_INTERVAL_MINIMAL 60000 // in milliSeconds +bool validWiFiSSID(const String& ssid); + +bool validWiFiCredentials(const String& ssid, const String& pass); + bool WiFiConnected(); diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted.cpp index 2d91f58db..af6bf3151 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted.cpp @@ -167,7 +167,15 @@ bool doSetAPinternal(bool enable) return false; } - if (Settings.ApCaptivePortal()) { + if (ESPEasy::net::wifi::shouldRedirectTo_setup()) { + # if FEATURE_DNS_SERVER + + if (dnsServerActive) { + dnsServerActive = false; + dnsServer.stop(); + } + # endif // if FEATURE_DNS_SERVER + # ifdef ESP32 # if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) diff --git a/src/src/DataStructs/SecurityStruct.cpp b/src/src/DataStructs/SecurityStruct.cpp index 40e7cb76d..f7ea4ae52 100644 --- a/src/src/DataStructs/SecurityStruct.cpp +++ b/src/src/DataStructs/SecurityStruct.cpp @@ -5,6 +5,8 @@ #include "../Globals/SecuritySettings.h" +#include "../../ESPEasy/net/wifi/ESPEasyWifi.h" + SecurityStruct::SecurityStruct() { ZERO_FILL(WifiSSID); @@ -90,10 +92,12 @@ bool SecurityStruct::hasWiFiCredentials() const { } bool SecurityStruct::hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot slot) const { + + if (slot == SecurityStruct::WiFiCredentialsSlot::first) - return (WifiSSID[0] != 0 && !String(WifiSSID).equalsIgnoreCase(F("ssid"))); + return ESPEasy::net::wifi::validWiFiSSID(WifiSSID); if (slot == SecurityStruct::WiFiCredentialsSlot::second) - return (WifiSSID2[0] != 0 && !String(WifiSSID2).equalsIgnoreCase(F("ssid"))); + return ESPEasy::net::wifi::validWiFiSSID(WifiSSID2); return false; } diff --git a/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp b/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp index eb2e1c6a6..b46155e8c 100644 --- a/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp +++ b/src/src/DataStructs/SecurityStruct_deviceSpecific.cpp @@ -2,6 +2,8 @@ #if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE +# include "../../ESPEasy/net/wifi/ESPEasyWifi.h" + int SecurityStruct_deviceSpecific::maxLength(KeyType keyType) { switch (keyType) @@ -75,19 +77,83 @@ bool SecurityStruct_deviceSpecific::hasWiFiCredentials() const { for (uint8_t i = 0; i < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE; ++i) { - if (hasWiFiCredentials(i)) return true; + if (hasWiFiCredentials(i)) { return true; } } return false; } bool SecurityStruct_deviceSpecific::hasWiFiCredentials(uint8_t index) const +{ + if (index >= MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE) { return false; } + String ssid, pass; + + if (!_kvs.getValue(createKey(KeyType::WiFi_SSID, index), ssid, KVS_StorageType::Enum::string_type) || + !_kvs.getValue(createKey(KeyType::WiFi_Password, index), pass, KVS_StorageType::Enum::string_type)) { + return false; + } + return ESPEasy::net::wifi::validWiFiCredentials(ssid, pass); +} + +bool SecurityStruct_deviceSpecific::getWiFiCredentials(uint8_t index, + String& ssid, + String& passwd) const { return - (index < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE) && - _kvs.hasKey(KVS_StorageType::Enum::string_type, - createKey(KeyType::WiFi_SSID, index)) && - _kvs.hasKey(KVS_StorageType::Enum::string_type, - createKey(KeyType::WiFi_Password, index)); + index < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE && + getCredentials(KeyType::WiFi_SSID, KeyType::WiFi_Password, index, ssid, passwd); +} + +void SecurityStruct_deviceSpecific::setWiFiCredentials( + uint8_t index, + const String& ssid, + const String& passwd) +{ + if (index < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE) + { + if (ESPEasy::net::wifi::validWiFiCredentials(ssid, passwd)) { + setCredentials( + KeyType::WiFi_SSID, + KeyType::WiFi_Password, + index, + ssid, + passwd); + } + else { + _kvs.clearKey( + KVS_StorageType::Enum::string_type, + createKey(KeyType::WiFi_SSID, index)); + _kvs.clearKey( + KVS_StorageType::Enum::string_type, + createKey(KeyType::WiFi_Password, index)); + } + } +} + +bool SecurityStruct_deviceSpecific::getControllerCredentials(uint8_t index, + String& user, + String& passwd) const +{ + return + (index < CONTROLLER_MAX) && getCredentials( + KeyType::Controller_User, + KeyType::Controller_Password, + index, + user, + passwd); +} + +void SecurityStruct_deviceSpecific::setControllerCredentials(uint8_t index, + const String& user, + const String& passwd) +{ + if (index < CONTROLLER_MAX) { + setCredentials( + KeyType::Controller_User, + KeyType::Controller_Password, + index, + user, + passwd); + } } /* @@ -133,7 +199,7 @@ void SecurityStruct_deviceSpecific::setCredentials(KeyType keytype_key, const String& key, const String& secret) { - _kvs.setValue(createKey(keytype_key, index), key, KVS_StorageType::Enum::string_type); + _kvs.setValue(createKey(keytype_key, index), key, KVS_StorageType::Enum::string_type); _kvs.setValue(createKey(keytype_secret, index), secret, KVS_StorageType::Enum::string_type); } diff --git a/src/src/DataStructs/SecurityStruct_deviceSpecific.h b/src/src/DataStructs/SecurityStruct_deviceSpecific.h index 2a4c05ec5..4bddb2fb2 100644 --- a/src/src/DataStructs/SecurityStruct_deviceSpecific.h +++ b/src/src/DataStructs/SecurityStruct_deviceSpecific.h @@ -53,63 +53,29 @@ public: // Move all set WiFi credentials to free up first entry // Typically used for setup page. -// void freeUpFirstWiFiCredentials(uint8_t index = 0); + // void freeUpFirstWiFiCredentials(uint8_t index = 0); bool getWiFiCredentials(uint8_t index, String& ssid, - String& passwd) const - { - return - index < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE && - getCredentials(KeyType::WiFi_SSID, KeyType::WiFi_Password, index, ssid, passwd); - } + String& passwd) const; void setWiFiCredentials( uint8_t index, const String& ssid, - const String& passwd) - { - if (index < MAX_EXTRA_WIFI_CREDENTIALS_SEPARATE_FILE) - { - setCredentials( - KeyType::WiFi_SSID, - KeyType::WiFi_Password, - index, - ssid, - passwd); - } - } + const String& passwd); bool getControllerCredentials(uint8_t index, String& user, - String& passwd) const - { - return - (index < CONTROLLER_MAX) && getCredentials( - KeyType::Controller_User, - KeyType::Controller_Password, - index, - user, - passwd); - } + String& passwd) const; void setControllerCredentials(uint8_t index, const String& user, - const String& passwd) - { - if (index < CONTROLLER_MAX) { - setCredentials( - KeyType::Controller_User, - KeyType::Controller_Password, - index, - user, - passwd); - } - } + const String& passwd); bool getValue(KeyType keytype, uint8_t index, String& value) const; + void setValue(KeyType keytype, uint8_t index, const String& value); diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 5f0d9b3dc..a917b92aa 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -991,7 +991,7 @@ void afterloadSettings() { #ifdef ESP32 // FIXME TD-er: Must also update hostname on other interfaces for ESP32 #endif - ESPEasy::net::CheckRunningServices(); // To update changes in hostname. + ESPEasy::net::CheckRunningServices(true); // To update changes in hostname. } /********************************************************************************************\ diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index d84c34269..e44fc50d3 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -167,7 +167,7 @@ void updateUDPport(bool force) // Or we may need to look into AsyncUDP as that allows to send to specific interfaces. const bool connected = ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN); //const bool connected = ESPEasy::net::NetworkConnected(); - if (!connected || lastUsedUDPPort != 0) { + if (!connected || (Settings.UDPPort != lastUsedUDPPort)) { if (lastUsedUDPPort != 0) { portUDP.stop(); lastUsedUDPPort = 0; @@ -180,7 +180,7 @@ void updateUDPport(bool force) } } - if (Settings.UDPPort != 0) { + if ((Settings.UDPPort != lastUsedUDPPort) && Settings.UDPPort != 0) { if (portUDP.begin(Settings.UDPPort) == 0) { #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_ERROR)) { diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index b12f60033..9ecf830fb 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -126,6 +126,7 @@ void sendHeadandTail_stdtemplate(bool Tail, bool rebooting) { sendHeadandTail(F("TmplStd"), Tail, rebooting); if (!Tail) { + // TODO TD-er: Must check clientConnectedToAP()? if (!clientIPinSubnetDefaultNetwork() && ESPEasy::net::wifi::wifiAPmodeActivelyUsed()) { addHtmlError(F("Warning: Connected via AP")); } @@ -504,7 +505,7 @@ void setWebserverRunning(bool state) { #endif } webserverRunning = state; - ESPEasy::net::CheckRunningServices(); // Uses webserverRunning state. + ESPEasy::net::CheckRunningServices(true); // Uses webserverRunning state. } void getWebPageTemplateDefault(const String& tmplName, WebTemplateParser& parser) From acb7288bddf12959c197efb7ead83000b39c12cb Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Apr 2026 01:26:04 +0200 Subject: [PATCH 07/20] [Build] Fix ESP8266 build --- src/src/WebServer/ESPEasy_WebServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 9ecf830fb..5d0db0b85 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -221,7 +221,7 @@ ESPEasy::net::networkIndex_t getNetworkIndex_ClientConnectsTo() return NETWORK_INDEX_WIFI_AP; } #ifdef ESP8266 - if (client_localIP == WiFi.IP()) { + if (client_localIP == WiFi.localIP()) { return NETWORK_INDEX_WIFI_STA; } #endif From 2e9bd456955cefa005c55d8e203979cc297b988b Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 1 May 2026 00:12:28 +0200 Subject: [PATCH 08/20] [ETH] Fix setting static IP for Ethernet network interfaces --- .../NWPluginData_static_runtime.cpp | 12 +++++++++- .../DataStructs/NWPluginData_static_runtime.h | 5 ++++ src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp | 2 +- .../NW003_data_struct_ETH_RMII.cpp | 12 ++++++++++ .../NW004_data_struct_ETH_SPI.cpp | 6 +++++ .../eth/ETH_NWPluginData_static_runtime.cpp | 7 ++++++ src/src/WebServer/Markup_Forms.cpp | 24 +++++++++++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp index 515d9b57b..9d85879d4 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp @@ -126,7 +126,7 @@ void NWPluginData_static_runtime::clear(networkIndex_t networkIndex) _connectionFailures = 0; - // FIXME TD-er: Should also clear dns cache? + // FIXME TD-er: Should also clear dns cache and/or static IP? } void NWPluginData_static_runtime::processEvent_and_clear() @@ -304,6 +304,15 @@ void NWPluginData_static_runtime::processEvents() } } +void NWPluginData_static_runtime::setStaticIP(const IPAddress & ip, const IPAddress & gateway, const IPAddress & subnetmask, const IPAddress & dns) +{ + _useStaticIP = IPAddressSet(ip) && IPAddressSet(gateway) && IPAddressSet(subnetmask); + _ip = ip; + _gateway = gateway; + _sn = subnetmask; + _dns = dns; +} + String NWPluginData_static_runtime::statusToString() const { String log; @@ -314,6 +323,7 @@ String NWPluginData_static_runtime::statusToString() const if (hasIP()) { log += F("IP "); + if (_useStaticIP) log += F("(static) "); } if (operational()) { diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h index 072ec61f9..d666a907b 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h @@ -127,6 +127,8 @@ struct NWPluginData_static_runtime { void processEvents(); + void setStaticIP(const IPAddress & ip, const IPAddress & gateway, const IPAddress & subnetmask, const IPAddress & dns); + // ============================================= // OnOffTimers for keeping track of: // - start/stop of interface @@ -157,6 +159,9 @@ struct NWPluginData_static_runtime { bool _enableIPv6{}; // Cached enableIPv6 flag as it is being used from callbacks #endif + IPAddress _ip, _gateway, _sn, _dns; + bool _useStaticIP{}; // Added to make sure we don't have to re-check IP from a callback + private: #ifdef ESP32 diff --git a/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp b/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp index 7e8eea062..0a53f13ce 100644 --- a/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp +++ b/src/ESPEasy/net/ESPEasyNetwork_ESP32.cpp @@ -147,7 +147,6 @@ bool NetworkConnected(bool force) { if (force || (timePassedSince(last_check_millis) > 50) || (last_check_millis == 0)) { last_check_millis = millis(); - last_result = Network.isOnline(); // FIXME TD-er: This is checking for NonAP interfaces, however we also have // ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN); @@ -161,6 +160,7 @@ bool NetworkConnected(bool force) { // - Webserver processNetworkEvents(); + last_result = Network.isOnline(); } return last_result; } diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp index 519d94f71..5645d0f3f 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp @@ -553,6 +553,12 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (!(data && iface)) { return false; } + { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + data->setStaticIP(ip, gateway, sn, dns); + } + if (data->started() && data->connected()) { if (EthLinkUp()) { return true; } data->mark_connect_failed(); @@ -595,6 +601,12 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { IPAddress ip, gateway, sn, dns; if (getStaticIPAddresses(ip, gateway, sn, dns)) { + addLog(LOG_LEVEL_INFO, strformat( + F("ETH: static IP: %s, GW: %s, SN: %s, DNS:%s"), + ip.toString().c_str(), + gateway.toString().c_str(), + sn.toString().c_str(), + dns.toString().c_str())); iface->config(ip, gateway, sn, dns); } diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index 31cb1c58f..056967685 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -441,6 +441,12 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (!(data && iface)) { return false; } + { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + data->setStaticIP(ip, gateway, sn, dns); + } + if (data->started() && data->connected()) { if (EthLinkUp()) { return true; } data->mark_connect_failed(); diff --git a/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp b/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp index f14a59057..83bdc73b0 100644 --- a/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp +++ b/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp @@ -39,6 +39,13 @@ struct ETH_stats_and_cache_t { // _eth.enableIPv6(_stats_and_cache._enableIPv6); } + if (_stats_and_cache._useStaticIP) { + _eth.config( + _stats_and_cache._ip, + _stats_and_cache._gateway, + _stats_and_cache._sn, + _stats_and_cache._dns); + } } void mark_stop(esp_eth_handle_t handle) diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index f3a415e30..690d16cdf 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -411,7 +411,31 @@ void addFormIPBox(const String & label, const IPAddress& ip) { const bool empty_IP = !IPAddressSet(ip); + addFormTextBox(label, id, (empty_IP) ? EMPTY_STRING : formatIP(ip)); + + /* + // TODO TD-er: Option to validate IP in HTML + // + // IP input HTML validation from: + // Source - https://stackoverflow.com/a/54796814 + // Posted by ptay, modified by community. See post 'Timeline' for change history + // Retrieved 2026-04-30, License - CC BY-SA 4.0 + // + // + + const String value = (empty_IP) ? EMPTY_STRING : formatIP(ip); + addRowLabel_tr_id(label, id); + + // Validation only for IPv4 address + addHtml(strformat( + F(""), + id.c_str(), + id.c_str(), + value.c_str() + )); + */ } From 0895c1e1b96f7178e5ef56b4efbb84d9ab703d8c Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 1 May 2026 00:37:39 +0200 Subject: [PATCH 09/20] [Build] Remove normal_beta_ESP8266_16M_LittleFS --- platformio_esp82xx_envs.ini | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index df323aead..9f6b301f8 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -328,26 +328,26 @@ lib_ignore = ${normal_beta_2ndheap.lib_ignore} ; NORMAL: 16M version --- LittleFS -------------- ; LittleFS is determined by using "LittleFS" in the pio env name -[env:normal_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${normal_beta.platform} -platform_packages = ${normal_beta.platform_packages} -build_flags = ${normal_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -lib_ignore = ESP32_ping - ESP32WebServer - ESP32HTTPUpdateServer - ServoESP32 - ${no_ir.lib_ignore} - TinyWireM - ESP8266SdFat - SD(esp8266) - SD - SDFS - ArduinoOTA - ESP8266mDNS - I2C AXP192 Power management +;[env:normal_beta_ESP8266_16M_LittleFS] +;extends = esp8266_16M +;platform = ${normal_beta.platform} +;platform_packages = ${normal_beta.platform_packages} +;build_flags = ${normal_beta.build_flags} +; ${esp8266_16M.build_flags} +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +;lib_ignore = ESP32_ping +; ESP32WebServer +; ESP32HTTPUpdateServer +; ServoESP32 +; ${no_ir.lib_ignore} +; TinyWireM +; ESP8266SdFat +; SD(esp8266) +; SD +; SDFS +; ArduinoOTA +; ESP8266mDNS +; I2C AXP192 Power management From aeb06ea8a39d5ec4552b5870adc1dbec99aa4082 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 1 May 2026 00:55:11 +0200 Subject: [PATCH 10/20] [Build] Fix missing include --- src/src/WebServer/ESPEasy_WebServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 5d0db0b85..c2630004a 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -62,6 +62,7 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_device_info.h" +#include "../Helpers/Networking.h" #include "../Helpers/OTA.h" #include "../Helpers/StringConverter.h" From 02ccc74235cb0d6e356018dc4099102113256b77 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 1 May 2026 12:23:32 +0200 Subject: [PATCH 11/20] [WiFi] Fix static IP for WiFi --- .../NWPluginData_static_runtime_ESP32.cpp | 10 ++++++++ .../NWPluginData_static_runtime_ESP8266.cpp | 14 +++++++++++ .../NW001_data_struct_WiFi_STA.cpp | 10 ++++++++ .../NW002_data_struct_WiFi_AP.cpp | 9 +++++++ .../NW003_data_struct_ETH_RMII.cpp | 15 ++++++----- .../NW004_data_struct_ETH_SPI.cpp | 15 ++++++----- .../eth/ETH_NWPluginData_static_runtime.cpp | 7 ------ .../net/wifi/ESPEasyWiFi_state_machine.cpp | 1 - src/ESPEasy/net/wifi/ESPEasyWifi.cpp | 25 ------------------- src/ESPEasy/net/wifi/ESPEasyWifi.h | 1 - src/src/Helpers/ESPEasy_Storage.cpp | 13 +++++----- 11 files changed, 68 insertions(+), 52 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp index 63a4daa5a..720791ab7 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp @@ -96,6 +96,16 @@ void NWPluginData_static_runtime::mark_start() if (!_netif) { return; } + if (_useStaticIP) { + _netif->config( + _ip, + _gateway, + _sn, + _dns); + } else { + _netif->config((uint32_t)0, (uint32_t)0, (uint32_t)0); + } + const String hostname = strformat( F("%s-%s"), NetworkCreateRFCCompliantHostname().c_str(), diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index 3b96690b0..f206d2b19 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -6,6 +6,7 @@ # include "../ESPEasyNetwork.h" # include "../../../src/Helpers/StringConverter.h" +# include "../wifi/ESPEasyWifi.h" namespace ESPEasy { namespace net { @@ -38,6 +39,19 @@ bool NWPluginData_static_runtime::hasIP() const void NWPluginData_static_runtime::mark_start() { + if (!_isAP) { + ESPEasy::net::wifi::setUseStaticIP(_useStaticIP); + if (_useStaticIP) { + WiFi.config( + _ip, + _gateway, + _sn, + _dns); + } else { + WiFi.config((uint32_t)0, (uint32_t)0, (uint32_t)0); + } + } + _startStopStats.setOn(); # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, _isAP ? F("AP: Started") : F("STA: Started")); diff --git a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp index cda7f7ab4..3f36ef62c 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW001_data_struct_WiFi_STA.cpp @@ -97,6 +97,16 @@ bool NW001_data_struct_WiFi_STA::init(EventStruct *event) // ESPEasy::net::wifi::setWifiMode(WIFI_OFF); */ + { + auto runtime_data = getNWPluginData_static_runtime(); + if (runtime_data) { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + runtime_data->setStaticIP(ip, gateway, sn, dns); + } + } + + ESPEasy::net::wifi::initWiFi(); return true; } diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp index 90e4c3028..6ae83fccd 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp @@ -91,6 +91,15 @@ bool NW002_data_struct_WiFi_AP::webform_getPort(KeyValueWriter *writer) { return bool NW002_data_struct_WiFi_AP::init(EventStruct *event) { + { + auto runtime_data = getNWPluginData_static_runtime(); + if (runtime_data) { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + runtime_data->setStaticIP(ip, gateway, sn, dns); + } + } + # ifdef ESP32 nw002_enable_NAPT = Settings.WiFi_AP_enable_NAPT(); # endif diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp index 5645d0f3f..72345e5ce 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp @@ -347,6 +347,15 @@ bool NW003_data_struct_ETH_RMII::webform_getPort(KeyValueWriter *writer) { retur bool NW003_data_struct_ETH_RMII::init(EventStruct *event) { _load(); + { + auto runtime_data = getNWPluginData_static_runtime(); + if (runtime_data) { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + runtime_data->setStaticIP(ip, gateway, sn, dns); + } + } + ETHConnectRelaxed(); return true; @@ -553,12 +562,6 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (!(data && iface)) { return false; } - { - IPAddress ip, gateway, sn, dns; - getStaticIPAddresses(ip, gateway, sn, dns); - data->setStaticIP(ip, gateway, sn, dns); - } - if (data->started() && data->connected()) { if (EthLinkUp()) { return true; } data->mark_connect_failed(); diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index 056967685..f86f57f05 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -293,6 +293,15 @@ bool NW004_data_struct_ETH_SPI::webform_getPort(KeyValueWriter *writer) { return bool NW004_data_struct_ETH_SPI::init(EventStruct *event) { _load(); + { + auto runtime_data = getNWPluginData_static_runtime(); + if (runtime_data) { + IPAddress ip, gateway, sn, dns; + getStaticIPAddresses(ip, gateway, sn, dns); + runtime_data->setStaticIP(ip, gateway, sn, dns); + } + } + ETHConnectRelaxed(); return true; @@ -441,12 +450,6 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (!(data && iface)) { return false; } - { - IPAddress ip, gateway, sn, dns; - getStaticIPAddresses(ip, gateway, sn, dns); - data->setStaticIP(ip, gateway, sn, dns); - } - if (data->started() && data->connected()) { if (EthLinkUp()) { return true; } data->mark_connect_failed(); diff --git a/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp b/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp index 83bdc73b0..f14a59057 100644 --- a/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp +++ b/src/ESPEasy/net/eth/ETH_NWPluginData_static_runtime.cpp @@ -39,13 +39,6 @@ struct ETH_stats_and_cache_t { // _eth.enableIPv6(_stats_and_cache._enableIPv6); } - if (_stats_and_cache._useStaticIP) { - _eth.config( - _stats_and_cache._ip, - _stats_and_cache._gateway, - _stats_and_cache._sn, - _stats_and_cache._dns); - } } void mark_stop(esp_eth_handle_t handle) diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index c46c64dc8..f0316ddcb 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -541,7 +541,6 @@ bool ESPEasyWiFi_t::connectSTA() // WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); # endif // if defined(ESP32) doSetConnectionSpeed(); - setupStaticIPconfig(); // Start the process of connecting or starting AP if (!WiFi_AP_Candidates.getNext(true)) diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index 989543efa..e41ab40cb 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -507,31 +507,6 @@ bool wifiAPmodeActivelyUsed() // reconnect? } -void setupStaticIPconfig() { - setUseStaticIP(WiFiUseStaticIP()); - - if (!WiFiUseStaticIP()) { return; } - const IPAddress ip(Settings.IP); - const IPAddress gw(Settings.Gateway); - const IPAddress subnet(Settings.Subnet); - const IPAddress dns(Settings.DNS); - - // WiFiEventData.dns0_cache = dns; - - WiFi.config(ip, gw, subnet, dns); -# ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat( - F("IP : Static IP : %s GW: %s SN: %s DNS: %s"), - formatIP(ip).c_str(), - formatIP(gw).c_str(), - formatIP(subnet).c_str(), - getValue(LabelType::DNS).c_str())); - } -# endif // ifndef BUILD_NO_DEBUG -} - // ******************************************************************************** // Formatting WiFi related strings // ******************************************************************************** diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index e31509600..80ea2fc74 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -113,7 +113,6 @@ bool setAPinternal(bool enable); // FIXME TD-er: Move to ESPEasyWifi_abstract void setUseStaticIP(bool enabled); bool WiFiUseStaticIP(); bool wifiAPmodeActivelyUsed(); -void setupStaticIPconfig(); String formatScanResult(int i, const String& separator); String formatScanResult(int i, diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index a917b92aa..f91856c63 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -582,7 +582,9 @@ void fileSystemCheck() #ifndef BUILD_NO_RAM_TRACKER checkRAM(F("fileSystemCheck")); #endif // ifndef BUILD_NO_RAM_TRACKER +# ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("FS : Mounting...")); +#endif #if defined(ESP32) && defined(USE_LITTLEFS) if ((getPartionCount(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS) != 0) @@ -593,7 +595,7 @@ void fileSystemCheck() #endif // if defined(ESP32) && defined(USE_LITTLEFS) { clearAllCaches(); - +# ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat( F("FS : " @@ -605,6 +607,7 @@ void fileSystemCheck() " mount successful, used %u bytes of %u"), SpiffsUsedBytes(), SpiffsTotalBytes())); } +# endif // Run garbage collection before any file is open. uint8_t retries = 3; @@ -711,7 +714,7 @@ bool Erase_WiFi_Calibration() { ESPEasy::net::wifi::setWifiMode(WIFI_OFF); if (!ESP.eraseConfig()) return false; - #ifndef LIMIT_BUILD_SIZE + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("WiFi : Erased WiFi calibration data")); #endif #endif @@ -1064,8 +1067,6 @@ String LoadSettings() SecuritySettings_deviceSpecific.load(); #endif - - // setupStaticIPconfig(); // FIXME TD-er: Must check if static/dynamic IP was changed and trigger a reconnect? Or is a reboot better when changing those settings? afterloadSettings(); SecuritySettings.validate(); @@ -1446,7 +1447,7 @@ String SaveTaskSettings(taskIndex_t TaskIndex) reinterpret_cast(&ExtraTaskSettings), sizeof(struct ExtraTaskSettingsStruct)); -#ifndef LIMIT_BUILD_SIZE +#ifndef BUILD_NO_DEBUG if (err.isEmpty()) { err = checkTaskSettings(TaskIndex); } @@ -1455,7 +1456,7 @@ String SaveTaskSettings(taskIndex_t TaskIndex) // FIXME TD-er: Is this still needed as it is also cleared on PLUGIN_INIT and PLUGIN_EXIT? UserVar.clear_computed(ExtraTaskSettings.TaskIndex); } -#ifndef LIMIT_BUILD_SIZE +#ifndef BUILD_NO_DEBUG else { addLog(LOG_LEVEL_INFO, F("Skip saving task settings, not changed")); } From 1889767d5ce59599c80e02461234134d28f2bd30 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 1 May 2026 12:41:35 +0200 Subject: [PATCH 12/20] [WiFi] Fix static IP for ESP8266 --- .../NWPluginData_static_runtime_ESP8266.cpp | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index f206d2b19..ff9d44425 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -39,19 +39,6 @@ bool NWPluginData_static_runtime::hasIP() const void NWPluginData_static_runtime::mark_start() { - if (!_isAP) { - ESPEasy::net::wifi::setUseStaticIP(_useStaticIP); - if (_useStaticIP) { - WiFi.config( - _ip, - _gateway, - _sn, - _dns); - } else { - WiFi.config((uint32_t)0, (uint32_t)0, (uint32_t)0); - } - } - _startStopStats.setOn(); # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, _isAP ? F("AP: Started") : F("STA: Started")); @@ -85,6 +72,20 @@ void NWPluginData_static_runtime::mark_lost_IP() void NWPluginData_static_runtime::mark_begin_establish_connection() { + if (!_isAP) { + ESPEasy::net::wifi::setUseStaticIP(_useStaticIP); + if (_useStaticIP) { + WiFi.config( + _ip, + _gateway, + _sn, + _dns); + mark_got_IP(); + } else { + WiFi.config((uint32_t)0, (uint32_t)0, (uint32_t)0); + } + } + _establishConnectStats.forceSet(true); _connectedStats.setOff(); _operationalStats.setOff(); From c57a77d022512d4dfc4d6cb908f4c751cc1e06f4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 3 May 2026 00:49:06 +0200 Subject: [PATCH 13/20] [ESP32S2] Add MAX 8M1M build for ESP32-S2 --- boards/esp32s2cdc_8M.json | 41 +++++++++++++++++++++++++++++++++++++ platformio_esp32s2_envs.ini | 11 ++++++++++ 2 files changed, 52 insertions(+) create mode 100644 boards/esp32s2cdc_8M.json diff --git a/boards/esp32s2cdc_8M.json b/boards/esp32s2cdc_8M.json new file mode 100644 index 000000000..1221c8258 --- /dev/null +++ b/boards/esp32s2cdc_8M.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld", + "memory_type": "dio_qspi" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DESP32_8M -DESP32S2 -DCONFIG_IDF_TARGET_ESP32S2=1 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1 -DCONFIG_TINYUSB_CDC_ENABLED=1", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32s2", + "variant": "esp32s2", + "filesystem": "littlefs", + "partitions": "boards/partitions/esp32_partition_app3520k_spiffs1088k.csv" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "espidf", + "arduino" + ], + "name": "Espressif Generic ESP32-S2 USB CDC 8M Flash ESPEasy 3520k Code/OTA 1088k FS", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "before_reset": "usb_reset", + "after_reset": "hard_reset", + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html", + "vendor": "Espressif" +} diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index 6b7e2b2c0..acfa73093 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -148,3 +148,14 @@ board = esp32s2cdc build_flags = ${esp32s2_common_LittleFS.build_flags} -D PLUGIN_CLIMATE_B_COLLECTION + +[env:max_ESP32s2_8M1M] +extends = esp32s2_common_LittleFS +board = esp32s2cdc_8M +build_flags = ${esp32s2_common_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA=1 + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED + -D ST7789_EXTRA_INIT=1 + -D P116_EXTRA_ST7789=1 +extra_scripts = ${esp32s2_common_LittleFS.extra_scripts} From 28a8b931db21198dd81f03aba92dc3c76a565a24 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 3 May 2026 00:49:29 +0200 Subject: [PATCH 14/20] [ESP-IDF] Update to latest IDF/Arduino code --- platformio_core_defs.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 37d694412..111ac0994 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -188,7 +188,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2403-0020-6.0/framework-arduinoespressif32-release_v6.0-1a97ddaa.tar.xz platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc152 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1204-1214-5.5/framework-arduinoespressif32-release_v5.5-f2a3fa2b.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2904-2115-5.5/framework-arduinoespressif32-release_v5.5-f2a3fa2b.tar.xz custom_remove_include = true From 746b0ae8d2ed5f478fd6d95367d40490475cdb5b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 3 May 2026 00:50:24 +0200 Subject: [PATCH 15/20] [Sysinfo] Fix reporting used sketch size on sysinfo page --- src/src/Helpers/StringProvider.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index e7df36d7f..0bf717824 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -1044,10 +1044,19 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) uint32_t maxSketchSize; bool use2step; OTA_possible(maxSketchSize, use2step); - str += strformat( - F("%d [kB] (%d kB not used)"), - (getSketchSize() >> 10), - (maxSketchSize - getSketchSize()) >> 10); + const uint32_t sketchsize_kB = getSketchSize() >> 10; + maxSketchSize >>= 10; + + if (maxSketchSize >= sketchsize_kB) + str += strformat( + F("%d [kB] (%d kB not used)"), + sketchsize_kB, + (maxSketchSize - sketchsize_kB)); + else + str += strformat( + F("%d [kB]"), + sketchsize_kB); + } else { str = (getSketchSize() >> 10); } From 3a0d2579ead71ea574a760b9c60d18b4e442e205 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 3 May 2026 00:53:23 +0200 Subject: [PATCH 16/20] [Console] Fix setting correct TX buffer size + add checks + cleanup --- .../Port_ESPEasySerial_HardwareSerial.cpp | 2 +- src/src/ESPEasyCore/ESPEasy_Console.cpp | 10 ---------- src/src/ESPEasyCore/ESPEasy_Console.h | 2 -- src/src/ESPEasyCore/ESPEasy_Console_Port.cpp | 14 ++++++++++---- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp index aad83e661..3d4a695f0 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp @@ -153,7 +153,7 @@ void Port_ESPEasySerial_HardwareSerial_t::begin(unsigned long baud) } if (_config.txBuffSize > 256) { - _config.txBuffSize = _serial->setRxBufferSize(_config.txBuffSize); + _config.txBuffSize = _serial->setTxBufferSize(_config.txBuffSize); } _serial->begin(baud, _config.config, _config.receivePin, _config.transmitPin, _config.inverse_logic); diff --git a/src/src/ESPEasyCore/ESPEasy_Console.cpp b/src/src/ESPEasyCore/ESPEasy_Console.cpp index 306b18aef..1acf0ac99 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console.cpp @@ -444,13 +444,3 @@ void EspEasy_Console_t::endPort() #endif // if USES_ESPEASY_CONSOLE_FALLBACK_PORT delay(10); } - -int EspEasy_Console_t::availableForWrite() -{ - auto serial = getPort(); - - if (serial != nullptr) { - return serial->availableForWrite(); - } - return 0; -} diff --git a/src/src/ESPEasyCore/ESPEasy_Console.h b/src/src/ESPEasyCore/ESPEasy_Console.h index a3c31c5cb..b266cc0d8 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console.h +++ b/src/src/ESPEasyCore/ESPEasy_Console.h @@ -47,8 +47,6 @@ private: void endPort(); - int availableForWrite(); - uint32_t _baudrate = 115200u; EspEasy_Console_Port _mainSerial; diff --git a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp index d0a5c54b1..3297e095e 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp @@ -148,6 +148,10 @@ bool EspEasy_Console_Port::process_serialWriteBuffer() bool EspEasy_Console_Port::process_consoleInput(uint8_t SerialInByte) { + if (!InputBuffer_Serial) { + return false; + } + if (isprint(SerialInByte)) { if (SerialInByteCounter < CONSOLE_INPUT_BUFFER_SIZE) { // add char to string if it still fits @@ -163,14 +167,16 @@ bool EspEasy_Console_Port::process_consoleInput(uint8_t SerialInByte) { // Ignore empty command if (SerialInByteCounter != 0) { - InputBuffer_Serial[SerialInByteCounter] = 0; // serial data completed + if (SerialInByteCounter < CONSOLE_INPUT_BUFFER_SIZE) { + InputBuffer_Serial[SerialInByteCounter] = 0; // serial data completed - String cmd(InputBuffer_Serial); + String cmd(InputBuffer_Serial); #if !FEATURE_COLORIZE_CONSOLE_LOGS - Logging.consolePrintln(concat('>', cmd)); + Logging.consolePrintln(concat('>', cmd)); #endif - ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd) }, true); + ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd) }, true); + } SerialInByteCounter = 0; InputBuffer_Serial[0] = 0; // serial data processed, clear buffer return true; From 31803147858adfaa36709885b481feaad4603cad Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 3 May 2026 00:56:05 +0200 Subject: [PATCH 17/20] [WiFi] Fix setting init/exit/gotIP flags on Network interfaces --- .../DataStructs/NWPluginData_static_runtime_ESP8266.cpp | 7 +++++-- src/ESPEasy/net/Globals/NWPlugins.cpp | 4 ++-- src/ESPEasy/net/Helpers/_NWPlugin_init.cpp | 8 ++++++-- src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp | 2 ++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index ff9d44425..32b27103b 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -79,8 +79,7 @@ void NWPluginData_static_runtime::mark_begin_establish_connection() _ip, _gateway, _sn, - _dns); - mark_got_IP(); + _dns); } else { WiFi.config((uint32_t)0, (uint32_t)0, (uint32_t)0); } @@ -96,6 +95,10 @@ void NWPluginData_static_runtime::mark_connected() { _establishConnectStats.setOff(); _connectedStats.setOn(); + if (_useStaticIP) { + // Since we won't get an event stating we got an IP, we need to trigger it here + mark_got_IP(); + } } void NWPluginData_static_runtime::log_connected() diff --git a/src/ESPEasy/net/Globals/NWPlugins.cpp b/src/ESPEasy/net/Globals/NWPlugins.cpp index a5aa8f672..10709b356 100644 --- a/src/ESPEasy/net/Globals/NWPlugins.cpp +++ b/src/ESPEasy/net/Globals/NWPlugins.cpp @@ -493,7 +493,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) } #endif // ifdef ESP32 } -#ifdef ESP32 +//#ifdef ESP32 if (Function == NWPlugin::Function::NWPLUGIN_EXIT) { // Cache.clearNetworkSettings(networkIndex); @@ -505,7 +505,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) } clearNWPluginData(event->NetworkIndex); } -#endif // ifdef ESP32 +//#endif // ifdef ESP32 } return success; } diff --git a/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp b/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp index b62012e54..2f8639577 100644 --- a/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp +++ b/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp @@ -2130,20 +2130,24 @@ bool do_NWPluginCall(networkDriverIndex_t networkDriverIndex, NWPlugin:: addLog(LOG_LEVEL_ERROR, strformat(F("Network %d was already initialized"), event->NetworkIndex + 1)); return false; } - bitSet(networkIndex_initialized, event->NetworkIndex); } else if (Function == NWPlugin::Function::NWPLUGIN_EXIT) { if (!bitRead(networkIndex_initialized, event->NetworkIndex)) { // FIXME TD-er: What to do here? Was not (yet) initialized // addLog(LOG_LEVEL_ERROR, strformat(F("Network %d was not (yet) initialized"), event->NetworkIndex + 1)); return false; } - bitClear(networkIndex_initialized, event->NetworkIndex); } START_TIMER; NWPlugin_ptr_t nwplugin_call = (NWPlugin_ptr_t)pgm_read_ptr(NWPlugin_ptr + networkDriverIndex.value); const bool res = nwplugin_call(Function, event, string); + if (res && Function == NWPlugin::Function::NWPLUGIN_INIT) { + bitSet(networkIndex_initialized, event->NetworkIndex); + } else if (Function == NWPlugin::Function::NWPLUGIN_EXIT) { + bitClear(networkIndex_initialized, event->NetworkIndex); + } + STOP_TIMER_NETWORK(networkDriverIndex, Function); return res; } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp index e5824e7a0..1054f9799 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp @@ -44,11 +44,13 @@ void doWiFiDisconnect() { if (doWifiIsSTA(WiFi.getMode())) { wifi_station_disconnect(); } + /* station_config conf{}; memset(&conf, 0, sizeof(conf)); ETS_UART_INTR_DISABLE(); wifi_station_set_config_current(&conf); ETS_UART_INTR_ENABLE(); + */ } bool doWifiIsAP(WiFiMode_t wifimode) { return (wifimode == WIFI_AP) || (wifimode == WIFI_AP_STA); } From c1bb5733aee937544c493a7df10455b3064cd258 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 7 May 2026 00:23:15 +0200 Subject: [PATCH 18/20] [ESP32S2] Fix using UART/USBCDC on ESP32S2 --- platformio_core_defs.ini | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 111ac0994..e528d1b28 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -187,8 +187,11 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform = https://github.com/Jason2866/platform-espressif32.git#IDF6 ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2403-0020-6.0/framework-arduinoespressif32-release_v6.0-1a97ddaa.tar.xz -platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc152 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2904-2115-5.5/framework-arduinoespressif32-release_v5.5-f2a3fa2b.tar.xz +;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc152 +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2904-2115-5.5/framework-arduinoespressif32-release_v5.5-f2a3fa2b.tar.xz + +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0405-1702-5.5/framework-arduinoespressif32-release_v5.5-f2a3fa2b.tar.xz custom_remove_include = true From f0d3b78bb4e829de14f898e642d1bf62f8d60bfa Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 7 May 2026 21:07:21 +0200 Subject: [PATCH 19/20] [WiFi] Fix some minor WiFi issues when saving settings (still 5min wait) --- lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp | 7 ++++++- lib/ESPEasySerial/Port_ESPEasySerial_USBCDC.cpp | 1 + src/ESPEasy/net/Helpers/_NWPlugin_init.cpp | 5 +++-- src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp | 1 + src/ESPEasy/net/wifi/ESPEasyWifi.cpp | 5 ++++- src/ESPEasy/net/wifi/ESPEasyWifi.h | 2 +- src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp | 3 ++- src/src/ESPEasyCore/ESPEasy_Console.cpp | 4 ---- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp index 3d4a695f0..1f96800f9 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp @@ -10,7 +10,12 @@ Port_ESPEasySerial_HardwareSerial_t::Port_ESPEasySerial_HardwareSerial_t() {} -Port_ESPEasySerial_HardwareSerial_t::~Port_ESPEasySerial_HardwareSerial_t() {} +Port_ESPEasySerial_HardwareSerial_t::~Port_ESPEasySerial_HardwareSerial_t() { + if (_serial != nullptr) { + _serial->flush(); + _serial->end(); + } +} void Port_ESPEasySerial_HardwareSerial_t::resetConfig(const ESPEasySerialConfig& config) { diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_USBCDC.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_USBCDC.cpp index c4e0bfaa0..7d901f2af 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_USBCDC.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_USBCDC.cpp @@ -128,6 +128,7 @@ Port_ESPEasySerial_USBCDC_t::Port_ESPEasySerial_USBCDC_t(const ESPEasySerialConf Port_ESPEasySerial_USBCDC_t::~Port_ESPEasySerial_USBCDC_t() { if (_serial != nullptr) { + _serial->flush(); _serial->end(); } } diff --git a/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp b/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp index 2f8639577..fe803fc98 100644 --- a/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp +++ b/src/ESPEasy/net/Helpers/_NWPlugin_init.cpp @@ -4,6 +4,7 @@ #include "../../../src/DataStructs/TimingStats.h" #include "../../../src/DataTypes/ESPEasy_plugin_functions.h" #include "../../../src/Globals/Settings.h" +#include "../../../src/Globals/ESPEasy_Scheduler.h" //#include "../../../src/Helpers/Misc.h" #include "../../../src/Helpers/StringConverter.h" #include "../Globals/NWPlugins.h" @@ -2204,10 +2205,10 @@ void NWPlugin_Exit_Init(networkIndex_t networkIndex) String dummy; // May need to call init later, so make sure exit is called first - NWPluginCall(NWPlugin::Function::NWPLUGIN_EXIT, &TempEvent, dummy); + Scheduler.setNetworkExitTimer(0, networkIndex); if (Settings.getNetworkEnabled(networkIndex)) { - NWPluginCall(NWPlugin::Function::NWPLUGIN_INIT, &TempEvent, dummy); + Scheduler.setNetworkInitTimer(0, networkIndex); } } } diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index f0316ddcb..c2dd51615 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -51,6 +51,7 @@ void ESPEasyWiFi_t::begin() { setState(WiFiState_e::IdleWaiting, 100); } else { if (WiFi_AP_Candidates.hasScanned()) { + // Has no candidates, but scan was performed. if (shouldStartAP_fallback()) { setState(WiFiState_e::AP_only, WIFI_STATE_MACHINE_AP_ONLY_TIMEOUT); } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index e41ab40cb..bfa43f8e9 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -188,7 +188,10 @@ void resetWiFi() { void initWiFi() { ESPEasyWiFi.setup(); } -void exitWiFi() { ESPEasyWiFi.disable(); } +void exitWiFi() { + ESPEasyWiFi.disable(); + WiFi_AP_Candidates.force_reload(); +} void loopWiFi() { ESPEasyWiFi.loop(); } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index 80ea2fc74..3098e6fd8 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -36,7 +36,7 @@ namespace wifi { # else # define WIFI_CONNECTION_CONSIDERED_STABLE 60000 // in milliSeconds # endif // if FEATURE_CUSTOM_PROVISIONING -# define WIFI_ALLOW_AP_AFTERBOOT_PERIOD 5 // in minutes +//# define WIFI_ALLOW_AP_AFTERBOOT_PERIOD 5 // in minutes # define WIFI_SCAN_INTERVAL_AP_USED 125000 // in milliSeconds # define WIFI_SCAN_INTERVAL_MINIMAL 60000 // in milliSeconds diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp index 1054f9799..0322ee0c5 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp @@ -42,7 +42,8 @@ bool WiFi_pre_STA_setup() { void doWiFiDisconnect() { // Only call disconnect when STA is active if (doWifiIsSTA(WiFi.getMode())) { - wifi_station_disconnect(); + WiFi.disconnect(true); +// wifi_station_disconnect(); } /* station_config conf{}; diff --git a/src/src/ESPEasyCore/ESPEasy_Console.cpp b/src/src/ESPEasyCore/ESPEasy_Console.cpp index 1acf0ac99..bb91b36bb 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console.cpp @@ -127,8 +127,6 @@ void EspEasy_Console_t::reInit() if (!mustHaveFallback) { if (_fallbackSerial._serial != nullptr) { - _fallbackSerial._serial->flush(); - _fallbackSerial._serial->end(); delete _fallbackSerial._serial; _fallbackSerial._serial = nullptr; somethingChanged = true; @@ -142,8 +140,6 @@ void EspEasy_Console_t::reInit() (_console_serial_txpin != Settings.console_serial_txpin) || !mustHaveSerial) { if (_mainSerial._serial != nullptr) { - _mainSerial._serial->flush(); - _mainSerial._serial->end(); delete _mainSerial._serial; _mainSerial._serial = nullptr; somethingChanged = true; From 6d814bb6ea194f985f5123075635b6d6d01b02d0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 8 May 2026 15:34:54 +0200 Subject: [PATCH 20/20] [WiFi] Fix WiFi reconnect + event duplicates on ESP8266 --- .../NWPluginData_static_runtime.cpp | 24 +++++++++---------- .../NWPluginData_static_runtime_ESP8266.cpp | 5 ++-- .../wifi/ESPEasyWiFi_STA_Event_ESP8266.cpp | 2 +- .../net/wifi/ESPEasyWiFi_state_machine.cpp | 16 +++++++++---- .../net/wifi/ESPEasyWifi_abstracted_ESP32.cpp | 2 +- src/src/DataStructs/EventQueue.cpp | 11 +++++++++ src/src/DataStructs/EventQueue.h | 5 ++++ src/src/Helpers/LongTermOnOffTimer.cpp | 13 +++++++++- src/src/Helpers/LongTermOnOffTimer.h | 6 +++++ src/src/Helpers/Scheduler_NetworkTimer.cpp | 3 ++- 10 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp index 9d85879d4..185e1a4c9 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.cpp @@ -99,12 +99,12 @@ bool NWPluginData_static_runtime::getTrafficCount(TX_RX_traffic_count& traffic) void NWPluginData_static_runtime::clear(networkIndex_t networkIndex) { - _connectedStats.clear(); - _gotIPStats.clear(); + _connectedStats.reset(); + _gotIPStats.reset(); #if FEATURE_USE_IPV6 - _gotIP6Stats.clear(); + _gotIP6Stats.reset(); #endif - _operationalStats.clear(); + _operationalStats.reset(); #if FEATURE_NETWORK_TRAFFIC_COUNT if (_netif) { @@ -268,17 +268,17 @@ void NWPluginData_static_runtime::processEvents() { if (_operationalStats.isOn()) { if (_isAP) { - eventQueue.add(F("WiFi#APmodeConnected")); + eventQueue.addDeDup(F("WiFi#APmodeConnected")); } else { - eventQueue.add(concat(_eventInterfaceName, F("#Connected"))); + eventQueue.addDeDup(concat(_eventInterfaceName, F("#Connected"))); } } else if (_operationalStats.isOff()) { if (_isAP) { - eventQueue.add(F("WiFi#APmodeDisconnected")); + eventQueue.addDeDup(F("WiFi#APmodeDisconnected")); } else { - eventQueue.add(concat(_eventInterfaceName, F("#Disconnected"))); + eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disconnected"))); } } } @@ -288,17 +288,17 @@ void NWPluginData_static_runtime::processEvents() if (_startStopStats.changedSinceLastCheck_and_clear() && Settings.UseRules) { if (_startStopStats.isOn()) { if (_isAP) { - eventQueue.add(F("WiFi#APmodeEnabled")); + eventQueue.addDeDup(F("WiFi#APmodeEnabled")); } else { - eventQueue.add(concat(_eventInterfaceName, F("#Enabled"))); + eventQueue.addDeDup(concat(_eventInterfaceName, F("#Enabled"))); } } else if (_startStopStats.isOff()) { if (_isAP) { - eventQueue.add(F("WiFi#APmodeDisabled")); + eventQueue.addDeDup(F("WiFi#APmodeDisabled")); } else { - eventQueue.add(concat(_eventInterfaceName, F("#Disabled"))); + eventQueue.addDeDup(concat(_eventInterfaceName, F("#Disabled"))); } } } diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index 32b27103b..dd8a23604 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -72,6 +72,9 @@ void NWPluginData_static_runtime::mark_lost_IP() void NWPluginData_static_runtime::mark_begin_establish_connection() { + _connectedStats.setOff(); + _operationalStats.setOff(); + if (!_isAP) { ESPEasy::net::wifi::setUseStaticIP(_useStaticIP); if (_useStaticIP) { @@ -86,8 +89,6 @@ void NWPluginData_static_runtime::mark_begin_establish_connection() } _establishConnectStats.forceSet(true); - _connectedStats.setOff(); - _operationalStats.setOff(); WiFi.hostname(NetworkCreateRFCCompliantHostname().c_str()); } diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP8266.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP8266.cpp index 25aff7ac1..c576d5afd 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP8266.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_STA_Event_ESP8266.cpp @@ -86,7 +86,7 @@ void ESPEasyWiFi_STA_EventHandler::onDisconnect(const WiFiEventStationModeDiscon if (WiFi.status() == WL_CONNECTED) { // See https://github.com/esp8266/Arduino/issues/5912 WiFi.persistent(false); - WiFi.disconnect(false); + WiFi.disconnect(true); delay(0); } } diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index c2dd51615..799b0b424 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -305,7 +305,7 @@ void ESPEasyWiFi_t::loop() /* if (Settings.UseRules) { - eventQueue.add(F("WiFi#Disconnected")); + eventQueue.addDeDup(F("WiFi#Disconnected")); } statusLED(false); */ @@ -336,7 +336,7 @@ void ESPEasyWiFi_t::disconnect() { doWiFiDisconnect(); } void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) { if (newState == _state) { return; } -# ifndef BUILD_NO_DEBUG +//# ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog( @@ -345,7 +345,7 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) { concat(F(" to: "), toString(newState)) + concat(F(" timeout: "), timeout)); } -# endif // ifndef BUILD_NO_DEBUG +//# endif // ifndef BUILD_NO_DEBUG if ((_state == WiFiState_e::AP_only) || (_state == WiFiState_e::AP_Fallback)) { @@ -361,7 +361,7 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) { wifi_STA_data->mark_disconnected(); if (WiFi.status() == WL_CONNECTED) { - WiFi.disconnect(); + WiFi.disconnect(true); } } } @@ -467,7 +467,7 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) { /* if (Settings.UseRules) { - eventQueue.add(F("WiFi#Connected")); + eventQueue.addDeDup(F("WiFi#Connected")); } statusLED(true); */ @@ -475,6 +475,12 @@ void ESPEasyWiFi_t::setState(WiFiState_e newState, uint32_t timeout) { } } + + auto wifi_STA_data = getWiFi_STA_NWPluginData_static_runtime(); + + if (wifi_STA_data) { + wifi_STA_data->processEvents(); + } } void ESPEasyWiFi_t::checkConnectProgress() {} diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp index 45c51c750..cb98c3b84 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP32.cpp @@ -213,7 +213,7 @@ bool doSetWifiMode(WiFiMode_t new_mode) const bool new_mode_AP_enabled = doWifiIsAP(new_mode); if (doWifiIsAP(cur_mode) && !new_mode_AP_enabled) { - eventQueue.add(F("WiFi#APmodeDisabled")); + eventQueue.addDeDup(F("WiFi#APmodeDisabled")); } if (doWifiIsAP(cur_mode) != new_mode_AP_enabled) { diff --git a/src/src/DataStructs/EventQueue.cpp b/src/src/DataStructs/EventQueue.cpp index bc60a85af..0f5605fcf 100644 --- a/src/src/DataStructs/EventQueue.cpp +++ b/src/src/DataStructs/EventQueue.cpp @@ -33,6 +33,17 @@ void EventQueueStruct::add(const __FlashStringHelper *event, bool deduplicate) add(str, deduplicate); } +void EventQueueStruct::addDeDup(const String& event) +{ + add(event, true); +} + +void EventQueueStruct::addDeDup(const __FlashStringHelper *event) +{ + add(event, true); +} + + void EventQueueStruct::addMove(String&& event, bool deduplicate) { if (!event.length()) { return; } diff --git a/src/src/DataStructs/EventQueue.h b/src/src/DataStructs/EventQueue.h index 644f8798d..89c3b64a1 100644 --- a/src/src/DataStructs/EventQueue.h +++ b/src/src/DataStructs/EventQueue.h @@ -17,6 +17,11 @@ struct EventQueueStruct { void add(const __FlashStringHelper *event, bool deduplicate = false); + void addDeDup(const String& event); + + void addDeDup(const __FlashStringHelper *event); + + void addMove(String&& event, bool deduplicate = false); diff --git a/src/src/Helpers/LongTermOnOffTimer.cpp b/src/src/Helpers/LongTermOnOffTimer.cpp index 9569c1ccd..2a50dfade 100644 --- a/src/src/Helpers/LongTermOnOffTimer.cpp +++ b/src/src/Helpers/LongTermOnOffTimer.cpp @@ -9,6 +9,12 @@ void LongTermOnOffTimer::clear() resetCount(); } +void LongTermOnOffTimer::reset() +{ + clear(); + _changedSinceLastCheck = false; +} + bool LongTermOnOffTimer::setOn() { if (isOn()) { return false; } @@ -43,7 +49,12 @@ bool LongTermOnOffTimer::set(bool onState) bool LongTermOnOffTimer::forceSet(bool onState) { - set(!onState); + /* + // TODO TD-er: Should we also change the timestamp? + if (onState) _onTimer.setNow(); + else _offTimer.setNow(); + */ + _changedSinceLastCheck = true; return set(onState); } diff --git a/src/src/Helpers/LongTermOnOffTimer.h b/src/src/Helpers/LongTermOnOffTimer.h index afe892ea4..acacbf075 100644 --- a/src/src/Helpers/LongTermOnOffTimer.h +++ b/src/src/Helpers/LongTermOnOffTimer.h @@ -6,8 +6,14 @@ class LongTermOnOffTimer { public: + // Clear all timers and counts + // _changedSinceLastCheck will be set when values were set before calling clear() void clear(); + // Reset to default + // _changedSinceLastCheck is also set to false + void reset(); + void resetCount() { _changeToOffCount = 0; _changeToOnCount = 0; } // Return true when state changed diff --git a/src/src/Helpers/Scheduler_NetworkTimer.cpp b/src/src/Helpers/Scheduler_NetworkTimer.cpp index 694b01b9a..adc4288b4 100644 --- a/src/src/Helpers/Scheduler_NetworkTimer.cpp +++ b/src/src/Helpers/Scheduler_NetworkTimer.cpp @@ -43,7 +43,8 @@ void ESPEasy_Scheduler::setNetworkTimer(unsigned long msecFromNow { if (!validNetworkIndex(networkIndex)) { return; } - if (!Settings.getNetworkEnabled(networkIndex)) { return; } + if (function != NWPlugin::Function::NWPLUGIN_EXIT && + !Settings.getNetworkEnabled(networkIndex)) { return; } const NWPluginTimerID timerID(networkIndex, Par1, function);