Persisting Configuration on Arduino: Using EEPROM.put/get for Reliable Data Storage
Use Arduino’s EEPROM library to store configuration structs that survive resets. Learn how to write, read, and validate data, handle wear‑leveling, and avoid common pitfalls.
07 Sept 2026, 06:05 UTC

Why Persist Configuration?
When an Arduino sketch resets—whether from a power cycle, watchdog reset, or a manual reboot—it starts from its initial state. If the sketch needs to remember user‑defined parameters (e.g., a device ID, calibration constants, or feature flags), those values must be stored in non‑volatile memory. The built‑in <EEPROM.h> library gives you a simple, type‑safe way to write and read data that survives resets.
Choosing the Right API Calls
The library offers three low‑level functions:
EEPROM.write(address, value)– writes a single byteEEPROM.read(address)– reads a single byteEEPROM.update(address, value)– writes a byte only if it differs from the existing value
For structured data, EEPROM.put(address, value) and EEPROM.get(address, value) wrap memcpy so you can store whole structs or arrays in one call. Internally they use write for each byte, so update is not used automatically.
Why update Matters on AVR
AVR EEPROM cells can be written about 100,000 times before they start to fail. If you write the same value repeatedly—e.g., in a loop that runs on every loop() call—you’ll exhaust the memory quickly. EEPROM.update mitigates this by checking the current byte and only writing when a change occurs, effectively reducing wear by up to 50% in many scenarios.
Flash‑Based EEPROM on SAMD Boards
SAMD‑based Arduinos (Zero, MKR, Nano 33) emulate EEPROM in flash memory. The API is identical, but the underlying storage has different characteristics: flash pages are 256 bytes, and each page can be erased and rewritten about 10,000–100,000 times. When writing large blocks, be careful not to overlap the bootloader or other reserved sections.
Defining a Configuration Structure
Below is a minimal example that stores a 16‑bit ID and an 8‑bit flag set. The struct is deliberately small to keep the EEPROM footprint tiny.
#include <EEPROM.h>
struct Config {
uint16_t deviceId; // 2 bytes
uint8_t flags; // 1 byte
uint8_t checksum; // 1 byte (simple XOR checksum)
};
// Packed layout to avoid compiler‑added padding
#pragma pack(push, 1)
struct Config config = {0x1234, 0x00, 0x00};
#pragma pack(pop)
const uint16_t EEPROM_ADDR = 0; // start at address 0
void setup() {
Serial.begin(115200);
while (!Serial) ; // wait for serial
loadConfig();
Serial.print("Loaded config: ID=0x");
Serial.print(config.deviceId, HEX);
Serial.print(" flags=0x");
Serial.println(config.flags, HEX);
}
void loop() {
// Example: toggle the first flag bit every reset
config.flags ^= 0x01;
config.checksum = computeChecksum(config);
EEPROM.put(EEPROM_ADDR, config);
Serial.println("Config written.");
delay(5000); // wait before next write
}
uint8_t computeChecksum(const Config &c) {
return c.deviceId ^ c.flags;
}
void loadConfig() {
Config temp;
EEPROM.get(EEPROM_ADDR, temp);
if (temp.checksum != computeChecksum(temp)) {
Serial.println("EEPROM data corrupted – resetting to defaults.");
config.deviceId = 0x1234;
config.flags = 0x00;
config.checksum = computeChecksum(config);
EEPROM.put(EEPROM_ADDR, config);
} else {
config = temp;
}
}
Verification Workflow
- Upload the sketch and open the Serial Monitor.
- Observe the printed
deviceIdandflagsvalues. - Reset the board (power cycle or
Ctrl‑R). - Confirm that the values printed after reset match the values before the reset.
- Optional: run the sketch for several minutes, then power‑cycle to ensure that repeated writes with
update(if you replaceEEPROM.putwith a manual byte‑wiseEEPROM.updateloop) do not alter the checksum.
Trade‑Offs and Limitations
- Finite Write Endurance – Even with
update, heavy write patterns can wear out the EEPROM. Keep write operations to a minimum and consider using auint32_twrite counter to monitor usage. - Data Corruption on Power Loss – A sudden power loss during a multi‑byte write can leave the EEPROM in an inconsistent state. The checksum field in the example detects this; for critical data, add a two‑byte CRC or a version number.
- Alignment and Padding – On some compilers, structs may have padding bytes. Use
#pragma pack(1)or__attribute__((packed))and verify the size withsizeof(Config)to ensure the layout matches your expectations. - SAMD Flash Emulation – Writing to flash is slower and may trigger a brief lockout of the CPU. Also, flash pages are larger than EEPROM cells; writing a large struct may inadvertently touch the bootloader area. Always check the board’s datasheet for the reserved flash region.
- Concurrent Access – The library is not thread‑safe. If you access EEPROM from an ISR or a task scheduler, guard the calls with
cli()/sei()or a mutex.
Actionable Take‑Away
To reliably persist configuration on an Arduino:
- Define a packed struct that contains all needed settings plus a simple checksum or CRC.
- Use
EEPROM.put/getfor simplicity, orEEPROM.updatefor byte‑wise writes if you anticipate frequent changes. - Validate data on boot with a checksum; if it fails, reset to defaults and rewrite.
- Keep an eye on write counts if you’re writing in a loop; consider using a write counter stored in EEPROM to track wear.
- For SAMD boards, double‑check that your offsets do not overlap the bootloader or other reserved flash areas.
With these practices, your Arduino sketch can maintain user‑defined settings across resets without risking premature EEPROM failure or data corruption.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.