Hola, recientemente me he estado metiendo con el tema de las SD y cómo no... ya que tenemos la posibilidad de leer y escribir en una memoria externa... ¿a nadie se le ha ocurrido el guardar valores de configuración en la SD? Por supuesto la respuesta es que a todo el mundo se le ha ocurrido... pero llegados a este punto... y sobre todo si vienes de otros lenguajes de programación de alto nivel... aparece un pequeño problemilla al tratar de hacer lo que uno quiere con el archivo de configuración...
Los tutoriales que he encontrado sin entrar en el tema de JSON... hablan de leer todo el archivo en un buffer... procesarlo poco a poco como si de chinos se tratase... y eso sólo para leer datos... cuando se trata de modificar un registro específico en el archivo de configuración el proceso es aún más tedioso puesto que hay que leer todo el archivo en un buffer... procesarlo hasta encontrar el valor a modificar... luego una vez eso hecho volcarlo todo el buffer de nuevo a la SD...
Aquí les propongo una forma distinta de hacerlo... algo menos engorrosa y que para los no iniciados creo que les será de gran ayuda...
Pongamos que tenemos un archivo config.ini como el siguiente:
[Creditos]
Autor = trystan4861
FechaCreacion = 2018/04/03
[/Creditos]
[Sensores]
Cantidad = 3
[Sensor 1]
Pin = A0
[/Sensor 1]
[Sensor 2]
Pin = 5
[/Sensor 2]
[Sensor 3]
Pin = 6
[/Sensor 3]
[/Sensores]
Como dice la mayoria de tutos que aparecen al googlear' «acceso a archivos en tarjeta sd en arduino» (o algo por el estio...) si queremos acceder a la parte del archivo que representa el valor del pin del sensor 3... como dije antes, tenemos que o bien leer y luego procesar toooodo el archivo (que no es moco de pavo, más cuando el archivo puede ser de miles de líneas) hasta encontrar lo que necesitamos... o bien saber en qué posición se encuentra dicho dato y hace un seek... (que si no llevas un control exhaustivo del mismo es una tarea faraónica...) para luego leer y procesar a partir de la posición buscada...
Y si lo que queremos en vez de leer un valor es modificar por ejemplo el pin del sensor 2... tenemos que tener todo el archivo en memoria para que tras procesar el valor a modificar y dejarlo como queremos que se guarde, se haga un volcado completo al archivo de configuración...
Por el momento ésta es la única forma que he encontrado para atacar el asunto de los archivos ini...
(Repito sin meternos con temas de JSON)
La verdad me pareció que era un poco... cómo decirlo... ¿arcaico? ¿poco eficiente?... en resumen... no me gustaba ni un pelo el tener que meterme con todo ese tedioso proceso para sacar un simple valor de un archivo de texto en una tarjeta SD de gigas de tamaño...
¿Gigas de tamaño? ¿Es cierto verdad? Ahora son tan baratas las microsd que por cuatro duros (unos 3 euros en realidad en aliexpress) puedes conseguirte tarjetas de hasta 16 gigas... ¿Teniendo taaaanto espacio libre por qué no...? y aquí llegó la idea...
En vez de tener la estructura del config.ini anterior... si extrapolamos el problema para que tanto el nombre del archivo de configuración como las secciones sean directorio y sus subdirectorios... que el nombre de la variable sea un nombre de archivo y el valor de dicha variable en realidad sea todo el contenido del mismo... ¿no tendríamos ya todo solucionado?
Se podría acceder directamente a un valor sin importarnos en qué subsección se encuentre, si tiene una o mil variables de configuración, además, podríamos modificar directamente el contenido de dicha variable sin necesidad de lidiar con el resto del archivo de configuración...
De modo que me puse al tema y creé 2 pequeñas, pero poderosas funciones que solventan el problema presentado de un plumazo... y esas son:
void Write_INI_Value(String INIFile,String KeyPath,String Key,String Value)
{
String FullPath="/"+INIFile+"/"+KeyPath+"/";
SD.mkdir(FullPath);
File MyINI=SD.open(FullPath+Key+".txt",O_CREAT|O_TRUNC|O_WRITE);
MyINI.print(Value);
MyINI.close();
}
String Read_INI_Value(String INIFile,String KeyPath,String Key,String DefaultValue="UNKNOWN_KEY",bool CreateIfNotExists=false)
{
String Value=DefaultValue;
String FullPath="/"+INIFile+"/"+KeyPath+"/";
File MyINI=SD.open(FullPath+Key+".txt",FILE_READ);
if (MyINI)
{
Value="";
while (MyINI.available()) Value+=(char) MyINI.read();
MyINI.close();
}
else if (CreateIfNotExists) Write_INI_Value(INIFile,KeyPath,Key,Value);
return Value;
}
De modo que estas dos funciones te permitirán acceder directamente a un valor específico de nuestro archivo de configuración virtual (recordemos que en realidad es una estructura de directorios en vez de un archivo real en sí)
La primera permite crear una estructura donde se almacenará el valor de una variable de configuración, permitiéndote crear distintos "archivos de configuración" simplemente cambiando el nombre del archivo a través de la variable INIFile... crear múltiples subsecciones usando la barra "/" como separador de subsecciones en la variable KeyPath...
Tal vez algunos piensen que sería mejor en vez de usar directamente mkdir comprobar si existe o no la estructura de directorios antes de hacerlo... pero si existe usas una función, si no existe usas dos... no sé exactamente cuanto tiempo se puede ahorrar, si es que se ahorra algo al comprobar si existe o no antes de tratar de crearlo... pero la función mkdir crea la estructura si no existe... y si existe simplemente devuelve un valor false indicando que no ha sido posible crearla... por algún error, entre ellos el que aparece cuando ya existe dicha estructura...
Para ver cómo interactuar con las funciones simplemente usa este código que te propongo a continuación, la primera vez que se ejecute mostrará un valor "UNKNOWN_KEY" al tratar de acceder a la configuración de la ip del router... a partir de la siguiente ejecución mostrará directamente el valor introducido...
/*
* Aproximación al acceso aleatorio a archivos ini en tarjeta sd para arduino
* by trystan4861 201800403
*
* Pines de la SDcard
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 10
*/
#include <SPI.h>
#include <SD.h>
#define INIFileName "Config"
String valor;
void Write_INI_Value(String INIFile,String KeyPath,String Key,String Value)
{
String FullPath="/"+INIFile+"/"+KeyPath+"/";
SD.mkdir(FullPath);
File MyINI=SD.open(FullPath+Key+".txt",O_CREAT|O_TRUNC|O_WRITE);
MyINI.println(Value);
MyINI.close();
}
String Read_INI_Value(String INIFile,String KeyPath,String Key,String DefaultValue="UNKNOWN_KEY",bool CreateIfNotExists=false)
{
String Value=DefaultValue;
String FullPath="/"+INIFile+"/"+KeyPath+"/";
File MyINI=SD.open(FullPath+Key+".txt",FILE_READ);
if (MyINI)
{
Value="";
while (MyINI.available()) Value+=(char) MyINI.read();
MyINI.close();
}
else if (CreateIfNotExists) Write_INI_Value(INIFile,KeyPath,Key,Value);
return Value;
}
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial); // wait for serial port to connect. Needed for native USB port only
Serial.println("Iniciando SDCard -> ");
if (!SD.begin(10))
{
Serial.println("¡Error al inicializar la SDCard!");
}
else
{
Serial.println("Procesando datos.");
valor=Read_INI_Value(INIFileName,"LAN","RouterIP");
Serial.print("(Primer acceso a la configuración) RouterIP: ");Serial.println(valor);
if (valor=="UNKNOWN_KEY") Write_INI_Value(INIFileName,"LAN","RouterIP","192.168.1.1");
Serial.print("(Segundo acceso a la configuración) RouterIP: ");valor=Read_INI_Value(INIFileName,"LAN","RouterIP");
Serial.println(valor);
valor=Read_INI_Value("SIGNIFICADO","DE/LA","VIDA");
Serial.print("LA RESPUESTA AL SIGNIFICADO DE LA VIDA ES: ");Serial.println(valor);
}
}
void loop() {}