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):

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:

Eintrag “New Tab” in der Tableiste rechts
Eintrag “New Tab” in der Tableiste rechts

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:

Im Arduino-Sketch selbst verweisen wir nur noch auf diese Konstanten:

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:

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).

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.

Weitere Literatur und Quellen

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).

Creative Commons Lizenzvertrag


  1. Ein guter erster Einblick, was der Präprozessor ist findet sich etwa hier oder direkt in der Doku hier

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: