Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

Day 21 of 30 · about 32 minutes

Saving settings in flash with Preferences

Today you build: A blink speed and boot counter that survive a power cut

  • Read 5 min
  • Upload & test 7 min
  • Try it 20 min

Community draft — report a mistake

Everything in a variable vanishes when the power goes — and in India, the power goes. A tank alarm’s thresholds, a light’s schedule, a calibration value: they need to live in flash memory.

Preferences: key–value storage

The ESP32 core includes Preferences (no install). Think of it as a cupboard with named drawers:

prefs.begin("lesson21", false);          // open the "lesson21" drawer, read/write
uint32_t ms = prefs.getUInt("blink", 500); // read "blink", or 500 if it was never saved
prefs.putUInt("blink", ms);               // save it

There are matching get/put pairs for Int, UInt, Float, Bool, String and more. The drawer name and keys can be up to 15 characters.

Use it

Upload, open the Serial Monitor at 115200 with line ending set to Newline, then:

  1. Type speed 150 and press Enter — the LED blinks fast.
  2. Press the EN button (reset) or unplug the board. It comes back at 150 ms, and the boot number has gone up.
  3. Type reset to clear the drawer.

Don’t wear out the flash

Flash cells survive a large but limited number of erase cycles (Espressif’s datasheets quote around 100,000). The library spreads writes around, but the rule still applies:

  • Write when a value changes, not every time loop() runs.
  • A boot counter (one write per boot) is fine. Saving a sensor reading every 100 ms for months is not.

The sketch only calls putUInt("blink", …) when you type a new speed.

Preferences vs EEPROM

On the Uno you’d use EEPROM.h, and the ESP32 core still has an EEPROM library (it’s emulated in flash and needs commit()). FlashReady’s own settings helper, fr_settings.h, uses it for Wi-Fi details. For your own values, Preferences is simpler: named keys instead of byte addresses, and no commit to forget.

Try it

  • Save the last LED state from day 19’s web server, so the light comes back the way it was after a power cut.
  • Store a min/max temperature (day 15) and keep it across restarts.

You’re done when…

  • At start-up the Serial Monitor (115200, Newline) shows Boot number … Blink speed 500 ms (from flash).
  • speed 150 prints Saved: 150 ms and the LED blinks fast.
  • After pressing EN, and again after unplugging for 10 s, it comes back at 150 ms with a higher boot number.
  • reset prints Cleared. Back to 500 ms….

Today's code

Verified — core esp32 3.3.12, 3 Nov 2026Compile-only
lesson-preferences.inoArduino C++Open in PlaygroundOpen in Playground
// Day 21 — Saving settings in flash with Preferences (ESP32 DevKit V1)
// No wiring (blue LED on GPIO 2). In the Serial Monitor (115200 baud, "Newline") type:
//   speed 200   → blink every 200 ms, and remember it after a reset or power cut
//   reset       → forget the saved speed and the boot count
// Press EN (reset) or unplug the board: the speed and boot count survive.
#include <Preferences.h>

const int LED_PIN = 2;
Preferences prefs;
uint32_t blinkMs = 500;
unsigned long lastBlink = 0;
bool ledOn = false;

void setup() {
  Serial.begin(115200);
  delay(500);
  pinMode(LED_PIN, OUTPUT);

  prefs.begin("lesson21", false);              // a named "drawer" in flash; false = read/write
  uint32_t boots = prefs.getUInt("boots", 0) + 1;
  prefs.putUInt("boots", boots);               // one write per boot: fine for flash wear
  blinkMs = prefs.getUInt("blink", 500);       // 500 if nothing was saved yet

  Serial.printf("\nBoot number %lu. Blink speed %lu ms (from flash).\n", (unsigned long)boots, (unsigned long)blinkMs);
  Serial.println("Type: speed <50..5000>  or  reset");
}

void handleCommand(String line) {
  line.trim();
  if (line.startsWith("speed ")) {
    long ms = line.substring(6).toInt();
    if (ms < 50 || ms > 5000) {
      Serial.println("Pick 50..5000 ms");
      return;
    }
    blinkMs = ms;
    prefs.putUInt("blink", blinkMs);           // saved only when it changes, never in a fast loop
    Serial.printf("Saved: %lu ms\n", (unsigned long)blinkMs);
  } else if (line == "reset") {
    prefs.clear();                             // empties this namespace only
    blinkMs = 500;
    Serial.println("Cleared. Back to 500 ms; boot count restarts next boot.");
  } else if (line.length()) {
    Serial.println("Unknown command");
  }
}

void loop() {
  if (Serial.available()) handleCommand(Serial.readStringUntil('\n'));

  if (millis() - lastBlink >= blinkMs) {
    lastBlink = millis();
    ledOn = !ledOn;
    digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
  }
}

↑↓ to move · Enter to open