IoT-Tutorials

ESP32 MQTT Tutorial: Sensordaten senden und eine LED mit der Arduino IDE steuern

Verbinden Sie einen ESP32 mit der Arduino IDE und PubSubClient mit einem MQTT Broker, senden Sie Temperaturwerte als JSON, steuern Sie die Onboard-LED über die Kommandozeile und steigen Sie mit Zugangsdaten aus dem Portal auf TLS um.

ESP32-Entwicklungsboard, das in einem ESP32-MQTT-Tutorial Temperaturdaten über MQTT sendet
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

Dieses ESP32-MQTT-Tutorial führt Sie von einem leeren Arduino-Sketch zu einem Board, das JSON-Telemetrie veröffentlicht, auf Befehle hört und seine Onboard-LED schaltet. Zuerst verbinden Sie sich mit dem kostenlosen öffentlichen MQTT-Broker broker.connectmq.com, danach stellen Sie denselben Sketch mit Zugangsdaten aus dem ConnectMQ-Portal auf TLS über Port 8883 um. Jeder Schritt wird mit mosquitto_sub und mosquitto_pub von Ihrem Rechner aus überprüft.

Was Sie bauen

Das fertige Gerät erledigt drei Dinge:

  1. Es verbindet sich mit dem WLAN und einem MQTT Broker, inklusive automatischer Reconnects.
  2. Es veröffentlicht alle fünf Sekunden eine JSON-Nachricht wie {"temperature":23.41,"uptime":125}.
  3. Es abonniert ein Befehls-Topic und schaltet die Onboard-LED ein oder aus, wenn es ON, OFF oder TOGGLE empfängt.

Die Topic-Struktur bündelt alles unter einem eindeutigen Präfix:

Topic Richtung Beispiel-Payload
connectmq/demo/<deviceId>/telemetry ESP32 veröffentlicht {"temperature":23.41,"uptime":125}
connectmq/demo/<deviceId>/led/set ESP32 abonniert ON, OFF, TOGGLE
connectmq/demo/<deviceId>/led/state ESP32 veröffentlicht (retained) ON oder OFF

Für dieses Tutorial genügt ein Demo-Präfix. In Industrie-4.0-Projekten folgen Topics dagegen meist der ISA-95-Anlagenhierarchie, etwa acme/werk-muenchen/montage/linie-1/zelle-3/telemetry, und bilden so einen Unified Namespace (UNS), den SPS, MES und Dashboards gemeinsam nutzen. Wenn Ihnen MQTT-Begriffe wie Topic, QoS oder Retained Message noch neu sind, lesen Sie zuerst Was ist ein MQTT Broker?.

Voraussetzungen

  • Ein ESP32-Entwicklungsboard (jedes ESP32 DevKit funktioniert; die meisten haben eine blaue LED an GPIO 2).
  • Arduino IDE 2.x mit dem über den Boardverwalter installierten Board-Paket esp32.
  • Die Bibliothek PubSubClient von Nick O'Leary, installiert über den Bibliotheksverwalter.
  • Ein 2,4-GHz-WLAN (der ESP32 unterstützt kein 5 GHz).
  • Optional: die Mosquitto-Kommandozeilen-Clients (mosquitto_sub, mosquitto_pub) auf Ihrem Rechner zum Testen.
  • Optional: ein DHT22-Sensor. Der Sketch simuliert die Temperatur standardmäßig, zusätzliche Hardware ist also nicht nötig.

Broker-Endpunkte für ESP32 MQTT

broker.connectmq.com stellt vier Listener bereit. Der ESP32 nutzt die ersten beiden; die WebSocket-Ports sind für Browser gedacht.

Protokoll Port Verwendung in diesem Tutorial
MQTT (TCP) 1883 Teil 1, anonymer Zugang zum öffentlichen MQTT-Broker
MQTT über TLS 8883 Teil 2, Zugangsdaten aus dem Portal
WebSocket 8083 Nicht verwendet (Browser-Clients)
Secure WebSocket 8084 Nicht verwendet (Browser-Clients)

Für den öffentlichen MQTT-Broker brauchen Sie kein Konto: Verbinden Sie sich ohne Benutzername und Passwort und mit einer nicht leeren, eindeutigen Client ID. Denken Sie daran, dass alles, was dort veröffentlicht wird, für jeden lesbar ist – senden Sie also nur Demodaten. Der öffentliche MQTT-Broker erlaubt 64 KB pro Nachricht, 10 Nachrichten pro Sekunde und 10 Abonnements pro Client sowie bis zu 8 Topic-Ebenen – mehr als genug für dieses Projekt. Details finden Sie im Leitfaden zum kostenlosen öffentlichen MQTT-Broker.

Teil 1: ESP32 MQTT über unverschlüsseltes TCP (Port 1883)

Eine eindeutige Client ID aus der MAC-Adresse erzeugen

Client IDs müssen eindeutig sein: Verbinden sich zwei Clients mit derselben ID, trennt der Broker den älteren. Jeder ESP32 hat eine ab Werk programmierte MAC-Adresse – die perfekte Quelle für eine stabile, eindeutige ID. Der Sketch erzeugt etwas wie esp32-a1b2c3d4e5f6 und verwendet es auch im Topic-Präfix.

Der vollständige Sketch

Legen Sie einen neuen Sketch an, fügen Sie den folgenden Code ein und tragen Sie die SSID und das Passwort Ihres WLANs ein.

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

// ---- WLAN ----
const char* WIFI_SSID     = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

// ---- MQTT ----
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;

const int LED_PIN = 2;                 // Onboard-LED bei den meisten ESP32 DevKits
const unsigned long PUBLISH_INTERVAL_MS = 5000;

WiFiClient netClient;
PubSubClient mqtt(netClient);

String deviceId;
String topicTelemetry, topicLedSet, topicLedState;
unsigned long lastPublish = 0;
unsigned long lastReconnectAttempt = 0;
bool ledOn = false;

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("\nWi-Fi connected, IP: ");
  Serial.println(WiFi.localIP());
}

String makeDeviceId() {
  uint8_t mac[6];
  WiFi.macAddress(mac);
  char buf[20];
  snprintf(buf, sizeof(buf), "esp32-%02x%02x%02x%02x%02x%02x",
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}

void publishLedState() {
  mqtt.publish(topicLedState.c_str(), ledOn ? "ON" : "OFF", true);  // retained
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  msg.trim();
  Serial.printf("Message on %s: %s\n", topic, msg.c_str());

  if (String(topic) != topicLedSet) return;
  if (msg.equalsIgnoreCase("ON"))          ledOn = true;
  else if (msg.equalsIgnoreCase("OFF"))    ledOn = false;
  else if (msg.equalsIgnoreCase("TOGGLE")) ledOn = !ledOn;
  else return;                             // alles andere ignorieren

  digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
  publishLedState();
}

bool connectMqtt() {
  Serial.printf("Connecting to MQTT as %s ... ", deviceId.c_str());
  if (mqtt.connect(deviceId.c_str())) {     // ohne Benutzername/Passwort: öffentlicher Bereich
    Serial.println("connected");
    mqtt.subscribe(topicLedSet.c_str(), 1);
    publishLedState();
    return true;
  }
  Serial.printf("failed, state=%d\n", mqtt.state());
  return false;
}

float readTemperature() {
  // Simulierter Sensor: 22-25 °C mit etwas Rauschen
  return 23.5 + 1.5 * sin(millis() / 60000.0) + random(-20, 20) / 100.0;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  connectWiFi();
  deviceId = makeDeviceId();
  String base = "connectmq/demo/" + deviceId;
  topicTelemetry = base + "/telemetry";
  topicLedSet    = base + "/led/set";
  topicLedState  = base + "/led/state";
  Serial.println("Topic prefix: " + base);

  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setBufferSize(512);   // Standard sind 256 Bytes, siehe unten
  mqtt.setKeepAlive(30);
}

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

  if (!mqtt.connected()) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt > 5000) {   // nicht blockierender Neuversuch alle 5 s
      lastReconnectAttempt = now;
      if (connectMqtt()) lastReconnectAttempt = 0;
    }
    return;
  }

  mqtt.loop();   // eingehende Nachrichten und Keepalive verarbeiten

  if (millis() - lastPublish >= PUBLISH_INTERVAL_MS) {
    lastPublish = millis();
    char json[96];
    snprintf(json, sizeof(json), "{\"temperature\":%.2f,\"uptime\":%lu}",
             readTemperature(), millis() / 1000);
    mqtt.publish(topicTelemetry.c_str(), json);
    Serial.printf("Published %s\n", json);
  }
}

Wählen Sie Board und Port aus, laden Sie den Sketch hoch und öffnen Sie den seriellen Monitor mit 115200 Baud. Sie sollten die IP-Adresse, das Topic-Präfix und alle fünf Sekunden eine Zeile Published sehen. Kopieren Sie die Geräte-ID aus dem Topic-Präfix; Sie brauchen sie im nächsten Schritt.

So funktioniert die Reconnect-Logik

Der Sketch blockiert nie endlos, während er auf den Broker wartet. Bricht die Verbindung ab, versucht loop() es alle fünf Sekunden erneut, während der Rest des Programms weiterläuft. Zwei Details sind wichtig:

  • mqtt.loop() muss häufig laufen. Es liest eingehende Pakete und sendet Keepalive-Pings; rufen Sie in Ihrer Schleife delay(10000) auf, hält der Broker den Client für tot.
  • Nach dem Wiederverbinden abonniert der Sketch erneut. Mit der standardmäßigen Clean Session überstehen Abonnements keinen Verbindungsabbruch, daher ist das erneute Abonnieren in connectMqtt() unverzichtbar.

mqtt.state() liefert bei einem Fehler einen numerischen Code: -2 bedeutet meist ein Netzwerkproblem (falscher Host, kein Internet), positive Werte stammen aus dem CONNACK des Brokers, zum Beispiel 2 für eine abgelehnte Kennung.

Einen echten DHT22-Sensor verwenden

Für echte Daten installieren Sie die „DHT sensor library“ von Adafruit, verbinden den Datenpin des Sensors mit GPIO 4 und ersetzen readTemperature():

#include <DHT.h>
DHT dht(4, DHT22);   // dht.begin() in setup() aufrufen

float readTemperature() {
  float t = dht.readTemperature();
  return isnan(t) ? -127.0 : t;   // statt NaN einen Platzhalterwert senden
}

Mit mosquitto_sub und mosquitto_pub prüfen

Öffnen Sie ein Terminal und abonnieren Sie alles unter dem Präfix Ihres Geräts. Ersetzen Sie esp32-a1b2c3d4e5f6 durch die ID aus dem seriellen Monitor.

mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-watch-81f3 \
  -t "connectmq/demo/esp32-a1b2c3d4e5f6/#" -v

Die Ausgabe sieht so aus:

connectmq/demo/esp32-a1b2c3d4e5f6/led/state OFF
connectmq/demo/esp32-a1b2c3d4e5f6/telemetry {"temperature":23.41,"uptime":125}
connectmq/demo/esp32-a1b2c3d4e5f6/telemetry {"temperature":23.47,"uptime":130}

Schalten Sie nun die LED aus einem zweiten Terminal:

mosquitto_pub -h broker.connectmq.com -p 1883 -i laptop-cmd-81f3 \
  -t "connectmq/demo/esp32-a1b2c3d4e5f6/led/set" -q 1 -m "TOGGLE"

Die LED wechselt ihren Zustand, und der Subscriber gibt den neuen Retained-Wert von led/state aus. Weil der Zustand retained ist, erhält jedes Dashboard, das später abonniert, sofort den aktuellen Wert. Das Abonnement verwendet QoS 1, damit der Broker den Befehl bestätigt; die Abwägungen erklärt MQTT QoS-Stufen erklärt.

Sie können auch beobachten, wie das Gerät kommt und geht: Abonnieren Sie $SYS/broker/connection/esp32-a1b2c3d4e5f6/state, und Sie sehen 1, wenn es sich verbindet, und 0, wenn es sich trennt.

Die Puffergröße von PubSubClient

PubSubClient reserviert einen einzigen Puffer für ein- und ausgehende Pakete, und der ist standardmäßig nur 256 Bytes groß. Dieser Puffer muss das gesamte MQTT-Paket aufnehmen: festen Header, Topic-Namen und Payload. Ein langes Topic plus ein mittelgroßes JSON-Dokument überschreiten das schnell. Passiert das, liefert publish() stillschweigend false zurück, und eingehende Nachrichten, die nicht hineinpassen, werden verworfen.

Rufen Sie setBufferSize() vor dem Verbinden auf und prüfen Sie den Rückgabewert von publish():

Payload-Größe (ca.) Empfohlenes setBufferSize()
Bis ca. 150 Bytes 256 (Standard)
Bis ca. 400 Bytes 512
Bis ca. 1.800 Bytes 2048
Größere Dokumente Nachricht aufteilen oder nur geänderte Felder senden

Jedes zusätzliche Byte geht vom RAM des ESP32 ab – dimensionieren Sie den Puffer also für Ihre tatsächlichen Nachrichten und nicht für das Maximum des Brokers.

Teil 2: ESP32 MQTT mit TLS auf Port 8883 absichern

Der öffentliche MQTT-Broker ist perfekt zum Lernen, doch Produktivgeräte sollten TLS und eigene Zugangsdaten verwenden. Melden Sie sich unter https://app.connectmq.com an, legen Sie einen Broker an und kopieren Sie den generierten Benutzernamen, das Passwort und die Client ID. Authentifizierte Clients landen in Ihrem eigenen, isolierten Mandanten statt im öffentlichen Bereich.

Client IDs sind bei authentifizierten Verbindungen auf die ausgegebene ID und deren _suffix-Varianten beschränkt. Geben Sie jedem Gerät ein eigenes Suffix, zum Beispiel YOUR_CLIENT_ID_esp32a, und Ihrem Laptop ein weiteres.

Änderungen am Sketch

Nur Netzwerk-Client, Port, Zugangsdaten und Topics ändern sich. Laden Sie das Zertifikat ISRG Root X1 (isrgrootx1.pem) von Let's Encrypt herunter; das Zertifikat des Brokers führt in seiner Kette darauf zurück. Fügen Sie den Inhalt in den Sketch ein:

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

const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 8883;
const char* MQTT_USER = "YOUR_USERNAME";
const char* MQTT_PASS = "YOUR_PASSWORD";
const char* MQTT_CLIENT_ID = "YOUR_CLIENT_ID_esp32a";

// ISRG Root X1 (vollständiges PEM aus isrgrootx1.pem einfügen)
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
...paste the certificate body here...
-----END CERTIFICATE-----
)EOF";

WiFiClientSecure netClient;
PubSubClient mqtt(netClient);

void syncClock() {
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  time_t now = time(nullptr);
  while (now < 1700000000) { delay(250); now = time(nullptr); }
}

// in setup(), nach connectWiFi():
//   syncClock();
//   netClient.setCACert(ROOT_CA);
//   mqtt.setServer(MQTT_HOST, MQTT_PORT);
//   Topic-Basis: "plant1/" + String(MQTT_CLIENT_ID) statt "connectmq/demo/..."

bool connectMqtt() {
  if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS)) {
    mqtt.subscribe(topicLedSet.c_str(), 1);
    publishLedState();
    return true;
  }
  return false;
}

Einige Hinweise:

  • setCACert() sorgt dafür, dass der ESP32 überprüft, ob er wirklich mit broker.connectmq.com spricht. Die vorherige Zeitsynchronisierung per NTP erlaubt dem TLS-Stack, die Gültigkeitsdaten des Zertifikats zu prüfen.
  • setInsecure() überspringt die Prüfung vollständig. Für einen Fünf-Minuten-Test ist das praktisch, es entfernt aber den Schutz vor Man-in-the-Middle-Angriffen – liefern Sie es also niemals aus.
  • TLS benötigt spürbar mehr RAM, und der erste Handshake dauert einen Moment. Das ist beim ESP32 normal.
  • Halten Sie Zugangsdaten aus Ihrem Git-Repository heraus, etwa in einer separaten secrets.h, die in .gitignore steht. Die Checkliste zur MQTT-Sicherheit behandelt die Speicherung von Geräte-Secrets und weitere Härtungsmaßnahmen.

Die sichere Verbindung prüfen

Abonnieren Sie mit Ihren eigenen Zugangsdaten und einem anderen Suffix. Unter Linux können Sie den CA-Speicher des Systems mit --capath /etc/ssl/certs nutzen; auf jedem Betriebssystem können Sie das heruntergeladene Root-Zertifikat übergeben:

mosquitto_sub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_laptop \
  -t "plant1/#" -v

Einen Befehl senden Sie auf die gleiche Weise:

mosquitto_pub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_cli \
  -t "plant1/YOUR_CLIENT_ID_esp32a/led/set" -q 1 -m "ON"

Sie können auch den MQTT-Client im Browser oder den Live-Topic-Explorer im ConnectMQ-Portal öffnen, um die Telemetrie ohne Installation zu verfolgen.

Fehlerbehebung

Symptom Wahrscheinliche Ursache Lösung
state=-2 in einer Schleife Kein Internet, falscher Host oder blockierter Port WLAN prüfen, Port 1883 versuchen, Firewall prüfen
state=2 Client ID abgelehnt Nicht leere, eindeutige ID verwenden; mit Zugangsdaten die ausgegebene ID oder eine _suffix-Variante nutzen
state=4 oder 5 Falsche Zugangsdaten Benutzername und Passwort erneut aus dem Portal kopieren
Veröffentlichungen stoppen, kein Fehler sichtbar Nachricht größer als der Puffer setBufferSize() erhöhen, Rückgabewert von publish() prüfen
Ständige Verbindungsabbrüche mqtt.loop() wird zu selten aufgerufen, oder ein anderer Client nutzt dieselbe ID Lange delay()-Aufrufe entfernen, jedem Client eine eigene ID geben
TLS schlägt fehl, unverschlüsseltes TCP funktioniert Falsche CA, Uhrzeit nicht gesetzt ISRG Root X1 verwenden, Zeit vor dem Verbinden per NTP synchronisieren

Starten Sie mit ConnectMQ

Sie haben jetzt einen ESP32, der Telemetrie streamt und Befehle über MQTT entgegennimmt – zuerst auf dem öffentlichen MQTT-Broker und dann über TLS mit eigenen Zugangsdaten. Wenn Sie über Demodaten hinausgehen möchten, legen Sie im kostenlosen Serverless-Plan unter https://app.connectmq.com einen verwalteten MQTT-Broker an – ohne Kreditkarte – und behalten Sie Ihre Flotte mit Dashboard, Geräteansicht und Schwellwert-Alarmen des Portals im Blick.

Häufig gestellte Fragen

Welche MQTT-Bibliothek sollte ich auf dem ESP32 mit der Arduino IDE verwenden?

PubSubClient ist die am weitesten verbreitete Wahl: klein, stabil und kompatibel mit WiFiClient und WiFiClientSecure. Sie unterstützt MQTT 3.1.1 mit QoS 0 beim Veröffentlichen und QoS 0 oder 1 bei Abonnements, was die meisten Sensorprojekte abdeckt.

Warum schlägt das Veröffentlichen auf meinem ESP32 ohne Fehlermeldung fehl?

Meistens ist das Paket größer als der Standardpuffer von PubSubClient mit 256 Bytes. Rufen Sie vor dem Verbinden mqtt.setBufferSize(512) oder einen größeren Wert auf und prüfen Sie den booleschen Rückgabewert von publish().

Brauche ich ein Konto, um ESP32 MQTT mit broker.connectmq.com zu testen?

Nein. Verbinden Sie sich ohne Benutzername und mit einer eindeutigen Client ID mit Port 1883 oder 8883, um den öffentlichen MQTT-Broker zu nutzen. Für echte Daten legen Sie ein kostenloses Konto an und verwenden die generierten Zugangsdaten.

Kann ich setInsecure() mit WiFiClientSecure verwenden?

Nur für schnelle Tests. Der Datenverkehr wird zwar verschlüsselt, die Prüfung des Servers aber übersprungen, sodass sich ein Angreifer als Broker ausgeben könnte. Verwenden Sie in allem, was Sie ausrollen, setCACert() mit ISRG Root X1.

Wie gebe ich mehreren ESP32-Boards auf einem Broker eigene Client IDs?

Hängen Sie an die im Portal ausgegebene Client ID ein Suffix an, etwa YOUR_CLIENT_ID_esp32a und YOUR_CLIENT_ID_esp32b. ConnectMQ akzeptiert bei authentifizierten Verbindungen die ausgegebene ID und deren _suffix-Varianten.

Verwandte Artikel

Die besten MQTT-CLI-Tools zum Testen, Skripten und Debuggen

Ein praxisnaher Vergleich von mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, der MQTT.js-CLI, curl und mqttui – mit demselben Test gegen broker.connectmq.com, damit Sie das richtige Tool zum Skripten und Debuggen wählen.

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.