Laufende Erkenntnisse bei ESP8266-Projekten.


File System (SPIFFS)

Das ESP8266-File-System stellt die üblichen Datei- und Directory-Strukturen und -Methoden bereit. Den Code findet man auf GitHub: pellepl/spiffs. Insbesondere findet man im zugehörigen Wiki viele wertvolle Hinweise.

Leider hat die Implementierung in der ESP8266-Arduino-Variante (Version 2.3.0) einen Bug. Es wird nicht erkannt, dass das SPIFFS voll ist. Man wird das SPIFFS "überfüllen". Weitere sinnvolle Zugriffe auf das SPIFFS sind dann nicht mehr möglich. Es hilft dann nur noch eine Neuformatierung des SPIFFS. Fips hat mich per E-Mail auf diesen Umstand aufmerksam gemacht. Dies betrifft (betraf) insbesondere meine Implementierung des SPIFFS-Editors als Web-Interface.

Das Problem ist zweigeteilt. Zum einen kann trotz gefüllten SPIFFS' weiter geschrieben werden. File::write() zerstört die SPIFFS-Struktur unbarmherzig. Das zweite Problem ist, dass FSInfo.usedBytes nicht den korrekten Füllstand anzeigt. Bei der Ermittlung des freien Speichers gibt es "Unschärfen", die ich leider nicht in der Lage bin aufzulösen. Das SPIFFS ist einfach sehr komplex und der Code nicht wirklich gut dokumentiert.

Fips hat mehrere Versuche angestellt, aber auch keine exakte Lösung gefunden. Aber er hat eine pragmatische Alternative aufgezeigt:

Man versieht FSInfo.usedBytes mit einem Sicherheitsaufschlag. Seinen Erfahrungen nach sind etwa 5% ausreichend (Faktor 1.05).  Der freie Speicher wird dadurch reduziert.

Wenn man nun den zur Verfügung stehenden Speicher kennt, hat man im Prinzip zwei Möglichkeiten:

Beim File-Upload im AsyncWebserver ist die Dateigröße nicht bekannt. Es kommt der zweite Fall zum tragen. Ein möglicher Code wäre:

const float SPIFFS_Savety = 1.05; // Sicherheitsaufschlag.

// ...

void fileUploadSpiffs(AsyncWebServerRequest * request, const String & filename, 
                      size_t index, uint8_t * data, size_t len, bool final) {

  static String fn;        // Dateiname
  static File f;           // Datei-Handle
  static size_t freeBytes; // Anzahl freier Bytes abzgl. Sicherheitsreserve
  static bool abort;       // Abbruch-Kennung  


  if (!index) {  // Initialisierung des Upload-Vorgangs
    FSInfo fs_info;
    SPIFFS.info(fs_info);
    freeBytes = fs_info.totalBytes - fs_info.usedBytes * SPIFFS_Savety; // Freie Kapazität berechnen
    fn = filename;
    if (!fn.startsWith("/"))
      fn = "/" + filename;
    f = SPIFFS.open(fn, "w");
    abort = false;
  }

  if (!abort) // Kein weiteres Schreiben, wenn Abbruch-Kennung gesetzt ist
    if (len >= freeBytes) { // Speicherverfügbarkeit prüfen
      f.close();            // Speicher nicht ausreichend
      SPIFFS.remove(fn);    // Datei löschen
      abort = true;         // Abbruch-Kennung setzen
    }
    else {
      f.write(data, len);   // Daten schreiben
      freeBytes -= len;     // Freien Speicher reduzieren 
    }

  if (final) {
    f.close(); // Doppeltes Schließen schadet nicht!
  }
}

// ...

WebServer.on("/spiffs.html", ..., .., fileUploadSpiffs);