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

Esp8266 : Système de fichier LittleFS

Un article pour expliquer comment utiliser le système de fichier LittleFS , afin de remplacer SPIFFS.

Le systéme de fichier SPIFFS est actuellement obsolète et sera supprimé dans les futures versions du noyau ESP8266. Il va falloir envisager de déplacer le code vers LittleFS

SPIFFS n’est plus pris en charge par le développeur , tandis que LittleFS est en développement actif, prend en charge de vrais répertoires et est parfois plus rapide.

Avant de commencer un petit rappel sur les deux systémes de fichiers existant : SPIFFS et LittleFS.

SPIFFS est le système de fichiers d’origine et est idéal pour les applications limitées en mémoire. SPIFFS consomme peu de ressources et est surtout orienté sur les petits fichiers . La prise en charge des répertoires n’est pas réelle.

LittleFS a été récemment ajouté et se concentre sur des performances et une prise en charge des répertoires plus élevées, mais a une taille de fichier minimum plus élevé (4K minimum par rapport à l’unité d’allocation de fichiers minimums de 256 octets de SPIFFS). Ce qui implique une occupation mémoire plus importante, surtout s’il y a de nombreux petits fichiers.

Ils partagent une API compatible mais ont une structure de fichiers différente . Le fait de passer d’un type à un autre entrainera une opération de formatage et ne conservera aucun fichier.

Utilisation de LittleFS

Comme expliqué ci-dessus , l’utilisation de LittleFS est très proche de celle de SPIFFS. Ci-dessous quelques exemples des commandes les plus utilisés

  • Appel de la bibliothéque : #include « LittleFS.h »
  • Monter le système de fichier :
LittleFS.begin()
  • Démonter le système de fichier
LittleFS.end()
  • Ouverture d’un fichier : Le chemin ( path ) sous la forme /filename.txt ou /dir/filename.txt
    Les modes sont : r,w,a,r+,w+ et a+
LittleFS.open(path, mode)
  • r Ouvre un fichier pour lecture. Le pointeur est positionné au début du fichier
  • r+ Ouvre pour lecture et écriture. Le pointeur est positionné au début du fichier
  • w Ecris nouveau fichier , si fichier existe l’ancien est supprimé. Le pointeur est positionné au début du fichier
  • w+ Ouvre pour lire et écrire. Le fichier est créé si il n’existe pas , ou écris par desus si présent. Le pointeur est positionné au debut du fichier
  • a Ouvre pour ajout en fin de fichier.Le fichier est créé si il n’existe pas. Le pointeur est positionné en fin de fichier.
  • a+ Ouvre pour lecture et ajout en fin de fichier. Le fichier est créé si il n’existe pas . Le pointeur est positionné au début pour lecture , mais à la fin pour l’ajout .

Vous trouverez plus d’informations sur la documentation officielle : ARDUINO ESP8266 ou sur la documentation dédié à la plateforme : PlatformIO

Pour expliquer comment utiliser LittleFs , il suffit de relire les deux articles que j’ai écrits sur SPIFFS . Les commandes étant très proches ce qui valable pour SPIFFS fonctionne aussi pour LittleFS.

Ces deux articles expliquent comment utiliser la mémoire flash et le système de fichier pour les modules ESP8266.

Adapter un code : de LittleFS à SPIFFS

Ces deux bibliothèques étant très proche l’une de l’autre , il est assez simple d’adapter son sketch , afin que celui ci prenne en compte la nouvelle bibliothèque LittleFS.

Reprenons l’exemple du sketch , dans l’article « EEPROM ou SPIFFFS » , dont voici le code complet:

//***************************
//* TUTO  enregistrement / lecture fichier config dans SPIFFS
//************************
// bibliothèque
#include <FS.h>   //Include SPIFFS
#include <ArduinoJson.h>

// ma structure config
struct sConfig {
  char nom[10];
  int NumeroPIN;
  bool defaut;
};
sConfig config;

// chemin fichier config dans memoire flash ( SPIFFS )
const char *fileconfig = "/config/config.json";  // fichier config
//taille buffer pour json
const size_t capacityConfig = 2*JSON_ARRAY_SIZE(3);

void chargeFS() {
  File file = SPIFFS.open(fileconfig, "r");
   if (!file) {
     //fichier config absent
     Serial.println("Fichier Config absent");
    }
  DynamicJsonDocument docConfig(capacityConfig);
  DeserializationError err = deserializeJson(docConfig, file);
  if (err) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(err.c_str());
  }

// affectation des valeurs , si existe pas on place une valeur par defaut
strlcpy(config.nom,docConfig["nom"] | "byfeel",sizeof(config.nom));
config.NumeroPIN = docConfig["pin"] | 10;
  config.defaut = docConfig["defaut"] | true;
 //fermeture fichier
  file.close();
}

void sauveFS() {
  String jsondoc="";
  DynamicJsonDocument docConfig(capacityConfig);
  docConfig["nom"]=config.nom;
  docConfig["pin"]=config.NumeroPIN;
  docConfig["defaut"]=config.defaut;

File  f = SPIFFS.open(fileconfig, "w");
  if (!f) {
    Serial.println("Fichier Config absent - création fichier");
   }
   serializeJson(docConfig,jsondoc);
   f.print(jsondoc);  // sauvegarde de la chaine
   f.close();
   Serial.print(jsondoc);
}

void setup() {
// affichage dans moniteur serie
Serial.begin(57600);
//montage du disque flash
SPIFFS.begin();
// charge dans un premier temps le fichier config
chargeFS();

// affiche les variables config apres lecture FS
Serial.print("valeur Nom : ");
Serial.println(config.nom);
Serial.println("Valeur PIN : "+String(config.NumeroPIN));
Serial.println("valeur defaut : "+String(config.defaut));

//modif donnée PIN
config.NumeroPIN=5;
// sauve fichier
sauveFS();

// lit de nouveau le fichier
chargeFS();

// affiche les variables config apres lecture du nouvel enregistrement
Serial.print("valeur Nom : ");
Serial.println(config.nom);
Serial.println("Nouvelle Valeur PIN : "+String(config.NumeroPIN));
Serial.println("valeur defaut : "+String(config.defaut));

// Bonus : info sur memoire SPIFFS pour DEBUG
FSInfo fsInfo;
// info fs
SPIFFS.info(fsInfo);
//fsInfo.totalBytes
float total=(fsInfo.totalBytes/1024);
Serial.println("info totalbytes : "+String(total)+" ko");
Serial.println("info usedbytes : "+String(fsInfo.usedBytes));
Serial.println("info blocksize : "+String(fsInfo.blockSize));
Serial.println("info pagesize: "+String(fsInfo.pageSize));
Serial.println("info maxOpenFiles : "+String(fsInfo.maxOpenFiles));
Serial.println("info maxPathLength : "+String(fsInfo.maxPathLength));

}
void loop() 
{   
}

Ce code explique comment enregistrer une structure au format json , pour une sauvegarde de configuration par exemple , et comment lire ce fichier . De plus il affiche aussi les informations de la structure FS ( vis fsInfo ).

Pour adapter le code , il suffit de remplacer quelques lignes :

L’appel de la bibliothéque :

//***************************
//* TUTO  enregistrement / lecture fichier config dans SPIFFS
//************************
// bibliothèque
// #include <FS.h>   //Include SPIFFS
#include "LittleFS.h" // LittleFS bibliotheque
#include <ArduinoJson.h>
.....

Puis dans les fonctions chargFS et sauveFS l’appel de la fonction open , je remplace SPIFFS par LittleFS

void chargeFS() {
  // File file = SPIFFS.open(fileconfig, "r");
  File file = LittleFS.open(fileconfig, "r");
......
.....
.....
et dans sauve FS
.......
..........
//File  f = SPIFFS.open(fileconfig, "w");
File  f = LittleFS.open(fileconfig, "w");
.............

Dans la fonction setup()

........
void setup() {
// affichage dans moniteur serie
Serial.begin(57600);
//montage du disque flash
//SPIFFS.begin();
LittleFS.begin();
.......

et la partie lecture des information de la structure FS :

// info fs
//SPIFFS.info(fsInfo);
LittleFS.info(fsInfo);

Ce qui donne le script final compatible avec LittleFS :

/**
 * TUTO  enregistrement / lecture fichier config dans LittleFS
 * Remplacement de SPIFFS
 */
// bibliothèque
#include <ArduinoJson.h>
//#include <FS.h>   //Include SPIFFS
#include "LittleFS.h"

// ma structure config
struct sConfig {
  char nom[10];
  int NumeroPIN;
  bool defaut;
};
sConfig config;

// chemin fichier config dans memoire flash ( SPIFFS )
const char *fileconfig = "/config/config.json";  // fichier config
//taille buffer pour json
const size_t capacityConfig = 2*JSON_ARRAY_SIZE(3);

void chargeFS() {
  File file = LittleFS.open(fileconfig, "r");
   if (!file) {
     //fichier config absent
     Serial.println("Fichier Config absent");
    }
  DynamicJsonDocument docConfig(capacityConfig);
  DeserializationError err = deserializeJson(docConfig, file);
  if (err) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(err.c_str());
  }

// affectation des valeurs , si existe pas on place une valeur par defaut
strlcpy(config.nom,docConfig["nom"] | "byfeel",sizeof(config.nom));
config.NumeroPIN = docConfig["pin"] | 10;
  config.defaut = docConfig["defaut"] | true;
 //fermeture fichier
  file.close();
}

void sauveFS() {
  String jsondoc="";
  DynamicJsonDocument docConfig(capacityConfig);
  docConfig["nom"]=config.nom;
  docConfig["pin"]=config.NumeroPIN;
  docConfig["defaut"]=config.defaut;

File  f = LittleFS.open(fileconfig, "w");
  if (!f) {
    Serial.println("Fichier Config absent - création fichier");
   }
   serializeJson(docConfig,jsondoc);
   f.print(jsondoc);  // sauvegarde de la chaine
   f.close();
   Serial.print(jsondoc);
}

void setup() {
// affichage dans moniteur serie
Serial.begin(9600);
//montage du disque flash
LittleFS.begin();
// charge dans un premier temps le fichier config
chargeFS();

// affiche les variables config apres lecture FS
Serial.print("valeur Nom : ");
Serial.println(config.nom);
Serial.println("Valeur PIN : "+String(config.NumeroPIN));
Serial.println("valeur defaut : "+String(config.defaut));

//modif donnée PIN
config.NumeroPIN=5;
// sauve fichier
sauveFS();

// lit de nouveau le fichier
chargeFS();

// affiche les variables config apres lecture du nouvel enregistrement
Serial.print("valeur Nom : ");
Serial.println(config.nom);
Serial.println("Nouvelle Valeur PIN : "+String(config.NumeroPIN));
Serial.println("valeur defaut : "+String(config.defaut));

// Bonus : info sur memoire SPIFFS pour DEBUG
FSInfo fsInfo;
// info fs
LittleFS.info(fsInfo);
//fsInfo.totalBytes
float total=(fsInfo.totalBytes/1024);
Serial.println("info totalbytes : "+String(total)+" ko");
Serial.println("info usedbytes : "+String(fsInfo.usedBytes));
Serial.println("info blocksize : "+String(fsInfo.blockSize));
Serial.println("info pagesize: "+String(fsInfo.pageSize));
Serial.println("info maxOpenFiles : "+String(fsInfo.maxOpenFiles));
Serial.println("info maxPathLength : "+String(fsInfo.maxPathLength));

}

void loop() 
{   
}

Une fois compilé dans votre module ESP , il ne reste plus qu’a ouvrir le terminal est lire le résultat :

valeur Nom : byfeel
Valeur PIN : 5
valeur defaut : 1
{"nom":"byfeel","pin":5,"defaut":true}valeur Nom : byfeel
Nouvelle Valeur PIN : 5
valeur defaut : 1
info totalbytes : 1000.00 ko
info usedbytes : 32768
info blocksize : 8192
info pagesize: 256
info maxOpenFiles : 5
info maxPathLength : 32

Transfert des fichiers dans le système de fichier LittleFS : Le dossier Data

Comme expliqué en début d’article , même si les deux bibliothèques sont très proche l’une de l’autre le système de fichier est différent .

Afin de transférer le dossier « data » dans ce nouvel espace, il faut utiliser les outils ou les instructions suivantes en fonction de votre plateforme de développement.

Sous platformIO :

Le système de fichier SPIFFS est utilisé par défaut , afin de maintenir la compatibilité des anciens projets. Pour choisir LittleFS comme système de fichier pour la compilation, il faut ajouter l’instruction suivante au fichier platform.ini

board_build.filesystem = littlefs

Exemple :

[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
debug_tool = custom
board_build.filesystem = littlefs

Pour uploader le dossier Data dans le module ESP , cela se passe de la même façon que pour SPIFS. Clic sur l’outil autre tache , puis choisir : PlatformIO: Upload File System Image

Sous ArduinoIDE :

Il faut utiliser un outil équivalent à ce qui existe pour SPIFFS et le positionner dans le dossier Tool du répertoire arduino.

L’outil est à télécharger sur ce dépot github : arduino-esp8266littlefs-plugin , une fois téléchargé , le décompresser et le coller dans le dossier Tool .

Installation de l’outil Tool dans ArduinoIDE

Dans l’interface ArduinoIDe , il suffit alors de se rendre dans le menu Outils et de sélectionner ESP8266 LittleFS Data Upload pour transférer le dossier data dans le nouveau système de fichiers.

Flasher facilement même avec LitlleFS

Avec le système de fichier au format LittleFS , il est toujours possible de « pousser » le dossier data dans la mémoire FS de l’ESP.

La méthode décrite dans l’article : flasher facilement le notifheure , explique l’utilisation des commandes en ligne esptool et espota.

python espota.py -i 192.168.1.2 -s -f littlefs.bin