ESP32 und Arduino

ESP32 Relais per MQTT: Netzlasten mit Befehlen und Retained State schalten

Schließen Sie ein Relaismodul an einen ESP32 an, schalten Sie es über ein MQTT-Befehls-Topic und veröffentlichen Sie den Zustand retained, damit jedes Dashboard und jede App jederzeit weiß, ob die Last an oder aus ist.

ESP32-Board, verdrahtet mit einem zweikanaligen Relaismodul in einem ESP32-Relais-MQTT-Projekt
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Mit einer ESP32-Relais-MQTT-Steuerung schalten Sie Lampen, Pumpen, Lüfter oder Schütze aus jeder App, jedem Skript oder Dashboard, das eine Nachricht veröffentlichen kann. In diesem Tutorial verdrahten Sie ein Relaismodul mit einem ESP32, abonnieren ein Befehls-Topic, veröffentlichen den Relaiszustand als Retained Message und stellen nach einem Neustart den zuletzt bekannten Zustand wieder her. Alles wird zunächst gegen den kostenlosen öffentlichen MQTT-Broker broker.connectmq.com getestet und dann für den Produktivbetrieb auf Zugangsdaten umgestellt.

So funktioniert das ESP32-Relais-MQTT-Design

Ein Relaisknoten ist der einfachste nützliche Aktor im IoT, aber genau hier gehen viele Projekte schief. Der übliche Fehler: MQTT wird wie ein Fernbedienungsknopf behandelt. Irgendetwas veröffentlicht ON, der ESP32 schaltet das Relais, und niemand weiß je, ob der Befehl wirklich angekommen ist. Ein besseres Design trennt, was jemand möchte, von dem, was das Gerät tatsächlich tut.

Topic Richtung Retained Payload
connectmq/demo/<id>/relay/1/set App zum ESP32 Nein ON, OFF, TOGGLE
connectmq/demo/<id>/relay/1/state ESP32 zu den Apps Ja ON oder OFF
connectmq/demo/<id>/status ESP32 zu den Apps Ja online oder offline (Last Will)

Das Befehls-Topic ist nicht retained. Wäre es das, würde jeder Reconnect einen alten Befehl erneut abspielen, und ein Relais, das jemand manuell ausgeschaltet hat, könnte plötzlich wieder angehen. Das State-Topic ist retained, sodass ein Dashboard, das eine Minute später geöffnet wird, sofort den aktuellen Zustand erhält, ohne auf die nächste Änderung zu warten. Das status-Topic nutzt den Last Will von MQTT, damit Subscriber erfahren, wenn der Knoten aus dem Netz fällt.

Sind Retained Messages und Last Will neu für Sie, erklärt der Überblick was ein MQTT-Broker macht beides in wenigen Minuten.

Hardware und Verdrahtung

Sie brauchen:

  • Ein beliebiges ESP32-Entwicklungsboard (DevKitC, NodeMCU-32S, WROOM- oder WROVER-Module funktionieren alle).
  • Ein Relaismodul mit 1, 2 oder 4 Kanälen und Optokoppler-Eingang. Für 5 V angebotene Module mit Jumpern für "High/Low Level Trigger" sind am flexibelsten.
  • Jumperkabel und ein USB-Kabel. Für alles, was am Netz hängt, ein geeignetes Gehäuse und Reihenklemmen.

Typische Verdrahtung eines 5-V-Moduls:

Pin am Relaismodul ESP32-Pin Hinweis
VCC 5V (VIN) Versorgt die Relaisspule
GND GND Gemeinsame Masse
IN1 GPIO 26 Steuersignal für Kanal 1
IN2 GPIO 27 Steuersignal für Kanal 2 (optional)

Die meisten günstigen Relaisboards sind active-low: Das Relais zieht an, wenn der Eingang auf GND gezogen wird. Im Sketch unten kehrt eine einzige Konstante diese Logik um. Meiden Sie für Relais GPIO 0, 2, 12 und 15, denn das sind Strapping-Pins, und ein Relais, das beim Booten klickt, ist lästig und bei manchen Lasten gefährlich. GPIO 34 bis 39 sind reine Eingänge und funktionieren nicht.

Ein Sicherheitshinweis: Wenn Sie Netzspannung schalten, muss das Relaismodul für die Last ausgelegt, die Hochvoltseite eingehaust und die Arbeit gemäß den örtlichen Elektrovorschriften ausgeführt sein. Schalten Sie während der Firmware-Entwicklung einen Niedervolt-LED-Streifen oder gar nichts und horchen Sie auf das Klicken. In Maschinen und Anlagen gilt zusätzlich: Ein per MQTT geschaltetes Relais ersetzt keine SPS und keine sicherheitsgerichtete Steuerung. Es eignet sich für Hilfsfunktionen wie Beleuchtung, Lüfter oder Signalleuchten, nicht für sicherheitsrelevante Abschaltungen.

Die Bibliotheken installieren

Installieren Sie in der Arduino IDE 2.x das Board-Paket esp32 von Espressif über den Boardverwalter und die Bibliothek PubSubClient von Nick O'Leary über den Bibliotheksverwalter. Weitere Bibliotheken sind nicht nötig. Haben Sie noch keinen MQTT-Sketch auf dem ESP32 gebaut, führt das ESP32-IoT-Tutorial ausführlicher durch die Board-Einrichtung und Ihren ersten Publish.

Der vollständige ESP32-Relais-MQTT-Sketch

Der folgende Sketch unterstützt beliebig viele Relais, behandelt Reconnects, ohne die Schleife zu blockieren, und veröffentlicht nach jeder Änderung den Zustand retained.

#include <WiFi.h>
#include <PubSubClient.h>

// ---------- Konfiguration ----------
const char* WIFI_SSID     = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* DEVICE_ID = "relay-7f3a91";          // eindeutig machen
const String BASE = String("connectmq/demo/") + DEVICE_ID;

const bool ACTIVE_LOW = true;                     // die meisten Relaisboards
const uint8_t RELAY_PINS[] = {26, 27};
const uint8_t RELAY_COUNT = sizeof(RELAY_PINS);
bool relayState[RELAY_COUNT] = {false};
bool restoreWindow = true;                        // retained Zustand einmal beim Booten übernehmen
unsigned long connectedAt = 0;

WiFiClient net;
PubSubClient mqtt(net);
unsigned long lastAttempt = 0;

void applyRelay(uint8_t i, bool on) {
  relayState[i] = on;
  digitalWrite(RELAY_PINS[i], (on ^ ACTIVE_LOW) ? HIGH : LOW);
}

void publishState(uint8_t i) {
  String topic = BASE + "/relay/" + String(i + 1) + "/state";
  mqtt.publish(topic.c_str(), relayState[i] ? "ON" : "OFF", true);
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  String t(topic);
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  msg.trim();
  msg.toUpperCase();

  // Erwartet: <BASE>/relay/<n>/set oder <BASE>/relay/<n>/state
  int start = BASE.length() + 7;                  // "/relay/" überspringen
  int slash = t.indexOf('/', start);
  if (slash < 0) return;
  int n = t.substring(start, slash).toInt();
  if (n < 1 || n > RELAY_COUNT) return;
  uint8_t i = n - 1;
  String leaf = t.substring(slash + 1);

  if (leaf == "state") {
    // Retained Zustand von vor dem Neustart: einmal wiederherstellen, dann ignorieren
    if (restoreWindow) applyRelay(i, msg == "ON");
    return;
  }
  if (leaf != "set") return;

  if (msg == "ON")          applyRelay(i, true);
  else if (msg == "OFF")    applyRelay(i, false);
  else if (msg == "TOGGLE") applyRelay(i, !relayState[i]);
  else return;                                    // unbekannte Befehle ignorieren

  publishState(i);
  Serial.printf("Relay %d -> %s\n", n, relayState[i] ? "ON" : "OFF");
}

bool connectMqtt() {
  String willTopic = BASE + "/status";
  bool ok = mqtt.connect(DEVICE_ID, nullptr, nullptr,
                         willTopic.c_str(), 1, true, "offline");
  if (!ok) {
    Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
    return false;
  }
  mqtt.publish(willTopic.c_str(), "online", true);
  for (uint8_t i = 0; i < RELAY_COUNT; i++) {
    String base = BASE + "/relay/" + String(i + 1);
    mqtt.subscribe((base + "/set").c_str(), 1);
    if (restoreWindow) mqtt.subscribe((base + "/state").c_str(), 1);
  }
  connectedAt = millis();
  Serial.println("MQTT connected");
  return true;
}

void setup() {
  Serial.begin(115200);
  for (uint8_t i = 0; i < RELAY_COUNT; i++) {
    pinMode(RELAY_PINS[i], OUTPUT);
    applyRelay(i, false);                         // sicherer Standard vor dem WLAN
  }
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setKeepAlive(30);
  mqtt.setSocketTimeout(5);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) return;

  if (!mqtt.connected()) {
    if (millis() - lastAttempt > 5000) {
      lastAttempt = millis();
      connectMqtt();
    }
    return;
  }
  mqtt.loop();

  // Das Wiederherstellungsfenster zwei Sekunden nach der ersten Verbindung schließen
  if (restoreWindow && millis() - connectedAt > 2000) {
    restoreWindow = false;
    for (uint8_t i = 0; i < RELAY_COUNT; i++) {
      String s = BASE + "/relay/" + String(i + 1) + "/state";
      mqtt.unsubscribe(s.c_str());
      publishState(i);                            // tatsächlichen Zustand bestätigen
    }
  }
}

Was das Wiederherstellungsfenster bewirkt

Startet der ESP32 nach einem Stromausfall neu, beginnen die Relais in der sicheren Stellung OFF. Bei der ersten Verbindung abonniert der Sketch kurz seine eigenen retained state-Topics. Der Broker liefert sofort den zuletzt gespeicherten Zustand, der Sketch übernimmt ihn, kündigt nach zwei Sekunden das Abonnement und veröffentlicht den tatsächlichen Zustand erneut. Sollen die Relais immer aus starten, setzen Sie restoreWindow auf false. Bei kritischen Lasten wie Heizungen ist "nach Neustart immer aus" meist die sicherere Strategie.

Bedenken Sie, dass dies voraussetzt, dass der Broker die Retained Message noch vorhält. Retained Messages leben so lange, wie der Broker sie aufbewahrt. Betrachten Sie die Wiederherstellung daher als Komfortfunktion und nicht als Garantie, und legen Sie die Last so aus, dass OFF immer ein akzeptabler Zustand ist.

Das Relais vom Computer aus testen

Öffnen Sie ein Terminal und beobachten Sie alles unter Ihrem Geräte-Präfix:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-7f3a91 -v -t "connectmq/demo/relay-7f3a91/#"

Senden Sie in einem zweiten Terminal Befehle:

mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-7f3a91 -t "connectmq/demo/relay-7f3a91/relay/1/set" -m ON
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-7f3a91 -t "connectmq/demo/relay-7f3a91/relay/1/set" -m TOGGLE

Sie sollten das Klicken hören und den retained Zustand als Echo sehen:

connectmq/demo/relay-7f3a91/status online
connectmq/demo/relay-7f3a91/relay/1/state ON
connectmq/demo/relay-7f3a91/relay/1/state OFF

Ziehen Sie nun den Stecker des ESP32. Innerhalb von etwa anderthalb Keep-Alive-Intervallen veröffentlicht der Broker den Last Will, und der Subscriber gibt status offline aus. Genau dieses Signal braucht ein Dashboard, um den Schalter auszugrauen. Da broker.connectmq.com ein öffentlicher MQTT-Broker für Tests und Prototypen ist, kann jeder, der Ihr Topic errät, Ihr Relais schalten. Lassen Sie daher nie eine echte Last am öffentlichen MQTT-Broker angeschlossen.

Checkliste zur Fehlerbehebung

  1. Relais klickt beim Booten. Sie nutzen einen Strapping-Pin, oder das Modul ist active-low und der Pin floatet, bevor setup() läuft. Wechseln Sie auf GPIO 26/27/32/33 und setzen Sie den Pegel nach Möglichkeit vor pinMode.
  2. Relais schaltet nie, obwohl die LED auf dem Modul leuchtet. Viele Module brauchen 5 V an VCC für die Spule; 3,3 V lassen die LED leuchten, reichen aber nicht, um die Kontakte anzuziehen. Manche Boards benötigen außerdem den gesteckten JD-VCC-Jumper.
  3. Befehle funktionieren einmal, dann nicht mehr. Prüfen Sie das serielle Log auf state=-3 (Verbindung verloren). Dieselbe Client ID auf zwei Geräten führt dazu, dass sie sich immer wieder gegenseitig hinauswerfen.
  4. ESP32 startet neu, wenn das Relais schaltet. Induktive Lasten verursachen Spannungseinbrüche und Störungen. Versorgen Sie die Relaisspule aus einem separaten 5-V-Netzteil mit gemeinsamer Masse und setzen Sie auf der Lastseite ein Snubber-Glied oder eine Freilaufdiode ein.
  5. State-Topic zeigt nach dem Neustart den falschen Wert. Etwas anderes veröffentlicht Retained Messages auf dem state-Topic. Nur das Gerät selbst sollte dort jemals veröffentlichen.
  6. Es kommt nichts an. Topic-Namen unterscheiden Groß- und Kleinschreibung, und ein überzähliger Schrägstrich am Ende erzeugt ein anderes Topic.

Mit Zugangsdaten und TLS in den Produktivbetrieb

Für eine echte Installation legen Sie im ConnectMQ-Portal einen Broker an und verwenden die generierten Werte für Benutzername, Passwort und Client ID. Wechseln Sie von WiFiClient zu WiFiClientSecure, verbinden Sie sich mit Port 8883 und übergeben Sie die Zugangsdaten an connect():

#include <WiFiClientSecure.h>
WiFiClientSecure net;
// in setup(): net.setCACert(ROOT_CA_PEM);   // ISRG Root X1 (Let's Encrypt)
mqtt.setServer("broker.connectmq.com", 8883);
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD",
             willTopic.c_str(), 1, true, "offline");

Da Client IDs an die ausgestellten Zugangsdaten gebunden sind, verwenden Sie die vom Portal generierte Client ID statt einer selbst ausgedachten. Die vollständige Zertifikatseinrichtung einschließlich Zeitsynchronisation und Speicheraspekten behandelt ESP32 MQTT über SSL/TLS. Muss Ihr Relaisknoten instabiles WLAN überstehen, lohnt sich zusätzlich die Backoff-Strategie aus ESP32-MQTT-Reconnect-Logik.

Ein verwalteter MQTT-Broker ist gerade bei Aktoren entscheidend: Ein Relais, das Sie nicht erreichen, ist schlimmer als ein Sensor, den Sie nicht auslesen können. Mit ConnectMQ erhalten Sie TLS auf 8883, Zugangsdaten pro Gerät und ein Portal, in dem Sie relay/+/state im Live-Topic-Explorer beobachten oder einen Testbefehl über den Browser-Client senden können, ohne ein Terminal zu öffnen.

Ideen zur Erweiterung des Relaisknotens

  • Timer. Akzeptieren Sie {"state":"ON","seconds":600} als JSON und schalten Sie nach zehn Minuten automatisch ab. Halten Sie den Timer auf dem Gerät, damit die Last auch bei Netzwerkausfall abschaltet.
  • Lokaler Taster. Ergänzen Sie einen Taster, der das Relais umschaltet und den neuen Zustand veröffentlicht, damit physischer Schalter und App nie voneinander abweichen.
  • Verriegelungen. Bei Motorwendeschaltungen oder Zwei-Wege-Ventilen verhindern Sie in der Firmware, nicht in der App, dass beide Kanäle gleichzeitig eingeschaltet werden.
  • Servo oder LEDs als Geschwister. Dasselbe Befehls-/Zustandsmuster funktioniert für die Servosteuerung per MQTT und adressierbare LED-Streifen.

Starten Sie mit ConnectMQ

Erstellen Sie einen kostenlosen Serverless Broker unter app.connectmq.com, ohne Kreditkarte. In weniger als einer Minute haben Sie Zugangsdaten, TLS ab Werk und einen Live-Topic-Explorer, in dem Sie Ihre Relais schalten sehen. Wachsen Sie über den kostenlosen Plan hinaus, zieht dieselbe Firmware auf einen kostenpflichtigen oder dedizierten Plan um, ohne dass Sie mehr als die Zugangsdaten ändern müssen.

Häufig gestellte Fragen

Sollte das Befehls-Topic für das Relais retained sein?

Nein. Setzen Sie Retain beim State-Topic, nicht beim Befehls-Topic. Ein retained Befehl würde bei jedem Reconnect erneut abgespielt und kann eine Last unerwartet schalten.

Welche QoS sollte ich für Relaisbefehle verwenden?

QoS 1 ist die praktische Wahl: Der Befehl wird mindestens einmal zugestellt, und da ON und OFF idempotent sind, richtet ein Duplikat keinen Schaden an. PubSubClient kann mit QoS 0 oder 1 abonnieren.

Kann ich mehrere Relais mit einem ESP32 steuern?

Ja. Ergänzen Sie die Pins in RELAY_PINS, und jeder erhält eigene Topics relay/<n>/set und relay/<n>/state. Ein einzelnes Wildcard-Abonnement auf relay/+/set funktioniert ebenfalls, wenn Sie die Kanalnummer aus dem Topic auslesen.

Woran erkenne ich, dass der ESP32 offline ist?

Abonnieren Sie das retained status-Topic. Der ESP32 veröffentlicht beim Verbinden online, und der Broker veröffentlicht die Last-Will-Nachricht offline, wenn die Verbindung ohne sauberes Trennen abbricht.

Verwandte Artikel

ESP32 NTP MQTT Zeitstempel: genaue Uhrzeit in jeder Nachricht

Synchronisieren Sie die ESP32-Uhr per NTP, formatieren Sie Zeitstempel in ISO 8601 und Epoch-Millisekunden und hängen Sie sie an MQTT-Payloads, damit Messwerte ihren echten Erfassungszeitpunkt behalten, auch wenn sie verspätet veröffentlicht werden.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.