One of the most misunderstood parts of Arduino projects is EEPROM (Electrically Erasable Programmable Read-Only Memory).
Beginners often either ignore it or use it wrong by writing too often, which wears out the memory too soon.
EEPROM is not a substitute for SRAM or Flash. It has one very specific job: to keep small amounts of data safe even when the power goes out or the system is reset.
This tutorial explains:
- What EEPROM is and how it differs from other memory
- When EEPROM should and should not be used
- Real-world EEPROM storage use cases
- Safe write strategies
- Many practical code examples
- Common mistakes and best practices
What EEPROM Is and Is Not
EEPROM Characteristics
- Non-volatile (data survives power loss)
- Byte-addressable
- Limited write endurance (~100,000 writes per cell)
- Slower than SRAM
- Small capacity (typically 512–4096 bytes depending on board)
EEPROM Is Best For
- Configuration settings
- Calibration values
- User preferences
- Counters and flags
- Last-known system state
EEPROM Is NOT For
- Frequently changing sensor data
- Logging large datasets
- Temporary variables
- High-speed writes
EEPROM vs SRAM vs Flash (Quick Reminder)
| Memory | Volatile | Speed | Size | Typical Use |
|---|---|---|---|---|
| SRAM | Yes | Fast | Very small | Variables |
| Flash | No | Fast | Large | Code, constants |
| EEPROM | No | Slow | Small | Persistent data |
Basic EEPROM Access
Including the EEPROM Library
#include <EEPROM.h>
Reading a Byte
byte value = EEPROM.read(0);
Writing a Byte
EEPROM.write(0, 42);
This always writes, even if the value didn’t change.
Safer Alternative: update()
EEPROM.update(0, 42);
update() only writes if the stored value is different, preserving EEPROM lifespan.
Use Case 1: Saving User Settings
Example: Storing a Brightness Setting
const int EEPROM_ADDR = 0;
int brightness = 128;
void setup() {
EEPROM.get(EEPROM_ADDR, brightness);
analogWrite(9, brightness);
}
void loop() {
// imagine brightness changes via buttons
EEPROM.update(EEPROM_ADDR, brightness);
}
Why EEPROM Works Here
- Changes infrequently
- Must persist across power cycles
- Small data size
Use Case 2: Device Calibration Data
Calibration is a perfect EEPROM use case.
Example: Sensor Calibration Offset
struct CalibrationData {
float offset;
float scale;
};
CalibrationData calib;
void setup() {
EEPROM.get(0, calib);
if (isnan(calib.offset)) {
calib.offset = 0.0;
calib.scale = 1.0;
EEPROM.put(0, calib);
}
}
EEPROM ensures calibration is retained even after firmware updates.
Use Case 3: Remembering Last System State
Example: Restore Last Mode on Startup
enum Mode { MODE_IDLE, MODE_RUN, MODE_ERROR };
Mode lastMode;
void setup() {
EEPROM.get(0, lastMode);
if (lastMode > MODE_ERROR) {
lastMode = MODE_IDLE;
}
}
void loop() {
// when mode changes
EEPROM.update(0, lastMode);
}
This allows systems to resume gracefully after power failure.
Use Case 4: Persistent Counters
Example: Power-On Counter
unsigned long bootCount;
void setup() {
EEPROM.get(0, bootCount);
bootCount++;
EEPROM.put(0, bootCount);
}
Caution
Writing on every boot is acceptable. Writing every loop is not.
Use Case 5: Feature Flags and Unlocks
Example: Feature Enabled Flag
bool featureEnabled;
void setup() {
EEPROM.get(0, featureEnabled);
if (!featureEnabled) {
// limited mode
}
}
This is commonly used in:
- Trial features
- Configuration locks
- Setup-complete flags
Use Case 6: Storing Small Data Structures
Example: Settings Struct
struct Settings {
int threshold;
byte mode;
bool enabled;
};
Settings settings;
void setup() {
EEPROM.get(0, settings);
if (settings.threshold == 0xFFFF) {
settings.threshold = 500;
settings.mode = 1;
settings.enabled = true;
EEPROM.put(0, settings);
}
}
EEPROM.put() automatically handles multi-byte writes safely.
Use Case 7: Menu-Based Configuration Systems
EEPROM is ideal for menu-driven devices.
Example: Save Menu Selection
byte menuSelection;
void setup() {
EEPROM.get(10, menuSelection);
}
void loop() {
// user changes menu
EEPROM.update(10, menuSelection);
}
Use Case 8: Storing Error Codes
Example: Last Error Code
byte lastError;
void logError(byte code) {
EEPROM.update(20, code);
}
On restart, diagnostics can report the last failure.
Managing EEPROM Wear (Very Important)
EEPROM Write Limits
- Typical endurance: ~100,000 writes per address
- Writing every second = worn out in ~28 hours
- Writing every minute = years of lifespan
Strategy 1: Write Only When Necessary
if (newValue != oldValue) {
EEPROM.update(addr, newValue);
}
Strategy 2: Delayed Writes
unsigned long lastSave = 0;
if (millis() - lastSave > 5000) {
EEPROM.put(addr, settings);
lastSave = millis();
}
Strategy 3: Wear Leveling (Advanced)
Rotate storage locations.
int baseAddr = (counter % 10) * sizeof(Settings);
EEPROM.put(baseAddr, settings);
This spreads wear across cells.
Common EEPROM Mistakes
Mistake 1: Writing Inside loop()
void loop() {
EEPROM.write(0, sensorValue); // dangerous
}
Fix: write only on change or on events.
Mistake 2: Using EEPROM Like RAM
EEPROM is slow and wears out. Treat it like permanent storage, not workspace.
Mistake 3: Not Validating Stored Data
Always validate loaded EEPROM values.
if (value < MIN || value > MAX) {
value = DEFAULT;
}
EEPROM Capacity Awareness
Example on Arduino Uno:
- EEPROM size: 1024 bytes
- Address range:
0–1023
Always ensure data fits.
if (sizeof(Settings) > EEPROM.length()) {
// error
}
EEPROM Alternatives
Sometimes EEPROM is not the best solution:
- Flash: large constants
- External EEPROM: more space
- FRAM: unlimited writes
- SD card: logs and datasets
EEPROM is best for small, critical data.
Practical Design Checklist
Before using EEPROM, ask:
- Does the data need to persist?
- Does it change infrequently?
- Is the size small?
- Can I tolerate limited write cycles?
If yes → EEPROM is appropriate.
Final Thoughts
When used correctly, EEPROM is one of the most useful tools for Arduino projects. It lets devices act like real products by remembering settings, recovering from power loss, and keeping their calibration.
If you use it wrong, it can make your memory wear out and cause strange behaviour.
When used correctly, it greatly improves reliability and the user experience.

