4. Project4 - Esp32 verbindung mit Meshnetzwerk

4.1. Projektaufbau

Eines der letzten Projekte behandelte das Thema MQTT und das Broker Netzwerk, welches ein gerne genutztes Protokoll für IoT Gerätekommunikation ist. Dieses mal soll es um eine weitere Vernetzungsmöglichkeit für IoT Geräte gehen - Ein Mesh Netzwerk. Um das Mesh-Netzwerk zu testen, werden vier ESP32 verwendet. Es gibt eine Collector Node, die eine LED-Diode mit dem IO-Port 02 verbunden hat. Diese Node erwartet ein Ein- und Ausschaltsignal für diese Diode sowie Temperaturwerte, die dann im seriellen Output geloggt werden. Zum Senden des Ein- und Ausschaltsignals und der Temperatur werden jeweils zwei weitere Nodes verwendet. Zur Verdeutlichung des Mesh-Netzwerks sind die beiden Nodes, die Daten senden, weiter entfernt, und die vierte Node dient zur Überbrückung der Distanz.

4.2. Meshnetzwerk

Ein Meshnetzwerk ist ein Netzwerk, in dem alle Geräte bzw. Knoten direkt oder indirekt miteinander kommunizieren können – ohne einen zentralen Router. Jeder Knoten kann: - Daten empfangen und senden - Nachrichten an andere Knoten weiterleiten (Relay-Funktion) - Neue Verbindungen automatisch aufbauen, wenn Knoten sich verbinden oder trennen

Mesh Netzwerk

4.3. Verwendete Techniken

4.3.1. Painless Mesh

Überblick painlessMesh ist eine Arduino-kompatible Bibliothek zur einfachen Erstellung von dynamischen Mesh-Netzwerken mit ESP8266- und ESP32-Mikrocontrollern.

Die Bibliothek verwaltet automatisch: - die Verbindungsstruktur zwischen Knoten - Routing von Nachrichten - Wiederverbindungen bei Netzänderungen

Grundfunktionen

  • Netzwerkaufbau ohne zentralen Server oder Gateway

  • Automatische Adressierung und Weiterleitung von Nachrichten

  • Unterstützung für Broadcast- und direkte Kommunikation

  • Kompatibel mit ESP8266 und ESP32

  • Unterstützung für Task-Scheduling über Scheduler

Nutzung

#include <painlessMesh.h>

painlessMesh mesh;

void setup() {
    mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);  // Optional: Debug-Ausgaben aktivieren
    mesh.init("NetzwerkName", "Passwort", 5555, WIFI_AP_STA, 6);  // Initialisierung
    mesh.onReceive([](uint32_t from, String &msg) {
        Serial.println("Nachricht empfangen: " + msg); //Ausgabe von empfangenen Nachrichten
    });
}

void loop() {
    mesh.update();  // Muss regelmäßig aufgerufen werden
}

Nutzung mit Scheduler Mit einem Scheduler-Objekt können wiederkehrende Aufgaben eingeplant werden:

Scheduler userScheduler;  // Scheduler-Objekt für benutzerdefinierte periodische Tasks

Task blinkTask(1000, TASK_FOREVER, []() {   // Definition eines Tasks, der alle 1000 ms (1 Sekunde) ausgeführt wird
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  // Onboard-LED an- und ausschalten
});

void setup() {
    mesh.init("NetzwerkName", "Passwort", &userScheduler, 5555, WIFI_AP_STA, 6);  // Initialisierung mit scheduler
    userScheduler.addTask(blinkTask);       // Fügt den blinkTask dem Scheduler hinzu
    blinkTask.enable();       // Aktiviert den Task, sodass er automatisch im Hintergrund läuft
}

Vorteile - Keine zentrale Steuerung notwendig (keine Single Points of Failure) - Skalierbar durch automatische Topologieanpassung - Ideal für mobile oder instabile Funkverbindungen - Geeignet für dezentrale IoT-Netze

Einschränkungen - Keine Ende-zu-Ende-Verschlüsselung eingebaut - Keine Authentifizierung einzelner Nodes - Begrenzte Reichweite und Bandbreite durch WiFi-Basis

4.3.2. Arduino IDE

Die Arduino IDE ist eine plattformübergreifende Entwicklungsumgebung zur Programmierung von Mikrocontrollern. Mit ihr kann auch der ESP32 programmiert, konfiguriert und überwacht werden. Sie bietet Funktionen zum Editieren, Kompilieren, Hochladen von Code sowie zur seriellen Kommunikation mit dem Gerät.

Zur verwendung von ESP32 muss die Boardunterstützung zuerst über den Boardverwalter hinzugefügt werden. Nach dem Verbinden per USB Kabel muss auch noch der jeweilige USB Port in der IDE ausgewählt werden. Das schreiben, kompilieren und hochladen des Codes ist ebenfalls sehr einfach: #. Schreibe den Code in eine .ino-Datei in der IDE. #. Klicke auf das Hochladen-Symbol (Pfeil nach rechts). #. Die IDE kompiliert den den Code und lädt ihn auf den ESP32. #. Beim hochladen des Codes muss die BOOT-Taste des ESP32 gedrückt gehalten werden.

Sobald der ESP32 den Code ausführt kann man sich die seriellen Logs direkt in der IDE direkt anzeigen lassen. Hierzu muss man lediglich den Serial Montor unter Tools öffnen und die Baudrate vor dem verbinden richtig einstellen.

Arduino IDE

4.4. Knoten

Im Folgenden werden die einzelnen Knoten mit ihrer jeweiligen Funktion erläutert.

4.4.1. Node_Bridge

Funktion: Diese Node dient als Brücke zwischen der collector Node und den beiden Sender Nodes.

Code: Starten die serielle Verbindung zum Computer mit 115200 Baud:

Serial.begin(115200);

Folgender Code dient zur Ausgabe von Texten im seriellen Monitor:

mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);

Zeigt bestimmte Debug-Nachrichten:

  • ERROR: Fehler

  • STARTUP: Startinformationen

  • CONNECTION: Verbindungsstatus

Starten und Verbinden mit Hilfe der PainlessMesh Bibliothek: .. code-block:: cpp

mesh.init(„espMesh“, „password123“, 5555, WIFI_AP_STA, 6);

verwendete Parameter: - „espMesh“: Name (SSID) des Mesh-Netzwerks – muss bei allen Knoten gleich sein - „password123“: Passwort des Netzwerks - 5555: Port, über den kommuniziert wird (bei allen Knoten gleich) - WIFI_AP_STA: WLAN-Modus (Access Point + Station) für Mesh-Funktion - 6: WLAN-Kanal – muss bei allen Knoten gleich sein

Bei erfolgreicher Verbindung weiterer Nodes erscheinen folgende Nachrichten in den Logs:

CONNECTION: eventScanDoneHandler: ARDUINO_EVENT_WIFI_SCAN_DONE
CONNECTION: scanComplete(): Scan finished
CONNECTION: scanComplete():--> Cleared old APs.
CONNECTION: scanComplete(): num = 26
CONNECTION:   found : espMesh, -57dBm
CONNECTION:   found : espMesh, -57dBm
CONNECTION:   Found 2 nodes
CONNECTION: connectToAP(): No unknown nodes found scan rate set to normal

Hier sieht man wie viele Nodes Verbunden sind und deren Verbindungsstärke.

4.4.2. Node_Collector

Funktion: Dieser Knoten empfängt Nachrichten von anderen Geräten und:

  • Schaltet eine verbundene LED an oder aus (erwartete Signale: LED_ON / LED_OFF)

  • Gibt eine Meldung aus, wenn Temperaturdaten empfangen werden

  • Logged jede Nachricht mit Zeitstempel

Code: Erstellung der Zeit-Angabe:

void receivedCallback(uint32_t from, String &msg) {
  String timestamp = String(hour()) + ":" + String(minute()) + ":" + String(second());
  logMessages += "[" + timestamp + "] From " + String(from) + ": " + msg + "\n";
}

LED-Reaktion:

if (msg == "LED_ON") {
  digitalWrite(LED_PIN, HIGH);
}
else if (msg == "LED_OFF") {
  digitalWrite(LED_PIN, LOW);
}

Temperaturerkennung:

else if (msg.indexOf("Temp") != -1) {
  Serial.println("warm:");
}

Zeitstempel setzen für das Logging:

setTime(12, 0, 0, 1, 1, 2025);

Auch hier die Mesh-Initialisierung (mit userScheduler):

mesh.init("espMesh", "password123", &userScheduler, 5555, WIFI_AP_STA, 6);

Timeoutwarnung mit Scheduler:

Task checkTimeoutTask(10000, TASK_FOREVER, []() {
  if (millis() - lastMessageTime > 15000) {
    Serial.println("WARNUNG: Keine Nachricht seit 15 Sekunden! Bitte Verbindung zu Sendern prüfen.");
  }
});

Variablen:

  • #define LED_PIN 2 - Deklaration der Pin, über die die LED gesteuert wird

  • String logMessages = „“; - String zur Ausgabe der Logs

  • bool ledState = false; - LED zu beginn auf „OFF“

Hier ist ein Beispiel der Log Meldungen aus dem Serial Monitor:

LOG: [12:0:25] From 4234133741: {"temp": 20.80}
LOG: [12:0:25] From 4234133741: {"temp": 29.50}
LOG: [12:0:25] From 4234133741: {"temp": 24.60}
LOG: [12:0:26] From 3520580029: LED_ON
LOG: [12:0:26] From 4234133741: {"temp": 21.20}
LOG: [12:0:27] From 4234133741: {"temp": 29.60}
LOG: [12:0:28] From 4234133741: {"temp": 23.20}
LOG: [12:0:29] From 4234133741: {"temp": 27.10}
LOG: [12:0:30] From 4234133741: {"temp": 29.60}
LOG: [12:0:31] From 3520580029: LED_OFF

4.4.3. Node_Led_Signal

Funktion: Dieser Knoten sendet alle 5 Sekunden eine Nachricht basierend auf dem internen Zustand: - „LED_ON“ wenn die LED ausgeschaltet war - „LED_OFF“ wenn die LED an war

Code: Das Senden der Nachrichten passiert über einen Task (repetitive Funktion):

Task sendMessageTask(SEND_INTERVAL, TASK_FOREVER, []() {
  ledState = !ledState;
  String msg = ledState ? "LED_ON" : "LED_OFF";
  mesh.sendBroadcast(msg);
});

Die Messagetask wird anhand von Scheduler aktualisiert, hierfür muss man den Task aktivieren:

userScheduler.addTask(sendMessageTask);
sendMessageTask.enable();

Empfangsbestätigung der Nachricht:

mesh.onReceive([](uint32_t from, String &msg) {
  Serial.println("Received something: " + msg);
});

Variablen: bool ledState = false; - Intern gespeicherter Zustand der LED

4.4.4. Node_Temperature

Dieser Knoten simuliert eine Temperaturmessung und sendet sie jede Sekunde als Nachricht:

float fakeTemp = random(200, 300) / 10.0;
String msg = "{\"temp\": " + String(fakeTemp) + "}";
mesh.sendBroadcast(msg);

Auch hier wird ein Task verwendet, damit der Code unabhängig von loop() regelmäßig läuft.

userScheduler.addTask(taskSendTemp);
taskSendTemp.enable();

4.5. Vergleich Mesh und MQTT-Broker

Struktur

  • Mesh-Netzwerk: Dezentral, jeder ist Knoten

  • MQTT mit zentralem Broker: Zentraler Server (Broker)

Kommunikation

  • Mesh-Netzwerk: Direkt oder über Relais-Knoten

  • MQTT mit zentralem Broker: Über definierte Topics via Broker

Abhängigkeit

  • Mesh-Netzwerk: Kein zentraler Punkt

  • MQTT mit zentralem Broker: Broker als zentraler Punkt notwendig

Robustheit

  • Mesh-Netzwerk: Hoch (Selbstheilung möglich)

  • MQTT mit zentralem Broker: Geringer bei Ausfall des Broker

Einrichtung

  • Mesh-Netzwerk: Bibliothek + einfache Initialisierung

  • MQTT mit zentralem Broker: Komplexer (hostapd, DHCP, Broker)

Beobachtung / Logging

  • Mesh-Netzwerk: Lokale Logs auf Geräten

  • MQTT mit zentralem Broker: Zentrale Protokollierung über Broker

Beispielhafte Anwendung

  • Mesh-Netzwerk: LED-Steuerung, Temperaturwarnung

  • MQTT mit zentralem Broker: Schaltbefehle über Weboberfläche

4.6. Meshnetzwerk-Test

4.6.1. Ziel des Tests

Überprüfung der Reichweite und Funktionalität der automatischen Nachrichtenweiterleitung im Mesh-Netzwerk mithilfe von painlessMesh.

4.6.2. Testaufbau

Drei ESP32-Boards wurden wie folgt verwendet:

  • Gerät A: Sender-Node

  • Gerät B: Empfänger-Node

  • Gerät C: Diente als Brücke (Relay-Knoten), also eine Repeater-Funktion im Mesh

4.6.3. Ablauf

  1. Startkonfiguration: Alle drei Geräte befinden sich in Funkreichweite zueinander. Gerät A sendet Temperaturdaten. Gerät B empfängt und loggt die Daten korrekt.

  2. Entfernungstest: Gerät A wurde schrittweise vom restlichen Aufbau entfernt. Sobald Gerät A außer Reichweite von Gerät B war, festgestellt durch Timeoutwarnung, kam es zu Nachrichtenausfällen – die Temperaturdaten wurden nicht mehr empfangen.

  3. Mesh-Wiederherstellung: Gerät C wurde nun zwischen A und B positioniert – auf halber Strecke, sodass beide Geräte wieder Reichweite zu Gerät C hatten. Sofort nach der neuen Positionierung wurde das Netzwerk dynamisch reorganisiert. Die Nachrichten von A konnten über Gerät C an B weitergeleitet werden. Die Logs auf Gerät B zeigten erneut die Temperaturdaten von Gerät A – das Mesh funktionierte wieder zuverlässig.

4.6.4. Beobachtungen

  • Ohne Relay-Gerät: keine Verbindung zwischen A und B bei zu großer Entfernung

  • Mit Relay-Gerät: Netzwerk funktionierte wie gewünscht – automatische Weiterleitung durch dynamische Mesh-Struktur

  • Die Umorganisation dauerte nur wenige Sekunden – dies zeigt die Selbstheilungsfähigkeit des Meshnetzwerks

4.6.5. Schlussfolgerung

Der Test demonstrierte anschaulich die Stärke eines Meshnetzwerks:

  • Flexible Topologie

  • Keine Notwendigkeit fester Infrastruktur

  • Dynamische Anpassung an die Umgebung und Funkreichweite

Die praktische Umsetzung zeigte, dass painlessMesh auch unter realen Bedingungen mit Entfernung und Hindernissen zuverlässige Kommunikation sicherstellen kann – vorausgesetzt, ein Relay-Gerät ist innerhalb der erforderlichen Reichweite vorhanden.

4.7. Fazit

painlessMesh vereinfacht das Erstellen stabiler Meshnetzwerke mit ESP32. Die Kombination aus Scheduler, onReceive() und sendBroadcast() erlaubt: - Automatische Nachrichtenverarbeitung - Wiederholte Aufgaben ohne die Haupt-Loop zu blockieren Die Knoten können verschiedenste Rollen übernehmen: Sender, Empfänger, Relais, Logger usw.

4.8. Quellen