Einen ESP32 per TLS mit einem Cloud-MQTT-Broker zu verbinden, ist der Schritt, der einen Prototyp auf dem Labortisch in etwas verwandelt, das Sie außerhalb Ihres Heimnetzes einsetzen können. In diesem Tutorial ziehen Sie einen ESP32-Sketch von einem lokalen Mosquitto-Broker auf Port 1883 zu einem ConnectMQ Serverless Broker auf Port 8883 um, mit korrekter Zertifikatsprüfung, Zugangsdaten, einem Status-Topic und zuverlässigen Reconnects.
Was sich ändert, wenn Sie den lokalen Broker verlassen
Die meisten ESP32-Projekte beginnen mit Mosquitto auf einem Raspberry Pi oder Laptop: reines TCP auf 1883, keine Authentifizierung, nur im LAN erreichbar. Der Umzug zu einem Cloud-MQTT-Broker ändert vier Dinge:
| Aspekt | Lokaler Broker | Cloud-Broker |
|---|---|---|
| Host | 192.168.1.20 |
broker.connectmq.com |
| Port | 1883 (reines TCP) | 8883 (TLS) |
| Authentifizierung | Oft keine | Benutzername, Passwort, Client ID |
| Netzwerk-Client | WiFiClient |
WiFiClientSecure |
| Zertifikat | Keines | Serverzertifikat, gegen eine CA geprüft |
| Erreichbarkeit | Nur LAN | Überall mit Internet |
Die MQTT-Logik selbst, also Topics, Publishes und Abonnements, bleibt gleich. Wenn Sie noch nie einen ESP32 mit MQTT verbunden haben, beginnen Sie mit dem ESP32-MQTT-Tutorial und kehren Sie dann hierher zurück.
Voraussetzungen
- Ein ESP32-Entwicklungsboard und ein USB-Kabel.
- Arduino IDE 2.x oder PlatformIO mit installiertem ESP32 Arduino Core (siehe die offizielle Dokumentation des Espressif Arduino Core).
- Die Bibliothek PubSubClient von Nick O'Leary.
- Ein kostenloser Serverless Broker. Registrieren Sie sich im Portal, wählen Sie den Plan Serverless (Free), legen Sie einen Broker an und kopieren Sie den generierten Benutzernamen, das Passwort und die Client ID.
Schritt 1: Das Root-CA-Zertifikat besorgen
Der Broker liefert ein Let's-Encrypt-Zertifikat aus. Der ESP32 muss der Root vertrauen, die die Kette signiert hat. Let's Encrypt führt bis zu ISRG Root X1, also betten Sie dieses Zertifikat ein. Sie können die Kette auf jedem Rechner mit OpenSSL selbst prüfen:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null
Sehen Sie sich die issuer-Zeilen am Anfang der Ausgabe an. Laden Sie dann ISRG Root X1 im PEM-Format von der Zertifikatsseite von Let's Encrypt herunter und fügen Sie es als Raw-String in Ihren Sketch ein. Wenn Sie die langlebige Root statt des eigenen Serverzertifikats einbetten, müssen Sie die Geräte nicht neu flashen, wenn das Serverzertifikat alle paar Monate erneuert wird.
Schritt 2: Der vollständige Sketch
Dieser Sketch verbindet sich mit dem WLAN, synchronisiert die Uhrzeit (die Prüfung von TLS-Zertifikaten braucht eine korrekte Uhr), verbindet sich per TLS mit Zugangsdaten zum Broker, publiziert einen retained Online-Status mit Last Will, abonniert ein Befehls-Topic und publiziert alle zehn Sekunden Telemetrie.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
// ---- WLAN ----
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
// ---- ConnectMQ ----
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";
const char* TOPIC_TELEMETRY = "devices/esp32-01/telemetry";
const char* TOPIC_STATUS = "devices/esp32-01/status";
const char* TOPIC_COMMAND = "devices/esp32-01/cmd";
// ISRG Root X1 (hier das vollständige PEM von Let's Encrypt einfügen)
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
PASTE_ISRG_ROOT_X1_PEM_CONTENT_HERE
-----END CERTIFICATE-----
)EOF";
WiFiClientSecure net;
PubSubClient mqtt(net);
unsigned long lastPublish = 0;
unsigned long lastReconnectAttempt = 0;
unsigned long reconnectDelay = 1000;
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\nWi-Fi connected, IP %s\n", WiFi.localIP().toString().c_str());
}
void syncTime() {
configTime(0, 0, "pool.ntp.org", "time.google.com");
Serial.print("Syncing time");
time_t now = time(nullptr);
while (now < 1700000000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println(" done");
}
void onMessage(char* topic, byte* payload, unsigned int length) {
String msg;
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
Serial.printf("Command on %s: %s\n", topic, msg.c_str());
if (msg == "led:on") digitalWrite(LED_BUILTIN, HIGH);
if (msg == "led:off") digitalWrite(LED_BUILTIN, LOW);
}
bool connectMqtt() {
Serial.print("Connecting to ConnectMQ... ");
bool ok = mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS,
TOPIC_STATUS, 1, true, "offline");
if (!ok) {
Serial.printf("failed, state=%d\n", mqtt.state());
return false;
}
Serial.println("connected");
mqtt.publish(TOPIC_STATUS, "online", true);
mqtt.subscribe(TOPIC_COMMAND, 1);
return true;
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
connectWiFi();
syncTime();
net.setCACert(ROOT_CA);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setKeepAlive(30);
mqtt.setBufferSize(512);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
if (!mqtt.connected()) {
unsigned long now = millis();
if (now - lastReconnectAttempt > reconnectDelay) {
lastReconnectAttempt = now;
if (connectMqtt()) {
reconnectDelay = 1000;
} else {
reconnectDelay = min(reconnectDelay * 2, 60000UL);
}
}
return;
}
mqtt.loop();
if (millis() - lastPublish > 10000) {
lastPublish = millis();
char payload[128];
snprintf(payload, sizeof(payload),
"{\"uptime_s\":%lu,\"rssi\":%d,\"heap\":%u}",
millis() / 1000, WiFi.RSSI(), ESP.getFreeHeap());
mqtt.publish(TOPIC_TELEMETRY, payload);
Serial.println(payload);
}
}
Hinweise zu den Entscheidungen:
- Retained Status plus Last Will. Der Broker publiziert in Ihrem Namen
offline, wenn der ESP32 ohne sauberen Disconnect wegfällt, und neue Subscriber sehen immer den letzten Status. Mehr zu diesem Muster unter ESP32 MQTT Last Will. - Nicht blockierendes Backoff. Reconnect-Versuche verdoppeln sich von einer Sekunde bis zu einer Minute, sodass eine Flotte den Broker nach einem Ausfall nicht bestürmt.
- Puffergröße. Der Standardpuffer von PubSubClient mit 256 Bytes verwirft größere Nachrichten stillschweigend; 512 Bytes geben Spielraum.
- Manche Boards haben kein
LED_BUILTIN. Definieren Sie es als LED-Pin Ihres Boards, oft GPIO 2.
Schritt 3: Vom Computer aus prüfen
Öffnen Sie den Browser-Client oder den Topic-Explorer im Portal und abonnieren Sie devices/#. Sie sollten alle zehn Sekunden Telemetrie sehen und online als retained Wert auf dem Status-Topic. Um einen Befehl über die Kommandozeile zu senden:
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID-cli \
-t devices/esp32-01/cmd -m 'led:on' -q 1
Verwenden Sie eine andere Client ID als die des ESP32. Wenn Ihr Plan eine einzige Client ID an die Zugangsdaten bindet, nutzen Sie stattdessen den Browser-Client im Portal oder legen Sie zusätzliche Zugangsdaten für Werkzeuge an.
Testen ohne Konto
Um den Sketch vor der Registrierung auszuprobieren, nutzen Sie den kostenlosen öffentlichen MQTT-Broker: Behalten Sie Port 8883 und dieselbe Root-CA bei, übergeben Sie leeren Benutzernamen und leeres Passwort (mqtt.connect(clientId)), wählen Sie eine beliebige, nicht leere, eindeutige Client ID und stellen Sie jedem Topic etwas Eindeutiges voran, etwa connectmq/demo/x8q2m/esp32-01/.... Der öffentliche MQTT-Broker ist für alle lesbar und nur für Tests und Prototypen gedacht, publizieren Sie dort also nichts Sensibles, und beachten Sie seine Limits: 64 KB pro Nachricht und 10 Nachrichten pro Sekunde und Client.
Fehlerbehebung
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
Sofort state=-2 |
TLS-Handshake fehlgeschlagen | Root-CA, Zeitsynchronisation und Port 8883 prüfen |
| Hängt bei der Zeitsynchronisation | NTP blockiert | UDP 123 freigeben oder einen erreichbaren NTP-Server nutzen |
state=4 |
Falscher Benutzername oder falsches Passwort | Zugangsdaten erneut aus dem Portal kopieren |
state=5 |
Nicht autorisiert | Client ID passt nicht zu den ausgegebenen Zugangsdaten |
| Verbindet sich und bricht in Schleife ab | Doppelte Client ID | Sicherstellen, dass jedes Gerät und Werkzeug eine eigene ID nutzt |
| Große Nachrichten kommen nie an | PubSubClient-Puffer zu klein | setBufferSize erhöhen |
| Funktioniert zu Hause, nicht vor Ort | Ausgehender Port 8883 blockiert | IT bitten, 8883 zu öffnen, oder WSS auf 8084 mit einem MQTT-over-WebSocket-Client nutzen |
Ausschließlich während der Entwicklung überspringt net.setInsecure() die Zertifikatsprüfung und hilft herauszufinden, ob die CA das Problem ist. Liefern Sie niemals Firmware damit aus. Bei tiefergehenden TLS-Problemen hilft der Leitfaden ESP32 MQTT SSL weiter.
Speicher, Energie und Lebenszyklus des Zertifikats
TLS auf einem ESP32 ist gut machbar, aber nicht kostenlos, und einige Designentscheidungen ersparen später Ärger.
Heap-Verbrauch. Eine TLS-Session benötigt einen spürbaren Teil des RAM für Puffer und Handshake. Auf einem einfachen ESP32 ist das für eine Verbindung unproblematisch, doch wenn Ihr Sketch zusätzlich einen Webserver, eine Kamera oder große JSON-Dokumente betreibt, beobachten Sie ESP.getFreeHeap() in der Telemetrie-Payload oben. Ein stetig sinkender Wert deutet meist auf ein Speicherleck in Ihrem eigenen Code hin, etwa String-Verkettung in einer Schleife, und nicht im TLS-Stack.
Die Verbindung offen halten. Der Handshake ist der teure Teil. Alle paar Sekunden zu verbinden, eine Nachricht zu publizieren und die Verbindung zu trennen, kostet weit mehr Energie und Zeit, als eine Session mit einem Keep Alive von 30 bis 60 Sekunden offen zu halten.
Batteriebetriebene Geräte. Wenn das Board zwischen Messungen in den Deep Sleep geht, zahlt jedes Aufwachen für WLAN-Anmeldung, NTP und einen TLS-Handshake. Senken Sie die Kosten, indem Sie seltener aufwachen und mehrere Messwerte in einem Publish bündeln, und speichern Sie die letzte NTP-Zeit eventuell im RTC-Speicher, damit Sie bei kurzem Schlaf eine vollständige Synchronisation überspringen können.
Ablauf des Root-Zertifikats. Wenn Sie die Root-CA statt des Leaf-Zertifikats des Servers einbetten, berühren routinemäßige Erneuerungen Ihre Geräte nie. Roots ändern sich jedoch irgendwann, halten Sie sich also einen Weg offen, das Zertifikat im Feld zu aktualisieren, entweder per OTA-Firmware-Update oder indem Sie das PEM im Flash ablegen, wo ein Provisionierungsablauf es ersetzen kann.
Checkliste für den Produktivbetrieb
- Speichern Sie Zugangsdaten in NVS oder provisionieren Sie sie bei der Fertigung, nicht in der Versionsverwaltung.
- Geben Sie jedem Gerät eine eigene Client ID.
- Betten Sie die Root-CA statt eines Leaf-Zertifikats ein.
- Verwenden Sie Reconnect-Backoff mit Jitter.
- Publizieren Sie einen retained Status mit Last Will.
- Halten Sie Payloads klein und strukturiert und Topics hierarchisch; in Industrieprojekten folgt die Hierarchie den ISA-95-Ebenen eines Unified Namespace (UNS), etwa
acme/werk-muenchen/montage/linie-2/esp32-01/telemetrystattdevices/esp32-01/telemetry. - Beobachten Sie die Geräte im Portal-Dashboard und legen Sie Schwellenwert-Alarmregeln für wichtige Kennzahlen an.
Das größere Bild zur Sicherheit finden Sie unter Best Practices für MQTT-Sicherheit.
Bringen Sie Ihren ESP32 auf ConnectMQ
Erstellen Sie unter app.connectmq.com ohne Kreditkarte einen kostenlosen Serverless Broker, fügen Sie die Zugangsdaten in den Sketch oben ein, und Ihr ESP32 spricht per TLS mit der Cloud. ConnectMQ ist ein verwalteter MQTT-Broker, sodass Zertifikate, Patches und Kapazität für Sie erledigt werden, während Sie sich auf die Firmware konzentrieren.
Häufig gestellte Fragen
Warum braucht mein ESP32 für TLS die korrekte Uhrzeit?
Die Zertifikatsprüfung kontrolliert Gültigkeitsdaten. Ohne NTP startet die Uhr des ESP32 im Jahr 1970, jedes Zertifikat wirkt ungültig, und der Handshake schlägt fehl.
Kann ich AsyncMqttClient statt PubSubClient verwenden?
Ja. AsyncMqttClient arbeitet nicht blockierend und unterstützt TLS je nach Core und Build-Optionen, während PubSubClient mit WiFiClientSecure das einfachste weit verbreitete Setup ist.
Welches Root-Zertifikat sollte ich für ConnectMQ einbetten?
ISRG Root X1, die Root von Let's Encrypt. Sie ist langlebig, sodass Erneuerungen des Serverzertifikats keine Firmware-Updates erfordern.
Bremst TLS den ESP32 spürbar aus?
Der Handshake dauert einen Moment und braucht zusätzlichen RAM, doch nach dem Verbindungsaufbau ist der Mehraufwand pro Nachricht gering. Halten Sie die Verbindung offen, statt sich für jeden Publish neu zu verbinden.



