From d2af79edfd0a6aa91dd42d60a27855a3529f049e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 17 Aug 2025 22:02:32 +0200 Subject: [PATCH 01/29] [Storage] Add settings for External EEPROM --- src/src/CustomBuild/define_plugin_sets.h | 13 +++ src/src/DataStructs/DeviceStruct.h | 21 ++++- src/src/DataStructs/SettingsStruct.h | 14 +++- src/src/DataStructs_templ/SettingsStruct.cpp | 36 ++++++++- src/src/Helpers/ESPEasy_FactoryDefault.cpp | 2 +- src/src/WebServer/DevicesPage.cpp | 58 ++++++++------ src/src/WebServer/DevicesPage.h | 9 +++ src/src/WebServer/HardwarePage.cpp | 83 ++++++++++++++++++++ src/src/WebServer/I2C_Scanner.cpp | 12 ++- src/src/WebServer/Markup_Forms.cpp | 3 + 10 files changed, 219 insertions(+), 32 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 9a6a2bc2f2..7bb3660f74 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3769,6 +3769,19 @@ To create/register a plugin, you have to : #endif #endif // if FEATURE_TASKVALUE_ATTRIBUTES +#ifndef FEATURE_EEPROM_EXTERNAL + #ifdef ESP32 + #define FEATURE_EEPROM_EXTERNAL 1 + #endif + #ifdef ESP8266 + #ifdef LIMIT_BUILD_SIZE + #define FEATURE_EEPROM_EXTERNAL 0 // Disabled for limited builds on ESP8266 + #else + #define FEATURE_EEPROM_EXTERNAL 1 // Enabled by default on ESP8266 + #endif + #endif +#endif // ifndef FEATURE_EEPROM_EXTERNAL + //-------------------HTTPResponseParser Section---------------- #ifndef FEATURE_THINGSPEAK_EVENT #if defined(PLUGIN_BUILD_MAX_ESP32) diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 09385a420a..6fd3a2c0d6 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -44,9 +44,28 @@ #define I2C_PERIPHERAL_BUS_CLOCK 0 // bit-offset for I2C bus used for the RTC clock device #define I2C_PERIPHERAL_BUS_WDT 3 // bit-offset for I2C bus used for the watchdog timer #define I2C_PERIPHERAL_BUS_PCFMCP 6 // bit-offset for I2C bus used for PCF & MCP direct access -// #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? +#if FEATURE_EEPROM_EXTERNAL +#define I2C_PERIPHERAL_BUS_EEPROM 9 // bit-offset for I2C bus used for an external EEPROM +#endif // if FEATURE_EEPROM_EXTERNAL +// #define I2C_PERIPHERAL_BUS_??? 12 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE +#if FEATURE_EEPROM_EXTERNAL +#define EEPROM_EXTERNAL_FLAGS_ADDRESS 0 // bit-offset for the I2C Address (8 bits) +#define EEPROM_EXTERNAL_FLAGS_SIZE 8 // bit-offset for the size-id of the EEPROM (3 bits) +#define EEPROM_EXTERNAL_FLAGS_MUX 16 // bit-offset for the multiplexer flags of the EEPROM (16 bits) + +#define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) +#define EEPROM_MUX_FLAGS_MULTI 8 // bit-offset within multiplexerflags for bits or port (1 bit) + +enum EEPROMExternal_Type_e : uint8_t { + AT24C64 = 0, // + AT24C128 = 1, // + AT24C256 = 2, // + AT24C512 = 3, // + AT24C1024 = 4, // +}; +#endif // if FEATURE_EEPROM_EXTERNAL /*********************************************************************************************\ * DeviceStruct diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 8c2a31699e..20292a8504 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -343,8 +343,20 @@ class SettingsStruct_tmpl uint8_t getI2CInterfaceRTC() const; uint8_t getI2CInterfaceWDT() const; uint8_t getI2CInterfacePCFMCP() const; + #if FEATURE_EEPROM_EXTERNAL + uint8_t getI2CInterfaceEEPROM() const; + #endif // if FEATURE_EEPROM_EXTERNAL #endif // if FEATURE_I2C_MULTIPLE + #if FEATURE_EEPROM_EXTERNAL + uint8_t EEPROMExternalI2CAddress() const; + void EEPROMExternalI2CAddress(uint8_t address); + uint16_t EEPROMExternalI2CMultiplexerFlags() const; + void EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags); + uint8_t EEPROMExternalSize() const; + void EEPROMExternalSize(uint8_t size); + #endif + #if FEATURE_I2CMULTIPLEXER int8_t getI2CMultiplexerType(uint8_t i2cBus) const; int8_t getI2CMultiplexerAddr(uint8_t i2cBus) const; @@ -414,7 +426,7 @@ class SettingsStruct_tmpl uint8_t WebLogLevel = 0; uint8_t SDLogLevel = 0; unsigned long BaudRate = 115200; - unsigned long MessageDelay_unused = 0; // MQTT settings now moved to the controller settings. + unsigned long EEPROMExternalFlags = 0; uint8_t deepSleep_wakeTime = 0; // 0 = Sleep Disabled, else time awake from sleep in seconds boolean CustomCSS = false; boolean DST = false; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 060b702784..a7b1e6939f 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -658,7 +658,7 @@ void SettingsStruct_tmpl::clearMisc() { # endif // ifdef ESP32 } BaudRate = DEFAULT_SERIAL_BAUD; - MessageDelay_unused = 0; + EEPROMExternalFlags = 0; deepSleep_wakeTime = 0; CustomCSS = false; WDI2CAddress = 0; @@ -1132,8 +1132,42 @@ template uint8_t SettingsStruct_tmpl::getI2CInterfacePCFMCP() const { return get3BitFromUL(I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP); } + +#if FEATURE_EEPROM_EXTERNAL +template +uint8_t SettingsStruct_tmpl::getI2CInterfaceEEPROM() const { + return get3BitFromUL(I2C_peripheral_bus, I2C_PERIPHERAL_BUS_EEPROM); +} +#endif // if FEATURE_EEPROM_EXTERNAL #endif // if FEATURE_I2C_MULTIPLE +#if FEATURE_EEPROM_EXTERNAL +template +uint8_t SettingsStruct_tmpl::EEPROMExternalI2CAddress() const { + return get8BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS); +} +template +void SettingsStruct_tmpl::EEPROMExternalI2CAddress(uint8_t address) { + set8BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS, address); +} +template +uint16_t SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags() const { + return get16BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX); +} +template +void SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags) { + set16BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX, muxFlags); +} +template +uint8_t SettingsStruct_tmpl::EEPROMExternalSize() const { + return static_cast(get3BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); +} +template +void SettingsStruct_tmpl::EEPROMExternalSize(uint8_t size) { + set3BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, static_cast(size)); +} +#endif // if FEATURE_EEPROM_EXTERNAL + #if FEATURE_I2CMULTIPLEXER template int8_t SettingsStruct_tmpl::getI2CMultiplexerType(uint8_t i2cBus) const { diff --git a/src/src/Helpers/ESPEasy_FactoryDefault.cpp b/src/src/Helpers/ESPEasy_FactoryDefault.cpp index 77a98564da..cc9da7c8eb 100644 --- a/src/src/Helpers/ESPEasy_FactoryDefault.cpp +++ b/src/src/Helpers/ESPEasy_FactoryDefault.cpp @@ -294,7 +294,7 @@ void ResetFactory(bool formatFS) // Settings.UseRules = DEFAULT_USE_RULES; Settings.ControllerEnabled[0] = DEFAULT_CONTROLLER_ENABLED; Settings.MQTTRetainFlag_unused = DEFAULT_MQTT_RETAIN; - Settings.MessageDelay_unused = DEFAULT_MQTT_DELAY; + // Settings.MessageDelay_unused = DEFAULT_MQTT_DELAY; Settings.MQTTUseUnitNameAsClientId_unused = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID; // allow to set default latitude and longitude diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 35c3b0435d..70a2fc6c21 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -298,22 +298,11 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task #endif // if FEATURE_I2C_MULTIPLE # if FEATURE_I2CMULTIPLEXER - - if (isI2CMultiplexerEnabled(i2cBus)) { - int multipleMuxPortsOption = getFormItemInt(F("taskdeviceflags1"), 0); - bitWrite(flags, I2C_FLAGS_MUX_MULTICHANNEL, multipleMuxPortsOption == 1); - - if (multipleMuxPortsOption == 1) { - uint8_t selectedPorts = 0; - - for (int x = 0; x < I2CMultiplexerMaxChannels(i2cBus); ++x) { - bitWrite(selectedPorts, x, isFormItemChecked(concat(F("taskdeviceflag1ch"), x))); - } - Settings.I2C_Multiplexer_Channel[taskIndex] = selectedPorts; - } else { - Settings.I2C_Multiplexer_Channel[taskIndex] = getFormItemInt(F("taskdevicei2cmuxport"), 0); - } - } + bool muxPortsOption{}; + int selectedPorts{}; + GetI2CMultiplexerFromPage(i2cBus, muxPortsOption, selectedPorts); + bitWrite(flags, I2C_FLAGS_MUX_MULTICHANNEL, muxPortsOption); + Settings.I2C_Multiplexer_Channel[taskIndex] = selectedPorts; # endif // if FEATURE_I2CMULTIPLEXER @@ -1393,10 +1382,16 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } #endif // if FEATURE_I2C_MULTIPLE # if FEATURE_I2CMULTIPLEXER + ShowI2CMultiplexerUI(i2cBus, + bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL), + Settings.I2C_Multiplexer_Channel[taskIndex]); + # endif // if FEATURE_I2CMULTIPLEXER +} +# if FEATURE_I2CMULTIPLEXER +void ShowI2CMultiplexerUI(uint8_t i2cBus, bool muxPortsOption, int taskDeviceI2CMuxPort) { // Show selector for an I2C multiplexer port if a multiplexer is configured if (isI2CMultiplexerEnabled(i2cBus)) { - bool multipleMuxPorts = bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), @@ -1404,8 +1399,8 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex constexpr int i2c_mux_channelOptions[] = { 0, 1 }; int i2c_mux_channelCount = 1; - if (Settings.I2C_Multiplexer_Type == I2C_MULTIPLEXER_PCA9540) { - multipleMuxPorts = false; // force off + if (Settings.getI2CMultiplexerType(i2cBus) == I2C_MULTIPLEXER_PCA9540) { + muxPortsOption = false; // force off } else { i2c_mux_channelCount++; } @@ -1417,10 +1412,10 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex selector.addFormSelector( F("Multiplexer channels"), F("taskdeviceflags1"), - multipleMuxPorts ? 1 : 0); + muxPortsOption ? 1 : 0); } - if (multipleMuxPorts) { + if (muxPortsOption) { addRowLabel(F("Select connections"), EMPTY_STRING); html_table(EMPTY_STRING, false); // Sub-table html_table_header(F("Channel"), 100); @@ -1433,11 +1428,10 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex html_TD(); addHtml(concat(F("Channel "), x)); html_TD(); - addCheckBox(concat(F("taskdeviceflag1ch"), x), bitRead(Settings.I2C_Multiplexer_Channel[taskIndex], x), false); + addCheckBox(concat(F("taskdeviceflag1ch"), x), bitRead(taskDeviceI2CMuxPort, x), false); } html_end_table(); } else { - int taskDeviceI2CMuxPort = Settings.I2C_Multiplexer_Channel[taskIndex]; const uint32_t mux_max = I2CMultiplexerMaxChannels(i2cBus); String i2c_mux_portoptions[mux_max + 1]; int i2c_mux_portchoices[mux_max + 1]; @@ -1461,9 +1455,25 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex taskDeviceI2CMuxPort); } } - # endif // if FEATURE_I2CMULTIPLEXER } +void GetI2CMultiplexerFromPage(uint8_t i2cBus, bool &muxPortsOption, int &selectedPorts) { + if (isI2CMultiplexerEnabled(i2cBus)) { + muxPortsOption = getFormItemInt(F("taskdeviceflags1"), 0) == 1; + + if (muxPortsOption) { + selectedPorts = 0; + + for (int x = 0; x < I2CMultiplexerMaxChannels(i2cBus); ++x) { + bitWrite(selectedPorts, x, isFormItemChecked(concat(F("taskdeviceflag1ch"), x))); + } + } else { + selectedPorts = getFormItemInt(F("taskdevicei2cmuxport"), -1); + } + } +} +# endif // if FEATURE_I2CMULTIPLEXER + void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 8b7bece77b..7ad5690c9a 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -58,6 +58,15 @@ void devicePage_show_serial_config(taskIndex_t taskIndex); void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); +# if FEATURE_I2CMULTIPLEXER +void ShowI2CMultiplexerUI(uint8_t i2cBus, + bool muxPortsOption, + int taskDeviceI2CMuxPort); +void GetI2CMultiplexerFromPage(uint8_t i2cBus, + bool &muxPortsOption, + int &selectedPorts); +#endif // if FEATURE_I2CMULTIPLEXER + void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); #if FEATURE_PLUGIN_STATS diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 32e4d1a58e..c10a2330f1 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -25,6 +25,10 @@ #include "../Helpers/Hardware_device_info.h" #endif // if FEATURE_I2C_MULTIPLE +#if FEATURE_EEPROM_EXTERNAL +#include "../WebServer/DevicesPage.h" // For using ShowI2CMultiplexerUI() and GetI2CMultiplexerFromPage() +#endif // if FEATURE_EEPROM_EXTERNAL + // ******************************************************************************** // Web Interface hardware page // ******************************************************************************** @@ -38,6 +42,8 @@ void handle_hardware() { TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); + html_add_form(); + if (isFormItem(F("pled"))) { String error; Settings.Pin_status_led = getFormItemInt(F("pled")); @@ -106,7 +112,36 @@ void handle_hardware() { } #endif // if FEATURE_I2C_INTERFACE_3 set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); + + // EEPROM settings + # if FEATURE_EEPROM_EXTERNAL + const uint8_t i2cBus = getFormItemInt(F("pi2cbuseeprom"), 0); + set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_EEPROM, i2cBus); + # endif // if FEATURE_EEPROM_EXTERNAL + #endif // if FEATURE_I2C_MULTIPLE + + #if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + constexpr uint8_t i2cBus = 0; + #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + + #if FEATURE_EEPROM_EXTERNAL + Settings.EEPROMExternalSize(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); + Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); + + # if FEATURE_I2CMULTIPLEXER + + bool muxPortsOption{}; + int selectedPorts{}; + GetI2CMultiplexerFromPage(i2cBus, muxPortsOption, selectedPorts); + uint16_t muxFlags{}; + bitWrite(muxFlags, EEPROM_MUX_FLAGS_MULTI, muxPortsOption); + set8BitToUL(muxFlags, EEPROM_MUX_FLAGS_PORT, selectedPorts); + Settings.EEPROMExternalI2CMultiplexerFlags(muxFlags); + # endif // if FEATURE_I2CMULTIPLEXER + + #endif // if FEATURE_EEPROM_EXTERNAL + #ifdef ESP32 Settings.InitSPI = getFormItemInt(F("initspi"), static_cast(SPI_Options_e::None)); if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-define SPI GPIO pins @@ -280,6 +315,54 @@ void handle_hardware() { } #endif // if FEATURE_I2C_MULTIPLE + #if FEATURE_EEPROM_EXTERNAL + { + addFormSubHeader(F("External I2C EEPROM")); + const __FlashStringHelper*eepromOptions[] = { + F("AT24C128"), + F("AT24C256"), + F("AT24C512"), + F("AT24C1024"), + }; + const int eepromTypes[] = { + static_cast(EEPROMExternal_Type_e::AT24C128), + static_cast(EEPROMExternal_Type_e::AT24C256), + static_cast(EEPROMExternal_Type_e::AT24C512), + static_cast(EEPROMExternal_Type_e::AT24C1024), + }; + constexpr uint8_t eepromSizeCount = NR_ELEMENTS(eepromTypes); + FormSelectorOptions eepromSizeSelector(eepromSizeCount, eepromOptions, eepromTypes); + eepromSizeSelector.addFormSelector(F("EEPROM Model/size"), F("eepromtype"), Settings.EEPROMExternalSize()); + + const uint8_t i2cAddressValues[] = { 0, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57 }; + constexpr int nrAddressOptions = NR_ELEMENTS(i2cAddressValues); + + addFormSelectorI2C(F("i2c_eeprom"), nrAddressOptions, i2cAddressValues, Settings.EEPROMExternalI2CAddress()); + + #if FEATURE_I2C_MULTIPLE + const uint8_t i2cBus = Settings.getI2CInterfaceEEPROM(); + if (i2cMaxBusCount > 1) { + I2CInterfaceSelector(F("I2C Bus"), + F("pi2cbuseeprom"), + i2cBus, + false); + + } + #endif // if FEATURE_I2C_MULTIPLE + + #if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + constexpr uint8_t i2cBus = 0; + #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + + #if FEATURE_I2CMULTIPLEXER + const uint16_t eepromMux = Settings.EEPROMExternalI2CMultiplexerFlags(); + ShowI2CMultiplexerUI(i2cBus, + bitRead(eepromMux, EEPROM_MUX_FLAGS_MULTI), + get8BitFromUL(eepromMux, EEPROM_MUX_FLAGS_PORT)); // Re-used from DevicesPage + #endif // if FEATURE_I2CMULTIPLEXER + } + #endif // if FEATURE_EEPROM_EXTERNAL + // SPI Init addFormSubHeader(F("SPI Interface")); #ifdef ESP32 diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 7ca8c567eb..9e1759d9bd 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -315,19 +315,23 @@ String getKnownI2Cdevice(uint8_t address) { case 0x4D: result += F("PCF8591,MCP3221,LM75A,INA219"); break; + case 0x50: + case 0x52: + result += F("AT24Cxx"); + break; case 0x51: - result += F("PCF8563"); + result += F("PCF8563,AT24Cxx"); break; case 0x53: - result += F("ADXL345,LTR390"); + result += F("ADXL345,LTR390,AT24Cxx"); break; case 0x55: - result += F("DFRobot Rotary enc,BeFlE Moisture"); + result += F("DFRobot Rotary enc,BeFlE Moisture,AT24Cxx"); break; case 0x54: case 0x56: case 0x57: - result += F("DFRobot Rotary enc"); + result += F("DFRobot Rotary enc,AT24Cxx"); break; case 0x58: result += F("SGP30,GP8403"); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 4ab8d9b338..00e9c69dd6 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -504,6 +504,9 @@ void addFormSelectorI2C(const String& id, { String option = formatToHex_decimal(addresses[x]); + if (0 == addresses[x]) { + option = F("Disabled"); + } else if (((x == 0) && (defaultAddress == 0)) || (defaultAddress == addresses[x])) { option += F(" (default)"); } From b3102012221c74fcb4fd3ee276e3f1cc8e452da5 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 17 Aug 2025 22:36:40 +0200 Subject: [PATCH 02/29] [Storage] Add settings for External EEPROM --- src/src/Helpers/Misc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/Helpers/Misc.h b/src/src/Helpers/Misc.h index 0f94ffad4c..c3123f0b79 100644 --- a/src/src/Helpers/Misc.h +++ b/src/src/Helpers/Misc.h @@ -19,11 +19,13 @@ #define setNBitToUL(N, B, V, M) N=(((N) & ~((M) << (B))) | (static_cast((V) & (M)) << (B))) #define getNBitFromUL(number, bitnr, mask) (((number) >> (bitnr)) & (mask)) +#define set16BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFFFUL) #define set8BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFUL) #define set4BitToUL(N, B, V) setNBitToUL(N, B, V, 0x0FUL) #define set3BitToUL(N, B, V) setNBitToUL(N, B, V, 0x07UL) #define set2BitToUL(N, B, V) setNBitToUL(N, B, V, 0x03UL) +#define get16BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFFFUL) #define get8BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFUL) #define get4BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x0FUL) #define get3BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x07UL) From e3864f18916f96c2076259926fa18c7cd9122b34 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 18 Aug 2025 22:33:33 +0200 Subject: [PATCH 03/29] [Storage] Implement initializing EEPROM and writing UserVars, several improvements --- lib/AT24Cx/AT24CX.cpp | 345 ++++++++++++++++++ lib/AT24Cx/AT24CX.h | 122 +++++++ lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino | 128 +++++++ .../AT24CX_search_IC/AT24CX_search_IC.ino | 40 ++ lib/AT24Cx/LICENSE | 21 ++ lib/AT24Cx/LIZENZ | 25 ++ lib/AT24Cx/README.md | 61 ++++ src/src/DataStructs/DeviceStruct.h | 7 - src/src/DataStructs/SettingsStruct.h | 4 +- src/src/DataStructs_templ/SettingsStruct.cpp | 6 +- src/src/Helpers/EEPROMExternal.cpp | 114 ++++++ src/src/Helpers/EEPROMExternal.h | 50 +++ src/src/Helpers/ESPEasyRTC.cpp | 84 ++++- src/src/Helpers/ESPEasyRTC.h | 1 + src/src/Helpers/Hardware_I2C.cpp | 72 ++++ src/src/Helpers/Hardware_I2C.h | 5 + src/src/WebServer/HardwarePage.cpp | 29 +- 17 files changed, 1092 insertions(+), 22 deletions(-) create mode 100644 lib/AT24Cx/AT24CX.cpp create mode 100644 lib/AT24Cx/AT24CX.h create mode 100644 lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino create mode 100644 lib/AT24Cx/AT24CX_search_IC/AT24CX_search_IC.ino create mode 100644 lib/AT24Cx/LICENSE create mode 100644 lib/AT24Cx/LIZENZ create mode 100644 lib/AT24Cx/README.md create mode 100644 src/src/Helpers/EEPROMExternal.cpp create mode 100644 src/src/Helpers/EEPROMExternal.h diff --git a/lib/AT24Cx/AT24CX.cpp b/lib/AT24Cx/AT24CX.cpp new file mode 100644 index 0000000000..bca40187cd --- /dev/null +++ b/lib/AT24Cx/AT24CX.cpp @@ -0,0 +1,345 @@ +/** + +AT24CX.cpp +Library for using the EEPROM AT24C32/64 + +Copyright (c) 2014 Christian Paul + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + */ +#include "AT24CX.h" +#include + +/** + * Constructor with AT24Cx EEPROM at index 0 + */ +AT24CX::AT24CX() { + init(0, 32, 4096); +} + +/** + * Constructor with AT24Cx EEPROM at given index, size of page and max size in bytes + */ +AT24CX::AT24CX(uint8_t index, uint8_t pageSize, uint32_t maxSize) { + init(index, pageSize, maxSize); +} + +/** + * Constructor with AT24C32 EEPROM at index 0 + */ +AT24C32::AT24C32() { + init(0, 32, 4096); +} +/** + * Constructor with AT24Cx EEPROM at given index + */ +AT24C32::AT24C32(uint8_t index) { + init(index, 32, 4096); +} + +/** + * Constructor with AT24C64 EEPROM at index 0 + */ +AT24C64::AT24C64() { + init(0, 32, 8192); +} +/** + * Constructor with AT24C64 EEPROM at given index + */ +AT24C64::AT24C64(uint8_t index) { + init(index, 32, 8192); +} + +/** + * Constructor with AT24C128 EEPROM at index 0 + */ +AT24C128::AT24C128() { + init(0, 64, 16384); +} +/** + * Constructor with AT24C128 EEPROM at given index + */ +AT24C128::AT24C128(uint8_t index) { + init(index, 64, 16384); +} + +/** + * Constructor with AT24C256 EEPROM at index 0 + */ +AT24C256::AT24C256() { + init(0, 64, 32768); +} +/** + * Constructor with AT24C128 EEPROM at given index + */ +AT24C256::AT24C256(uint8_t index) { + init(index, 64, 32768); +} + +/** + * Constructor with AT24C512 EEPROM at index 0 + */ +AT24C512::AT24C512() { + init(0, 128, 65536); +} +/** + * Constructor with AT24C512 EEPROM at given index + */ +AT24C512::AT24C512(uint8_t index) { + init(index, 128, 65536); +} + +/** + * Init + */ +void AT24CX::init(uint8_t index, uint8_t pageSize, uint32_t maxSize) { + _id = AT24CX_ID | (index & 0x7); + _pageSize = pageSize; + _maxSize = maxSize; + // Wire.begin(); +} + +/** + * Check address not reached + */ +bool AT24CX::checkSize(uint32_t address, uint32_t size) { + return (address + size - 1) <= _maxSize; +} + +/** + * Write byte + */ +void AT24CX::write(uint32_t address, uint8_t data) { + if (!checkSize(address, 1)) { + return; + } + Wire.beginTransmission(_id); + if(Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + Wire.write(data); + Wire.endTransmission(); + delay(20); + } +} + +/** + * Write integer + */ +void AT24CX::writeInt(uint32_t address, uint16_t data) { + write(address, (uint8_t*)&data, 2); +} + +/** + * Write long + */ +void AT24CX::writeLong(uint32_t address, uint32_t data) { + write(address, (uint8_t*)&data, 4); +} + +/** + * Write float + */ +void AT24CX::writeFloat(uint32_t address, float data) { + write(address, (uint8_t*)&data, 4); +} + +/** + * Write double + */ +void AT24CX::writeDouble(uint32_t address, double data) { + write(address, (uint8_t*)&data, 8); +} + +/** + * Write chars + */ +void AT24CX::writeChars(uint32_t address, char *data, int length) { + write(address, (uint8_t*)data, length); +} + +/** + * Write bytes + */ +void AT24CX::writeBytes(uint32_t address, uint8_t *data, int length) { + write(address, data, length); +} + +/** + * Read integer + */ +uint16_t AT24CX::readInt(uint32_t address) { + memset(_b, 0, sizeof(_b)); + read(address, _b, 2); + return *(uint32_t*)&_b[0]; +} + +/** + * Read long + */ +uint32_t AT24CX::readLong(uint32_t address) { + read(address, _b, 4); + return *(unsigned long*)&_b[0]; +} + +/** + * Read float + */ +float AT24CX::readFloat(uint32_t address) { + read(address, _b, 4); + return *(float*)&_b[0]; +} + +/** + * Read double + */ +double AT24CX::readDouble(uint32_t address) { + read(address, _b, 8); + return *(double*)&_b[0]; +} + +/** + * Read chars + */ +void AT24CX::readChars(uint32_t address, char *data, int n) { + read(address, (uint8_t*)data, n); +} + +/** + * Read bytes + */ +void AT24CX::readBytes(uint32_t address, uint8_t *data, int n) { + read(address, data, n); +} + +/** + * Write sequence of n bytes + */ +void AT24CX::write(uint32_t address, uint8_t *data, int n) { + if (!checkSize(address, n)) { + return; + } + // status quo + int c = n; // bytes left to write + int offD = 0; // current offset in data pointer + int offP; // current offset in page + int nc = 0; // next n bytes to write + + // write alle bytes in multiple steps + while (c > 0) { + // calc offset in page + offP = address % _pageSize; + // maximal 30 bytes to write + nc = min(min(c, 30), _pageSize - offP); + write(address, data, offD, nc); + c-=nc; + offD+=nc; + address+=nc; + } +} + +/** + * Write sequence of n bytes from offset + */ +void AT24CX::write(uint32_t address, uint8_t *data, int offset, int n) { + if (!checkSize(address, n)) { + return; + } + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + uint8_t *adr = data+offset; + Wire.write(adr, n); + Wire.endTransmission(); + delay(20); + } +} + +/** + * Read byte + */ +uint8_t AT24CX::read(uint32_t address) { + if (!checkSize(address, 1)) { + return 0; + } + uint8_t b = 0; + int r = 0; + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + if (Wire.endTransmission()==0) { + Wire.requestFrom(_id, 1); + while (Wire.available() > 0 && r<1) { + b = (uint8_t)Wire.read(); + r++; + } + } + } + return b; +} + +/** + * Read sequence of n bytes + */ +void AT24CX::read(uint32_t address, uint8_t *data, int n) { + if (!checkSize(address, n)) { + return; + } + int c = n; + int offD = 0; + // read until are n bytes read + while (c > 0) { + // read maximal 32 bytes + int nc = c; + if (nc > 32) + nc = 32; + read(address, data, offD, nc); + address+=nc; + offD+=nc; + c-=nc; + } +} + + +/** + * Read sequence of n bytes to offset + */ +void AT24CX::read(uint32_t address, uint8_t *data, int offset, int n) { + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + if (Wire.endTransmission()==0) { + int r = 0; + Wire.requestFrom(_id, n); + while (Wire.available() > 0 && r +#include + +// // byte +// typedef uint8_t byte; + +// AT24Cx I2C adress +// 80 +// 0x50 +#define AT24CX_ID 0b01010000 + +// general class definition +class AT24CX { +public: + AT24CX(); + AT24CX(uint8_t index, uint8_t pageSize, uint32_t maxSize); + void write(uint32_t address, uint8_t data); + void write(uint32_t address, uint8_t *data, int n); + void writeInt(uint32_t address, uint16_t data); + void writeLong(uint32_t address, uint32_t data); + void writeFloat(uint32_t address, float data); + void writeDouble(uint32_t address, double data); + void writeChars(uint32_t address, char *data, int length); + void writeBytes(uint32_t address, uint8_t *data, int length); + uint8_t read(uint32_t address); + void read(uint32_t address, uint8_t *data, int n); + uint16_t readInt(uint32_t address); + uint32_t readLong(uint32_t address); + float readFloat(uint32_t address); + double readDouble(uint32_t address); + void readChars(uint32_t address, char *data, int n); + void readBytes(uint32_t address, uint8_t *data, int n); +protected: + void init(uint8_t index, uint8_t pageSize, uint32_t maxSize); +private: + void read(uint32_t address, uint8_t *data, int offset, int n); + void write(uint32_t address, uint8_t *data, int offset, int n); + bool checkSize(uint32_t address, uint32_t size); + int _id; + uint8_t _b[8]; + uint8_t _pageSize; + uint32_t _maxSize{}; +}; + +// AT24C32 class definiton +class AT24C32 : public AT24CX { +public: + AT24C32(); + AT24C32(uint8_t index); +}; + +// AT24C64 class definiton +class AT24C64 : public AT24CX { +public: + AT24C64(); + AT24C64(uint8_t index); +}; + +// AT24C128 class definiton +class AT24C128 : public AT24CX { +public: + AT24C128(); + AT24C128(uint8_t index); +}; + +// AT24C256 class definiton +class AT24C256 : public AT24CX { +public: + AT24C256(); + AT24C256(uint8_t index); +}; + +// AT24C512 class definiton +class AT24C512 : public AT24CX { +public: + AT24C512(); + AT24C512(uint8_t index); +}; + + + +#endif diff --git a/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino b/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino new file mode 100644 index 0000000000..b49cadab5c --- /dev/null +++ b/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino @@ -0,0 +1,128 @@ +/* +* +* Read and write demo of the AT24CX library +* Written by Christian Paul, 2014-11-24 +* +* +*/ + +// include libraries +#include +#include + +// EEPROM object +AT24CX mem; + +// setup +void setup() { + // serial init + Serial.begin(115200); + Serial.println("AT24CX read/write demo"); + Serial.println("----------------------"); +} + +// main loop +void loop() { + // read and write byte + Serial.println("Write 42 to address 12"); + mem.write(12, 42); + Serial.println("Read byte from address 12 ..."); + byte b = mem.read(12); + Serial.print("... read: "); + Serial.println(b, DEC); + Serial.println(); + + // read and write integer + Serial.println("Write 65000 to address 15"); + mem.writeInt(15, 65000); + Serial.println("Read integer from address 15 ..."); + unsigned int i = mem.readInt(15); + Serial.print("... read: "); + Serial.println(i, DEC); + Serial.println(); + + // read and write long + Serial.println("Write 3293732729 to address 20"); + mem.writeLong(20, 3293732729UL); + Serial.println("Read long from address 20 ..."); + unsigned long l = mem.readLong(20); + Serial.print("... read: "); + Serial.println(l, DEC); + Serial.println(); + + // read and write long + Serial.println("Write 1111111111 to address 31"); + mem.writeLong(31, 1111111111); + Serial.println("Read long from address 31 ..."); + unsigned long l2 = mem.readLong(31); + Serial.print("... read: "); + Serial.println(l2, DEC); + Serial.println(); + + // read and write float + Serial.println("Write 3.14 to address 40"); + mem.writeFloat(40, 3.14); + Serial.println("Read float from address 40 ..."); + float f = mem.readFloat(40); + Serial.print("... read: "); + Serial.println(f, DEC); + Serial.println(); + + // read and write double + Serial.println("Write 3.14159265359 to address 50"); + mem.writeDouble(50, 3.14159265359); + Serial.println("Read double from address 50 ..."); + double d = mem.readDouble(50); + Serial.print("... read: "); + Serial.println(d, DEC); + Serial.println(); + + // read and write char + Serial.print("Write chars: '"); + char msg[] = "This is a message"; + Serial.print(msg); + Serial.println("' to address 200"); + mem.writeChars(200, msg, sizeof(msg)); + Serial.println("Read chars from address 200 ..."); + char msg2[30]; + mem.readChars(200, msg2, sizeof(msg2)); + Serial.print("... read: '"); + Serial.print(msg2); + Serial.println("'"); + Serial.println(); + + // write array of bytes + Serial.println("Write array of 80 bytes at address 1000"); + byte xy[] = {0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9, // 10 x 3 = 30 + 10,11,12,13,14,15,16,17,18,19, // 10 + 120,121,122,123,124,125,126,127,128,129, // 10 + 130,131,132,133,134,135,136,137,138,139, // 10 + 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219}; // 20 + mem.write(1000, (byte*)xy, sizeof(xy)); + + // read bytes with multiple steps + Serial.println("Read 80 single bytes starting at address 1000"); + for (int i=0; i for definitons and differences. + +Written by Christian Paul, 2014 - 2015. +This software is released under the terms of the MIT license. +See the file LICENSE or LIZENZ for details, please. + +You can use any of the eight possibles EEPROM devices on the I2C bus. + +Constructor + + AT24CX(byte pageSize); + +uses the device with index 0 and given page size. You can select a device with given index between 0 and 8 with constructor + + AT24CX(byte index, byte pageSize); + +Than, you can single write or read single bytes from the EEPROM with + + void write(unsigned int address, byte data); + byte read(unsigned int address); + +or write and read an array of bytes with + + void write(unsigned int address, byte *data, int n); + void read(unsigned int address, byte *data, int n); + +For writing integers, long, float, double or sequences of chars you can use the comfort functions + + void writeInt(unsigned int address, unsigned int data); + void writeLong(unsigned int address, unsigned long data); + void writeFloat(unsigned int address, float data); + void writeDouble(unsigned int address, double data); + void writeChars(unsigned int address, char *data, int length); + +Reading the values is done by using + + unsigned int readInt(unsigned int address); + unsigned long readLong(unsigned int address); + float readFloat(unsigned int address); + double readDouble(unsigned int address); + void readChars(unsigned int address, char *data, int n); + +Alternative you can use the individual classes with predefined page sizes: + + AT24C32(); + AT24C64(); + AT24C128(); + AT24C256(); + AT24C512(); + +or with different index than 0: + + AT24C32(byte index); + AT24C64(byte index); + AT24C128(byte index); + AT24C256(byte index); + AT24C512(byte index); + diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 6fd3a2c0d6..8762fcf8ce 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -58,13 +58,6 @@ #define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) #define EEPROM_MUX_FLAGS_MULTI 8 // bit-offset within multiplexerflags for bits or port (1 bit) -enum EEPROMExternal_Type_e : uint8_t { - AT24C64 = 0, // - AT24C128 = 1, // - AT24C256 = 2, // - AT24C512 = 3, // - AT24C1024 = 4, // -}; #endif // if FEATURE_EEPROM_EXTERNAL /*********************************************************************************************\ diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 20292a8504..6610f15516 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -353,8 +353,8 @@ class SettingsStruct_tmpl void EEPROMExternalI2CAddress(uint8_t address); uint16_t EEPROMExternalI2CMultiplexerFlags() const; void EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags); - uint8_t EEPROMExternalSize() const; - void EEPROMExternalSize(uint8_t size); + uint8_t EEPROMExternalType() const; + void EEPROMExternalType(uint8_t type); #endif #if FEATURE_I2CMULTIPLEXER diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index a7b1e6939f..7f64bb962f 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1159,12 +1159,12 @@ void SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags(uint16_t mu set16BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX, muxFlags); } template -uint8_t SettingsStruct_tmpl::EEPROMExternalSize() const { +uint8_t SettingsStruct_tmpl::EEPROMExternalType() const { return static_cast(get3BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); } template -void SettingsStruct_tmpl::EEPROMExternalSize(uint8_t size) { - set3BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, static_cast(size)); +void SettingsStruct_tmpl::EEPROMExternalType(uint8_t type) { + set3BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); } #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp new file mode 100644 index 0000000000..3a05b2bfe0 --- /dev/null +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -0,0 +1,114 @@ +#include "../Helpers/EEPROMExternal.h" +#include "../Globals/Settings.h" +#include "../Helpers/I2C_access.h" +#include "../../ESPEasy_common.h" + +#if FEATURE_EEPROM_EXTERNAL + +AT24CX *EEPROMExternal = nullptr; + +/** + * Switch to I2C Bus and multiplexer channel of External EEPROM + */ +uint8_t selectEEPROMI2CBusAndMultiplexer() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if (eepromAddress > 0) { // EEPROM Configured? + # if FEATURE_I2C_MULTIPLE + const uint8_t i2cBus = Settings.getI2CInterfaceEEPROM(); + # else // if FEATURE_I2C_MULTIPLE + constexpr uint8_t i2cBus = 0; + # endif // if FEATURE_I2C_MULTIPLE + + I2CSelectHighClockSpeed(i2cBus); + + # if FEATURE_I2CMULTIPLEXER + const uint16_t eepromFlags = Settings.EEPROMExternalI2CMultiplexerFlags(); + const int eepromMuxPort = get8BitFromUL(eepromFlags, EEPROM_MUX_FLAGS_PORT); + const bool eepromMulti = bitRead(eepromFlags, EEPROM_MUX_FLAGS_MULTI); + I2CMultiplexerSelectByBusAndMux(i2cBus, eepromMulti, eepromMuxPort); + # endif // if FEATURE_I2CMULTIPLEXER + + if (0 == I2C_wakeup(eepromAddress)) { + return eepromAddress; + } + } + return 0; +} + +uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { + switch (type) { + case EEPROMExternal_Type_e::AT24C256: + return 32768; + case EEPROMExternal_Type_e::AT24C512: + return 65536; + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + return 131072; + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + return 262144; + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + return 4096; + case EEPROMExternal_Type_e::AT24C64: + return 8192; + case EEPROMExternal_Type_e::AT24C128: + return 16384; + } + return 0; +} + +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize) { + pageSize = 0; + + switch (type) { + case EEPROMExternal_Type_e::AT24C256: + pageSize = 64; + case EEPROMExternal_Type_e::AT24C512: + pageSize = 128; + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + pageSize = 128; + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + pageSize = 128; + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + pageSize = 32; + case EEPROMExternal_Type_e::AT24C64: + pageSize = 32; + case EEPROMExternal_Type_e::AT24C128: + pageSize = 64; + } + return getEEPROMSize(type); +} + +const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { + switch (type) { + case EEPROMExternal_Type_e::AT24C256: + return F("AT24C256"); + case EEPROMExternal_Type_e::AT24C512: + return F("AT24C512"); + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + return F("AT24C1024"); + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + return F("AT24C2048"); + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + return F("AT24C32"); + case EEPROMExternal_Type_e::AT24C64: + return F("AT24C64"); + case EEPROMExternal_Type_e::AT24C128: + return F("AT24C128"); + } + return F(""); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h new file mode 100644 index 0000000000..b0f71ec444 --- /dev/null +++ b/src/src/Helpers/EEPROMExternal.h @@ -0,0 +1,50 @@ +#pragma once +#include "../../ESPEasy_common.h" + +#if FEATURE_EEPROM_EXTERNAL + +# include + +extern AT24CX *EEPROMExternal; + +// Start writing the base RTC struct from this offset (not currently saving this to EEPROM) // TODO +# define EEPROM_BASERTC_START_OFFSET (0) + +// Start writing the UserVar values from this offset, should be > sizeof(RTCStruct) that is 32 currently +# define EEPROM_USERVAR_START_OFFSET (100) + +// Write the UserVar-checksum from this offset, right after the UserVar values +# define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) + +// Offset for storing GPIO states // TODO +# define EEPROM_GPIO_MCPPCF_START_OFFSET (1024) + +// Choose an arbitrary but fixed offset +# define EEPROM_CUSTOM_START_OFFSET (2048) + +// Enable/disable some models +# define EEPROM_SUPPORT_AT24C1024 1 +# define EEPROM_SUPPORT_AT24C2048 0 + +// Supported AT24Cxxx devices +enum class EEPROMExternal_Type_e : uint8_t { + AT24C256 = 0, // Default, 32 kB + AT24C512 = 1, // 64 kB + # if EEPROM_SUPPORT_AT24C1024 + AT24C1024 = 2, // 128 kB + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + AT24C2048 = 3, // 256 kB (not supported yet) + # endif // if EEPROM_SUPPORT_AT24C2048 + AT24C32 = 4, // 4 kB, not endorsed, but widely available types: + AT24C64 = 5, // 8 kB + AT24C128 = 6, // 16 kB +}; + +uint8_t selectEEPROMI2CBusAndMultiplexer(); + +uint32_t getEEPROMSize(EEPROMExternal_Type_e type); +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize); +const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 1c501d13cd..65a18d6486 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -12,6 +12,12 @@ #include "../Helpers/CRC_functions.h" #include "../../ESPEasy_common.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../Helpers/EEPROMExternal.h" +#include "../Helpers/Hardware_I2C.h" +#include "../Helpers/StringConverter.h" +#endif // if FEATURE_EEPROM_EXTERNAL + #ifdef ESP8266 #include #endif @@ -81,6 +87,11 @@ // - RTCStruct to keep information on reboot reason, last used WiFi, etc. // - UserVar to keep task values persistent just like on ESP8266 +/** + * With EEPROMExternal (AT24cxxx) enabled and configured: + * - UserVar will be stored in external EEPROM + * - + */ @@ -134,7 +145,7 @@ void initRTC() saveToRTC(); UserVar.clear(); - saveUserVarToRTC(); + saveUserVarToRTC(true); } /********************************************************************************************\ @@ -158,8 +169,59 @@ bool readFromRTC() /********************************************************************************************\ Save values to RTC memory \*********************************************************************************************/ -bool saveUserVarToRTC() +bool saveUserVarToRTC() { + return saveUserVarToRTC(false); +} + +bool saveUserVarToRTC(bool initial) { + #if FEATURE_EEPROM_EXTERNAL + // Check if we have an external EEPROM available on the configured I2C bus & channel, and save all task values there + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + if (!initial && (nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? + + if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + uint32_t eepromWritten{}; + uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + + if (UserVar.compute_CRC32() != checksum) { // Only save if data changed + for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { + const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); + if (taskValues != nullptr) { + for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { + const size_t index = (task * VARS_PER_TASK) + varNr; + EEPROMExternal->writeLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof(uint32_t)), + taskValues->getUint32(varNr)); + eepromWritten += sizeof(uint32_t); + } + } + } + EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, + UserVar.compute_CRC32()); + eepromWritten += sizeof(uint32_t); + } + + #ifndef BUILD_NO_DEBUG + // if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { // FIXME + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: UserVar: %u bytes written to %s"), eepromWritten, FsP(getEEPROMName(eepromType)))); + } + #endif // ifndef BUILD_NO_DEBUG + + } + #if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + #if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + #else //if FEATURE_I2C_MULTIPLE + 0 + #endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + #endif // if FEATURE_I2CMULTIPLEXER + } + #endif // if FEATURE_EEPROM_EXTERNAL + // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. #if defined(ESP32) @@ -168,7 +230,6 @@ bool saveUserVarToRTC() if (taskValues != nullptr) { for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { const size_t index = (task * VARS_PER_TASK) + varNr; - constexpr bool raw = true; UserVar_RTC[index] = taskValues->getUint32(varNr); } } @@ -193,6 +254,23 @@ bool saveUserVarToRTC() \*********************************************************************************************/ bool readUserVarFromRTC() { + // const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + // if ((nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? + + // if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // // TODO Check checksum and if correct, restore UserVar values + // } + // #if FEATURE_I2CMULTIPLEXER + // I2CMultiplexerOff( + // #if FEATURE_I2C_MULTIPLE + // Settings.getI2CInterfaceEEPROM() + // #else //if FEATURE_I2C_MULTIPLE + // 0 + // #endif // if FEATURE_I2C_MULTIPLE + // ); // Restore the Multiplexer channel + // #endif // if FEATURE_I2CMULTIPLEXER + // } + // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. #if defined(ESP32) diff --git a/src/src/Helpers/ESPEasyRTC.h b/src/src/Helpers/ESPEasyRTC.h index d98182f83d..58bdf5269c 100644 --- a/src/src/Helpers/ESPEasyRTC.h +++ b/src/src/Helpers/ESPEasyRTC.h @@ -17,6 +17,7 @@ bool readFromRTC(); Save values to RTC memory \*********************************************************************************************/ bool saveUserVarToRTC(); +bool saveUserVarToRTC(bool initial); /********************************************************************************************\ Read RTC struct from RTC memory diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index 733ecb4445..34e31ac72a 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -7,6 +7,9 @@ #include "../Helpers/I2C_access.h" #include "../Helpers/StringConverter.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL #include @@ -81,6 +84,55 @@ void initI2C() { } } } + + #if FEATURE_EEPROM_EXTERNAL + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if ((nullptr != EEPROMExternal) && (eepromAddress == 0)) { // Cleanup when turning off EEPROM + delete EEPROMExternal; + EEPROMExternal = nullptr; + } + + if ((nullptr == EEPROMExternal) && (eepromAddress > 0)) { + const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); + if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // We have an I2C device at this address, let's assume it's an EEPROM... + uint8_t pageSize = 0; + const uint32_t eepromSize = getEEPROMSize(eepromType, pageSize); + EEPROMExternal = new (std::nothrow) AT24CX(eepromAddress, pageSize, eepromSize); + + if (nullptr != EEPROMExternal) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: %s initialized at address 0x%02x"), + FsP(getEEPROMName(eepromType)), + eepromAddress)); + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: Initialization of %s failed"), + FsP(getEEPROMName(eepromType)))); + } + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: No %s found at address 0x%02x"), + FsP(getEEPROMName(eepromType)), + eepromAddress)); + } + } + + #if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + #if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + #else //if FEATURE_I2C_MULTIPLE + 0 + #endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + #endif // if FEATURE_I2CMULTIPLEXER + } + #endif // if FEATURE_EEPROM_EXTERNAL + I2CSelectHighClockSpeed(0); // Select first interface by default } @@ -247,6 +299,26 @@ uint8_t I2CMultiplexerShiftBit(uint8_t i2cBus, uint8_t i) { return toWrite; } +void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, bool singleMulti, int muxPort) { + uint8_t toWrite{}; + + if ((singleMulti && (muxPort > 0))|| + (!singleMulti && (muxPort > -1))) { + if (!singleMulti) { + uint8_t i = muxPort; + + if (i < 8) { + toWrite = I2CMultiplexerShiftBit(i2cBus, i); + } + } else { + toWrite = muxPort; // Bitpattern is already correctly stored + } + } + + SetI2CMultiplexer(i2cBus, toWrite); + +} + // As initially constructed by krikk in PR#254, quite adapted // utility method for the I2C multiplexer // select the multiplexer port given as parameter, if taskIndex < 0 then take that abs value as the port to select (to allow I2C scanner) diff --git a/src/src/Helpers/Hardware_I2C.h b/src/src/Helpers/Hardware_I2C.h index 22b3d3947b..3f060f9719 100644 --- a/src/src/Helpers/Hardware_I2C.h +++ b/src/src/Helpers/Hardware_I2C.h @@ -22,6 +22,11 @@ void I2CBegin(int8_t sda, #if FEATURE_I2CMULTIPLEXER bool isI2CMultiplexerEnabled(uint8_t i2cBus); +uint8_t I2CMultiplexerShiftBit(uint8_t i2cBus, uint8_t i); + +void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, + bool singleMulti, + int muxPort); void I2CMultiplexerSelectByTaskIndex(taskIndex_t taskIndex); void I2CMultiplexerSelect(uint8_t i2cBus, uint8_t i); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c10a2330f1..dbae93a3b5 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -26,6 +26,7 @@ #endif // if FEATURE_I2C_MULTIPLE #if FEATURE_EEPROM_EXTERNAL +#include "../Helpers/EEPROMExternal.h" #include "../WebServer/DevicesPage.h" // For using ShowI2CMultiplexerUI() and GetI2CMultiplexerFromPage() #endif // if FEATURE_EEPROM_EXTERNAL @@ -126,7 +127,7 @@ void handle_hardware() { #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL #if FEATURE_EEPROM_EXTERNAL - Settings.EEPROMExternalSize(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); + Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); # if FEATURE_I2CMULTIPLEXER @@ -319,20 +320,34 @@ void handle_hardware() { { addFormSubHeader(F("External I2C EEPROM")); const __FlashStringHelper*eepromOptions[] = { - F("AT24C128"), - F("AT24C256"), - F("AT24C512"), - F("AT24C1024"), + getEEPROMName(EEPROMExternal_Type_e::AT24C256), + getEEPROMName(EEPROMExternal_Type_e::AT24C512), + #if EEPROM_SUPPORT_AT24C1024 + getEEPROMName(EEPROMExternal_Type_e::AT24C1024), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(EEPROMExternal_Type_e::AT24C2048), + #endif // if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(EEPROMExternal_Type_e::AT24C32), + getEEPROMName(EEPROMExternal_Type_e::AT24C64), + getEEPROMName(EEPROMExternal_Type_e::AT24C128), }; const int eepromTypes[] = { - static_cast(EEPROMExternal_Type_e::AT24C128), static_cast(EEPROMExternal_Type_e::AT24C256), static_cast(EEPROMExternal_Type_e::AT24C512), + #if EEPROM_SUPPORT_AT24C1024 static_cast(EEPROMExternal_Type_e::AT24C1024), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + static_cast(EEPROMExternal_Type_e::AT24C2048), + #endif // if EEPROM_SUPPORT_AT24C2048 + static_cast(EEPROMExternal_Type_e::AT24C32), + static_cast(EEPROMExternal_Type_e::AT24C64), + static_cast(EEPROMExternal_Type_e::AT24C128), }; constexpr uint8_t eepromSizeCount = NR_ELEMENTS(eepromTypes); FormSelectorOptions eepromSizeSelector(eepromSizeCount, eepromOptions, eepromTypes); - eepromSizeSelector.addFormSelector(F("EEPROM Model/size"), F("eepromtype"), Settings.EEPROMExternalSize()); + eepromSizeSelector.addFormSelector(F("EEPROM Model/size"), F("eepromtype"), Settings.EEPROMExternalType()); const uint8_t i2cAddressValues[] = { 0, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57 }; constexpr int nrAddressOptions = NR_ELEMENTS(i2cAddressValues); From 97d1308cc8598bae66df7a3b8a2459c2cfaa9bb5 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 19 Aug 2025 13:16:12 +0200 Subject: [PATCH 04/29] [Storage] Reduce writing to only when data changes, add FRAM to I2C scanner, code optimizations --- src/src/Helpers/EEPROMExternal.cpp | 23 ++++++++++++++--------- src/src/Helpers/ESPEasyRTC.cpp | 20 +++++++++++++++----- src/src/WebServer/I2C_Scanner.cpp | 13 ++++++++----- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index 3a05b2bfe0..a1f31f2f5e 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -88,27 +88,32 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, } const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { + # ifndef BUILD_NO_DEBUG + switch (type) { case EEPROMExternal_Type_e::AT24C256: - return F("AT24C256"); + return F("AT24C256/MB85RC256"); case EEPROMExternal_Type_e::AT24C512: - return F("AT24C512"); - # if EEPROM_SUPPORT_AT24C1024 + return F("AT24C512/MB85RC512"); + # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: - return F("AT24C1024"); - # endif // if EEPROM_SUPPORT_AT24C1024 - # if EEPROM_SUPPORT_AT24C2048 + return F("AT24C1024/MB85RC1M"); + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: return F("AT24C2048"); - # endif // if EEPROM_SUPPORT_AT24C2048 + # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: return F("AT24C32"); case EEPROMExternal_Type_e::AT24C64: - return F("AT24C64"); + return F("AT24C64/MB85RC64"); case EEPROMExternal_Type_e::AT24C128: - return F("AT24C128"); + return F("AT24C128/MB85RC128"); } return F(""); + # else // ifndef BUILD_NO_DEBUG + return F("EEPROM/FRAM"); + # endif // ifndef BUILD_NO_DEBUG } #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 65a18d6486..cddac3bbd4 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -99,6 +99,7 @@ //#define RTC_STRUCT_DEBUG +constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); #ifdef ESP32 constexpr size_t UserVar_nrelements = VARS_PER_TASK * TASKS_MAX; @@ -181,8 +182,10 @@ bool saveUserVarToRTC(bool initial) if (!initial && (nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + #ifndef BUILD_NO_DEBUG uint32_t eepromWritten{}; - uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + #endif // ifndef BUILD_NO_DEBUG + const uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); if (UserVar.compute_CRC32() != checksum) { // Only save if data changed for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { @@ -190,15 +193,22 @@ bool saveUserVarToRTC(bool initial) if (taskValues != nullptr) { for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { const size_t index = (task * VARS_PER_TASK) + varNr; - EEPROMExternal->writeLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof(uint32_t)), - taskValues->getUint32(varNr)); - eepromWritten += sizeof(uint32_t); + const uint32_t newData = taskValues->getUint32(varNr); // Only update EEPROM is data differs + if (newData != EEPROMExternal->readLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof_uint32_t))) { + EEPROMExternal->writeLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof_uint32_t), + newData); + #ifndef BUILD_NO_DEBUG + eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG + } } } } EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, UserVar.compute_CRC32()); - eepromWritten += sizeof(uint32_t); + #ifndef BUILD_NO_DEBUG + eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG } #ifndef BUILD_NO_DEBUG diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 9e1759d9bd..5e3145ffdc 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -317,21 +317,21 @@ String getKnownI2Cdevice(uint8_t address) { break; case 0x50: case 0x52: - result += F("AT24Cxx"); + result += F("AT24Cxx,MB85RCxx"); break; case 0x51: - result += F("PCF8563,AT24Cxx"); + result += F("PCF8563,AT24Cxx,MB85RCxx"); break; case 0x53: - result += F("ADXL345,LTR390,AT24Cxx"); + result += F("ADXL345,LTR390,AT24Cxx,MB85RCxx"); break; case 0x55: - result += F("DFRobot Rotary enc,BeFlE Moisture,AT24Cxx"); + result += F("DFRobot Rotary enc,BeFlE Moisture,AT24Cxx,MB85RCxx"); break; case 0x54: case 0x56: case 0x57: - result += F("DFRobot Rotary enc,AT24Cxx"); + result += F("DFRobot Rotary enc,AT24Cxx,MB85RCxx"); break; case 0x58: result += F("SGP30,GP8403"); @@ -404,6 +404,9 @@ String getKnownI2Cdevice(uint8_t address) { case 0x78: result += F("LiquidLevel"); break; + case 0x7C: + result += F("MB85RCxx"); + break; case 0x7f: result += F("Arduino PME,XDB401"); break; From 759dff70b84a1b7fefadb03743d8cdf29497bd60 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 19 Aug 2025 20:55:14 +0200 Subject: [PATCH 05/29] [Storage] Add WriteEE,slot,value command and [ReadEE#slot] for retrieving values --- src/src/Commands/EEPROMExternal.cpp | 35 ++++++++ src/src/Commands/EEPROMExternal.h | 8 ++ src/src/Commands/InternalCommands.cpp | 6 ++ src/src/Commands/InternalCommands_decoder.cpp | 3 + src/src/Commands/InternalCommands_decoder.h | 3 + src/src/Helpers/EEPROMExternal.cpp | 86 +++++++++++++++++-- src/src/Helpers/EEPROMExternal.h | 14 +++ src/src/Helpers/StringParser.cpp | 29 ++++++- 8 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 src/src/Commands/EEPROMExternal.cpp create mode 100644 src/src/Commands/EEPROMExternal.h diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp new file mode 100644 index 0000000000..156eed87c4 --- /dev/null +++ b/src/src/Commands/EEPROMExternal.cpp @@ -0,0 +1,35 @@ +#include "../Commands/EEPROMExternal.h" +#include "../Helpers/EEPROMExternal.h" + +#include "../../ESPEasy_common.h" + +#include "../Commands/Common.h" + +#include "../DataStructs/ESPEasy_EventStruct.h" + +#include "../Helpers/Misc.h" +#include "../Helpers/Numerical.h" +#include "../Helpers/StringConverter.h" + +#if FEATURE_EEPROM_EXTERNAL + +// Command: WriteEE,, : set a slot value. 0 is 'erased' +// Command: WriteEE,erase,erase : reset all slots to 0 +const __FlashStringHelper* Command_writeEE(struct EventStruct *event, const char *Line) +{ + uint32_t slot{}; + float value{}; + + if (validUIntFromString(parseString(Line, 2), slot) && validFloatFromString(parseString(Line, 3), value)) { + return return_command_boolean_result_flashstr(writeEEPROMSlot(slot, value)); + } else if (equals(parseString(Line, 2), F("erase")) && equals(parseString(Line, 3), F("erase"))) { + for (uint32_t slot = 0; slot < getEEPROMMaxSlots(); ++slot) { + writeEEPROMSlot(slot, 0.0f); + } + addLog(LOG_LEVEL_INFO, F("EEPROM: All slot-values erased.")); + return return_command_success_flashstr(); + } + return return_command_failed_flashstr(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/EEPROMExternal.h b/src/src/Commands/EEPROMExternal.h new file mode 100644 index 0000000000..c42ac560a4 --- /dev/null +++ b/src/src/Commands/EEPROMExternal.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../../ESPEasy_common.h" + +#if FEATURE_EEPROM_EXTERNAL +const __FlashStringHelper* Command_writeEE(struct EventStruct *event, + const char *Line); +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index dce54f5a3e..da56dd66a9 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -13,6 +13,9 @@ #include "../Commands/Common.h" #include "../Commands/Controller.h" #include "../Commands/Diagnostic.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../Commands/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL #include "../Commands/GPIO.h" #include "../Commands/HTTP.h" #include "../Commands/InternalCommands_decoder.h" @@ -483,6 +486,9 @@ bool InternalCommands::executeInternalCommand() case ESPEasy_cmd_e::wifissid: COMMAND_CASE_R(Command_Wifi_SSID, 1); // WiFi.h case ESPEasy_cmd_e::wifissid2: COMMAND_CASE_R(Command_Wifi_SSID2, 1); // WiFi.h case ESPEasy_cmd_e::wifistamode: COMMAND_CASE_R(Command_Wifi_STAMode, 0); // WiFi.h +#if FEATURE_EEPROM_EXTERNAL + case ESPEasy_cmd_e::writeee: COMMAND_CASE_R(Command_writeEE, 2); // EEPROMExternal.h +#endif // if FEATURE_EEPROM_EXTERNAL case ESPEasy_cmd_e::NotMatched: diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index 0c6115f6eb..38669cc097 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -286,6 +286,9 @@ const char Internal_commands_w[] PROGMEM = "wdconfig|" "wdread|" #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + "writeee|" +#endif // if FEATURE_EEPROM_EXTERNAL ; const char* getInternalCommand_Haystack_Offset(const char firstLetter, int& offset) diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index b5c3f5fa32..5445d5851b 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -241,6 +241,9 @@ enum class ESPEasy_cmd_e : uint8_t { wdconfig, wdread, #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + writeee, +#endif // if FEATURE_EEPROM_EXTERNAL NotMatched // Keep as last one diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index a1f31f2f5e..7247a81b51 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -7,6 +7,8 @@ AT24CX *EEPROMExternal = nullptr; +constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); + /** * Switch to I2C Bus and multiplexer channel of External EEPROM */ @@ -36,6 +38,9 @@ uint8_t selectEEPROMI2CBusAndMultiplexer() { return 0; } +/** + * EEPROM size in bytes + */ uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { switch (type) { case EEPROMExternal_Type_e::AT24C256: @@ -60,6 +65,9 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { return 0; } +/** + * EEPROM pagesize in bytes + */ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, uint8_t & pageSize) { pageSize = 0; @@ -87,17 +95,20 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, return getEEPROMSize(type); } +/** + * EEPROM/FRAM name + */ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { # ifndef BUILD_NO_DEBUG switch (type) { case EEPROMExternal_Type_e::AT24C256: - return F("AT24C256/MB85RC256"); + return F("AT24C256 / MB85RC256"); case EEPROMExternal_Type_e::AT24C512: - return F("AT24C512/MB85RC512"); + return F("AT24C512 / MB85RC512"); # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: - return F("AT24C1024/MB85RC1M"); + return F("AT24C1024 / MB85RC1M"); # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: @@ -106,9 +117,9 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { case EEPROMExternal_Type_e::AT24C32: return F("AT24C32"); case EEPROMExternal_Type_e::AT24C64: - return F("AT24C64/MB85RC64"); + return F("AT24C64 / MB85RC64"); case EEPROMExternal_Type_e::AT24C128: - return F("AT24C128/MB85RC128"); + return F("AT24C128 / MB85RC128"); } return F(""); # else // ifndef BUILD_NO_DEBUG @@ -116,4 +127,69 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { # endif // ifndef BUILD_NO_DEBUG } +/** + * EEPROM address for slot or 0 when error + */ +uint32_t getEEPROMAddressForSlot(uint32_t slot) { + if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if ((eepromSize > 0) && (slot < getEEPROMMaxSlots())) { + const uint32_t slotAddr = EEPROM_CUSTOM_START_OFFSET + (slot * sizeof_uint32_t); + + if (slotAddr < eepromSize) { + return slotAddr; + } + } + } + return 0; +} + +/** + * EEPROM available number of slots + * NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! + */ +uint32_t getEEPROMMaxSlots() { + if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if (eepromSize > 0) { + const uint32_t slotMax = ((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t; + + return slotMax; + } + } + return 0; +} + +/** + * EEPROM write value to slot if the slot is valid + */ +bool writeEEPROMSlot(uint32_t slot, + float data) { + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if (addr > 0) { + const float oldData = EEPROMExternal->readLong(addr); + + if (!essentiallyEqual(oldData, data)) { + EEPROMExternal->writeFloat(addr, data); + } + return true; + } + return false; +} + +/** + * EEPROM read value from slot or 0 when invalid + */ +float readEEPROMSlot(uint32_t slot) { + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if (addr > 0) { + return EEPROMExternal->readFloat(addr); + } + return 0; +} + #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index b0f71ec444..c03c174e0c 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -22,6 +22,14 @@ extern AT24CX *EEPROMExternal; // Choose an arbitrary but fixed offset # define EEPROM_CUSTOM_START_OFFSET (2048) +# if FEATURE_STRING_VARIABLES + +// NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! +# define EEPROM_CUSTOM_DIVISOR (2) // Split in slots- and strings- halves +# else // if FEATURE_STRING_VARIABLES +# define EEPROM_CUSTOM_DIVISOR (1) // Use all for slots +# endif // if FEATURE_STRING_VARIABLES + // Enable/disable some models # define EEPROM_SUPPORT_AT24C1024 1 # define EEPROM_SUPPORT_AT24C2048 0 @@ -47,4 +55,10 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type); uint32_t getEEPROMSize(EEPROMExternal_Type_e type, uint8_t & pageSize); const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); + +uint32_t getEEPROMAddressForSlot(uint32_t slot); +uint32_t getEEPROMMaxSlots(); +bool writeEEPROMSlot(uint32_t slot, + float data); +float readEEPROMSlot(uint32_t slot); #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index bd7ebe2ade..18448dbc3a 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -21,7 +21,9 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" - +#if FEATURE_EEPROM_EXTERNAL +#include "../Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL /********************************************************************************************\ Parse string template @@ -164,10 +166,16 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us const bool devNameEqStr = equals(deviceName, F("str")); const bool devNameEqLength = equals(deviceName, F("length")); #endif // if FEATURE_STRING_VARIABLES + #if FEATURE_EEPROM_EXTERNAL + const bool devNameEqReadEE = equals(deviceName, F("readee")); + #endif // if FEATURE_EEPROM_EXTERNAL if (devNameEqInt || equals(deviceName, F("var")) #if FEATURE_STRING_VARIABLES || devNameEqStr || devNameEqLength #endif // if FEATURE_STRING_VARIABLES + #if FEATURE_EEPROM_EXTERNAL + || devNameEqReadEE + #endif // if FEATURE_EEPROM_EXTERNAL ) { // Address an internal variable either as float or as int @@ -195,6 +203,25 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us tmpString); } else #endif + #if FEATURE_EEPROM_EXTERNAL + if (devNameEqReadEE) { + uint32_t slot{}; + String value; + if (validUIntFromString(valueName, slot)) { + value = toString(readEEPROMSlot(slot)); + } else if (valueName.equalsIgnoreCase(F("max"))) { + value = getEEPROMMaxSlots(); + } + if (!value.isEmpty()) { + transformValue( + newString, + minimal_lineSize, + std::move(value), + format, + tmpString); + } + } else + #endif // if FEATURE_EEPROM_EXTERNAL { const ESPEASY_RULES_FLOAT_TYPE floatvalue = getCustomFloatVar(valueName); unsigned char nr_decimals = maxNrDecimals_fpType(floatvalue); From 872b6289d5e6e091b48e8e8320d1806b72525809 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 19 Aug 2025 20:59:50 +0200 Subject: [PATCH 06/29] [Storage] Add overview of all values in external EEPROM (new button on Tools page) --- src/src/WebServer/ESPEasy_WebServer.cpp | 4 ++ src/src/WebServer/EepromVarPage.h | 8 +++ src/src/WebServer/EpromVarPage.cpp | 90 +++++++++++++++++++++++++ src/src/WebServer/ToolsPage.cpp | 4 ++ 4 files changed, 106 insertions(+) create mode 100644 src/src/WebServer/EepromVarPage.h create mode 100644 src/src/WebServer/EpromVarPage.cpp diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 5ba648ab3e..e85f534bf6 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -12,6 +12,7 @@ #include "../WebServer/CustomPage.h" #include "../WebServer/DevicesPage.h" #include "../WebServer/DownloadPage.h" +#include "../WebServer/EepromVarPage.h" #include "../WebServer/FactoryResetPage.h" #include "../WebServer/FileList.h" #include "../WebServer/HTML_wrappers.h" @@ -299,6 +300,9 @@ void WebServerInit() #ifdef WEBSERVER_SYSVARS web_server.on(F("/sysvars"), handle_sysvars); #endif // WEBSERVER_SYSVARS +#if FEATURE_EEPROM_EXTERNAL + web_server.on(F("/eepromvars"), handle_eepromvars); +#endif // if FEATURE_EEPROM_EXTERNAL #ifdef WEBSERVER_TIMINGSTATS web_server.on(F("/timingstats"), handle_timingstats); #endif // WEBSERVER_TIMINGSTATS diff --git a/src/src/WebServer/EepromVarPage.h b/src/src/WebServer/EepromVarPage.h new file mode 100644 index 0000000000..ed4edb5620 --- /dev/null +++ b/src/src/WebServer/EepromVarPage.h @@ -0,0 +1,8 @@ +#pragma once + +#if FEATURE_EEPROM_EXTERNAL +# include "../WebServer/common.h" + +void handle_eepromvars(); + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp new file mode 100644 index 0000000000..d237dc9009 --- /dev/null +++ b/src/src/WebServer/EpromVarPage.cpp @@ -0,0 +1,90 @@ +#include "../WebServer/EepromVarPage.h" + + +#include "../WebServer/ESPEasy_WebServer.h" +#include "../WebServer/AccessControl.h" +#include "../WebServer/Markup.h" +#include "../WebServer/Markup_Forms.h" +#include "../WebServer/HTML_wrappers.h" + +#include "../Globals/Settings.h" + +#include "../Helpers/EEPROMExternal.h" + +#include "../Helpers/StringConverter.h" + +// #if FEATURE_STRING_VARIABLES +// # include "../Helpers/StringParser.h" +// #endif // if + +#if FEATURE_EEPROM_EXTERNAL + +void handle_eepromvars() { + if (!isLoggedIn()) { return; } + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + // the table header + html_table_class_normal(); + html_TR(); + html_table_header(F("External EEPROM"), 300); + html_table_header(getEEPROMName(static_cast(Settings.EEPROMExternalType())), 400); + html_table_header(F("")); + html_TR(); + html_table_header(F("Slot"), 300); + html_table_header(F("Value (only non-zero values)"), 400); + html_table_header(F("")); + + const uint32_t maxSlots = getEEPROMMaxSlots(); + uint32_t count{}; + + for (uint32_t slot = 0; slot < maxSlots; ++slot) { + const float value = readEEPROMSlot(slot); + + if (slot % 50 == 0) { delay(0); } + + if (!essentiallyZero(value)) { + ++count; + html_TR_TD(); + addHtmlInt(slot); + html_TD(); + addHtml(toString(value)); + html_TD(); + } + } + + // TODO: List String values in EEPROM + + addTableSeparator(F("Summary"), 3, 2); + + html_TR_TD(); + addHtml(F("Slots occupied: ")); + + if (0 == count) { + addHtml(F("none")); + } else { + addHtmlInt(count); + } + html_TD(); + addHtml(F("Slots available: ")); + addHtmlInt(maxSlots - count); + + if (count > 0) { + addHtml(F(" of ")); + addHtmlInt(maxSlots); + } + html_TD(); + + // TODO: List String summary in EEPROM + + html_end_table(); + } else { + addHtml(F("External EEPROM not enabled.")); + } + html_end_form(); + sendHeadandTail_stdtemplate(_TAIL); + TXBuffer.endStream(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/WebServer/ToolsPage.cpp b/src/src/WebServer/ToolsPage.cpp index 8889e4af99..7579b17b24 100644 --- a/src/src/WebServer/ToolsPage.cpp +++ b/src/src/WebServer/ToolsPage.cpp @@ -94,6 +94,10 @@ void handle_tools() { addWideButtonPlusDescription(F("sysvars"), F("System Variables"), F("Show all system variables and conversions")); # endif // ifdef WEBSERVER_SYSVARS + #if FEATURE_EEPROM_EXTERNAL + addWideButtonPlusDescription(F("eepromvars"), F("External EEPROM values"), F("Show all values stored in the external EEPROM")); + #endif // if FEATURE_EEPROM_EXTERNAL + addFormSubHeader(F("Wifi")); addWideButtonPlusDescription(F("/?cmd=wificonnect"), F("Connect"), F("Connects to known Wifi network")); From beced1361464b07eae17f4dddc72f9ac1d1d6a0d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 21 Aug 2025 13:22:40 +0200 Subject: [PATCH 07/29] [Storage] Restore Task Values optionally on cold or warm boot, code improvements --- src/src/DataStructs/SettingsStruct.h | 11 +++- src/src/ESPEasyCore/ESPEasy_setup.cpp | 12 ++++ src/src/Helpers/CRC_functions.cpp | 26 ++++++++ src/src/Helpers/CRC_functions.h | 7 +++ src/src/Helpers/EEPROMExternal.cpp | 45 ++++++++++--- src/src/Helpers/EEPROMExternal.h | 6 ++ src/src/Helpers/ESPEasyRTC.cpp | 91 ++++++++++++++++++++------- src/src/WebServer/HardwarePage.cpp | 8 +++ 8 files changed, 173 insertions(+), 33 deletions(-) diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 6610f15516..e6aa54ae0f 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -218,6 +218,13 @@ class SettingsStruct_tmpl void ShowUnitOfMeasureOnDevicesPage(bool value) { VariousBits_2.ShowUnitOfMeasureOnDevicesPage = !value; } #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE + #if FEATURE_EEPROM_EXTERNAL + bool RestoreUserVarsFromEEPROMOnColdBoot() const { return VariousBits_2.RestoreUserVarsFromEEPROMOnColdBoot; } + void RestoreUserVarsFromEEPROMOnColdBoot(bool value) { VariousBits_2.RestoreUserVarsFromEEPROMOnColdBoot = value; } + bool RestoreUserVarsFromEEPROMOnWarmBoot() const { return VariousBits_2.RestoreUserVarsFromEEPROMOnWarmBoot; } + void RestoreUserVarsFromEEPROMOnWarmBoot(bool value) { VariousBits_2.RestoreUserVarsFromEEPROMOnWarmBoot = value; } + #endif // if FEATURE_EEPROM_EXTERNAL + // Flag indicating whether all task values should be sent in a single event or one event per task value (default behavior) bool CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const; void CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value); @@ -597,8 +604,8 @@ class SettingsStruct_tmpl uint32_t DisableSaveConfigAsTar : 1; // Bit 05 uint32_t PassiveWiFiScan : 1; // Bit 06 // inverted uint32_t ShowUnitOfMeasureOnDevicesPage : 1; // Bit 07 // inverted - uint32_t unused_08 : 1; // Bit 08 - uint32_t unused_09 : 1; // Bit 09 + uint32_t RestoreUserVarsFromEEPROMOnColdBoot : 1; // Bit 08 + uint32_t RestoreUserVarsFromEEPROMOnWarmBoot : 1; // Bit 09 uint32_t unused_10 : 1; // Bit 10 uint32_t unused_11 : 1; // Bit 11 uint32_t unused_12 : 1; // Bit 12 diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 23546c739d..ed1345ad78 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -34,6 +34,9 @@ #include "../Helpers/StringGenerator_System.h" #include "../WebServer/ESPEasy_WebServer.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL #ifdef USE_RTOS_MULTITASKING # include "../Helpers/Networking.h" @@ -398,6 +401,15 @@ void ESPEasy_setup() logMemUsageAfter(F("hardwareInit()")); #endif // ifndef BUILD_NO_RAM_TRACKER + #if FEATURE_EEPROM_EXTERNAL + if ((checkEEPROMEnabled() > 0) && + ((lastBootCause <= BOOT_CAUSE_COLD_BOOT) && Settings.RestoreUserVarsFromEEPROMOnColdBoot() || + (lastBootCause <= BOOT_CAUSE_SOFT_RESTART) && Settings.RestoreUserVarsFromEEPROMOnWarmBoot()) + ) { + readUserVarFromRTC(); // Once more to fetch UserVar data from now available EEPROM/FRAM + } + #endif // if FEATURE_EEPROM_EXTERNAL + node_time.restoreFromRTC(); Settings.UseRTOSMultitasking = false; // For now, disable it, we experience heap corruption. diff --git a/src/src/Helpers/CRC_functions.cpp b/src/src/Helpers/CRC_functions.cpp index ba76121624..d18e93fa21 100644 --- a/src/src/Helpers/CRC_functions.cpp +++ b/src/src/Helpers/CRC_functions.cpp @@ -57,6 +57,32 @@ uint32_t calc_CRC32(const uint8_t *data, size_t length) { return crc; } +uint32_t calc_CRC32(tDataReader dataReader, size_t length) { + uint32_t crc = 0xffffffff; + size_t index{}; + + if (dataReader) { + while (length--) { + uint8_t c = dataReader(index); + ++index; + + for (uint32_t i = 0x80; i > 0; i >>= 1) { + bool bit = crc & 0x80000000; + + if (c & i) { + bit = !bit; + } + crc <<= 1; + + if (bit) { + crc ^= 0x04c11db7; + } + } + } + } + return crc; +} + uint8_t calc_CRC8(const uint8_t *data, size_t length) { /* diff --git a/src/src/Helpers/CRC_functions.h b/src/src/Helpers/CRC_functions.h index dfbf589d8a..d418112b02 100644 --- a/src/src/Helpers/CRC_functions.h +++ b/src/src/Helpers/CRC_functions.h @@ -3,6 +3,10 @@ #include "../../ESPEasy_common.h" +#include + +typedef std::function tDataReader; + int calc_CRC16(const String& text); int IRAM_ATTR calc_CRC16(const char *ptr, @@ -11,6 +15,9 @@ int IRAM_ATTR calc_CRC16(const char *ptr, uint32_t calc_CRC32(const uint8_t *data, size_t length); +uint32_t calc_CRC32(tDataReader dataReader, + size_t length); + uint8_t calc_CRC8(const uint8_t *data, size_t length); diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index 7247a81b51..e453bab8ed 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -9,6 +9,19 @@ AT24CX *EEPROMExternal = nullptr; constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); +/** + * Check if the EEPROM is properly initialized and enabled. + * Returns the I2C address if all is OK + */ +uint8_t checkEEPROMEnabled() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if ((nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? + return eepromAddress; + } + return 0; +} + /** * Switch to I2C Bus and multiplexer channel of External EEPROM */ @@ -128,10 +141,10 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { } /** - * EEPROM address for slot or 0 when error + * EEPROM address for slot or 0xFFFF when error */ uint32_t getEEPROMAddressForSlot(uint32_t slot) { - if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + if (checkEEPROMEnabled() > 0) { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if ((eepromSize > 0) && (slot < getEEPROMMaxSlots())) { @@ -142,7 +155,25 @@ uint32_t getEEPROMAddressForSlot(uint32_t slot) { } } } - return 0; + return std::numeric_limits::max(); +} + +/** + * EEPROM address for task and varnr or 0xFFFF when error + */ +uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, taskVarIndex_t varNr) { + if (checkEEPROMEnabled() > 0) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if ((eepromSize > 0) && validTaskIndex(task) && validTaskVarIndex(varNr)) { + const uint32_t slotAddr = EEPROM_USERVAR_START_OFFSET + (((task * VARS_PER_TASK) + varNr) * sizeof_uint32_t); + + if (slotAddr < eepromSize) { + return slotAddr; + } + } + } + return std::numeric_limits::max(); } /** @@ -150,7 +181,7 @@ uint32_t getEEPROMAddressForSlot(uint32_t slot) { * NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! */ uint32_t getEEPROMMaxSlots() { - if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + if (checkEEPROMEnabled() > 0) { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if (eepromSize > 0) { @@ -169,7 +200,7 @@ bool writeEEPROMSlot(uint32_t slot, float data) { const uint32_t addr = getEEPROMAddressForSlot(slot); - if (addr > 0) { + if (addr != std::numeric_limits::max()) { const float oldData = EEPROMExternal->readLong(addr); if (!essentiallyEqual(oldData, data)) { @@ -186,10 +217,10 @@ bool writeEEPROMSlot(uint32_t slot, float readEEPROMSlot(uint32_t slot) { const uint32_t addr = getEEPROMAddressForSlot(slot); - if (addr > 0) { + if (addr != std::numeric_limits::max()) { return EEPROMExternal->readFloat(addr); } - return 0; + return 0.0f; } #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index c03c174e0c..6618a9456a 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -1,6 +1,8 @@ #pragma once #include "../../ESPEasy_common.h" +#include "../DataTypes/TaskIndex.h" + #if FEATURE_EEPROM_EXTERNAL # include @@ -49,6 +51,8 @@ enum class EEPROMExternal_Type_e : uint8_t { AT24C128 = 6, // 16 kB }; +uint8_t checkEEPROMEnabled(); + uint8_t selectEEPROMI2CBusAndMultiplexer(); uint32_t getEEPROMSize(EEPROMExternal_Type_e type); @@ -57,6 +61,8 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); uint32_t getEEPROMAddressForSlot(uint32_t slot); +uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, + taskVarIndex_t varNr); uint32_t getEEPROMMaxSlots(); bool writeEEPROMSlot(uint32_t slot, float data); diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index cddac3bbd4..2d881b528a 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -178,25 +178,29 @@ bool saveUserVarToRTC(bool initial) { #if FEATURE_EEPROM_EXTERNAL // Check if we have an external EEPROM available on the configured I2C bus & channel, and save all task values there - const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); - if (!initial && (nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (!initial && (eepromAddress > 0)) { // EEPROM Configured? if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM #ifndef BUILD_NO_DEBUG uint32_t eepromWritten{}; + uint32_t startmicros{}; #endif // ifndef BUILD_NO_DEBUG const uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); if (UserVar.compute_CRC32() != checksum) { // Only save if data changed + #ifndef BUILD_NO_DEBUG + startmicros = micros(); + #endif // ifndef BUILD_NO_DEBUG for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); if (taskValues != nullptr) { for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { const size_t index = (task * VARS_PER_TASK) + varNr; const uint32_t newData = taskValues->getUint32(varNr); // Only update EEPROM is data differs - if (newData != EEPROMExternal->readLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof_uint32_t))) { - EEPROMExternal->writeLong(EEPROM_USERVAR_START_OFFSET + (index * sizeof_uint32_t), - newData); + const uint32_t addr = getEEPROMAddressForTaskValue(task, varNr); + if (newData != EEPROMExternal->readLong(addr)) { + EEPROMExternal->writeLong(addr, newData); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; #endif // ifndef BUILD_NO_DEBUG @@ -208,14 +212,14 @@ bool saveUserVarToRTC(bool initial) UserVar.compute_CRC32()); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; + startmicros = micros() - startmicros; #endif // ifndef BUILD_NO_DEBUG } #ifndef BUILD_NO_DEBUG - // if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { // FIXME - if (loglevelActiveFor(LOG_LEVEL_INFO)) { + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: UserVar: %u bytes written to %s"), eepromWritten, FsP(getEEPROMName(eepromType)))); + addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), eepromWritten, startmicros / 1000.0f, FsP(getEEPROMName(eepromType)))); } #endif // ifndef BUILD_NO_DEBUG @@ -259,27 +263,66 @@ bool saveUserVarToRTC(bool initial) #endif } +#if FEATURE_EEPROM_EXTERNAL +uint8_t readDataForUserVars(size_t index) { + if (nullptr != EEPROMExternal) { + return EEPROMExternal->read(EEPROM_USERVAR_START_OFFSET + index); + } + return 0; +} +#endif // if FEATURE_EEPROM_EXTERNAL + /********************************************************************************************\ Read RTC struct from RTC memory \*********************************************************************************************/ bool readUserVarFromRTC() { - // const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); - // if ((nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? - - // if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM - // // TODO Check checksum and if correct, restore UserVar values - // } - // #if FEATURE_I2CMULTIPLEXER - // I2CMultiplexerOff( - // #if FEATURE_I2C_MULTIPLE - // Settings.getI2CInterfaceEEPROM() - // #else //if FEATURE_I2C_MULTIPLE - // 0 - // #endif // if FEATURE_I2C_MULTIPLE - // ); // Restore the Multiplexer channel - // #endif // if FEATURE_I2CMULTIPLEXER - // } + #if FEATURE_EEPROM_EXTERNAL + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { // EEPROM Configured and restoring of Task Values enabled? + bool result = false; + + if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // Check checksum and if correct, restore UserVar values + const uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); + #ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: readUserVarFromRTC calculated: %u, expected: %u equal: %c"), + calcsum, checksum, calcsum == checksum ? 'Y' : 'n')); + } + #endif // ifndef BUILD_NO_DEBUG + if (calcsum == checksum) { + addLog(LOG_LEVEL_INFO, F("INIT : Restoring Task Values from EEPROM.")); + result = true; + for (size_t i = 0; i < (TASKS_MAX * VARS_PER_TASK) && result; ++i) { + const taskIndex_t taskIndex = i / VARS_PER_TASK; + const uint8_t varNr = i % VARS_PER_TASK; + // Store in raw form, so we don't apply formula as we don't really know what type is required. + const uint32_t addr = getEEPROMAddressForTaskValue(taskIndex, varNr); + if (addr != std::numeric_limits::max()) { + TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(taskIndex); + taskValues->setUint32(varNr, EEPROMExternal->readLong(addr)); + } else { + result = false; + } + } + } + } + #if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + #if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + #else //if FEATURE_I2C_MULTIPLE + 0 + #endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + #endif // if FEATURE_I2CMULTIPLEXER + if (result) { + return true; // We did all that was needed + } + } + #endif // if FEATURE_EEPROM_EXTERNAL // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index dbae93a3b5..04ff9f3390 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -129,6 +129,8 @@ void handle_hardware() { #if FEATURE_EEPROM_EXTERNAL Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); + Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(F("lduvcbee"))); + Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(F("lduvwbee"))); # if FEATURE_I2CMULTIPLEXER @@ -375,6 +377,12 @@ void handle_hardware() { bitRead(eepromMux, EEPROM_MUX_FLAGS_MULTI), get8BitFromUL(eepromMux, EEPROM_MUX_FLAGS_PORT)); // Re-used from DevicesPage #endif // if FEATURE_I2CMULTIPLEXER + + const bool eepromChecked = checkEEPROMEnabled() > 0; + addRowLabel(F("EEPROM Enabled")); + addEnabled(eepromChecked); + addFormCheckBox(F("Cold boot: Restore Task Values from EEPROM"), F("lduvcbee"), Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); + addFormCheckBox(F("Warm boot: Restore Task Values from EEPROM"), F("lduvwbee"), Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); } #endif // if FEATURE_EEPROM_EXTERNAL From 623664d99b15afa62d004510e8158a64008dd7b0 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 21 Aug 2025 13:23:36 +0200 Subject: [PATCH 08/29] [Storage] Show Task Values in EEPROM by using tasks=1 parameter for Eeprom Values page --- src/src/WebServer/EpromVarPage.cpp | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index d237dc9009..7e236de2c5 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -8,8 +8,10 @@ #include "../WebServer/HTML_wrappers.h" #include "../Globals/Settings.h" +#include "../Globals/ExtraTaskSettings.h" #include "../Helpers/EEPROMExternal.h" +#include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/StringConverter.h" @@ -21,16 +23,57 @@ void handle_eepromvars() { if (!isLoggedIn()) { return; } + + const bool showTasks = getFormItemInt(F("tasks"), 0) != 0; + TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); - if ((nullptr != EEPROMExternal) && (Settings.EEPROMExternalI2CAddress() > 0)) { + if (checkEEPROMEnabled() > 0) { // the table header html_table_class_normal(); html_TR(); html_table_header(F("External EEPROM"), 300); html_table_header(getEEPROMName(static_cast(Settings.EEPROMExternalType())), 400); + html_table_header(F(""), 400); html_table_header(F("")); + + if (showTasks) { + html_TR(); + html_table_header(F("Task"), 300); + html_table_header(F("Value"), 400); + html_table_header(F("Content"), 400); + html_table_header(F("")); + + for (taskIndex_t tsk = 0; tsk < TASKS_MAX; ++tsk) { + LoadTaskSettings(tsk); + html_TR_TD(); + addHtmlInt(tsk + 1); + addHtml(' '); + addHtml(getTaskDeviceName(tsk)); + + for (taskVarIndex_t var = 0; var < VARS_PER_TASK; ++var) { + if (var != 0) { + html_TR_TD(); + } + html_TD(); + addHtmlInt(var + 1); + addHtml(' '); + addHtml(getTaskValueName(tsk, var)); + html_TD(); + const uint32_t addr = getEEPROMAddressForTaskValue(tsk, var); + const float value = EEPROMExternal->readFloat(addr); + const uint32_t data = EEPROMExternal->readLong(addr); + + if (isnan(value) || (addr == std::numeric_limits::max())) { + addHtml('-'); + } else { + addHtml(strformat(F("%s (0x%04x)"), floatToString(value, ExtraTaskSettings.TaskDeviceValueDecimals[var]), data)); + } + } + delay(0); + } + } html_TR(); html_table_header(F("Slot"), 300); html_table_header(F("Value (only non-zero values)"), 400); @@ -44,7 +87,7 @@ void handle_eepromvars() { if (slot % 50 == 0) { delay(0); } - if (!essentiallyZero(value)) { + if (!isnan(value) && !essentiallyZero(value)) { ++count; html_TR_TD(); addHtmlInt(slot); From 65f10f4d6b57028529320c71cd4b58b75b2addca Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 22 Aug 2025 14:31:24 +0200 Subject: [PATCH 09/29] [Storage] Add check for WriteProtected EEPROM and show on Hardware page --- src/src/Helpers/EEPROMExternal.cpp | 53 ++++++++++++++++++++++++++++-- src/src/Helpers/EEPROMExternal.h | 44 +++++++++++++++---------- src/src/Helpers/ESPEasyRTC.cpp | 4 +-- src/src/Helpers/Hardware_I2C.cpp | 6 ++++ src/src/WebServer/EpromVarPage.cpp | 18 ++++++---- src/src/WebServer/HardwarePage.cpp | 3 ++ 6 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index e453bab8ed..679c34c5e5 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -2,10 +2,12 @@ #include "../Globals/Settings.h" #include "../Helpers/I2C_access.h" #include "../../ESPEasy_common.h" +#include "../Helpers/StringConverter.h" #if FEATURE_EEPROM_EXTERNAL -AT24CX *EEPROMExternal = nullptr; +AT24CX *EEPROMExternal = nullptr; +EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); @@ -22,6 +24,53 @@ uint8_t checkEEPROMEnabled() { return 0; } +/** + * Check if the EEPROM is write-protected + * when forced = false only detect if current state is Undefined + * - read a random byte in the first half of the address space (some chips ony WP the first half of the space!) + * - write 0xAA and read back -> if unequal: read-only + * - write 0x55 and read back -> if unequal: read-only + * - Still OK: + * - restore original byte + * - Set status read-write + */ +EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced) { + if ((nullptr != EEPROMExternal) && ((EEPROMExternal_WriteProtect_e::Undefined == EEPROMExternalWriteProtect) || forced)) { + const uint32_t addr = random(0, getEEPROMSize(static_cast(Settings.EEPROMExternalType())) / 2); + const uint8_t original = EEPROMExternal->read(addr); + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: Writeable check, addr: 0x%04x data: 0x%02X"), addr, original)); + } + # endif // ifndef BUILD_NO_DEBUG + EEPROMExternal->write(addr, 0xAA); + uint8_t newdata = EEPROMExternal->read(addr); + + if (0xAA != newdata) { // write failed + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadOnly; + } else { + EEPROMExternal->write(addr, 0x55); + newdata = EEPROMExternal->read(addr); + + if (0x55 != newdata) { // write failed + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadOnly; + } else { + EEPROMExternal->write(addr, original); + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadWrite; + } + } + } + return EEPROMExternalWriteProtect; +} + +/** + * Is the EEPROM WriteProtected? + */ +bool isEEPROMExternalWriteProtected() { + return EEPROMExternal_WriteProtect_e::ReadWrite != checkEEPROMExternalWriteProtected(); +} + /** * Switch to I2C Bus and multiplexer channel of External EEPROM */ @@ -200,7 +249,7 @@ bool writeEEPROMSlot(uint32_t slot, float data) { const uint32_t addr = getEEPROMAddressForSlot(slot); - if (addr != std::numeric_limits::max()) { + if ((addr != std::numeric_limits::max()) && !isEEPROMExternalWriteProtected()) { const float oldData = EEPROMExternal->readLong(addr); if (!essentiallyEqual(oldData, data)) { diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index 6618a9456a..e7132c368f 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -7,26 +7,32 @@ # include +enum class EEPROMExternal_WriteProtect_e : uint8_t { + Undefined = 0, + ReadWrite = 1, + ReadOnly = 2, +}; + extern AT24CX *EEPROMExternal; +extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // Start writing the base RTC struct from this offset (not currently saving this to EEPROM) // TODO # define EEPROM_BASERTC_START_OFFSET (0) // Start writing the UserVar values from this offset, should be > sizeof(RTCStruct) that is 32 currently -# define EEPROM_USERVAR_START_OFFSET (100) +# define EEPROM_USERVAR_START_OFFSET (EEPROM_BASERTC_START_OFFSET + 128) // Write the UserVar-checksum from this offset, right after the UserVar values # define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) -// Offset for storing GPIO states // TODO -# define EEPROM_GPIO_MCPPCF_START_OFFSET (1024) +// Offset for storing GPIO states, directly following the UserVar storage and checksum +# define EEPROM_GPIO_MCPPCF_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) // Choose an arbitrary but fixed offset # define EEPROM_CUSTOM_START_OFFSET (2048) -# if FEATURE_STRING_VARIABLES - // NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! +# if FEATURE_STRING_VARIABLES # define EEPROM_CUSTOM_DIVISOR (2) // Split in slots- and strings- halves # else // if FEATURE_STRING_VARIABLES # define EEPROM_CUSTOM_DIVISOR (1) // Use all for slots @@ -51,20 +57,22 @@ enum class EEPROMExternal_Type_e : uint8_t { AT24C128 = 6, // 16 kB }; -uint8_t checkEEPROMEnabled(); +uint8_t checkEEPROMEnabled(); +EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced = false); +bool isEEPROMExternalWriteProtected(); -uint8_t selectEEPROMI2CBusAndMultiplexer(); +uint8_t selectEEPROMI2CBusAndMultiplexer(); -uint32_t getEEPROMSize(EEPROMExternal_Type_e type); -uint32_t getEEPROMSize(EEPROMExternal_Type_e type, - uint8_t & pageSize); -const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); +uint32_t getEEPROMSize(EEPROMExternal_Type_e type); +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize); +const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); -uint32_t getEEPROMAddressForSlot(uint32_t slot); -uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, - taskVarIndex_t varNr); -uint32_t getEEPROMMaxSlots(); -bool writeEEPROMSlot(uint32_t slot, - float data); -float readEEPROMSlot(uint32_t slot); +uint32_t getEEPROMAddressForSlot(uint32_t slot); +uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, + taskVarIndex_t varNr); +uint32_t getEEPROMMaxSlots(); +bool writeEEPROMSlot(uint32_t slot, + float data); +float readEEPROMSlot(uint32_t slot); #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 2d881b528a..92b5528787 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -179,7 +179,7 @@ bool saveUserVarToRTC(bool initial) #if FEATURE_EEPROM_EXTERNAL // Check if we have an external EEPROM available on the configured I2C bus & channel, and save all task values there const uint8_t eepromAddress = checkEEPROMEnabled(); - if (!initial && (eepromAddress > 0)) { // EEPROM Configured? + if (!initial && (eepromAddress > 0) && !isEEPROMExternalWriteProtected()) { // EEPROM Configured and writable? if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM #ifndef BUILD_NO_DEBUG @@ -197,7 +197,7 @@ bool saveUserVarToRTC(bool initial) if (taskValues != nullptr) { for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { const size_t index = (task * VARS_PER_TASK) + varNr; - const uint32_t newData = taskValues->getUint32(varNr); // Only update EEPROM is data differs + const uint32_t newData = taskValues->getUint32(varNr); // Only update EEPROM if data differs const uint32_t addr = getEEPROMAddressForTaskValue(task, varNr); if (newData != EEPROMExternal->readLong(addr)) { EEPROMExternal->writeLong(addr, newData); diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index 34e31ac72a..f28ed2f062 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -107,6 +107,12 @@ void initI2C() { FsP(getEEPROMName(eepromType)), eepromAddress)); } + + checkEEPROMExternalWriteProtected(); + + if (isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(checkEEPROMExternalWriteProtected()))); + } } else { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: Initialization of %s failed"), diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index 7e236de2c5..54cc4f859b 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -34,14 +34,16 @@ void handle_eepromvars() { html_table_class_normal(); html_TR(); html_table_header(F("External EEPROM"), 300); - html_table_header(getEEPROMName(static_cast(Settings.EEPROMExternalType())), 400); - html_table_header(F(""), 400); + html_table_header(getEEPROMName(static_cast(Settings.EEPROMExternalType())), 500); + html_table_header(isEEPROMExternalWriteProtected() ? F("Write-protected!") : F(""), 400); html_table_header(F("")); if (showTasks) { html_TR(); + + // sub-table header html_table_header(F("Task"), 300); - html_table_header(F("Value"), 400); + html_table_header(F("Value"), 500); html_table_header(F("Content"), 400); html_table_header(F("")); @@ -75,8 +77,11 @@ void handle_eepromvars() { } } html_TR(); + + // sub-table header html_table_header(F("Slot"), 300); - html_table_header(F("Value (only non-zero values)"), 400); + html_table_header(F("Value (only non-zero values)"), 500); + html_table_header(F("")); html_table_header(F("")); const uint32_t maxSlots = getEEPROMMaxSlots(); @@ -93,11 +98,12 @@ void handle_eepromvars() { addHtmlInt(slot); html_TD(); addHtml(toString(value)); - html_TD(); + html_TD(2); } } // TODO: List String values in EEPROM + // TODO: List C016 Cache entries in EEPROM addTableSeparator(F("Summary"), 3, 2); @@ -109,7 +115,7 @@ void handle_eepromvars() { } else { addHtmlInt(count); } - html_TD(); + addHtml(F("")); addHtml(F("Slots available: ")); addHtmlInt(maxSlots - count); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 04ff9f3390..7787814d3b 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -381,6 +381,9 @@ void handle_hardware() { const bool eepromChecked = checkEEPROMEnabled() > 0; addRowLabel(F("EEPROM Enabled")); addEnabled(eepromChecked); + if (isEEPROMExternalWriteProtected()) { + addHtml(F(" Write-protected!")); + } addFormCheckBox(F("Cold boot: Restore Task Values from EEPROM"), F("lduvcbee"), Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); addFormCheckBox(F("Warm boot: Restore Task Values from EEPROM"), F("lduvwbee"), Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); } From ce8453e925369ad5460a894dc13c1cd461516850 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 22 Aug 2025 14:31:50 +0200 Subject: [PATCH 10/29] [Storage] Add command to re-check for WriteProtected EEPROM --- src/src/Commands/EEPROMExternal.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp index 156eed87c4..92c94ed6bd 100644 --- a/src/src/Commands/EEPROMExternal.cpp +++ b/src/src/Commands/EEPROMExternal.cpp @@ -28,6 +28,14 @@ const __FlashStringHelper* Command_writeEE(struct EventStruct *event, const char } addLog(LOG_LEVEL_INFO, F("EEPROM: All slot-values erased.")); return return_command_success_flashstr(); + } else if (equals(parseString(Line, 2), F("check")) && equals(parseString(Line, 3), F("wp"))) { + addLog(LOG_LEVEL_INFO, F("EEPROM: Check write-protect.")); + checkEEPROMExternalWriteProtected(true); + + if (isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(checkEEPROMExternalWriteProtected()))); + } + return return_command_success_flashstr(); } return return_command_failed_flashstr(); } From c56c3f6795cd7c54e2c079d808a6fbdf0740f34d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 22 Aug 2025 14:42:54 +0200 Subject: [PATCH 11/29] [Storage] Improved initialization and format source --- src/src/Helpers/Hardware_I2C.cpp | 42 +++++++++++++++++--------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index f28ed2f062..ab4cebec6c 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -8,7 +8,7 @@ #include "../Helpers/StringConverter.h" #if FEATURE_EEPROM_EXTERNAL -#include "../Helpers/EEPROMExternal.h" +# include "../Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL #include @@ -36,12 +36,12 @@ void initI2C() { { if (Settings.isI2CEnabled(i2cBus)) { #ifndef BUILD_MINIMAL_OTA - #if !FEATURE_I2C_MULTIPLE + # if !FEATURE_I2C_MULTIPLE addLog(LOG_LEVEL_INFO, F("INIT : I2C Bus")); - #else // if !FEATURE_I2C_MULTIPLE + # else // if !FEATURE_I2C_MULTIPLE addLog(LOG_LEVEL_INFO, concat(F("INIT : I2C Bus "), i2cBus)); - #endif // if !FEATURE_I2C_MULTIPLE - #endif + # endif // if !FEATURE_I2C_MULTIPLE + #endif // ifndef BUILD_MINIMAL_OTA I2CSelectHighClockSpeed(i2cBus); // Set normal clock speed, on I2C Bus 1 (index 0) } } @@ -88,24 +88,26 @@ void initI2C() { #if FEATURE_EEPROM_EXTERNAL const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); - if ((nullptr != EEPROMExternal) && (eepromAddress == 0)) { // Cleanup when turning off EEPROM + if ((nullptr != EEPROMExternal) || (eepromAddress == 0)) { // Cleanup when turning off EEPROM delete EEPROMExternal; - EEPROMExternal = nullptr; + EEPROMExternal = nullptr; + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; } if ((nullptr == EEPROMExternal) && (eepromAddress > 0)) { const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); + if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM // We have an I2C device at this address, let's assume it's an EEPROM... - uint8_t pageSize = 0; + uint8_t pageSize = 0; const uint32_t eepromSize = getEEPROMSize(eepromType, pageSize); EEPROMExternal = new (std::nothrow) AT24CX(eepromAddress, pageSize, eepromSize); if (nullptr != EEPROMExternal) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: %s initialized at address 0x%02x"), - FsP(getEEPROMName(eepromType)), - eepromAddress)); + FsP(getEEPROMName(eepromType)), + eepromAddress)); } checkEEPROMExternalWriteProtected(); @@ -127,15 +129,15 @@ void initI2C() { } } - #if FEATURE_I2CMULTIPLEXER + # if FEATURE_I2CMULTIPLEXER I2CMultiplexerOff( - #if FEATURE_I2C_MULTIPLE + # if FEATURE_I2C_MULTIPLE Settings.getI2CInterfaceEEPROM() - #else //if FEATURE_I2C_MULTIPLE + # else // if FEATURE_I2C_MULTIPLE 0 - #endif // if FEATURE_I2C_MULTIPLE - ); // Restore the Multiplexer channel - #endif // if FEATURE_I2CMULTIPLEXER + # endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + # endif // if FEATURE_I2CMULTIPLEXER } #endif // if FEATURE_EEPROM_EXTERNAL @@ -208,10 +210,11 @@ void I2CBegin(int8_t sda, int8_t scl, uint32_t clockFreq, uint32_t clockStretch) // No need to change the clock speed. return; } - if (sda == -1 || scl == -1) { + + if ((sda == -1) || (scl == -1)) { #ifdef ESP32 Wire.end(); -#endif +#endif // ifdef ESP32 last_sda = sda; last_scl = scl; return; @@ -308,7 +311,7 @@ uint8_t I2CMultiplexerShiftBit(uint8_t i2cBus, uint8_t i) { void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, bool singleMulti, int muxPort) { uint8_t toWrite{}; - if ((singleMulti && (muxPort > 0))|| + if ((singleMulti && (muxPort > 0)) || (!singleMulti && (muxPort > -1))) { if (!singleMulti) { uint8_t i = muxPort; @@ -322,7 +325,6 @@ void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, bool singleMulti, int muxPo } SetI2CMultiplexer(i2cBus, toWrite); - } // As initially constructed by krikk in PR#254, quite adapted From 44e9d3776895fac46cf8f605bee34a9150039e57 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 22 Aug 2025 22:38:02 +0200 Subject: [PATCH 12/29] [Storage] [C016] Store Cache data in EEPROM --- .../DataStructs/RTC_cache_handler_struct.cpp | 86 +++++++++++++++++++ src/src/Helpers/EEPROMExternal.h | 16 +++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 28027fc833..7e76e497a1 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -11,6 +11,10 @@ #include "../ESPEasyCore/ESPEasy_backgroundtasks.h" #include "../ESPEasyCore/ESPEasy_Log.h" +#if FEATURE_EEPROM_EXTERNAL +# include "../Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL + #ifdef ESP8266 # include #endif // ifdef ESP8266 @@ -26,6 +30,60 @@ RTC_NOINIT_ATTR RTC_cache_struct RTC_cache; RTC_NOINIT_ATTR uint8_t RTC_cache_data[RTC_CACHE_DATA_SIZE]; #endif // ifdef ESP32 +/********************************************************************************************** + * EEPROM Helper function for RTC Cache + **********************************************************************************************/ +#if FEATURE_EEPROM_EXTERNAL +void EEPROMSaveWritePos() { + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { + // Save new position + if (EEPROMExternal->readInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET) != RTC_cache.writePos) { + EEPROMExternal->writeInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET, RTC_cache.writePos); + } + } +} + +void EEPROMSaveMetaDataChecksum() { + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { + // Save metadata checksum + if (EEPROMExternal->readLong(EEPROM_RTC_CACHE_META_CRC_OFFSET) != RTC_cache.checksumMetadata) { + EEPROMExternal->writeLong(EEPROM_RTC_CACHE_META_CRC_OFFSET, RTC_cache.checksumMetadata); + } + } +} + +void EEPROMSaveCacheChecksum() { + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { + // Save cache checksum + if (EEPROMExternal->readLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET) != RTC_cache.checksumData) { + EEPROMExternal->writeLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET, RTC_cache.checksumData); + } + } +} + +void EEPROMSaveRTCcache() { + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { + size_t count{}; + for (uint16_t idx = 0; idx < sizeof(RTC_cache); ++idx) { + const uint32_t metaAddr = EEPROM_RTC_CACHE_META_OFFSET + idx; + const uint8_t oldData = EEPROMExternal->read(metaAddr); + const uint8_t newData = reinterpret_cast(&RTC_cache)[idx]; + if (oldData != newData) { + EEPROMExternal->write(metaAddr, newData); + ++count; + } + } + // Save metadata checksum + EEPROMSaveMetaDataChecksum(); + // Save cache checksum + EEPROMSaveCacheChecksum(); + } +} +#endif // if FEATURE_EEPROM_EXTERNAL /********************************************************************************************\ RTC located cache @@ -234,6 +292,24 @@ bool RTC_cache_handler_struct::write(const uint8_t *data, unsigned int size) { ++RTC_cache.writePos; } + #if FEATURE_EEPROM_EXTERNAL + const uint8_t eepromAddress = checkEEPROMEnabled(); + if (eepromAddress > 0) { + size_t count{}; + for (unsigned int idx = 0; idx < size; ++idx) { + const uint32_t cacheAddr = EEPROM_RTC_CACHE_START_OFFSET + idx; + const uint8_t oldData = EEPROMExternal->read(cacheAddr); + const uint8_t newData = data[idx]; + if (oldData != newData) { + EEPROMExternal->write(cacheAddr, newData); + ++count; + } + } + // Save new position + EEPROMSaveWritePos(); + } + #endif // if FEATURE_EEPROM_EXTERNAL + // Now store the updated part of the buffer to the RTC memory. // Pad some extra bytes around it to allow sample sizes not multiple of 4 bytes. int startOffset = RTC_cache.writePos - size; @@ -490,6 +566,11 @@ bool RTC_cache_handler_struct::saveRTCcache(unsigned int startOffset, size_t nrB { RTC_cache.checksumData = getDataChecksum(); RTC_cache.checksumMetadata = calc_CRC32(reinterpret_cast(&RTC_cache), sizeof(RTC_cache) - sizeof(uint32_t)); + + #if FEATURE_EEPROM_EXTERNAL + EEPROMSaveRTCcache(); + #endif // if FEATURE_EEPROM_EXTERNAL + #ifdef ESP32 return true; #endif // ifdef ESP32 @@ -558,6 +639,11 @@ void RTC_cache_handler_struct::clearRTCcacheData() { RTC_cache_data[i] = 0; } RTC_cache.writePos = 0; + + #if FEATURE_EEPROM_EXTERNAL + // Save new position + EEPROMSaveWritePos(); + #endif // if FEATURE_EEPROM_EXTERNAL } // Return true if any cache file found diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index e7132c368f..989c0a1198 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -1,10 +1,11 @@ #pragma once #include "../../ESPEasy_common.h" -#include "../DataTypes/TaskIndex.h" - #if FEATURE_EEPROM_EXTERNAL +#include "../DataTypes/TaskIndex.h" +#include "../DataStructs/RTCCacheStruct.h" + # include enum class EEPROMExternal_WriteProtect_e : uint8_t { @@ -25,8 +26,15 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // Write the UserVar-checksum from this offset, right after the UserVar values # define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) -// Offset for storing GPIO states, directly following the UserVar storage and checksum -# define EEPROM_GPIO_MCPPCF_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) +// Even when not enabled, we still reserve the space +# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) +# define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) +# define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) +# define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) +# define EEPROM_RTC_CACHE_CHECKSUM_OFFSET (EEPROM_RTC_CACHE_META_CRC_OFFSET + sizeof(uint32_t)) + +// Offset for storing GPIO states, directly following the RTC Cache storage and checksum +# define EEPROM_GPIO_MCPPCF_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) // Choose an arbitrary but fixed offset # define EEPROM_CUSTOM_START_OFFSET (2048) From 3f3205d29da20d6c2de53b9491b2a7ecbc42eb0f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 23 Aug 2025 21:05:45 +0200 Subject: [PATCH 13/29] [Storage] Code improvements --- src/src/Helpers/StringProvider.cpp | 10 ++++++++++ src/src/Helpers/StringProvider.h | 5 +++++ src/src/WebServer/HardwarePage.cpp | 8 ++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index c9f6bdecae..2f9df6cdcf 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -146,6 +146,11 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::SHOW_UOM_ON_DEVICES_PAGE: return F("Show Unit of Measure"); #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE + #if FEATURE_EEPROM_EXTERNAL + case LabelType::EEPROM_RESTORE_ON_COLDBOOT: return F("Cold boot: Restore Task Values from EEPROM"); + case LabelType::EEPROM_RESTORE_ON_WARMBOOT: return F("Warm boot: Restore Task Values from EEPROM"); + #endif // if FEATURE_EEPROM_EXTERNAL + case LabelType::BOOT_TYPE: return F("Last Boot Cause"); case LabelType::BOOT_COUNT: return F("Boot Count"); case LabelType::DEEP_SLEEP_ALTERNATIVE_CALL: return F("Deep Sleep Alternative"); @@ -442,6 +447,11 @@ String getValue(LabelType::Enum label) { case LabelType::SHOW_UOM_ON_DEVICES_PAGE: return jsonBool(Settings.ShowUnitOfMeasureOnDevicesPage()); #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE + #if FEATURE_EEPROM_EXTERNAL + case LabelType::EEPROM_RESTORE_ON_COLDBOOT: return jsonBool(Settings.RestoreUserVarsFromEEPROMOnColdBoot()); + case LabelType::EEPROM_RESTORE_ON_WARMBOOT: return jsonBool(Settings.RestoreUserVarsFromEEPROMOnWarmBoot()); + #endif // if FEATURE_EEPROM_EXTERNAL + case LabelType::BOOT_TYPE: return getLastBootCauseString(); case LabelType::BOOT_COUNT: break; case LabelType::DEEP_SLEEP_ALTERNATIVE_CALL: return jsonBool(Settings.UseAlternativeDeepSleep()); diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index f15d3bc819..c919678b8f 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -94,6 +94,11 @@ struct LabelType { SHOW_UOM_ON_DEVICES_PAGE, #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE + #if FEATURE_EEPROM_EXTERNAL + EEPROM_RESTORE_ON_COLDBOOT, + EEPROM_RESTORE_ON_WARMBOOT, + #endif // if FEATURE_EEPROM_EXTERNAL + BOOT_TYPE, // Cold boot BOOT_COUNT, // 0 RESET_REASON, // Software/System restart diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 7787814d3b..4701c76a68 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -129,8 +129,8 @@ void handle_hardware() { #if FEATURE_EEPROM_EXTERNAL Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); - Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(F("lduvcbee"))); - Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(F("lduvwbee"))); + Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_COLDBOOT)); + Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_WARMBOOT)); # if FEATURE_I2CMULTIPLEXER @@ -384,8 +384,8 @@ void handle_hardware() { if (isEEPROMExternalWriteProtected()) { addHtml(F(" Write-protected!")); } - addFormCheckBox(F("Cold boot: Restore Task Values from EEPROM"), F("lduvcbee"), Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); - addFormCheckBox(F("Warm boot: Restore Task Values from EEPROM"), F("lduvwbee"), Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); + addFormCheckBox(LabelType::EEPROM_RESTORE_ON_COLDBOOT, Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); + addFormCheckBox(LabelType::EEPROM_RESTORE_ON_WARMBOOT, Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); } #endif // if FEATURE_EEPROM_EXTERNAL From 11e96346a6f380464919efa6e5a419c428ea8b58 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 25 Aug 2025 12:44:10 +0200 Subject: [PATCH 14/29] [Storage] Move Settings attributes to next available bits --- src/src/DataStructs/SettingsStruct.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index e6aa54ae0f..a059a72cf5 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -604,11 +604,11 @@ class SettingsStruct_tmpl uint32_t DisableSaveConfigAsTar : 1; // Bit 05 uint32_t PassiveWiFiScan : 1; // Bit 06 // inverted uint32_t ShowUnitOfMeasureOnDevicesPage : 1; // Bit 07 // inverted - uint32_t RestoreUserVarsFromEEPROMOnColdBoot : 1; // Bit 08 - uint32_t RestoreUserVarsFromEEPROMOnWarmBoot : 1; // Bit 09 + uint32_t unused_08 : 1; // Bit 08 + uint32_t unused_09 : 1; // Bit 09 uint32_t unused_10 : 1; // Bit 10 - uint32_t unused_11 : 1; // Bit 11 - uint32_t unused_12 : 1; // Bit 12 + uint32_t RestoreUserVarsFromEEPROMOnColdBoot : 1; // Bit 11 + uint32_t RestoreUserVarsFromEEPROMOnWarmBoot : 1; // Bit 12 uint32_t unused_13 : 1; // Bit 13 uint32_t unused_14 : 1; // Bit 14 uint32_t unused_15 : 1; // Bit 15 From 7c1c2632ca8bdd5920e49a5579317459eb66cd70 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 27 Aug 2025 19:41:15 +0200 Subject: [PATCH 15/29] [Storage] Limit Rules values storage to max. 1024 slots --- src/src/Helpers/EEPROMExternal.cpp | 4 ++-- src/src/Helpers/EEPROMExternal.h | 4 ++-- src/src/WebServer/HardwarePage.cpp | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index 679c34c5e5..20cf799e75 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -226,7 +226,7 @@ uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, taskVarIndex_t varNr) { } /** - * EEPROM available number of slots + * EEPROM available number of slots, max 1024 * NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! */ uint32_t getEEPROMMaxSlots() { @@ -234,7 +234,7 @@ uint32_t getEEPROMMaxSlots() { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if (eepromSize > 0) { - const uint32_t slotMax = ((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t; + const uint32_t slotMax = min((unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t), 1024ul); return slotMax; } diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index 989c0a1198..efe9c2bd53 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -41,9 +41,9 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! # if FEATURE_STRING_VARIABLES -# define EEPROM_CUSTOM_DIVISOR (2) // Split in slots- and strings- halves +# define EEPROM_CUSTOM_DIVISOR (2u) // Split in slots- and strings- halves # else // if FEATURE_STRING_VARIABLES -# define EEPROM_CUSTOM_DIVISOR (1) // Use all for slots +# define EEPROM_CUSTOM_DIVISOR (1u) // Use all for slots # endif // if FEATURE_STRING_VARIABLES // Enable/disable some models diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 4701c76a68..c663bf00d6 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -381,8 +381,12 @@ void handle_hardware() { const bool eepromChecked = checkEEPROMEnabled() > 0; addRowLabel(F("EEPROM Enabled")); addEnabled(eepromChecked); - if (isEEPROMExternalWriteProtected()) { + if (eepromChecked && isEEPROMExternalWriteProtected()) { addHtml(F(" Write-protected!")); + } + if (eepromChecked && !isEEPROMExternalWriteProtected()) { + addRowLabel(F("'WriteEE' slots available")); + addHtmlInt(getEEPROMMaxSlots()); } addFormCheckBox(LabelType::EEPROM_RESTORE_ON_COLDBOOT, Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); addFormCheckBox(LabelType::EEPROM_RESTORE_ON_WARMBOOT, Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); From 8c7a88a793e1af19f7dbe52cb7b3f95cfff25f01 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 27 Aug 2025 19:44:32 +0200 Subject: [PATCH 16/29] [Storage] Separate EEPROM and FRAM types in settings --- src/src/DataStructs/DeviceStruct.h | 2 +- src/src/DataStructs_templ/SettingsStruct.cpp | 4 +- src/src/Helpers/EEPROMExternal.cpp | 42 +++++++++++++++++--- src/src/Helpers/EEPROMExternal.h | 17 ++++++-- src/src/WebServer/HardwarePage.cpp | 22 ++++++++++ 5 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 8762fcf8ce..98ca671c23 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -52,7 +52,7 @@ #if FEATURE_EEPROM_EXTERNAL #define EEPROM_EXTERNAL_FLAGS_ADDRESS 0 // bit-offset for the I2C Address (8 bits) -#define EEPROM_EXTERNAL_FLAGS_SIZE 8 // bit-offset for the size-id of the EEPROM (3 bits) +#define EEPROM_EXTERNAL_FLAGS_SIZE 8 // bit-offset for the size-id of the EEPROM (4 bits) #define EEPROM_EXTERNAL_FLAGS_MUX 16 // bit-offset for the multiplexer flags of the EEPROM (16 bits) #define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 7f64bb962f..6322f7a21b 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1160,11 +1160,11 @@ void SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags(uint16_t mu } template uint8_t SettingsStruct_tmpl::EEPROMExternalType() const { - return static_cast(get3BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); + return static_cast(get4BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); } template void SettingsStruct_tmpl::EEPROMExternalType(uint8_t type) { - set3BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); + set4BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); } #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/src/Helpers/EEPROMExternal.cpp index 20cf799e75..8244b05ef1 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/src/Helpers/EEPROMExternal.cpp @@ -106,22 +106,29 @@ uint8_t selectEEPROMI2CBusAndMultiplexer() { uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { switch (type) { case EEPROMExternal_Type_e::AT24C256: + case EEPROMExternal_Type_e::MB85RC256: return 32768; case EEPROMExternal_Type_e::AT24C512: + case EEPROMExternal_Type_e::MB85RC512: return 65536; # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: + case EEPROMExternal_Type_e::MB85RC1M: return 131072; # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: + case EEPROMExternal_Type_e::MB85RC2M: return 262144; # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: + case EEPROMExternal_Type_e::MB85RC32: return 4096; case EEPROMExternal_Type_e::AT24C64: + case EEPROMExternal_Type_e::MB85RC64: return 8192; case EEPROMExternal_Type_e::AT24C128: + case EEPROMExternal_Type_e::MB85RC128: return 16384; } return 0; @@ -136,22 +143,29 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, switch (type) { case EEPROMExternal_Type_e::AT24C256: + case EEPROMExternal_Type_e::MB85RC256: pageSize = 64; case EEPROMExternal_Type_e::AT24C512: + case EEPROMExternal_Type_e::MB85RC512: pageSize = 128; # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: + case EEPROMExternal_Type_e::MB85RC1M: pageSize = 128; # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: + case EEPROMExternal_Type_e::MB85RC2M: pageSize = 128; # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: + case EEPROMExternal_Type_e::MB85RC32: pageSize = 32; case EEPROMExternal_Type_e::AT24C64: + case EEPROMExternal_Type_e::MB85RC64: pageSize = 32; case EEPROMExternal_Type_e::AT24C128: + case EEPROMExternal_Type_e::MB85RC128: pageSize = 64; } return getEEPROMSize(type); @@ -165,12 +179,12 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { switch (type) { case EEPROMExternal_Type_e::AT24C256: - return F("AT24C256 / MB85RC256"); + return F("AT24C256"); case EEPROMExternal_Type_e::AT24C512: - return F("AT24C512 / MB85RC512"); + return F("AT24C512"); # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: - return F("AT24C1024 / MB85RC1M"); + return F("AT24C1024"); # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: @@ -179,9 +193,27 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { case EEPROMExternal_Type_e::AT24C32: return F("AT24C32"); case EEPROMExternal_Type_e::AT24C64: - return F("AT24C64 / MB85RC64"); + return F("AT24C64"); case EEPROMExternal_Type_e::AT24C128: - return F("AT24C128 / MB85RC128"); + return F("AT24C128"); + case EEPROMExternal_Type_e::MB85RC256: + return F("MB85RC256"); + case EEPROMExternal_Type_e::MB85RC512: + return F("MB85RC512"); + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::MB85RC1M: + return F("MB85RC1M"); + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::MB85RC2M: + return F("MB85RC2M"); + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::MB85RC32: + return F("MB85RC32"); + case EEPROMExternal_Type_e::MB85RC64: + return F("MB85RC64"); + case EEPROMExternal_Type_e::MB85RC128: + return F("MB85RC128"); } return F(""); # else // ifndef BUILD_NO_DEBUG diff --git a/src/src/Helpers/EEPROMExternal.h b/src/src/Helpers/EEPROMExternal.h index efe9c2bd53..90ee0c4dde 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/src/Helpers/EEPROMExternal.h @@ -3,8 +3,8 @@ #if FEATURE_EEPROM_EXTERNAL -#include "../DataTypes/TaskIndex.h" -#include "../DataStructs/RTCCacheStruct.h" +# include "../DataTypes/TaskIndex.h" +# include "../DataStructs/RTCCacheStruct.h" # include @@ -50,7 +50,7 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; # define EEPROM_SUPPORT_AT24C1024 1 # define EEPROM_SUPPORT_AT24C2048 0 -// Supported AT24Cxxx devices +// Supported AT24Cxxx and MB85RCxxx devices enum class EEPROMExternal_Type_e : uint8_t { AT24C256 = 0, // Default, 32 kB AT24C512 = 1, // 64 kB @@ -63,6 +63,17 @@ enum class EEPROMExternal_Type_e : uint8_t { AT24C32 = 4, // 4 kB, not endorsed, but widely available types: AT24C64 = 5, // 8 kB AT24C128 = 6, // 16 kB + MB85RC256 = 7, // 32 kB + MB85RC512 = 8, // 64 kB + # if EEPROM_SUPPORT_AT24C1024 + MB85RC1M = 9, // 128 kB + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + MB85RC2M = 10, // 256 kB (not supported yet) + # endif // if EEPROM_SUPPORT_AT24C2048 + MB85RC32 = 11, // 4 kB, not endorsed, but widely available types: + MB85RC64 = 12, // 8 kB + MB85RC128 = 13, // 16 kB }; uint8_t checkEEPROMEnabled(); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c663bf00d6..a53556731d 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -333,6 +333,17 @@ void handle_hardware() { getEEPROMName(EEPROMExternal_Type_e::AT24C32), getEEPROMName(EEPROMExternal_Type_e::AT24C64), getEEPROMName(EEPROMExternal_Type_e::AT24C128), + getEEPROMName(EEPROMExternal_Type_e::MB85RC256), + getEEPROMName(EEPROMExternal_Type_e::MB85RC512), + #if EEPROM_SUPPORT_AT24C1024 + getEEPROMName(EEPROMExternal_Type_e::MB85RC1M), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(EEPROMExternal_Type_e::MB85RC2M), + #endif // if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(EEPROMExternal_Type_e::MB85RC32), + getEEPROMName(EEPROMExternal_Type_e::MB85RC64), + getEEPROMName(EEPROMExternal_Type_e::MB85RC128), }; const int eepromTypes[] = { static_cast(EEPROMExternal_Type_e::AT24C256), @@ -346,6 +357,17 @@ void handle_hardware() { static_cast(EEPROMExternal_Type_e::AT24C32), static_cast(EEPROMExternal_Type_e::AT24C64), static_cast(EEPROMExternal_Type_e::AT24C128), + static_cast(EEPROMExternal_Type_e::MB85RC256), + static_cast(EEPROMExternal_Type_e::MB85RC512), + #if EEPROM_SUPPORT_AT24C1024 + static_cast(EEPROMExternal_Type_e::MB85RC1M), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + static_cast(EEPROMExternal_Type_e::MB85RC2M), + #endif // if EEPROM_SUPPORT_AT24C2048 + static_cast(EEPROMExternal_Type_e::MB85RC32), + static_cast(EEPROMExternal_Type_e::MB85RC64), + static_cast(EEPROMExternal_Type_e::MB85RC128), }; constexpr uint8_t eepromSizeCount = NR_ELEMENTS(eepromTypes); FormSelectorOptions eepromSizeSelector(eepromSizeCount, eepromOptions, eepromTypes); From bd67cc8d30b3be4076ee589112b265906cd41000 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 27 Aug 2025 19:47:42 +0200 Subject: [PATCH 17/29] [Storage] Add setting per Task Value for saving in EEPROM (enabled by default) --- src/src/DataStructs/Caches.cpp | 18 ++++++++++++ src/src/DataStructs/Caches.h | 10 ++++++- .../DataStructs/ExtraTaskSettingsStruct.cpp | 13 +++++++++ src/src/DataStructs/ExtraTaskSettingsStruct.h | 12 ++++++++ src/src/Helpers/ESPEasyRTC.cpp | 27 +++++++++++++---- src/src/WebServer/DevicesPage.cpp | 29 +++++++++++++++++++ src/src/WebServer/EpromVarPage.cpp | 2 +- 7 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/src/DataStructs/Caches.cpp b/src/src/DataStructs/Caches.cpp index b3fc6ac5c8..b4604a6917 100644 --- a/src/src/DataStructs/Caches.cpp +++ b/src/src/DataStructs/Caches.cpp @@ -324,6 +324,21 @@ uint8_t Caches::getTaskVarCustomVType(taskIndex_t TaskIndex, #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE +#if FEATURE_EEPROM_EXTERNAL +uint8_t Caches::getTaskVarStoreInEEPROM(taskIndex_t TaskIndex, + taskVarIndex_t taskVarIndex) { + if (validTaskIndex(TaskIndex) && (validTaskVarIndex(taskVarIndex))) { + auto it = getExtraTaskSettings(TaskIndex); + + if (it != extraTaskSettings_cache.end()) { + return it->second.storeInEEPROM[taskVarIndex]; + } + } + return false; +} + +#endif // if FEATURE_EEPROM_EXTERNAL + void Caches::updateExtraTaskSettingsCache() { const taskIndex_t TaskIndex = ExtraTaskSettings.TaskIndex; @@ -374,6 +389,9 @@ void Caches::updateExtraTaskSettingsCache() #if FEATURE_CUSTOM_TASKVAR_VTYPE tmp.customVType[i] = ExtraTaskSettings.getTaskVarCustomVType(i); #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE + #if FEATURE_EEPROM_EXTERNAL + tmp.storeInEEPROM[i] = ExtraTaskSettings.getTaskVarStoreInEEPROM(i); + #endif // if FEATURE_EEPROM_EXTERNAL } #ifdef ESP32 tmp.TaskDevicePluginConfigLong_index_used = 0; diff --git a/src/src/DataStructs/Caches.h b/src/src/DataStructs/Caches.h index 46fcd0f64f..f4ba0d166e 100644 --- a/src/src/DataStructs/Caches.h +++ b/src/src/DataStructs/Caches.h @@ -57,6 +57,9 @@ struct ExtraTaskSettings_cache_t { #if FEATURE_CUSTOM_TASKVAR_VTYPE uint8_t customVType[VARS_PER_TASK] = { 0 }; // single-value VType per taskValue #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE + #if FEATURE_EEPROM_EXTERNAL + bool storeInEEPROM[VARS_PER_TASK]{}; + #endif // if FEATURE_EEPROM_EXTERNAL }; typedef std::map TaskIndexNameMap; @@ -123,7 +126,12 @@ struct Caches { uint8_t getTaskVarCustomVType(taskIndex_t taskIndex, taskVarIndex_t taskVarIndex); #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE - + + #if FEATURE_EEPROM_EXTERNAL + uint8_t getTaskVarStoreInEEPROM(taskIndex_t taskIndex, + taskVarIndex_t taskVarIndex); + #endif // if FEATURE_EEPROM_EXTERNAL + // Update all cached values, except the checksum. void updateExtraTaskSettingsCache(); diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp index 1a474eafd5..a5a9366ea2 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -290,6 +290,19 @@ void ExtraTaskSettingsStruct::setTaskVarCustomVType(taskVarIndex_t taskVarIndex, } #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE +#if FEATURE_EEPROM_EXTERNAL +bool ExtraTaskSettingsStruct::getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const { // Inverted + if (!validTaskVarIndex(taskVarIndex)) { return false; } + return !bitRead(VariousBits[taskVarIndex], 24); +} + +void ExtraTaskSettingsStruct::setTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex, + bool store) { // Inverted + if (validTaskVarIndex(taskVarIndex)) { + bitWrite(VariousBits[taskVarIndex], 24, !store); + } +} +#endif // if FEATURE_EEPROM_EXTERNAL void ExtraTaskSettingsStruct::populateDeviceValueNamesSeq( const __FlashStringHelper *valuename, diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index 687ebe5577..7b9609e4d9 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -84,6 +84,12 @@ struct ExtraTaskSettingsStruct uint8_t customVType); #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE + #if FEATURE_EEPROM_EXTERNAL + bool getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const; + void setTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex, + bool store); + #endif // if FEATURE_EEPROM_EXTERNAL + void populateDeviceValueNamesSeq(const __FlashStringHelper *valuename, size_t nrValues, uint8_t defaultDecimals, @@ -102,6 +108,12 @@ struct ExtraTaskSettingsStruct float TaskDeviceMaxValue[VARS_PER_TASK]; float TaskDeviceErrorValue[VARS_PER_TASK]; uint32_t VariousBits[VARS_PER_TASK]; + /** Mapping of VariousBits: + * - 0..7 : PluginStats config (8 bits) + * - 8..15 : UnitOfMeasure index (8 bits) + * - 16..23 : CustomValueType index (8 bits) + * - 24 : Store value in EEPROM (1 bit, inverted) + */ }; diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 92b5528787..f3aa98f958 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -13,9 +13,13 @@ #include "../../ESPEasy_common.h" #if FEATURE_EEPROM_EXTERNAL +#include "../Globals/Cache.h" #include "../Helpers/EEPROMExternal.h" +#include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_I2C.h" #include "../Helpers/StringConverter.h" + +uint8_t readDataForUserVars(size_t index); // Forward declaration #endif // if FEATURE_EEPROM_EXTERNAL #ifdef ESP8266 @@ -195,11 +199,13 @@ bool saveUserVarToRTC(bool initial) for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); if (taskValues != nullptr) { + LoadTaskSettings(task); for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const size_t index = (task * VARS_PER_TASK) + varNr; - const uint32_t newData = taskValues->getUint32(varNr); // Only update EEPROM if data differs + const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) + ? taskValues->getUint32(varNr) + : std::numeric_limits::max(); // NaN when read as float const uint32_t addr = getEEPROMAddressForTaskValue(task, varNr); - if (newData != EEPROMExternal->readLong(addr)) { + if (newData != EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs EEPROMExternal->writeLong(addr, newData); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; @@ -208,8 +214,10 @@ bool saveUserVarToRTC(bool initial) } } } + // Calculate checksum for all stored values, not equal to the UserVar checksum! + const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, - UserVar.compute_CRC32()); + calcsum); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; startmicros = micros() - startmicros; @@ -295,14 +303,21 @@ bool readUserVarFromRTC() if (calcsum == checksum) { addLog(LOG_LEVEL_INFO, F("INIT : Restoring Task Values from EEPROM.")); result = true; + taskIndex_t lastTask = TASKS_MAX; for (size_t i = 0; i < (TASKS_MAX * VARS_PER_TASK) && result; ++i) { const taskIndex_t taskIndex = i / VARS_PER_TASK; const uint8_t varNr = i % VARS_PER_TASK; + if (taskIndex != lastTask) { + LoadTaskSettings(taskIndex); + lastTask = taskIndex; + } // Store in raw form, so we don't apply formula as we don't really know what type is required. const uint32_t addr = getEEPROMAddressForTaskValue(taskIndex, varNr); if (addr != std::numeric_limits::max()) { - TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(taskIndex); - taskValues->setUint32(varNr, EEPROMExternal->readLong(addr)); + if (Cache.getTaskVarStoreInEEPROM(taskIndex, varNr)) { // Restore only when enabled + TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(taskIndex); + taskValues->setUint32(varNr, EEPROMExternal->readLong(addr)); + } } else { result = false; } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 70a2fc6c21..c8c015a3d4 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -29,6 +29,9 @@ # include "../Helpers/_Plugin_init.h" # include "../Helpers/_Plugin_SensorTypeHelper.h" # include "../Helpers/_Plugin_Helper_serial.h" +#if FEATURE_EEPROM_EXTERNAL +# include "../Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" # include "../Helpers/StringConverter.h" @@ -426,6 +429,12 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # if FEATURE_PLUGIN_FILTER ExtraTaskSettings.enablePluginFilter(varNr, isFormItemChecked(getPluginCustomArgName(F("TDFIL"), varNr))); # endif // if FEATURE_PLUGIN_FILTER + #if FEATURE_EEPROM_EXTERNAL + if (checkEEPROMEnabled() > 0) { + ExtraTaskSettings.setTaskVarStoreInEEPROM(varNr, isFormItemChecked(getPluginCustomArgName(F("TDEE"), varNr))); + } + #endif // if FEATURE_EEPROM_EXTERNAL + # if FEATURE_PLUGIN_STATS PluginStats_Config_t pluginStats_Config; pluginStats_Config.setEnabled(isFormItemChecked(getPluginCustomArgName(F("TDS"), varNr))); @@ -1705,6 +1714,13 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde ++colCount; } + #if FEATURE_EEPROM_EXTERNAL + if (checkEEPROMEnabled() > 0) { + html_table_header(F("Eeprom"), 30); + ++colCount; + } + #endif // if FEATURE_EEPROM_EXTERNAL + # if FEATURE_PLUGIN_STATS if (device.PluginStats) @@ -1776,6 +1792,19 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde addNumericBox(id, Cache.getTaskDeviceValueDecimals(taskIndex, varNr), 0, 6); } + #if FEATURE_EEPROM_EXTERNAL + if (checkEEPROMEnabled() > 0) { + html_TD(); + addCheckBox( + getPluginCustomArgName(F("TDEE"), varNr), + Cache.getTaskVarStoreInEEPROM(taskIndex, varNr), false + #if FEATURE_TOOLTIPS + , F("Store value in External EEPROM") + #endif // if FEATURE_TOOLTIPS + ); + } + #endif // if FEATURE_EEPROM_EXTERNAL + # if FEATURE_PLUGIN_STATS if (device.PluginStats) diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index 54cc4f859b..ad6fdf415e 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -67,7 +67,7 @@ void handle_eepromvars() { const float value = EEPROMExternal->readFloat(addr); const uint32_t data = EEPROMExternal->readLong(addr); - if (isnan(value) || (addr == std::numeric_limits::max())) { + if (isnan(value) || (addr == std::numeric_limits::max()) || !ExtraTaskSettings.getTaskVarStoreInEEPROM(var)) { addHtml('-'); } else { addHtml(strformat(F("%s (0x%04x)"), floatToString(value, ExtraTaskSettings.TaskDeviceValueDecimals[var]), data)); From 6a3cc81cc486c677c06017baf885b98e3c06f594 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 27 Aug 2025 19:49:01 +0200 Subject: [PATCH 18/29] [Storage] Add [ReadEE#WP] to retrieve the Write Protected state of the EEPROM, 1 = write-protected --- src/src/Helpers/StringParser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index 18448dbc3a..8db683d97a 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -211,6 +211,8 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us value = toString(readEEPROMSlot(slot)); } else if (valueName.equalsIgnoreCase(F("max"))) { value = getEEPROMMaxSlots(); + } else if (valueName.equalsIgnoreCase(F("wp"))) { + value = isEEPROMExternalWriteProtected() ? 1 : 0; } if (!value.isEmpty()) { transformValue( From a1134640689b8e956149856808a9ac3a8868f846 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 27 Aug 2025 20:36:19 +0200 Subject: [PATCH 19/29] [Storage] Simplify EpromVarPage by only showing enabled tasks, and not show hex display for 0 values --- src/src/WebServer/EpromVarPage.cpp | 61 +++++++++++++++++------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index ad6fdf415e..c3ae9eb695 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -24,7 +24,8 @@ void handle_eepromvars() { if (!isLoggedIn()) { return; } - const bool showTasks = getFormItemInt(F("tasks"), 0) != 0; + const bool showTasks = getFormItemInt(F("tasks"), 1) == 1; + const bool allTasks = getFormItemInt(F("enabled"), 0) != 0; TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); @@ -42,38 +43,46 @@ void handle_eepromvars() { html_TR(); // sub-table header - html_table_header(F("Task"), 300); - html_table_header(F("Value"), 500); - html_table_header(F("Content"), 400); + html_table_header(allTasks ? F("Task") : F("Task (Enabled)"), 300); + html_table_header(F("Value"), 500); + html_table_header(F("Content"), 400); html_table_header(F("")); for (taskIndex_t tsk = 0; tsk < TASKS_MAX; ++tsk) { - LoadTaskSettings(tsk); - html_TR_TD(); - addHtmlInt(tsk + 1); - addHtml(' '); - addHtml(getTaskDeviceName(tsk)); + const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(tsk); - for (taskVarIndex_t var = 0; var < VARS_PER_TASK; ++var) { - if (var != 0) { - html_TR_TD(); - } - html_TD(); - addHtmlInt(var + 1); + if ((Settings.TaskDeviceEnabled[tsk] && validDeviceIndex(DeviceIndex)) || allTasks) { + LoadTaskSettings(tsk); + html_TR_TD(); + addHtmlInt(tsk + 1); addHtml(' '); - addHtml(getTaskValueName(tsk, var)); - html_TD(); - const uint32_t addr = getEEPROMAddressForTaskValue(tsk, var); - const float value = EEPROMExternal->readFloat(addr); - const uint32_t data = EEPROMExternal->readLong(addr); - - if (isnan(value) || (addr == std::numeric_limits::max()) || !ExtraTaskSettings.getTaskVarStoreInEEPROM(var)) { - addHtml('-'); - } else { - addHtml(strformat(F("%s (0x%04x)"), floatToString(value, ExtraTaskSettings.TaskDeviceValueDecimals[var]), data)); + addHtml(getTaskDeviceName(tsk)); + + for (taskVarIndex_t var = 0; var < VARS_PER_TASK; ++var) { + if (var != 0) { + html_TR_TD(); + } + html_TD(); + addHtmlInt(var + 1); + addHtml(' '); + addHtml(getTaskValueName(tsk, var)); + html_TD(); + const uint32_t addr = getEEPROMAddressForTaskValue(tsk, var); + const float value = EEPROMExternal->readFloat(addr); + const uint32_t data = EEPROMExternal->readLong(addr); + + if (isnan(value) || (addr == std::numeric_limits::max()) || !ExtraTaskSettings.getTaskVarStoreInEEPROM(var)) { + addHtml('-'); + } else { + addHtml(floatToString(value, ExtraTaskSettings.TaskDeviceValueDecimals[var])); + + if (!essentiallyZero(value)) { // Only show hex values for non-zero values to reduce eye-strain + addHtml(strformat(F(" (0x%04x)"), data)); + } + } } + delay(0); } - delay(0); } } html_TR(); From 1708c737cb1782c9b21293f182a5985bc6e69cdf Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 28 Aug 2025 13:08:27 +0200 Subject: [PATCH 20/29] [Storage] Move EEPROM code to ESPEasy::eeprom namespace, store and check EEPROM parameters --- .../eeprom}/Helpers/EEPROMExternal.cpp | 168 +++++++++++++++--- .../eeprom}/Helpers/EEPROMExternal.h | 57 ++++-- src/src/Commands/EEPROMExternal.cpp | 14 +- .../DataStructs/RTC_cache_handler_struct.cpp | 32 ++-- src/src/ESPEasyCore/ESPEasy_setup.cpp | 4 +- src/src/Helpers/ESPEasyRTC.cpp | 50 +++--- src/src/Helpers/Hardware_I2C.cpp | 56 +----- src/src/Helpers/StringParser.cpp | 8 +- src/src/WebServer/DevicesPage.cpp | 8 +- src/src/WebServer/EpromVarPage.cpp | 23 +-- src/src/WebServer/HardwarePage.cpp | 69 +++---- 11 files changed, 299 insertions(+), 190 deletions(-) rename src/{src => ESPEasy/eeprom}/Helpers/EEPROMExternal.cpp (61%) rename src/{src => ESPEasy/eeprom}/Helpers/EEPROMExternal.h (62%) diff --git a/src/src/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp similarity index 61% rename from src/src/Helpers/EEPROMExternal.cpp rename to src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp index 8244b05ef1..1c4bbb98ac 100644 --- a/src/src/Helpers/EEPROMExternal.cpp +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -1,16 +1,142 @@ #include "../Helpers/EEPROMExternal.h" -#include "../Globals/Settings.h" -#include "../Helpers/I2C_access.h" -#include "../../ESPEasy_common.h" -#include "../Helpers/StringConverter.h" +#include "../../../src/Globals/Settings.h" +#include "../../../src/Helpers/I2C_access.h" +#include "../../../ESPEasy_common.h" +#include "../../../src/Helpers/StringConverter.h" #if FEATURE_EEPROM_EXTERNAL +namespace ESPEasy { +namespace eeprom { AT24CX *EEPROMExternal = nullptr; EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); +/** + * Initialize the external EEPROM device and variables + */ +void initializeEEPROMExternal() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if ((nullptr != ESPEasy::eeprom::EEPROMExternal) || (eepromAddress == 0)) { // Cleanup when turning off EEPROM + delete ESPEasy::eeprom::EEPROMExternal; + ESPEasy::eeprom::EEPROMExternal = nullptr; + ESPEasy::eeprom::EEPROMExternalWriteProtect = ESPEasy::eeprom::EEPROMExternal_WriteProtect_e::Undefined; + } + + if ((nullptr == ESPEasy::eeprom::EEPROMExternal) && (eepromAddress > 0)) { + const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = + static_cast(Settings.EEPROMExternalType()); + + if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // We have an I2C device at this address, let's assume it's an EEPROM... + uint8_t pageSize = 0; + const uint32_t eepromSize = ESPEasy::eeprom::getEEPROMSize(eepromType, pageSize); + ESPEasy::eeprom::EEPROMExternal = new (std::nothrow) AT24CX(eepromAddress, pageSize, eepromSize); + + if (nullptr != ESPEasy::eeprom::EEPROMExternal) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: %s initialized at address 0x%02x"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)), + eepromAddress)); + } + + ESPEasy::eeprom::checkEEPROMExternalWriteProtected(); + + if (ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), + static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: Initialization of %s failed"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); + } + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: No %s found at address 0x%02x"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)), + eepromAddress)); + } + } + + # if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + # if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + # else // if FEATURE_I2C_MULTIPLE + 0 + # endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + # endif // if FEATURE_I2CMULTIPLEXER + } +} + +/** + * Check the stored parameters in the EEPROM with current data and settings + * - Version + * - Max. tasks + * - Vars per tasks + * - RTC Cache address + * - Pinstate address + */ +bool validateEEPROMExternalParameters() { + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); + const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); + const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); + const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); + const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); + + if ((EEPROM_PARAMS_CURRENT_VERSION == eepromVersionParam) && + (TASKS_MAX == eepromTasksMaxParam) && + (VARS_PER_TASK == eepromVarsPerTaskParam) && + (EEPROM_RTC_CACHE_START_OFFSET == eepromRtcCacheParam) && + (EEPROM_GPIO_PINSTATE_START_OFFSET == eepromPinstateParam) && + (EEPROM_GPIO_PINSTATE_END_OFFSET <= EEPROM_CUSTOM_START_OFFSET)) { + return true; + } + + return false; +} + +/** + * Update the stored parameters in EEPROM + * - Version + * - Max. tasks + * - Vars per tasks + * - RTC Cache address + * - Pinstate address + */ +void updateEEPROMExternalParameters() { + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); + const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); + const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); + const uint32_t eepromRtcCacheParam = EEPROMExternal->readInt(EEPROM_PARAMS_RTC_CACHE_ADDRESS); + const uint32_t eepromPinstateParam = EEPROMExternal->readInt(EEPROM_PARAMS_PINSTATE_ADDRESS); + + if (EEPROM_PARAMS_CURRENT_VERSION != eepromVersionParam) { + EEPROMExternal->writeInt(EEPROM_PARAMS_VERSION_ADDRESS, EEPROM_PARAMS_CURRENT_VERSION); + } + + if (TASKS_MAX != eepromTasksMaxParam) { + EEPROMExternal->writeInt(EEPROM_PARAMS_TASKS_MAX, TASKS_MAX); + } + + if (VARS_PER_TASK != eepromVarsPerTaskParam) { + EEPROMExternal->writeInt(EEPROM_PARAMS_VARS_PER_TASK, VARS_PER_TASK); + } + + if (EEPROM_RTC_CACHE_START_OFFSET != eepromRtcCacheParam) { + EEPROMExternal->writeLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS, EEPROM_RTC_CACHE_START_OFFSET); + } + + if (EEPROM_GPIO_PINSTATE_START_OFFSET != eepromPinstateParam) { + EEPROMExternal->writeLong(EEPROM_PARAMS_PINSTATE_ADDRESS, EEPROM_GPIO_PINSTATE_START_OFFSET); + } +} + /** * Check if the EEPROM is properly initialized and enabled. * Returns the I2C address if all is OK @@ -107,29 +233,29 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { switch (type) { case EEPROMExternal_Type_e::AT24C256: case EEPROMExternal_Type_e::MB85RC256: - return 32768; + return 32768ul; case EEPROMExternal_Type_e::AT24C512: case EEPROMExternal_Type_e::MB85RC512: - return 65536; + return 65536ul; # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: case EEPROMExternal_Type_e::MB85RC1M: - return 131072; + return 131072ul; # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: case EEPROMExternal_Type_e::MB85RC2M: - return 262144; + return 262144ul; # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: case EEPROMExternal_Type_e::MB85RC32: - return 4096; + return 4096ul; case EEPROMExternal_Type_e::AT24C64: case EEPROMExternal_Type_e::MB85RC64: - return 8192; + return 8192ul; case EEPROMExternal_Type_e::AT24C128: case EEPROMExternal_Type_e::MB85RC128: - return 16384; + return 16384ul; } return 0; } @@ -144,29 +270,29 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, switch (type) { case EEPROMExternal_Type_e::AT24C256: case EEPROMExternal_Type_e::MB85RC256: - pageSize = 64; + pageSize = 64u; case EEPROMExternal_Type_e::AT24C512: case EEPROMExternal_Type_e::MB85RC512: - pageSize = 128; + pageSize = 128u; # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: case EEPROMExternal_Type_e::MB85RC1M: - pageSize = 128; + pageSize = 128u; # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: case EEPROMExternal_Type_e::MB85RC2M: - pageSize = 128; + pageSize = 128u; # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: case EEPROMExternal_Type_e::MB85RC32: - pageSize = 32; + pageSize = 32u; case EEPROMExternal_Type_e::AT24C64: case EEPROMExternal_Type_e::MB85RC64: - pageSize = 32; + pageSize = 32u; case EEPROMExternal_Type_e::AT24C128: case EEPROMExternal_Type_e::MB85RC128: - pageSize = 64; + pageSize = 64u; } return getEEPROMSize(type); } @@ -266,7 +392,8 @@ uint32_t getEEPROMMaxSlots() { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if (eepromSize > 0) { - const uint32_t slotMax = min((unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t), 1024ul); + const uint32_t slotMax = min((unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t), + 1024ul); return slotMax; } @@ -303,5 +430,6 @@ float readEEPROMSlot(uint32_t slot) { } return 0.0f; } - +} // namespace eeprom +} // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h similarity index 62% rename from src/src/Helpers/EEPROMExternal.h rename to src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index 90ee0c4dde..071bdec32f 100644 --- a/src/src/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -1,13 +1,17 @@ #pragma once -#include "../../ESPEasy_common.h" +#include "../../../ESPEasy_common.h" #if FEATURE_EEPROM_EXTERNAL -# include "../DataTypes/TaskIndex.h" -# include "../DataStructs/RTCCacheStruct.h" +# include "../../../src/DataTypes/TaskIndex.h" +# include "../../../src/DataStructs/PortStatusStruct.h" +# include "../../../src/DataStructs/RTCCacheStruct.h" +# include "../../../src/DataStructs/RTCStruct.h" # include +namespace ESPEasy { +namespace eeprom { enum class EEPROMExternal_WriteProtect_e : uint8_t { Undefined = 0, ReadWrite = 1, @@ -17,9 +21,19 @@ enum class EEPROMExternal_WriteProtect_e : uint8_t { extern AT24CX *EEPROMExternal; extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; +# define EEPROM_PARAMS_CURRENT_VERSION (1) // Let's start with version 1 +# define EEPROM_PINSTATE_ELEMENT_COUNT (64) // Number of PinState elements (16 bytes each) to store in EEPROM + // Start writing the base RTC struct from this offset (not currently saving this to EEPROM) // TODO # define EEPROM_BASERTC_START_OFFSET (0) +// Some system parameters to check before restoring anything +# define EEPROM_PARAMS_VERSION_ADDRESS (32) +# define EEPROM_PARAMS_TASKS_MAX (EEPROM_PARAMS_VERSION_ADDRESS + sizeof(uint16_t)) +# define EEPROM_PARAMS_VARS_PER_TASK (EEPROM_PARAMS_TASKS_MAX + sizeof(uint16_t)) +# define EEPROM_PARAMS_RTC_CACHE_ADDRESS (EEPROM_PARAMS_VARS_PER_TASK + sizeof(uint16_t)) +# define EEPROM_PARAMS_PINSTATE_ADDRESS (EEPROM_PARAMS_RTC_CACHE_ADDRESS + sizeof(uint32_t)) + // Start writing the UserVar values from this offset, should be > sizeof(RTCStruct) that is 32 currently # define EEPROM_USERVAR_START_OFFSET (EEPROM_BASERTC_START_OFFSET + 128) @@ -33,8 +47,10 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; # define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) # define EEPROM_RTC_CACHE_CHECKSUM_OFFSET (EEPROM_RTC_CACHE_META_CRC_OFFSET + sizeof(uint32_t)) -// Offset for storing GPIO states, directly following the RTC Cache storage and checksum -# define EEPROM_GPIO_MCPPCF_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) +// Offset for storing GPIO pinstates, directly following the RTC Cache storage and checksum +# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) +# define EEPROM_PINSTATE_ELEMENT_SIZE (sizeof(uint32_t) + sizeof(portStatusStruct)) +# define EEPROM_GPIO_PINSTATE_END_OFFSET (EEPROM_GPIO_PINSTATE_START_OFFSET + (EEPROM_PINSTATE_ELEMENT_COUNT * EEPROM_PINSTATE_ELEMENT_SIZE)) // Choose an arbitrary but fixed offset # define EEPROM_CUSTOM_START_OFFSET (2048) @@ -52,30 +68,35 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // Supported AT24Cxxx and MB85RCxxx devices enum class EEPROMExternal_Type_e : uint8_t { - AT24C256 = 0, // Default, 32 kB - AT24C512 = 1, // 64 kB + AT24C256 = 0, // Default, 32 kB + AT24C512 = 1, // 64 kB # if EEPROM_SUPPORT_AT24C1024 - AT24C1024 = 2, // 128 kB + AT24C1024 = 2, // 128 kB # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 - AT24C2048 = 3, // 256 kB (not supported yet) + AT24C2048 = 3, // 256 kB (not supported yet) # endif // if EEPROM_SUPPORT_AT24C2048 - AT24C32 = 4, // 4 kB, not endorsed, but widely available types: - AT24C64 = 5, // 8 kB - AT24C128 = 6, // 16 kB + AT24C32 = 4, // 4 kB, not endorsed, but widely available + AT24C64 = 5, // 8 kB + AT24C128 = 6, // 16 kB MB85RC256 = 7, // 32 kB MB85RC512 = 8, // 64 kB # if EEPROM_SUPPORT_AT24C1024 - MB85RC1M = 9, // 128 kB + MB85RC1M = 9, // 128 kB # endif // if EEPROM_SUPPORT_AT24C1024 # if EEPROM_SUPPORT_AT24C2048 - MB85RC2M = 10, // 256 kB (not supported yet) + MB85RC2M = 10, // 256 kB (not supported yet) # endif // if EEPROM_SUPPORT_AT24C2048 - MB85RC32 = 11, // 4 kB, not endorsed, but widely available types: - MB85RC64 = 12, // 8 kB - MB85RC128 = 13, // 16 kB + MB85RC32 = 11, // 4 kB, not endorsed, possibly not available + MB85RC64 = 12, // 8 kB + MB85RC128 = 13, // 16 kB }; +void initializeEEPROMExternal(); + +bool validateEEPROMExternalParameters(); +void updateEEPROMExternalParameters(); + uint8_t checkEEPROMEnabled(); EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced = false); bool isEEPROMExternalWriteProtected(); @@ -94,4 +115,6 @@ uint32_t getEEPROMMaxSlots(); bool writeEEPROMSlot(uint32_t slot, float data); float readEEPROMSlot(uint32_t slot); +} // namespace eeprom +} // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp index 92c94ed6bd..598fb044e7 100644 --- a/src/src/Commands/EEPROMExternal.cpp +++ b/src/src/Commands/EEPROMExternal.cpp @@ -1,5 +1,5 @@ #include "../Commands/EEPROMExternal.h" -#include "../Helpers/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #include "../../ESPEasy_common.h" @@ -21,19 +21,19 @@ const __FlashStringHelper* Command_writeEE(struct EventStruct *event, const char float value{}; if (validUIntFromString(parseString(Line, 2), slot) && validFloatFromString(parseString(Line, 3), value)) { - return return_command_boolean_result_flashstr(writeEEPROMSlot(slot, value)); + return return_command_boolean_result_flashstr(ESPEasy::eeprom::writeEEPROMSlot(slot, value)); } else if (equals(parseString(Line, 2), F("erase")) && equals(parseString(Line, 3), F("erase"))) { - for (uint32_t slot = 0; slot < getEEPROMMaxSlots(); ++slot) { - writeEEPROMSlot(slot, 0.0f); + for (uint32_t slot = 0; slot < ESPEasy::eeprom::getEEPROMMaxSlots(); ++slot) { + ESPEasy::eeprom::writeEEPROMSlot(slot, 0.0f); } addLog(LOG_LEVEL_INFO, F("EEPROM: All slot-values erased.")); return return_command_success_flashstr(); } else if (equals(parseString(Line, 2), F("check")) && equals(parseString(Line, 3), F("wp"))) { addLog(LOG_LEVEL_INFO, F("EEPROM: Check write-protect.")); - checkEEPROMExternalWriteProtected(true); + ESPEasy::eeprom::checkEEPROMExternalWriteProtected(true); - if (isEEPROMExternalWriteProtected()) { - addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(checkEEPROMExternalWriteProtected()))); + if (ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); } return return_command_success_flashstr(); } diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 7e76e497a1..30f4f335e9 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -12,7 +12,7 @@ #include "../ESPEasyCore/ESPEasy_Log.h" #if FEATURE_EEPROM_EXTERNAL -# include "../Helpers/EEPROMExternal.h" +# include "../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL #ifdef ESP8266 @@ -35,45 +35,45 @@ RTC_NOINIT_ATTR uint8_t RTC_cache_data[RTC_CACHE_DATA_SIZE]; **********************************************************************************************/ #if FEATURE_EEPROM_EXTERNAL void EEPROMSaveWritePos() { - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { // Save new position - if (EEPROMExternal->readInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET) != RTC_cache.writePos) { - EEPROMExternal->writeInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET, RTC_cache.writePos); + if (ESPEasy::eeprom::EEPROMExternal->readInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET) != RTC_cache.writePos) { + ESPEasy::eeprom::EEPROMExternal->writeInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET, RTC_cache.writePos); } } } void EEPROMSaveMetaDataChecksum() { - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { // Save metadata checksum - if (EEPROMExternal->readLong(EEPROM_RTC_CACHE_META_CRC_OFFSET) != RTC_cache.checksumMetadata) { - EEPROMExternal->writeLong(EEPROM_RTC_CACHE_META_CRC_OFFSET, RTC_cache.checksumMetadata); + if (ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_RTC_CACHE_META_CRC_OFFSET) != RTC_cache.checksumMetadata) { + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_RTC_CACHE_META_CRC_OFFSET, RTC_cache.checksumMetadata); } } } void EEPROMSaveCacheChecksum() { - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { // Save cache checksum - if (EEPROMExternal->readLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET) != RTC_cache.checksumData) { - EEPROMExternal->writeLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET, RTC_cache.checksumData); + if (ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET) != RTC_cache.checksumData) { + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET, RTC_cache.checksumData); } } } void EEPROMSaveRTCcache() { - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { size_t count{}; for (uint16_t idx = 0; idx < sizeof(RTC_cache); ++idx) { const uint32_t metaAddr = EEPROM_RTC_CACHE_META_OFFSET + idx; - const uint8_t oldData = EEPROMExternal->read(metaAddr); + const uint8_t oldData = ESPEasy::eeprom::EEPROMExternal->read(metaAddr); const uint8_t newData = reinterpret_cast(&RTC_cache)[idx]; if (oldData != newData) { - EEPROMExternal->write(metaAddr, newData); + ESPEasy::eeprom::EEPROMExternal->write(metaAddr, newData); ++count; } } @@ -293,15 +293,15 @@ bool RTC_cache_handler_struct::write(const uint8_t *data, unsigned int size) { } #if FEATURE_EEPROM_EXTERNAL - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { size_t count{}; for (unsigned int idx = 0; idx < size; ++idx) { const uint32_t cacheAddr = EEPROM_RTC_CACHE_START_OFFSET + idx; - const uint8_t oldData = EEPROMExternal->read(cacheAddr); + const uint8_t oldData = ESPEasy::eeprom::EEPROMExternal->read(cacheAddr); const uint8_t newData = data[idx]; if (oldData != newData) { - EEPROMExternal->write(cacheAddr, newData); + ESPEasy::eeprom::EEPROMExternal->write(cacheAddr, newData); ++count; } } diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index ed1345ad78..dcf2a35aaa 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -35,7 +35,7 @@ #include "../WebServer/ESPEasy_WebServer.h" #if FEATURE_EEPROM_EXTERNAL -#include "../Helpers/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL #ifdef USE_RTOS_MULTITASKING @@ -402,7 +402,7 @@ void ESPEasy_setup() #endif // ifndef BUILD_NO_RAM_TRACKER #if FEATURE_EEPROM_EXTERNAL - if ((checkEEPROMEnabled() > 0) && + if ((ESPEasy::eeprom::checkEEPROMEnabled() > 0) && ((lastBootCause <= BOOT_CAUSE_COLD_BOOT) && Settings.RestoreUserVarsFromEEPROMOnColdBoot() || (lastBootCause <= BOOT_CAUSE_SOFT_RESTART) && Settings.RestoreUserVarsFromEEPROMOnWarmBoot()) ) { diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index f3aa98f958..0db0c20faf 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -13,8 +13,8 @@ #include "../../ESPEasy_common.h" #if FEATURE_EEPROM_EXTERNAL +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #include "../Globals/Cache.h" -#include "../Helpers/EEPROMExternal.h" #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_I2C.h" #include "../Helpers/StringConverter.h" @@ -182,15 +182,18 @@ bool saveUserVarToRTC(bool initial) { #if FEATURE_EEPROM_EXTERNAL // Check if we have an external EEPROM available on the configured I2C bus & channel, and save all task values there - const uint8_t eepromAddress = checkEEPROMEnabled(); - if (!initial && (eepromAddress > 0) && !isEEPROMExternalWriteProtected()) { // EEPROM Configured and writable? + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); + if (!initial && (eepromAddress > 0) && !ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { // EEPROM Configured and writable? - if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM #ifndef BUILD_NO_DEBUG uint32_t eepromWritten{}; uint32_t startmicros{}; #endif // ifndef BUILD_NO_DEBUG - const uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + // Update system parameters if not correct + ESPEasy::eeprom::updateEEPROMExternalParameters(); + + const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); if (UserVar.compute_CRC32() != checksum) { // Only save if data changed #ifndef BUILD_NO_DEBUG @@ -204,9 +207,9 @@ bool saveUserVarToRTC(bool initial) const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) ? taskValues->getUint32(varNr) : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = getEEPROMAddressForTaskValue(task, varNr); - if (newData != EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - EEPROMExternal->writeLong(addr, newData); + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); + if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs + ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; #endif // ifndef BUILD_NO_DEBUG @@ -216,8 +219,7 @@ bool saveUserVarToRTC(bool initial) } // Calculate checksum for all stored values, not equal to the UserVar checksum! const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, - calcsum); + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; startmicros = micros() - startmicros; @@ -226,8 +228,10 @@ bool saveUserVarToRTC(bool initial) #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); - addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), eepromWritten, startmicros / 1000.0f, FsP(getEEPROMName(eepromType)))); + const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = + static_cast(Settings.EEPROMExternalType()); + addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), + eepromWritten, startmicros / 1000.0f, FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); } #endif // ifndef BUILD_NO_DEBUG @@ -273,8 +277,8 @@ bool saveUserVarToRTC(bool initial) #if FEATURE_EEPROM_EXTERNAL uint8_t readDataForUserVars(size_t index) { - if (nullptr != EEPROMExternal) { - return EEPROMExternal->read(EEPROM_USERVAR_START_OFFSET + index); + if (nullptr != ESPEasy::eeprom::EEPROMExternal) { + return ESPEasy::eeprom::EEPROMExternal->read(EEPROM_USERVAR_START_OFFSET + index); } return 0; } @@ -286,21 +290,23 @@ uint8_t readDataForUserVars(size_t index) { bool readUserVarFromRTC() { #if FEATURE_EEPROM_EXTERNAL - const uint8_t eepromAddress = checkEEPROMEnabled(); + const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); if (eepromAddress > 0) { // EEPROM Configured and restoring of Task Values enabled? bool result = false; - if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // Check system parameters with last stored values + bool eepromParamsOK = ESPEasy::eeprom::validateEEPROMExternalParameters(); // Check checksum and if correct, restore UserVar values - const uint32_t checksum = EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: readUserVarFromRTC calculated: %u, expected: %u equal: %c"), - calcsum, checksum, calcsum == checksum ? 'Y' : 'n')); + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: readUserVarFromRTC calculated: %u, expected: %u equal: %c, params: %c"), + calcsum, checksum, calcsum == checksum ? 'Y' : 'n', eepromParamsOK ? 'Y' : 'n')); } #endif // ifndef BUILD_NO_DEBUG - if (calcsum == checksum) { + if (eepromParamsOK && (calcsum == checksum)) { addLog(LOG_LEVEL_INFO, F("INIT : Restoring Task Values from EEPROM.")); result = true; taskIndex_t lastTask = TASKS_MAX; @@ -312,11 +318,11 @@ bool readUserVarFromRTC() lastTask = taskIndex; } // Store in raw form, so we don't apply formula as we don't really know what type is required. - const uint32_t addr = getEEPROMAddressForTaskValue(taskIndex, varNr); + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(taskIndex, varNr); if (addr != std::numeric_limits::max()) { if (Cache.getTaskVarStoreInEEPROM(taskIndex, varNr)) { // Restore only when enabled TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(taskIndex); - taskValues->setUint32(varNr, EEPROMExternal->readLong(addr)); + taskValues->setUint32(varNr, ESPEasy::eeprom::EEPROMExternal->readLong(addr)); } } else { result = false; diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index ab4cebec6c..b1c87a0962 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -8,7 +8,7 @@ #include "../Helpers/StringConverter.h" #if FEATURE_EEPROM_EXTERNAL -# include "../Helpers/EEPROMExternal.h" +# include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL #include @@ -86,59 +86,7 @@ void initI2C() { } #if FEATURE_EEPROM_EXTERNAL - const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); - - if ((nullptr != EEPROMExternal) || (eepromAddress == 0)) { // Cleanup when turning off EEPROM - delete EEPROMExternal; - EEPROMExternal = nullptr; - EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; - } - - if ((nullptr == EEPROMExternal) && (eepromAddress > 0)) { - const EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); - - if (0 != selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM - // We have an I2C device at this address, let's assume it's an EEPROM... - uint8_t pageSize = 0; - const uint32_t eepromSize = getEEPROMSize(eepromType, pageSize); - EEPROMExternal = new (std::nothrow) AT24CX(eepromAddress, pageSize, eepromSize); - - if (nullptr != EEPROMExternal) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: %s initialized at address 0x%02x"), - FsP(getEEPROMName(eepromType)), - eepromAddress)); - } - - checkEEPROMExternalWriteProtected(); - - if (isEEPROMExternalWriteProtected()) { - addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(checkEEPROMExternalWriteProtected()))); - } - } else { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: Initialization of %s failed"), - FsP(getEEPROMName(eepromType)))); - } - } - } else { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: No %s found at address 0x%02x"), - FsP(getEEPROMName(eepromType)), - eepromAddress)); - } - } - - # if FEATURE_I2CMULTIPLEXER - I2CMultiplexerOff( - # if FEATURE_I2C_MULTIPLE - Settings.getI2CInterfaceEEPROM() - # else // if FEATURE_I2C_MULTIPLE - 0 - # endif // if FEATURE_I2C_MULTIPLE - ); // Restore the Multiplexer channel - # endif // if FEATURE_I2CMULTIPLEXER - } + ESPEasy::eeprom::initializeEEPROMExternal(); #endif // if FEATURE_EEPROM_EXTERNAL I2CSelectHighClockSpeed(0); // Select first interface by default diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index 8db683d97a..71f04cfaa5 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -22,7 +22,7 @@ #include "../Helpers/StringGenerator_GPIO.h" #if FEATURE_EEPROM_EXTERNAL -#include "../Helpers/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL /********************************************************************************************\ @@ -208,11 +208,11 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us uint32_t slot{}; String value; if (validUIntFromString(valueName, slot)) { - value = toString(readEEPROMSlot(slot)); + value = toString(ESPEasy::eeprom::readEEPROMSlot(slot)); } else if (valueName.equalsIgnoreCase(F("max"))) { - value = getEEPROMMaxSlots(); + value = ESPEasy::eeprom::getEEPROMMaxSlots(); } else if (valueName.equalsIgnoreCase(F("wp"))) { - value = isEEPROMExternalWriteProtected() ? 1 : 0; + value = ESPEasy::eeprom::isEEPROMExternalWriteProtected() ? 1 : 0; } if (!value.isEmpty()) { transformValue( diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index c8c015a3d4..ddaf5cbf3b 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -30,7 +30,7 @@ # include "../Helpers/_Plugin_SensorTypeHelper.h" # include "../Helpers/_Plugin_Helper_serial.h" #if FEATURE_EEPROM_EXTERNAL -# include "../Helpers/EEPROMExternal.h" +# include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #endif // if FEATURE_EEPROM_EXTERNAL # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" @@ -430,7 +430,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task ExtraTaskSettings.enablePluginFilter(varNr, isFormItemChecked(getPluginCustomArgName(F("TDFIL"), varNr))); # endif // if FEATURE_PLUGIN_FILTER #if FEATURE_EEPROM_EXTERNAL - if (checkEEPROMEnabled() > 0) { + if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { ExtraTaskSettings.setTaskVarStoreInEEPROM(varNr, isFormItemChecked(getPluginCustomArgName(F("TDEE"), varNr))); } #endif // if FEATURE_EEPROM_EXTERNAL @@ -1715,7 +1715,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde } #if FEATURE_EEPROM_EXTERNAL - if (checkEEPROMEnabled() > 0) { + if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { html_table_header(F("Eeprom"), 30); ++colCount; } @@ -1793,7 +1793,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde } #if FEATURE_EEPROM_EXTERNAL - if (checkEEPROMEnabled() > 0) { + if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { html_TD(); addCheckBox( getPluginCustomArgName(F("TDEE"), varNr), diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index c3ae9eb695..6f0869d07e 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -10,7 +10,7 @@ #include "../Globals/Settings.h" #include "../Globals/ExtraTaskSettings.h" -#include "../Helpers/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/StringConverter.h" @@ -30,13 +30,16 @@ void handle_eepromvars() { TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); - if (checkEEPROMEnabled() > 0) { + if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { // the table header html_table_class_normal(); html_TR(); - html_table_header(F("External EEPROM"), 300); - html_table_header(getEEPROMName(static_cast(Settings.EEPROMExternalType())), 500); - html_table_header(isEEPROMExternalWriteProtected() ? F("Write-protected!") : F(""), 400); + html_table_header(F("External EEPROM"), + 300); + html_table_header(ESPEasy::eeprom::getEEPROMName(static_cast(Settings.EEPROMExternalType())), + 500); + html_table_header(ESPEasy::eeprom::isEEPROMExternalWriteProtected() ? F("Write-protected!") : F(""), + 400); html_table_header(F("")); if (showTasks) { @@ -67,9 +70,9 @@ void handle_eepromvars() { addHtml(' '); addHtml(getTaskValueName(tsk, var)); html_TD(); - const uint32_t addr = getEEPROMAddressForTaskValue(tsk, var); - const float value = EEPROMExternal->readFloat(addr); - const uint32_t data = EEPROMExternal->readLong(addr); + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(tsk, var); + const float value = ESPEasy::eeprom::EEPROMExternal->readFloat(addr); + const uint32_t data = ESPEasy::eeprom::EEPROMExternal->readLong(addr); if (isnan(value) || (addr == std::numeric_limits::max()) || !ExtraTaskSettings.getTaskVarStoreInEEPROM(var)) { addHtml('-'); @@ -93,11 +96,11 @@ void handle_eepromvars() { html_table_header(F("")); html_table_header(F("")); - const uint32_t maxSlots = getEEPROMMaxSlots(); + const uint32_t maxSlots = ESPEasy::eeprom::getEEPROMMaxSlots(); uint32_t count{}; for (uint32_t slot = 0; slot < maxSlots; ++slot) { - const float value = readEEPROMSlot(slot); + const float value = ESPEasy::eeprom::readEEPROMSlot(slot); if (slot % 50 == 0) { delay(0); } diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index a53556731d..7270321784 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -26,7 +26,7 @@ #endif // if FEATURE_I2C_MULTIPLE #if FEATURE_EEPROM_EXTERNAL -#include "../Helpers/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" #include "../WebServer/DevicesPage.h" // For using ShowI2CMultiplexerUI() and GetI2CMultiplexerFromPage() #endif // if FEATURE_EEPROM_EXTERNAL @@ -127,7 +127,8 @@ void handle_hardware() { #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL #if FEATURE_EEPROM_EXTERNAL - Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), static_cast(EEPROMExternal_Type_e::AT24C256))); + Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256))); Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_COLDBOOT)); Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_WARMBOOT)); @@ -322,52 +323,52 @@ void handle_hardware() { { addFormSubHeader(F("External I2C EEPROM")); const __FlashStringHelper*eepromOptions[] = { - getEEPROMName(EEPROMExternal_Type_e::AT24C256), - getEEPROMName(EEPROMExternal_Type_e::AT24C512), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C512), #if EEPROM_SUPPORT_AT24C1024 - getEEPROMName(EEPROMExternal_Type_e::AT24C1024), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C1024), #endif // if EEPROM_SUPPORT_AT24C1024 #if EEPROM_SUPPORT_AT24C2048 - getEEPROMName(EEPROMExternal_Type_e::AT24C2048), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C2048), #endif // if EEPROM_SUPPORT_AT24C2048 - getEEPROMName(EEPROMExternal_Type_e::AT24C32), - getEEPROMName(EEPROMExternal_Type_e::AT24C64), - getEEPROMName(EEPROMExternal_Type_e::AT24C128), - getEEPROMName(EEPROMExternal_Type_e::MB85RC256), - getEEPROMName(EEPROMExternal_Type_e::MB85RC512), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C32), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C64), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C128), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC256), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC512), #if EEPROM_SUPPORT_AT24C1024 - getEEPROMName(EEPROMExternal_Type_e::MB85RC1M), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC1M), #endif // if EEPROM_SUPPORT_AT24C1024 #if EEPROM_SUPPORT_AT24C2048 - getEEPROMName(EEPROMExternal_Type_e::MB85RC2M), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC2M), #endif // if EEPROM_SUPPORT_AT24C2048 - getEEPROMName(EEPROMExternal_Type_e::MB85RC32), - getEEPROMName(EEPROMExternal_Type_e::MB85RC64), - getEEPROMName(EEPROMExternal_Type_e::MB85RC128), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC32), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC64), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC128), }; const int eepromTypes[] = { - static_cast(EEPROMExternal_Type_e::AT24C256), - static_cast(EEPROMExternal_Type_e::AT24C512), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C512), #if EEPROM_SUPPORT_AT24C1024 - static_cast(EEPROMExternal_Type_e::AT24C1024), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C1024), #endif // if EEPROM_SUPPORT_AT24C1024 #if EEPROM_SUPPORT_AT24C2048 - static_cast(EEPROMExternal_Type_e::AT24C2048), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C2048), #endif // if EEPROM_SUPPORT_AT24C2048 - static_cast(EEPROMExternal_Type_e::AT24C32), - static_cast(EEPROMExternal_Type_e::AT24C64), - static_cast(EEPROMExternal_Type_e::AT24C128), - static_cast(EEPROMExternal_Type_e::MB85RC256), - static_cast(EEPROMExternal_Type_e::MB85RC512), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C32), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C64), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C128), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC256), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC512), #if EEPROM_SUPPORT_AT24C1024 - static_cast(EEPROMExternal_Type_e::MB85RC1M), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC1M), #endif // if EEPROM_SUPPORT_AT24C1024 #if EEPROM_SUPPORT_AT24C2048 - static_cast(EEPROMExternal_Type_e::MB85RC2M), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC2M), #endif // if EEPROM_SUPPORT_AT24C2048 - static_cast(EEPROMExternal_Type_e::MB85RC32), - static_cast(EEPROMExternal_Type_e::MB85RC64), - static_cast(EEPROMExternal_Type_e::MB85RC128), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC32), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC64), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC128), }; constexpr uint8_t eepromSizeCount = NR_ELEMENTS(eepromTypes); FormSelectorOptions eepromSizeSelector(eepromSizeCount, eepromOptions, eepromTypes); @@ -400,15 +401,15 @@ void handle_hardware() { get8BitFromUL(eepromMux, EEPROM_MUX_FLAGS_PORT)); // Re-used from DevicesPage #endif // if FEATURE_I2CMULTIPLEXER - const bool eepromChecked = checkEEPROMEnabled() > 0; + const bool eepromChecked = ESPEasy::eeprom::checkEEPROMEnabled() > 0; addRowLabel(F("EEPROM Enabled")); addEnabled(eepromChecked); - if (eepromChecked && isEEPROMExternalWriteProtected()) { + if (eepromChecked && ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { addHtml(F(" Write-protected!")); } - if (eepromChecked && !isEEPROMExternalWriteProtected()) { + if (eepromChecked && !ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { addRowLabel(F("'WriteEE' slots available")); - addHtmlInt(getEEPROMMaxSlots()); + addHtmlInt(ESPEasy::eeprom::getEEPROMMaxSlots()); } addFormCheckBox(LabelType::EEPROM_RESTORE_ON_COLDBOOT, Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); addFormCheckBox(LabelType::EEPROM_RESTORE_ON_WARMBOOT, Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); From 495f9024a036c4ecba8bd9736677f6d260d6bc5b Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 28 Aug 2025 15:00:26 +0200 Subject: [PATCH 21/29] [Storage] Store and check real UserVar checksum, temp. enable some EEPROM logging at INFO level --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 3 ++- src/src/Helpers/ESPEasyRTC.cpp | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index 071bdec32f..c3de989b09 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -39,9 +39,10 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // Write the UserVar-checksum from this offset, right after the UserVar values # define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) +# define EEPROM_USERVAR_REAL_CHECKSUM (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) // Even when not enabled, we still reserve the space -# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) +# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) # define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) # define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) # define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 0db0c20faf..91c4070955 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -193,7 +193,7 @@ bool saveUserVarToRTC(bool initial) // Update system parameters if not correct ESPEasy::eeprom::updateEEPROMExternalParameters(); - const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); + const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_REAL_CHECKSUM); if (UserVar.compute_CRC32() != checksum) { // Only save if data changed #ifndef BUILD_NO_DEBUG @@ -218,19 +218,27 @@ bool saveUserVarToRTC(bool initial) } } // Calculate checksum for all stored values, not equal to the UserVar checksum! - const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_REAL_CHECKSUM, checksum); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG + const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); + if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); + #ifndef BUILD_NO_DEBUG + eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG + } + #ifndef BUILD_NO_DEBUG startmicros = micros() - startmicros; #endif // ifndef BUILD_NO_DEBUG } #ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { // FIXME LOG_LEVEL_DEBUG const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); - addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), eepromWritten, startmicros / 1000.0f, FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); } #endif // ifndef BUILD_NO_DEBUG @@ -301,7 +309,7 @@ bool readUserVarFromRTC() const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); #ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { // FIXME LOG_LEVEL_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: readUserVarFromRTC calculated: %u, expected: %u equal: %c, params: %c"), calcsum, checksum, calcsum == checksum ? 'Y' : 'n', eepromParamsOK ? 'Y' : 'n')); } From a89685c8dd1c43a3807a381022a3f7ec78f6cd6f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 28 Aug 2025 20:10:26 +0200 Subject: [PATCH 22/29] [Storage] Fix compilation for builds without P146 or C016 included --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp | 15 +++++++++++++-- src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 19 +++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp index 1c4bbb98ac..6f429bcdf4 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -86,13 +86,18 @@ bool validateEEPROMExternalParameters() { const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); - const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); + # if FEATURE_RTC_CACHE_STORAGE + const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); + # endif // if FEATURE_RTC_CACHE_STORAGE + if ((EEPROM_PARAMS_CURRENT_VERSION == eepromVersionParam) && (TASKS_MAX == eepromTasksMaxParam) && (VARS_PER_TASK == eepromVarsPerTaskParam) && + # if FEATURE_RTC_CACHE_STORAGE (EEPROM_RTC_CACHE_START_OFFSET == eepromRtcCacheParam) && + # endif // if FEATURE_RTC_CACHE_STORAGE (EEPROM_GPIO_PINSTATE_START_OFFSET == eepromPinstateParam) && (EEPROM_GPIO_PINSTATE_END_OFFSET <= EEPROM_CUSTOM_START_OFFSET)) { return true; @@ -113,9 +118,12 @@ void updateEEPROMExternalParameters() { const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); - const uint32_t eepromRtcCacheParam = EEPROMExternal->readInt(EEPROM_PARAMS_RTC_CACHE_ADDRESS); const uint32_t eepromPinstateParam = EEPROMExternal->readInt(EEPROM_PARAMS_PINSTATE_ADDRESS); + # if FEATURE_RTC_CACHE_STORAGE + const uint32_t eepromRtcCacheParam = EEPROMExternal->readInt(EEPROM_PARAMS_RTC_CACHE_ADDRESS); + # endif // if FEATURE_RTC_CACHE_STORAGE + if (EEPROM_PARAMS_CURRENT_VERSION != eepromVersionParam) { EEPROMExternal->writeInt(EEPROM_PARAMS_VERSION_ADDRESS, EEPROM_PARAMS_CURRENT_VERSION); } @@ -128,9 +136,12 @@ void updateEEPROMExternalParameters() { EEPROMExternal->writeInt(EEPROM_PARAMS_VARS_PER_TASK, VARS_PER_TASK); } + # if FEATURE_RTC_CACHE_STORAGE + if (EEPROM_RTC_CACHE_START_OFFSET != eepromRtcCacheParam) { EEPROMExternal->writeLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS, EEPROM_RTC_CACHE_START_OFFSET); } + # endif // if FEATURE_RTC_CACHE_STORAGE if (EEPROM_GPIO_PINSTATE_START_OFFSET != eepromPinstateParam) { EEPROMExternal->writeLong(EEPROM_PARAMS_PINSTATE_ADDRESS, EEPROM_GPIO_PINSTATE_START_OFFSET); diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index c3de989b09..62a7f82b55 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -41,15 +41,22 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; # define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) # define EEPROM_USERVAR_REAL_CHECKSUM (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) +# if FEATURE_RTC_CACHE_STORAGE + // Even when not enabled, we still reserve the space -# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) -# define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) -# define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) -# define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) -# define EEPROM_RTC_CACHE_CHECKSUM_OFFSET (EEPROM_RTC_CACHE_META_CRC_OFFSET + sizeof(uint32_t)) +# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) +# define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) +# define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) +# define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) +# define EEPROM_RTC_CACHE_CHECKSUM_OFFSET (EEPROM_RTC_CACHE_META_CRC_OFFSET + sizeof(uint32_t)) +# endif // if FEATURE_RTC_CACHE_STORAGE // Offset for storing GPIO pinstates, directly following the RTC Cache storage and checksum -# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) +# if FEATURE_RTC_CACHE_STORAGE +# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) +# else // if FEATURE_RTC_CACHE_STORAGE +# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) +# endif // if FEATURE_RTC_CACHE_STORAGE # define EEPROM_PINSTATE_ELEMENT_SIZE (sizeof(uint32_t) + sizeof(portStatusStruct)) # define EEPROM_GPIO_PINSTATE_END_OFFSET (EEPROM_GPIO_PINSTATE_START_OFFSET + (EEPROM_PINSTATE_ELEMENT_COUNT * EEPROM_PINSTATE_ELEMENT_SIZE)) From 276d574cd411f198f2e94fca2eb48ea44eb7a1b9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 29 Aug 2025 21:50:21 +0200 Subject: [PATCH 23/29] [Storage] Revert choice of saving all Task Values: Only selected Task Values will be saved in EEPROM --- src/src/DataStructs/ExtraTaskSettingsStruct.cpp | 8 ++++---- src/src/DataStructs/ExtraTaskSettingsStruct.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp index a5a9366ea2..1d8f5ede8c 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -291,15 +291,15 @@ void ExtraTaskSettingsStruct::setTaskVarCustomVType(taskVarIndex_t taskVarIndex, #endif // if FEATURE_CUSTOM_TASKVAR_VTYPE #if FEATURE_EEPROM_EXTERNAL -bool ExtraTaskSettingsStruct::getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const { // Inverted +bool ExtraTaskSettingsStruct::getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const { if (!validTaskVarIndex(taskVarIndex)) { return false; } - return !bitRead(VariousBits[taskVarIndex], 24); + return bitRead(VariousBits[taskVarIndex], 24); } void ExtraTaskSettingsStruct::setTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex, - bool store) { // Inverted + bool store) { if (validTaskVarIndex(taskVarIndex)) { - bitWrite(VariousBits[taskVarIndex], 24, !store); + bitWrite(VariousBits[taskVarIndex], 24, store); } } #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index 7b9609e4d9..5a1d04ba5e 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -112,7 +112,7 @@ struct ExtraTaskSettingsStruct * - 0..7 : PluginStats config (8 bits) * - 8..15 : UnitOfMeasure index (8 bits) * - 16..23 : CustomValueType index (8 bits) - * - 24 : Store value in EEPROM (1 bit, inverted) + * - 24 : Store value in EEPROM (1 bit) */ }; From bb3463818622ae0b37ce0090794a6a61d2707401 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 30 Aug 2025 14:19:37 +0200 Subject: [PATCH 24/29] [Storage] Remove unneeded UserVar checksum write --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 5 +- src/src/Helpers/ESPEasyRTC.cpp | 58 +++++++++------------ 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index 62a7f82b55..f79357c5de 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -39,12 +39,11 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; // Write the UserVar-checksum from this offset, right after the UserVar values # define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) -# define EEPROM_USERVAR_REAL_CHECKSUM (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) # if FEATURE_RTC_CACHE_STORAGE // Even when not enabled, we still reserve the space -# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) +# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) # define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) # define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) # define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) @@ -55,7 +54,7 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; # if FEATURE_RTC_CACHE_STORAGE # define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) # else // if FEATURE_RTC_CACHE_STORAGE -# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_USERVAR_REAL_CHECKSUM + sizeof(uint32_t)) +# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) # endif // if FEATURE_RTC_CACHE_STORAGE # define EEPROM_PINSTATE_ELEMENT_SIZE (sizeof(uint32_t) + sizeof(portStatusStruct)) # define EEPROM_GPIO_PINSTATE_END_OFFSET (EEPROM_GPIO_PINSTATE_START_OFFSET + (EEPROM_PINSTATE_ELEMENT_COUNT * EEPROM_PINSTATE_ELEMENT_SIZE)) diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 91c4070955..135ae1382d 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -193,48 +193,38 @@ bool saveUserVarToRTC(bool initial) // Update system parameters if not correct ESPEasy::eeprom::updateEEPROMExternalParameters(); - const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_REAL_CHECKSUM); - - if (UserVar.compute_CRC32() != checksum) { // Only save if data changed - #ifndef BUILD_NO_DEBUG - startmicros = micros(); - #endif // ifndef BUILD_NO_DEBUG - for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { - const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); - if (taskValues != nullptr) { - LoadTaskSettings(task); - for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) - ? taskValues->getUint32(varNr) - : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); - if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); - #ifndef BUILD_NO_DEBUG - eepromWritten += sizeof_uint32_t; - #endif // ifndef BUILD_NO_DEBUG - } + #ifndef BUILD_NO_DEBUG + startmicros = micros(); + #endif // ifndef BUILD_NO_DEBUG + for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { + const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); + if (taskValues != nullptr) { + LoadTaskSettings(task); + for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { + const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) + ? taskValues->getUint32(varNr) + : std::numeric_limits::max(); // NaN when read as float + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); + if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs + ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); + #ifndef BUILD_NO_DEBUG + eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG } } } - // Calculate checksum for all stored values, not equal to the UserVar checksum! - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_REAL_CHECKSUM, checksum); + } + // Calculate checksum for all stored values, not equal to the UserVar checksum! + const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); + if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); #ifndef BUILD_NO_DEBUG eepromWritten += sizeof_uint32_t; #endif // ifndef BUILD_NO_DEBUG - const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); - #ifndef BUILD_NO_DEBUG - eepromWritten += sizeof_uint32_t; - #endif // ifndef BUILD_NO_DEBUG - } - #ifndef BUILD_NO_DEBUG - startmicros = micros() - startmicros; - #endif // ifndef BUILD_NO_DEBUG } - #ifndef BUILD_NO_DEBUG + startmicros = micros() - startmicros; + if (loglevelActiveFor(LOG_LEVEL_INFO)) { // FIXME LOG_LEVEL_DEBUG const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = static_cast(Settings.EEPROMExternalType()); From a8f25c8fe87b5358319bfb37e006eb128b3a740d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 30 Aug 2025 21:07:00 +0200 Subject: [PATCH 25/29] [Storage] Save only Enabled tasks values in EEPROM --- src/src/Helpers/ESPEasyRTC.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 135ae1382d..2d9ee04fc4 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -191,25 +191,28 @@ bool saveUserVarToRTC(bool initial) uint32_t startmicros{}; #endif // ifndef BUILD_NO_DEBUG // Update system parameters if not correct + const bool paramsOk = ESPEasy::eeprom::validateEEPROMExternalParameters(); ESPEasy::eeprom::updateEEPROMExternalParameters(); #ifndef BUILD_NO_DEBUG startmicros = micros(); #endif // ifndef BUILD_NO_DEBUG for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { - const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); - if (taskValues != nullptr) { - LoadTaskSettings(task); - for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) - ? taskValues->getUint32(varNr) - : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); - if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); - #ifndef BUILD_NO_DEBUG - eepromWritten += sizeof_uint32_t; - #endif // ifndef BUILD_NO_DEBUG + if (Settings.TaskDeviceEnabled[task] || !paramsOk) { // Only check enabled tasks or when re-writing the params + const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); + if (taskValues != nullptr) { + LoadTaskSettings(task); + for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { + const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) + ? taskValues->getUint32(varNr) + : std::numeric_limits::max(); // NaN when read as float + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); + if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs + ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); + #ifndef BUILD_NO_DEBUG + eepromWritten += sizeof_uint32_t; + #endif // ifndef BUILD_NO_DEBUG + } } } } From d969c223268820c26982774e838932b3284d5d01 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 30 Aug 2025 22:39:47 +0200 Subject: [PATCH 26/29] [Storage] Add some missing round brackets --- src/src/ESPEasyCore/ESPEasy_setup.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index dcf2a35aaa..a647531a92 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -403,8 +403,8 @@ void ESPEasy_setup() #if FEATURE_EEPROM_EXTERNAL if ((ESPEasy::eeprom::checkEEPROMEnabled() > 0) && - ((lastBootCause <= BOOT_CAUSE_COLD_BOOT) && Settings.RestoreUserVarsFromEEPROMOnColdBoot() || - (lastBootCause <= BOOT_CAUSE_SOFT_RESTART) && Settings.RestoreUserVarsFromEEPROMOnWarmBoot()) + (((lastBootCause <= BOOT_CAUSE_COLD_BOOT) && Settings.RestoreUserVarsFromEEPROMOnColdBoot()) || + ((lastBootCause <= BOOT_CAUSE_SOFT_RESTART) && Settings.RestoreUserVarsFromEEPROMOnWarmBoot())) ) { readUserVarFromRTC(); // Once more to fetch UserVar data from now available EEPROM/FRAM } From f40334e6a77a7fda5350a2de87dcc7162598cdcf Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 31 Aug 2025 22:27:07 +0200 Subject: [PATCH 27/29] [Storage] Handle EEPROM saving in background, with optional save interval --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp | 156 ++++++++++++++++++ src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 44 +++++ src/src/CustomBuild/define_plugin_sets.h | 23 +++ src/src/DataStructs/DeviceStruct.h | 2 + src/src/DataStructs/SettingsStruct.h | 5 +- src/src/DataStructs_templ/SettingsStruct.cpp | 8 + src/src/Helpers/ESPEasyRTC.cpp | 91 +++++----- src/src/Helpers/ESPEasy_checks.cpp | 2 +- src/src/Helpers/PeriodicalActions.cpp | 7 + src/src/Helpers/StringProvider.cpp | 2 + src/src/Helpers/StringProvider.h | 1 + src/src/WebServer/HardwarePage.cpp | 2 + 12 files changed, 302 insertions(+), 41 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp index 6f429bcdf4..224bb3bd44 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -441,6 +441,162 @@ float readEEPROMSlot(uint32_t slot) { } return 0.0f; } + +# if FEATURE_EEPROM_BACKGROUND + +// A map of tasks to be executed +std::map EEPROMTaskMap; +uint16_t EEPROMSaveDelaySeconds{}; + +/** + * Convert TaskType to text + */ +const __FlashStringHelper* TaskDataTypeToString(EEPROMExternalTaskType_e type) { + switch (type) { + case EEPROMExternalTaskType_e::None: return F(""); + case EEPROMExternalTaskType_e::UserVars: return F("UserVars"); + case EEPROMExternalTaskType_e::ValueSlots: return F("ValueSlots"); + case EEPROMExternalTaskType_e::C016Caches: return F("C016 Cache elements"); + case EEPROMExternalTaskType_e::PinStates: return F("Pinstates"); + } + return F(""); +} + +/** + * Perform the actual task, bij calling the provided function + */ +void EEPROM_execute_task(void *parameter) { + EEPROMExternalTaskData*_task_data = static_cast(parameter); + + if ((_task_data->status == EEPROMExternalTaskState_e::Starting) && (nullptr != _task_data->function)) { + _task_data->status = EEPROMExternalTaskState_e::Processing; + + selectEEPROMI2CBusAndMultiplexer(); + + _task_data->timer.setNow(); + + // Blocking operation + _task_data->data = _task_data->function(); + + // Results are in + _task_data->duration = _task_data->timer.millisPassedSince(); + + # if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + # if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + # else // if FEATURE_I2C_MULTIPLE + 0 + # endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + # endif // if FEATURE_I2CMULTIPLEXER + + _task_data->status = EEPROMExternalTaskState_e::Ready; + } + + _task_data->function = nullptr; // Don't run again accidently + # if FEATURE_EEPROM_RTOS_TASK + _task_data->taskHandle = NULL; + vTaskDelete(_task_data->taskHandle); + # endif // if FEATURE_EEPROM_RTOS_TASK +} + +/** + * Insert a task into the taskmap, 1 per task type, ignore new task when same type is already scheduled to run, or currently running + */ +bool EEPROMAddTask(EEPROMExternalTaskType_e type, + EEPROMExternalTaskData taskData) { + auto task = EEPROMTaskMap.find(type); + + // Is a task is already in progress, skip until the next AddTask + if ((task == EEPROMTaskMap.end()) || (EEPROMExternalTaskState_e::Available == task->second.status)) { + EEPROMTaskMap[type] = taskData; // Insert task + task = EEPROMTaskMap.find(type); + + if (task != EEPROMTaskMap.end()) { // Upserted successfully + task->second.status = EEPROMExternalTaskState_e::Starting; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, + strformat(F("EEPROM: AddTask upserted for %s, status %u (tasks: %u)" + # if FEATURE_EEPROM_RTOS_TASK + " (RTOS)" + # endif // if FEATURE_EEPROM_RTOS_TASK + ), + FsP(TaskDataTypeToString(task->second.type)), static_cast(task->second.status), EEPROMTaskMap.size())); + } + # endif // ifndef BUILD_NO_DEBUG + } else { + addLog(LOG_LEVEL_ERROR, F("EEPROM: Task not inserted")); + } + + # if FEATURE_EEPROM_RTOS_TASK + EEPROMExternalLoop(); // Kick off if it's a RTOS task + # endif // if FEATURE_EEPROM_RTOS_TASK + return true; + } + return false; +} + +/** + * Check the task map for tasks to run and start that, also report about finished tasks, and clean those for re-use + */ +bool EEPROMExternalLoop() { + if (0 != EEPROMSaveDelaySeconds) { + EEPROMSaveDelaySeconds--; + return false; + } + EEPROMSaveDelaySeconds = Settings.EEPROMSaveDelaySeconds(); + + for (auto task = EEPROMTaskMap.begin(); task != EEPROMTaskMap.end(); ++task) { + if ((EEPROMExternalTaskState_e::Ready == task->second.status) || + (EEPROMExternalTaskState_e::Error == task->second.status)) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: Write %s %s %d in %u msec." + # if FEATURE_EEPROM_RTOS_TASK + " (RTOS)" + # endif // if FEATURE_EEPROM_RTOS_TASK + ), + FsP(TaskDataTypeToString(task->second.type)), + FsP(EEPROMExternalTaskState_e::Ready == task->second.status ? F("success") : F("failed")), + task->second.data, + task->second.duration)); + } + task->second.status = EEPROMExternalTaskState_e::Available; + } + + if (EEPROMExternalTaskState_e::Starting == task->second.status) { + if (nullptr != task->second.function) { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: Loop starting task: %s"), FsP(TaskDataTypeToString(task->second.type)))); + } + # endif // ifndef BUILD_NO_DEBUG + # if FEATURE_EEPROM_RTOS_TASK + xTaskCreatePinnedToCore( + EEPROM_execute_task, // Function that should be called + "EEPROM.write()", // Name of the task (for debugging) + 4000, // Stack size (bytes) + &task->second, // Parameter to pass + 1, // Task priority + &task->second.taskHandle, // Task handle + xPortGetCoreID() // Core you want to run the task on (0 or 1) + ); + # else // if FEATURE_EEPROM_RTOS_TASK + EEPROM_execute_task(&task->second); + # endif // if FEATURE_EEPROM_RTOS_TASK + } else { + task->second.status = EEPROMExternalTaskState_e::Available; // Recycle + } + } + } + return false; +} + +# endif // if FEATURE_EEPROM_BACKGROUND } // namespace eeprom } // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index f79357c5de..101f66f83a 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -7,6 +7,8 @@ # include "../../../src/DataStructs/PortStatusStruct.h" # include "../../../src/DataStructs/RTCCacheStruct.h" # include "../../../src/DataStructs/RTCStruct.h" +# include "../../../src/Helpers/LongTermTimer.h" +# include # include @@ -122,6 +124,48 @@ uint32_t getEEPROMMaxSlots(); bool writeEEPROMSlot(uint32_t slot, float data); float readEEPROMSlot(uint32_t slot); + +# if FEATURE_EEPROM_BACKGROUND +enum class EEPROMExternalTaskState_e : uint8_t { + Available = 0, + Starting = 1, + Processing = 2, + Ready = 3, + Error = 4, +}; + +enum class EEPROMExternalTaskType_e : uint8_t { + None = 0, + UserVars = 1, + ValueSlots = 2, + C016Caches = 3, + PinStates = 4, +}; + +struct EEPROMExternalTaskData { + EEPROMExternalTaskState_e status = EEPROMExternalTaskState_e::Available; + EEPROMExternalTaskType_e type = EEPROMExternalTaskType_e::None; + uint32_t (*function)() = nullptr; + LongTermTimer timer; + uint32_t duration{}; + int32_t data{}; + + # if FEATURE_EEPROM_RTOS_TASK + + // This is C-code, so not set to nullptr, but to NULL + TaskHandle_t taskHandle = NULL; + # endif // if FEATURE_EEPROM_RTOS_TASK +}; + +const __FlashStringHelper* TaskDataTypeToString(EEPROMExternalTaskType_e type); +bool EEPROMAddTask(EEPROMExternalTaskType_e type, + EEPROMExternalTaskData taskData); +bool EEPROMExternalLoop(); + +extern std::map EEPROMTaskMap; +extern uint16_t EEPROMSaveDelaySeconds; + +# endif // if FEATURE_EEPROM_BACKGROUND } // namespace eeprom } // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 7bb3660f74..769e8f3b3e 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3781,6 +3781,29 @@ To create/register a plugin, you have to : #endif #endif #endif // ifndef FEATURE_EEPROM_EXTERNAL +#ifndef FEATURE_EEPROM_BACKGROUND + #ifdef ESP32 + #define FEATURE_EEPROM_BACKGROUND 1 + #ifndef FEATURE_EEPROM_RTOS_TASK + #define FEATURE_EEPROM_RTOS_TASK 0 // I2C is not thread-safe + #endif + #endif // ifdef ESP32 + #ifdef ESP8266 + #ifdef LIMIT_BUILD_SIZE + #define FEATURE_EEPROM_BACKGROUND 0 // Disabled for limited builds on ESP8266 + #else + #define FEATURE_EEPROM_BACKGROUND 1 // Enabled by default on ESP8266 + #endif + #endif // ifdef ESP8266 +#endif // ifndef FEATURE_EEPROM_BACKGROUND +#if defined(ESP8266) && defined(FEATURE_EEPROM_RTOS_TASK) && FEATURE_EEPROM_RTOS_TASK + #undef FEATURE_EEPROM_RTOS_TASK + #define FEATURE_EEPROM_RTOS_TASK 0 // Not supported on ESP8266 +#endif // if defined(ESP8266) && defined(FEATURE_EEPROM_RTOS_TASK) && FEATURE_EEPROM_RTOS_TASK +#ifndef FEATURE_EEPROM_RTOS_TASK + #define FEATURE_EEPROM_RTOS_TASK 0 +#endif // ifndef FEATURE_EEPROM_RTOS_TASK + //-------------------HTTPResponseParser Section---------------- #ifndef FEATURE_THINGSPEAK_EVENT diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 98ca671c23..79ce6428e8 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -58,6 +58,8 @@ #define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) #define EEPROM_MUX_FLAGS_MULTI 8 // bit-offset within multiplexerflags for bits or port (1 bit) +#define EEPROM_SAVEOPTIONS_DELAY 0 // bit-offset within EEPROMSaveOptions for the Save Delay (seconds) + #endif // if FEATURE_EEPROM_EXTERNAL /*********************************************************************************************\ diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index a059a72cf5..34ef360440 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -362,6 +362,8 @@ class SettingsStruct_tmpl void EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags); uint8_t EEPROMExternalType() const; void EEPROMExternalType(uint8_t type); + uint8_t EEPROMSaveDelaySeconds() const; + void EEPROMSaveDelaySeconds(uint8_t seconds); #endif #if FEATURE_I2CMULTIPLEXER @@ -468,7 +470,8 @@ class SettingsStruct_tmpl int8_t I2C3_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C3_Multiplexer_Addr = -1; int8_t I2C3_Multiplexer_ResetPin = -1; - unsigned int OLD_TaskDeviceID[N_TASKS - 7] = {0}; //UNUSED: this can be reused + uint32_t EEPROMSaveOptions = 0; + unsigned int OLD_TaskDeviceID[N_TASKS - 8] = {0}; //UNUSED: this can be reused // FIXME TD-er: When used on ESP8266, this conversion union may not work // It might work as it is 32-bit in size. diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 6322f7a21b..bf995d60d3 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1166,6 +1166,14 @@ template void SettingsStruct_tmpl::EEPROMExternalType(uint8_t type) { set4BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); } +template +uint8_t SettingsStruct_tmpl::EEPROMSaveDelaySeconds() const { + return get8BitFromUL(EEPROMSaveOptions, EEPROM_SAVEOPTIONS_DELAY); +} +template +void SettingsStruct_tmpl::EEPROMSaveDelaySeconds(uint8_t seconds) { + set8BitToUL(EEPROMSaveOptions, EEPROM_SAVEOPTIONS_DELAY, seconds); +} #endif // if FEATURE_EEPROM_EXTERNAL #if FEATURE_I2CMULTIPLEXER diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 2d9ee04fc4..1b58c00b97 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -171,6 +171,47 @@ bool readFromRTC() return RTC.ID1 == 0xAA && RTC.ID2 == 0x55; } +#if FEATURE_EEPROM_EXTERNAL +uint32_t saveUserVarToEEPROM() { + uint32_t eepromWritten{}; + // Update system parameters if not correct + const bool paramsOk = ESPEasy::eeprom::validateEEPROMExternalParameters(); + + if (!paramsOk) { + ESPEasy::eeprom::updateEEPROMExternalParameters(); + } + + for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { + if (Settings.TaskDeviceEnabled[task] || !paramsOk) { // Only check enabled tasks or when re-writing the params + const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); + if (taskValues != nullptr) { + LoadTaskSettings(task); + for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { + const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) + ? taskValues->getUint32(varNr) + : std::numeric_limits::max(); // NaN when read as float + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); + if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs + ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); + eepromWritten += sizeof_uint32_t; + } + } + } + } + } + + if (eepromWritten) { // Did we change anything? Only then do the slow operation of CRC calculation by reading all bytes + // Calculate checksum for all stored values, not equal to the UserVar checksum! + const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); + if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { + ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); + eepromWritten += sizeof_uint32_t; + } + } + return eepromWritten; +} +#endif // if FEATURE_EEPROM_EXTERNAL + /********************************************************************************************\ Save values to RTC memory \*********************************************************************************************/ @@ -186,45 +227,17 @@ bool saveUserVarToRTC(bool initial) if (!initial && (eepromAddress > 0) && !ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { // EEPROM Configured and writable? if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + #if FEATURE_EEPROM_BACKGROUND + ESPEasy::eeprom::EEPROMExternalTaskData taskdata; + taskdata.type = ESPEasy::eeprom::EEPROMExternalTaskType_e::UserVars; + taskdata.function = saveUserVarToEEPROM; + ESPEasy::eeprom::EEPROMAddTask(ESPEasy::eeprom::EEPROMExternalTaskType_e::UserVars, taskdata); + #else // if FEATURE_EEPROM_BACKGROUND #ifndef BUILD_NO_DEBUG - uint32_t eepromWritten{}; - uint32_t startmicros{}; - #endif // ifndef BUILD_NO_DEBUG - // Update system parameters if not correct - const bool paramsOk = ESPEasy::eeprom::validateEEPROMExternalParameters(); - ESPEasy::eeprom::updateEEPROMExternalParameters(); - - #ifndef BUILD_NO_DEBUG - startmicros = micros(); - #endif // ifndef BUILD_NO_DEBUG - for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { - if (Settings.TaskDeviceEnabled[task] || !paramsOk) { // Only check enabled tasks or when re-writing the params - const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); - if (taskValues != nullptr) { - LoadTaskSettings(task); - for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) - ? taskValues->getUint32(varNr) - : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); - if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); - #ifndef BUILD_NO_DEBUG - eepromWritten += sizeof_uint32_t; - #endif // ifndef BUILD_NO_DEBUG - } - } - } - } - } - // Calculate checksum for all stored values, not equal to the UserVar checksum! - const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); - #ifndef BUILD_NO_DEBUG - eepromWritten += sizeof_uint32_t; - #endif // ifndef BUILD_NO_DEBUG - } + uint32_t startmicros = micros(); + const uint32_t eepromWritten = + #endif + saveUserVarToEEPROM(); #ifndef BUILD_NO_DEBUG startmicros = micros() - startmicros; @@ -235,7 +248,7 @@ bool saveUserVarToRTC(bool initial) eepromWritten, startmicros / 1000.0f, FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); } #endif // ifndef BUILD_NO_DEBUG - + #endif // if FEATURE_EEPROM_BACKGROUND } #if FEATURE_I2CMULTIPLEXER I2CMultiplexerOff( diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f2ab659359..2452316500 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -185,7 +185,7 @@ void run_compiletime_checks() { static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS - static_assert((228 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. + static_assert((232 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. static_assert((200 + (67 * TASKS_MAX)) == offsetof(SettingsStruct, ControllerEnabled), ""); // Used to compute true offset. diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index 8c0f3bc22c..4606a8dc06 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -43,6 +43,9 @@ #include "../../ESPEasy_fdwdecl.h" #endif +#if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND #define PLUGIN_ID_MQTT_IMPORT 37 @@ -170,6 +173,10 @@ void runOncePerSecond() PluginCall(PLUGIN_ONCE_A_SECOND, 0, dummy); // unsigned long elapsed = micros() - start; + #if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND + ESPEasy::eeprom::EEPROMExternalLoop(); + #endif // if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND + // I2C Watchdog feed if (Settings.WDI2CAddress != 0) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 2f9df6cdcf..9a47c7c5f1 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -149,6 +149,7 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { #if FEATURE_EEPROM_EXTERNAL case LabelType::EEPROM_RESTORE_ON_COLDBOOT: return F("Cold boot: Restore Task Values from EEPROM"); case LabelType::EEPROM_RESTORE_ON_WARMBOOT: return F("Warm boot: Restore Task Values from EEPROM"); + case LabelType::EEPROM_SAVE_DELAY: return F("Save interval (seconds)"); #endif // if FEATURE_EEPROM_EXTERNAL case LabelType::BOOT_TYPE: return F("Last Boot Cause"); @@ -450,6 +451,7 @@ String getValue(LabelType::Enum label) { #if FEATURE_EEPROM_EXTERNAL case LabelType::EEPROM_RESTORE_ON_COLDBOOT: return jsonBool(Settings.RestoreUserVarsFromEEPROMOnColdBoot()); case LabelType::EEPROM_RESTORE_ON_WARMBOOT: return jsonBool(Settings.RestoreUserVarsFromEEPROMOnWarmBoot()); + case LabelType::EEPROM_SAVE_DELAY: return toString(Settings.EEPROMSaveDelaySeconds()); #endif // if FEATURE_EEPROM_EXTERNAL case LabelType::BOOT_TYPE: return getLastBootCauseString(); diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index c919678b8f..e781b79954 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -97,6 +97,7 @@ struct LabelType { #if FEATURE_EEPROM_EXTERNAL EEPROM_RESTORE_ON_COLDBOOT, EEPROM_RESTORE_ON_WARMBOOT, + EEPROM_SAVE_DELAY, #endif // if FEATURE_EEPROM_EXTERNAL BOOT_TYPE, // Cold boot diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 7270321784..bc26f97501 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -132,6 +132,7 @@ void handle_hardware() { Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_COLDBOOT)); Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_WARMBOOT)); + Settings.EEPROMSaveDelaySeconds(getFormItemInt(LabelType::EEPROM_SAVE_DELAY)); # if FEATURE_I2CMULTIPLEXER @@ -413,6 +414,7 @@ void handle_hardware() { } addFormCheckBox(LabelType::EEPROM_RESTORE_ON_COLDBOOT, Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); addFormCheckBox(LabelType::EEPROM_RESTORE_ON_WARMBOOT, Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); + addFormNumericBox(LabelType::EEPROM_SAVE_DELAY, Settings.EEPROMSaveDelaySeconds(), 0, 250); } #endif // if FEATURE_EEPROM_EXTERNAL From 6f1a12b86353abe65e05fab2d56ae3b600e3171d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 1 Sep 2025 23:43:27 +0200 Subject: [PATCH 28/29] [Storage] Fixes and improvements for Params check --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp | 19 +++++++++++++----- src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 8 ++++++-- src/src/Helpers/ESPEasyRTC.cpp | 20 ++++++++++--------- src/src/Helpers/ESPEasy_checks.cpp | 9 ++++++++- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp index 224bb3bd44..e589f9c03c 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -10,6 +10,8 @@ namespace ESPEasy { namespace eeprom { AT24CX *EEPROMExternal = nullptr; EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; +bool EEPROMParamsOkState{}; +LongTermTimer EEPROMParamsOkTimer; constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); @@ -82,12 +84,19 @@ void initializeEEPROMExternal() { * - RTC Cache address * - Pinstate address */ -bool validateEEPROMExternalParameters() { +bool validateEEPROMExternalParameters(bool force) { + if (!force && (EEPROMParamsOkTimer.millisPassedSince() < EEPROM_PARAMSOK_STATE_TIMEOUT)) { // When called within timeout return cached + // result + return EEPROMParamsOkState; + } const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); + EEPROMParamsOkTimer.setNow(); + EEPROMParamsOkState = false; + # if FEATURE_RTC_CACHE_STORAGE const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); # endif // if FEATURE_RTC_CACHE_STORAGE @@ -100,10 +109,10 @@ bool validateEEPROMExternalParameters() { # endif // if FEATURE_RTC_CACHE_STORAGE (EEPROM_GPIO_PINSTATE_START_OFFSET == eepromPinstateParam) && (EEPROM_GPIO_PINSTATE_END_OFFSET <= EEPROM_CUSTOM_START_OFFSET)) { - return true; + EEPROMParamsOkState = true; } - return false; + return EEPROMParamsOkState; } /** @@ -118,10 +127,10 @@ void updateEEPROMExternalParameters() { const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); - const uint32_t eepromPinstateParam = EEPROMExternal->readInt(EEPROM_PARAMS_PINSTATE_ADDRESS); + const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); # if FEATURE_RTC_CACHE_STORAGE - const uint32_t eepromRtcCacheParam = EEPROMExternal->readInt(EEPROM_PARAMS_RTC_CACHE_ADDRESS); + const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); # endif // if FEATURE_RTC_CACHE_STORAGE if (EEPROM_PARAMS_CURRENT_VERSION != eepromVersionParam) { diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index 101f66f83a..68c2a14781 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -22,9 +22,11 @@ enum class EEPROMExternal_WriteProtect_e : uint8_t { extern AT24CX *EEPROMExternal; extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; +extern bool EEPROMParamsOkState; +extern LongTermTimer EEPROMParamsOkTimer; # define EEPROM_PARAMS_CURRENT_VERSION (1) // Let's start with version 1 -# define EEPROM_PINSTATE_ELEMENT_COUNT (64) // Number of PinState elements (16 bytes each) to store in EEPROM +# define EEPROM_PINSTATE_ELEMENT_COUNT (48) // Number of PinState elements (16 bytes each) to store in EEPROM // Start writing the base RTC struct from this offset (not currently saving this to EEPROM) // TODO # define EEPROM_BASERTC_START_OFFSET (0) @@ -75,6 +77,8 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; # define EEPROM_SUPPORT_AT24C1024 1 # define EEPROM_SUPPORT_AT24C2048 0 +# define EEPROM_PARAMSOK_STATE_TIMEOUT (180000) // 3 minutes + // Supported AT24Cxxx and MB85RCxxx devices enum class EEPROMExternal_Type_e : uint8_t { AT24C256 = 0, // Default, 32 kB @@ -103,7 +107,7 @@ enum class EEPROMExternal_Type_e : uint8_t { void initializeEEPROMExternal(); -bool validateEEPROMExternalParameters(); +bool validateEEPROMExternalParameters(bool force = false); void updateEEPROMExternalParameters(); uint8_t checkEEPROMEnabled(); diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 1b58c00b97..ca3303d5a2 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -185,15 +185,17 @@ uint32_t saveUserVarToEEPROM() { if (Settings.TaskDeviceEnabled[task] || !paramsOk) { // Only check enabled tasks or when re-writing the params const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); if (taskValues != nullptr) { - LoadTaskSettings(task); for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const uint32_t newData = Cache.getTaskVarStoreInEEPROM(task, varNr) - ? taskValues->getUint32(varNr) - : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); - if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); - eepromWritten += sizeof_uint32_t; + const bool storeValue = Cache.getTaskVarStoreInEEPROM(task, varNr); + if (!paramsOk || storeValue) { + const uint32_t newData = storeValue + ? taskValues->getUint32(varNr) + : std::numeric_limits::max(); // NaN when read as float + const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); + if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs + ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); + eepromWritten += sizeof_uint32_t; + } } } } @@ -310,7 +312,7 @@ bool readUserVarFromRTC() if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM // Check system parameters with last stored values - bool eepromParamsOK = ESPEasy::eeprom::validateEEPROMExternalParameters(); + const bool eepromParamsOK = ESPEasy::eeprom::validateEEPROMExternalParameters(true); // Check checksum and if correct, restore UserVar values const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index 2452316500..9e8a93a4b1 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -47,6 +47,9 @@ #include "../DataStructs/NotificationSettingsStruct.h" #endif // if FEATURE_NOTIFIER +#if FEATURE_EEPROM_EXTERNAL +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL // ******************************************************************************** // Check struct sizes at compile time @@ -197,8 +200,12 @@ void run_compiletime_checks() { // to determine nr of bits in a struct. static_assert(GPIO_DIRECTION_NR_BITS== NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)), "Correct GPIO_DIRECTION_NR_BITS"); + #if FEATURE_EEPROM_EXTERNAL + static_assert(EEPROM_CUSTOM_START_OFFSET > EEPROM_GPIO_PINSTATE_END_OFFSET, "EEPROM GPIO Pinstates overlap with Slots values!"); + #endif // if FEATURE_EEPROM_EXTERNAL - #endif + + #endif // ifndef LIMIT_BUILD_SIZE } #ifndef LIMIT_BUILD_SIZE From c95689c373fe273ad96c4f479015949c3e80bc2a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 6 Apr 2026 20:03:18 +0200 Subject: [PATCH 29/29] [Storage] Reduce functionality to only support writeee command and readee variable retrieval --- src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp | 294 +++--------------- src/ESPEasy/eeprom/Helpers/EEPROMExternal.h | 104 +------ src/src/Commands/EEPROMExternal.cpp | 22 +- src/src/CustomBuild/define_plugin_sets.h | 22 -- src/src/DataStructs/Caches.cpp | 18 -- src/src/DataStructs/Caches.h | 7 - src/src/DataStructs/DeviceStruct.h | 2 - .../DataStructs/ExtraTaskSettingsStruct.cpp | 13 - src/src/DataStructs/ExtraTaskSettingsStruct.h | 5 - .../DataStructs/RTC_cache_handler_struct.cpp | 86 ----- src/src/DataStructs/SettingsStruct.h | 14 +- src/src/DataStructs_templ/SettingsStruct.cpp | 22 +- src/src/ESPEasyCore/ESPEasy_setup.cpp | 13 - src/src/Helpers/ESPEasyRTC.cpp | 159 ---------- src/src/Helpers/ESPEasy_checks.cpp | 9 - src/src/Helpers/PeriodicalActions.cpp | 8 - src/src/Helpers/StringParser.cpp | 4 + src/src/Helpers/StringProvider.cpp | 15 - src/src/Helpers/StringProvider.h | 6 - src/src/WebServer/DevicesPage.cpp | 28 -- src/src/WebServer/EpromVarPage.cpp | 62 +--- src/src/WebServer/HardwarePage.cpp | 6 - 22 files changed, 100 insertions(+), 819 deletions(-) diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp index e589f9c03c..0e6d06ce5d 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -13,7 +13,7 @@ EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect = EEPROMExternal_WriteP bool EEPROMParamsOkState{}; LongTermTimer EEPROMParamsOkTimer; -constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); +constexpr uint32_t sizeof_eeprom_slot = sizeof(double); /** * Initialize the external EEPROM device and variables @@ -89,26 +89,12 @@ bool validateEEPROMExternalParameters(bool force) { // result return EEPROMParamsOkState; } - const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); - const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); - const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); - const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); EEPROMParamsOkTimer.setNow(); EEPROMParamsOkState = false; - # if FEATURE_RTC_CACHE_STORAGE - const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); - # endif // if FEATURE_RTC_CACHE_STORAGE - - if ((EEPROM_PARAMS_CURRENT_VERSION == eepromVersionParam) && - (TASKS_MAX == eepromTasksMaxParam) && - (VARS_PER_TASK == eepromVarsPerTaskParam) && - # if FEATURE_RTC_CACHE_STORAGE - (EEPROM_RTC_CACHE_START_OFFSET == eepromRtcCacheParam) && - # endif // if FEATURE_RTC_CACHE_STORAGE - (EEPROM_GPIO_PINSTATE_START_OFFSET == eepromPinstateParam) && - (EEPROM_GPIO_PINSTATE_END_OFFSET <= EEPROM_CUSTOM_START_OFFSET)) { + if (EEPROM_PARAMS_CURRENT_VERSION == eepromVersionParam) { EEPROMParamsOkState = true; } @@ -118,43 +104,13 @@ bool validateEEPROMExternalParameters(bool force) { /** * Update the stored parameters in EEPROM * - Version - * - Max. tasks - * - Vars per tasks - * - RTC Cache address - * - Pinstate address */ void updateEEPROMExternalParameters() { - const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); - const uint16_t eepromTasksMaxParam = EEPROMExternal->readInt(EEPROM_PARAMS_TASKS_MAX); - const uint16_t eepromVarsPerTaskParam = EEPROMExternal->readInt(EEPROM_PARAMS_VARS_PER_TASK); - const uint32_t eepromPinstateParam = EEPROMExternal->readLong(EEPROM_PARAMS_PINSTATE_ADDRESS); - - # if FEATURE_RTC_CACHE_STORAGE - const uint32_t eepromRtcCacheParam = EEPROMExternal->readLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS); - # endif // if FEATURE_RTC_CACHE_STORAGE + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); if (EEPROM_PARAMS_CURRENT_VERSION != eepromVersionParam) { EEPROMExternal->writeInt(EEPROM_PARAMS_VERSION_ADDRESS, EEPROM_PARAMS_CURRENT_VERSION); } - - if (TASKS_MAX != eepromTasksMaxParam) { - EEPROMExternal->writeInt(EEPROM_PARAMS_TASKS_MAX, TASKS_MAX); - } - - if (VARS_PER_TASK != eepromVarsPerTaskParam) { - EEPROMExternal->writeInt(EEPROM_PARAMS_VARS_PER_TASK, VARS_PER_TASK); - } - - # if FEATURE_RTC_CACHE_STORAGE - - if (EEPROM_RTC_CACHE_START_OFFSET != eepromRtcCacheParam) { - EEPROMExternal->writeLong(EEPROM_PARAMS_RTC_CACHE_ADDRESS, EEPROM_RTC_CACHE_START_OFFSET); - } - # endif // if FEATURE_RTC_CACHE_STORAGE - - if (EEPROM_GPIO_PINSTATE_START_OFFSET != eepromPinstateParam) { - EEPROMExternal->writeLong(EEPROM_PARAMS_PINSTATE_ADDRESS, EEPROM_GPIO_PINSTATE_START_OFFSET); - } } /** @@ -213,9 +169,7 @@ EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced) { /** * Is the EEPROM WriteProtected? */ -bool isEEPROMExternalWriteProtected() { - return EEPROMExternal_WriteProtect_e::ReadWrite != checkEEPROMExternalWriteProtected(); -} +bool isEEPROMExternalWriteProtected() { return EEPROMExternal_WriteProtect_e::ReadWrite != checkEEPROMExternalWriteProtected(); } /** * Switch to I2C Bus and multiplexer channel of External EEPROM @@ -250,7 +204,8 @@ uint8_t selectEEPROMI2CBusAndMultiplexer() { * EEPROM size in bytes */ uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { - switch (type) { + switch (type) + { case EEPROMExternal_Type_e::AT24C256: case EEPROMExternal_Type_e::MB85RC256: return 32768ul; @@ -287,7 +242,8 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, uint8_t & pageSize) { pageSize = 0; - switch (type) { + switch (type) + { case EEPROMExternal_Type_e::AT24C256: case EEPROMExternal_Type_e::MB85RC256: pageSize = 64u; @@ -321,21 +277,20 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, * EEPROM/FRAM name */ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { - # ifndef BUILD_NO_DEBUG - - switch (type) { + switch (type) + { case EEPROMExternal_Type_e::AT24C256: return F("AT24C256"); case EEPROMExternal_Type_e::AT24C512: return F("AT24C512"); - # if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::AT24C1024: return F("AT24C1024"); - # endif // if EEPROM_SUPPORT_AT24C1024 - # if EEPROM_SUPPORT_AT24C2048 + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C2048: return F("AT24C2048"); - # endif // if EEPROM_SUPPORT_AT24C2048 + # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::AT24C32: return F("AT24C32"); case EEPROMExternal_Type_e::AT24C64: @@ -346,14 +301,14 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { return F("MB85RC256"); case EEPROMExternal_Type_e::MB85RC512: return F("MB85RC512"); - # if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C1024 case EEPROMExternal_Type_e::MB85RC1M: return F("MB85RC1M"); - # endif // if EEPROM_SUPPORT_AT24C1024 - # if EEPROM_SUPPORT_AT24C2048 + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::MB85RC2M: return F("MB85RC2M"); - # endif // if EEPROM_SUPPORT_AT24C2048 + # endif // if EEPROM_SUPPORT_AT24C2048 case EEPROMExternal_Type_e::MB85RC32: return F("MB85RC32"); case EEPROMExternal_Type_e::MB85RC64: @@ -362,9 +317,6 @@ const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { return F("MB85RC128"); } return F(""); - # else // ifndef BUILD_NO_DEBUG - return F("EEPROM/FRAM"); - # endif // ifndef BUILD_NO_DEBUG } /** @@ -375,7 +327,7 @@ uint32_t getEEPROMAddressForSlot(uint32_t slot) { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if ((eepromSize > 0) && (slot < getEEPROMMaxSlots())) { - const uint32_t slotAddr = EEPROM_CUSTOM_START_OFFSET + (slot * sizeof_uint32_t); + const uint32_t slotAddr = EEPROM_CUSTOM_START_OFFSET + (slot * sizeof_eeprom_slot); if (slotAddr < eepromSize) { return slotAddr; @@ -386,34 +338,14 @@ uint32_t getEEPROMAddressForSlot(uint32_t slot) { } /** - * EEPROM address for task and varnr or 0xFFFF when error - */ -uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, taskVarIndex_t varNr) { - if (checkEEPROMEnabled() > 0) { - const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); - - if ((eepromSize > 0) && validTaskIndex(task) && validTaskVarIndex(varNr)) { - const uint32_t slotAddr = EEPROM_USERVAR_START_OFFSET + (((task * VARS_PER_TASK) + varNr) * sizeof_uint32_t); - - if (slotAddr < eepromSize) { - return slotAddr; - } - } - } - return std::numeric_limits::max(); -} - -/** - * EEPROM available number of slots, max 1024 - * NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! + * EEPROM available number of slots, max use all available space minus some administrative bytes */ uint32_t getEEPROMMaxSlots() { if (checkEEPROMEnabled() > 0) { const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); if (eepromSize > 0) { - const uint32_t slotMax = min((unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_uint32_t), - 1024ul); + const uint32_t slotMax = (unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_eeprom_slot); return slotMax; } @@ -424,15 +356,27 @@ uint32_t getEEPROMMaxSlots() { /** * EEPROM write value to slot if the slot is valid */ +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + bool writeEEPROMSlot(uint32_t slot, - float data) { + double data) +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +bool writeEEPROMSlot(uint32_t slot, + float data) +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +{ const uint32_t addr = getEEPROMAddressForSlot(slot); if ((addr != std::numeric_limits::max()) && !isEEPROMExternalWriteProtected()) { - const float oldData = EEPROMExternal->readLong(addr); + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + const double oldData = EEPROMExternal->readDouble(addr); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + const float oldData = EEPROMExternal->readDouble(addr); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE if (!essentiallyEqual(oldData, data)) { - EEPROMExternal->writeFloat(addr, data); + EEPROMExternal->writeDouble(addr, data); // Always write double size! } return true; } @@ -442,170 +386,30 @@ bool writeEEPROMSlot(uint32_t slot, /** * EEPROM read value from slot or 0 when invalid */ -float readEEPROMSlot(uint32_t slot) { +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +double readEEPROMSlot(uint32_t slot) { const uint32_t addr = getEEPROMAddressForSlot(slot); if (addr != std::numeric_limits::max()) { - return EEPROMExternal->readFloat(addr); + return EEPROMExternal->readDouble(addr); } - return 0.0f; + return 0.0; } -# if FEATURE_EEPROM_BACKGROUND - -// A map of tasks to be executed -std::map EEPROMTaskMap; -uint16_t EEPROMSaveDelaySeconds{}; +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -/** - * Convert TaskType to text - */ -const __FlashStringHelper* TaskDataTypeToString(EEPROMExternalTaskType_e type) { - switch (type) { - case EEPROMExternalTaskType_e::None: return F(""); - case EEPROMExternalTaskType_e::UserVars: return F("UserVars"); - case EEPROMExternalTaskType_e::ValueSlots: return F("ValueSlots"); - case EEPROMExternalTaskType_e::C016Caches: return F("C016 Cache elements"); - case EEPROMExternalTaskType_e::PinStates: return F("Pinstates"); - } - return F(""); -} - -/** - * Perform the actual task, bij calling the provided function - */ -void EEPROM_execute_task(void *parameter) { - EEPROMExternalTaskData*_task_data = static_cast(parameter); - - if ((_task_data->status == EEPROMExternalTaskState_e::Starting) && (nullptr != _task_data->function)) { - _task_data->status = EEPROMExternalTaskState_e::Processing; - - selectEEPROMI2CBusAndMultiplexer(); - - _task_data->timer.setNow(); - - // Blocking operation - _task_data->data = _task_data->function(); - - // Results are in - _task_data->duration = _task_data->timer.millisPassedSince(); - - # if FEATURE_I2CMULTIPLEXER - I2CMultiplexerOff( - # if FEATURE_I2C_MULTIPLE - Settings.getI2CInterfaceEEPROM() - # else // if FEATURE_I2C_MULTIPLE - 0 - # endif // if FEATURE_I2C_MULTIPLE - ); // Restore the Multiplexer channel - # endif // if FEATURE_I2CMULTIPLEXER - - _task_data->status = EEPROMExternalTaskState_e::Ready; - } - - _task_data->function = nullptr; // Don't run again accidently - # if FEATURE_EEPROM_RTOS_TASK - _task_data->taskHandle = NULL; - vTaskDelete(_task_data->taskHandle); - # endif // if FEATURE_EEPROM_RTOS_TASK -} - -/** - * Insert a task into the taskmap, 1 per task type, ignore new task when same type is already scheduled to run, or currently running - */ -bool EEPROMAddTask(EEPROMExternalTaskType_e type, - EEPROMExternalTaskData taskData) { - auto task = EEPROMTaskMap.find(type); - - // Is a task is already in progress, skip until the next AddTask - if ((task == EEPROMTaskMap.end()) || (EEPROMExternalTaskState_e::Available == task->second.status)) { - EEPROMTaskMap[type] = taskData; // Insert task - task = EEPROMTaskMap.find(type); - - if (task != EEPROMTaskMap.end()) { // Upserted successfully - task->second.status = EEPROMExternalTaskState_e::Starting; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, - strformat(F("EEPROM: AddTask upserted for %s, status %u (tasks: %u)" - # if FEATURE_EEPROM_RTOS_TASK - " (RTOS)" - # endif // if FEATURE_EEPROM_RTOS_TASK - ), - FsP(TaskDataTypeToString(task->second.type)), static_cast(task->second.status), EEPROMTaskMap.size())); - } - # endif // ifndef BUILD_NO_DEBUG - } else { - addLog(LOG_LEVEL_ERROR, F("EEPROM: Task not inserted")); - } +float readEEPROMSlot(uint32_t slot) { + const uint32_t addr = getEEPROMAddressForSlot(slot); - # if FEATURE_EEPROM_RTOS_TASK - EEPROMExternalLoop(); // Kick off if it's a RTOS task - # endif // if FEATURE_EEPROM_RTOS_TASK - return true; + if (addr != std::numeric_limits::max()) { + return EEPROMExternal->readDouble(addr); // Always read double size! } - return false; + return 0.0f; } -/** - * Check the task map for tasks to run and start that, also report about finished tasks, and clean those for re-use - */ -bool EEPROMExternalLoop() { - if (0 != EEPROMSaveDelaySeconds) { - EEPROMSaveDelaySeconds--; - return false; - } - EEPROMSaveDelaySeconds = Settings.EEPROMSaveDelaySeconds(); - - for (auto task = EEPROMTaskMap.begin(); task != EEPROMTaskMap.end(); ++task) { - if ((EEPROMExternalTaskState_e::Ready == task->second.status) || - (EEPROMExternalTaskState_e::Error == task->second.status)) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: Write %s %s %d in %u msec." - # if FEATURE_EEPROM_RTOS_TASK - " (RTOS)" - # endif // if FEATURE_EEPROM_RTOS_TASK - ), - FsP(TaskDataTypeToString(task->second.type)), - FsP(EEPROMExternalTaskState_e::Ready == task->second.status ? F("success") : F("failed")), - task->second.data, - task->second.duration)); - } - task->second.status = EEPROMExternalTaskState_e::Available; - } - - if (EEPROMExternalTaskState_e::Starting == task->second.status) { - if (nullptr != task->second.function) { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: Loop starting task: %s"), FsP(TaskDataTypeToString(task->second.type)))); - } - # endif // ifndef BUILD_NO_DEBUG - # if FEATURE_EEPROM_RTOS_TASK - xTaskCreatePinnedToCore( - EEPROM_execute_task, // Function that should be called - "EEPROM.write()", // Name of the task (for debugging) - 4000, // Stack size (bytes) - &task->second, // Parameter to pass - 1, // Task priority - &task->second.taskHandle, // Task handle - xPortGetCoreID() // Core you want to run the task on (0 or 1) - ); - # else // if FEATURE_EEPROM_RTOS_TASK - EEPROM_execute_task(&task->second); - # endif // if FEATURE_EEPROM_RTOS_TASK - } else { - task->second.status = EEPROMExternalTaskState_e::Available; // Recycle - } - } - } - return false; -} +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -# endif // if FEATURE_EEPROM_BACKGROUND } // namespace eeprom } // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h index 68c2a14781..634f0bc34d 100644 --- a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -4,11 +4,7 @@ #if FEATURE_EEPROM_EXTERNAL # include "../../../src/DataTypes/TaskIndex.h" -# include "../../../src/DataStructs/PortStatusStruct.h" -# include "../../../src/DataStructs/RTCCacheStruct.h" -# include "../../../src/DataStructs/RTCStruct.h" # include "../../../src/Helpers/LongTermTimer.h" -# include # include @@ -18,6 +14,7 @@ enum class EEPROMExternal_WriteProtect_e : uint8_t { Undefined = 0, ReadWrite = 1, ReadOnly = 2, + }; extern AT24CX *EEPROMExternal; @@ -25,53 +22,17 @@ extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; extern bool EEPROMParamsOkState; extern LongTermTimer EEPROMParamsOkTimer; -# define EEPROM_PARAMS_CURRENT_VERSION (1) // Let's start with version 1 -# define EEPROM_PINSTATE_ELEMENT_COUNT (48) // Number of PinState elements (16 bytes each) to store in EEPROM +# define EEPROM_PARAMS_CURRENT_VERSION (1) // Let's start with version 1 -// Start writing the base RTC struct from this offset (not currently saving this to EEPROM) // TODO +// Start from this offset # define EEPROM_BASERTC_START_OFFSET (0) // Some system parameters to check before restoring anything # define EEPROM_PARAMS_VERSION_ADDRESS (32) -# define EEPROM_PARAMS_TASKS_MAX (EEPROM_PARAMS_VERSION_ADDRESS + sizeof(uint16_t)) -# define EEPROM_PARAMS_VARS_PER_TASK (EEPROM_PARAMS_TASKS_MAX + sizeof(uint16_t)) -# define EEPROM_PARAMS_RTC_CACHE_ADDRESS (EEPROM_PARAMS_VARS_PER_TASK + sizeof(uint16_t)) -# define EEPROM_PARAMS_PINSTATE_ADDRESS (EEPROM_PARAMS_RTC_CACHE_ADDRESS + sizeof(uint32_t)) - -// Start writing the UserVar values from this offset, should be > sizeof(RTCStruct) that is 32 currently -# define EEPROM_USERVAR_START_OFFSET (EEPROM_BASERTC_START_OFFSET + 128) - -// Write the UserVar-checksum from this offset, right after the UserVar values -# define EEPROM_USERVAR_CHECKSUM_OFFSET (EEPROM_USERVAR_START_OFFSET + (TASKS_MAX * VARS_PER_TASK * sizeof(uint32_t))) - -# if FEATURE_RTC_CACHE_STORAGE - -// Even when not enabled, we still reserve the space -# define EEPROM_RTC_CACHE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) -# define EEPROM_RTC_CACHE_WRITEPOS_OFFSET (EEPROM_RTC_CACHE_START_OFFSET + RTC_CACHE_DATA_SIZE) -# define EEPROM_RTC_CACHE_META_OFFSET (EEPROM_RTC_CACHE_WRITEPOS_OFFSET + sizeof(uint16_t)) -# define EEPROM_RTC_CACHE_META_CRC_OFFSET (EEPROM_RTC_CACHE_META_OFFSET + sizeof(RTC_cache_struct)) -# define EEPROM_RTC_CACHE_CHECKSUM_OFFSET (EEPROM_RTC_CACHE_META_CRC_OFFSET + sizeof(uint32_t)) -# endif // if FEATURE_RTC_CACHE_STORAGE - -// Offset for storing GPIO pinstates, directly following the RTC Cache storage and checksum -# if FEATURE_RTC_CACHE_STORAGE -# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_RTC_CACHE_CHECKSUM_OFFSET + sizeof(uint32_t)) -# else // if FEATURE_RTC_CACHE_STORAGE -# define EEPROM_GPIO_PINSTATE_START_OFFSET (EEPROM_USERVAR_CHECKSUM_OFFSET + sizeof(uint32_t)) -# endif // if FEATURE_RTC_CACHE_STORAGE -# define EEPROM_PINSTATE_ELEMENT_SIZE (sizeof(uint32_t) + sizeof(portStatusStruct)) -# define EEPROM_GPIO_PINSTATE_END_OFFSET (EEPROM_GPIO_PINSTATE_START_OFFSET + (EEPROM_PINSTATE_ELEMENT_COUNT * EEPROM_PINSTATE_ELEMENT_SIZE)) - -// Choose an arbitrary but fixed offset -# define EEPROM_CUSTOM_START_OFFSET (2048) - -// NB: Only first half of EEPROM_CUSTOM_START_OFFSET available for slots when String Variables feature enabled! -# if FEATURE_STRING_VARIABLES -# define EEPROM_CUSTOM_DIVISOR (2u) // Split in slots- and strings- halves -# else // if FEATURE_STRING_VARIABLES -# define EEPROM_CUSTOM_DIVISOR (1u) // Use all for slots -# endif // if FEATURE_STRING_VARIABLES + +// Start writing the Custom slot values from this offset so we have some room for settings, if needed +# define EEPROM_CUSTOM_START_OFFSET (EEPROM_BASERTC_START_OFFSET + 128) +# define EEPROM_CUSTOM_DIVISOR (1u) // Use all for slots // Enable/disable some models # define EEPROM_SUPPORT_AT24C1024 1 @@ -103,6 +64,7 @@ enum class EEPROMExternal_Type_e : uint8_t { MB85RC32 = 11, // 4 kB, not endorsed, possibly not available MB85RC64 = 12, // 8 kB MB85RC128 = 13, // 16 kB + }; void initializeEEPROMExternal(); @@ -122,54 +84,18 @@ uint32_t getEEPROMSize(EEPROMExternal_Type_e type, const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); uint32_t getEEPROMAddressForSlot(uint32_t slot); -uint32_t getEEPROMAddressForTaskValue(taskIndex_t task, - taskVarIndex_t varNr); + uint32_t getEEPROMMaxSlots(); +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool writeEEPROMSlot(uint32_t slot, + double data); +double readEEPROMSlot(uint32_t slot); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE bool writeEEPROMSlot(uint32_t slot, float data); float readEEPROMSlot(uint32_t slot); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -# if FEATURE_EEPROM_BACKGROUND -enum class EEPROMExternalTaskState_e : uint8_t { - Available = 0, - Starting = 1, - Processing = 2, - Ready = 3, - Error = 4, -}; - -enum class EEPROMExternalTaskType_e : uint8_t { - None = 0, - UserVars = 1, - ValueSlots = 2, - C016Caches = 3, - PinStates = 4, -}; - -struct EEPROMExternalTaskData { - EEPROMExternalTaskState_e status = EEPROMExternalTaskState_e::Available; - EEPROMExternalTaskType_e type = EEPROMExternalTaskType_e::None; - uint32_t (*function)() = nullptr; - LongTermTimer timer; - uint32_t duration{}; - int32_t data{}; - - # if FEATURE_EEPROM_RTOS_TASK - - // This is C-code, so not set to nullptr, but to NULL - TaskHandle_t taskHandle = NULL; - # endif // if FEATURE_EEPROM_RTOS_TASK -}; - -const __FlashStringHelper* TaskDataTypeToString(EEPROMExternalTaskType_e type); -bool EEPROMAddTask(EEPROMExternalTaskType_e type, - EEPROMExternalTaskData taskData); -bool EEPROMExternalLoop(); - -extern std::map EEPROMTaskMap; -extern uint16_t EEPROMSaveDelaySeconds; - -# endif // if FEATURE_EEPROM_BACKGROUND } // namespace eeprom } // namespace ESPEasy #endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp index 598fb044e7..da332d51df 100644 --- a/src/src/Commands/EEPROMExternal.cpp +++ b/src/src/Commands/EEPROMExternal.cpp @@ -18,22 +18,36 @@ const __FlashStringHelper* Command_writeEE(struct EventStruct *event, const char *Line) { uint32_t slot{}; - float value{}; - if (validUIntFromString(parseString(Line, 2), slot) && validFloatFromString(parseString(Line, 3), value)) { + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + double value{}; + bool validValue = validDoubleFromString(parseString(Line, 3), value); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + float value{}; + bool validValue = validFloatFromString(parseString(Line, 3), value); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (validUIntFromString(parseString(Line, 2), slot) && validValue) { return return_command_boolean_result_flashstr(ESPEasy::eeprom::writeEEPROMSlot(slot, value)); } else if (equals(parseString(Line, 2), F("erase")) && equals(parseString(Line, 3), F("erase"))) { for (uint32_t slot = 0; slot < ESPEasy::eeprom::getEEPROMMaxSlots(); ++slot) { + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEasy::eeprom::writeEEPROMSlot(slot, 0.0); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE ESPEasy::eeprom::writeEEPROMSlot(slot, 0.0f); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (slot % 50 == 0) { delay(0); } } addLog(LOG_LEVEL_INFO, F("EEPROM: All slot-values erased.")); return return_command_success_flashstr(); } else if (equals(parseString(Line, 2), F("check")) && equals(parseString(Line, 3), F("wp"))) { addLog(LOG_LEVEL_INFO, F("EEPROM: Check write-protect.")); ESPEasy::eeprom::checkEEPROMExternalWriteProtected(true); - + if (ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { - addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); + addLog(LOG_LEVEL_INFO, + concat(F("EEPROM: Write-protected! Status: "), static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); } return return_command_success_flashstr(); } diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 92bdc56104..2ac5f20246 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -4323,28 +4323,6 @@ To create/register a plugin, you have to : #endif #endif #endif // ifndef FEATURE_EEPROM_EXTERNAL -#ifndef FEATURE_EEPROM_BACKGROUND - #ifdef ESP32 - #define FEATURE_EEPROM_BACKGROUND 1 - #ifndef FEATURE_EEPROM_RTOS_TASK - #define FEATURE_EEPROM_RTOS_TASK 0 // I2C is not thread-safe - #endif - #endif // ifdef ESP32 - #ifdef ESP8266 - #ifdef LIMIT_BUILD_SIZE - #define FEATURE_EEPROM_BACKGROUND 0 // Disabled for limited builds on ESP8266 - #else - #define FEATURE_EEPROM_BACKGROUND 1 // Enabled by default on ESP8266 - #endif - #endif // ifdef ESP8266 -#endif // ifndef FEATURE_EEPROM_BACKGROUND -#if defined(ESP8266) && defined(FEATURE_EEPROM_RTOS_TASK) && FEATURE_EEPROM_RTOS_TASK - #undef FEATURE_EEPROM_RTOS_TASK - #define FEATURE_EEPROM_RTOS_TASK 0 // Not supported on ESP8266 -#endif // if defined(ESP8266) && defined(FEATURE_EEPROM_RTOS_TASK) && FEATURE_EEPROM_RTOS_TASK -#ifndef FEATURE_EEPROM_RTOS_TASK - #define FEATURE_EEPROM_RTOS_TASK 0 -#endif // ifndef FEATURE_EEPROM_RTOS_TASK #ifndef FEATURE_PLUGIN_LIST #ifdef ESP32 diff --git a/src/src/DataStructs/Caches.cpp b/src/src/DataStructs/Caches.cpp index f5bf17de88..29f76b27eb 100644 --- a/src/src/DataStructs/Caches.cpp +++ b/src/src/DataStructs/Caches.cpp @@ -340,21 +340,6 @@ uint8_t Caches::getTaskVarStateClass(taskIndex_t TaskIndex, #endif // if FEATURE_MQTT_STATE_CLASS -#if FEATURE_EEPROM_EXTERNAL -uint8_t Caches::getTaskVarStoreInEEPROM(taskIndex_t TaskIndex, - taskVarIndex_t taskVarIndex) { - if (validTaskIndex(TaskIndex) && (validTaskVarIndex(taskVarIndex))) { - auto it = getExtraTaskSettings(TaskIndex); - - if (it != extraTaskSettings_cache.end()) { - return it->second.storeInEEPROM[taskVarIndex]; - } - } - return false; -} - -#endif // if FEATURE_EEPROM_EXTERNAL - void Caches::updateExtraTaskSettingsCache() { const taskIndex_t TaskIndex = ExtraTaskSettings.TaskIndex; @@ -408,9 +393,6 @@ void Caches::updateExtraTaskSettingsCache() #if FEATURE_MQTT_STATE_CLASS tmp.mqttStateClass[i] = ExtraTaskSettings.getTaskVarStateClass(i); #endif // if FEATURE_MQTT_STATE_CLASS - #if FEATURE_EEPROM_EXTERNAL - tmp.storeInEEPROM[i] = ExtraTaskSettings.getTaskVarStoreInEEPROM(i); - #endif // if FEATURE_EEPROM_EXTERNAL } #ifdef ESP32 tmp.TaskDevicePluginConfigLong_index_used = 0; diff --git a/src/src/DataStructs/Caches.h b/src/src/DataStructs/Caches.h index 46019bc1da..31f9e5b606 100644 --- a/src/src/DataStructs/Caches.h +++ b/src/src/DataStructs/Caches.h @@ -60,9 +60,6 @@ struct ExtraTaskSettings_cache_t { #if FEATURE_MQTT_STATE_CLASS uint8_t mqttStateClass[VARS_PER_TASK] = { 0 }; // state_class = None, Measurement, Measurement Angle, Total, Total Increasing #endif // if FEATURE_MQTT_STATE_CLASS - #if FEATURE_EEPROM_EXTERNAL - bool storeInEEPROM[VARS_PER_TASK]{}; - #endif // if FEATURE_EEPROM_EXTERNAL }; typedef std::map TaskIndexNameMap; @@ -136,10 +133,6 @@ struct Caches { uint8_t getTaskVarStateClass(taskIndex_t taskIndex, taskVarIndex_t taskVarIndex); #endif // if FEATURE_MQTT_STATE_CLASS - #if FEATURE_EEPROM_EXTERNAL - uint8_t getTaskVarStoreInEEPROM(taskIndex_t taskIndex, - taskVarIndex_t taskVarIndex); - #endif // if FEATURE_EEPROM_EXTERNAL // Update all cached values, except the checksum. void updateExtraTaskSettingsCache(); diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index ff70c86fdf..5bf20e3aa4 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -62,8 +62,6 @@ #define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) #define EEPROM_MUX_FLAGS_MULTI 8 // bit-offset within multiplexerflags for bits or port (1 bit) -#define EEPROM_SAVEOPTIONS_DELAY 0 // bit-offset within EEPROMSaveOptions for the Save Delay (seconds) - #endif // if FEATURE_EEPROM_EXTERNAL // Stored in Settings.I2C_SPI_bus_Flags !!! #define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp index 768d0113a6..8cfc37c6ad 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -303,19 +303,6 @@ void ExtraTaskSettingsStruct::setTaskVarStateClass(taskVarIndex_t taskVarIndex, } } #endif // if FEATURE_MQTT_STATE_CLASS -#if FEATURE_EEPROM_EXTERNAL -bool ExtraTaskSettingsStruct::getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const { - if (!validTaskVarIndex(taskVarIndex)) { return false; } - return bitRead(VariousBits[taskVarIndex], 27); -} - -void ExtraTaskSettingsStruct::setTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex, - bool store) { - if (validTaskVarIndex(taskVarIndex)) { - bitWrite(VariousBits[taskVarIndex], 27, store); - } -} -#endif // if FEATURE_EEPROM_EXTERNAL void ExtraTaskSettingsStruct::populateDeviceValueNamesSeq( diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index 5265f813a1..dc5ad44614 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -89,11 +89,6 @@ struct ExtraTaskSettingsStruct void setTaskVarStateClass(taskVarIndex_t taskVarIndex, uint8_t stateClass); #endif // if FEATURE_MQTT_STATE_CLASS - #if FEATURE_EEPROM_EXTERNAL - bool getTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex) const; - void setTaskVarStoreInEEPROM(taskVarIndex_t taskVarIndex, - bool store); - #endif // if FEATURE_EEPROM_EXTERNAL void populateDeviceValueNamesSeq(const __FlashStringHelper *valuename, size_t nrValues, diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 9df700ba3d..4703692c93 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -11,10 +11,6 @@ #include "../ESPEasyCore/ESPEasy_backgroundtasks.h" #include "../ESPEasyCore/ESPEasy_Log.h" -#if FEATURE_EEPROM_EXTERNAL -# include "../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#endif // if FEATURE_EEPROM_EXTERNAL - #ifdef ESP8266 # include #endif // ifdef ESP8266 @@ -30,61 +26,6 @@ RTC_NOINIT_ATTR RTC_cache_struct RTC_cache; RTC_NOINIT_ATTR uint8_t RTC_cache_data[RTC_CACHE_DATA_SIZE]; #endif // ifdef ESP32 -/********************************************************************************************** - * EEPROM Helper function for RTC Cache - **********************************************************************************************/ -#if FEATURE_EEPROM_EXTERNAL -void EEPROMSaveWritePos() { - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { - // Save new position - if (ESPEasy::eeprom::EEPROMExternal->readInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET) != RTC_cache.writePos) { - ESPEasy::eeprom::EEPROMExternal->writeInt(EEPROM_RTC_CACHE_WRITEPOS_OFFSET, RTC_cache.writePos); - } - } -} - -void EEPROMSaveMetaDataChecksum() { - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { - // Save metadata checksum - if (ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_RTC_CACHE_META_CRC_OFFSET) != RTC_cache.checksumMetadata) { - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_RTC_CACHE_META_CRC_OFFSET, RTC_cache.checksumMetadata); - } - } -} - -void EEPROMSaveCacheChecksum() { - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { - // Save cache checksum - if (ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET) != RTC_cache.checksumData) { - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_RTC_CACHE_CHECKSUM_OFFSET, RTC_cache.checksumData); - } - } -} - -void EEPROMSaveRTCcache() { - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { - size_t count{}; - for (uint16_t idx = 0; idx < sizeof(RTC_cache); ++idx) { - const uint32_t metaAddr = EEPROM_RTC_CACHE_META_OFFSET + idx; - const uint8_t oldData = ESPEasy::eeprom::EEPROMExternal->read(metaAddr); - const uint8_t newData = reinterpret_cast(&RTC_cache)[idx]; - if (oldData != newData) { - ESPEasy::eeprom::EEPROMExternal->write(metaAddr, newData); - ++count; - } - } - // Save metadata checksum - EEPROMSaveMetaDataChecksum(); - // Save cache checksum - EEPROMSaveCacheChecksum(); - } -} -#endif // if FEATURE_EEPROM_EXTERNAL - /********************************************************************************************\ RTC located cache \*********************************************************************************************/ @@ -283,24 +224,6 @@ bool RTC_cache_handler_struct::write(const uint8_t *data, unsigned int size) { ++RTC_cache.writePos; } - #if FEATURE_EEPROM_EXTERNAL - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { - size_t count{}; - for (unsigned int idx = 0; idx < size; ++idx) { - const uint32_t cacheAddr = EEPROM_RTC_CACHE_START_OFFSET + idx; - const uint8_t oldData = ESPEasy::eeprom::EEPROMExternal->read(cacheAddr); - const uint8_t newData = data[idx]; - if (oldData != newData) { - ESPEasy::eeprom::EEPROMExternal->write(cacheAddr, newData); - ++count; - } - } - // Save new position - EEPROMSaveWritePos(); - } - #endif // if FEATURE_EEPROM_EXTERNAL - // Now store the updated part of the buffer to the RTC memory. // Pad some extra bytes around it to allow sample sizes not multiple of 4 bytes. int startOffset = RTC_cache.writePos - size; @@ -581,10 +504,6 @@ bool RTC_cache_handler_struct::saveRTCcache(unsigned int startOffset, size_t nrB RTC_cache.checksumData = getDataChecksum(); RTC_cache.checksumMetadata = calc_CRC32(reinterpret_cast(&RTC_cache), sizeof(RTC_cache) - sizeof(uint32_t)); - #if FEATURE_EEPROM_EXTERNAL - EEPROMSaveRTCcache(); - #endif // if FEATURE_EEPROM_EXTERNAL - #ifdef ESP32 return true; #endif // ifdef ESP32 @@ -653,11 +572,6 @@ void RTC_cache_handler_struct::clearRTCcacheData() { RTC_cache_data[i] = 0; } RTC_cache.writePos = 0; - - #if FEATURE_EEPROM_EXTERNAL - // Save new position - EEPROMSaveWritePos(); - #endif // if FEATURE_EEPROM_EXTERNAL } // Return true if any cache file found diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 867aef212c..496ee5bc6f 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -225,12 +225,6 @@ class SettingsStruct_tmpl inline void ShowUnitOfMeasureOnDevicesPage(bool value) { VariousBits_2.ShowUnitOfMeasureOnDevicesPage = !value; } #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - #if FEATURE_EEPROM_EXTERNAL - bool RestoreUserVarsFromEEPROMOnColdBoot() const { return VariousBits_2.RestoreUserVarsFromEEPROMOnColdBoot; } - void RestoreUserVarsFromEEPROMOnColdBoot(bool value) { VariousBits_2.RestoreUserVarsFromEEPROMOnColdBoot = value; } - bool RestoreUserVarsFromEEPROMOnWarmBoot() const { return VariousBits_2.RestoreUserVarsFromEEPROMOnWarmBoot; } - void RestoreUserVarsFromEEPROMOnWarmBoot(bool value) { VariousBits_2.RestoreUserVarsFromEEPROMOnWarmBoot = value; } - #endif // if FEATURE_EEPROM_EXTERNAL #if CONFIG_SOC_WIFI_SUPPORT_5G wifi_band_mode_t WiFi_band_mode() const { if (VariousBits_2.WiFi_band_mode == 0) return WIFI_BAND_MODE_AUTO; @@ -428,9 +422,7 @@ class SettingsStruct_tmpl void EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags); uint8_t EEPROMExternalType() const; void EEPROMExternalType(uint8_t type); - uint8_t EEPROMSaveDelaySeconds() const; - void EEPROMSaveDelaySeconds(uint8_t seconds); - #endif + #endif // if FEATURE_EEPROM_EXTERNAL #if FEATURE_I2CMULTIPLEXER int8_t getI2CMultiplexerType(uint8_t i2cBus) const; @@ -758,8 +750,8 @@ class SettingsStruct_tmpl uint32_t ShowUnitOfMeasureOnDevicesPage : 1; // Bit 07 // inverted uint32_t WiFi_band_mode : 2; // Bit 08 & 09 uint32_t WiFi_AP_enable_NAPT : 1; // Bit 10 // inverted - uint32_t RestoreUserVarsFromEEPROMOnColdBoot : 1; // Bit 11 - uint32_t RestoreUserVarsFromEEPROMOnWarmBoot : 1; // Bit 12 + uint32_t unused_11 : 1; // Bit 11 + uint32_t unused_12 : 1; // Bit 12 uint32_t MQTTConnectInBackground : 1; // Bit 13 // inverted uint32_t StartAPfallback_NoCredentials : 1; // Bit 14 // inverted diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index ce43fd9faf..6dd738d65e 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1355,7 +1355,7 @@ uint8_t SettingsStruct_tmpl::getI2CInterfacePCFMCP() const { } #if FEATURE_EEPROM_EXTERNAL -template +template uint8_t SettingsStruct_tmpl::getI2CInterfaceEEPROM() const { return get3BitFromUL(I2C_peripheral_bus, I2C_PERIPHERAL_BUS_EEPROM); } @@ -1363,38 +1363,30 @@ uint8_t SettingsStruct_tmpl::getI2CInterfaceEEPROM() const { #endif // if FEATURE_I2C_MULTIPLE #if FEATURE_EEPROM_EXTERNAL -template +template uint8_t SettingsStruct_tmpl::EEPROMExternalI2CAddress() const { return get8BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS); } -template +template void SettingsStruct_tmpl::EEPROMExternalI2CAddress(uint8_t address) { set8BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS, address); } -template +template uint16_t SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags() const { return get16BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX); } -template +template void SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags) { set16BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX, muxFlags); } -template +template uint8_t SettingsStruct_tmpl::EEPROMExternalType() const { return static_cast(get4BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); } -template +template void SettingsStruct_tmpl::EEPROMExternalType(uint8_t type) { set4BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); } -template -uint8_t SettingsStruct_tmpl::EEPROMSaveDelaySeconds() const { - return get8BitFromUL(EEPROMSaveOptions, EEPROM_SAVEOPTIONS_DELAY); -} -template -void SettingsStruct_tmpl::EEPROMSaveDelaySeconds(uint8_t seconds) { - set8BitToUL(EEPROMSaveOptions, EEPROM_SAVEOPTIONS_DELAY, seconds); -} #endif // if FEATURE_EEPROM_EXTERNAL #if FEATURE_I2CMULTIPLEXER diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 025bf9a842..6a4d9288dc 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -35,10 +35,6 @@ #include "../Helpers/StringGenerator_System.h" #include "../WebServer/ESPEasy_WebServer.h" -#if FEATURE_EEPROM_EXTERNAL -#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#endif // if FEATURE_EEPROM_EXTERNAL - #ifdef USE_RTOS_MULTITASKING # include "../Helpers/Networking.h" # include "../Helpers/PeriodicalActions.h" @@ -404,15 +400,6 @@ void ESPEasy_setup() logMemUsageAfter(F("hardwareInit()")); #endif // ifndef BUILD_NO_RAM_TRACKER - #if FEATURE_EEPROM_EXTERNAL - if ((ESPEasy::eeprom::checkEEPROMEnabled() > 0) && - (((lastBootCause <= BOOT_CAUSE_COLD_BOOT) && Settings.RestoreUserVarsFromEEPROMOnColdBoot()) || - ((lastBootCause <= BOOT_CAUSE_SOFT_RESTART) && Settings.RestoreUserVarsFromEEPROMOnWarmBoot())) - ) { - readUserVarFromRTC(); // Once more to fetch UserVar data from now available EEPROM/FRAM - } - #endif // if FEATURE_EEPROM_EXTERNAL - node_time.restoreFromRTC(); Settings.UseRTOSMultitasking = false; // For now, disable it, we experience heap corruption. diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index ca3303d5a2..a73153487d 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -12,16 +12,6 @@ #include "../Helpers/CRC_functions.h" #include "../../ESPEasy_common.h" -#if FEATURE_EEPROM_EXTERNAL -#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#include "../Globals/Cache.h" -#include "../Helpers/ESPEasy_Storage.h" -#include "../Helpers/Hardware_I2C.h" -#include "../Helpers/StringConverter.h" - -uint8_t readDataForUserVars(size_t index); // Forward declaration -#endif // if FEATURE_EEPROM_EXTERNAL - #ifdef ESP8266 #include #endif @@ -171,49 +161,6 @@ bool readFromRTC() return RTC.ID1 == 0xAA && RTC.ID2 == 0x55; } -#if FEATURE_EEPROM_EXTERNAL -uint32_t saveUserVarToEEPROM() { - uint32_t eepromWritten{}; - // Update system parameters if not correct - const bool paramsOk = ESPEasy::eeprom::validateEEPROMExternalParameters(); - - if (!paramsOk) { - ESPEasy::eeprom::updateEEPROMExternalParameters(); - } - - for (taskIndex_t task = 0; task < TASKS_MAX; ++task) { - if (Settings.TaskDeviceEnabled[task] || !paramsOk) { // Only check enabled tasks or when re-writing the params - const TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(task); - if (taskValues != nullptr) { - for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { - const bool storeValue = Cache.getTaskVarStoreInEEPROM(task, varNr); - if (!paramsOk || storeValue) { - const uint32_t newData = storeValue - ? taskValues->getUint32(varNr) - : std::numeric_limits::max(); // NaN when read as float - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(task, varNr); - if (newData != ESPEasy::eeprom::EEPROMExternal->readLong(addr)) { // Only update EEPROM if data differs - ESPEasy::eeprom::EEPROMExternal->writeLong(addr, newData); - eepromWritten += sizeof_uint32_t; - } - } - } - } - } - } - - if (eepromWritten) { // Did we change anything? Only then do the slow operation of CRC calculation by reading all bytes - // Calculate checksum for all stored values, not equal to the UserVar checksum! - const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - if (calcsum != ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET)) { - ESPEasy::eeprom::EEPROMExternal->writeLong(EEPROM_USERVAR_CHECKSUM_OFFSET, calcsum); - eepromWritten += sizeof_uint32_t; - } - } - return eepromWritten; -} -#endif // if FEATURE_EEPROM_EXTERNAL - /********************************************************************************************\ Save values to RTC memory \*********************************************************************************************/ @@ -223,47 +170,6 @@ bool saveUserVarToRTC() { bool saveUserVarToRTC(bool initial) { - #if FEATURE_EEPROM_EXTERNAL - // Check if we have an external EEPROM available on the configured I2C bus & channel, and save all task values there - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (!initial && (eepromAddress > 0) && !ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { // EEPROM Configured and writable? - - if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM - #if FEATURE_EEPROM_BACKGROUND - ESPEasy::eeprom::EEPROMExternalTaskData taskdata; - taskdata.type = ESPEasy::eeprom::EEPROMExternalTaskType_e::UserVars; - taskdata.function = saveUserVarToEEPROM; - ESPEasy::eeprom::EEPROMAddTask(ESPEasy::eeprom::EEPROMExternalTaskType_e::UserVars, taskdata); - #else // if FEATURE_EEPROM_BACKGROUND - #ifndef BUILD_NO_DEBUG - uint32_t startmicros = micros(); - const uint32_t eepromWritten = - #endif - saveUserVarToEEPROM(); - #ifndef BUILD_NO_DEBUG - startmicros = micros() - startmicros; - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { // FIXME LOG_LEVEL_DEBUG - const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = - static_cast(Settings.EEPROMExternalType()); - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: UserVar: %u bytes (%.2f ms) written to %s"), - eepromWritten, startmicros / 1000.0f, FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); - } - #endif // ifndef BUILD_NO_DEBUG - #endif // if FEATURE_EEPROM_BACKGROUND - } - #if FEATURE_I2CMULTIPLEXER - I2CMultiplexerOff( - #if FEATURE_I2C_MULTIPLE - Settings.getI2CInterfaceEEPROM() - #else //if FEATURE_I2C_MULTIPLE - 0 - #endif // if FEATURE_I2C_MULTIPLE - ); // Restore the Multiplexer channel - #endif // if FEATURE_I2CMULTIPLEXER - } - #endif // if FEATURE_EEPROM_EXTERNAL - // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. #if defined(ESP32) @@ -291,76 +197,11 @@ bool saveUserVarToRTC(bool initial) #endif } -#if FEATURE_EEPROM_EXTERNAL -uint8_t readDataForUserVars(size_t index) { - if (nullptr != ESPEasy::eeprom::EEPROMExternal) { - return ESPEasy::eeprom::EEPROMExternal->read(EEPROM_USERVAR_START_OFFSET + index); - } - return 0; -} -#endif // if FEATURE_EEPROM_EXTERNAL - /********************************************************************************************\ Read RTC struct from RTC memory \*********************************************************************************************/ bool readUserVarFromRTC() { - #if FEATURE_EEPROM_EXTERNAL - const uint8_t eepromAddress = ESPEasy::eeprom::checkEEPROMEnabled(); - if (eepromAddress > 0) { // EEPROM Configured and restoring of Task Values enabled? - bool result = false; - - if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM - // Check system parameters with last stored values - const bool eepromParamsOK = ESPEasy::eeprom::validateEEPROMExternalParameters(true); - // Check checksum and if correct, restore UserVar values - const uint32_t checksum = ESPEasy::eeprom::EEPROMExternal->readLong(EEPROM_USERVAR_CHECKSUM_OFFSET); - const uint32_t calcsum = calc_CRC32(readDataForUserVars, TASKS_MAX * VARS_PER_TASK * sizeof_uint32_t); - #ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_INFO)) { // FIXME LOG_LEVEL_DEBUG - addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: readUserVarFromRTC calculated: %u, expected: %u equal: %c, params: %c"), - calcsum, checksum, calcsum == checksum ? 'Y' : 'n', eepromParamsOK ? 'Y' : 'n')); - } - #endif // ifndef BUILD_NO_DEBUG - if (eepromParamsOK && (calcsum == checksum)) { - addLog(LOG_LEVEL_INFO, F("INIT : Restoring Task Values from EEPROM.")); - result = true; - taskIndex_t lastTask = TASKS_MAX; - for (size_t i = 0; i < (TASKS_MAX * VARS_PER_TASK) && result; ++i) { - const taskIndex_t taskIndex = i / VARS_PER_TASK; - const uint8_t varNr = i % VARS_PER_TASK; - if (taskIndex != lastTask) { - LoadTaskSettings(taskIndex); - lastTask = taskIndex; - } - // Store in raw form, so we don't apply formula as we don't really know what type is required. - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(taskIndex, varNr); - if (addr != std::numeric_limits::max()) { - if (Cache.getTaskVarStoreInEEPROM(taskIndex, varNr)) { // Restore only when enabled - TaskValues_Data_t* taskValues = UserVar.getRawTaskValues_Data(taskIndex); - taskValues->setUint32(varNr, ESPEasy::eeprom::EEPROMExternal->readLong(addr)); - } - } else { - result = false; - } - } - } - } - #if FEATURE_I2CMULTIPLEXER - I2CMultiplexerOff( - #if FEATURE_I2C_MULTIPLE - Settings.getI2CInterfaceEEPROM() - #else //if FEATURE_I2C_MULTIPLE - 0 - #endif // if FEATURE_I2C_MULTIPLE - ); // Restore the Multiplexer channel - #endif // if FEATURE_I2CMULTIPLEXER - if (result) { - return true; // We did all that was needed - } - } - #endif // if FEATURE_EEPROM_EXTERNAL - // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. #if defined(ESP32) diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index 97713d4d3c..12c4659766 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -47,10 +47,6 @@ #include "../DataStructs/NotificationSettingsStruct.h" #endif // if FEATURE_NOTIFIER -#if FEATURE_EEPROM_EXTERNAL -#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#endif // if FEATURE_EEPROM_EXTERNAL - // ******************************************************************************** // Check struct sizes at compile time // Usage: @@ -193,11 +189,6 @@ void run_compiletime_checks() { // to determine nr of bits in a struct. static_assert(GPIO_DIRECTION_NR_BITS== NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)), "Correct GPIO_DIRECTION_NR_BITS"); - #if FEATURE_EEPROM_EXTERNAL - static_assert(EEPROM_CUSTOM_START_OFFSET > EEPROM_GPIO_PINSTATE_END_OFFSET, "EEPROM GPIO Pinstates overlap with Slots values!"); - #endif // if FEATURE_EEPROM_EXTERNAL - - #endif // ifndef LIMIT_BUILD_SIZE } diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index c7daef17de..d31ec79f1b 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -45,10 +45,6 @@ #include "../../ESPEasy_fdwdecl.h" #endif -#if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND -#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#endif // if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND - #define PLUGIN_ID_MQTT_IMPORT 37 @@ -209,10 +205,6 @@ void runOncePerSecond() PluginCall(PLUGIN_ONCE_A_SECOND, 0, dummy); // unsigned long elapsed = micros() - start; - #if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND - ESPEasy::eeprom::EEPROMExternalLoop(); - #endif // if FEATURE_EEPROM_EXTERNAL && FEATURE_EEPROM_BACKGROUND - #if FEATURE_NETWORK_STATS for (ESPEasy::net::networkIndex_t x = 0; x < NETWORK_MAX; x++) { if (Settings.getNetworkEnabled(x)) { diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index 86c95fb38c..26a1cf542d 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -209,7 +209,11 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us uint32_t slot{}; String value; if (validUIntFromString(valueName, slot)) { + #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + value = doubleToString(ESPEasy::eeprom::readEEPROMSlot(slot)); + #else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE value = toString(ESPEasy::eeprom::readEEPROMSlot(slot)); + #endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } else if (valueName.equalsIgnoreCase(F("max"))) { value = ESPEasy::eeprom::getEEPROMMaxSlots(); } else if (valueName.equalsIgnoreCase(F("wp"))) { diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index ddaeeebe25..5103e950e6 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -484,21 +484,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } #endif // if FEATURE_MQTT_CONNECT_BACKGROUND - #if FEATURE_EEPROM_EXTERNAL - case LabelType::EEPROM_RESTORE_ON_COLDBOOT: - { - return KeyValueStruct(F("Cold boot: Restore Task Values from EEPROM"), Settings.RestoreUserVarsFromEEPROMOnColdBoot()); - } - case LabelType::EEPROM_RESTORE_ON_WARMBOOT: - { - return KeyValueStruct(F("Warm boot: Restore Task Values from EEPROM"), Settings.RestoreUserVarsFromEEPROMOnWarmBoot()); - } - case LabelType::EEPROM_SAVE_DELAY: - { - return KeyValueStruct(F("Save interval (seconds)"), Settings.EEPROMSaveDelaySeconds()); - } - #endif // if FEATURE_EEPROM_EXTERNAL - #if CONFIG_SOC_WIFI_SUPPORT_5G case LabelType::WIFI_BAND_MODE: { diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 42224c1849..1594f63c95 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -113,12 +113,6 @@ struct LabelType { MQTT_CONNECT_IN_BACKGROUND, #endif // if FEATURE_MQTT_CONNECT_BACKGROUND - #if FEATURE_EEPROM_EXTERNAL - EEPROM_RESTORE_ON_COLDBOOT, - EEPROM_RESTORE_ON_WARMBOOT, - EEPROM_SAVE_DELAY, - #endif // if FEATURE_EEPROM_EXTERNAL - BOOT_TYPE, // Cold boot BOOT_COUNT, // 0 RESET_REASON, // Software/System restart diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 140a89c5a9..048ea21cea 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -29,9 +29,6 @@ # include "../Helpers/_Plugin_init.h" # include "../Helpers/_Plugin_SensorTypeHelper.h" # include "../Helpers/_Plugin_Helper_serial.h" -#if FEATURE_EEPROM_EXTERNAL -# include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" -#endif // if FEATURE_EEPROM_EXTERNAL # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" # include "../Helpers/SPI_Helper.h" @@ -456,11 +453,6 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # if FEATURE_PLUGIN_FILTER ExtraTaskSettings.enablePluginFilter(varNr, isFormItemChecked(getPluginCustomArgName(F("TDFIL"), varNr))); # endif // if FEATURE_PLUGIN_FILTER - #if FEATURE_EEPROM_EXTERNAL - if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { - ExtraTaskSettings.setTaskVarStoreInEEPROM(varNr, isFormItemChecked(getPluginCustomArgName(F("TDEE"), varNr))); - } - #endif // if FEATURE_EEPROM_EXTERNAL # if FEATURE_PLUGIN_STATS PluginStats_Config_t pluginStats_Config; @@ -1844,13 +1836,6 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde ++colCount; } - #if FEATURE_EEPROM_EXTERNAL - if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { - html_table_header(F("Eeprom"), 30); - ++colCount; - } - #endif // if FEATURE_EEPROM_EXTERNAL - # if FEATURE_PLUGIN_STATS if (device.PluginStats) @@ -1990,19 +1975,6 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde addNumericBox(id, Cache.getTaskDeviceValueDecimals(taskIndex, varNr), 0, 6); } - #if FEATURE_EEPROM_EXTERNAL - if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { - html_TD(); - addCheckBox( - getPluginCustomArgName(F("TDEE"), varNr), - Cache.getTaskVarStoreInEEPROM(taskIndex, varNr), false - #if FEATURE_TOOLTIPS - , F("Store value in External EEPROM") - #endif // if FEATURE_TOOLTIPS - ); - } - #endif // if FEATURE_EEPROM_EXTERNAL - # if FEATURE_PLUGIN_STATS if (device.PluginStats) diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp index 6f0869d07e..cd3d3ead3c 100644 --- a/src/src/WebServer/EpromVarPage.cpp +++ b/src/src/WebServer/EpromVarPage.cpp @@ -15,18 +15,11 @@ #include "../Helpers/StringConverter.h" -// #if FEATURE_STRING_VARIABLES -// # include "../Helpers/StringParser.h" -// #endif // if - #if FEATURE_EEPROM_EXTERNAL void handle_eepromvars() { if (!isLoggedIn()) { return; } - const bool showTasks = getFormItemInt(F("tasks"), 1) == 1; - const bool allTasks = getFormItemInt(F("enabled"), 0) != 0; - TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); @@ -42,52 +35,6 @@ void handle_eepromvars() { 400); html_table_header(F("")); - if (showTasks) { - html_TR(); - - // sub-table header - html_table_header(allTasks ? F("Task") : F("Task (Enabled)"), 300); - html_table_header(F("Value"), 500); - html_table_header(F("Content"), 400); - html_table_header(F("")); - - for (taskIndex_t tsk = 0; tsk < TASKS_MAX; ++tsk) { - const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(tsk); - - if ((Settings.TaskDeviceEnabled[tsk] && validDeviceIndex(DeviceIndex)) || allTasks) { - LoadTaskSettings(tsk); - html_TR_TD(); - addHtmlInt(tsk + 1); - addHtml(' '); - addHtml(getTaskDeviceName(tsk)); - - for (taskVarIndex_t var = 0; var < VARS_PER_TASK; ++var) { - if (var != 0) { - html_TR_TD(); - } - html_TD(); - addHtmlInt(var + 1); - addHtml(' '); - addHtml(getTaskValueName(tsk, var)); - html_TD(); - const uint32_t addr = ESPEasy::eeprom::getEEPROMAddressForTaskValue(tsk, var); - const float value = ESPEasy::eeprom::EEPROMExternal->readFloat(addr); - const uint32_t data = ESPEasy::eeprom::EEPROMExternal->readLong(addr); - - if (isnan(value) || (addr == std::numeric_limits::max()) || !ExtraTaskSettings.getTaskVarStoreInEEPROM(var)) { - addHtml('-'); - } else { - addHtml(floatToString(value, ExtraTaskSettings.TaskDeviceValueDecimals[var])); - - if (!essentiallyZero(value)) { // Only show hex values for non-zero values to reduce eye-strain - addHtml(strformat(F(" (0x%04x)"), data)); - } - } - } - delay(0); - } - } - } html_TR(); // sub-table header @@ -109,14 +56,15 @@ void handle_eepromvars() { html_TR_TD(); addHtmlInt(slot); html_TD(); + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + addHtml(doubleToString(value)); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE addHtml(toString(value)); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE html_TD(2); } } - // TODO: List String values in EEPROM - // TODO: List C016 Cache entries in EEPROM - addTableSeparator(F("Summary"), 3, 2); html_TR_TD(); @@ -137,8 +85,6 @@ void handle_eepromvars() { } html_TD(); - // TODO: List String summary in EEPROM - html_end_table(); } else { addHtml(F("External EEPROM not enabled.")); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c7b8c541b7..dcb0203f92 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -65,9 +65,6 @@ void handle_hardware() { Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256))); Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); - Settings.RestoreUserVarsFromEEPROMOnColdBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_COLDBOOT)); - Settings.RestoreUserVarsFromEEPROMOnWarmBoot(isFormItemChecked(LabelType::EEPROM_RESTORE_ON_WARMBOOT)); - Settings.EEPROMSaveDelaySeconds(getFormItemInt(LabelType::EEPROM_SAVE_DELAY)); # if FEATURE_I2CMULTIPLEXER @@ -228,9 +225,6 @@ void handle_hardware() { addRowLabel(F("'WriteEE' slots available")); addHtmlInt(ESPEasy::eeprom::getEEPROMMaxSlots()); } - addFormCheckBox(getLabel(LabelType::EEPROM_RESTORE_ON_COLDBOOT), getInternalLabel(LabelType::EEPROM_RESTORE_ON_COLDBOOT), Settings.RestoreUserVarsFromEEPROMOnColdBoot() && eepromChecked, !eepromChecked); - addFormCheckBox(getLabel(LabelType::EEPROM_RESTORE_ON_WARMBOOT), getInternalLabel(LabelType::EEPROM_RESTORE_ON_WARMBOOT), Settings.RestoreUserVarsFromEEPROMOnWarmBoot() && eepromChecked, !eepromChecked); - addFormNumericBox(getLabel(LabelType::EEPROM_SAVE_DELAY), getInternalLabel(LabelType::EEPROM_SAVE_DELAY), Settings.EEPROMSaveDelaySeconds(), 0, 250); } #endif // if FEATURE_EEPROM_EXTERNAL