Ecrire des données dans LittleFS sur esp8266

Bpnjour,
Je souhaiterais de l'aide afin de solutionner mon problème.
j'ai un projet de station météo en cours et pour celui-ci j'aurais besoin d'écrire et lire régulièrement 2 données qui seront mises en mémoire dans le SPIFFS en utilisant la bibliothèque LittleFS et sur ESP8266.
Les données sont du type pluieJour par exemple = 2,54mm et la deuxième est le currentJour = 1 par exemple.
elles sont donc déclarer pour la première en float et la deuxième en int.
float pluieJour;
int currentJour;

comment puis je les envoyer dans le spiffs pour les conserver en cas de redemarrage de l'esp?
sachant que je les actualise et les relis toutes les 2 minutes !
merci pour votre aide.
bien cordialement
Philippe

SPIFFS n'est plus recommandé. il vaut mieux passer sur LitteFS

un truc comme ça devrait écrire toutes les 2 minutes dans le fichier (tapé ici, non testé)

#include <LittleFS.h>

const char * fichierLog = "/data.log";
const uint32_t deuxMinutes = 120000ul; // deux minutes en ms
uint32_t derniereSauvegarde = -deuxMinutes;
float pluie;
uint32_t numeroEnregistrement = 0;

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

  // Start filing subsystem
  if (LittleFS.begin()) {
    Serial.println("LittleFS Activé");
  } else {
    Serial.println("Erreur accès LittleFS");
    while (true) yield();
  }

  if (! LittleFS.exists(fichierLog)) {
    // on le crée avec un en tête
    LittleFS.format();
    File fichier = LittleFS.open(fichierLog, "w+");
    if (fichier) {
      fichier.println(F("PLUIE\tNUMERO"));
      fichier.close();
    } else {
      Serial.println(F("Erreur LittleFS création du fichier log"));
      while (true) yield();
    }
  }
}



void loop() {
  if (millis() - derniereSauvegarde >= deuxMinutes) {
    // deux minutes se sont écoulées depuis la dernière sauvegarde
    derniereSauvegarde = millis();
    File fichier = LittleFS.open(fichierLog, "a"); // mode a pour ajouter à la fin
    if (fichier) {
      pluie = random(0, 1000) / 100.0; // nombre aléatoire, bien sûr il faudrait accéder à la vraie valeur
      numeroEnregistrement++;

      fichier.print(pluie, 2);                  // 2 décimales
      fichier.write('\t');                      // une tabulation
      fichier.println(numeroEnregistrement);    // le N° suivi d'un passage à la ligne
      fichier.close();
    } else {
      Serial.println(F("Erreur LittleFS pas d'accès au fichier log"));
    }
  }
}

Est-ce bien sérieux de stocker en FLASH des données que l'on peut aller lire à volonté ?
Mais bon, si tu y tiens, les tutoriels ne manquent pas sur le WEB :
GOOGLE

@hbachetti
Merci beaucoup pour votre réponse, le fait de stocker en Littlefs doit me permettre de ne pas perdre les données lors d'un redémarrage intempestif de l'esp (coupure de courant par ex) , car s'agissant d'un projet de station météo, la quantité de pluie par jour déjà enregistrée repartirait de zéro.
Bien cordialement

@J-M-L Bonjour et merci beaucoup pour votre réponse et réactivité, votre travail m'a beaucoup intéressé car n'ayant jamais travaillé avec ce système de fichiers Littlefs, je ne savais pas comment l'aborder.
Je vous transmets ci joint le code de la partie de mon projet qui traite du pluviomètre pour que vous puissiez m'aider à intégrer votre suggestion de solution à cette partie :slight_smile:

void getDataPluvio() {
  
  
  
  Serial.println("Execution de la fonction getDataPluvio().");
  float h_total = 0.00;                 // Initialisation de la variable qui contiendra le nbre total d'eau tombée sur 1 heure

  // On calcul le niveau depuis la dernière interogation.
  // raincnt = 35;  pour essai
  
  float pluie = raincnt * 0.2794;       // Nombre d'impulsion multiplié par la hauteur d'eau en mm d'un godet
  raincnt = 0;                          // On réinitialise le compteur.

  // On ajoute le niveau à h_fifo
  
  h_fifo.push(pluie);  
  Serial.print("Pluie en 6 minutes= "); Serial.print(String(pluie)); Serial.println(" mm ");

  // Calcul des précipitations en mm/h, On récupére la hauteur d'eau tombée sur les 6 dernières minutes.
  
  using index_h = decltype(h_fifo)::index_t;
  for (index_h i = 0; i < h_fifo.size(); i++) {
    h_total += h_fifo[i];
  }
    
    h_total = h_total * 10;  // On calcul la valeur à envoyer pour une heure. soit la valeur pour 6 mns x 10 //
    
    pluieHeure = h_total;    //variable créée pour récupérer donnée dans Weathercloud
  
    Serial.print("RAINRATE Pluie en 1 heure = "); Serial.println(String((int)(round(h_total)))); 
    
//-----------------------------------------//
//--- Calcul de quantité pluie par jour.---//
//-----------------------------------------//
      
    int currentJour = NTP_Jour();
    
    Serial.print("Jour de pluie actuel:");
    Serial.println(currentJour);
    
    if(currentJour != jourCourant) {
      jourCourant = currentJour;
      pluieJour = 0;
     
    }

   
//---------------------------------------------//
//--- Ajout de la quantité de pluie calculée---//
//---------------------------------------------//    
    pluieJour += pluie;
    
    Serial.print("RAINCOUNT Pluie dans la journée = "); Serial.println(String((round(pluieJour*100)/100))); 


//-----------------------------------------------------------------------//
//---- envoi des données de pluie h_total et pluieJour sur InfluxDb -----//
//-----------------------------------------------------------------------//

    digitalWrite(LED_BUILTIN, LOW); // Turn the LED on

    HTTPClient http;  
    WiFiClient client;

    static long counter = 0;
    int httpCode = 0;  
   
    char payloadStr[150];

    sprintf(payloadStr, "h_total=%d.%02d&pluieJour=%d.%02d", (int)h_total, (int)abs(h_total*100)%100, (int)pluieJour, (int)abs(pluieJour*100)%100);
     
    Serial.println(payloadStr);

    char corlysisUrl[200];
    sprintf(corlysisUrl, "http://%s:%s/bme/%s/%s", host, port, token, measurement);  //measurement=bmeweather measurement2=bmegarage

    do {

      http.begin(client, corlysisUrl);
      http.addHeader("Content-Type", "application/x-www-form-urlencoded");
      httpCode = http.POST(payloadStr);

      Serial.print("http result:");
      Serial.println(httpCode);

      http.writeToStream(&Serial);
      http.end();

      counter++;
      delay(1000); // on attend 1 sec avant chaque tentative
    } while (httpCode != 200 && counter < 5);

    digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off

      }

Le but étant de ne pas perdre la valeur de pluieJour et jourCourant en cas de redémarrage
soit volontaire (bug) soit coupure de courant:
Merci encore pour votre patience et aide, bien cordialement
Philippe

vous émettez le payload vers votre serveur à quelle fréquence ?

@J-M-L , merci de me suivre dans ce projet.
J'envoie les données pluvio toutes les 2 minutes sur le serveur de mon fils et sur Weathercloud toutes les 10 minutes.

OK et une fois envoyée vous pouvez considérer les données comme sauvegardées ?

@J-M-L oui bien sur elles sont à l'abri, mais le problème se pose pour l'envoi des données sur le site Weathercloud, si panne de courant ou redémarrage de l'esp la valeur de pluieJour retombe à zéro et sur Weathercloud la quantité de pluie indiquée est donc de zéro même s'il a déjà plu dans la journée.
Sauvegarder ces données ( pluieJour et jourCourant) dans le littlefs me permettrait de reprendre
ces données et continuer à ajouter la quantité de pluie éventuelle.

Oui, effectivement, s'il s'agit d'un pluviomètre à godet, c'est justifié.

Il faut savoir que la mémoire FLASH a une durée de vie d'environ 10000 cycles d'effacement, mais LittleFS sait détecter les bad blocks, et les mettre de côté (wear leveling).
Il aurait été beaucoup plus hasardeux de travailler sans système de fichiers.

Bonjour Philippe,

J'allais vous mettre en garde contre l'usure prématurée de la FLASH, mais hbachetti a été plus rapide.
Si votre alimentation électrique est instable à ce point, une batterie tampon peut aussi être une bonne solution.

Bonne bidouille,

MicroQuettas

PS : j'ai aussi une bidouille en cours avec ce pluviomètre. J'ai reconnu le nombre magique 0.2794 = 0.011 pouces...

oui mais vu le volume de données à stocker on va mettre longtemps à remplir un block avant de devoir l'effacer et ici on stocke peu de données à chaque écriture - donc les 10,000 cycles répartis disons sur 1 Mo affecté à la flash vont sans doute vous durer à la louche une centaine d'année à raison d'une écriture toutes les 2 minutes


pour revenir au code

  • il faudrait une convention pour savoir si le fichier contient des données à reprendre ou pas

  • il faut définir à quelle période ∆t vous allez écrire dans le fichier, quitte à perdre quelques ticks du pluviomètre

  • dans le setup au moment du boot il faut relire le fichier et voir s'il a des données à reprendre et si oui initialiser le compteur à cette valeur, sinon on commence à 0

  • dans la loop, tous les ∆t vous ouvrez le fichier et rajouter à la fin la valeur actuelle

  • une fois les 2 émissions vers les serveurs effectuées, vous videz le fichier

@J-M-L @hbachetti @MicroQuettas
Concernant la durée de vie de la Flash, je me suis dirigé justement vers le SPIFFS (Littlefs) plutôt
que l'EEPROM car j'avais lu sur les forums que c'est cette dernière qui a une durée limitée alors que l'on pouvait écrire et réécrire indéfiniment sur le SPIFFS(Littlefs), alors qu'en est il en fait ?
Si vous avez des certitudes je suis preneur :wink:

@J-M-L pour le code, il faut juste (façon de parler) m'indiquer comment stocker les 2 valeurs (pluieJour et jourCourant) dans le Littlefs et comment aller les lire et les modifier si besoin. Le reste du code gère la suite.
En clair si le code redémarre, je dois voir si on a changé de jour, et si oui on peut repartir de zéro avec la valeur pluieJour, sinon récupérer la dernière valeur de pluieJour pour lui rajouter ensuite comme prévu dans le code actuel l'éventuelle pluie tombée ensuite.
J'espère être clair, merci d'avance.

Bonsoir

Est-ce à dire que la mise à jour d'une donnée se fait en écrivant toujours dans une partie vierge d'un bloc, donc avec un certain 'glissement' dans l'adressage physique des cases mémoire utilisées ?

Si oui le wear levelling est d'un bon niveau ! (la réponse bien entendu dans le code de la biliothèque !)
Les blocs seraient graduellement remplis les un après les autres , reportant au loin la nécessité de commencer à en effacer un pour permettre à nouveau son utilsation
Une écriture en Flash si ma mémoire est bonne consiste à mettre des bits à zéro dans des octets de valeur 0xFF, l'effacement ne pouvant se faire que collectvement , pour un bloc entier

Oui je crois me souvenir qu’on remplit un bloc puis qu’il est effacé et on recommence. Au bout d’un moment le block est endommagé et donc on passe au bloc suivant et on recommence

J'y pense maintenant : j'avais étudié un montage pour sauvegarder des données lors d'une coupure de courant :

Avec un ESP8266 il faudra gonfler généreusement la valeur du condensateur.

@hbachetti Super boulot mais impossible à transposer dans mon projet, trop complexe, je rappelle mon but c'est de sauvegarder 2 données en SPIFFS ou EEPROM pour pouvoir les retrouver en cas de coupure, merci encore pour votre aide :+1:

Bonjour

je rappelle mon but c'est de sauvegarder 2 données en SPIFFS ou EEPROM pour pouvoir les retrouver en cas de coupure,

Vu la fréquence des mises à jour des deux données , rester sur l'idée initaile 'LittleFS' qui gère assez bien l'usure de la mémoire Flash

-EEPROM : éviter vu l'exposition maximale à l'usure de la mémoire Flash (où est émulée de l'EEPROM)
-SPIFFS à éviter également : développement abandonné, bibliothèque non maintenue depuis juillet 2017 + 'wear levelling' sommaire

autres solutions : câbler une petite EEPROM I2C ou de la FRAM pour éviter de passer par un système de fichiers

@al1fch merci pour votre intérêt pour mon projet et le temps pris pour me répondre.
J'ai bien compris que je dois éviter d'écrire ces 2 données dans l'eeprom, pouvez vous m'aider à coder alors cet enregistrement des 2 données dans le Littlefs ? j'y connais pas grand chose et je m'inspire souvent des sketchs déjà fournis et que j'essaye d'adapter à mes besoins et surtout connaissances, merci. cordialement Philippe