EEPROM.update() compatibility boundary between AVR and ESP32 cores
0 reputation · 07 May 2020, 04:50 UTC
0 reputation · 07 May 2020, 04:50 UTC
I am designing a retry-safe configuration store for a sketch that must run on both AVR boards and ESP32 cores. The goal is to use EEPROM.update() so that repeated attempts with identical values do not consume additional write cycles, while still handling power-loss interruption for multi-byte records. On AVR cores, update() programs the cell immediately when the value differs, but on ESP32 cores the library emulates EEPROM in RAM and requires EEPROM.commit() to persist to flash. Because update() returns void, a skipped write and a failed write are indistinguishable at the API level.
The unresolved decision is how to structure a retry loop that preserves the no-op behavior of update() on AVR while ensuring the same data survives a reset on ESP32. I also need to decide whether to add an application-level integrity check for multi-byte records, since the library provides no checksum or journal, and whether to implement a ring-buffer layout to avoid concentrating writes on fixed addresses.
Which persistence boundary should the retry logic treat as authoritative: the immediate AVR write or the deferred ESP32 commit? Is there a documented core-agnostic pattern that keeps update() idempotent without silently dropping data on ESP32? For multi-byte structures, what is the minimal integrity framing the application must add to detect torn writes after a power loss?
33775 reputation · 07 May 2020, 11:23 UTC
To maintain compatibility, your retry logic must treat the platform-specific flush as the authoritative boundary. On AVR, the boundary is the completion of EEPROM.update(). On ESP32, the boundary is the completion of EEPROM.commit().
Because EEPROM.update() returns void on both architectures, you cannot rely on the API to signal whether a write was skipped or performed. To ensure idempotency and prevent unnecessary flash wear on ESP32, you must implement change detection at the application level.
The most reliable pattern is to maintain a RAM-based shadow of your configuration and gate the persistence call with a dirty flag. This prevents EEPROM.commit() from triggering a flash sector erase/write cycle if no values actually changed.
EEPROM.update() and set a isDirty boolean to true.persistConfig()) that executes EEPROM.commit() only if isDirty is true and the target architecture is ESP32.void persistConfig() {
#ifdef ARDUINO_ARCH_ESP32
if (isDirty) {
EEPROM.commit();
isDirty = false;
}
#endif
// AVR writes immediately; no action needed here
}
Since neither core provides journaling or atomic multi-byte writes, a power loss during a write sequence will result in a "torn write." To detect and recover from this, avoid using a single fixed address. Instead, implement a Dual-Slot (A/B) Layout.
Each record should be framed with the following minimal header:
Recovery Logic: On boot, calculate the CRC for both Slot A and Slot B. If both are valid, select the one with the higher generation counter. If only one is valid, use that one. If neither is valid, revert to factory defaults.
EEPROM.put() with raw C structs, as padding and alignment differ between AVR (8-bit) and ESP32 (32-bit). Serialize data into a uint8_t array.commit() often rewrites an entire flash sector.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.