La utilización de la EEPROM en los proyectos de Arduino solía ser un tema relativamente escabroso:
En mi caso, recuerdo terminar desarmando cada variable en bytes y guardando así byte a byte su contenido.
Luego debía repetir el proceso a la inversa, leyendo uno a uno los bytes almacenados en la EEPROM y recuperando los valores tipo integer, double, o char[] que en su momento había almacenado allí.
No obstante hay un mecanismo tremendamente sencillo (y útil) que una vez lo integras a tu código, seguramente te acompañará en cada proyecto que realices.
Hoy, este es mi mecanismo favorito para almacenar parámetros de configuración y valores que requieren ser preservados ante un reset de mis ESP8266 (o básicamente cualquier otro microcontrolador compatible con Arduino, quizás con alguna pequeñísima modificación).
Como primer paso, al inicio de nuestro código no debemos olvidarnos de incluir la librería EEPROM.h:
#include <EEPROM.h> // Manejo de la EEPROM
Una struct encapsulando las variables en EEPROM
En nuestro código vamos a definir una estructura que contenga las variables necesarias por ejemplo para la configuración de nuestro proyecto.
/* Estructura que carga y guarda en eeprom */ struct config_t { int puerto,retries=0; char ssid[12]; byte valor1,valor2,valor3 = 0; IPAddress sts; } config;
Y luego agrego estas dos funciones a mi código. Bastará llamarlas cada vez que sea necesario guardar o cargar el contenido de ese struct en la EEPROM.
//Guardo en la eeprom el struct config void eepromsave() { EEPROM.begin(sizeof(config)); EEPROM.put(0, config); //Guardo la config EEPROM.end(); } //Cargo de la eeprom el struct config void eepromload() { EEPROM.begin(sizeof(config)); EEPROM.get(0, config); //Cargo config EEPROM.end(); }
Por si no estás ducho en el tema de estructuras, puedes utilizar las variables como si fuera cualquier variable, pero dentro de la estructura.
Ejemplo: para guardar un valor en la variable INT llamada puerto, haces
config.puerto = valor;
¿ Cómo funciona una struct en este contexto ?
En la interna, nuestra estructura se almacena en memoria con su contenido ordenado en forma contigua, en bloque.
El nombre de la estructura ( tal como ocurre con el nombre de cualquier variable) actúa como un simple puntero donde se inicia su espacio de almacenamiento en memoria.
El otro dato necesario es la cantidad de bytes que ocupa la estructura, y eso se obtiene haciendo un simple sizeof(config).
Esos dos datos son los únicos necesarios para identificar ese bloque de memoria que contiene nuestra estructura.
Por eso es tan simple, guardar todo de una sola vez en la EEPROM: Simplemente nos limitamos a guardar o cargar el paquete de bytes definido por la estructura, con su inicio y tamaño.
Lo de adentro, se resuelve solo, porque las variables están declaradas explícitamente en su interior. Si necesitas agregar más variables en tu proyecto, simplemente los agregas dentro de tu estructura config.
Consejo a considerar al inicio de tu proyecto
Debes considerar que la EEPROM puede contener cualquier valor arbitrario (basura) tal como viene de fábrica.
Por este motivo te conviene inicializar previamente tu estructura con valores conocidos, «por defecto» y guardarlos en la EEPROM, antes de leer nada desde allí, la primera vez que corre tu código
Para lograr esto puedes agregar en tu código una función como la siguiente:
//Inicializo valores para eeprom void eeprominit() { config.eepromok=49834; config.puerto=2828; config.retries=3; strncpy(config.ssid, "Miproyecto01", 12); config.valor1=10; config.valor2=127; config.valor3=255; config.sts[0] = 0; config.sts[1] = 0; config.sts[2] = 0; config.sts[3] = 0; //Guardo valores por defecto en la eeprom eepromsave(); }
Al iniciar la ejecución de tu código, cargas la config desde la eeprom.
A continuación, verificas que una variable INT que has puesto dentro de la estructura config para este propósito tenga un valor arbitrario conocido:
Como ejemplo, digamos el valor 49834 en la variable config.eepromok
Si la variable tiene ese valor, quiere decir que está todo bien. Pero si hay cualquier otro valor, llamas a la función eeprominit() porque eso significa que la eeprom aún no fue inicializada.