ESP32 Preferences Library: Non-Volatile Storage NVS Guide
The ESP32 Preferences library NVS non-volatile storage system is one of the most powerful yet underutilised features of the ESP32 microcontroller platform. Whether you are building a smart home device, an industrial sensor node, or a connected gadget, you will almost always need to store configuration data that persists across reboots and power cycles. In this comprehensive guide, we cover everything you need to know about the Preferences library — from basic read/write operations to advanced namespace management and wear-levelling strategies — tailored for hobbyists, students, and professional developers in India.
What Is NVS and Why Does It Matter?
NVS stands for Non-Volatile Storage, a partition on the ESP32’s flash memory that retains data even when power is removed. Unlike SRAM variables that vanish on reset, NVS data survives power cuts, brownouts, and watchdog timer resets. Espressif designed NVS with wear levelling built in, so writing the same key repeatedly does not destroy a single flash sector the way naive EEPROM emulation does.
The Preferences library is Arduino’s high-level wrapper around the ESP-IDF NVS API. It abstracts away partition handles, blob serialisation, and commit operations, giving you a clean key-value interface that feels familiar if you have used Arduino’s EEPROM library before — except it is far more reliable and feature-rich.
Common use cases in Indian maker projects include:
- Storing WiFi SSID and password entered via a captive portal, so the device reconnects automatically after a power cut
- Saving calibration offsets for temperature, pressure, or weight sensors
- Keeping a boot counter or event log for diagnostics
- Persisting user preferences such as display brightness, alarm thresholds, or language selection
- Storing device-unique identifiers provisioned during manufacturing
Ai Thinker NodeMCU-32S-ESP32 Development Board – IPEX Version
A reliable dual-core ESP32 development board with 4 MB flash — plenty of NVS partition space for storing hundreds of key-value pairs.
Getting Started with the Preferences Library
The Preferences library ships with the ESP32 Arduino core, so no additional installation is required. Open Arduino IDE, make sure you have the ESP32 board package installed (version 2.x or 3.x), and you are ready to go.
The library lives in the header Preferences.h. Here is the minimal skeleton every project needs:
#include <Preferences.h>
Preferences prefs;
void setup() {
Serial.begin(115200);
// Open namespace "myapp" in read-write mode (false = RW)
prefs.begin("myapp", false);
// Write an integer
prefs.putInt("counter", 42);
// Read it back (default 0 if key absent)
int val = prefs.getInt("counter", 0);
Serial.println(val); // prints 42
prefs.end(); // close to flush and free handle
}
void loop() {}
Key points about initialisation:
- prefs.begin(namespace, readOnly) — opens or creates a namespace. Pass
trueas the second argument to open in read-only mode, which prevents accidental writes. - prefs.end() — always call this when you are done. It flushes pending writes to flash and releases the NVS handle, freeing RAM.
- Namespace names must be 15 characters or fewer (NVS limitation).
- Key names must be 15 characters or fewer.
Supported Data Types and Read/Write Operations
The Preferences library supports a rich set of data types, covering all common Arduino and ESP32 use cases:
| Data Type | Put Method | Get Method | Size |
|---|---|---|---|
| bool | putBool() | getBool() | 1 byte |
| uint8_t | putUChar() | getUChar() | 1 byte |
| int16_t | putShort() | getShort() | 2 bytes |
| int32_t | putInt() | getInt() | 4 bytes |
| uint32_t | putUInt() | getUInt() | 4 bytes |
| int64_t | putLong64() | getLong64() | 8 bytes |
| float | putFloat() | getFloat() | 4 bytes |
| double | putDouble() | getDouble() | 8 bytes |
| String | putString() | getString() | variable |
| byte array (blob) | putBytes() | getBytes() | variable |
Example storing and retrieving a WiFi password string:
prefs.begin("wifi", false);
prefs.putString("ssid", "MyHomeNetwork");
prefs.putString("pass", "SuperSecret123");
prefs.end();
// Later, on boot:
prefs.begin("wifi", true); // read-only
String ssid = prefs.getString("ssid", "");
String pass = prefs.getString("pass", "");
prefs.end();
Working with Namespaces
Namespaces are NVS’s way of organising keys into logical groups — similar to folders in a file system. A single ESP32 project can have multiple namespaces, each opened independently. This is extremely useful for modular firmware where different subsystems (WiFi manager, OTA updater, sensor calibration) manage their own storage without risk of key name collisions.
Important namespace rules:
- Maximum 15 characters per namespace name
- A maximum of approximately 127 namespaces can exist simultaneously in the default NVS partition (4096 entries total)
- You can only have one namespace open at a time per
Preferencesobject. Create multiplePreferencesobjects if you need concurrent access - Use
prefs.clear()to wipe all keys in the current namespace - Use
prefs.remove("key")to delete a specific key
Preferences wifiPrefs;
Preferences sensorPrefs;
wifiPrefs.begin("wifi", false);
sensorPrefs.begin("sensors", false);
wifiPrefs.putString("ssid", "FactoryDefault");
sensorPrefs.putFloat("tempOffset", -0.5);
sensorPrefs.putFloat("humidOffset", 1.2);
wifiPrefs.end();
sensorPrefs.end();
DHT11 Digital Relative Humidity and Temperature Sensor Module
Store DHT11 calibration offsets in NVS Preferences so your temperature readings stay accurate even after firmware updates.
Practical Examples: WiFi Credentials, Counters, and Sensor Data
Example 1: Persistent Boot Counter
A boot counter is the simplest NVS use case and a great debugging tool to track unexpected reboots on deployed devices:
#include <Preferences.h>
Preferences prefs;
void setup() {
Serial.begin(115200);
prefs.begin("diagnostic", false);
uint32_t boots = prefs.getUInt("bootCount", 0);
boots++;
prefs.putUInt("bootCount", boots);
prefs.end();
Serial.printf("Boot number: %un", boots);
}
void loop() {}
Example 2: WiFi Manager with NVS
This pattern is the backbone of most production ESP32 devices in India. On first boot, the device enters AP mode for configuration. Credentials are saved to NVS and used automatically on subsequent boots:
bool loadWiFiCreds(String &ssid, String &pass) {
Preferences p;
p.begin("wifi", true);
ssid = p.getString("ssid", "");
pass = p.getString("pass", "");
p.end();
return ssid.length() > 0;
}
void saveWiFiCreds(const String &ssid, const String &pass) {
Preferences p;
p.begin("wifi", false);
p.putString("ssid", ssid);
p.putString("pass", pass);
p.end();
}
Example 3: Storing Struct as Blob
For complex configuration objects, serialise the struct as a raw byte blob:
struct Config {
float setpoint;
uint16_t interval;
bool alertEnabled;
};
void saveConfig(const Config &cfg) {
Preferences p;
p.begin("config", false);
p.putBytes("cfg", &cfg, sizeof(cfg));
p.end();
}
bool loadConfig(Config &cfg) {
Preferences p;
p.begin("config", true);
size_t s = p.getBytes("cfg", &cfg, sizeof(cfg));
p.end();
return s == sizeof(cfg);
}
BMP280 Barometric Pressure and Altitude Sensor I2C/SPI Module
Use NVS to store sea-level reference pressure for your BMP280, so altitude calculations remain consistent across power cycles.
Wear Levelling, Limitations and Best Practices
Flash memory cells have a finite write endurance — typically 10,000 to 100,000 erase cycles for NOR flash. NVS mitigates this with a page rotation mechanism: rather than overwriting the same sector repeatedly, it writes to the next available slot and periodically compacts the partition. This dramatically extends flash lifespan for typical IoT workloads.
However, there are limits you must respect:
- Do not write sensor readings every second. If you sample a sensor at 1 Hz and write to NVS each time, you will exhaust the flash within weeks. Instead, write only on meaningful changes or on a slow periodic schedule (every few minutes).
- NVS partition size: The default NVS partition is 24 KB (6 pages of 4 KB). You can customise this in the partition table if needed. Each page holds up to 126 entries; with overhead, this gives roughly 500-1000 usable key-value pairs depending on value sizes.
- String length limit: Strings can be up to 4000 bytes, and blobs up to 508 KB, but each chunk is 8 bytes per blob entry, so large blobs consume many NVS entries.
- Thread safety: The ESP-IDF NVS layer is not inherently thread-safe. If you use FreeRTOS tasks, use a mutex to protect Preferences calls.
- isKey() check: Before reading, use
prefs.isKey("mykey")to verify a key exists without relying on default values alone.
Best practices summary:
- Always call
prefs.end()after every begin/read/write session - Open in read-only mode (
true) when you are not writing — faster and safer - Group related keys in the same namespace; use separate namespaces for separate subsystems
- Never write inside a tight loop — batch writes or throttle to minutes/hours for frequently changing values
- Validate data after reading with range checks before using in safety-critical calculations
- Use
prefs.freeEntries()during development to monitor remaining NVS space
30Pin ESP32 Expansion Board with Type-C USB and Micro USB
This expansion board makes prototyping ESP32 NVS projects easier with convenient breakout pins for all sensors and peripherals.
Frequently Asked Questions
Q: Does the Preferences library erase data when I upload new firmware?
A: No, by default uploading a new sketch over USB does not erase the NVS partition. However, if you select “Erase Flash” in Arduino IDE or use esptool.py erase_flash, all flash including NVS is wiped. To erase only NVS, call nvs_flash_erase() from ESP-IDF or call prefs.clear() on each namespace from your sketch.
Q: How many keys can I store in the ESP32 NVS?
A: The default NVS partition (24 KB, 6 pages) can hold approximately 126 key-value pairs per page. In practice, considering overhead, you can reliably store several hundred simple key-value pairs. For complex configurations with large strings or blobs, use a custom partition table to increase NVS size to 64 KB or more.
Q: Can I use the Preferences library on ESP8266?
A: No. The Preferences library is specific to the ESP32 family (ESP32, ESP32-S2, ESP32-S3, ESP32-C3, etc.). On ESP8266, use the EEPROM library or LittleFS for persistent storage.
Q: Is NVS data encrypted?
A: The standard NVS partition is not encrypted. ESP-IDF does support NVS encryption (using the NVS encryption key stored in the efuse), but this requires custom partition tables and ESP-IDF configuration — it is not available through the Arduino Preferences library directly. For sensitive data like OAuth tokens, consider Flash Encryption + NVS Encryption via ESP-IDF.
Q: What is the difference between EEPROM and Preferences on ESP32?
A: The Arduino EEPROM library on ESP32 is a compatibility wrapper that stores data in a special flash partition and requires manual EEPROM.commit() calls. It lacks wear levelling and has a fixed 4 KB size. The Preferences library is purpose-built for ESP32, uses the full NVS system with wear levelling, supports multiple data types natively, and is the recommended approach for any new project.
Start Building with ESP32 Today
Ready to put the Preferences library to work? Zbotic.in stocks a wide range of ESP32 development boards, sensors, and accessories delivered across India. Build smarter IoT devices with reliable non-volatile storage from day one.
Add comment