Salvare dati permanenti su ESP32 con Preferences (NVS)

Pubblicato il 14/05/2026 ESP32

Salvare il valore di un contatore o le credenziali WiFi in modo persistente non richiede piu' scrivere in EEPROM emulata. Da anni ESP32 offre NVS (Non-Volatile Storage) esposto via la libreria Preferences — 3 righe per leggere, 3 righe per scrivere, garanzia di wear leveling automatico.

1. Namespace: raggruppa i tuoi dati

Ogni set di variabili sta dentro un namespace (max 15 caratteri) per non collidere con librerie di terze parti.

#include <Preferences.h>
Preferences prefs;

void setup() {
  prefs.begin("mia_app", /*readOnly=*/false);   // apre il namespace
}

2. Scrittura

prefs.putUInt("boot_count", 42);
prefs.putFloat("k_calib", 1.023f);
prefs.putBool("dark_mode", true);
prefs.putString("wifi_ssid", "TuaRete");
prefs.putString("wifi_pass", "PasswordSegreta");
prefs.putBytes("cert", cert_data, cert_len);   // fino a ~500 KB

3. Lettura con default

uint32_t n     = prefs.getUInt   ("boot_count", 0);      // 0 se non presente
float    k     = prefs.getFloat  ("k_calib", 1.0f);
bool     dark  = prefs.getBool   ("dark_mode", false);
String   ssid  = prefs.getString ("wifi_ssid", "");
// bytes: passi un buffer, ritorna la lunghezza effettiva
uint8_t buf[512];
size_t   n_read = prefs.getBytes("cert", buf, sizeof(buf));

4. Contatore di boot: esempio classico

void setup() {
  prefs.begin("stats", false);
  uint32_t boots = prefs.getUInt("count", 0) + 1;
  prefs.putUInt("count", boots);
  prefs.end();
  Serial.printf("Boot n. %u
", boots);
}

5. Cancellare tutto (factory reset)

prefs.begin("mia_app", false);
prefs.clear();               // svuota solo questo namespace
prefs.end();

#include <nvs_flash.h>
nvs_flash_erase();           // wipe TOTALE (radicale, richiede reboot dopo)
nvs_flash_init();

6. Limiti da conoscere

  • Chiavi max 15 caratteri. "boot_count" OK, "contatore_boots" NO
  • Stringhe fino a ~4000 bytes; oltre usa putBytes o SPIFFS/LittleFS
  • Il write ha un limite di ~100.000 cicli per cella. Evita loop tipo "scrivi ogni sample" — accumula e scrivi periodicamente
  • Se il namespace non esiste al primo begin ottieni un errore nvs_open failed: NOT_FOUND ma viene creato: nessun impatto

7. Namespace di stato vs configurazione

Best practice: separa i namespace per proposito.

Preferences cfg, stats;
cfg.begin("config", false);      // credenziali, URL server, calibrazione
stats.begin("stats", false);     // boot count, error count, uptime medio

8. Esempio completo: credenziali WiFi con fallback AP

void connectWiFi() {
  prefs.begin("wifi", true);     // read-only
  String ssid = prefs.getString("ssid", "");
  String pass = prefs.getString("pass", "");
  prefs.end();
  if (ssid.length() == 0) {
    WiFi.softAP("ESP32-Setup");  // modalita' AP per config web
    return;
  }
  WiFi.begin(ssid.c_str(), pass.c_str());
}
Attenzione: non salvare password in chiaro in NVS se il dispositivo puo' cadere in mani sbagliate. NVS non e' cifrato di default. Per dati sensibili abilita NVS Encryption (usa e-fuse per la chiave, non modificabile dopo).