ESP32 MQTT Anleitung: Mosquitto, Home Assistant und ESPHome
ESP32 MQTT Anleitung: Mosquitto installieren, mit PubSubClient Sensordaten senden und ein Relais schalten, Home Assistant Discovery, ESPHome-YAML und QoS.
Die Kombination ESP32 und MQTT ist der verbreitetste Weg, Sensoren und Relais im Haus miteinander und mit einem Smart-Home-System wie Home Assistant sprechen zu lassen. MQTT ist ein schlankes Nachrichtenprotokoll für kleine Geräte: Der ESP32 legt die gemessene Temperatur in einem Topic wie "ev/salon/sicaklik" ab, und jedes Programm, das dieses Topic abonniert hat, bekommt den Wert sofort. Genauso erreicht der Befehl "ON", den du in der Home-Assistant-App auf dem Handy auslöst, den ESP32 in deutlich unter einer Sekunde und schaltet das Relais. Anders als bei HTTP wird nicht für jede Anfrage eine neue Verbindung aufgebaut; MQTT arbeitet über eine einzige offene Verbindung mit Headern ab zwei Byte und ist dadurch schnell und schont WLAN und Prozessor.
In dieser Anleitung baust du von Grund auf einen funktionierenden Smart-Home-Knoten. Zuerst lernst du die MQTT-Begriffe Broker, Topic, Publish und Subscribe kennen, dann installierst du den Mosquitto-Broker in Home Assistant oder auf einem Raspberry Pi. Anschließend sendest du mit der Bibliothek PubSubClient Temperatur und Luftfeuchtigkeit eines DHT22 vom ESP32, hörst auf Relais-Befehle und ergänzt MQTT Discovery, damit das Gerät von selbst in Home Assistant erscheint. Außerdem behandeln wir QoS und Retain, die Last-Will-Nachricht (LWT), die das Gerät bei einem Verbindungsabbruch als "nicht verfügbar" markiert, die ESPHome-Alternative ganz ohne C++, die Unterschiede bei ESP8266 und Raspberry Pi Pico W sowie die häufigsten Fehler.

Was ist MQTT und wie funktioniert es?
MQTT (Message Queuing Telemetry Transport) arbeitet nach dem Publish/Subscribe-Prinzip. In der Mitte sitzt ein Server, der Broker; ESP32, Home Assistant, dein Handy oder ein Programm auf dem PC verbinden sich als Clients mit ihm. Die Clients kennen sich nicht und wissen nichts von den IP-Adressen der anderen: Einer veröffentlicht eine Nachricht in einem Topic, und der Broker leitet sie an alle weiter, die dieses Topic abonniert haben. Stell es dir wie eine Post vor: Der ESP32 wirft einen Brief in das Fach "ev/salon/sicaklik", und Home Assistant, das dieses Fach beobachtet, bekommt ihn sofort. Weil immer der Client die Verbindung aufbaut, braucht der ESP32 weder eine feste IP noch eine Portfreigabe im Router; er muss nur die Adresse des Brokers kennen.
| Begriff | Wozu dient er? | Beispiel |
|---|---|---|
| Broker | Server, der Nachrichten empfängt und an die Abonnenten verteilt | Mosquitto, der Mosquitto-Broker in Home Assistant |
| Client | Jedes Gerät oder Programm, das sich mit dem Broker verbindet; jedes hat eine eindeutige Client-ID | ESP32, Home Assistant, MQTT Explorer |
| Topic | Adresse einer Nachricht aus Ebenen, die durch Schrägstriche getrennt sind | ev/salon/sicaklik |
| Publish (veröffentlichen) | Eine Nachricht an ein Topic senden | Den Wert "23.4" an ev/salon/sicaklik schicken |
| Subscribe (abonnieren) | Sich anmelden, um die Nachrichten eines Topics zu erhalten | Der ESP32 hört auf ev/salon/role/komut |
| Payload (Nutzdaten) | Inhalt der Nachricht: Text, Zahl oder JSON | ON, 23.4, {"sicaklik":23.4} |
| Retain | Der Broker speichert die letzte Nachricht eines Topics und gibt sie neuen Abonnenten sofort | Relais-Zustand: ON |
| LWT (Last Will) | Nachricht, die der Broker im Namen eines Clients veröffentlicht, wenn dieser unerwartet wegfällt | ev/salon/baglanti: offline |
| QoS | Zustellgarantie einer Nachricht (0, 1 oder 2) | QoS 1 für Relais-Befehle |
Warum MQTT? Vergleich mit Webserver, Blynk und Telegram
| Methode | Vorteile | Nachteile | Ideal für |
|---|---|---|---|
| MQTT + Home Assistant | Lokal und schnell, in beide Richtungen; Hunderte Geräte in einem Dashboard, Automationen | Ein Broker muss laufen | Das ganze Haus zentral steuern |
| ESP32-Webserver | Kein zusätzlicher Server, läuft im Browser | Jedes Gerät hat eine eigene Seite, keine Automationen zwischen Geräten | Bedienfeld für ein einzelnes Gerät |
| Blynk IoT | Fertiges Handy-Dashboard, Zugriff über das Internet | Cloud-abhängig, Grenzen im Gratis-Tarif | Schnelle mobile Prototypen |
| Telegram-Bot | Kostenlose Sofortbenachrichtigungen, keine Portfreigabe | Keine Diagramme und kein Dashboard | Alarme und einfache Befehle |
| ESP-NOW | ESP-Boards kommunizieren ohne Router und Broker | Nur zwischen ESP-Boards, erreicht Home Assistant nicht direkt | Daten von einem Batteriesensor zu einem Gateway bringen |
Kurz gesagt: MQTT spielt seine Stärke aus, je mehr Geräte du hast. Kommt ein neuer Sensor dazu, fasst du keinen bestehenden Code an, sondern veröffentlichst einfach in einem neuen Topic. Willst du ein einzelnes Gerät im Browser steuern, kommst du mit ESP32 Async Webserver und WebSocket schneller ans Ziel; für Benachrichtigungen aufs Handy gibt es ESP32 Telegram-Bot und für ein fertiges Handy-Dashboard Blynk mit dem ESP32. Für einen Gartensensor außerhalb der Router-Reichweite ist es eine gute Lösung, die Daten per ESP-NOW zu einem Gateway-ESP32 zu schicken, der mit MQTT verbunden ist.
MQTT-Topics und Platzhalter (Wildcards)
Topics sehen aus wie Ordnerpfade: "ev/salon/sicaklik" (türkisch für Haus/Wohnzimmer/Temperatur) hat drei Ebenen und unterscheidet Groß- und Kleinschreibung, "Ev/Salon" und "ev/salon" sind also zwei verschiedene Topics. Beginne nicht mit einem Schrägstrich ("/ev/salon" erzeugt eine leere erste Ebene) und verzichte auf Leerzeichen und Umlaute; einfache Buchstaben, Ziffern, Bindestrich und Unterstrich reichen. Beim Abonnieren gibt es zwei Platzhalter: + ersetzt genau eine Ebene ("ev/+/sicaklik" liefert die Temperatur aller Räume), # deckt alles danach ab und darf nur am Ende stehen ("ev/#" liefert alle Nachrichten im Haus). Platzhalter gelten nur beim Abonnieren; veröffentlicht wird immer in genau ein Topic. Topics, die mit $ beginnen (etwa $SYS), sind für die Statistiken des Brokers reserviert und werden von einem #-Abo nicht erfasst. Für das Projekt in dieser Anleitung nutzen wir diese Struktur:
| Topic | Veröffentlicht von | Abonniert von | Retain |
|---|---|---|---|
| ev/salon/sicaklik | ESP32 (alle 30 Sekunden) | Home Assistant | Ja |
| ev/salon/nem | ESP32 (alle 30 Sekunden) | Home Assistant | Ja |
| ev/salon/role/komut | Home Assistant, Handy, Automationen | ESP32 | Nein |
| ev/salon/role/durum | ESP32 (bei jeder Änderung) | Home Assistant | Ja |
| ev/salon/baglanti | ESP32 (online), Broker (offline) | Home Assistant | Ja |
| homeassistant/status | Home Assistant (online beim Start) | ESP32 | Nein |
MQTT-Broker installieren: Mosquitto
Der verbreitetste kostenlose MQTT-Broker ist Eclipse Mosquitto; selbst auf einem Raspberry Pi bewältigt er mühelos Hunderte Nachrichten pro Sekunde. Es gibt drei einfache Wege: die offizielle Mosquitto-broker-App, wenn du Home Assistant OS nutzt, das apt-Paket auf einem separaten Raspberry Pi oder Linux-Rechner oder den Mosquitto-Installer unter Windows. Egal welchen Weg du wählst: Der Broker nutzt Port 1883 für unverschlüsseltes MQTT und 8883 für TLS-verschlüsseltes MQTT und muss nur im selben Heimnetz wie der ESP32 sein. In Home Assistant läuft die Einrichtung so:
- Öffne in Home Assistant Einstellungen > Apps (in älteren Versionen heißt der Bereich Add-ons), klicke auf App installieren und wähle Mosquitto broker aus der Liste.
- Klicke auf Installieren; danach auf Starten, und lass die Option zum Start beim Booten aktiviert.
- Lege einen eigenen Benutzer für den ESP32 an: unter Einstellungen > Personen > Benutzer einen Benutzer "esp32" mit einem starken Passwort. Siehst du diesen Reiter nicht, aktiviere in deinem Profil den erweiterten Modus. Die Namen "homeassistant" und "addons" sind für den Broker reserviert, verwende sie nicht.
- Auf der Seite Einstellungen > Geräte & Dienste erscheint die MQTT-Integration meist von selbst als "Entdeckt"; klicke auf Konfigurieren und bestätige. Falls nicht, suche über Integration hinzufügen nach MQTT und wähle die automatische Konfiguration von Mosquitto.
- Im ESP32-Code trägst du als Broker-Adresse die IP von Home Assistant ein (zum Beispiel 192.168.1.10), als Port 1883 und den eben angelegten Benutzer.
Auf einem Linux-Rechner wie einem Raspberry Pi oder Ubuntu sind es nur ein paar Befehle. Installiere Broker und Testwerkzeuge mit sudo apt install mosquitto mosquitto-clients. Lege mit sudo mosquitto_passwd -c /etc/mosquitto/passwd esp32 den Benutzer "esp32" an und gib das Passwort zweimal ein; die Option -c legt die Datei neu an, lass sie beim zweiten Benutzer also weg, sonst werden die bisherigen Benutzer gelöscht. Erstelle dann im Ordner /etc/mosquitto/conf.d/ eine Datei lokal.conf mit den drei Zeilen unten und starte den Dienst mit sudo systemctl restart mosquitto neu. Unter Windows trägst du dieselben Zeilen in die Datei mosquitto.conf im Installationsordner ein und startest den Dienst Mosquitto Broker neu.
| Zeile | Bedeutung |
|---|---|
| listener 1883 | Lauscht auf Port 1883 an allen Netzwerkschnittstellen; ohne diese Zeile nimmt Mosquitto 2.x nur Verbindungen vom selben Rechner an |
| allow_anonymous false | Lehnt Verbindungen ohne Benutzername und Passwort ab; in 2.x ohnehin Standard, aber es ausdrücklich hinzuschreiben ist eine gute Gewohnheit |
| password_file /etc/mosquitto/passwd | Verwendet die mit mosquitto_passwd erstellte Passwortdatei; die Passwörter liegen dort als Hash |
Teste den Broker vom PC aus, bevor du zum ESP32 übergehst; wenn du Broker- und Codeprobleme von Anfang an trennst, sparst du viel Zeit. Starte in einem Terminal mit mosquitto_sub -h 192.168.1.10 -u esp32 -P deinpasswort -t "ev/#" -v das Mithören aller Topics im Haus. Gib in einem zweiten Terminal mosquitto_pub -h 192.168.1.10 -u esp32 -P deinpasswort -t ev/salon/test -m hallo ein. Erscheint im ersten Terminal die Zeile "ev/salon/test hallo", läuft der Broker und das Passwort stimmt. Die Option -v schreibt das Topic vor jede Nachricht, die Option -r sendet die Nachricht als Retain.
Bauteile und Schaltplan
Für das Projekt brauchst du ein ESP32-Entwicklungsboard, einen DHT22-Temperatur- und Feuchtigkeitssensor, ein Relaismodul, das sich mit 3,3 V ansteuern lässt, ein Breadboard und Jumper-Kabel. Der DHT22 ist genauer als der DHT11: Er misst die Temperatur auf ±0,5 °C und die Luftfeuchtigkeit auf etwa ±2–5 % und arbeitet von -40 bis 80 °C; die Sensorfamilie erklären wir ausführlich unter DHT11 Temperatur- und Feuchtigkeitssensor. Hast du einen DHT11, ändere im Code einfach DHT22 in DHT11. Für den ersten Verbindungstest reichen ein ESP32 und ein USB-Kabel; Sensor und Relais kannst du später ergänzen.



| Verbindung | ESP32 | ESP8266 (NodeMCU) | Raspberry Pi Pico W |
|---|---|---|---|
| DHT22 VCC | 3V3 | 3V3 | 3V3(OUT) |
| DHT22 DATA | GPIO 4 | D2 (GPIO 4) | GP15 |
| DHT22 GND | GND | GND | GND |
| Relais VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| Relais IN | GPIO 26 | D1 (GPIO 5) | GP14 |
| Relais GND | GND | GND | GND |
PubSubClient in der Arduino IDE installieren
- Ist das ESP32-Boardpaket in der Arduino IDE noch nicht installiert, hole das zuerst nach; die Schritte stehen unter Was ist der ESP32?.
- Öffne Werkzeuge > Bibliotheken verwalten, gib PubSubClient ins Suchfeld ein und installiere die Bibliothek von Nick O'Leary. Der Code in dieser Anleitung wurde mit Version 2.8 kompiliert.
- Suche im selben Fenster nach DHT sensor library und installiere die Bibliothek von Adafruit; bei der Frage nach Abhängigkeiten wählst du Install all, damit auch Adafruit Unified Sensor installiert wird.
- Gibt es Probleme beim Installieren, hilft die Anleitung Arduino-Bibliotheken hinzufügen.
Erster Test: ESP32 mit dem MQTT-Broker verbinden
Bevor die Sensoren dazukommen, prüfen wir mit dem einfachsten Code, ob WLAN-Daten, Broker-Adresse und Passwort stimmen. Der folgende Code verbindet sich mit dem Broker, veröffentlicht "merhaba" (hallo) im Topic "test/esp32/durum" und gibt jede Nachricht, die auf "test/esp32/komut" ankommt, im seriellen Monitor aus. Ersetze WLAN-Name, Passwort, Broker-IP und MQTT-Benutzer durch deine eigenen Daten, lade den Code hoch und öffne den seriellen Monitor mit 115200 Baud:
1#include <WiFi.h>
2#include <PubSubClient.h>
3
4const char* WIFI_SSID = "AgAdi"; // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
5const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
6const char* MQTT_SUNUCU = "192.168.1.10"; // broker'ın IP adresi / broker IP address
7const int MQTT_PORT = 1883; // şifresiz MQTT portu / unencrypted MQTT port
8const char* MQTT_KULLANICI = "esp32"; // broker kullanıcı adı / broker username
9const char* MQTT_SIFRE = "mqttSifre"; // broker şifresi / broker password
10
11WiFiClient wifiIstemci; // TCP bağlantısı / TCP connection
12PubSubClient mqtt(wifiIstemci); // MQTT istemcisi / MQTT client
13
14// Abone olunan konuya mesaj gelince çalışır / runs when a subscribed topic receives a message
15void mesajGeldi(char* topic, byte* payload, unsigned int length) {
16 Serial.print("Mesaj [");
17 Serial.print(topic);
18 Serial.print("] ");
19 for (unsigned int i = 0; i < length; i++) {
20 Serial.print((char)payload[i]); // payload sonunda \0 yoktur / payload is not null-terminated
21 }
22 Serial.println();
23}
24
25void setup() {
26 Serial.begin(115200);
27 WiFi.mode(WIFI_STA);
28 WiFi.begin(WIFI_SSID, WIFI_PASS);
29 while (WiFi.status() != WL_CONNECTED) {
30 delay(500);
31 Serial.print(".");
32 }
33 Serial.print("\nIP: ");
34 Serial.println(WiFi.localIP());
35
36 mqtt.setServer(MQTT_SUNUCU, MQTT_PORT); // broker adresi / broker address
37 mqtt.setCallback(mesajGeldi); // gelen mesaj fonksiyonu / incoming message handler
38}
39
40void loop() {
41 if (!mqtt.connected()) {
42 Serial.print("MQTT baglaniyor... ");
43 if (mqtt.connect("esp32-test", MQTT_KULLANICI, MQTT_SIFRE)) { // istemci kimliği benzersiz olmalı / client ID must be unique
44 Serial.println("baglandi");
45 mqtt.subscribe("test/esp32/komut"); // bu konuyu dinle / listen to this topic
46 mqtt.publish("test/esp32/durum", "merhaba"); // ilk mesaj / first message
47 } else {
48 Serial.print("hata, rc=");
49 Serial.println(mqtt.state()); // hata kodu / error code
50 delay(5000);
51 return;
52 }
53 }
54 mqtt.loop(); // mesajları işle, bağlantıyı canlı tut / process messages, keep the connection alive
55}Sobald im seriellen Monitor "baglandi" (verbunden) steht, sende vom Terminal auf deinem PC mosquitto_pub -h 192.168.1.10 -u esp32 -P deinpasswort -t test/esp32/komut -m hi; nach wenigen Millisekunden erscheint im seriellen Monitor die Zeile "Mesaj [test/esp32/komut] hi". Schlägt die Verbindung fehl, gibt der Code mit mqtt.state() einen Fehlercode aus und versucht es nach 5 Sekunden erneut. Diese Codes verraten dir genau, wo das Problem liegt:
| rc | Name in PubSubClient | Bedeutung und Lösung |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Der Broker hat nicht rechtzeitig geantwortet; schwaches Netz oder überlasteter Broker |
| -3 | MQTT_CONNECTION_LOST | Eine bestehende Verbindung ist abgebrochen; meist weil loop() lange nicht aufgerufen wurde oder das WLAN weg war |
| -2 | MQTT_CONNECT_FAILED | Die TCP-Verbindung kam nie zustande: falsche IP oder falscher Port, Broker aus, Mosquitto im Local-only-Modus oder eine Firewall blockiert 1883 |
| -1 | MQTT_DISCONNECTED | Die Verbindung wurde sauber getrennt (disconnect aufgerufen) oder bestand noch nie |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Die Client-ID wurde abgelehnt; eine leere oder ungültige Client-ID wurde verwendet |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Benutzername oder Passwort falsch (manche Broker liefern diesen Code) |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Nicht autorisiert; Mosquitto liefert diesen Code sowohl bei falschem Passwort als auch beim Versuch, sich ohne Zugangsdaten zu verbinden |
Mit dem ESP32 Sensordaten veröffentlichen und auf Relais-Befehle hören
Jetzt bauen wir das eigentliche Projekt. Der folgende Code liest den DHT22 alle 30 Sekunden aus und veröffentlicht Temperatur und Luftfeuchtigkeit mit Retain-Flag, schaltet das Relais mit den Befehlen ON, OFF und TOGGLE aus dem Topic "ev/salon/role/komut" und meldet den neuen Zustand an "ev/salon/role/durum". Beim Verbinden hinterlegt er beim Broker einen Last Will, stellt sich Home Assistant per MQTT Discovery vor und versucht nach einem Verbindungsabbruch alle 5 Sekunden einen Neuaufbau, ohne loop() zu blockieren. Dank des Blocks #if defined(ESP8266) am Anfang lässt sich derselbe Code ohne Änderung auch für einen NodeMCU kompilieren.
1#if defined(ESP8266)
2#include <ESP8266WiFi.h>
3#else
4#include <WiFi.h>
5#endif
6#include <PubSubClient.h>
7#include <DHT.h>
8
9// ---- Ayarlar / Settings ----
10const char* WIFI_SSID = "AgAdi";
11const char* WIFI_PASS = "Sifre123";
12const char* MQTT_SUNUCU = "192.168.1.10"; // broker IP (Home Assistant ya da Raspberry Pi) / broker IP (Home Assistant or Raspberry Pi)
13const int MQTT_PORT = 1883;
14const char* MQTT_KULLANICI = "esp32";
15const char* MQTT_SIFRE = "mqttSifre";
16const char* ISTEMCI_ID = "esp32-salon"; // her cihazda farklı olmalı / must be different on every device
17
18#if defined(ESP8266)
19const int DHT_PIN = 4; // NodeMCU D2
20const int ROLE_PIN = 5; // NodeMCU D1
21#else
22const int DHT_PIN = 4; // ESP32 GPIO 4
23const int ROLE_PIN = 26; // ESP32 GPIO 26
24#endif
25const bool ROLE_AKTIF_LOW = true; // çoğu röle modülü LOW ile çeker / most relay modules switch on LOW
26const unsigned long OLCUM_ARALIGI = 30000; // 30 saniyede bir ölçüm / one reading every 30 s
27
28// ---- Konular (topic) / Topics ----
29const char* T_SICAKLIK = "ev/salon/sicaklik";
30const char* T_NEM = "ev/salon/nem";
31const char* T_KOMUT = "ev/salon/role/komut"; // ON, OFF ya da TOGGLE / ON, OFF or TOGGLE
32const char* T_DURUM = "ev/salon/role/durum";
33const char* T_BAGLANTI = "ev/salon/baglanti"; // online / offline (son vasiyet / last will)
34const char* T_HA_DURUM = "homeassistant/status"; // HA açılınca "online" yayınlar / HA publishes "online" when it starts
35
36WiFiClient wifiIstemci;
37PubSubClient mqtt(wifiIstemci);
38DHT dht(DHT_PIN, DHT22);
39
40bool roleAcik = false;
41unsigned long sonOlcum = 0;
42unsigned long sonDeneme = 0;
43
44// Röleyi sür ve yeni durumu kalıcı (retain) yayınla / drive the relay and publish the new state as retained
45void roleAyarla(bool acik) {
46 roleAcik = acik;
47 bool seviye = ROLE_AKTIF_LOW ? !acik : acik;
48 digitalWrite(ROLE_PIN, seviye ? HIGH : LOW);
49 mqtt.publish(T_DURUM, acik ? "ON" : "OFF", true);
50 Serial.println(acik ? "Role: ON" : "Role: OFF");
51}
52
53// Tek bir Home Assistant keşif mesajı gönder / send one Home Assistant discovery message
54void kesifYayinla(const char* tur, const char* nesne, const char* ad, const char* ekAlanlar) {
55 char konu[96];
56 snprintf(konu, sizeof(konu), "homeassistant/%s/%s/%s/config", tur, ISTEMCI_ID, nesne);
57 char json[600];
58 snprintf(json, sizeof(json),
59 "{\"name\":\"%s\",\"unique_id\":\"%s_%s\",%s,\"availability_topic\":\"%s\","
60 "\"device\":{\"identifiers\":[\"%s\"],\"name\":\"Salon ESP32\",\"model\":\"ESP32 + DHT22\"}}",
61 ad, ISTEMCI_ID, nesne, ekAlanlar, T_BAGLANTI, ISTEMCI_ID);
62 if (!mqtt.publish(konu, json, true)) { // false: tampon küçük ya da bağlantı yok / false: buffer too small or not connected
63 Serial.println("Kesif mesaji gonderilemedi");
64 }
65}
66
67// Sıcaklık, nem ve röleyi Home Assistant'a tanıt / announce temperature, humidity and relay to Home Assistant
68void haKesifGonder() {
69 char ek[200];
70 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"temperature\",\"unit_of_measurement\":\"°C\",\"state_class\":\"measurement\"", T_SICAKLIK);
71 kesifYayinla("sensor", "sicaklik", "Sıcaklık", ek);
72 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"humidity\",\"unit_of_measurement\":\"%%\",\"state_class\":\"measurement\"", T_NEM);
73 kesifYayinla("sensor", "nem", "Nem", ek);
74 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"command_topic\":\"%s\"", T_DURUM, T_KOMUT);
75 kesifYayinla("switch", "role", "Röle", ek);
76}
77
78// DHT22'yi oku ve değerleri yayınla / read the DHT22 and publish the values
79void olcumGonder() {
80 float t = dht.readTemperature();
81 float h = dht.readHumidity();
82 if (isnan(t) || isnan(h)) {
83 Serial.println("DHT22 okunamadi");
84 return;
85 }
86 char deger[12];
87 snprintf(deger, sizeof(deger), "%.1f", t); // 23.4 gibi / like 23.4
88 mqtt.publish(T_SICAKLIK, deger, true);
89 snprintf(deger, sizeof(deger), "%.0f", h); // 45 gibi / like 45
90 mqtt.publish(T_NEM, deger, true);
91 Serial.printf("Sicaklik %.1f C, nem %.0f %%\n", t, h);
92}
93
94// Abone olunan konulara mesaj gelince çalışır / runs when a subscribed topic receives a message
95void mesajGeldi(char* topic, byte* payload, unsigned int length) {
96 // Önce kopyala: yayın yapınca kütüphane aynı tamponu kullanır / copy first: publishing reuses the same buffer
97 String konu = topic;
98 String mesaj;
99 for (unsigned int i = 0; i < length; i++) mesaj += (char)payload[i];
100 mesaj.trim();
101
102 if (konu == T_KOMUT) {
103 if (mesaj == "ON") roleAyarla(true);
104 else if (mesaj == "OFF") roleAyarla(false);
105 else if (mesaj == "TOGGLE") roleAyarla(!roleAcik);
106 } else if (konu == T_HA_DURUM && mesaj == "online") {
107 haKesifGonder(); // HA yeniden başladı, tanıtımı yenile / HA restarted, announce again
108 mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
109 }
110}
111
112bool mqttBaglan() {
113 Serial.print("MQTT baglaniyor... ");
114 // Bağlantı koparsa broker T_BAGLANTI'ya "offline" yazar / if we drop, the broker publishes "offline" to T_BAGLANTI
115 if (!mqtt.connect(ISTEMCI_ID, MQTT_KULLANICI, MQTT_SIFRE, T_BAGLANTI, 1, true, "offline")) {
116 Serial.print("hata, rc=");
117 Serial.println(mqtt.state());
118 return false;
119 }
120 Serial.println("baglandi");
121 mqtt.publish(T_BAGLANTI, "online", true);
122 mqtt.subscribe(T_KOMUT, 1); // komutları QoS 1 ile dinle / listen to commands with QoS 1
123 mqtt.subscribe(T_HA_DURUM);
124 haKesifGonder();
125 mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
126 return true;
127}
128
129void setup() {
130 Serial.begin(115200);
131 pinMode(ROLE_PIN, OUTPUT);
132 roleAyarla(false); // güvenli başlangıç: röle kapalı / safe start: relay off
133 dht.begin();
134
135 WiFi.mode(WIFI_STA);
136 WiFi.begin(WIFI_SSID, WIFI_PASS);
137 Serial.print("Wi-Fi");
138 while (WiFi.status() != WL_CONNECTED) {
139 delay(500);
140 Serial.print(".");
141 }
142 Serial.print(" IP: ");
143 Serial.println(WiFi.localIP());
144
145 mqtt.setServer(MQTT_SUNUCU, MQTT_PORT);
146 mqtt.setCallback(mesajGeldi);
147 mqtt.setBufferSize(1024); // keşif JSON'u varsayılan 256 bayta sığmaz / discovery JSON doesn't fit the default 256 bytes
148}
149
150void loop() {
151 if (!mqtt.connected()) {
152 unsigned long simdi = millis();
153 if (sonDeneme == 0 || simdi - sonDeneme >= 5000) { // 5 sn'de bir dene, loop'u kilitleme / retry every 5 s without blocking
154 sonDeneme = simdi;
155 if (mqttBaglan()) sonOlcum = simdi - OLCUM_ARALIGI; // bağlanınca hemen ölç / measure right after connecting
156 }
157 return;
158 }
159 mqtt.loop(); // gelen komutlar ve keep-alive / incoming commands and keep-alive
160
161 if (millis() - sonOlcum >= OLCUM_ARALIGI) {
162 sonOlcum = millis();
163 olcumGonder();
164 }
165}| Zeile | Aufgabe |
|---|---|
| mqtt.connect(ISTEMCI_ID, ..., T_BAGLANTI, 1, true, "offline") | Meldet beim Verbinden auch den Last Will an: Fällt der ESP32 unerwartet weg, schreibt der Broker dauerhaft "offline" nach ev/salon/baglanti |
| mqtt.publish(T_BAGLANTI, "online", true) | Setzt den Status direkt nach dem Verbinden auf "online"; Home Assistant zeigt das Gerät wieder als verfügbar |
| mqtt.subscribe(T_KOMUT, 1) | Hört mit QoS 1 auf Relais-Befehle; PubSubClient unterstützt beim Abonnieren nur QoS 0 und 1 |
| mqtt.setBufferSize(1024) | Vergrößert den Paketpuffer von standardmäßig 256 Byte; das größte Discovery-Paket hat etwa 355 Byte |
| mqtt.loop() | Liest eingehende Nachrichten und sendet Keep-alive-Pakete (PINGREQ); muss in jedem Durchlauf von loop aufgerufen werden |
| ROLE_AKTIF_LOW | Die meisten Relaismodule schalten bei LOW; schaltet deins bei HIGH, setze den Wert auf false |
| OLCUM_ARALIGI | Messintervall; der DHT22 darf höchstens alle 2 Sekunden gelesen werden, für Raumtemperatur sind 30–60 Sekunden ideal |
Einige Details im Code sind bewusst so gewählt. Die Funktion mesajGeldi() kopiert Topic und Payload zuerst in Strings, denn PubSubClient nutzt einen einzigen Puffer für ein- und ausgehende Nachrichten: Sobald du im Callback publish() aufrufst, werden die Daten überschrieben, auf die topic und payload zeigen. Die Payload hat außerdem kein abschließendes \0 wie ein C-String, deshalb lesen wir genau length Zeichen. Für Messwerte und Relais-Zustand nutzen wir Retain, damit Home Assistant nach einem Neustart sofort den letzten Wert sieht, statt 30 Sekunden zu warten. Auch das Timing läuft über millis(): Ein langes delay() in loop() würde die Keep-alive-Pakete verzögern, und der Broker würde die Verbindung trennen. Diese Technik erklären wir ausführlich unter Arduino millis() statt delay().
So sieht es in Home Assistant aus: MQTT Discovery
Home Assistant hört ständig auf Topics mit dem Präfix homeassistant/. Veröffentlicht ein Gerät eine JSON-Konfiguration in einem Topic der Form homeassistant/[Komponente]/[Knoten]/[Objekt]/config, legt Home Assistant die Entität automatisch an; das nennt sich MQTT Discovery. Unser Code sendet drei Nachrichten: zwei für die Sensoren (Temperatur und Luftfeuchtigkeit) unter homeassistant/sensor/esp32-salon/... und eine für das Relais unter homeassistant/switch/esp32-salon/role/config. Weil sie mit Retain gesendet werden, überstehen sie auch einen Neustart von Home Assistant. Außerdem abonniert der Code homeassistant/status: Home Assistant schreibt dort beim Start "online", und der ESP32 meldet sich erneut an. Bei vielen Geräten empfiehlt die Home-Assistant-Dokumentation, diese erneute Anmeldung mit einer kleinen zufälligen Verzögerung zu senden.
| JSON-Feld | Bedeutung | Unser Wert |
|---|---|---|
| name | Anzeigename der Entität; der Gerätename wird davor gesetzt | Sıcaklık (erscheint als Salon ESP32 Sıcaklık) |
| unique_id | Dauerhafte ID der Entität; ohne sie lassen sich Name und Bereich nicht in der Oberfläche ändern | esp32-salon_sicaklik |
| state_topic | Topic, aus dem der Wert gelesen wird | ev/salon/sicaklik |
| command_topic | Topic, an das Schaltbefehle (switch) gesendet werden | ev/salon/role/komut |
| device_class | Legt Symbol, Einheitenauswahl und Diagrammart fest | temperature, humidity |
| unit_of_measurement | Maßeinheit | °C und % |
| state_class | Der Wert measurement sorgt für Langzeitstatistiken und Diagramme | measurement |
| availability_topic | Topic für den Status online/offline; die Standard-Payloads sind online und offline | ev/salon/baglanti |
| device | Fasst die Entitäten auf einer Gerätekarte zusammen | identifiers, name, model |
Ein paar Sekunden nach dem Hochladen erscheint unter Einstellungen > Geräte & Dienste > MQTT ein Gerät namens "Salon ESP32". Auf seiner Gerätekarte findest du die Sensoren für Temperatur und Luftfeuchtigkeit sowie einen Relais-Schalter; ein Klick auf die Temperatur öffnet den Verlauf, ein Klick auf den Schalter lässt das Relais anziehen. Du kannst diese Entitäten aufs Dashboard legen, Sprachassistenten freigeben oder in Automationen nutzen: Eine Regel wie "steigt die Wohnzimmertemperatur über 27 °C, schalte das Relais mit dem Ventilator ein, unter 25 °C wieder aus" ist mit ein paar Klicks erstellt. Ziehst du den Stecker des ESP32, wechseln die Entitäten nach spätestens etwa 22 Sekunden auf "Nicht verfügbar"; daran siehst du, dass der Last Will funktioniert. Willst du kein Discovery nutzen, kannst du dieselben Entitäten auch von Hand in der configuration.yaml unter mqtt: als sensor und switch anlegen.
QoS und Retain: Was nimmst du wann?
QoS (Quality of Service) legt fest, wie sicher du sein willst, dass eine Nachricht ankommt. Ein wichtiges Detail: QoS gilt getrennt auf zwei Strecken. Zwischen Absender und Broker gilt die QoS des Absenders, zwischen Broker und Abonnent die QoS des Abonnements; der Abonnent bekommt die niedrigere der beiden Garantien. PubSubClient unterstützt beim Veröffentlichen nur QoS 0 und beim Abonnieren QoS 0 und 1. Im Heim-WLAN geht mit QoS 0 nur sehr selten etwas verloren, trotzdem ist QoS 1 für das Befehls-Abo eine günstige Versicherung.
| QoS | Garantie | Wie funktioniert es? | Wann verwenden? |
|---|---|---|---|
| 0 | Höchstens einmal | Die Nachricht wird einmal ohne Bestätigung gesendet; bricht die Verbindung genau dann ab, kann sie verloren gehen | Häufige Sensormesswerte |
| 1 | Mindestens einmal | Der Empfänger bestätigt mit PUBACK, ohne Bestätigung wird erneut gesendet; die Nachricht kann doppelt ankommen | Relais-Befehle, Alarme |
| 2 | Genau einmal | Vierstufiger Handshake mit PUBREC, PUBREL und PUBCOMP; die langsamste Stufe | Zähler und Ähnliches, wo Doppelte schaden; von PubSubClient nicht unterstützt |
Beim Retain-Flag geht es nicht um Zustellung, sondern um Gedächtnis: Der Broker speichert pro Topic nur die letzte Retain-Nachricht und schickt sie jedem neuen Abonnenten sofort. Ideal ist das für Nachrichten zum "aktuellen Zustand" wie Messwerte, Relais-Zustand und Online-Status, gefährlich dagegen für Befehle und einmalige Ereignisse. Der Last Will (LWT) ergänzt das: Erhält der Broker vom Client 1,5-mal so lange wie das Keep-alive-Intervall kein Paket, hält er die Verbindung für verloren und veröffentlicht die Last-Will-Nachricht. Das Standard-Keep-alive von PubSubClient beträgt 15 Sekunden, ein ausgesteckter ESP32 erscheint also nach spätestens etwa 22 Sekunden als "offline". Mit einer Zeile wie mqtt.setKeepAlive(30) kannst du das ändern, aber je länger der Wert, desto später bemerkst du einen Abbruch.
Die Alternative ohne Code: ESPHome
ESPHome ist ein kostenloses Werkzeug, das ein in einer YAML-Datei beschriebenes Gerät in fertige ESP32-Firmware verwandelt; du schreibst keine einzige Zeile C++. Installiere in Home Assistant unter Einstellungen > Apps den ESPHome Device Builder, öffne seine Oberfläche und lege mit dem Assistenten Neues Gerät eine Konfiguration an. Der erste Upload erfolgt einmalig per USB: Chrome oder Edge können das Board direkt programmieren, und falls das nicht klappt, spielst du die vom Assistenten heruntergeladene Datei mit der Seite ESPHome Web auf. Alle späteren Updates laufen drahtlos über WLAN (OTA). Die folgende Konfiguration entspricht dem Arduino-Projekt oben: Sie liest den DHT22 an GPIO 4 alle 30 Sekunden und stellt das Low-aktive Relais an GPIO 26 als Schalter bereit. WLAN-Daten und Passwörter werden per !secret aus einer separaten secrets.yaml gelesen:
1esphome:
2 name: salon-sensor # küçük harf, rakam ve tire / lowercase, digits and hyphens
3 friendly_name: Salon Sensör
4
5esp32:
6 board: esp32dev
7
8logger:
9
10api: # Home Assistant ile doğrudan bağlantı / direct link to Home Assistant
11 encryption:
12 key: !secret api_anahtari
13
14ota:
15 - platform: esphome
16 password: !secret ota_sifresi
17
18wifi:
19 ssid: !secret wifi_ssid
20 password: !secret wifi_sifresi
21 ap: # Wi-Fi yoksa kendi ağını açar / opens its own network if Wi-Fi fails
22 ssid: "Salon-Sensor"
23 password: "yedek1234"
24
25captive_portal:
26
27sensor:
28 - platform: dht
29 pin: GPIO4
30 model: DHT22
31 update_interval: 30s
32 temperature:
33 name: "Sıcaklık"
34 humidity:
35 name: "Nem"
36
37switch:
38 - platform: gpio
39 name: "Röle"
40 pin:
41 number: GPIO26
42 inverted: true # aktif LOW röle / active-LOW relay
43 restore_mode: ALWAYS_OFF # açılışta kapalı / off at boot
44
45# MQTT ile kullanmak için api: bloğunu sil, bunu aç / to use MQTT, delete api: and uncomment this
46# mqtt:
47# broker: 192.168.1.10
48# username: esp32
49# password: !secret mqtt_sifresiEinen Unterschied solltest du kennen: ESPHome nutzt standardmäßig nicht MQTT, sondern seine eigene verschlüsselte Native API. Home Assistant findet das Gerät im Netz von selbst und braucht überhaupt keinen Broker. MQTT lohnt sich nur, wenn auch andere Systeme wie Node-RED dieselben Daten lesen sollen oder wenn du Home Assistant nicht nutzt. Dazu entfernst du die Kommentarzeichen vor den mqtt:-Zeilen am Dateiende und löschst den Block api:; tust du das nicht, startet das Gerät neu, sobald sich 15 Minuten lang niemand mit der Native API verbunden hat (das ist die Einstellung reboot_timeout). Im MQTT-Modus sendet ESPHome die Discovery-Nachrichten für Home Assistant und die online/offline-Meldungen in salon-sensor/status selbst, erledigt also automatisch alles, was wir im Arduino-Code von Hand gemacht haben.
| Merkmal | PubSubClient (Arduino-Code) | ESPHome (YAML) |
|---|---|---|
| Programmieren | Du schreibst alles selbst in C++ | Nur YAML; die Firmware wird automatisch kompiliert |
| Einbindung in Home Assistant | Du sendest die Discovery-JSONs selbst | Wird automatisch gefunden |
| Flexibilität | Unbegrenzt; jede Bibliothek und jede Logik ist möglich | Auf fertige Komponenten begrenzt, aber Hunderte Sensoren werden unterstützt; C++ per Lambda ergänzbar |
| Drahtlose Updates | ArduinoOTA musst du selbst einbauen | Ist dabei |
| Broker nötig | Ja | Mit der Native API nicht |
| Lerneffekt | Vermittelt das MQTT-Protokoll und Programmieren | Schnelle Ergebnisse, verbirgt die Technik dahinter |
MQTT mit ESP8266 und Raspberry Pi Pico W
Der vollständige Code oben läuft unverändert auf ESP8266 NodeMCU und Wemos D1 Mini: Wählst du unter Werkzeuge > Board den NodeMCU 1.0, nimmt der Block #if defined(ESP8266) automatisch ESP8266WiFi.h statt WiFi.h sowie die Pins D2 und D1. Beim Raspberry Pi Pico W nutzen wir das MicroPython-Modul umqtt.simple. Es ist in der MicroPython-Firmware des Pico W nicht enthalten; installiere es in Thonny über Extras > Pakete verwalten mit der Suche nach umqtt.simple oder tippe bei bestehender WLAN-Verbindung in der REPL import mip; mip.install("umqtt.simple"). Das dht-Modul für den DHT22 ist dagegen schon in der Firmware. Mit dem folgenden Code übernimmt ein Pico W in der Küche dieselbe Mess- und Relaisaufgabe. Damit er kurz bleibt, fehlen die Discovery-Nachrichten; damit der Pico W in Home Assistant erscheint, veröffentlichst du dieselben Discovery-JSONs wie im ESP32-Code mit den ev/mutfak-Topics oder legst die Entitäten in der configuration.yaml unter mqtt: von Hand an. Speicherst du die Datei als main.py, startet sie bei jedem Einschalten:
1import network, time, dht
2from machine import Pin
3from umqtt.simple import MQTTClient # önce mip ile kur / install with mip first
4
5WIFI_SSID = "AgAdi" # Wi-Fi adın / your Wi-Fi name
6WIFI_PASS = "Sifre123" # Wi-Fi şifren / your Wi-Fi password
7MQTT_SUNUCU = "192.168.1.10" # broker IP / broker IP
8ISTEMCI_ID = "pico-mutfak" # benzersiz olmalı / must be unique
9
10T_SICAKLIK = b"ev/mutfak/sicaklik"
11T_NEM = b"ev/mutfak/nem"
12T_KOMUT = b"ev/mutfak/role/komut"
13T_DURUM = b"ev/mutfak/role/durum"
14T_BAGLANTI = b"ev/mutfak/baglanti"
15
16sensor = dht.DHT22(Pin(15)) # DHT22 DATA -> GP15
17role = Pin(14, Pin.OUT, value=1) # aktif LOW röle kapalı başlar / active-LOW relay starts off
18led = Pin("LED", Pin.OUT) # Pico W dahili LED / Pico W onboard LED
19
20wlan = network.WLAN(network.STA_IF)
21wlan.active(True)
22wlan.connect(WIFI_SSID, WIFI_PASS)
23while not wlan.isconnected():
24 time.sleep(0.5)
25print("IP:", wlan.ifconfig()[0])
26
27def mesaj_geldi(konu, mesaj): # ikisi de bytes gelir / both arrive as bytes
28 if konu == T_KOMUT and mesaj in (b"ON", b"OFF"):
29 acik = mesaj == b"ON"
30 role.value(0 if acik else 1)
31 led.value(1 if acik else 0)
32 istemci.publish(T_DURUM, mesaj, retain=True)
33
34istemci = MQTTClient(ISTEMCI_ID, MQTT_SUNUCU, user="esp32", password="mqttSifre", keepalive=60) # Mosquitto kullanıcısı / Mosquitto user
35istemci.set_callback(mesaj_geldi)
36istemci.set_last_will(T_BAGLANTI, b"offline", retain=True) # son vasiyet / last will
37
38def baglan():
39 while True:
40 try:
41 if not wlan.isconnected(): # Wi-Fi koptuysa yeniden bağlan / reconnect Wi-Fi if it dropped
42 wlan.connect(WIFI_SSID, WIFI_PASS)
43 time.sleep(5)
44 istemci.connect()
45 istemci.publish(T_BAGLANTI, b"online", retain=True)
46 istemci.publish(T_DURUM, b"OFF" if role.value() else b"ON", retain=True)
47 istemci.subscribe(T_KOMUT)
48 print("MQTT baglandi")
49 return
50 except Exception as e: # MQTTException(5): yetki yok / not authorized
51 print("MQTT hatasi:", e)
52 time.sleep(5)
53
54baglan()
55son = time.ticks_add(time.ticks_ms(), -30000) # ilk ölçüm hemen / first reading right away
56while True:
57 try:
58 istemci.check_msg() # bekleyen komut var mı? / any pending command?
59 if time.ticks_diff(time.ticks_ms(), son) >= 30000:
60 son = time.ticks_ms()
61 try:
62 sensor.measure()
63 except OSError:
64 print("DHT22 okunamadi")
65 istemci.ping() # veri yoksa da bağlantıyı canlı tut / keep the link alive without data
66 else:
67 istemci.publish(T_SICAKLIK, "{:.1f}".format(sensor.temperature()).encode(), retain=True)
68 istemci.publish(T_NEM, "{:.0f}".format(sensor.humidity()).encode(), retain=True)
69 except OSError:
70 print("Baglanti koptu, yeniden baglaniliyor")
71 time.sleep(5)
72 baglan()
73 time.sleep_ms(100)ESP32-MQTT-Projektideen
- Temperaturkarte für jeden Raum: Stell in jeden Raum einen ESP32 mit DHT22 oder BME280 und verfolge das ganze Haus per Abo auf ev/+/sicaklik in einem Diagramm.
- Smarte Lampe und Steckdose: Verknüpfe das Relais mit einer Home-Assistant-Automation, die bei Sonnenuntergang ein- und um Mitternacht ausschaltet.
- Tür- und Fenstersensor: Sende mit einem Reedkontakt beim Öffnen der Tür eine MQTT-Nachricht und bekomme eine Handy-Benachrichtigung, wenn niemand zu Hause ist.
- Flurlicht mit Bewegungsmelder: Ein PIR-Sensor meldet Bewegung, eine Home-Assistant-Automation schaltet das Licht.
- Pflanzenbewässerung: Veröffentliche den Wert eines Bodenfeuchtesensors und starte die Pumpe per MQTT-Befehl, wenn die Erde trocken ist.
- Energiemonitor: Miss mit einem ACS712-Stromsensor den Strom eines Geräts und stelle den Tagesverbrauch grafisch dar.
- Batteriebetriebener Sensor: Baue einen Knoten, der misst, per MQTT sendet und wieder schlafen geht; Details unter ESP32 Deep Sleep.
Häufige ESP32-MQTT-Fehler und Lösungen
| Symptom oder Meldung | Ursache | Lösung |
|---|---|---|
| Dauerhaft rc=-2 | Falsche Broker-IP oder falscher Port, Broker aus oder Mosquitto im Local-only-Modus | IP prüfen, listener 1883 in die Konfiguration schreiben, Port 1883 in der Firewall freigeben |
| rc=5 | Benutzername oder Passwort falsch oder Verbindungsversuch ohne Zugangsdaten | Passwort am PC mit mosquitto_sub testen; den Benutzer bei Bedarf neu anlegen |
| Verbindet sich, fällt nach wenigen Sekunden ab, Endlosschleife | Zwei Geräte nutzen dieselbe Client-ID; Mosquitto schreibt "Client esp32-salon already connected, closing old connection." ins Log | Jedem Gerät eine eindeutige ISTEMCI_ID geben |
| Discovery-Nachrichten kommen nicht an, publish() liefert false | Der PubSubClient-Puffer hat standardmäßig 256 Byte, das JSON passt nicht hinein | In setup() mqtt.setBufferSize(1024) aufrufen |
| Häufig rc=-3 | Ein langes delay() in loop() verhindert Keep-alive-Pakete, oder das WLAN-Signal ist schwach | Timing mit millis() lösen, Board näher an den Router bringen |
| Befehle kommen beim ESP32 nicht an | Topic-Name weicht in Schreibweise oder Groß-/Kleinschreibung ab, oder subscribe() wird nach dem Neuverbinden nicht wiederholt | Topic mit mosquitto_sub prüfen; subscribe() in die Verbindungsfunktion setzen |
| Relais schaltet beim Start von selbst ein | Auf dem Befehls-Topic liegt eine alte Retain-Nachricht | Mit mosquitto_pub -r -n -t ev/salon/role/komut löschen |
| Entitäten in Home Assistant "Nicht verfügbar" | Im Availability-Topic steht offline, oder online ist anders geschrieben | ev/salon/baglanti mithören; sicherstellen, dass der Code nach dem Verbinden online sendet |
| Kryptische Zeichen statt eines Werts | Die Payload endet nicht mit \0 und wurde ohne length gelesen | Payload in einer Schleife bis length kopieren |
| ESPHome-Gerät startet alle 15 Minuten neu | Im MQTT-Modus steht noch der Block api:, aber Home Assistant verbindet sich nie | Block api: löschen oder reboot_timeout: 0s setzen |
Mit einem Broker, ein paar Topics und PubSubClient ist dein ESP32 zu einem Smart-Home-Gerät geworden, das dieselbe Sprache spricht wie der Rest des Hauses. Du kannst neue Sensoren, weitere Räume und andere Boards wie den Pico W hinzufügen und alles zentral in Home Assistant verwalten. In der BlueGrays-App findest du Projekte wie ESP32-WLAN-Verbindung, Temperatur- und Feuchtemessung mit dem DHT22, smarte Beleuchtung mit PIR und Relais, Echtzeitsteuerung per WebSocket und smarte Pflanzenbewässerung Schritt für Schritt mit Schaltplänen und Code.
Häufige Fragen
Was ist MQTT und wofür wird es verwendet?
MQTT ist ein schlankes Nachrichtenprotokoll, bei dem Geräte über einen Broker Nachrichten anhand von Topic-Namen senden und empfangen. Weil es wenig Bandbreite und Energie braucht, eignet es sich ideal für kleine Geräte wie den ESP32, um Sensordaten zu senden und Befehle zu empfangen. Home Assistant, Node-RED und die meisten industriellen IoT-Systeme sprechen MQTT.
Welchen Port nutzt MQTT?
Unverschlüsseltes MQTT nutzt standardmäßig Port 1883, TLS-verschlüsseltes MQTT Port 8883. Der WebSocket-Port für Verbindungen aus dem Browser hängt vom Broker ab; beim Mosquitto-Broker in Home Assistant ist es 1884. Im Heimnetz verbindet sich der ESP32 meist mit 1883.
Braucht MQTT eine Internetverbindung?
Nein. Läuft der Broker zu Hause auf einem Raspberry Pi oder in Home Assistant, bleibt der Verkehr zwischen ESP32 und Broker komplett im lokalen Netz; Sensoren und Relais funktionieren auch bei Internetausfall weiter. Internet brauchst du nur für den Zugriff von außen oder einen Cloud-Broker.
Warum erscheint mein ESP32 nicht in Home Assistant?
Die häufigsten Gründe sind eine Discovery-Nachricht, die nicht in den 256-Byte-Puffer von PubSubClient passt, ein falsch geschriebenes config-Topic oder ein Anführungszeichenfehler im JSON. Höre mit mosquitto_sub auf homeassistant/#, um zu prüfen, ob die Nachricht beim Broker ankommt, und stelle sicher, dass der Code die Zeile mqtt.setBufferSize(1024) enthält. Außerdem muss die MQTT-Integration von Home Assistant mit demselben Broker verbunden sein.
Soll ich ESPHome oder MQTT mit Arduino verwenden?
Willst du schnell ein funktionierendes Smart-Home-Gerät, macht ESPHome weniger Arbeit: Du schreibst YAML, das Gerät erscheint von selbst in Home Assistant und wird drahtlos aktualisiert. Willst du das MQTT-Protokoll lernen, eigene Logik schreiben oder mit anderen Systemen als Home Assistant arbeiten, ist Arduino mit PubSubClient flexibler. Beides lässt sich problemlos im selben Haus kombinieren.
Was ist der Unterschied zwischen MQTT QoS 0, 1 und 2?
QoS 0 sendet eine Nachricht einmal ohne Bestätigung, QoS 1 sendet erneut, bis sie bestätigt ist, wobei sie doppelt ankommen kann, und QoS 2 garantiert mit einem vierstufigen Handshake, dass sie genau einmal ankommt. Für Messwerte reicht QoS 0, für Befehle QoS 1. PubSubClient kann nur mit QoS 0 veröffentlichen und mit QoS 0 oder 1 abonnieren.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
LED Blinken mit ESP32
Lehrt die grundlegende Verwendung von GPIO-Ausgängen mit ESP32. Wir erreichen einen Blinkeffekt, indem wir eine LED über einen digitalen Pin ein- und ausschalten. ESP32-Version des klassischen Blink-Projekts auf Arduino UNO; die grundlegenden Blink-Befehle in der Arduino-Umgebung bleiben gleich, aber die Pinbelegung und die GPIO-Pegel von 3,3 V des ESP32 unterscheiden sich von denen des Arduino UNO.

ESP32-WLAN-STA-Verbindung (mit LCD-Display)
In diesem Projekt verbinden wir den ESP32 im Station-Modus (STA) sicher und fehlertolerant mit dem heimischen WLAN. Wir lagern SSID und Passwort in eine separate secrets.h-Datei aus, um Leaks zu Git zu verhindern, richten einen Auto-Reconnect-Mechanismus ein und zeigen RSSI-Signalpegel und IP-Adresse auf einem I2C-LCD-Display an. Die interne LED dient als Statusanzeige. Ein Verbindungsmanager auf Produktionsniveau.

ESP32 DHT22 Temperatur und Luftfeuchtigkeit (mit LCD-Display)
Bei diesem Projekt werden die Umgebungstemperatur und die Luftfeuchtigkeit mit einem DHT22-Sensor gemessen und in Echtzeit auf einem 16x2 I2C-LCD angezeigt. Mithilfe der Adafruit-DHT-Bibliothek werden die Daten über das One-Wire-Protokoll vom Sensor ausgelesen. Dieses Projekt bildet die Grundlage für IoT-Wetterstationen, Smart-Home-Automatisierung oder Umgebungsüberwachungssysteme in Gewächshäusern. Die Kommunikation mit dem DHT22 erfolgt über den GPIO-4-Pin des ESP32.

ESP32 Intelligente Beleuchtung (PIR + LDR + Relais + OLED)
Verwandle deinen Flur, dein Badezimmer oder deine Speisekammer mit dem ESP32 in intelligente Beleuchtung! Mit einem PIR-Bewegungssensor, einem LDR-Lichtsensor und einem Relaismodul realisieren wir das klassische Szenario "Licht geht an, wenn im Dunkeln Bewegung erkannt wird" — bei Tageslicht bleibt das Licht trotz Bewegung aus, was tagsüber Energie spart. Das Licht bleibt 30 Sekunden lang an; bei anhaltender Bewegung wird der Timer zurückgesetzt und das Licht bleibt länger an. Das SSD1306 OLED-D

ESP32-WebSocket-Zähler (2 Tasten + LCD + Web-Echtzeit)
In diesem Projekt ändern zwei am ESP32 angeschlossene Taster (GPIO 5 zählt hoch, GPIO 4 zählt herunter) einen Zähler. Der Wert wird sofort auf einem 16x2-I2C-LCD und über die WebSocket-Unterstützung von ESPAsyncWebServer in allen verbundenen Browsern aktualisiert, ohne die Seite neu zu laden. Statt HTTP-Polling wird eine einzige dauerhafte Verbindung genutzt; der ESP32 sendet den neuen Wert mit ws.textAll() an alle.

ESP32 mDNS: Zugriff auf das Gerät über „bluegrays-esp.local“ (mit LCD-Display)
In diesem Projekt fügen wir dem ESP32 einen mDNS-Dienst (Multicast DNS) hinzu, um den Zugriff nicht über eine IP-Adresse, sondern über einen lesbaren Hostnamen wie bluegrays-esp.local zu ermöglichen. Selbst wenn sich die vom DHCP zugewiesene IP-Adresse ändert, können Sie das Gerät immer unter demselben Namen erreichen. Indem wir zusätzlich einen kleinen Webserver darauf betreiben, senden wir über den Browser unter der Adresse http://bluegrays-esp.local/ Befehle zur LED-Steuerung. Auf dem I2C

ESP32 Bewegungsalarmsystem (PIR + Buzzer + LED + Telegram)
Ein intelligentes Sicherheitsalarmsystem, das Buzzer und LED aktiviert, sobald der PIR-Sensor Bewegung erkennt, und gleichzeitig eine sofortige Benachrichtigung per Telegram an den Besitzer sendet. Ein 30-sekündiger Cooldown begrenzt die Häufigkeit wiederholter Alarme; dieser Mechanismus überprüft weder die Gültigkeit der PIR-Erkennungen noch verhindert er Fehlauslösungen. Geeignet für Heimsicherheit, Garagenüberwachung, Lagerräume oder Arbeitszimmer. Mit ESP32-CAM sind Foto-Benachrichtigungen m
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 200 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

