#include <EEPROM.h>
/**
@class EEPlus32Class
@brief High-endurance EEPROM storage for a 32-bit value (uint32_t) with Wear Leveling and CRC.
DESIGN PRINCIPLES:
1. Wear Leveling: Rotates writes across 170 slots (6 bytes each) in the 1024-byte EEPROM.
This extends the lifespan from 100k to ~17 million write cycles.
2. Integrity: Uses an 8-bit CRC to validate data. If a write is interrupted by power loss,
the system identifies the corruption and falls back to the last valid entry.
3. Automatic Recovery: On begin(), the class scans all slots to find the newest valid sequence.
APPLICATION:
- storage.begin(defaultVal): Initializes and finds the latest entry.
- storage.write(val): Moves to the next slot and stores the 32-bit value.
- storage.read(): Returns the current 32-bit value.
*/
static const uint32_t MAGIC_NUMBER = 0xBEEFCAFE;
static const int MAGIC_ADDR = 0;
static const int EEPROM_SIZE = 1024;
static const int SLOT_SIZE = 6; // [1B Seq | 4B Value | 1B CRC]
static const int START_ADDR = sizeof(uint32_t);
static const int MAX_SLOTS = (EEPROM_SIZE - START_ADDR) / SLOT_SIZE;
class EEPlus32Class {
private:
int _currentSlot = 0;
uint8_t _lastSeq = 0;
struct Slot {
uint8_t seq;
uint32_t value;
uint8_t crc;
};
// CRC-8 calculation for sequence and 32-bit value
uint8_t calculateCRC(uint8_t seq, uint32_t val) {
uint8_t crc = seq;
for (int i = 0; i < 4; i++) {
crc ^= (uint8_t)((val >> (i * 8)) & 0xFF);
}
return crc;
}
public:
/**
@brief Starts the storage system. Formats if Magic Number is missing.
*/
void begin(uint32_t defaultValue = 0) {
uint32_t storedMagic;
EEPROM.get(MAGIC_ADDR, storedMagic);
if (storedMagic != MAGIC_NUMBER) {
EEPROM.put(MAGIC_ADDR, MAGIC_NUMBER);
_currentSlot = -1;
_lastSeq = 0;
write(defaultValue);
} else {
uint8_t maxSeq = 0;
int bestSlot = 0;
bool firstValidFound = false;
for (int i = 0; i < MAX_SLOTS; i++) {
Slot s;
EEPROM.get(START_ADDR + (i * SLOT_SIZE), s);
if (s.crc == calculateCRC(s.seq, s.value)) {
if (!firstValidFound || (uint8_t)(s.seq - maxSeq) < 128) {
maxSeq = s.seq;
bestSlot = i;
firstValidFound = true;
}
}
}
_currentSlot = bestSlot;
_lastSeq = maxSeq;
}
}
/**
@brief Writes a 32-bit value to the next wear-leveling slot.
*/
void write(uint32_t newValue) {
if (read() == newValue) return;
_currentSlot = (_currentSlot + 1) % MAX_SLOTS;
_lastSeq++;
Slot s;
s.seq = _lastSeq;
s.value = newValue;
s.crc = calculateCRC(s.seq, s.value);
EEPROM.put(START_ADDR + (_currentSlot * SLOT_SIZE), s);
}
/**
@brief Retrieves the latest valid 32-bit value.
*/
uint32_t read() {
uint32_t val;
// Read directly from the current slot's value offset
EEPROM.get(START_ADDR + (_currentSlot * SLOT_SIZE) + offsetof(Slot, value), val);
return val;
}
};
EEPlus32Class myEE;
void setup() {
Serial.begin(115200);
Serial.println("Start");
myEE.begin(0);
myEE.write(12345);
Serial.println(myEE.read());
}
void loop() {
// put your main code here, to run repeatedly:
}