From b189972a2eced796754739b10b2d02797149c86f Mon Sep 17 00:00:00 2001 From: Kirill Berezin Date: Tue, 14 Nov 2023 16:58:37 +0000 Subject: [PATCH] Paged EEPROM writing --- examples/P1P2Monitor/P1P2Config.h | 13 ++++-- examples/P1P2Monitor/P1P2Monitor.ino | 65 +++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/examples/P1P2Monitor/P1P2Config.h b/examples/P1P2Monitor/P1P2Config.h index b006348..69814d1 100644 --- a/examples/P1P2Monitor/P1P2Config.h +++ b/examples/P1P2Monitor/P1P2Config.h @@ -113,16 +113,23 @@ // -verbose (verbosity level) // // to reset EEPROM to settings in P1P2Config.h, either erase EEPROM, or change EEPROM_SIGNATURE in P1P2Config.h -#define EEPROM_SIGNATURE "P1P2SIG01" // change this every time you wish to re-init EEPROM settings to the settings in P1P2Config.h +#define EEPROM_SIGNATURE "P1P2SIG02" // change this every time you wish to re-init EEPROM settings to the settings in P1P2Config.h #define EEPROM_ADDRESS_CONTROL_ID 0x00 // 1 byte for CONTROL_ID (Daikin-specific) #define EEPROM_ADDRESS_COUNTER_STATUS 0x01 // 1 byte for counterrepeatingrequest (Daikin-specific) #define EEPROM_ADDRESS_VERBOSITY 0x02 // 1 byte for verbose // 0x03 .. 0x0F reserved #define EEPROM_ADDRESS_SIGNATURE 0x10 // should be highest, in view of unspecified strlen(EEPROM_SIGNATURE) +#define EEPROM_MAX_SIZE 1024 // max size of EEPROM (1kB for ATmega328P) +#define EEPROM_ACTIVE_PAGE_HEADER 0x01 // 1 byte for byte selection of active page +#define EEPROM_PAGE_HEADER 0x01 // 1 byte for write count +#define EEPROM_PAGE_SIZE EEPROM_BLOCK_HEADER + EEPROM_ADDRESS_SIGNATURE // Single addressable page ID +#define EEPROM_MAX_PAGE_INDEX ((EEPROM_MAX_SIZE - strlen(EEPROM_SIGNATURE) - EEPROM_ACTIVE_PAGE_HEADER) / EEPROM_PAGE_SIZE) - 1 // Max adressable page index +#define EEPROM_CYCLE_EVERY_WRITES 1000 // EEPROM write cycle every 1000 writes +#define EEPROM_WRITE_DECIMATION_BITS 0x4 // Update page write stats every 16 writes #ifdef EF_SERIES // Write budget: thottle parameter writes to limit flash memory wear -#define TIME_WRITE_PERMISSION 3600 // on avg max one write per 3600s allowed +#define TIME_WRITE_PERMISSION 3600/60 // on avg max one write per 60s allowed #define MAX_WRITE_PERMISSION 100 // budget never higher than 100 (don't allow to burn more than 100 writes at once) // 8-bit, so max 254; 255 is "infinity" (i.e. no budget limit) #define INIT_WRITE_PERMISSION 10 // initial write budget upon boot (255 = unlimited; recommended: 10) #define WR_CNT 1 // number of write repetitions for writing a paramter. 1 should work reliably, no real need for higher value @@ -130,7 +137,7 @@ // Error budget: P1P2Monitor should not see any errors except upon start falling into a packet // so if P1P2Monitor sees see too many errors, it stops writing // -#define TIME_ERRORS_PERMITTED 3600 // on avg max one error per 3600s allowed (otherwise writing will be switched off) +#define TIME_ERRORS_PERMITTED 3600/60 // on avg max one error per 60s allowed (otherwise writing will be switched off) #define MAX_ERRORS_PERMITTED 20 // budget never higher than this (don't allow too many errors before we switch writing off) // 8-bit, so max 255 #define INIT_ERRORS_PERMITTED 10 // initial error budget upon boot #define MIN_ERRORS_PERMITTED 5 // don't start control unless error budget is at least this value diff --git a/examples/P1P2Monitor/P1P2Monitor.ino b/examples/P1P2Monitor/P1P2Monitor.ino index 8a01e66..8291672 100644 --- a/examples/P1P2Monitor/P1P2Monitor.ino +++ b/examples/P1P2Monitor/P1P2Monitor.ino @@ -147,7 +147,51 @@ const byte Compile_Options = 0 // multi-line statement #ifdef EEPROM_SUPPORT #include -void initEEPROM() { + +static byte signatureOffsetEeprom = 0; +static byte activePageId = 0; +static byte activePageWrites = 0; + +void loadPagedMemoryState() { + activePageId = EEPROM.read(signatureOffsetEeprom); + activePageWrites = readPagedEEPROM(-EEPROM_PAGE_HEADER) << 4; +} + +void promoteNewPage() { + uint8_t oldPageId = activePageId; + activePageId = (activePageId + 1) % EEPROM_PAGE_COUNT; + activePageWrites = 1; + // Copy content of current page to new one + for (uint8_t i = 0; i < EEPROM_PAGE_SIZE; i++) { + uint8_t value = EEPROM.read(signatureOffsetEeprom + EEPROM_PAGE_SIZE * oldPageId + i); + EEPROM.update(signatureOffsetEeprom + EEPROM_PAGE_SIZE * activePageId + i, value); + } + // Reset active page writes counter + EEPROM.update(signatureOffsetEeprom + EEPROM_PAGE_SIZE * activePageId, 0); + // Promition finished, update main header + EEPROM.update(signatureOffsetEeprom, activePageId); +} + + +void updatePagedEEPROM(int8_t x, y) { + uint16_t pageBegin = signatureOffsetEeprom + EEPROM_PAGE_SIZE * activePageId; + activePageWrites += 1; + uint8_t decimationMask = (1 << EEPROM_WRITE_DECIMATION_BITS) - 1; + if ((activePageWrites & decimationMask) == 0) { + // Store page writes divided by 16 to reduced stress, its good enough + EEPROM.update(pageBegin, activePageWrites >> EEPROM_WRITE_DECIMATION_BITS) + } + + if (activePageWrites > EEPROM_CYCLE_EVERY_WRITES) promoteNewPage(); + EEPROM.update(addr + EEPROM_PAGE_HEADER, y); +} + +uint8_t readPagedEEPROM(int8_t x) { + uint16_t pageBegin = signatureOffsetEeprom + EEPROM_PAGE_SIZE * activePageId + EEPROM_PAGE_HEADER; + return EEPROM.read(pageBegin + x); +} + +bool verifyEEPROMSignature() { if (verbose) Serial.println(F("* checking EEPROM")); bool sigMatch = 1; for (uint8_t i = 0; i < strlen(EEPROM_SIGNATURE); i++) sigMatch &= (EEPROM.read(EEPROM_ADDRESS_SIGNATURE + i) == EEPROM_SIGNATURE[i]); @@ -155,14 +199,25 @@ void initEEPROM() { Serial.print(F("* EEPROM sig match")); Serial.println(sigMatch); } - if (!sigMatch) { + signatureOffsetEeprom = strlen(EEPROM_SIGNATURE); + return sigMatch; +} + +void initEEPROM() { + bool sigMatch = verifyEEPROMSignature(); + if (sigMatch) { + loadPagedMemoryState(); + } else { if (verbose) Serial.println(F("* EEPROM sig mismatch, initializing EEPROM")); for (uint8_t i = 0; i < strlen(EEPROM_SIGNATURE); i++) EEPROM.update(EEPROM_ADDRESS_SIGNATURE + i, EEPROM_SIGNATURE[i]); // no '\0', not needed - EEPROM.update(EEPROM_ADDRESS_CONTROL_ID, CONTROL_ID_DEFAULT); // Daikin specific - EEPROM.update(EEPROM_ADDRESS_COUNTER_STATUS, COUNTERREPEATINGREQUEST); // Daikin specific - EEPROM.update(EEPROM_ADDRESS_VERBOSITY, INIT_VERBOSE); + updatePagedEEPROM(EEPROM_ADDRESS_CONTROL_ID, CONTROL_ID_DEFAULT); // Daikin specific + updatePagedEEPROM(EEPROM_ADDRESS_COUNTER_STATUS, COUNTERREPEATINGREQUEST); // Daikin specific + updatePagedEEPROM(EEPROM_ADDRESS_VERBOSITY, INIT_VERBOSE); + promoteNewPage(); } } + + #define EEPROM_update(x, y) { EEPROM.update(x, y); }; #else /* EEPROM_SUPPORT */ #define EEPROM_update(x, y) {}; // dummy function to avoid cluttering code with #ifdef EEPROM_SUPPORT