Skip to content

Commit

Permalink
Paged EEPROM writing
Browse files Browse the repository at this point in the history
  • Loading branch information
Enelar committed Nov 14, 2023
1 parent e8b5880 commit b189972
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 8 deletions.
13 changes: 10 additions & 3 deletions examples/P1P2Monitor/P1P2Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,31 @@
// -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

// 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
Expand Down
65 changes: 60 additions & 5 deletions examples/P1P2Monitor/P1P2Monitor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,77 @@ const byte Compile_Options = 0 // multi-line statement

#ifdef EEPROM_SUPPORT
#include <EEPROM.h>
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]);
if (verbose) {
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
Expand Down

0 comments on commit b189972

Please sign in to comment.