From c998cce1a86799648ac25fa75cb42d590cf087a6 Mon Sep 17 00:00:00 2001 From: Marcel Peterkau Date: Sun, 24 Aug 2025 13:14:06 +0200 Subject: [PATCH] improved EEPROM-Initialize and recovery, renamed typo in varname and comments by ChatGPT --- Software/include/config.h | 58 ++++-- Software/include/globals.h | 2 +- Software/src/can.cpp | 2 +- Software/src/config.cpp | 397 ++++++++++++++++++++++++------------- Software/src/debugger.cpp | 6 +- Software/src/webui.cpp | 2 +- 6 files changed, 304 insertions(+), 163 deletions(-) diff --git a/Software/include/config.h b/Software/include/config.h index d0b3321..ff9b26e 100644 --- a/Software/include/config.h +++ b/Software/include/config.h @@ -1,27 +1,26 @@ /** * @file config.h + * @brief Configuration structures and EEPROM API for ChainLube firmware. * - * @brief Header file for configuration settings and EEPROM operations in the ChainLube application. + * Defines EEPROM layout versions, configuration and persistence data structures, + * and the public functions for storing, loading, formatting and validating + * configuration/persistence records. * - * This file defines configuration settings for the ChainLube project, including default values, - * EEPROM structures, and functions for EEPROM operations. It also defines enums for different sources - * of speed input, GPS baud rates, and CAN bus sources. Additionally, it includes functions for EEPROM handling - * such as storing, retrieving, and formatting configuration data. - * - * @author Marcel Peterkau - * @date 09.01.2024 + * Notes: + * - The system always boots with defaults in RAM; EEPROM is optional. + * - DTC handling for EEPROM availability and integrity is centralized in EEPROM_Process(). */ #ifndef _CONFIG_H_ #define _CONFIG_H_ -#include -#include +#include #include #include "dtc.h" #include "common.h" -#define EEPROM_STRUCTURE_REVISION 4 // Increment this version when changing EEPROM structures +// Increment when EEPROM structure changes +#define EEPROM_STRUCTURE_REVISION 4 #if PCB_REV == 1 || PCB_REV == 2 || PCB_REV == 3 #define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC64 @@ -29,9 +28,14 @@ #define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC256 #endif +/** + * @brief EEPROM request state machine codes. + * + * Used by globals.requestEEAction to schedule EEPROM operations. + */ typedef enum EERequest_e { - EE_IDLE, + EE_IDLE = 0, EE_CFG_SAVE, EE_CFG_LOAD, EE_CFG_FORMAT, @@ -39,11 +43,13 @@ typedef enum EERequest_e EE_PDS_LOAD, EE_PDS_FORMAT, EE_FORMAT_ALL, - EE_ALL_SAVE - + EE_ALL_SAVE, + EE_REINITIALIZE } EERequest_t; -// Structure for persistence data stored in EEPROM +/** + * @brief Wear-levelled persistence data block. + */ typedef struct { uint16_t writeCycleCounter; @@ -54,7 +60,9 @@ typedef struct uint32_t checksum; } persistenceData_t; -// Structure for configuration settings stored in EEPROM +/** + * @brief User configuration stored in EEPROM. + */ typedef struct { uint8_t EEPROM_Version; @@ -85,7 +93,9 @@ typedef struct uint32_t checksum; } LubeConfig_t; -// Default configuration settings +/** + * @brief Factory defaults for configuration (in RAM). + */ const LubeConfig_t LubeConfig_defaults = { 0, 8000, 4000, 320, DEFAULT_PUMP_DOSE, 30, 1, 150, 70, 18, 2000, 25, 500, 10, SOURCE_IMPULSE, BAUD_115200, @@ -100,21 +110,31 @@ const LubeConfig_t LubeConfig_defaults = { true, 0}; +/* ==== Public API ==== */ + +// Initialization & main process void InitEEPROM(); void EEPROM_Process(); + +// Config & persistence access void StoreConfig_EEPROM(); void GetConfig_EEPROM(); void StorePersistence_EEPROM(); void GetPersistence_EEPROM(); void FormatConfig_EEPROM(); void FormatPersistence_EEPROM(); +void MovePersistencePage_EEPROM(boolean reset); + +// Utilities uint32_t Checksum_EEPROM(uint8_t const *data, size_t len); void dumpEEPROM(uint16_t memoryAddress, uint16_t length); -void MovePersistencePage_EEPROM(boolean reset); uint32_t ConfigSanityCheck(bool autocorrect = false); bool validateWiFiString(char *string, size_t size); +/* ==== Externals ==== */ + extern LubeConfig_t LubeConfig; extern persistenceData_t PersistenceData; -extern uint16_t eePersistenceMarker; +extern uint16_t eePersistenceAddress; + #endif // _CONFIG_H_ diff --git a/Software/include/globals.h b/Software/include/globals.h index 2123a07..dd52e66 100644 --- a/Software/include/globals.h +++ b/Software/include/globals.h @@ -27,7 +27,7 @@ typedef struct Globals_s EERequest_t requestEEAction = EE_IDLE; /**< EEPROM-related request */ char DeviceName[33]; /**< Device name */ char FlashVersion[10]; /**< Flash version */ - uint16_t eePersistanceAdress; /**< EEPROM persistence address */ + uint16_t eePersistenceAddress; /**< EEPROM persistence address */ uint8_t TankPercentage; /**< Tank percentage */ bool hasDTC; /**< Flag indicating the presence of Diagnostic Trouble Codes (DTC) */ bool measurementActive; /**< Flag indicating active measurement */ diff --git a/Software/src/can.cpp b/Software/src/can.cpp index fed9947..b075de5 100644 --- a/Software/src/can.cpp +++ b/Software/src/can.cpp @@ -143,7 +143,7 @@ void sendCANDebugMessage() data[5] = (0x01 & globals.hasDTC) | ((0x01 & globals.measurementActive) << 1); break; case 2: - memcpy(&data[1], &globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress)); + memcpy(&data[1], &globals.eePersistenceAddress, sizeof(globals.eePersistenceAddress)); memcpy(&data[3], &PersistenceData.tankRemain_microL, sizeof(PersistenceData.tankRemain_microL)); break; case 3: diff --git a/Software/src/config.cpp b/Software/src/config.cpp index 3a333b7..1dc6652 100644 --- a/Software/src/config.cpp +++ b/Software/src/config.cpp @@ -1,56 +1,181 @@ /** * @file config.cpp - * @brief Implementation of EEPROM and configuration-related functions. + * @brief EEPROM handling and configuration storage for the ChainLube firmware. * - * This file contains functions for managing EEPROM storage and handling configuration data. - * It includes the definitions of configuration structures, EEPROM access, and utility functions. + * Responsibilities: + * - Bring-up of the external I²C EEPROM + * - Robust availability checks with optional bus recovery + * - Central processing of EEPROM requests (save/load/format/move page) + * - CRC32 utilities and debug dump helpers + * + * Design notes: + * - The device boots with sane in-RAM defaults so the system stays operable + * even when EEPROM is missing. Actual lube execution is gated by DTCs elsewhere. + * - The DTC DTC_NO_EEPROM_FOUND is set/cleared only in EEPROM_Process(), never here ad-hoc. + * - Background recovery is non-blocking and driven by millis(). */ +#include +#include + #include "config.h" #include "debugger.h" #include "globals.h" -// Instance of I2C_eeprom for EEPROM access +// Recovery edge flag: set when availability changes 0 -> 1 +static bool eeRecoveredOnce = false; +// Non-blocking retry scheduling +static uint32_t eeNextTryMs = 0; +static uint32_t eeRetryIntervalMs = 2000; // ms between background attempts + +// I²C EEPROM instance I2C_eeprom ee(0x50, EEPROM_SIZE_BYTES); -// Configuration and persistence data structures +// Configuration and persistence data LubeConfig_t LubeConfig; persistenceData_t PersistenceData; -// EEPROM version identifier -const uint16_t eeVersion = EEPROM_STRUCTURE_REVISION; +// EEPROM structure version (bumped when layout changes) +const uint16_t eeVersion = EEPROM_STRUCTURE_REVISION; -// Flag indicating whether EEPROM is available -boolean eeAvailable = false; +// Latched availability flag +static bool eeAvailable = false; -// Offsets within EEPROM for LubeConfig and PersistenceData +// EEPROM layout offsets const uint16_t startofLubeConfig = 16; const uint16_t startofPersistence = 16 + sizeof(LubeConfig) + (sizeof(LubeConfig) % 16); -// Function prototype to check EEPROM availability -boolean checkEEPROMavailable(); +// availability probe +bool EEPROM_Available(bool recover = false, uint8_t attempts = 3, uint16_t delay_ms = 25); + +// Robust EEPROM handling (internal helpers) +void I2C_BusReset(); +bool TryRecoverEEPROM(uint8_t attempts = 5, uint16_t delay_ms = 50); /** - * @brief Initializes EEPROM and checks its availability. + * @brief Initialize I²C and EEPROM driver, load in-RAM defaults. * - * This function initializes the EEPROM using the I2C_eeprom instance and checks if it's available. + * Loads defaults into RAM to keep the application operational. + * Availability is checked but no DTC is set here—EEPROM_Process() is the single place + * that sets/clears DTC_NO_EEPROM_FOUND. */ void InitEEPROM() { LubeConfig = LubeConfig_defaults; PersistenceData = {0}; + + Wire.begin(); ee.begin(); - checkEEPROMavailable(); + + eeAvailable = ee.isConnected(); } /** - * @brief Processes EEPROM actions based on the request from the global state. + * @brief Try to free a stuck I²C bus and enforce a STOP condition. * - * This function processes EEPROM actions based on the request from the global state. - * It performs actions such as saving, loading, and formatting EEPROM data for both configuration and persistence. + * Pulses SCL up to 9 times to release a held SDA, then issues a STOP (SDA ↑ while SCL ↑). + * Finally returns control to Wire. + */ +void I2C_BusReset() +{ + pinMode(SCL, OUTPUT_OPEN_DRAIN); + pinMode(SDA, INPUT_PULLUP); + + for (int i = 0; i < 9; i++) + { + digitalWrite(SCL, LOW); + delayMicroseconds(5); + digitalWrite(SCL, HIGH); + delayMicroseconds(5); + } + pinMode(SDA, OUTPUT_OPEN_DRAIN); + digitalWrite(SDA, LOW); + delayMicroseconds(5); + digitalWrite(SCL, HIGH); + delayMicroseconds(5); + digitalWrite(SDA, HIGH); + delayMicroseconds(5); + + pinMode(SCL, INPUT); + pinMode(SDA, INPUT); +} + +/** + * @brief Attempt to recover EEPROM connectivity. + * + * Sequence per attempt: + * - I²C bus reset + * - Wire.begin() + * - ee.begin() + * - short settle delay + * + * On first successful probe (0->1) the eeRecoveredOnce flag is raised. + * + * @param attempts Number of attempts + * @param delay_ms Delay between attempts (after ee.begin()) + * @return true if EEPROM is reachable after recovery, false otherwise + */ +bool TryRecoverEEPROM(uint8_t attempts, uint16_t delay_ms) +{ + for (uint8_t n = 0; n < attempts; n++) + { + I2C_BusReset(); + + // ESP8266 core: Wire.end() is not available; re-begin is sufficient + Wire.begin(); + delay(2); + + ee.begin(); + delay(delay_ms); + + if (ee.isConnected()) + { + if (!eeAvailable) + eeRecoveredOnce = true; // edge 0 -> 1 + eeAvailable = true; + return true; + } + } + eeAvailable = false; + return false; +} + +/** + * @brief Central EEPROM task: background recovery, DTC handling, and request dispatch. + * + * Called periodically from the main loop. Non-blocking by design. + * - Schedules gentle recovery tries based on millis() + * - Sets DTC_NO_EEPROM_FOUND when unavailable + * - On successful recovery edge, clears DTC and reloads CFG/PDS exactly once + * - Executes requested actions (save/load/format/move) */ void EEPROM_Process() { + // Background recovery (single soft attempt per interval) + const uint32_t now = millis(); + if (!EEPROM_Available() && now >= eeNextTryMs) + { + (void)TryRecoverEEPROM(1, 10); + eeNextTryMs = now + eeRetryIntervalMs; + } + + // Central DTC handling + if (!EEPROM_Available()) + { + MaintainDTC(DTC_NO_EEPROM_FOUND, true); + } + + // Recovery edge: clear DTC and reload persisted data exactly once + if (EEPROM_Available() && eeRecoveredOnce) + { + MaintainDTC(DTC_NO_EEPROM_FOUND, false); + GetConfig_EEPROM(); + GetPersistence_EEPROM(); + eeRecoveredOnce = false; + Debug_pushMessage("EEPROM recovered – reloaded CFG/PDS\n"); + } + + // Request dispatcher switch (globals.requestEEAction) { case EE_CFG_SAVE: @@ -58,33 +183,39 @@ void EEPROM_Process() globals.requestEEAction = EE_IDLE; Debug_pushMessage("Stored EEPROM CFG\n"); break; + case EE_CFG_LOAD: GetConfig_EEPROM(); globals.requestEEAction = EE_IDLE; Debug_pushMessage("Loaded EEPROM CFG\n"); break; + case EE_CFG_FORMAT: FormatConfig_EEPROM(); globals.requestEEAction = EE_IDLE; GetConfig_EEPROM(); Debug_pushMessage("Formatted EEPROM CFG\n"); break; + case EE_PDS_SAVE: StorePersistence_EEPROM(); globals.requestEEAction = EE_IDLE; Debug_pushMessage("Stored EEPROM PDS\n"); break; + case EE_PDS_LOAD: GetPersistence_EEPROM(); globals.requestEEAction = EE_IDLE; Debug_pushMessage("Loaded EEPROM PDS\n"); break; + case EE_PDS_FORMAT: FormatPersistence_EEPROM(); globals.requestEEAction = EE_IDLE; GetPersistence_EEPROM(); Debug_pushMessage("Formatted EEPROM PDS\n"); break; + case EE_FORMAT_ALL: FormatConfig_EEPROM(); FormatPersistence_EEPROM(); @@ -93,73 +224,93 @@ void EEPROM_Process() globals.requestEEAction = EE_IDLE; Debug_pushMessage("Formatted EEPROM ALL\n"); break; + case EE_ALL_SAVE: StorePersistence_EEPROM(); StoreConfig_EEPROM(); globals.requestEEAction = EE_IDLE; Debug_pushMessage("Stored EEPROM ALL\n"); break; + + case EE_REINITIALIZE: + { + // quick burst of attempts + const bool ok = TryRecoverEEPROM(5, 20); + if (ok) + { + // Edge & reload are handled by the block above + Debug_pushMessage("EEPROM reinitialize OK\n"); + } + else + { + MaintainDTC(DTC_NO_EEPROM_FOUND, true); + Debug_pushMessage("EEPROM reinitialize FAILED\n"); + } + globals.requestEEAction = EE_IDLE; + break; + } + case EE_IDLE: default: globals.requestEEAction = EE_IDLE; + break; } } /** - * @brief Stores the configuration data in EEPROM. + * @brief Store configuration to EEPROM (with CRC and sanity report). * - * This function calculates the checksum for the configuration data, updates it, and stores it in EEPROM. - * It also performs a sanity check on the configuration and raises a diagnostic trouble code (DTC) if needed. + * Writes only if EEPROM is available. On completion, DTC_EEPROM_CFG_SANITY is + * raised if any config fields are out of plausible bounds (bitmask payload). */ void StoreConfig_EEPROM() { LubeConfig.checksum = 0; LubeConfig.checksum = Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)); - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; ee.updateBlock(startofLubeConfig, (uint8_t *)&LubeConfig, sizeof(LubeConfig)); - uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false); - - if (ConfigSanityCheckResult > 0) + const uint32_t sanity = ConfigSanityCheck(false); + if (sanity > 0) { - MaintainDTC(DTC_EEPROM_CFG_SANITY, true, ConfigSanityCheckResult); + MaintainDTC(DTC_EEPROM_CFG_SANITY, true, sanity); } } /** - * @brief Retrieves the configuration data from EEPROM. + * @brief Load configuration from EEPROM and validate. * - * This function reads the configuration data from EEPROM, performs a checksum validation, - * and conducts a sanity check on the configuration. It raises a diagnostic trouble code (DTC) if needed. + * Performs CRC check and sanity validation and raises the respective DTCs: + * - DTC_EEPROM_CFG_BAD if CRC fails + * - DTC_EEPROM_CFG_SANITY with bitmask payload if values are out of bounds */ void GetConfig_EEPROM() { - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; ee.readBlock(startofLubeConfig, (uint8_t *)&LubeConfig, sizeof(LubeConfig)); - uint32_t checksum = LubeConfig.checksum; + const uint32_t checksum = LubeConfig.checksum; LubeConfig.checksum = 0; - MaintainDTC(DTC_EEPROM_CFG_BAD, (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) != checksum)); + MaintainDTC(DTC_EEPROM_CFG_BAD, + (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) != checksum)); LubeConfig.checksum = checksum; - uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false); - - MaintainDTC(DTC_EEPROM_CFG_SANITY, (ConfigSanityCheckResult > 0), ConfigSanityCheckResult); - + const uint32_t sanity = ConfigSanityCheck(false); + MaintainDTC(DTC_EEPROM_CFG_SANITY, (sanity > 0), sanity); } /** - * @brief Stores the persistence data in EEPROM. + * @brief Store persistence record to EEPROM (wear-levelled page). * - * This function increments the write cycle counter, performs a checksum calculation on the persistence data, - * and stores it in EEPROM. It also handles EEPROM page movement when needed. + * Increments the write-cycle counter and moves the page if close to the limit. + * Writes only if EEPROM is available. */ void StorePersistence_EEPROM() { @@ -171,28 +322,27 @@ void StorePersistence_EEPROM() PersistenceData.checksum = 0; PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)); - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; - ee.updateBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData)); + ee.updateBlock(globals.eePersistenceAddress, (uint8_t *)&PersistenceData, sizeof(PersistenceData)); } /** - * @brief Retrieves the persistence data from EEPROM. + * @brief Load persistence record, validating address range and CRC. * - * This function reads the EEPROM to get the start address of the persistence data. - * If the start address is out of range, it resets and stores defaults. Otherwise, - * it reads from EEPROM and checks if the data is correct. + * If the stored start address is out of range, the persistence partition is reset, + * formatted, and DTC_EEPROM_PDSADRESS_BAD is raised. + * Otherwise, the record is read and checked; DTC_EEPROM_PDS_BAD is raised on CRC failure. */ void GetPersistence_EEPROM() { - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; - ee.readBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress)); - // if we got the StartAdress of Persistance and it's out of Range - we Reset it and store defaults - // otherwise we Read from eeprom and check if everything is correct - if (globals.eePersistanceAdress < startofPersistence || globals.eePersistanceAdress > ee.getDeviceSize()) + ee.readBlock(0, (uint8_t *)&globals.eePersistenceAddress, sizeof(globals.eePersistenceAddress)); + + if (globals.eePersistenceAddress < startofPersistence || globals.eePersistenceAddress > ee.getDeviceSize()) { MovePersistencePage_EEPROM(true); FormatPersistence_EEPROM(); @@ -200,74 +350,65 @@ void GetPersistence_EEPROM() } else { - ee.readBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData)); + ee.readBlock(globals.eePersistenceAddress, (uint8_t *)&PersistenceData, sizeof(PersistenceData)); - uint32_t checksum = PersistenceData.checksum; + const uint32_t checksum = PersistenceData.checksum; PersistenceData.checksum = 0; - MaintainDTC(DTC_EEPROM_PDS_BAD, (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum)); + MaintainDTC(DTC_EEPROM_PDS_BAD, + (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum)); PersistenceData.checksum = checksum; } } /** - * @brief Formats the configuration partition in EEPROM. - * - * This function resets the configuration data to defaults and stores it in EEPROM. + * @brief Reset the configuration partition to defaults and write it. */ void FormatConfig_EEPROM() { - Debug_pushMessage("Formatting Config-Partition\n"); + Debug_pushMessage("Formatting Config partition\n"); LubeConfig = LubeConfig_defaults; LubeConfig.EEPROM_Version = eeVersion; StoreConfig_EEPROM(); } /** - * @brief Formats the persistence partition in EEPROM. - * - * This function resets the persistence data to defaults and stores it in EEPROM. + * @brief Reset the persistence partition and write an empty record. */ void FormatPersistence_EEPROM() { - Debug_pushMessage("Formatting Persistance-Partition\n"); + Debug_pushMessage("Formatting Persistence partition\n"); PersistenceData = {0}; - // memset(&PersistenceData, 0, sizeof(PersistenceData)); StorePersistence_EEPROM(); } + /** - * @brief Moves the persistence page in EEPROM. + * @brief Advance the persistence page (wear levelling) and store the new start address. * - * This function adjusts the persistence page address and resets the write cycle counter. + * When end-of-device (or reset=true), wrap back to startofPersistence. + * Requires EEPROM availability. * - * @param reset If true, the function resets the persistence page address to the start of the partition. + * @param reset If true, force wrap to the start of the partition. */ void MovePersistencePage_EEPROM(boolean reset) { - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; - globals.eePersistanceAdress += sizeof(PersistenceData); + globals.eePersistenceAddress += sizeof(PersistenceData); PersistenceData.writeCycleCounter = 0; - // Check if we reached the end of the EEPROM and start over at the beginning - if ((globals.eePersistanceAdress + sizeof(PersistenceData)) > ee.getDeviceSize() || reset) + if ((globals.eePersistenceAddress + sizeof(PersistenceData)) > ee.getDeviceSize() || reset) { - globals.eePersistanceAdress = startofPersistence; + globals.eePersistenceAddress = startofPersistence; } - ee.updateBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress)); + ee.updateBlock(0, (uint8_t *)&globals.eePersistenceAddress, sizeof(globals.eePersistenceAddress)); } /** - * @brief Calculate CRC-32 checksum for a block of data. - * - * This function implements the CRC-32 algorithm. - * - * @param data Pointer to the data block. - * @param len Length of the data block in bytes. - * @return CRC-32 checksum. + * @brief Compute CRC-32 (poly 0xEDB88320) over a byte buffer. */ uint32_t Checksum_EEPROM(uint8_t const *data, size_t len) { @@ -275,55 +416,43 @@ uint32_t Checksum_EEPROM(uint8_t const *data, size_t len) return 0; uint32_t crc = 0xFFFFFFFF; - uint32_t mask; - while (len--) { crc ^= *data++; - for (uint8_t k = 0; k < 8; k++) - { - mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } + crc = (crc >> 1) ^ (0xEDB88320 & (-(int32_t)(crc & 1))); } - return ~crc; } /** - * @brief Dump a portion of EEPROM contents for debugging. + * @brief Print a hex/ASCII dump of a region of the EEPROM for debugging. * - * This function prints the contents of a specified portion of EEPROM in a formatted way. - * - * @param memoryAddress Starting address in EEPROM. - * @param length Number of bytes to dump. + * Output format: + * Address 00 01 02 ... 0F ASCII + * 0x00000: XX XX ... ..... */ void dumpEEPROM(uint16_t memoryAddress, uint16_t length) { #define BLOCK_TO_LENGTH 16 - if (!checkEEPROMavailable()) + if (!EEPROM_Available()) return; char ascii_buf[BLOCK_TO_LENGTH + 1]; sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII"); - // Print column headers Debug_pushMessage(PSTR("\nAddress ")); for (int x = 0; x < BLOCK_TO_LENGTH; x++) Debug_pushMessage("%3d", x); - // Align address and length to BLOCK_TO_LENGTH boundaries - memoryAddress = memoryAddress / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH; - length = (length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH; + memoryAddress = (memoryAddress / BLOCK_TO_LENGTH) * BLOCK_TO_LENGTH; + length = ((length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH) * BLOCK_TO_LENGTH; - // Iterate through the specified portion of EEPROM for (unsigned int i = 0; i < length; i++) { - int blockpoint = memoryAddress % BLOCK_TO_LENGTH; + const int blockpoint = memoryAddress % BLOCK_TO_LENGTH; - // Print ASCII representation header for each block if (blockpoint == 0) { ascii_buf[BLOCK_TO_LENGTH] = 0; @@ -331,55 +460,54 @@ void dumpEEPROM(uint16_t memoryAddress, uint16_t length) Debug_pushMessage("\n0x%05X:", memoryAddress); } - // Read and print each byte ascii_buf[blockpoint] = ee.readByte(memoryAddress); Debug_pushMessage(" %02X", ascii_buf[blockpoint]); - // Replace non-printable characters with dots in ASCII representation if (ascii_buf[blockpoint] < 0x20 || ascii_buf[blockpoint] > 0x7E) ascii_buf[blockpoint] = '.'; memoryAddress++; } - // Print a new line at the end of the dump Debug_pushMessage("\n"); } /** - * @brief Check if EEPROM is available and connected. + * @brief Unified availability probe with optional recovery. * - * This function checks if the EEPROM is available and connected. If not, it triggers - * a diagnostic trouble code (DTC) indicating the absence of EEPROM. + * Fast path returns the latched availability flag. If not available, + * performs a direct probe and, optionally, a recovery sequence. * + * @param recover If true, attempt recovery when not available (default: false). + * @param attempts Recovery attempts (default: 3). + * @param delay_ms Delay between attempts in ms (default: 25). * @return true if EEPROM is available, false otherwise. */ -boolean checkEEPROMavailable() +bool EEPROM_Available(bool recover, uint8_t attempts, uint16_t delay_ms) { - // Check if EEPROM is connected - if (!ee.isConnected()) + if (eeAvailable) + return true; + + if (ee.isConnected()) { - // Trigger DTC for no EEPROM found - MaintainDTC(DTC_NO_EEPROM_FOUND, true); - return false; + eeAvailable = true; + eeRecoveredOnce = true; // edge 0 -> 1 + return true; } - // Clear DTC for no EEPROM found since it's available now - MaintainDTC(DTC_NO_EEPROM_FOUND, false); + if (recover) + { + return TryRecoverEEPROM(attempts, delay_ms); + } - // EEPROM is available - return true; + return false; } /** - * @brief Perform sanity check on configuration settings. + * @brief Validate config fields; return bitmask of invalid entries. * - * This function checks the validity of various configuration settings and returns a bitmask - * indicating which settings need to be reset. If autocorrect is enabled, it resets the settings - * to their default values. - * - * @param autocorrect If true, automatically correct invalid settings by resetting to defaults. - * @return A bitmask indicating which settings need to be reset. + * If autocorrect is true, invalid fields are reset to default values. + * Each bit in the returned mask identifies a specific field-group that was out-of-bounds. */ uint32_t ConfigSanityCheck(bool autocorrect) { @@ -513,22 +641,17 @@ uint32_t ConfigSanityCheck(bool autocorrect) if (autocorrect) strncpy(LubeConfig.wifi_client_password, LubeConfig_defaults.wifi_client_password, sizeof(LubeConfig.wifi_client_password)); } - // Return the bitmask indicating which settings need to be reset + return setting_reset_bits; } /** - * @brief Validates whether a given string contains only characters allowed in WiFi SSIDs and passwords. + * @brief Validate that a string contains only characters allowed for Wi‑Fi SSIDs/passwords. * - * This function checks each character in the provided string to ensure - * that it contains only characters allowed in WiFi SSIDs and passwords. - * It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as - * the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + * Allowed: A‑Z, a‑z, 0‑9 and the printable ASCII punctuation: ! " # $ % & ' ( ) * + , - . / : ; + * < = > ? @ [ \ ] ^ _ ` { | } ~ * - * @param string Pointer to the string to be validated. - * @param size Size of the string including the null-terminator. - * @return true if the string contains only allowed characters or is NULL, - * false otherwise. + * @return true if valid (or empty), false otherwise. */ bool validateWiFiString(char *string, size_t size) { @@ -539,10 +662,8 @@ bool validateWiFiString(char *string, size_t size) { char c = string[i]; if (c == '\0') - { - // Reached the end of the string, all characters were valid WiFi characters. - return true; - } + return true; // reached end with valid chars + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '!' || c == '"' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '(' || @@ -552,11 +673,9 @@ bool validateWiFiString(char *string, size_t size) c == '\\' || c == ']' || c == '^' || c == '_' || c == '`' || c == '{' || c == '|' || c == '}' || c == '~')) { - // Found a character that is not a valid WiFi character. return false; } } - // If the loop completes without finding a null terminator, the string is invalid. + // No NUL within buffer: treat as invalid return false; } - diff --git a/Software/src/debugger.cpp b/Software/src/debugger.cpp index 57aa644..d8c1330 100644 --- a/Software/src/debugger.cpp +++ b/Software/src/debugger.cpp @@ -314,6 +314,8 @@ static const std::map &getCmdMap() } dumpEEPROM(start, len); }}, + {"reinitEE", [](const String &args) + { globals.requestEEAction = EE_REINITIALIZE; }}, {"resetPageEE", [](const String &) { MovePersistencePage_EEPROM(true); }}, {"dumpCFG", [](const String &) @@ -462,7 +464,7 @@ void Debug_dumpGlobals() Debug_pushMessage("requestEEAction: %d\n", globals.requestEEAction); Debug_pushMessage("DeviceName: %s\n", globals.DeviceName); Debug_pushMessage("FlashVersion: %s\n", globals.FlashVersion); - Debug_pushMessage("eePersistanceAdress: %d\n", globals.eePersistanceAdress); + Debug_pushMessage("eePersistanceAdress: %d\n", globals.eePersistenceAddress); Debug_pushMessage("TankPercentage: %d\n", globals.TankPercentage); Debug_pushMessage("hasDTC: %d\n", globals.hasDTC); } @@ -476,7 +478,7 @@ void Debug_dumpPersistance() Debug_pushMessage("tankRemain_microL: %d\n", PersistenceData.tankRemain_microL); Debug_pushMessage("TravelDistance_highRes_mm: %d\n", PersistenceData.TravelDistance_highRes_mm); Debug_pushMessage("checksum: %d\n", PersistenceData.checksum); - Debug_pushMessage("PSD Adress: 0x%04X\n", globals.eePersistanceAdress); + Debug_pushMessage("PSD Adress: 0x%04X\n", globals.eePersistenceAddress); } /** diff --git a/Software/src/webui.cpp b/Software/src/webui.cpp index f205739..9841066 100644 --- a/Software/src/webui.cpp +++ b/Software/src/webui.cpp @@ -479,7 +479,7 @@ void WebServerEEJSON_Callback(AsyncWebServerRequest *request) generateJsonObject_PersistenceData(persis); JsonObject eepart = json["eepart"].to(); - sprintf(buffer, "0x%04X", globals.eePersistanceAdress); + sprintf(buffer, "0x%04X", globals.eePersistenceAddress); eepart["PersistanceAddress"] = buffer; serializeJsonPretty(json, *response);