Beispiel 3: Web-Server mit SPIFFS-Editor und Platzhaltern in HTML-Datei

Dieses Beispiel ergänzt das Beispiel 2. Die vorgefertigte Site SPIFFS-Editor wird eingebunden. Hierdurch wird es möglich per Web-Interface Dateien im SPIFFS manipulieren, ohne das SPIFFS neu laden oder das Programm neu kompilieren zu müssen. Dieser Mechanismus ist z.B. bestens geeignet, HTML-Dateien im SPIFFS durch neuere Varianten auszutauschen.

Der AsyncWebServer besitzt den Mechanismus des Template Processing. Dieser Mechanismus erlaubt es, Platzhalter in der HTML-Datei durch aktuelle Werte zu ersetzen. Besonders interessant ist dies für Sites, die aus dem SPIFSS geladen werden. Die Dokumentation beschreibt dies im Abschnitt Respond with content coming from a File containing templates. Eine dort als processor benannte Methode bekommt die Templates vorgelegt und liefert den Ersatztext zurück. Im Gegensatz zur Dokumentation, wird auch hier eine statische Member-Methode benutzt. Der Vorgang des Template-Processing wird an einer neuen Web-Site SysTimeWebSiteClass dargestellt.

Die "SPIFFS-Editor"-Web-Site nutzt diesen Mechanismus ebenfalls um die aktuellen SPIFFS-Daten auf der Sie zu platzieren

SysTimeWebSiteClass

Die Seite ist als Datei "systime.html" im SPIFFS abgelegt. In dieser Datei gibt es einen Platzhalter mit der Bezeichnung "millis". Dieser wird zur Laufzeit durch den aktuellen Wert des System-Timers millis() ersetzt. Hier das Ergebnis:

Platzhalter-Text

Auf die Unterschiede bei der Zeitangabe wird später eingegangen.

Der HTML-Code der Seite "systime.html" enthält zweimal den Paragrafen (ohne die Text-Dekoration) …

<p>Die aktuelle Systemzeit ist %millis% seit Start.</p>

… einmal zu Beginn der Seite und einmal am Ende. "%millis%" ist der Platzhalter, der beim Aufruf der Seite ersetzt werden soll.

Die Klasse SysTimeWebSiteClass, die für diese Site zuständig ist, enthält die aus dem vorhergehenden Beispiel bekannten Methoden zur Bereitstellung der URLs. Hinzu kommt die Member-Funktion textProcessor, die für die Ersetzung der Platzhalter zuständig ist. Ein spezieller Request-Handler ist nicht notwendig, die Default-Implementierung entnimmt der URL den Dateinamen und ruft die entsprechende Datei ab.

class SysTimeWebSiteClass : public UrsAsyncWebSite {
 protected:
   // Ersetzt die Platzhalter
   String templateProcessor(String TemplString) override {
     if (TemplString == "millis") return String(millis());
     return String();
   }

   // Liefert die URL für diese Seite. Gleichzeitig Dateiname der HTML-Datei.
   virtual String getUrl() override { return "systime.html"; }

   // Liefert alternative Ressourcen-Bezeichnungen.
   virtual String getRewrites() override { return ("systime|systime.htm|time|time.html|time.htm");}
};

Der Text zwischen den beiden Platzhaltern ist so lang, dass er nicht in einem Stück an den Browser zurück geliefert werden kann. Zwischen dem Versand der einzelnen Datenblöcken (Chunk) wird andere Dinge erledigt. Dadurch entsteht der Zeitversatz zwischen dem Ersatz des ersten und des zweiten Platzhalters. Dies kann zu Problemen führen. Z.B. im SPIFFS-Editor Informationen über die aktuelle Belegung des Dateisystems ausgegeben. Auch hier wird mit mit Platzhaltern gearbeitet. Ohne besondere Vorkehrungen, besteht das Risiko, dass auf Grund des Zeitversatzes inkonsistente Daten angezeigt werden. Wie man dieses Problem löst wird im nächsten Beispiel erläutert.

Einbinden des SPIFFS-Editors

MyWebServerClass ist nun ein Baukasten mit mehreren Web-Sites. Einige, wie "index.html" oder "qr.html", können einfach aus dem SPIFFS geladen werden. Die Funktionalität der drei Seiten durch die Klassen InfoWebSiteClass (s. Beispiel 2), HelloWebSiteClass und SysTimeWebSiteClass (s.o.) werden komplett durch eigenen Code erstellt bzw. Komponenten werden per Code aufbereitet. Die Klasse UrsAsyncSpiffsEditorClass (s. SPIFFS-Editor), mit der das Dateiverzeichnis angezeigt und Dateien hochegeladen oder gelöscht werden können, stellt eine komplett vorgefertigte Seite bereit.

class MyWebServerClass : public UrsAsyncWebServer {
protected:
  // Überschreibt die Methode 'notFoundHandler' von 'UrsAsyncWebServer'
  virtual void notFoundHandler(AsyncWebServerRequest *request);

  InfoWebSiteClass infoWebSite;
  HelloWebSiteClass helloWebSite;
  HelloWebSiteClass halloWebSite;
  SysTimeWebSiteClass sysTimeWebSite;
  UrsAsyncSpiffsEditorClass spiffsEditorWebSite;

public:
  // Initialisiert eine neue Instanz von MyWebServerClass:
  MyWebServerClass(int port = 80) : UrsAsyncWebServer(port),
                                    helloWebSite("hello.html", "hello", "Welcome "),
                                    halloWebSite("hallo.html", "hallo", "Herzlich willkommen ") {} 
  // Startet den Web.Server
  void begin(); 
};

In der Methode begin müssen wieder alle Web-Sites registriert werden:

void MyWebServerClass::begin() {
  // Web-Sites registrieren (WICHTIG: muss vor serveStatic("/"...) geschehen!)
  registerWebSite(infoWebSite);
  registerWebSite(helloWebSite);
  registerWebSite(halloWebSite);
  registerWebSite(sysTimeWebSite);
  registerWebSite(spiffsEditorWebSite);

  // Mögliche Anfragen zur Startseite auf index.html umlenken
  rewrite("/index", "/index.html");     // index     -> index.html
  rewrite("/index.htm", "/index.html"); // index.htm -> index.html
  serveStatic("/urs.ico", SPIFFS, "/urs.ico", "max-age=3600");
  serveStatic("/", SPIFFS, "/").setDefaultFile("index.html"); // alle anderen Dateien aus den SPIFFS, 
                                                              // root -> index.html
  // Die Basis-Klasse starten
  UrsAsyncWebServer::begin();

  // Erfolgsmeldung ausgeben
  Serial.println("Web-Server gestartet");
}

Die Seite "spiffs.html" erzeugt nun diesen Output:

SPIFFS-Editor

System-Neustart (reboot)

Der SPIFFS-Editor erlaubt es, per Schaltfläche einen Neustart des Systems auszulösen. Dazu wird die statische boolesche Member-Variable des Web-Servers shouldReboot auf true gesetzt. Diese Variable muss in loop (in der ".ino"-Datei) regelmäßig abgefragt und entsprechend reagiert werden. Wie bereits beschrieben, kann ESP.restart nicht in den asynchron ausgeführten Request-Handlern aufgerufen werden. Dies führt zu recht merkwürdigen Fehlermeldungen. Siehe auch Restart-Steuerung.

void loop() {
  // Aktionenanforderungen des Webservers synchron bearbeiten 
  // -------------------------------------------------------------------------
  if (MyWebServer.shouldReboot) { // Reboot angefordert
    Serial.println("Rebooting...");
    Serial.flush(); // Ausgabe per Serial abschließen
    ESP.restart();  // Reboot auslösen
  }
}

SPIFFS-Dateien

Damit das Ganze funktioniert, sind zwei weitere Dateien im Dateisystem notwendig. Die Datei "index.html" ist ebenfalls wieder ergänzt wurden.

Dateien

Für alle Dateien sind Muster im Verzeichnis "data" des Beispiels abgelegt.

Download

Das Beispiel ist als "UrsAsyncWebServerExample3" in der Bibliotheks-ZIP-Datei enthalten.