In this part you will learn:
Damit der ESP32 eine Web-Oberfläche bekommt, muss er auf Anfragen, die standardmäßig auf Port 80 ankommen, antworten. Wie das grundsätzlich funktioniert, habe ich in this tutorial beschrieben. Ich habe diese Funktionalität noch einmal erweitert und in einem Modul zusammengefasst. Du kannst es dir hier anschauen oder direkt mit upip
installieren. Zusätzlich brauchst du noch das Modul ep_config
. Also:
Damit wir auch etwas zum Anzeigen haben, erstellen wir uns die Datei index.html
im Ordner html
mit folgendem Inhalt:
<html>
<head>
<title>Heater Controller</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Und kopieren sie schon mal auf den ESP32 in den Ordner html
:
Damit der ESP32 sie auch anzeigt, ergänzen wir die Datei main.py
um folgenden Inhalt:
import network
import time
import ep_logging
import ep_http
import ep_file_server
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Set DHCP host name to recognize the device in your router
wlan.config(dhcp_hostname="Heater")
# Replace your SSID and password
wlan.connect("<SSID>", "<password>")
while not wlan.isconnected():
time.sleep(1)
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
fs = ep_file_server.file_server(
html_dir="/html/",
default_file="index.html",
logger=logger_http
)
routes = [
("^(.*)$", lambda sock, req: fs.serve(sock, req)), # every route is forwarded to file server
]
http_server = ep_http.http_server(routes=routes, micropython_optimize=True, logger=logger_http)
http_server.start()
Wenn wir den ESP32 jetzt neu starten und seine IP-Adresse in den Browser eingeben, sollten wir den Schriftzug „Hello World“ angezeigt bekommen. Die IP-Adresse bekommen wir über unseren Router heraus, oder wir lassen sie uns anzeigen mit dem Befehl:
Was passiert hier? Der http_server
lauscht auf Port 80 (Standard für HTTP) und nimmt die Anfrage entgegen. Dann parst er die empfangenen Daten und schaut die Routen von oben nach unten durch und übergibt die Anfrage an die Route, die passt. Der Regex-String ^(.*)$
bedeutet: Anfang des Strings (^
) – dann irgendein Zeichen (.
) belibig oft (*
) – Ende des Strings ($
). Es wird also jede Anfrage an den file_server
weitergeleitet. Dieser schaut dann, ob die angefragte Datei vorhanden ist und gibt sie zurück, falls möglich.
Wie bereits erwähnt, wollen wir aber auch Konfigurationen vornehmen und müssen dazu Dateien verändern können. Dafür habe ich eine mini-REST-Server gebaut, mit dem du Config-Dateien bearbeiten kannst. Dafür brauchst du zunächst mal eine Config-Datei im json-Format. Wir erstellen uns die Datei network_config.json
:
{
"wifi_config": {
"ap_ssid": "ESP32",
"ap_pass": "eydam-protoyping",
"dhcp_hostname": "ESP32"
},
"wifi_nets": [
{
"ssid": "ssid1",
"pass": "pass1"
},
{
"ssid": "ssid2",
"pass": "pass2"
}
]
}
Und kopieren sie auf den ESP32.
Wir könnten hier auch etwas anderes reinschreiben. Dem REST-Server ist das egal. Ich nutze diese Datei aber später, um daraus direkt die WiFi-Einstellungen zu laden.
Im erstem Block (wifi_config
) machen wir generelle WiFi-Einstellungen. Unter welchem Namen der ESP32 im Router auftaucht und, falls wir uns nicht mit einem bestehenden WiFi verbinden können, wie unser Access-Point heißen soll und welches Passwort er haben soll.
Im zweiten Block (wifi_nets
) haben wir eine Liste mit SSIDs und Passwörtern, mit denen wir uns verbinden können. Falls ein Netz mal ausfällt, können wir uns mit einem anderen verbinden.
Diese Datei wollen wir jetzt über den Webbrowser bearbeiten können. Dazu fügen wir die neue Route hinzu:
import network
import time
import ep_logging
import ep_http
import ep_file_server
import ep_rest_server
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Set DHCP host name to recognize the device in your router
wlan.config(dhcp_hostname="Heater")
# Replace your SSID and password
wlan.connect("<SSID>", "<password>")
while not wlan.isconnected():
time.sleep(1)
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
fs = ep_file_server.file_server(
html_dir="/html/",
default_file="index.html",
logger=logger_http
)
crs_nw = ep_rest_server.config_rest_server(
config_file="./network_config.json",
logger=logger_http
)
routes = [
("^\/?rest/nw\/?([A-Za-z0-9_\.\/]*)\??([A-Za-z0-9_\.\/]*)$", lambda sock, req: crs_nw.serve(sock, req)),
("^(.*)$", lambda sock, req: fs.serve(sock, req)),
]
http_server = ep_http.http_server(routes=routes, micropython_optimize=True, logger=logger_http)
http_server.start()
Wir können uns den Inhalt der json-Datei jetzt schon anzeigen lassen, indem wir die IP-Adresse des ESP32 in den Webbrowser eingeben, gefolgt von /rest/nw
(so, wie wir unsere Route genannt haben).
Das Bearbeiten machen wir in einer neuen HTML-Seite, die wir nw_config.html
nennen. Du kannst dir den Inhalt der Datei nw_config_v1.html
(github) kopieren und sie dann auf den ESP32 in den Ordner html
kopieren. Hierin ist nur HTML und JavaScript.
Jedenfalls, wenn du jetzt alles auf den ESP32 kopierst, ihn neu startest und die Seite http://[IP-des-ESP32]/nw_config.html
besuchst, solltest du eine Webseite angezeigt bekommen, die kurz nach dem Laden die aktuellen Einstellungen aus network_config.json
nachlädt. Wenn du auf dieser Seite Änderungen machst und unten auf den Button „Speichern“ klickst, erscheint unter dem Button die Nachricht „OK“ und zeigt damit an, dass die Daten auf dem ESP32 gespeichert wurden. Wenn du die Seite aktualisierst, sollten die geänderten Daten erscheinen. In der Tabelle WiFi-Netze gibt es die Spalte BSSID. Die kannst du jetzt erstmal ignorieren.
Es wäre ja jetzt noch komfortabel, wenn wir uns die verfügbaren WiFi-Netze anzeigen lassen können. Dafür habe ich das Modul sensor_rest_server
vorgesehen. Ich habe es eigentlich vorgesehen, um Sensoren auszulesen. Aber man kann das WiFi-Modul ja auch irgendwie als Sensor betrachten. Um es zu nutzen erweitere die Datei main.py
folgendermaßen:
import network
import time
import ep_logging
import ep_http
import ep_file_server
import ep_rest_server
import ubinascii
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Set DHCP host name to recognize the device in your router
wlan.config(dhcp_hostname="Heater")
# Replace your SSID and password
wlan.connect("<SSID>", "<password>")
while not wlan.isconnected():
time.sleep(1)
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
def scan_wifi(wlan):
nets = wlan.scan()
result = []
for ssid, bssid, channel, rssi, authmode, hidden in nets:
net = {
"ssid": ssid.decode("ascii"),
"bssid": ubinascii.hexlify(bssid).upper(),
"channel": channel,
"rssi": rssi,
"authmode": authmode,
"hidden": hidden
}
result.append(net)
return result
fs = ep_file_server.file_server(
html_dir="/html/",
default_file="index.html",
logger=logger_http
)
crs_nw = ep_rest_server.config_rest_server(
config_file="./network_config.json",
logger=logger_http
)
srs = ep_rest_server.sensor_rest_server(
[
("^wifinets$", lambda path: scan_wifi(wlan)),
],
logger=logger_http
)
routes = [
("^\/?rest/nw\/?([A-Za-z0-9_\.\/]*)\??([A-Za-z0-9_\.\/]*)$", lambda sock, req: crs_nw.serve(sock, req)),
("^\/?sensor\/?([A-Za-z0-9_\.\/]*)\??([A-Za-z0-9_\.\/]*)$", lambda sock, req: srs.serve(sock, req)),
("^(.*)$", lambda sock, req: fs.serve(sock, req)),
]
http_server = ep_http.http_server(routes=routes, micropython_optimize=True, logger=logger_http)
http_server.start()
Der sensor_rest_server
führt Funktionen aus, die ihm übergeben werden. Wenn wir ihm also eine Liste mit Regex-Strings und den dazugehörigen Funktionen geben (die Funktionen müssen ein Objekt zurück geben, was sich in einen JSON-String überführen lässt), dann können wir die Funktionen vom Browser aus aufrufen und uns das Ergebnis anschauen. Wenn du jetzt also http://[IP-des-ESP32]/sensor/wifinets
in die Adresszeile eingibst, solltest du eine Liste mit den verfügbaren WiFi-Netzen bekommen. Hinweis: Ich muss meinen ESP32 manchmal über den Reset-Button neu starten, damit ich ein Ergebnis bekomme.
Wir brauchen jetzt nur unsere nw_config.html
anpassen, damit wir uns die Netze anzeigen lassen können (siehe nw_config_v2.html
auf github). Meine Seite sieht jetzt so aus:
(Ich hab die BSSIDs mal unkenntlich gemacht. Keine Ahnung, ob man damit irgendetwas anfangen kann.) Probiere mal aus, ob alles funktioniert.
Jetzt bekommt die Spalte BSSID auch eine Bedeutung. Damit können wir unser WiFi-Netz eindeutig identifizieren, falls wir mehrere mit dem gleichen Namen haben sollten. Du kannst also bestimmen, mit welchem Access Point genau sich dein ESP32 verbinden soll. Wenn du die Zelle leer lässt, wird der Access Point mit dem stärksten Signal verwendet.
Damit wir die so erstellte Konfiguration auch nutzen um uns mit dem WiFi zu verbinden, sind wieder Änderungen in der main.py
nötig. Wir nehmen zum Verbinden mein Modul ep_wifi
(installieren wieder mit upip.install("micropython-eydam-prototyping-wifi")
). Diesem Modul kannst du die Config-Datei übergeben. Es probiert dann Netz für Netz durch, bis es sich mit einem Verbinden konnte. Falls keines gefunden werden konnte, wird ein neues Netz aufgemacht, mit dem du dich dann verbinden kannst und den ESP32 so konfigurieren kannst.
Achso, wir können den ESP32 über diesen sensor_rest_server
auch neustarten. Das habe ich in diesen Schritt mal mit eingebaut:
import network
import time
import ep_logging
import ep_http
import ep_file_server
import ep_rest_server
import ubinascii
import machine
import ep_wifi
wifi = ep_wifi.wifi("./network_config.json", max_time_wait_for_connect=10)
wlan, ssid, bssid = wifi.connect()
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
def scan_wifi(wlan):
nets = wlan.scan()
result = []
for ssid, bssid, channel, rssi, authmode, hidden in nets:
net = {
"ssid": ssid.decode("ascii"),
"bssid": ubinascii.hexlify(bssid).upper(),
"channel": channel,
"rssi": rssi,
"authmode": authmode,
"hidden": hidden
}
result.append(net)
return result
fs = ep_file_server.file_server(
html_dir="/html/",
default_file="index.html",
logger=logger_http
)
crs_nw = ep_rest_server.config_rest_server(
config_file="./network_config.json",
logger=logger_http
)
srs = ep_rest_server.sensor_rest_server(
[
("^wifinets$", lambda path: scan_wifi(wlan)),
("^reset$", lambda path: machine.reset()),
],
logger=logger_http
)
routes = [
("^\/?rest/nw\/?([A-Za-z0-9_\.\/]*)\??([A-Za-z0-9_\.\/]*)$", lambda sock, req: crs_nw.serve(sock, req)),
("^\/?sensor\/?([A-Za-z0-9_\.\/]*)\??([A-Za-z0-9_\.\/]*)$", lambda sock, req: srs.serve(sock, req)),
("^(.*)$", lambda sock, req: fs.serve(sock, req)),
]
http_server = ep_http.http_server(routes=routes, micropython_optimize=True, logger=logger_http)
http_server.start()
Wenn du jetzt http:///sensor/reset
aufrufst, dann bekommst du keine Seite zurückgegeben, sondern der ESP32 startet neu.
So, Zeit für eine kurze Pause. Weiter geht’s dann im nächsten Kapitel. Du kannst ja als kleine Hausaufgabe die Webseiten ein bisschen schöner gestalten. Ein bisschen CSS und das ganze sieht viel besser aus.
Eydam prototyping
Saccasner Strasse 19
03096 Schmogrow-Fehrow
Germany