ESP ins WLAN integrieren
https://oer-informatik.de/esp_wifi
tl/dr; (ca. 5 min Lesezeit): Ein ESP (32/8266) ist mit wenigen Zeilen Code ins heimische WLAN integriert. Der hier verwendete Weg stellt sicher, dass aus vielen unterschiedlichen WLANs das geeignetste ausgesucht wird. Außerdem wird wiederkehrend überprüft, ob die WLAN-Verbindung noch besteht, diese ggf. neu aufgebaut und - wenn der Aufbau zu häufig scheitert - der ESP neu gestartet. Ausserdem wird ein Weg vorgestellt, die WLAN-Zugangsdaten vor der unbeabsichtigten Veröffentlichung in Versionsverwaltungen und Codesharing zu schützen.
Es gibt eine ganze Reihe unterschiedlicher Bibliotheken, die für ESPs eine WLAN-Verbindung herstellen. Die meisten bauen auf WiFi.h
auf. Mein aktueller Favorit ist (ESP8266
-)WiFiMulti
: das ist in der Lage, mehrere WLAN-Verbindungen zu verwalten und die passendste zu aktivieren. Das ist z.B. sinnvoll, wenn man das Device an unterschiedlichen Orten betreibt oder den Accesspoint des Handys für Testzwecke nutzen will.
Der Arduino-Sketch muss in der üblichen Reihenfolge angepasst werden:
Die nötigen Bibliotheken müssen importiert werden.
Die Konfigurationsvariablen müssen gesetzt werden. Hierbei sollen Passwörter nicht im regulären Quelltext stehen. Die Instanzen der Bibliothek gebildet werden.
In der
setup()
müssen die erforderlichen Dienste gestartet werden und eine Option des Debuggings aktiviert werden.In der
loop()
müssen alle Operationen aufgerufen werden, die zyklisch nötig sind um die Verbindung aufrecht zu halten.Neue Funktionen, die die Verbindung (wieder-)herstellen müssen implementiert werden.
Fast-Track: Der gesamte Code zum copy/pasten
Für die Ungeduldigen: der komplette Beispielcode, der für ESP32 oder ESP8266 eine WiFi-Verbindung aufbaut, findet sich in diesem Repository zum Copy/Pasten. Einzig eine individuelle Datei secrets.h
muss dann noch nach dem Beispiel erstellt werden.
#if defined(ESP8266)
#pragma message "Compiling Libraries for ESP8266-based boards"
#include <ESP8266WiFiMulti.h> // aktivieren für ESP8266
ESP8266WiFiMulti wifiMulti;
#elif defined(ESP32)
#pragma message "Compiling Libraries for ESP32-based boards"
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#else
#error "Nor ESP32 or ESP8266 recognized - you have to choose the libraries manually"
#endif
#include "secrets.h" // Passwords saved in this file to be hidden from versioncontrol and sharing
// WiFi-Settings (if not defined in secrets.h replace your SSID/PW here)
const char* WIFI_SSID = SECRET_WIFI_SSID; // Wifi network name (SSID)
const char* WIFI_PASSWORD = SECRET_WIFI_PASSWORD; // Wifi network password
const uint32_t CONNECTION_TIMEOUT_MS = 10000; // WiFi connect timeout per AP.
const uint32_t MAX_CONNECTION_RETRY = 20; // Reboot ESP after __ times connection errors
void setup(){
Serial.begin(115200); // Activate debugging via serial monitor
WiFi.mode(WIFI_STA); // Connectmode Station: as client on accesspoint
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD); // multpile networks possible
ensureWIFIConnection(); // Call connection-function for the first time
}
void loop() {
ensureWIFIConnection(); // make sure, WiFi is still alive, reboot if necessary
}
void ensureWIFIConnection() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("No WIFI Connection found. Re-establishing...");
int connectionRetry = 0;
while ((wifiMulti.run(CONNECTION_TIMEOUT_MS) != WL_CONNECTED)) {
delay(1000);
connectionRetry++;
Serial.println("WLAN Connection attempt number " + String(connectionRetry));
if (connectionRetry > MAX_CONNECTION_RETRY) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
}
Serial.println("WiFi is connected");
Serial.println("IP address: " + (WiFi.localIP().toString()));
Serial.println("Connected to (SSID): " + String(WiFi.SSID()));
Serial.println("Signal strength (RSSI): " + String(WiFi.RSSI()) + "(-50 = perfect / -100 no signal)");
}
}
Abgesehen von ein paar Zeilen Code in der ensureWIFIConnection()
kein Hexenwerk. Gucken wir uns das im Detail an:
Die einzelnen Abschnitte zur Herstellung der WLAN-Konnektivität:
Import der Bibliotheken
Kernstück ist die Bibliothek WiFiMulti.h
für den ESP32 bzw. für den etwas älteren ESP8266 die ESP8266WiFiMulti.h
. Die wesentlichen Kernfunktionalitäten sind auch bereits in den meisten anderen Beispielen enthalten - z.B. lohnt sich wie immer ein Blick in die Beispiele, die die Bibliotheken mitbringen (in der Arduino-IDE: z.B. File/Examples/WiFi/WiFiMulti bzw. WiFiScan). Uns reicht zunächst der Import, für den ESP32
bzw. für den ESP8266:
Eigentlich deklariere ich globale Variablen immer unterhalb der Importe. Weil diese jedoch wie die Importe auch vom verwendeten Board abhängen mache ich das direkt nach den jeweiligen Importen. Das eigentliche Herzstück ist eine Instanz der Klasse WiFiMulti
, die wir mit dem - per Konvention kleingeschriebenen- Namen wifiMulti
ansprechen. Da wir für ESP32 und ESP8266 unterschiedliche Bibliotheken nutzen weicht auch diese Codezeile ab.
ESP32:
ESP8266:
Ich nutze beide Microprozessoren, daher definiere ich zu Beginn des Programms eine “Präprozessor-Direktive”1 (ESP32
) und lade die Bibliotheken des ESP32, wenn diese Direktive gefunden wird - sonst lade ich die ESP8266-Bibliotheken (und deklariere die entsprechenden Variablen):
#if defined(ESP8266)
#pragma message "Compiling Libraries for ESP8266-based boards"
#include <ESP8266WiFiMulti.h> // aktivieren für ESP8266
ESP8266WiFiMulti wifiMulti;
#elif defined(ESP32)
#pragma message "Compiling Libraries for ESP32-based boards"
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#else
#error "Nor ESP32 or ESP8266 recognized - you have to choose the libraries manually"
#endif
Konfigurationsvariablen setzen
Der zweite Punkt ist auch gleich ein heikler: ich möchte mein WLAN-Passwort nicht im Arduino-Sketch speichern (da dieser in die Versionsverwaltung wandern würde). Ein gangbarer Weg ist es, die schützenwerten Daten in einer gesonderten Datei zu speichern, die - wie die Bibliotheken oben - eingefügt wird. Per Konvention nenne ich diese Datei secrets.h
. Wer seinen Quellcode mit git versioniert sollte in der .gitignore
einen Eintrag mit *secrets.h
ergänzen.
Wir müssen eine neue Datei mit Namen secrets.h
erzeugen per Tastenkombination Ctrl
-Shift
-n
oder über das Punktemenü rechts in der Tableiste:

Die Datei secrets.h
definiert die Passwörter dann als Präprozessor Direktiven (vereinfacht: vor dem Kompilieren wird einmal Suchen/Ersetzen mit diesen Werten im Programmcode durchgeführt). Der Inhalt der Datei secrets.h
sieht etwa so aus:
#pragma once // Only run once, even if included multiple times
#define SECRET_WIFI_SSID "meinWLAN"; // Wifi network name (SSID)
#define SECRET_WIFI_PASSWORD "1234567890123456"; // Wifi network password
Im Arduino-Sketch selbst verweisen wir nur noch auf diese Konstanten:
// WiFi-Settings (if not defined in secrets.h replace your SSID/PW here)
const char* WIFI_SSID = SECRET_WIFI_SSID; // Wifi network name (SSID)
const char* WIFI_PASSWORD = SECRET_WIFI_PASSWORD; // Wifi network password
Danach legen wir fest, nach wie vielen Millisekunden ein Verbindungsversuch abgebrochen wird (ich habe 10s verwendet) und nach wie vielen Verbindungsversuchen der ESP komplett neu gestartet werden soll:
const uint32_t CONNECTION_TIMEOUT_MS = 10000; // WiFi connect timeout per AP.
const uint32_t MAX_CONNECTION_RETRY = 20; // Reboot ESP after __ times connection errors
Konfiguration in der setup()
aktivieren
In der setup()
stellen wir die Serielle Verbindung her, um per USB/seriellem Monitor debuggen zu können,
legen wir sicherheitshalber nochmal fest, dass unser ESP als Client an einen bestehenden Accesspoint angemeldet werden soll (station mode),
fügen wir dann so viele WiFi-Netz Zugangsdaten hinzu, wie wir eben benötigen (ggf. müssen weitere Konfigurations-Daten auch oben in die
secrets.h
eingetragen werden, um auch diese zu schützen),rufen wir erstmals unsere neue Funktion zum Aufbau der WLAN-Verbindung auf (diese wird unten erläutert).
void setup(){
Serial.begin(115200); // Activate debugging via serial monitor
WiFi.mode(WIFI_STA); // Connectmode Station: as client on accesspoint
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD); // multpile networks possible
ensureWIFIConnection();
}
Verbindungsaufbau in der loop()
überprüfen
Da die WLAN-Verbindung abbrechen kann - oder bei bewegten Devices ein anderes WLAN einen besseren Empfang bieten kann - sollten wir zyklisch überprüfen, ob die Verbindung noch besteht. Dies kann beispielsweise zu Beginn der loop()
passieren. Es wird die gleiche Funktion wie in der setup()
aufgerufen. Im nächsten Abschnitt mehr dazu…
Herzstück der Verbindungsüberprüfung: ensureWIFIConnection()
Last but not least das absolute Herzstück dieser Variante: unsere neue Funktion ensureWIFIConnection()
. Sie übernimmt folgende Aufgaben:
Der aktuellen WiFi-Status wird überprüft (
if (WiFi.status() != WL_CONNECTED) {..}
). Nur für den Fall, dass keine Verbindung besteht werden weitere Schritte unternommen.Wenn keine Verbindung besteht, wird eine vorgegebene Anzahl von Versuchen ein neuer Verbindungsaufbau gestartet (
while ((wifiMulti.run(CONNECTION_TIMEOUT_MS) != WL_CONNECTED)) {...}
). Jeder Verbindungsaufbau wir nach einer gegebenen Anzahl an Millisekunden abgebrochen (CONNECTION_TIMEOUT_MS
).Wenn auch nach der vorgegebenen Anzahl an Versuchen (
MAX_CONNECTION_RETRY
) nicht gelingt, eine Verbindung aufzubauen, wird der ESP neu gestartet (ESP.restart();
).Der Verbindungsstatus nach einem gescheiterten oder geglückten Verbindungsaufbau wird immer am seriellen Monitor ausgegeben.
void ensureWIFIConnection() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("No WIFI Connection found. Re-establishing...");
int connectionRetry = 0;
while ((wifiMulti.run(CONNECTION_TIMEOUT_MS) != WL_CONNECTED)) {
delay(1000);
connectionRetry++;
Serial.println("WLAN Connection attempt number " + String(connectionRetry), true);
if (connectionRetry > MAX_CONNECTION_RETRY) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
}
Serial.println("WiFi is connected");
Serial.println("IP address: " + (WiFi.localIP().toString()));
Serial.println("Connected to (SSID): " + String(WiFi.SSID()));
Serial.println("Signal strength (RSSI): " + String(WiFi.RSSI()) + "(-50 = perfect / -100 no signal)");
}
}
Weitere Literatur und Quellen
- Umgang mit (WLAN-)Passwörtern in Arduino-Dateien: Andrea Grandi: How to safely store Arduino secrets
Quellen und offene Ressourcen (OER)
Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:
https://gitlab.com/oer-informatik/mcu/arduino-esp.
Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).