Funcionalidad de SPIFFS.

Funcionalidad de SPIFFS.

QUÉ ES SPIFFS.

Podemos decir que SPIFFS es un sistema de archivos destinado a dispositivos flash SPI NOR embebidos. Vamos a aprender en detalle cómo utilizar el SPIFFS (del inglés Serial Peripheral Interface Flash Fail System), como configurar nuestro IDE de Arduino y así, poder cargar archivos en el ESP8266, aunque el sistema de archivos flash SPIFFS se almacena en el mismo chip que el boceto, la programación de un nuevo boceto no modificará el contenido del sistema de archivos SPIFFS. Esto permite utilizar el sistema de archivos para almacenar datos del boceto, archivos de configuración o contenido para el servidor web.

Fig. 1

Lo que viene a decir es que el sistema flash SPI dispone como de un disco duro interno, y como tal se puede particionar el sistema, de modo que vamos a explicar como acceder a este disco interno, para leer/guardar, particionar y borrar datos en él. No iremos más allá del almacenamiento SPIFFS en este artículo.

Por supuesto, usted puede escribir un código para leer y escribir los datos de una manera más accesible, aunque es incómodo y propenso a errores. Especialmente si los datos almacenados tienen formatos diferentes, digamos que una parte es un objeto JSON y la siguiente es HTML, deberá crear una tabla de contenido (TOC) y realizar un seguimiento de los tamaños y puntos de inicio de estos bloques.

Fig. 2

De manera que los ESP8266 nos permiten actuar sobre dicho disco, cuyo tamaño varía de un modelo a otro (los modelos más antiguos, no ofrecen), se trata de un banco de memoria flash del sistema que podemos particionar y así usarlo para almacenar nuestro código como un sistema de archivos. El sistema, archiva datos que no cambian con frecuencia, como configuración de sensores, páginas web, configuración del boceto, etc.

Bueno, en algún sitio oí decir con cierta razón: «tampoco es bueno morder más de lo que puedas masticar«. Quiero decir que, estamos empezando y lo propio sería realizar una presentación y algún ejemplo que nos de un resultado y nos aliente a seguir entrando en el tema del SPIFFS. Así que, brevemente veremos cómo usar el SPIFFS, cuanto flash tiene un dispositivo y configuraremos el IDE de Arduino para cargar nuestros archivos en el ESP8266.

Necesitaremos un IDE Arduino 1.6.9 o mayor, ya vimos como configurar el IDE de Arduino también debería tener instalado en su sistema el python2.7. Escoja un ESP8266-01 y prepárese para realizar con un poco de paciencia, una práctica con la que reforzar los conocimientos que adquiera en este artículo.

En primer lugar, es conveniente y necesario saber cuánta memoria tiene nuestro flash SPI. Esto puede ser deducido en la hoja de datos del ESP8266-01. Conociendo esto, primero debemos decidir aproximadamente la cantidad del flash que deseamos darle a los SPIFFS.

Para cargar sus bocetos en el SPIFFS del ESP8266, es aconsejable seguir estos pasos:

  • Asegúrese de tener un subdirectorio dentro de su directorio de boceto llamado ‘data’,
  • Coloque los archivos que desea cargar en el directorio ‘data’
  • Vaya al menú ‘Herramientas’, seleccione el dispositivo ESP8266 correcto y elija el ‘Tamaño de Flash’ que necesita en el SPIFFS, es decir ‘2Mb (1Mb SPIFFS’). Vea la tabla 1.
  • Asegúrese de que el cuadro de diálogo del monitor serie esté cerrado.
  • Prepare el dispositivo ESP8266 (ESP8266-01) para cargar.
  • Mantenga pulsado ‘Flash’,
  • Pulso ‘Reset’
  • Lanzamiento ‘Flash’
  • Desde el menú Herramientas, seleccione ‘ESP8266 Sketch Data Upload’ Vea la foto 3 arriba de la imagen
  • Una vez finalizada la carga. La ventana del mensaje IDE de Arduino mostrará 100% de carga. Ver foto 3 abajo de la imagen.

Si usted utiliza las versiones ESP8266-01, ESP 05, ESP 07, etc. para su comodidad, puede utilizar la herramienta que se describe en este artículo.

LIBRERÍAS.

La librería ESP8266FS es una herramienta que se debe integrar en el IDE de Arduino, con lo que se añadirá una opción en el menú Herramientas, con el que podremos cargar los archivos de la carpeta ‘data’ de nuestro boceto, en la partición SPIFFS del ESP8266.

Ahora tenemos que descargar la librería ESP8266FS desde:
https://github.com/esp8266/arduino-esp8266fs-plugin/releases/…  o desde
https://github.com/esp8266/arduino-esp8266fs-plugin/releases/…

Sigamos, descomprima el ZIP, vaya a C:\Program Files\Arduino\tools (en mi caso), y descargue la carpeta ESP8266FS o si lo prefiere, cree en la librería una carpeta con el nombre ESP8266FS y dentro otra carpeta con el nombre tool y dentro, descargue el archivo esp8266fs.jar, debe quedar algo así, C:\Program Files\Arduino\tools\ESP8266FS\tool\esp8266fs.jar.

Fig. 3

Hago hincapié, el software que debemos utilizar es el IDE Arduino 1.6.9 o superior. Ahora sólo tenemos que reiniciar el ID de Arduino e iremos al menú Herramientas y nos mostrará una ventana similar a esta:

Fig. 4

Los archivos que queremos transferir en el área SPIFFS desde el IDE de Arduino, los depositamos en la carpeta ‘data’ de nuestro proyecto es decir, por ej.:

/SPIFFsUsage/
	data/
	   filename.txt

Esta herramienta carga cualquier archivo que se encuentre dentro de una carpeta denominada «data«. La carpeta data debe estar dentro de la carpeta de su boceto, dentro de la cual estará el archivo filename.txt, si no existe dicho archivo, se creará.

Atención: La acción que vamos a realizar, borrará todo el contenido de SPIFFS del ESP8266 y cargará los datos contenidos en la carpeta data. No se conservan los archivos que hubiera originales / anteriores, por lo que si necesita dichos datos, debería hacer una copia antes de cargar los nuevos datos de la carpeta data, sin embargo no afectará al boceto.

FUNCIONES DE LA LIBRERÍA SPIFFS

Veamos cómo usar SPIFFS en nuestro programa con Arduino para ESP8266. Aquí, sólo veremos alguna de las funciones que se pueden utilizar con esta librería:

  • Nuestro boceto, debe incluir entre otras la librería «FS.h«. Ésta librería «FS.h» no se puede utilizar junto a la librería SD ya que actualmente son incompatibles.
  • Para iniciar el uso de SPIFFS deberemos incluir: SPIFFS.begin(); si funciona correctamente devolverá true.
  • De igual manera cuando queramos dejar de utilizarlo usaremos la función SPIFFS.end();.
  • Podemos formatear la unidad con: SPIFFS.format(); Si queremos guardar/borrar datos del programa. Esta función puede ser llamada antes o incluso después de begin().
  • También podemos hacer particiones. Para saber más sobre las particiones, lea
  • Para asegurarnos que existe un archivo usaremos: SPIFFS.exists(path);. Devolverá true si el archivo existe. Recordar que el path es el nombre completo, no un archivo dentro de una carpeta.
  • También hay un filtro que utiliza algo parecido a carpetas: SPIFFS.openDir(path, mode);. Abre un fichero, path debe ser un camino absoluto comenzando con un slash (p.ej. /dir/filename.txt), mode es una letra que especifica el modo de acceso. Puede ser una de las siguientes: rwar+w+a+, que se puede usar así:
  • La secuencia se coloca en el comienzo del archivo.

r -Abre un fichero de texto para leerlo.
r+ -Abre un fichero para lectura y escritura.
w Trunca en fichero con una longitud cero o crea un fichero
de texto para escritura.
w+ -Abre para lectura y escritura. El fichero si no existe, se
crea de lo contrario se trunca.
a -Abre el fichero para añadir (escribiendo al final del
fichero). El fichero si no existe, se crea.

a+ -Abre el fichero para añadir (escribiendo al final del
fichero). El fichero si no existe se crea.
La posición inicial para lectura es al comienzo del fichero,
pero la salida siempre se añade al final del fichero.

.

  • El objeto directorio Dir
    El propósito del objeto Dir es iterar sobre los ficheros dentro del directorio. Provee los métodos:

Dir dir = SPIFFS.openDir(«/data»);
while (dir.next()) {
  Serial.print(dir.fileName()); // Nombre del archivo
  File f = dir.openFile(«r»); // Abrir archivo
  Serial.println(f.size()); // Tamaño, se vera más tarde.
  if (dir.fileSize()) {
  File f=dir.openFile(‘r’);
  Serial.println(f.size());
 }
}

  • Muestra información del sistema de archivos:

FSInfo fs_info;
SPIFFS.info(fs_info);

  • Si necesitamos eliminar un sólo fichero usaremos:

SPIFFS.remove(path);    Devuelve true si ha sido correctamente eliminado

  • Si queremos renombrar un fichero usaremos:

SPIFFS.rename(pathFrom, pathTo); Devuelve true si ha sido correctamente cambiado de nombre.
SPIFFS_rename        – Para renombrar archivos.
SPIFFS_clearerr       – Para borra el último error.
SPIFFS_info                – Devuelve información sobre bytes usados ​​y totales en fs.
SPIFFS_format          – Para formatear el sistema de archivos.
SPIFFS_mounted      – Comprueba si el sistema de archivos está montado.

  • Para obtener información del sistema de archivos usaremos:

FSInfo fs_info;
SPIFFS.info(fs_info);

Donde la estructura FSInfo es:

struct FSInfo {
size_t totalBytes; // Tamaño total utilizable en el sistema de archivos en bytes
size_t usedBytes; // Bytes usados en el sistema de archivos
size_t blockSize; // Tamaño del bloque, en bytes
size_t pageSize; // Tamaño de la página, en bytes
size_t maxOpenFiles; // Num. máximo de archivos simultáneamente abiertos.
size_t maxPathLength; // Tamaño máximo del path, incluyendo el ‘\0’ final. Normalmente debería ser 32.
};

Otra cualidad del flash ESP8266 es que también admite actualizaciones OTA, no obstante, en este artículo nos limitaremos a la funcionalidad de SPIFFS.

ESTRUCTURA DEL SISTEMA DE ARCHIVOS

Sólo a titulo de ejemplo: «En primer lugar, necesitamos saber cuánta memoria tiene el flash SPI, su tamaño de página o tamaño de página físico y su/s sector/es de bloque (es decir, tamaño de bloque físico). Esto puede ser deducido en la hoja de datos. Teniendo esto, primero se debe decidir aproximadamente la cantidad del flash SPI que deseamos darle a los SPIFFS.

Digamos que su flash tiene tamaños de bloque físico de 4kB, 16kB y 64kB. Entonces puede tener tamaños de bloque lógicos de n * 4kB, o n * 16kB, o n * 64kB, donde n es un número entero. La opción más común es establecer el tamaño del bloque lógico en la misma cantidad que un tamaño de bloque físico. El tamaño del sistema de archivos debe ser un múltiplo entero del tamaño de bloque lógico«.

Tabla 1

Como se puede apreciar hay materia que estudiar, por lo que si está interesado usted puede adquirir los conocimientos que estime siguiendo las referencias que se muestran más abajo.

Tabla 2

Para más información, consulte la wiki para configurar spiffsintegrar spiffsusar spiffs y optimizar spiffs.

El sistema de archivos tiene unas limitaciones que debemos tener en cuenta:

  • La longitud máxima de la ruta de archivos es 31 caracteres.
  • No admite directorios. Sin embargo, podemos usar «/» dentro del nombre de archivo («/proyecto/boceto1.txt» sería un nombre completo, no un archivo dentro de la carpeta «proyecto»).

Como vemos SPIFFS no soporta directorios, sólo almacena una lista ‘plana’ de ficheros. Por el contrario que un sistema de ficheros tradicional, el carácter (slash) ‘/’ está permitido en los nombres de ficheros, de modo que las funciones que se ocupan de listar directorios por ejemplo, openDir(«/website»), básicamente solo filtra los nombres de archivo y conserva los que comienzan con el prefijo solicitado ( /website/).

La longitud máxima de la ruta de archivos es 31 caracteres, este límite se alcanza fácilmente y si se ignora, los problemas podrían pasar desapercibidos porque no aparecerá ningún mensaje de error en la compilación ni en el tiempo de ejecución. Se recomienda mantener los nombres de archivo cortos y no usar directorios anidados profundamente. Por ejemplo, el nombre de archivo /website/images/bird_thumbnail.jpg, tiene 34 caracteres y causará problemas si se utiliza, por ejemplo en exists()

CUÁNTA FLASH TIENE MI ESP8266.

Es tanta la variedad de suministradores de los dispositivos ESP8266, a parte que no están disponibles los datos de estos productos y por tanto no son claros ni concisos. He estado leyendo que hay que averiguar qué tamaño tiene nuestro flash y que existe un método bastante fiable y rápido consistente en cargar el dispositivo con el boceto CheckFlashConfig.ino y revisar la salida del monitor serie como se muestra en la imagen de la figura 8.

El código CheckFlashConfig.ino es un ejemplo que viene en la librería que nos permitirá conocer los valores del ID flash realtamaño del Flash ide, la velocidad Flash ideFlash ide mode y si la Configuración del chip flash ok. Abra el monitor serie para ver los valores que tiene su ESP.

Al compilar el boceto CheckFlashConfig.ino, puede que obtenga usted algún error, si es así, observe la siguiente figura.

Fig. 5

Revise en el Menú/Herramientas/Placa… la versión de la tarjeta, debe ser la versión 2.6.0 (no la 2.5.0).

Fig. 6

Modifique su tarjeta elegida y compile, esta vez sin error.

Fig. 7

Ahora, ya puede subir su boceto CheckFlashConfig.ino y abra su monitor serie, para comprobar el tamaño de su SPIFFS.

Fig, 8

Para alcanzar un conocimiento optimo, siga la regla: prueba, ensayo error y prueba.

PONER EN USO.

  • Abra un boceto (o cree uno nuevo y guárdelo).
  • Vaya al directorio de bocetos (elija Boceto> Mostrar carpeta de bocetos).
  • Cree un directorio con el nombre data con los archivos que desee en el sistema.
  • Asegúrese de haber seleccionado una placa, un puerto y el monitor serie cerrado.
  • Seleccione Herramientas> ESP8266 Elemento de menú Cargar datos de croquis .
    Ya debería comenzar a cargar los archivos en el sistema de archivos flash ESP8266.
    Cuando haya terminado, la barra de estado IDE mostrará el mensaje de carga de
    imagen SPIFFS. Puede tardar unos minutos para tamaños de sistema de archivos grandes.

VEAMOS UNOS EJEMPLOS.

Como ya se ha dicho deberemos incluir un archivo por ejemplo file.txt con los datos que necesitemos subir, dentro de una carpeta con el nombre data.

Aunque los ejemplos que se describen en este artículo hacen referencia al ESP8266-01, funcionan igualmente con los ESP-07, ESP8266-12, ESP8266-12E, ESP8266-13, etc.

EJEMPLO DE UTILIDAD SPIFFS.

Leer y escribir en un archivo.

/*leer_escribir_spiffs.ino
El código intenta primero abrir un archivo llamado f.txt. 
Si esto tiene éxito, leerá línea por línea e imprimirá el 
resultado en la consola serie. Si no puede abrir el archivo, 
probablemente no exista todavía. El código creará el 
archivo y lo llenará con algunos pares de valor / clave 
imaginarios, un par por línea. 
*/

#include "FS.h" // incluir librería FS.h

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

// Siempre usa esto para "montar" el sistema de archivos
bool result = SPIFFS.begin();
Serial.println("SPIFFS opened: " + result);

// esto abre el archivo "prueba.txt" en modo lectura
File f = SPIFFS.open("/prueba.txt", "r");

if (!f) {
  Serial.println("File doesn't exist yet. Creating it");

// abre el archivo en modo de escritura
 File f = SPIFFS.open("/prueba.txt", "w");
 if (!f) {
  Serial.println("file creation failed");
 }
// ahora escribe dos líneas en estilo de clave/valor con caracteres de fin de línea
  f.println("ssid=abc");
  f.println("password=123455secret");
} else {
// podríamos abrir el archivo.
 while(f.available()) {
// Permite leer línea por línea desde el archivo
  String line = f.readStringUntil('\n');
  Serial.println(line);
 }

  }
  file.close();
 }

void loop() {
  // nada más que hacer por ahora, esto es sólo una simple prueba
}

El siguiente es otro ejemplo más elaborado que, muestra como leer y escribir en un archivo.

EJEMPLO DE LECTURA Y ESCRITURA DE ARCHIVOS SPIFFS.

// EJEMPLO DE LECTURA Y ESCRITURA DE ARCHIVOS
//
// El boceto se adjunta aquí, tal vez sea de utilidad para cualquiera.
// Este boceto Spiffs es muy básico, escribe 10 cadenas en 
// el sistema de archivos SPIFFS, y luego los lee de nuevo
// Para doc. SPIFFS ver:
// https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md
// Compilar en Arduino 1.6.7. Funciona bien en el tablero Wemos D1 ESP8266.

#include "FS.h"

void setup() {
Serial.begin(9600);
Serial.println("Ejemplo de SPIFFS muy básico, escribe 10 líneas en el sistema de archivos y luego las lee\n");
SPIFFS.begin();
//Las siguientes líneas se deben hacer SOLAMENTE UNA VEZ !!!!!
//Cuando SPIFFS está formateado UNA VEZ, puede comentar estas líneas!!
Serial.println("Espere 30 segundos para que se formatee el SPIFFS"); 
SPIFFS.format();
Serial.println("Spiffs formateado"); 
}

void loop() {

// abrir archivo para escribir
File f = SPIFFS.open("/f.txt", "w");
if (!f) {
Serial.println("Fallo apertura de archivo"); 
}
Serial.println("====== Escribiendo en el archivo SPIFFS =========");
// write 10 strings to file
for (int i=1; i < =10; i++){
f.print("Millis() : ");
f.println(millis());
Serial.println(millis());
}

f.close();

// open file for reading
f = SPIFFS.open("/f.txt", "r");
if (!f) {
Serial.println("Fallo apertura de archivo"); 
} Serial.println("====== Leyendo del archivo SPIFFS ======="); 
// write 10 strings to file
for (int i=1; i< =10; i++){
String s=f.readStringUntil('\n');
Serial.print(i);
Serial.print(":");
Serial.println(s);
}

// Espera unos segundos antes de volver a hacerlo.
delay(3000);
} 

El siguiente boceto nos muestra un caso en el que nos hemos visto alguna vez, cuando hemos perdido la conexión del suministro eléctrico, perdiendo el último estado o valor de una función o un simple pulsador. En este caso, se recuperará el estado en que ha quedado un LED en el momento de perder por algún motivo la energía.

EJEMPLO PRÁCTICO DE UTILIDAD SPIFFS.

Muestra como recuperar el último estado de un LED, después del reinicio.

/* estado_led_memoria.ino
Código de https://www.filipeflop.com/

El primer intento se utiliza en un archivo para guardar el estado on/off 
de un LED. Presionando el botón podemos cambiar el estado. Incluso si la 
energía se apaga, el estado del LED se guardará en la memoria. Cuando 
conectamos nuevamente la placa, el sistema busca el estado guardado en la
memoria y actualiza el estado del LED.

Es importante notar que el programa debe grabarse dos veces. La primera 
vez la línea 62 ( escritura de archivo ( "off", "/state.txt") ) debe ser 
sin comentar, creando un archivo en la memoria. En la segunda y demás 
programaciones, la línea debe ser comentada, pues el archivo ya existirá 
en la memoria.
*/

#include "FS.h"

int led = D1;
int llave = D0; 

/**
* escribe contenido en un archivo
* string state - contenido a escribir en el archivo
* string path - archivo que se va a escribir
*/

void writeFile(String state, String path) {
 //Abre el archivo para escribir ("w" write))
 //Sobreescribe el contenido del archivo
 File rFile = SPIFFS.open(path,"w+");
 if(!rFile){
  Serial.println("Error al abrir archivo!");
 } else {
  rFile.println(state);
  Serial.print("grabó estado: ");
  Serial.println(state);
 }
 rFile.close();
}

/**
* el contenido de un archivo
* param cadena path - archivo que se va a leer
* return string - contenido leído del archivo
*/

String readFile(String path) {
 File rFile = SPIFFS.open(path,"r");
 if (!rFile) {
  Serial.println("Error al abrir archivo!");
 }
 String content = rFile.readStringUntil('\r'); 
 Serial.print("lectura de estado: ");
 Serial.println(content);
 rFile.close();
 return content;
}

/**
* @desc inicializa el sistema de archivos
*/

void openFS(void){
 //Abre el sistema de archivos
 if(!SPIFFS.begin()){
  Serial.println("\nError al abrir el sistema de archivos");
 } else {
  Serial.println("\nSistema de archivos abierto con éxito!");
 }
}

void setup() {
 pinMode(led, OUTPUT);
 pinMode(llave, INPUT); 
 Serial.begin(9600);
 openFS();

// en el primer upload de programa el archivo state.txt se debe crear con el contenido "off"
// en el segundo upload la línea debe ser comentada.
// writeFile("off", "/state.txt"); // esta línea debe ser comentada

// comprueba el último estado del LED y se activa de acuerdo
 String state = readFile("/state.txt");
 if(state == "on") {
  digitalWrite(led, HIGH);
 }
 else if(state == "off") {
  digitalWrite(led, LOW);
 }
}

void loop() {
/*
* comprueba el estado anterior que salvo en el archivo,
* activa el LED de acuerdo y escribe el nuevo
* estado del archivo.
*/
 if(digitalRead(llave) == LOW) // chave
 {
  String state = readFile("/state.txt");
  if(state == "off")
  {
  writeFile("on", "/state.txt");
  digitalWrite(led, HIGH);
 }
 else if(state == "on")
 {
  writeFile("off", "/state.txt");
  digitalWrite(led, LOW);
 }
  while(digitalRead(llave) == LOW); 
 }
}

En el boceto, lo primero es incluir la librería FS.h y en este caso asignar los pines que vamos a utilizar como son:

 D0 como salida para manejar el LED y 
 D1 como entrada para el pulsador que actuará con el LED

Considero que está bastante documentado y es bastante explicito el código. Se comprende que tenemos que subir el código una primera vez descomentada, la línea:

writeFile("off", "/state.txt");

Para que se guarde el estado ‘off’ en la memoria flash. De modo que la segunda vez que subamos el código, dicha línea la debemos comentar.

// writeFile("off", "/state.txt");

Si todo ha ido bien y así lo espero, ya podemos comprobar el efecto que hemos subido con este código. Primero, podemos presionar el pulsador y en cada pulsación se enciende o apaga el LED, al pulsar de nuevo se encenderá, desconectemos la energía quitando el cable USB o batería, con lo cual se apagará el LED y si volvemos a conectar el cable USB o la alimentación, veremos que el LED vuelve a encenderse, que es el estado que tenía al quitar la energía.

Vídeo Carga de datos en SPIFFS

En el vídeo se aprecia lo descrito, en esta práctica he guardado el estado del LED encendido, por que se aprecia mejor que se ha guardado, ya que al volver a darle energía el LED aparecerá encendido, usted puede guardar otra información que se adapte a sus necesidades como datos de sensores, datos para una base de datos, hojas excel, etc.

Esto es todo, por este simple tutorial, espero sus preguntas. No haga malas prácticas, sin insultos ni faltas de respeto, sea positivo.

REFERENCIAS:

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.