AVR microcontrollers include a small EEPROM (Electrically Erasable Programmable Read-Only Memory) for non-volatile storage. On the ATmega328P it is 1 KB.
#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).
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.
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.
The ESP32 uses an external SPI Flash chip (typically 4 MB). Its layout is defined by a 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
nvs: Non-Volatile Storage — key-value storeapp0/app1: two firmware slots for OTA updatesspiffs: filesystem for user filesCustom partition tables can be defined in a CSV file and selected in Arduino-ESP32 board settings.
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).
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.
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));
| 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 |