Project Overview
Arduino EEPROM - Save Settings That Survive Power Loss: This Arduino Uno EEPROM project shows how to store a button-controlled LED state and restore it after a reset or full power loss, then expands to a reusable settings struct pattern for real builds.
You will start with a simple on/off state saved to a single EEPROM address, then move to saving multiple configuration values with EEPROM.put() and restoring them with EEPROM.get(). You will also add a “magic number” so a first boot loads safe defaults instead of random data, and follow write-cycle rules so the EEPROM lasts.
- Time: ~30 minutes
- Skill level: Beginner
- What you will build: A persistent LED toggle, then a reusable settings-that-survive-reboots pattern with a counter, a threshold, and a calibration value.
Parts List
From ShillehTek
- Arduino Uno R3 Super Starter Kit - includes an Uno-compatible board, breadboard, and jumpers for the EEPROM examples
- Tactile Button Kit - used as the input to toggle and save state
- Resistor Kit - use a 220 ohm resistor for the external LED option
- 400-Point Breadboard - quick prototyping for the button/LED wiring
- Dupont Jumper Wires - connections between Arduino pins, button, and LED
External
- None
Note: EEPROM cells are rated for about 100,000 writes each. That is plenty for human-changed settings, and far too few for anything written in loop(). Use EEPROM.update() (writes only if the value changed) and only save when something actually changes.
Step-by-Step Guide
Step 1 - Wire a Button and an LED
Goal: Build the smallest possible test rig for saving and restoring state.
What to do: Wire the button between D10 and GND and use the internal pull-up resistor. For the LED, you can use D13 (the onboard LED), or wire an external LED for a brighter output: D8 to 220 ohm to LED to GND.
Expected result: Your hardware is ready to toggle an LED from a button press.
Step 2 - The Classic Demo: A State That Survives Reset
Goal: Confirm EEPROM persistence by restoring the last LED state after power loss.
Code:
#include <EEPROM.h>
const int BTN = 10, LED = 13, ADDR = 0;
bool state;
void setup() {
pinMode(BTN, INPUT_PULLUP); pinMode(LED, OUTPUT);
state = EEPROM.read(ADDR) == 1; // restore last state (blank EEPROM reads 255 = off)
digitalWrite(LED, state);
}
void loop() {
if (digitalRead(BTN) == LOW) {
state = !state;
digitalWrite(LED, state);
EEPROM.update(ADDR, state ? 1 : 0); // save only if it changed
delay(300); // crude debounce
}
}
What to do: Upload the sketch, press the button to turn the LED on, then unplug the board and plug it back in.
Expected result: The LED comes back on by itself. Turn it off, power-cycle, and it stays off.
Step 3 - The Real Pattern: A Settings Struct
Goal: Store and restore multiple settings safely in one operation.
What to do: Put related settings in a struct and save the whole struct using EEPROM.put(). Add a “magic number” so the code can detect a first boot (or corrupted data) and load defaults instead of using garbage values.
Code:
#include <EEPROM.h>
struct Settings {
uint16_t magic; // 0xCAFE means "these are valid"
uint16_t boots; // how many times the board has started
int threshold; // e.g. an alarm level
float calOffset; // e.g. a sensor correction
bool ledOn;
};
const uint16_t MAGIC = 0xCAFE;
const int ADDR = 0;
Settings cfg;
void save() { EEPROM.put(ADDR, cfg); } // writes only bytes that changed
void load() {
EEPROM.get(ADDR, cfg);
if (cfg.magic != MAGIC) { // first boot (or corrupted): defaults
cfg = { MAGIC, 0, 500, 0.0, false };
save();
}
}
void setup() {
Serial.begin(9600);
load();
cfg.boots++; // count this start-up
save();
Serial.print("boot #"); Serial.println(cfg.boots);
Serial.print("threshold "); Serial.println(cfg.threshold);
Serial.print("calOffset "); Serial.println(cfg.calOffset, 3);
}
void loop() {
// change a setting from the Serial Monitor: type t=700 or c=1.25
if (Serial.available()) {
String s = Serial.readStringUntil('\n'); s.trim();
if (s.startsWith("t=")) cfg.threshold = s.substring(2).toInt();
if (s.startsWith("c=")) cfg.calOffset = s.substring(2).toFloat();
save();
Serial.println("saved");
}
}
What to do: Upload, open the Serial Monitor (newline line ending), type t=700, then reset the board.
Expected result: “boot #” climbs with every reset, and “threshold 700” comes back after the reset. The struct survived.
Step 4 - Rules for Not Wearing It Out
Goal: Keep the EEPROM healthy for long-term projects.
What to do: Do not put a save in loop() without a “did it change?” check. For values that change often (a counter or a log), spread writes across addresses (wear leveling), or save only on a button press or on a schedule (for example, once per day). If you update the struct layout later, change MAGIC so older data is discarded cleanly. Also remember that a blank EEPROM reads 255 for each byte, so never assume unused memory contains zero.
Expected result: Persistent settings that keep working for the life of the project.
Step 5 - On the ESP32 and Beyond
Goal: Apply the persistence idea correctly on other microcontrollers.
What to do: ESP32 and ESP8266 do not have true EEPROM. The EEPROM library emulates it in flash, so you must call EEPROM.begin(size) and EEPROM.commit(). On ESP32, the better approach is the Preferences library for key/value storage with wear leveling built in (for example, prefs.putInt("threshold", 700)). If you need to store thousands of records, use an SD card or an external I2C EEPROM chip such as the AT24C32 found on DS3231 RTC modules.
Expected result: You can pick the right persistence tool for each board family.
Conclusion
In this Arduino EEPROM tutorial, you built an LED state that survives power loss and a reusable settings struct pattern using EEPROM.put(), EEPROM.get(), and a magic number to handle first boot defaults safely.
Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.
Photo credit: Images referenced from galoebn on Hackster.io.







