Come usare la memoria EEPROM sul NodeMCU ESP8266

Cosa è la memoria EEPROM

La memoria EEPROM (acronimo di Electrically Erasable Programmable Read-Only Memory) è un tipo di memoria presente in tantissimi dispositivi elettronici (tra cui i nostri amati dispositivi come Arduino o NodeMCU ESP8266/ESP32 etc) utilizzata per memorizzare piccole quantità di dati in maniera “quasi” permanente. In pratica, il dato memorizzato viene mantenuto finchè non viene modificato (dall’utente o, in generale, dal software che gestisce il dispositivo) e rimane immagazzinato anche in mancanza di alimentazione. Detto in altri termini, il dato rimane invariato anche se l’alimentazione viene a mancare e modificato (aggiornato) solo se previsto dall’utente o dal software. Questo tipo di memoria è quindi molto utile per memorizzare lo stato di alcune variabili che non si vuole perdere nel caso dovesse mancare l’alimentazione elettrica (per esempio lo stato di un GPIO oppure l’orario di accensione o spegnimento di un dispositivo etc). Più in generale consente di memorizzare delle impostazioni di un dispositivo che non vogliamo perdere con il suo spegnimento.

Bisogna rimarcare un fatto: le memorie EEPROM non hanno una vita “infinita”. Infatti il numero di scritture possibili è limitato (seppur abbastanza elevato). Tipicamente sono possibili circa 100000 cicli di scrittura/aggiornamento del dato, numero comunque sufficiente per i nostri esperimenti.

Come usare la EEPROM su NodeMCU

L’utilizzo della EEPROM è abbastanza semplice e ricalca quello su Arduino. Si usa la libreria EEPROM che deve essere inclusa nel solito modo:

#include <EEPROM.h>

La prima cosa da fare è inizializzare la EEPROM nella funzione setup:

EEPROM.begin(512)

Il nostro dispositivo ha 512 locazioni di memoria.

Un dato memorizzato in un certo indirizzo può essere letto:

EEPROM.read(address)

In questo caso viene restituito il dato memorizzato (come byte) all’indirizzo address. Se la locazione a quell’indirizzo non è mai stata scritta, il dato restituito sarà un valore casuale.

Oppure può essere scritto in una certa locazione:

EEPROM.write(address, value)

In questo caso viene scritto il valore value (come byte) all’indirizzo address.

Un dato già scritto ad un certo indirizzo può essere aggiornato tramite il comando:

EEPROM.update(address, value)

La funzione update aggiorna il dato solo se è diverso da quello presente. In questo modo si evitano scritture inutili e si risparmiano cicli di scrittura (allungando la vita della EEPROM).

Per leggere un qualunque tipo di dato o di oggetto (come per esempio un float o una struct) presente in un certo indirizzo usiamo la funzione get:

EEPROM.get(address, data)

Per scrivere un qualunque tipo di dato o di oggetto in un dato indirizzo (come per esempio un float o una struct) usiamo la funzione put:

EEPROM.put(address, data)

Per rendere effettiva la scrittura è sempre necessario dare il comando commit:

EEPROM.commit()

Esempi di codice per la lettura e la scrittura della EEPROM

Creiamo il progetto per lo sketch di scrittura su PlatformIO

Abbiamo già visto in un articolo precedente come creare un progetto usando l’ottimo IDE PlatformIO. Creiamo quindi il nostro progetto seguendo le indicazioni dell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO. In questo caso possiamo saltare la parte su come aggiungere le librerie al progetto in quanto non le utilizzeremo.

Scarichiamo ora il codice di esempio:

Sovrascriviamo il file main.cpp appena scaricato su quello del progetto creato precedentemente e carichiamo lo sketch sul dispositivo.

Analizziamo il codice

Inizialmente vengono incluse le librerie che ci servono:

#include <Arduino.h>
#include <EEPROM.h>

Definiamo ora le variabili che contengono i valori che vogliamo memorizzare nella EEPROM:

String hhon = "16";                 
String mmon = "25";
String hhoff = "18";
String mmoff = "32";

.......

String timerStatusEE = "1";

Stiamo immaginando di voler memorizzare l’orario di accensione e di spegnimento di un dispositivo. Le variabili hhon e mmon contengono l’orario di accensione (16:25) mentre le variabili hhoff e mmoff contengono l’orario di spegnimento (18:32). La variabile timerStatusEE contiene uno stato, per esempio lo stato del timer (attivato o disattivato) che desideriamo memorizzare nella EEPROM.

Definiamo anche le variabili che contengono gli indirizzi della EEPROM in cui memorizzeremo i valori:

int hhon_address = 2;
int mmon_address = 4;
int hhoff_address = 6;
int mmoff_address = 8;

......

int timerStatusEE_address = 10;

Memorizziamo la prima variabile (hhon) a partire dall’indirizzo 2 (hhon_address). Questa variabile ha lunghezza 2 (celle), quindi la variabile successiva (mmon) la memorizzeremo all’indirizzo 4. Anche questa variabile ha lunghezza 2, quindi la successiva (hhoff) la memorizzeremo all’indirizzo 6 e così via.

Andiamo ora sulla funzione setup:

Serial.begin(9600);
delay(1000);
EEPROM.begin(512);  //Initialize EEPROM

Come prima cosa inizializziamo la porta seriale e la EEPROM.

Poi impostiamo la scrittura delle variabili:

for(int i = 0; i < hhon.length(); i++) {
    EEPROM.write(hhon_address + i, hhon[i]);
}

for(int i = 0; i < mmon.length(); i++) {
    EEPROM.write(mmon_address + i, mmon[i]);
}

for(int i = 0; i < hhoff.length(); i++) {
    EEPROM.write(hhoff_address + i, hhoff[i]);
}

for(int i = 0; i < mmoff.length(); i++) {
    EEPROM.write(mmoff_address + i, mmoff[i]);
}

EEPROM.write(timerStatusEE_address, timerStatusEE[0]);

Infine finalizziamo la scrittura tramite la funzione commit e stampiamo un messaggio sul Serial Monitor:

EEPROM.commit();

Serial.println("Done!");

Lasciamo la funzione loop vuota in quanto non ci serve.

A questo punto i dati sono stati memorizzati nella EEPROM negli indirizzi indicati. Questi dati si manterranno anche dopo lo spegnimento del dispositivo, ed è ciò che andremo a verificare fra poco. Prima di passare al passo successivo, scolleghiamo la NodeMCU dalla USB in modo da interromperne l’alimentazione.

Creiamo il progetto per lo sketch di lettura su PlatformIO

Possiamo ora creare un altro progetto relativo alla lettura della EEPROM come fatto al punto precedente e sostituiamo, come al solito, il file main.cpp con quello che scaricheremo dal link sottostante:

Ricolleghiamo la NodeMCU alla USB e carichiamo lo sketch.

Analizziamo il codice

Come al solito includiamo le librerie:

#include <Arduino.h>
#include <EEPROM.h>

e inizializziamo come stringhe vuote le variabili in cui metteremo i valori letti dalla EEPROM:

String hhonEE = "";
String mmonEE = "";
String hhoffEE = "";
String mmoffEE = "";

.......

String timerStatusEE = "";

Reimpostiamo gli indirizzi della EEPROM da cui dovremo estrarre i dati con, ovviamente, gli stessi valori dello sketch di scrittura:

int hhonEE_address = 2;
int mmonEE_address = 4;
int hhoffEE_address = 6;
int mmoffEE_address = 8;

.....

int timerStatusEE_address = 10;

Nella funzione setup inizializziamo la porta seriale e la EEPROM:

Serial.begin(9600);
delay(1000);
EEPROM.begin(512);  //Initialize EEPROM

Vediamo ora la lettura del primo dato hhonEE:

hhonEE = char(EEPROM.read(hhonEE_address));
hhonEE += char(EEPROM.read(hhonEE_address + 1)); 

Nella prima riga viene letto il valore che si trova all’indirizzo hhonEE_address e memorizzato nella variabile hhonEE. Nella seconda riga viene letto il valore memorizzato nella locazione successiva (perché sappiamo che il valore totale occupa 2 locazioni) e viene concatenato col valore letto precedentemente in modo da formare il valore completo nella variabile hhonEE.

Lo stesso concetto è utilizzato per la lettura delle altre variabili:

mmonEE = char(EEPROM.read(mmonEE_address));
mmonEE += char(EEPROM.read(mmonEE_address + 1));

hhoffEE = char(EEPROM.read(hhoffEE_address)); 
hhoffEE += char(EEPROM.read(hhoffEE_address + 1));

mmoffEE = char(EEPROM.read(mmoffEE_address));
mmoffEE += char(EEPROM.read(mmoffEE_address + 1));

Infine vi è la lettura della variabile timerStatusEE, che occupa una sola locazione, e la stampa di un messaggio sul Serial Monitor:

timerStatusEE = char(EEPROM.read(timerStatusEE_address));

Nella funzione loop abbiamo la stampa ciclica (ogni 5 secondi) delle variabili lette:

Serial.println(hhonEE);
Serial.println(mmonEE);
Serial.println(hhoffEE);
Serial.println(mmoffEE);

Serial.println(timerStatusEE);

delay(5000);

Se tutto è andato bene dovremmo vedere stampati sul Serial Monitor i valori che abbiamo memorizzato con lo sketch di scrittura, come visibile nell’immagine seguente:

Lettura della EEPROM del modulo NodeMCU visualizzata sul Serial Monitor
Lettura della EEPROM del modulo NodeMCU visualizzata sul Serial Monitor

Un esempio d’uso in un progetto reale

Puoi vedere un esempio pratico di utilizzo su un progetto reale andando a leggere l’articolo Come realizzare un irrigatore automatico comandato da Telegram con il NodeMCU ESP8266.

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
Torna in alto