Vamos a programar #97 - Ver el contenido de la memoria EEPROM de arduino.
Hola de nuevo a todos, el día de hoy vamos a continuar con un poco mas de Arduino, pero ma precisamente de la memoria EEPROM.
En post anteriores hemos visto que las placas de arduino poseen una memoria que no es volátil y que sirve para guardar cosas, dependiendo del modelo, tendremos hasta 4096 bytes de espacio, si es poco lo que se va a guardar nos sirve. Recientemente he estado trabajando en un proyecto que hace uso de la memoria, pero surgió un problema, me interesa saber cual es el contenido y me resulta molesto llamar de una por una las variables para ver su contenido.
Para resolverlo, decidí hacer un visualizador de memoria al mas puro estilo XVI32 (pero mucho mas chafa), el chiste es tener una forma de visualizar un poco la información. Para lograrlo use el siguiente código.
// Mostrar el contenido de la memoria EEPROM de arduino al estilo XVI32
#include <EEPROM.h>
uint8_t PrintableCharacter(uint8_t TheChar){
if (TheChar < 31 || TheChar > 127)
return 46;
else
return TheChar;
}
void setup(){
Serial.begin(115200);
while (!Serial) {
;
}
for (int Block = 0 ; (Block * 16) <= (EEPROM.length() - 16) ; Block++){
uint8_t Values[16];
for (uint8_t CurrentValue = 0 ; CurrentValue <= 16 ; CurrentValue++){
int address = (Block * 16) + CurrentValue;
Values[CurrentValue] = EEPROM.read(address);
}
char buf[100];
sprintf(buf, "%04X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c",
Block * 16,
Values[0] , Values[1] , Values[2] , Values[3] , Values[4] , Values[5] , Values[6] , Values[7] ,
Values[8] , Values[9] , Values[10] , Values[11] , Values[12] , Values[13] , Values[14] , Values[15] ,
PrintableCharacter(Values[0]) , PrintableCharacter(Values[1]) , PrintableCharacter(Values[2]) , PrintableCharacter(Values[3]) , PrintableCharacter(Values[4]) , PrintableCharacter(Values[5]) , PrintableCharacter(Values[6]) , PrintableCharacter(Values[7]) ,
PrintableCharacter(Values[8]) , PrintableCharacter(Values[9]) , PrintableCharacter(Values[10]) , PrintableCharacter(Values[11]) , PrintableCharacter(Values[12]) , PrintableCharacter(Values[13]) , PrintableCharacter(Values[14]) , PrintableCharacter(Values[15]));
Serial.println(buf);
}
}
void loop(){
}El programa consta de una función (mas las dos por default que usa Arduino) la primera de ellas del tipo "uint8_t" es "PrintableCharacter()" y su función es la determinar si el numero que se la pasa cómo parámetro esta dentro del rango de caracteres imprimibles, si el numero esta dentro del rango 31 ~ 127, la función regresa el mismo numero que se paso cómo parámetro, en caso contrario regresa el numero 46 que en los valores ASCIIrepresenta al punto ".".
Dentro del procedimiento setup(), simplemente hacemos un ciclo "for" que recorrerá desde cero hasta "EEPROM.length() - 16 " pero cómo queremos que cuente de 16 en 16, cada línea consistirá de 16 bytes que se leerán y se almacenaran en un buffer llamado "Values[]", una vez que se ha leído cada bloque, se desplegara usando el siguiente formato: cuatro números hexadecimales para la dirección, dos números hexadecimales para cada valor y además llamaremos a la función "PrintableCharacter" para saber si debemos de mostrar un punto o el valor ASCII del numero.
Cómo podrás observar, funciona y ahora podremos ver de manera mas sencilla el contenido de la memoria, hay que recordar que esta es independiente del espacio donde se almacena el programa por lo que podemos ejecutar este y una vez visualizados los datos deseados, volver a instalar el programa que usemos para escribir en la EEPROM. Cómo de costumbre, puedes bajar el código de mi dropbox para probarlo. En el siguiente post, veremos cómo editar la memoria desde la computadora y repasaremos cómo es que arduino almacena los tipos y las variables en memoria.
Los leo luego.
Vamos a programar #96 - Guardando configuraciones en la EEPROM de arduino.
Hola de nuevo a todos, el día de hoy vamos a continuar con mas sobre la EEPROM de arduino. EN el post anterior vimos cuales son las funciones disponibles para trabajar con ella (y nada mas) pero no vimos ningún ejemplo practico.
Para el ejemplo del día de hoy vamos a suponer que queremos guardar una configuración simple donde solo tenemos dos ajustes. El primero será una cadena de texto de longitud 5 y la segunda un entero (en arduino por default los enteros son de 16 bits para los modelos UNO). Para eso podemos crear código cómo el que sigue.
#include <EEPROM.h> //Para limpiar la memoria, solo deberia de ser usado una vez //Se puede quitar los comentarios, compilar y subir a arduino y despues del primer uso //Eliminarse o comentarse de nuevo para no gastar la vida util de la memoria EEPROM // for (int i = 0 ; i < EEPROM.length() ; i++) { // EEPROM.write(i, 0); // } int CheckData = EEPROM.get(0, CheckData); struct MySettings{ char MyName[20]; int MyValue; }; void setup() { int Address = 0; Serial.begin(9600); while (!Serial) { ; } if (CheckData > -1) { Serial.println("Al parecer no se guardado la configuracion antes"); EEPROM.put(Address , int(-1)); Address += sizeof(CheckData); MySettings SavSettings = { "XWORK", 88 }; EEPROM.put(Address , SavSettings); Serial.println("Guardado"); }else{ Serial.println("Parece haber datos validos de configuracion"); Address += sizeof(CheckData); MySettings SavSettings; EEPROM.get(Address , SavSettings); Serial.println(SavSettings.MyName); Serial.println(SavSettings.MyValue); Serial.println("Cargado"); } } void loop() { }
Antes que nada, hay que tomar un par de consideraciones. A pesar de que tenemos un numero de "seguridad", lo ideal seria limpiar la memoria EEPROM una vez antes de hacer uso de ella, así sabríamos que es lo que contiene antes de hacer uso de ella, si no lo hacemos, no podemos asegurar que es lo que hay y a la hora de revisar podríamos tener un valor válido para "CheckData" pero no significaría que existan datos útiles para cargar. En los ejemplos de arduino existe un sketch llamado "eeprom_clear" y su función es esa, la de definir todos los bytes a 0. La otra consideración es que usamos directamente las funciones "EEPROM.get()" y "EEPROM.put()" y la ventaja de estas es que acepta tipos y estructuras, pero sobre todo, solo actualiza los valores si son diferentes a los que se encuentran guardados (lo que incrementa el tiempo de vida de la EEPROM).
Ahora si veamos cómo es que funciona el código. Para empezar, primero creamos una variable llamada "CheckData" y su valor será lo que este guardado en la memoria EEPROM en la dirección 0. Hay que recordar que la función "EEPROM.get()" toma dos parámetros, el primero será la dirección en la cual va a leer y el segundo el tamaño de los datos en este caso espera un tipo y "CheckData" es del tipo "int". Una vez que tenemos ese valor, creamos una estructura llamada "MySettings" que contendrá dos campos, el primero un arreglo del tipo "char" con una longitud de 20 llamado "MyName" y el segundo un entero llamado "MyValue" del tipo "int".
Dentro de la función "setup()", lo primero que hacemos es revisar el valor de "CheckData", esta variable la usaremos cómo comprobador, en est caso comprobamos que su valor sea mayor que 0, si es así, asumimos que no hemos guardado los datos antes, por lo que procedemos a hacerlo. Cómo lo primero que hicimos fue cargar el número de comprobación "CheckData", entonces es lo primero que hacemos es guardar esa variable con un valor de -1 en la direccion 0 (para este momento la variable "Address" es 0), una vez que se escribió, hay que movernos. Un poco mas arriba mencioné que Arduino UNO usa dos bytes para guardar los enteros, pero cómo es poco practico movernos "de byte en byte", usaremos la función "sizeof()", esta recibe cómo parámetro un tipo y devuelve su longitud en bytes, entonces lo que hacemos, es simplemente asignar el resultado de la llamada a la función "Address".
Una vez que esta guardado el número de comprobación, podemos guardar nuestros datos, para eso primero creamos una variable de nuestra estructura, en este caso "SavSettings" y le asignamos valores. Con los valores asignados, procedemos a guardarlos usando "EEPROM.put()". Cómo primer parámetro (dirección en donde guardar), le asignaremos la variable "Address" y cómo segundo parámetro, "SavSettings".
Si la comprobación dice que ya hay valores validos, simplemente leemos los datos que estén después de "CheckData" usando "EEPROM.get()" con parámetros "Address" para la dirección y "SavSettings" cómo el tipo que se va a leer. AL finalizar la lectura, simplemente los ponemos en el monitor serie y los mostramos.
Ahora solo a manera de ejemplo, veamos el caso para guardar dos campos diferentes.
#include <EEPROM.h>
//Para limpiar la memoria, solo deberia de ser usado una vez
//Se puede quitar los comentarios, compilar y subir a arduino y despues del primer uso
//Eliminarse o comentarse de nuevo para no gastar la vida util de la memoria EEPROM
// for (int i = 0 ; i < EEPROM.length() ; i++) {
// EEPROM.write(i, 0);
// }
int CheckData = EEPROM.get(0, CheckData);
struct MySettings{
char MyName[20];
int MyValue;
};
void setup() {
int Address = 0;
Serial.begin(9600);
while (!Serial) {
;
}
if (CheckData > -1)
{
Serial.println("Al parecer no se guardado la configuracion antes");
EEPROM.put(Address , int(-1));
Address += sizeof(CheckData);
MySettings SavSettings = {
"XWORK",
88
};
EEPROM.put(Address , SavSettings);
Address += sizeof(MySettings);
MySettings SavSettings2 = {
"MPM88",
90
};
EEPROM.put(Address , SavSettings2);
Serial.println("Guardado");
}else{
Serial.println("Parece haber datos validos de configuracion");
Address += sizeof(CheckData);
MySettings SavSettings;
MySettings SavSettings2;
EEPROM.get(Address , SavSettings);
Serial.println("Datos 1");
Serial.println(SavSettings.MyName);
Serial.println(SavSettings.MyValue);
Address += sizeof(MySettings);
EEPROM.get(Address , SavSettings2);
Serial.println("Datos 2");
Serial.println(SavSettings2.MyName);
Serial.println(SavSettings2.MyValue);
Serial.println("Cargado");
}
}
void loop() {
}
Lo importante del ejemplo anterior es que para leer el segundo campo, simplemente debemos de hacer uso de "sizeof()" pasando cómo parámetro la estructura "MySettings" para establecer el inicio de la siguiente dirección.
Y bien, por ahora es todo. Cómo de costumbre, puedes bajar el código de mi dropbox para probarlo.
Los leo luego.
Vamos a programar #95 - Arduino y su EEPROM
Hola de nuevo a todos, el dia de hoy vamos a ver cómo usar la memoria EEPROM que viene incluida en la mayoría de los Arduinos.
EL otro día mientras trabajaba en uno de los proyectos que voy a publicar cuando el blog cumpla 5 años. De nueva cuenta quería guardar las configuraciones para poder retornarlas posteriormente, al crear un archivo de prueba con la mayoría de las configuraciones que planeo usar, el archivo difícilmente pesaba 120 bytes. Suponiendo que la mayoría de las memorias disponibles al día de hoy tienen una capacidad de 4GB (4,000,000,000 bytes), eso significaría que podría guardar unos 33 millones de archivos de configuración.
El almacenamiento es relativamente barato, por lo que no tendría ningún problema en usar una tarjeta de memoria de esa capacidad, pero desde el punto de vista de lo optimo; es un desperdicio. Por eso me decidí a usar la memoria que viene incluida con arduino. Pero antes que nada , que es una memoria EEPROM.
EEPROM o E²PROM son las siglas de Electrically Erasable Programmable Read-Only Memory. Es un tipo de memoria ROM que puede ser programada, borrada y reprogramada eléctricamente. Wikipedia/EEPROM
EN arduino la podemos usar para guardar las configuraciones ya que; dependiendo del modelo; nos va a dar unos cuantos bytes. Para los modelos basados en el ATMEGA328P; tenemos 1024 bytes, para el ATMEGA168; 512 bytes, para los modelos ATMEGA1280 y ATMEGA2560; 4096 bytes.
Para poder usarla, debemos de incluir la libreria "EEPROM.h" que contiene las siguientes funciones:
- EEPROM.Clear(): Limpia la memoria llenandola con ceros.
- EEPROM.Read(Address): Lee la memoria y regresa el valor del byte en la dirección marcada por el parámetro "Address".
- EEPROM.Write(Address, Value):Escribe un valor en la memoria, El valor será el parámetro "Value" y lo hará en la dirección "Address"
- EEPROM.Get(Address,Data): Lee la memoria y regresa el valor en la dirección "Address". El parámetro "Data", indica un tipo o estructura y será leída cada vez (por ejemplo si asignamos a Data un tipo "int", leerá todos los bytes que lo conforman).
- EEPROM.Put(Address,Data): Escribe en la memoria en la dirección "Address" los datos en "Data", puede ser cualquier tipo de los soportados o una estructura. Al igual que EEPROM.Get() se escribirán todos los bytes que conformen al tipo o estructura..
- EEPROM.Update(Address, Value): Actualiza el valore de "Value" en "Address" solo si el valor es distinto al existente (Solo un byte)






