Ein ESP32-DHT22-MQTT-Sensor ist das klassische erste IoT-Projekt, und es lohnt sich, ihn richtig zu bauen. In diesem Tutorial schließen Sie einen DHT22 an, lesen ihn ohne die üblichen NaN-Fehler aus, veröffentlichen Temperatur, Luftfeuchtigkeit und Hitzeindex als JSON an broker.connectmq.com und verfolgen die Messwerte anschließend live im Topic-Explorer von ConnectMQ.
Was Sie brauchen
- Ein ESP32-Entwicklungsboard (beliebiger klassischer ESP32, S3, C3 oder C6).
- Einen DHT22 (auch als AM2302 verkauft), entweder den nackten 4-Pin-Sensor oder ein 3-Pin-Breakout-Modul.
- Einen 10-kOhm-Widerstand, wenn Sie den nackten Sensor verwenden. Breakout-Module haben ihn meist bereits an Bord.
- Jumper-Kabel und ein Breadboard.
- Arduino IDE 2.x mit dem esp32-Board-Paket.
- Bibliotheken aus dem Bibliotheksverwalter: DHT sensor library von Adafruit, Adafruit Unified Sensor und PubSubClient von Nick O'Leary.
Den DHT22 an den ESP32 anschließen
| DHT22-Pin | Pin-Nummer am nackten Sensor | ESP32-Pin | Hinweise |
|---|---|---|---|
| VCC | 1 | 3V3 | 3,3 V funktioniert; Kabel kurz halten |
| DATA | 2 | GPIO 4 | 10-kOhm-Pull-up von DATA nach 3V3 |
| NC | 3 | nicht verbunden | Nur beim nackten Sensor |
| GND | 4 | GND |
GPIO 4 ist auf einem klassischen ESP32 eine sichere Wahl, weil er keine Rolle als Boot-Strapping-Pin hat. Vermeiden Sie GPIO 0, 2, 12 und 15 für Sensoren; ihr Zustand beim Reset beeinflusst den Bootvorgang. Auf C3 und C6 wählen Sie einen beliebigen freien GPIO, der kein Strapping-Pin ist, und ändern die Konstante DHT_PIN.
Halten Sie den Sensor einige Zentimeter vom ESP32 entfernt. Spannungsregler und Funkmodul des Boards erwärmen sich im Betrieb, und ein direkt auf das Board geklebter DHT22 misst ein bis zwei Grad zu viel.
Wie sich der DHT22 verhält
Der DHT22 nutzt ein Eindraht-Protokoll mit strengem Timing. Zwei Eigenschaften prägen den Code:
- Er kann nur alle 2 Sekunden ausgelesen werden. Schnelleres Auslesen liefert zwischengespeicherte Werte oder Fehler. Für MQTT-Telemetrie reicht ein Messwert alle 10 bis 60 Sekunden völlig aus.
- Lesevorgänge schlagen gelegentlich fehl. Ein WLAN-Interrupt im falschen Moment verfälscht das Bit-Timing, und die Bibliothek liefert
NaN. Produktionscode muss das prüfen und es erneut versuchen, statt Unsinn zu veröffentlichen.
Die Genauigkeit liegt bei etwa einem halben Grad Celsius und einigen Prozent relativer Luftfeuchtigkeit. Wenn Sie eine genauere Luftfeuchtigkeit oder einen Luftdruckwert benötigen, verwendet das ESP32-BME280-Tutorial einen I2C-Sensor, der weniger timingempfindlich ist.
Topic- und Payload-Design
Verwenden Sie ein eindeutiges Präfix, damit Ihre Daten nicht mit denen anderer Nutzer des öffentlichen MQTT-Brokers kollidieren:
| Topic | Retained | Payload |
|---|---|---|
connectmq/demo/dht7w3p/telemetry |
Nein | {"temperature":22.4,"humidity":48.1,"heatIndex":22.1,"rssi":-61} |
connectmq/demo/dht7w3p/status |
Ja | online oder offline (Last Will) |
Ein JSON-Objekt pro Messung hält zusammengehörige Werte beieinander, sodass ein Konsument nie eine Temperatur mit einem Feuchtewert aus einem anderen Moment kombiniert. Für die Überwachung des Hallenklimas in einem Werk ersetzen Sie das Demo-Präfix später durch einen Pfad entlang der Anlagenhierarchie, etwa acme/werk-muenchen/halle-2/linie-1/klima/telemetry.
Der vollständige ESP32-DHT22-MQTT-Sketch
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ---------- settings ----------
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* PREFIX = "connectmq/demo/dht7w3p"; // durch eine eigene zufällige ID ersetzen
const uint8_t DHT_PIN = 4;
const uint32_t INTERVAL_MS = 15000;
DHT dht(DHT_PIN, DHT22);
WiFiClient net;
PubSubClient mqtt(net);
String clientId, topicTelemetry, topicStatus;
unsigned long lastRead = 0;
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print(".");
}
Serial.printf(" connected, IP %s\n", WiFi.localIP().toString().c_str());
}
void connectMqtt() {
while (!mqtt.connected()) {
Serial.print("MQTT... ");
if (mqtt.connect(clientId.c_str(), topicStatus.c_str(), 1, true, "offline")) {
Serial.println("connected");
mqtt.publish(topicStatus.c_str(), "online", true);
} else {
Serial.printf("failed (state %d), retry in 5 s\n", mqtt.state());
delay(5000);
}
}
}
bool readDht(float& t, float& h) {
for (int attempt = 0; attempt < 3; attempt++) {
h = dht.readHumidity();
t = dht.readTemperature(); // Celsius
if (!isnan(h) && !isnan(t)) return true;
delay(2100); // DHT22 braucht 2 s zwischen Lesevorgängen
}
return false;
}
void setup() {
Serial.begin(115200);
dht.begin();
clientId = "esp32-dht22-" + String((uint32_t)ESP.getEfuseMac(), HEX);
topicTelemetry = String(PREFIX) + "/telemetry";
topicStatus = String(PREFIX) + "/status";
connectWifi();
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
connectMqtt();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWifi();
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
if (millis() - lastRead < INTERVAL_MS) return;
lastRead = millis();
float t, h;
if (!readDht(t, h)) {
Serial.println("DHT22 read failed three times, skipping this cycle");
mqtt.publish(topicStatus.c_str(), "sensor-error", true);
return;
}
float heatIndex = dht.computeHeatIndex(t, h, false); // false = Celsius
char payload[160];
snprintf(payload, sizeof(payload),
"{\"temperature\":%.1f,\"humidity\":%.1f,\"heatIndex\":%.1f,\"rssi\":%d}",
t, h, heatIndex, WiFi.RSSI());
if (mqtt.publish(topicTelemetry.c_str(), payload)) {
Serial.println(payload);
} else {
Serial.println("publish failed");
}
}
Ein Hinweis zum Status-Topic: Nach einem Sensorfehler veröffentlicht der Sketch sensor-error als Retained-Status, und die nächste erfolgreiche Verbindung überschreibt ihn mit online. Wenn Sie ein separates Health-Topic bevorzugen, ergänzen Sie eines; entscheidend ist, dass ein defekter Sensor sichtbar wird, statt stumm zu bleiben.
Intervall wählen und Energie sparen
Das 15-Sekunden-Intervall im Sketch eignet sich für eine Demo am Schreibtisch. Für echte Räume sollten Sie überlegen, wofür die Daten gedacht sind. Komfortüberwachung braucht selten mehr als einen Messwert pro Minute, ein Alarm für ein Gewächshaus oder einen Serverschrank vielleicht alle 30 Sekunden. Seltenere Veröffentlichungen verringern den Broker-Verkehr und machen die Daten leichter darstellbar.
Läuft der Knoten mit Batterien, ist eine ständig gehaltene WLAN-Verbindung der größte Stromfresser. Besser ist dieser Ablauf: aus dem Deep Sleep aufwachen, Sensor mit Strom versorgen, zwei Sekunden warten, auslesen, mit WLAN und Broker verbinden, veröffentlichen, sauber trennen und dann esp_deep_sleep_start() mit Timer-Wakeup aufrufen. In diesem Modus ist der Last Will weniger nützlich, weil jeder Schlaf wie eine Trennung aussieht; veröffentlichen Sie daher stattdessen einen Retained-Wert lastSeen und lassen Sie das Dashboard Sensoren markieren, die sich einige Intervalle lang nicht gemeldet haben. Versorgen Sie den DHT22 über einen GPIO-Pin, wenn er im Schlaf vollständig aus sein soll, und warten Sie nach dem Einschalten die vollen zwei Sekunden bis zum ersten Lesevorgang.
Mit der Kommandozeile testen
Abonnieren Sie mit dem Mosquitto-Client und einer beliebigen eindeutigen Client ID:
mosquitto_sub -h broker.connectmq.com -p 1883 -i dht-viewer-7w3p -t "connectmq/demo/dht7w3p/#" -v
Hauchen Sie den Sensor an, und die Luftfeuchtigkeit springt innerhalb von ein oder zwei Messungen nach oben. Denken Sie daran, dass der öffentliche MQTT-Broker geteilt und für jeden lesbar ist – gut für eine Schreibtisch-Demo, nicht aber für Daten, die privat bleiben sollen. Die Anleitung zum kostenlosen öffentlichen MQTT-Broker listet seine Grenzen auf.
ESP32-DHT22-MQTT-Daten im Topic-Explorer ansehen
Die Kommandozeile ist schnell, doch bei mehreren Sensoren ist eine grafische Ansicht angenehmer. Erstellen Sie ein kostenloses Konto unter app.connectmq.com, wählen Sie den kostenlosen Serverless-Plan, legen Sie einen Broker an und kopieren Sie den generierten Benutzernamen, das Passwort und die Client ID. Ändern Sie den Sketch dann so, dass er sich mit Zugangsdaten über TLS verbindet:
#include <WiFiClientSecure.h>
WiFiClientSecure net; // ersetzt WiFiClient
PubSubClient mqtt(net);
// ISRG Root X1 (Let's Encrypt) im PEM-Format, kopiert von letsencrypt.org
static const char ROOT_CA[] = R"PEM(
-----BEGIN CERTIFICATE-----
...paste ISRG Root X1 here...
-----END CERTIFICATE-----
)PEM";
// in setup(), vor connectMqtt():
net.setCACert(ROOT_CA);
mqtt.setServer("broker.connectmq.com", 8883);
// in connectMqtt():
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD",
topicStatus.c_str(), 1, true, "offline");
Öffnen Sie Ihren Broker im ConnectMQ-Portal und wechseln Sie zum Live-Topic-Explorer. Ihr Telemetrie-Topic erscheint im Baum, und jeder neue Messwert wird direkt aktualisiert. Im selben Portal können Sie eine Alarmregel mit Schwellwert anlegen, zum Beispiel Luftfeuchtigkeit über 70 Prozent, und ausgelöste Alarme im Alarmprotokoll prüfen. Für langfristige Diagramme betreiben Sie Node-RED, Telegraf oder einen kleinen Python-Subscriber, der sich als MQTT-Client mit dem Broker verbindet und in die Datenbank Ihrer Wahl schreibt.
Fehlerbehebung
- Jeder Lesevorgang liefert NaN. Prüfen Sie den Pull-up-Widerstand und ob DATA wirklich am in
DHT_PINeingestellten Pin hängt. Ein fehlender Pull-up ist bei nackten Sensoren die häufigste Ursache. - Gelegentliches NaN. Unter WLAN-Last normal. Die Wiederholungsschleife fängt das ab; verkürzen Sie die 2-Sekunden-Pause nicht.
- Temperatur hängt oder ändert sich nur langsam. Der DHT22 reagiert träge und braucht Dutzende Sekunden, um sich einzupendeln. Das liegt am Sensor, nicht an MQTT.
- Messwerte ein paar Grad zu hoch. Eigenerwärmung durch das Board. Setzen Sie den Sensor mit einem kurzen Kabel ab.
- Lange Kabel verursachen Fehler. Das Eindraht-Protokoll ist empfindlich. Halten Sie das Kabel kurz oder verringern Sie den Pull-up-Wert etwas.
- MQTT-State -2. Die TCP-Verbindung ist fehlgeschlagen; prüfen Sie das WLAN und ob ausgehender Verkehr auf Port 1883 erlaubt ist.
Wie Sie größere Payloads sicher formatieren, zeigt ArduinoJson für ESP32-MQTT-Payloads. Wenn Sie mehrere Temperaturmesspunkte, aber keine Luftfeuchtigkeit benötigen, passt das DS18B20-Multisensor-Projekt besser.
Starten Sie mit ConnectMQ
Ein DHT22-Knoten wird erst nützlich, wenn seine Daten an einem zuverlässigen Ort landen. ConnectMQ bietet Ihnen einen Cloud-MQTT-Broker mit TLS auf Port 8883, MQTT 3.1.1 und 5.0, einem Live-Topic-Explorer und Alarmregeln sowie einen kostenlosen Serverless-Plan ohne Kreditkarte. Flashen Sie die TLS-Version dieses Sketches mit Ihren Zugangsdaten und machen Sie mit dem ESP32-IoT-Tutorial für weitere Sensor- und Geräteprojekte weiter.
Häufig gestellte Fragen
Wie oft sollte ein ESP32 DHT22-Messwerte per MQTT veröffentlichen?
Alle 10 bis 60 Sekunden ist für die Raumüberwachung üblich. Der DHT22 lässt sich nicht häufiger als alle 2 Sekunden auslesen, und seine träge Reaktion macht schnelleres Veröffentlichen sinnlos.
Kann ich statt eines DHT22 einen DHT11 verwenden?
Ja. Ändern Sie im Konstruktor DHT22 in DHT11. Der DHT11 hat eine geringere Genauigkeit und einen kleineren Messbereich, daher sind DHT22 oder BME280 für alles jenseits einer Demo die bessere Wahl.
Sollte ich Temperatur und Luftfeuchtigkeit auf getrennten Topics veröffentlichen?
Beides funktioniert. Ein einzelnes JSON-Objekt hält die Werte eines Zeitpunkts zusammen, während getrennte Topics praktisch für Tools sind, die eine Zahl pro Topic erwarten.
Warum ein Retained-Status-Topic mit Last Will verwenden?
Der Broker veröffentlicht die Retained-Nachricht offline, wenn der ESP32 verschwindet, ohne sich abzumelden. So kann jedes Dashboard einen ausgefallenen Sensor von einem ruhigen unterscheiden.



