Chapter 4: ROM and Flash Storage

Arduino EEPROM

AVR microcontrollers include a small EEPROM (Electrically Erasable Programmable Read-Only Memory) for non-volatile storage. On the ATmega328P it is 1 KB.

Using EEPROM.h

#include <EEPROM.h>

const int ADDR = 0;

void writeValue(int val) {
    EEPROM.put(ADDR, val);  // writes any type
}

int readValue() {
    int val;
    EEPROM.get(ADDR, val);
    return val;
}

EEPROM.update() writes a byte only if the value has changed, extending the lifespan (rated ~100,000 write cycles per byte).

Wear Leveling

For frequently updated values (counters, sensor calibration), write to different addresses in rotation rather than always writing to address 0. This spreads wear across the EEPROM array.


PROGMEM for Read-Only Data

See Chapter 3 for PROGMEM details. The key point: any constant array (lookup tables, font data, menu strings) that will never change at runtime belongs in Flash, not SRAM.


ESP32 Flash Architecture

The ESP32 uses an external SPI Flash chip (typically 4 MB). Its layout is defined by a partition table.

Default Partition Table

# Name,   Type, SubType,  Offset,   Size
nvs,      data, nvs,      0x9000,   0x5000
otadata,  data, ota,      0xe000,   0x2000
app0,     app,  ota_0,    0x10000,  0x140000
app1,     app,  ota_1,    0x150000, 0x140000
spiffs,   data, spiffs,   0x290000, 0x170000

Custom partition tables can be defined in a CSV file and selected in Arduino-ESP32 board settings.

NVS — Non-Volatile Storage

NVS is a key-value store designed for small configuration data. It handles wear leveling and power-loss safety internally.

#include <Preferences.h>

Preferences prefs;

void saveConfig(const char* ssid, int interval) {
    prefs.begin("myapp", false);  // namespace, read-write
    prefs.putString("ssid", ssid);
    prefs.putInt("interval", interval);
    prefs.end();
}

void loadConfig() {
    prefs.begin("myapp", true);   // read-only
    String ssid = prefs.getString("ssid", "default");
    int interval = prefs.getInt("interval", 60);
    prefs.end();
}

NVS is the recommended replacement for EEPROM on ESP32. Do not use the EEPROM.h library on ESP32 for new code (it exists for compatibility but is less efficient).

SPIFFS and LittleFS

For larger file storage (HTML pages, JSON configs, audio clips, images), use a filesystem partition.

SPIFFS (SPI Flash File System) is the older option — simple but no directory support and slower.

LittleFS is the recommended replacement: faster, supports directories, better wear leveling.

#include <LittleFS.h>

void setup() {
    if (!LittleFS.begin(true)) {  // true = format if mount fails
        Serial.println("LittleFS mount failed");
        return;
    }

    File f = LittleFS.open("/config.json", "r");
    if (f) {
        String content = f.readString();
        f.close();
        Serial.println(content);
    }
}

Upload files to the filesystem using the “ESP32 LittleFS Data Upload” plugin for Arduino IDE, or via PlatformIO’s board_build.filesystem = littlefs.


Storing Constants in Flash (ESP32)

Unlike AVR, ESP32 can access Flash through a memory-mapped cache (XIP — Execute In Place). String literals and const arrays are automatically placed in Flash and read through the cache — no PROGMEM or _P functions needed.

However, Flash access has higher latency than SRAM. For performance-critical inner loops, copy frequently accessed tables into SRAM first.

// This array lives in Flash automatically
const uint16_t lookupTable[1024] = { /* ... */ };

// For hot loops, copy to SRAM first
uint16_t ramTable[1024];
memcpy(ramTable, lookupTable, sizeof(lookupTable));

Flash Read/Write Speeds

Operation Speed
SRAM read ~1 cycle (~4 ns at 240 MHz)
Flash (cached, cache hit) ~5–10 cycles
Flash (uncached / cache miss) ~100–200 cycles
EEPROM write (AVR) ~3.4 ms/byte
NVS write (ESP32) ~10–50 ms per key
LittleFS write ~1–10 KB/s

Previous: RAM Management Next: Power Modes Home