Scrivere e leggere dati sulla EEPROM di Arduino

Leggere e scrivere dati sulla EEPROM di Arduino

In questo articolo ti parlo della libreria EEPROM di Arduino e di alcuni esempi su come leggere e scrivere dati permanenti sulla memoria.

Alcune volte, durante lo sviluppo dei nostri progettini, ci troviamo nella situazione in cui un determinato dato o valore, che sia esso un’impostazione dell’utente o l’ultima lettura di un sensore, debba essere salvato da qualche parte per poterlo ritrovare dopo l’interruzione dell’alimentazione o anche semplicemente successivamente ad un reset.

Per risolvere questo “problema”, entro certi limiti, possiamo utilizzare l’EEPROM presente sui vari Arduino/Geniuino AVR based.

Che cos’è una EEPROM?

Sensa scendere troppo nei dettagli, EEPROM sta per Electrically Erasable Programmable Read-Only Memory e quindi un tipo di memoria non volatile, utilizzabile per memorizzare piccole quantità di dati, sulla quale le operazioni di scrittura, cancellazione e riscrittura hanno luogo elettricamente.
Ogni cella di memoria può contenere un byte ed in base alla versione di Arduino, possiamo avere a disposizione dai 512 byte ai 4KB.

Per l’esattezza 1024 bytes per gli ATmega328P, 512 bytes per gli ATmega168 e ATmega8, 4 KB (4096 bytes) per gli ATmega1280 e ATmega2560.

Diciamo che non è proprio un hard disk, ma utilizzata in modo opportuno può assolvere egregiamente al suo compito.

Attenzione però! I cicli di scrittura, cancellazione e riscrittura non sono illimitati. Ogni EEPROM ha un numero finito di operazioni possibili, quindi è bene tenerne conto in fase di scrittura del nostro codice.

Stando alla documentazione ufficiale, una EEPROM ha una vita di circa 100.000 cicli.

La libreria EEPROM di Arduino

Arduino mette a disposizione una libreria per avere accesso in lettura e scrittura sulla memoria.

Per poterla utilizzare nei nostri sketch basterà includerla tramite:
#include <EEPROM.h>

Metodi della libreria EEPROM di Arduino

I metodi restituiti dalla libreria EEPROM di Arduino sono cinque e mezzo 😁.
Dico cinque e mezzo perché l’ultimo è una via di mezzo tra metodo e operatore.

METODODESCRIZIONE
read(indirizzo)Restituisce il byte contenuto all’indirizzo specificato.
write(indirizzo, valore)Scrive un byte all’indirizzo specificato.
update(indirizzo, valore)Simile al precedente, scrive il valore solo se diverso da quello precedentemente contenuto nella cella di memoria.
get(indirizzo, referenza)Imposta il valore della referenza con il contenuto della cella all’indirizzo specificato.
put(indirizzo, referenza)Imposta il contenuto della cella con il valore e tipo della referenza passata.
EEPROM[indice]Utilizzato per accedere in lettura o in scrittura sulla EEPROM come se fosse un array. L’indice passato equivale all’indirizzo della cella.
Metodi della libreria EEPROM di Arduino

Libreria EEPROM di Arduino esempi

Per mostrarti tutti i metodi in azione e per evitare di scrivere tante volte codice simile ho pensato di fare in questo modo.
Un primo sketch con i vari metodi di scrittura, ed un secondo con quelli in lettura.
Come sempre ampiamente commentati ed intuitivi.

Se qualcosa non dovesse essere chiara, utilizza la sezione commenti a fondo articolo.

Codice per la scrittura della EEPROM su Arduino

//Includo la libreria
#include <EEPROM.h>

void setup() {
  Serial.begin(9600);

  while (!Serial) {
    //Attendo che venga aperto il monitor seriale
  }

  /*
     Definisco degli indirizzi di memoria.
     Ciascuno per ogni esempio.
  */
  int IND_0 = 0; // Per il metodo write() e read() nel successivo sketch
  int IND_1 = 10; // Per il metodo put() e get() nel successivo sketch
  int IND_2 = 20; // Per il metodo put() e get() con tipo di dato custom
  int IND_3 = 40; // Per il metodo EEPROM[] e update()

  //Valore di tipo float per il primo esempio put() e get()
  float tipoFloat = 0.65;

  //Struttura custom per il secondo esempio put() e get()
  struct CustomObj {
    byte numero;
    float conVirgola;
    char testo[10];
  };

  //Oggetto custom per il secondo esempio put() e get()
  CustomObj mioCustomObj = {
    2,
    9.21000f,
    "Ciaone"
  };

  //Il metodo write
  EEPROM.write(IND_0, 25); //salvo all'indirizzo 0 il valore 25
  Serial.println("Scrittura con metodo write eseguita.");

  //Il metodo put - primo esempio
  EEPROM.put(IND_1, tipoFloat); //salvo all'indirizzo 1 il valore di tipoFloat con il relativo tipo (float)
  Serial.println("Scrittura con metodo put - primo esempio eseguita.");

  //Il metodo put - secondo esempio
  EEPROM.put(IND_2, mioCustomObj); //salvo all'indirizzo 2 l'oggetto mioCustomObj avente struttura CustomObj
  Serial.println("Scrittura con metodo put - secondo esempio eseguita.");

  //EEPROM come array
  EEPROM[IND_3] = 9; //salvo all'indirizzo 3 il numero 9
  Serial.println("Scrittura con sintassi array eseguita.");

  Serial.println("Adesso carica il secondo sketch e controlla che ci sia tutto");

}

void loop() {
  // tutto avviene nel setup
}

Codice per la lettura della EEPROM su Arduino

//Includo la libreria
#include <EEPROM.h>

void setup() {
  Serial.begin(9600);

  while (!Serial) {
    //Attendo che venga aperto il monitor seriale
  }

  /*
     Definisco gli stessi indirizzi di memoria dello sketch precedente.
  */
  int IND_0 = 0; // Per il metodo write() e read() nel successivo sketch
  int IND_1 = 10; // Per il metodo put() e get() nel successivo sketch
  int IND_2 = 20; // Per il metodo put() e get() con tipo di dato custom
  int IND_3 = 40; // Per il metodo EEPROM[] e update()

  //Valore di tipo float per il primo esempio put() e get()
  float tipoFloat;

  //Struttura custom per il secondo esempio put() e get()
  struct CustomObj {
    byte numero;
    float conVirgola;
    char testo[10];
  };

  //Oggetto custom per il secondo esempio put() e get()
  CustomObj mioCustomObj;

  //Il metodo read
  int valPrimoEsempio = EEPROM.read(IND_0); //leggo dall'indirizzo 0
  Serial.print("Lettura con metodo read mi aspetto 25: ");
  Serial.println(valPrimoEsempio, DEC);// mi aspetto il valore 25

  //Il metodo get - primo esempio
  EEPROM.get(IND_1, tipoFloat); //leggo dall'indirizzo 1 il valore di tipo float e lo metto nella variabile tipoFloat
  Serial.print("Lettura con metodo get - primo esempio mi aspetto 0.65: ");
  Serial.println(tipoFloat, 2); // mi aspetto il valore 0.65

  //Il metodo get - secondo esempio
  EEPROM.get(IND_2, mioCustomObj); //leggo dall'indirizzo 2 e metto il contenuto nel mio oggetto custom
  Serial.println("Lettura con metodo get - secondo esempio mi aspetto (2, 9.21, 'Ciaone') ");
  Serial.println(mioCustomObj.numero, DEC); // mi aspetto 2
  Serial.println(mioCustomObj.conVirgola, 2); // mi aspetto 9.21
  Serial.println(mioCustomObj.testo);  // mi aspetto "Ciaone"

  //EEPROM come array
  EEPROM[IND_3] = 9; //salvo all'indirizzo 3 il numero 9
  Serial.println("Scrittura con sintassi array eseguita.");

  int valAsArray = EEPROM[IND_3]; //leggo dall'indirizzo 3
  Serial.print("Lettura con sintassi array mi aspetto 9: ");
  Serial.println(valAsArray); // mi aspetto 9

  //Aggiorno ultimo valore con metodo update()
  EEPROM.update(IND_3, 18);
  Serial.println("Aggiornamento valore con metodo update eseguito.");

  //leggo nuovamente dall'indirizzo 3
  int valAfterUpdate = EEPROM[IND_3];
  Serial.print("Lettura con sintassi array mi aspetto 18: ");
  Serial.println(valAfterUpdate); // mi aspetto 18

  Serial.println("Fatto!");

}

void loop() {
  // tutto avviene nel setup
}

Conclusione

Abbastanza semplice no?
Come avrai notato tra un indirizzo e l’altro ho lasciato un certo numero di spazi, questo perché se un dato è troppo grosso per entrare nella cella specificata, in automatico verrà spalmato in quelle successivamente adiacenti.

Se deciderai di impiegarla nei tuoi futuri progetti, ricorda sempre il numero finito di cicli di scrittura/cancellazione e considera anche il tempo impiegato per il salvataggio di un dato che si aggira intorno ai 3 ms.

Spero possa esserti utile!

Potrebbero interessarti anche...