Einleitung
In der Vergangenheit habe ich öfter Projekte für das ESP8266 erstellt. Viele Leute online betten ihren HTML-Code in den C++-Code ein. Aus Gründen der Softwarearchitektur habe ich mich entschlossen eine Lösung für diese unsaubere Art der Programmierung zu finden und das Backend und Frontend zu trennen.ESP8266 und ESP32
Bevor ich wie üblich direkt loslegen möchte ich das ESP8266 und ESP32 vorstellen. Für meine Projekte habe ich zwei der meiner Meinung nach sinnvollsten und preiswertesten Devboards herausgesucht und stelle diese vor.Wemos D1 mini (ESP8266)
Das erste Bild zeigt die Vorderseite und das zweite die Rückseite. Es ist eine USB-Buchse vorhanden über die das Modul geflasht werden kann.
Cores: 1
Clock Speed: 80 Mhz
SRAM: 96 kB (ca. 50 kB nutzbar)
Flash Speicher: 4 MB bzw. 16 MB (pro)
Kosten: ab 2,50 €
ESP32-CAM (ESP32)
Leider ist keine USB-Buchse vorhanden und es muss über einen extra USB-to-Serial-Konverter geflasht werden. Allerdings besitzt das Board bereits eine Kamera, was später für Bilderkennung noch interessant sein könnte.
Cores: 2 (einer nur für WiFi)
Clock Speed: je Core 80 Mhz
SRAM: 520 kB
Flash Speicher: 4 MB
Kosten: ab 5,50 €
Da das ESP32 das ESP8266 meiner Meinung nach in naher Zukunft nicht ersetzen wird (preislich und wegen des höheren Stromverbrauches), entschloss ich mich mein Projekt für beide Chips kompatibel zu machen. Ich setzte mir also ein Limit von max. 10 kB Verbrauch im Heap, da noch andere Software auf dem Modul laufen sollte.
Async Webserver
Der Entwicklerme-no-dev hat auf Github sein Projekt ESPAsyncWebServer
veröffentlicht, welches einen asynchronen Webserver bereitstellt, der nicht in der loop()-Methode
aufgerufen werden muss. Das hat die folgenden Vorteile:
- mehrere Verbindungen gleichzeitig verarbeiten
- deutlich schneller als synchrone Webserver
- eventbasierte Entwicklung
Also habe ich nachgedacht, welche leichtgewichtigen Protokolle für das Hochladen von Dateien geeignet sein könnten und mir sind die folgenden zwei eingefallen:
- FTP: geringer Overhead bei der Dateiübertragung, aber komplexes Protokoll (PASV usw.)
- Webdav: mehr Overhead bei der Übertragung, allerdings Plug-in für einen bereits vorhandenen Webserver
Webdav
Zuerst einmal müssen wir uns Webdav ansehen, bevor es zur Implementierung geht. Im RFC 4918 standardisiert, erweitert es HTTP um folgende Verben:- PROPFIND: Ruft Eigenschaften einer Ressource ab und gibt diese im XML-Format aus. Wird ebenfalls zum Auflisten von Verzeichnissen verwendet.
- PROPPATCH: Ändert und Löscht mehrere Eigenschaften einer Ressource in einem einzigen atomaren Aufruf.
- MKCOL: Erzeugt Verzeichnisse
- COPY: Kopiert eine Ressource
- MOVE: Verschiebt eine Ressource bzw. benennt diese um
- DELETE: Löscht eine Ressource
- LOCK: Sperrt eine Ressource
- UNLOCK: Entsperrt eine Ressource
Für das Speichern der Daten habe ich mich für LittleFS entschieden. Im Gegensatz zu SPIFFS unterstützt dieses Verzeichnisse. Auf die Implementierung werde ich in diesem Post nicht eingehen, da dieser sonst zu lang wird. Wer sich trotzdem für meinen Code interessiert, kann gerne unter folgenden Links auf Github nachsehen:
- AsyncWebdav.cpp
- komplettes Projekt
Nutzung des Plug-ins
Um das Plug-in zu nutzen müssen wir erst einmal einen Sketch erstellen. Folgender Code reicht dafür bereits aus:#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
#include <AsyncWebdav.h>
const char* ssid = "ssid";
const char* password = "pass";
AsyncWebServer server(80);
AsyncWebdav dav("/dav");
void setup(void){
// init serial and wifi
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// wait for connection
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// init littlefs
LittleFS.begin();
// add websocket handler
server.addHandler(&dav);
// start webserver
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
server.begin();
}
void loop(void){
// do whatever you want
}
Im ersten Schritt laden wir alle Abhängigkeiten. Anschließend legen wir die WLAN-Zugangsdaten fest und initialisieren den Webserver und das Plug-in. In
setup() verbindet sich der Sketch zum WLAN
und startet den Webserver mitsamt dem Plug-in. Wie man hier auch sehen kann, wird nichts in
loop() ausgeführt.
Um den Sketch hochzuladen, müssen nach dem Verbinden des Moduls mit dem Computer in Arduino die Einstellungen angepasst werden. Möglicherweise gibt es mehrere COM-Ports.
Den korrekten Port kann man herausfinden, indem man sich mit einem Programm (z. B. HTerm) die Ausgabe der jeweiligen Ports anzeigen lässt und das Modul neu startet. Beim Starten des Moduls werden mit der Baudrate 74880 Startinformationen übertragen.
load 0x4010f000, len 1392, room 16 <\r><\n> tail 0<\r><\n> chksum 0xd0<\r><\n> csum 0xd0<\r><\n> v3d128e5c<\r><\n>Beispiel: Ausgabe beim Starten des ESP8266
Nach dem Flashen des Chips sollte via COM die IP-Adresse angezeigt werden. Die Baudrate muss wie im Code angegeben auf 115200 gestellt werden.
.<\r><\n> Connected to dennisparsch<\r><\n> IP address: 10.0.60.30<\r><\n>Beispiel: Ausgabe des Chips nach dem Hochfahren.
Einbinden des ESP8266
Das ESP kann nun wie ein normales Netzlaufwerk eingebunden werden. Dazu muss man unterComputer > Netzlaufwerk verbinden klicken und die Daten eintragen.
Aufgrund eines Fehlers (hier nachzulesen) in Windows wird die Laufwerkgröße falsch angezeigt. Wird eine andere Software, wie z. B. NetDrive3 zum Einbinden der Ressource genutzt, dann wird die Größe korrekt angezeigt.
Lädt man nun eine Datei
index.html mit dem Inhalt Hallo Welt hoch, dann wird
diese beim Aufruf der Seite im Browser angezeigt:
Auf diese Weise lassen sich sogar Bilder und größere Dateien hochladen. Im Anschluss habe ich eine Seite mit Vue.js erstellt, welche mir Informationen über das Board anzeigt und sich automatisch aktualisiert. Wie ich diese Seite erstellt habe, werde ich aufgrund der Komplexität in diesem Blogpost nicht erklären.
Tests
Zu Beginn dieses Blogposts hatte ich geschrieben, dass ich nicht mehr als 10 kB Heap verbrauchen wollte. Da die Software erst mal fertig war, konnte ich nun Tests laufen lassen:
Was die Werte bedeuten:
- Speicherverbrauch nach dem Hochfahren des Moduls in
loop() - Anzeige des Laufwerks unter
Computer(Abfrage der Kapazität) - Auflisten der Dateien auf dem Speicher (5 Dateien zum Zeitpunkt des Aufrufs)
- Hochladen von drei Dateien mit je ca. 5 kB
Zuletzt habe ich den Unterschied beim Verbrauch gemessen, wenn das Modul gar nicht aktiv ist.
- nicht aktiv: 48,74 kB
- aktiv: 46,16 kB
- Differenz: 2,58 kB