Les Carnets de Byfeel domotique , Objets connectés , DIY , Programmation, Nouvelles Technologies ….

Utilisation de la mémoire SPIFFS sur ESP8266

Maj: 01/08/2020 – le système SPIFFS étant passé en « dépréciation » il faut envisager LittleFS , voir article sur LittleFS

Dans cet article , je vais expliquer comment utiliser la mémoire SPIFFS de votre module ESP8266.

A la suite de cet article vous saurez comment :

  • importer des fichiers dans la mémoire SPIFFS
  • Manipuler les fichiers en lecture et en écriture
  • Comment monter rapidement une interface WEB dynamique
  • Monter un serveur FTP
  • Formater la mémoire.

Chapitre 1 : Utilisation de la mémoire SPIFFS ( cet article )
Chapitre 2 : EEPROM ou SPIFFS

La structure mémoire des modules ESP8266

Lors de mes différents articles , nous avons eu l’occasion de travailler sur la mémoire où est stocké notre sketch. (cette mémoire est lue à chaque démarrage , les variables sont donc réinitialiser avec les valeurs par défaut).

L’espace OTA est un dossier temporaire pour les mises à jour via OTA

EEPROM : Permet de stocker des infos, qui pourront être lues après redémarrage. Nous sommes sur une mémoire plus lente (que la mémoire dédiée au sketch et qui dispose d’une durée de vie limitée en écriture . Ne pas en abuser).

File system : ou mémoire SPIFFS , ce que nous allons étudier, aujourd’hui , est un espace de stockage (que l’on pourrait assimiler à une SDcard ).

Selon les modules utilisés, la taille du fichier systéme peut varier de 64ko à 3Mo.

Comment transférer un fichier dans la mémoire SPIFFS

Pour cela , il suffit d’utiliser la bibliothèque  : FS.h

Tous les détails de cette bibliothèque ici.

Il existe un outil qui permet d’uploader automatiquement , tous les fichiers dans la mémoire SPIFFS depuis l’interface Arduino.

Lien vers outils de téléchargement SPIFFS ( outil FS )

Pour l’installer il faut :

  1. dans votre dossier arduino , créér un dossier tools
  2. décompresser l’outil téléchargé dans le dossier tools . Vous devriez avoir une arborescence sous la forme suivante : <home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar
Structure du dossier tools dans dossier arduino

Re-démarrer l’interface Arduino. Dans le dossier de votre sketch , créér un dossier data . Les fichiers déposés dans ce dossier , seront transférés dans la mémoire SPIFFS.
Par exemple : 

Une fois votre sketch ouvert , dans l’interface Arduino , sélectionner dans le menu outil : ESP8266 Sketch data upload.

téléchargement fichier SPIFFS

Le contenu du dossier data sera transféré dans la mémoire SPIFFS. Ici le fichier test.txt.
Cela peut être plus ou moins long , en fonction de la vitesse d’upload sélectionnée , penser à régler la vitesse d’upload à son maximum . Pour le transfert de vos sketchs , vous la redescendrez. Puis effectuer le transfert de votre sketch de façon habituelle.

Comment accéder à ce fichier

Apres avoir créé un fichier text de test , déposez le dans le dossier data , de notre sketch .

par exemple le fichier texte  : « ceci est un fichier test SPIFFS »

Je vais écrire un sketch , me permettant d’afficher le contenu de ce fichier , dans la console .

/*
 * SPIFFS test
 */
#include    //Include File System Headers

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

  //demarrage file system
  SPIFFS.begin();
  Serial.println("Demarrage file System");
 
  File dataFile = SPIFFS.open("/test.txt", "r");   //Ouverture fichier pour le lire
  Serial.println("Lecture du fichier en cours:");
  
  //Affichage des données du fichier
  for(int i=0;i<dataFile.size();i++)
  {
    Serial.print((char)dataFile.read());    //Read file
  }
  dataFile.close();
}

void loop() {
}

Une fois le sketch uploader , j’ouvre la console et j’obtiens le résultat suivant :

Comment écrire dans un fichier avec SPIFFS

l’écriture est tout aussi simple , que la lecture , il suffit d’ouvrir le fichier en mode écriture avec l’option w.

File f = SPIFFS.open(« /test.txt », « w »);

pour écrire du texte , il suffit d’utiliser la fonction : f.print(« mon nouveau texte »);

Attention toute nouvelle écriture dans le fichier supprime les anciennes données du fichiers.Il faut penser à sauvegarder dans une variable les données du fichier avant d’écrire la nouvelle info.

par exemple par la fonction suivante :

fileTxt = f.readString();

Ci-dessous exemple sketch :

/*  
*** SPIFFS test  
*/ 
#include “FS.h”  //Include File System Headers 
String fileTxt; 
void setup() {   
     Serial.begin(115200);   
     Serial.println();   //demarrage file system   
    SPIFFS.begin();   
    Serial.println("Demarrage file System");     
    File f = SPIFFS.open("/test.txt", "r");   //Ouverture fichier pour le lire 
    Serial.println("Lecture du fichier en cours:"); //Affichage des données du fichier  
    fileTxt = f.readString(); // on recupere le contenu entier du ficher 
    Serial.println(fileTxt);   
    f.close();
    Serial.println(""); 
} 
void loop() {   
// écrire dans fichier test.txt
 File  f = SPIFFS.open("/test.txt", "w");
 f.println(fileTxt);  // on réécrit l'ancien contenu sauvegardé au préalable 
  f.print("Ecriture nouvelle info dupuis sketch :"); // puis les nouvelles infos. 
  f.println(millis());   f.close(); // on ferme le fichier une fois les enregistrements terminés
 // on ouvre de nouveau pour lecture
    f = SPIFFS.open("/test.txt", "r");
    fileTxt = f.readString();
      //Affichage des données du fichier
     Serial.println("affichage de la nouvelle chaine de caractére :");
     Serial.println(fileTxt);
     f.close();   //pause de 80 secondes , afin d'éviter de remplir la mémoire trop vite.Pensez à débrancher votre ESP ou d'uploader un nouveau sketch avant de saturer votre mémoire.
   delay(80000);
 }

Voila ce que l’on observe dans la console :

Pensez a débrancher votre ESP, ou uploader un sketch différent avant de saturer votre mémoire  !!  Car ce programme écrit en permanence dans le fichier txt .

SPIFFS pour quel usage

Au quotidien , à quoi peut servir d’enregistrer des fichiers dans la mémoire SPIFFS.

  • Garder un historique des températures ou d’autres données , par exemple.
  • Lire et sauvegarder un fichier de configuration
  • stocker , les fichiers d’un site WEB ( par exemple , feuille de style , fichier javascript …. )
  • et bien d’autres possibilités encore.

Pour illustrer un de ces usages  , voici comment mettre en place une interface WEB , pour allumer ou éteindre la led interne.

Une interface Web dynamique

Pour cela , je vais récupérer le fichier jquery.mini.js chez Jquery et le déposer dans le dossier data de mon sketch.

A l’aide d’un éditeur de texte , j’écris mon fichier index.html .

<!DOCTYPE html>
<html>
<head>
<cript src="jquery.min.js"></script>
</head>
<body>
<center>
<h1>Tuto SPIFFS + jquery - Byfeel</h1>
    <p>Demo jquery , pour on/off led interne</p>
<div>Temperature : <span id="Temp"></span> </div>
<form action='/setLED' method='POST' id='FormLED'>
<fieldset>
    <legend>LED interne 2 :</legend>
<INPUT type='radio' name='LED' value='on' id='on' checked><label for='on'>ON</label>
<INPUT type='radio' name='LED' value='off' id='off'><label for='off'>OFF</label>
    </fieldset>
    <INPUT type='submit' value='Envoyer' id='bouton_LED'/>
    </form>
    <a href="https://byfeel.info">byfeel.info</a><br>
   <div id="info"></div>
<script>
   $("#FormLED").submit(function(){
    $.ajax({
                url: '/setLED',
                type: 'post',
                data: $("#FormLED").serialize(),
                success: function(data) {
                   $("#info").html("<p> Commende LED : "+data+"</p>");
                }
        });
    return false;
}); 
    
 function loadInfo(){
  $( "#Temp" ).load( "/getInfo");
 }
    
loadInfo(); // premiere execution
setInterval(function(){
    loadInfo() // rappel toutes les 10 secondes
}, 10000);
</script>
</body>
</html>

que je dépose aussi , dans mon fichier data. Ce fichier index , sera ma page de démarrage de mon nouveau serveur WEB. Cette page affiche un simple formulaire , composé de deux boutons radio ( on et off ) et d’un bouton envoyer. Le script Javascript , permet lors de l’appui sur le bouton d’envoyer l’info on ou off  à la page : http://ip_module/setLED .

Afin que cette page soit gérée par mon module , je modifie mon sketch en conséquence.

Script sketch ( pensez à renseigner vos codes WIFI )

/*
 * SPIFFS test
 */

#include 
#include 
#include    //Include SPIFFS



//WiFi Connection configuration
const char *ssid = "Mon-point-WIFI";
const char *password = "Mon-mot-de-passe-wifi";

#define led 2

//***********************************
//************* Gestion du serveur WEB
//***********************************
ESP8266WebServer server(80);

void handleRoot(){
  server.sendHeader("Location", "/index.html",true);   //Redirige vers page index.html sur SPIFFS
  server.send(302, "text/plane","");
}

// fonction de traitement SETLED
void handleLED(){
  String SetLED = server.arg("LED");
  Serial.println(SetLED);
  if (SetLED=="on")  digitalWrite(led, LOW);
  else  digitalWrite(led, HIGH);
  
  server.send(200, "text/plane",SetLED);
}

// fonction permettant de gérer les pages "not found"
void handleWebRequests(){
  if(loadFromSpiffs(server.uri())) return;
  String message = "File Not Detected\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  Serial.println(message);
}

//********** fin serveur web

void setup() {

  Serial.begin(115200);
  Serial.println();

  //démarrage file system
  SPIFFS.begin();
  Serial.println("Demarrage file System");

 // Conexion WIFI
  WiFi.begin(ssid, password);    
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  //Si connexion affichage info dans console
  Serial.println("");
  Serial.print("Connection ok sur le reseau :  ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP()); 

  //Initialize Webserver
  server.on("/",handleRoot);
  server.on("/setLED",handleLED); //pour gerer led
  server.onNotFound(handleWebRequests); //Pour page not found - redirige vers index.html
  server.begin(); 

  pinMode(led, OUTPUT); 

}

void loop() {
 server.handleClient();

}

// gestion du not found.
bool loadFromSpiffs(String path){
  String dataType = "text/plain";
  if(path.endsWith("/")) path += "index.htm";

  if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
  else if(path.endsWith(".html")) dataType = "text/html";
  else if(path.endsWith(".htm")) dataType = "text/html";
  else if(path.endsWith(".css")) dataType = "text/css";
  else if(path.endsWith(".js")) dataType = "application/javascript";
  else if(path.endsWith(".png")) dataType = "image/png";
  else if(path.endsWith(".gif")) dataType = "image/gif";
  else if(path.endsWith(".jpg")) dataType = "image/jpeg";
  else if(path.endsWith(".ico")) dataType = "image/x-icon";
  else if(path.endsWith(".xml")) dataType = "text/xml";
  else if(path.endsWith(".pdf")) dataType = "application/pdf";
  else if(path.endsWith(".zip")) dataType = "application/zip";
  File dataFile = SPIFFS.open(path.c_str(), "r");
  if (server.hasArg("download")) dataType = "application/octet-stream";
  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
  }

  dataFile.close();
  return true;
}

La fonction setLED ressemble à ça :

// fonction de traitement SETLED
void handleLED(){                              // on répond à l’appel de la page
String SetLED = server.arg(« LED »);  // on recupére l’argument
Serial.println(SetLED);                  // info de debug pour la console
if (SetLED== »on ») digitalWrite(led, LOW);   // si On, on allume si Off on éteint
else digitalWrite(led, HIGH);
server.send(200, « text/plane »,SetLED);  // on renvoie la valeur
}

A vous de tester :

la structure de votre dossier ressemble à ça :

Il faut maintenant dans l’ordre :

  • uploader le contenu data , par le menu outil : ESP8266 sketch data upload
  • uploader votre sketch
  • ouvrir la console , pour récupérer l’adresse ip de votre module
  • lancer l’interface WEB , pour obtenir la page ci dessous :

Ce petit exemple a pour but de monter comment mettre en place une interface WEB dynamique , pour s’interfacer avec vos modules.

Le but d’aujourd’hui n’est pas de détailler ce script , mais de montrer comment utiliser la mémoire SPIFFS , pour stocker les pages WEB.

Un serveur FTP

Selon la vitesse sélectionnée , l’upload du dossier data , peut être plus ou moins long . A chaque Upload , l’intégralité de la mémoire est écrite , même pour un petit fichier de quelques Ko. Si mémoire de 3Mo , les 3 Mo seront envoyés.

Par contre lors d’un développement , il devient vite fastidieux de recharger la config complète pour une toute petite modification.

Il existe pour cela , une bibliothèque , qui permet d’émuler un serveur FTP , sur le SPIFFS.

ESP8266FTPServer de nailbuster .

Télécharger cette bibliothèque , et insérez la dans votre bibliothèque Arduino.

Ce serveur FTP est encore en développement , mais il permet un accès rapide à la mémoire SPIFFS pour des petites modifications sur les fichiers. Il y a encore pas mal de bug , mais cela fonctionne très bien pour le transfert des fichiers.

Ci-dessous le sketch , pour utilisation du serveur FTP.

/*
 * SPIFFS test
 */

#include 
#include 
#include    //Include SPIFFS

// FTPserver
#include "ESP8266FtpServer.h"



//WiFi Connection configuration
const char *ssid = "MON-AP-WIFI";
const char *password = "MOT-de-Passe";

#define led 2

// on crée l'instance FTP
FtpServer ftpSrv;

//***********************************
//************* Gestion du serveur WEB
//***********************************
ESP8266WebServer server(80);

void handleRoot(){
  server.sendHeader("Location", "/index.html",true);   //Redirige vers page index.html sur SPIFFS
  server.send(302, "text/plane","");
}

// fonction de traitement SETLED
void handleLED(){
  String SetLED = server.arg("LED");
  Serial.println(SetLED);
  if (SetLED=="on")  digitalWrite(led, LOW);
  else  digitalWrite(led, HIGH);
  
  server.send(200, "text/plane",SetLED);
}

void handleWebRequests(){
  if(loadFromSpiffs(server.uri())) return;
  String message = "File Not Detected\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  Serial.println(message);
}

//********** fin serveur web

void setup() {

  Serial.begin(115200);
  Serial.println();

 // Conexion WIFI
  WiFi.begin(ssid, password);    
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  //démarrage file system
  if (SPIFFS.begin()) {
    Serial.println("Demarrage file System");
    ftpSrv.begin("esp8266","pass");   // demarrage serveur ftp avec user = esp8266 et mdp = password
  }
  else Serial.println("Erreur file System");
  
  //Si connexion affichage info dans console
  Serial.println("");
  Serial.print("Connection ok sur le reseau :  ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP()); 



  //Initialize Webserver
  server.on("/",handleRoot);
  server.on("/setLED",handleLED); //Rpour gerer led
  server.onNotFound(handleWebRequests); //Set setver all paths
  server.begin(); 

  pinMode(led, OUTPUT); 

}

void loop() {
 server.handleClient();
 ftpSrv.handleFTP();

}

// gestion du not found.
bool loadFromSpiffs(String path){
  String dataType = "text/plain";
  if(path.endsWith("/")) path += "index.htm";

  if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
  else if(path.endsWith(".html")) dataType = "text/html";
  else if(path.endsWith(".htm")) dataType = "text/html";
  else if(path.endsWith(".css")) dataType = "text/css";
  else if(path.endsWith(".js")) dataType = "application/javascript";
  else if(path.endsWith(".png")) dataType = "image/png";
  else if(path.endsWith(".gif")) dataType = "image/gif";
  else if(path.endsWith(".jpg")) dataType = "image/jpeg";
  else if(path.endsWith(".ico")) dataType = "image/x-icon";
  else if(path.endsWith(".xml")) dataType = "text/xml";
  else if(path.endsWith(".pdf")) dataType = "application/pdf";
  else if(path.endsWith(".zip")) dataType = "application/zip";
  File dataFile = SPIFFS.open(path.c_str(), "r");
  if (server.hasArg("download")) dataType = "application/octet-stream";
  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
  }

  dataFile.close();
  return true;
}

il y a quatre lignes qui ont été rajoutées :

  • #include « ESP8266FtpServer.h »
  • FtpServer ftpSrv;  ( pour créer l’instance ftp )
  • ftpSrv.begin(« esp8266″, »pass »);   Dans fonction setup() , pour démarrer le serveur avec utilisateur esp8266 et mdp : pass
  • ftpSrv.handleFTP(); dans la boucle loop , pour écouter les requêtes FTP.

Le serveur FTP  n’est pas compatible avec tous les clients FTP . J’ai testé avec filezilla , cela fonctionne , mais il faut faire attention aux paramètres suivants :

parametre ftp filezilla

Pas de mode sécurisé , ni support d’aucun cryptage et une connexion simultanée uniquement.

Ce n’est pas indispensable , mais lors des phases de développement , c’est quand même plus pratique. Cela permet d’apporter rapidement des retouches aux fichier index.html , pour les test .

Formater l’espace SPIFFS

Tous les fichiers envoyés dans l’espace SPIFFS , reste présent . Pour effacer complètement la partie SPIFFS , il existe une commande .

SPIFFS.format();

à utiliser aprés la commande SPIFFS.begin();

Enregistrer un fichier de config en memoire EEPROM ou SPIFFS , pour en savoir plus consulter mon dernier article.

8 commentaires sur “Utilisation de la mémoire SPIFFS sur ESP8266”

Les commentaires sont fermés.