Laufende Erkenntnisse bei ESP8266-Projekten.
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);