Wasserleck-Erkennung mit IoT macht aus einem unbemerkten Tropfen unter der Spüle einen Alarm auf Ihrem Smartphone und, noch besser, ein geschlossenes Ventil, bevor der Boden ruiniert ist. In diesem Tutorial bauen Sie günstige ESP32-Lecksensoren, die Alarme über MQTT veröffentlichen, sowie eine separate Ventilsteuerung, die alle Sensoren abonniert, die Wasserzufuhr automatisch absperrt und geschlossen bleibt, bis eine Person sie zurücksetzt.
Systemüberblick
Das Design trennt Erkennung und Reaktion ganz bewusst:
- Lecksensor-Knoten sitzen überall dort, wo Wasser auftreten kann: unter Spülen, hinter der Waschmaschine, neben dem Warmwasserbereiter. Jeder veröffentlicht einen retained Zustand
dryoderwet. - Der Broker verteilt Alarme an alle Interessierten.
- Eine Ventilsteuerung in der Nähe der Hauptzuleitung abonniert mit einer einzigen Wildcard alle Leck-Topics und schließt beim ersten
weteinen motorisierten Kugelhahn. - Ihr Smartphone, Home Assistant oder Node-RED abonnieren ebenfalls, für Benachrichtigungen und Verlauf.
Durch die getrennten Rollen können Sie Sensoren hinzufügen, ohne den Ventilcode anzufassen, und beliebig viele Apps können denselben Alarm beobachten. Es bedeutet aber auch, dass die Ventilsteuerung ausfallsicher sein muss; dazu weiter unten mehr.
Wo Lecksensoren platziert werden sollten
Lecks entstehen an vorhersehbaren Stellen. Platzieren Sie die Sensoren jeweils am tiefsten Punkt in der Nähe dieser Stellen, denn Wasser fließt nach unten und sammelt sich, bevor es sich ausbreitet:
| Ort | Typische Ursache | Platzierungstipp |
|---|---|---|
| Unter der Küchenspüle | Lockerer Siphon, defekter Panzerschlauch | Hinten auf dem Schrankboden, unter den Eckventilen |
| Hinter der Waschmaschine | Geplatzter Zulaufschlauch, verstopfter Ablauf | Auf dem Boden direkt unter den Schlauchanschlüssen |
| Neben dem Warmwasserbereiter | Korrodierter Speicher, ablassendes Sicherheitsventil | In der Auffangwanne oder an deren tiefster Kante |
| Geschirrspüler | Türdichtung, Zulaufventil | Unter der Sockelblende, falls zugänglich |
| WCs und Waschtischunterschränke | Füllventil, gerissene Zuleitung | Hinter dem WC-Fuß, im Unterschrank |
| Keller oder Hauswirtschaftsraum | Hauptzuleitung, Ausfall der Tauchpumpe | Nahe dem Bodenablauf, in der tiefsten Ecke |
Benennen Sie jeden Sensor nach seinem Ort statt nach einer Nummer. Wenn um 3 Uhr nachts ein Alarm eintrifft, sagt Ihnen laundry, wohin Sie laufen müssen; sensor-4 nicht. In Gewerbe- und Industriegebäuden gilt dasselbe Prinzip im Großen: Leckagesensoren in Technikzentralen oder an Kühlwasserkreisläufen ordnen Sie in den Unified Namespace (UNS) des Standorts nach ISA-95 ein, etwa acme/werk-muenchen/haustechnik/kuehlwasser/leck-03/state.
Stückliste
| Teil | Menge | Zweck |
|---|---|---|
| ESP32 DevKit | 1 pro Sensor + 1 für das Ventil | WLAN und MQTT |
| Zwei Edelstahlschrauben oder eine Leiterplattensonde | 1 Paar pro Sensor | Elektroden, die das Wasser überbrückt |
| 100-kΩ-Widerstand | 1 pro Sensor | Pull-down für die analoge Messung |
| Motorisierter Kugelhahn, 12 V, mit Endschaltern | 1 | Sperrt die Hauptzuleitung ab |
| 2-Kanal-Relaismodul | 1 | Steuert die Leitungen zum Öffnen/Schließen des Ventils |
| 12-V-Netzteil | 1 | Stromversorgung des Ventils |
| Optionaler Summer | 1 pro Sensor | Lokaler Alarm ohne Netzwerk |
Fertige Lecksonden mit zwei Kontaktflächen funktionieren genauso gut wie Schrauben durch ein kleines Kunststoffgehäuse. Entscheidend ist, dass die Elektroden ein bis zwei Millimeter über dem Boden sitzen, damit schon ein dünner Wasserfilm sie überbrückt.
Einen Lecksensor verdrahten
Wasser leitet im Vergleich zu einem Draht schlecht, und eine dauerhafte Gleichspannung an den Elektroden führt über Monate zu Korrosion. Die Lösung: Die Sonde wird nur während der Messung versorgt.
| Anschluss | ESP32-Pin |
|---|---|
| Elektrode A | GPIO 25 (Anregung, nur beim Messen auf HIGH) |
| Elektrode B | GPIO 34 (ADC-Eingang) |
| 100-kΩ-Widerstand | GPIO 34 nach GND |
| Optionaler Summer | GPIO 26 |
Im trockenen Zustand liest der ADC nahezu null. Bei Nässe fließt Strom von GPIO 25 durch das Wasser in GPIO 34, und der Messwert steigt. Leitungswasser ergibt in der Regel einen deutlichen Sprung; destilliertes Wasser leitet kaum, aber echte Lecks bestehen selten aus destilliertem Wasser.
Lecksensor-Sketch für die Wasserleck-Erkennung mit IoT
Der Knoten misst alle zwei Sekunden, verlangt drei aufeinanderfolgende nasse Messwerte, bevor er Alarm auslöst (um Spritzer beim Wischen zu ignorieren), und veröffentlicht einen retained Zustand, damit neue Abonnenten ihn sofort sehen.
#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* BROKER = "broker.connectmq.com";
const char* BASE = "connectmq/demo/w5n8c1/home1"; // Ihr eigenes zufälliges Präfix
const char* LOCATION = "kitchen-sink";
const char* CLIENT_ID = "leak-kitchen-sink-w5n8c1";
const int EXCITE_PIN = 25;
const int SENSE_PIN = 34;
const int BUZZER_PIN = 26;
const int WET_THRESHOLD = 600; // 0..4095, mit einem nassen Tuch kalibrieren
const int CONFIRM_SAMPLES = 3;
WiFiClient net;
PubSubClient mqtt(net);
char tState[96], tAvail[96], tRaw[96];
bool wet = false;
int wetCount = 0, dryCount = 0;
int readProbe() {
digitalWrite(EXCITE_PIN, HIGH);
delay(5);
int v = analogRead(SENSE_PIN);
digitalWrite(EXCITE_PIN, LOW);
return v;
}
void connectMqtt() {
while (!mqtt.connected()) {
if (mqtt.connect(CLIENT_ID, nullptr, nullptr, tAvail, 1, true, "offline")) {
mqtt.publish(tAvail, "online", true);
mqtt.publish(tState, wet ? "wet" : "dry", true);
} else {
delay(3000);
}
}
}
void setup() {
pinMode(EXCITE_PIN, OUTPUT);
digitalWrite(EXCITE_PIN, LOW);
pinMode(BUZZER_PIN, OUTPUT);
snprintf(tState, sizeof tState, "%s/%s/leak/state", BASE, LOCATION);
snprintf(tAvail, sizeof tAvail, "%s/%s/leak/availability", BASE, LOCATION);
snprintf(tRaw, sizeof tRaw, "%s/%s/leak/raw", BASE, LOCATION);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(BROKER, 1883);
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
static unsigned long last = 0;
if (millis() - last < 2000) return;
last = millis();
int v = readProbe();
if (v > WET_THRESHOLD) { wetCount++; dryCount = 0; }
else { dryCount++; wetCount = 0; }
if (!wet && wetCount >= CONFIRM_SAMPLES) {
wet = true;
mqtt.publish(tState, "wet", true);
} else if (wet && dryCount >= 15) { // 30 s trocken vor dem Zurücksetzen
wet = false;
mqtt.publish(tState, "dry", true);
}
digitalWrite(BUZZER_PIN, wet ? HIGH : LOW); // lokaler Alarm funktioniert offline
static unsigned long lastRaw = 0;
if (millis() - lastRaw > 60000) {
lastRaw = millis();
char buf[8];
snprintf(buf, sizeof buf, "%d", v);
mqtt.publish(tRaw, buf); // nützlich für Kalibrierung und Drift
}
}
Der Summer wird lokal angesteuert, sodass ein Leck auch dann hörbar ist, wenn das WLAN ausgefallen ist. Ein solcher lokaler Fallback ist in jedem sicherheitsrelevanten Projekt eine gute Gewohnheit.
Die Ventilsteuerung
Der Ventilknoten abonniert <base>/+/leak/state, was auf jeden Sensor in jedem Raum passt. Beim ersten wet schließt er das Ventil und verriegelt: Das Ventil bleibt geschlossen, bis jemand OPEN sendet und kein Sensor mehr Nässe meldet.
#include <WiFi.h>
#include <PubSubClient.h>
#include <set>
#include <string>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* BROKER = "broker.connectmq.com";
const char* BASE = "connectmq/demo/w5n8c1/home1";
const char* CLIENT_ID = "valve-main-w5n8c1";
const int RELAY_OPEN = 32;
const int RELAY_CLOSE = 33;
const unsigned long TRAVEL_MS = 8000; // Datenblatt Ihres Ventils prüfen
WiFiClient net;
PubSubClient mqtt(net);
char tLeakAll[96], tValve[96], tValveSet[96], tAvail[96];
std::set<std::string> wetSensors;
bool closedLatched = false;
void driveValve(bool close) {
digitalWrite(close ? RELAY_OPEN : RELAY_CLOSE, LOW);
digitalWrite(close ? RELAY_CLOSE : RELAY_OPEN, HIGH);
delay(TRAVEL_MS);
digitalWrite(RELAY_OPEN, LOW);
digitalWrite(RELAY_CLOSE, LOW);
mqtt.publish(tValve, close ? "closed" : "open", true);
}
void onMessage(char* topic, byte* payload, unsigned int len) {
std::string t(topic), p((char*)payload, len);
if (t == tValveSet) {
if (p == "CLOSE") { closedLatched = true; driveValve(true); }
else if (p == "OPEN") {
if (wetSensors.empty()) { closedLatched = false; driveValve(false); }
else mqtt.publish(tValve, "closed (sensor still wet)", true);
}
return;
}
if (p == "wet") {
wetSensors.insert(t);
if (!closedLatched) { closedLatched = true; driveValve(true); }
} else if (p == "dry") {
wetSensors.erase(t);
}
}
void connectMqtt() {
while (!mqtt.connected()) {
if (mqtt.connect(CLIENT_ID, nullptr, nullptr, tAvail, 1, true, "offline")) {
mqtt.publish(tAvail, "online", true);
mqtt.subscribe(tLeakAll, 1);
mqtt.subscribe(tValveSet, 1);
} else {
delay(3000);
}
}
}
void setup() {
pinMode(RELAY_OPEN, OUTPUT);
pinMode(RELAY_CLOSE, OUTPUT);
snprintf(tLeakAll, sizeof tLeakAll, "%s/+/leak/state", BASE);
snprintf(tValve, sizeof tValve, "%s/utility/main-valve/state", BASE);
snprintf(tValveSet, sizeof tValveSet, "%s/utility/main-valve/state/set", BASE);
snprintf(tAvail, sizeof tAvail, "%s/utility/main-valve/availability", BASE);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(BROKER, 1883);
mqtt.setCallback(onMessage);
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
}
Da die Sensorzustände retained sind, erfährt die Ventilsteuerung von jedem Leck, das während ihres Neustarts aufgetreten ist, sobald sie erneut abonniert. Das ist der Hauptgrund, Alarmzustände als retained Messages zu speichern, statt einmalige Ereignisse zu senden.
Das System testen
Beobachten Sie alles und simulieren Sie dann ein Leck mit einem nassen Tuch oder durch manuelles Veröffentlichen:
B=connectmq/demo/w5n8c1/home1
mosquitto_sub -h broker.connectmq.com -p 1883 -i mon-w5n8c1 -t "$B/#" -v
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-w5n8c1 -r -q 1 -t $B/laundry/leak/state -m wet
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-w5n8c1 -r -q 1 -t $B/laundry/leak/state -m dry
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-w5n8c1 -q 1 -t $B/utility/main-valve/state/set -m OPEN
Löschen Sie nach dem Test das simulierte retained Topic mit einer leeren retained Message (-r -n). Der öffentliche MQTT-Broker ist ideal für diese Laborphase, seine Topics sind jedoch für jeden lesbar und er ist nicht für Produktivdaten gedacht; lassen Sie dort also kein echtes Ventil lauschen.
Fehlerfälle, die Sie einplanen sollten
- Ein Sensor verliert die Stromversorgung. Sein Last Will veröffentlicht
offline. Alarmieren Sie auch darauf; ein toter Sensor schützt nichts. - Die Ventilsteuerung verliert das WLAN. Sie kann keine Alarme empfangen. Erwägen Sie, den kritischsten Sensor (Warmwasserbereiter) als festverdrahtete Absicherung direkt an einen GPIO der Ventilsteuerung anzuschließen.
- Das Ventil klemmt. Kugelhähne, die sich nie bewegen, können sich festsetzen. Betätigen Sie das Ventil monatlich tagsüber mit einem geplanten CLOSE/OPEN.
- Fehlalarme. Kalibrieren Sie
WET_THRESHOLDanhand des Raw-Topics und behalten Sie die Bestätigungszählung bei. - Stromausfall. Eine kleine USV hält Router und Ventilsteuerung am Leben; batteriebetriebene Sensoren können Deep Sleep mit einem ext0-Wake-up über die Sonde nutzen.
Alarmeskalation, die auch wirklich bemerkt wird
Ein Alarm, den niemand sieht, ist kein Alarm. Eine einfache Eskalationsleiter funktioniert gut:
- Lokaler Summer am Sensor, sofort und ganz ohne Netzwerk.
- Push-Benachrichtigung von Home Assistant, Node-RED oder einem kleinen Python-Subscriber, sobald ein
wet-Zustand eintrifft. - Das Ventil schließt automatisch, und der geschlossene Zustand wird als zweite Benachrichtigung gesendet, damit Sie wissen, dass die Zufuhr abgesperrt ist.
- Wiederholte Erinnerungen alle fünfzehn Minuten, solange ein Sensor nass ist oder das Ventil verriegelt bleibt.
- Offline-Alarme, wenn ein Sensor oder die Ventilsteuerung
offlineveröffentlicht, denn Stille kann ein Problem verbergen.
Halten Sie die Benachrichtigungslogik aus den Sensoren heraus. Die Geräte veröffentlichen Fakten; die Subscriber entscheiden, wen sie wie oft informieren.
Umstieg auf einen privaten Broker
Für eine echte Installation erstellen Sie im Portal einen Broker und stellen dann beide Sketches auf WiFiClientSecure, Port 8883, die CA ISRG Root X1 sowie Ihre generierten Werte YOUR_USERNAME, YOUR_PASSWORD und YOUR_CLIENT_ID um. Jedes Gerät braucht seine eigene Client ID. Im ConnectMQ-Portal können Sie jedes Leck-Topic im Live-Topic-Explorer beobachten und Schwellwert-Alarmregeln anlegen, zum Beispiel auf den rohen Sondenwert.
Für Benachrichtigungen auf dem Smartphone kann Home Assistant dieselbe Wildcard abonnieren; unser Leitfaden zu Home-Assistant-MQTT-Automatisierungen zeigt die Trigger-Syntax. Wenn Sie außerdem versteckte Lecks anhand des nächtlichen Verbrauchs erkennen möchten, kombinieren Sie das Projekt mit einem Wasserzähler über MQTT. Der Leitfaden zur MQTT-Topic-Struktur im Smart Home erklärt die hier verwendeten Konventionen für /set und retained Zustände.
Schützen Sie Ihr Zuhause mit ConnectMQ
Ein Lecksystem muss online sein, wenn es darauf ankommt; ein verwalteter MQTT-Broker ist daher eine sinnvolle Grundlage. ConnectMQ bietet Ihnen TLS, generierte Zugangsdaten und Alarmregeln in einem Portal. Erstellen Sie unter app.connectmq.com einen kostenlosen Serverless-Broker, ohne Kreditkarte, und ziehen Sie Ihre Sensoren in wenigen Minuten um. Weitere Heimprojekte finden Sie im Leitfaden zur IoT-Automatisierung.
Häufig gestellte Fragen
Warum bleibt das Ventil geschlossen, nachdem der Boden trocken ist?
Das ist Absicht. Ein Leck bedeutet in der Regel, dass etwas defekt ist. Deshalb bleibt das Ventil verriegelt, bis eine Person nachsieht und OPEN sendet, was erst funktioniert, wenn alle Sensoren wieder trocken melden.
Kann ich die Lecksensoren mit Batterien betreiben?
Ja. Nutzen Sie Deep Sleep mit periodischem Aufwachen oder wecken Sie den ESP32 über ext0 an der Sonde, und verbinden Sie sich nur, wenn sich der Zustand ändert. Rechnen Sie damit, die Schaltung anpassen zu müssen, denn die analoge Sonde muss versorgt sein, um Wasser zu erkennen.
Warum retained Messages für Leckalarme verwenden?
Ein retained wet-Zustand wird jedem Client in dem Moment zugestellt, in dem er abonniert. So erfährt auch eine Ventilsteuerung oder App, die während eines Lecks neu startet, sofort davon.
Korrodiert die Sonde?
Eine dauerhafte Gleichspannung an den Elektroden beschleunigt die Korrosion. Wenn Sie die Sonde wie im Sketch nur für wenige Millisekunden pro Messung versorgen und Elektroden aus Edelstahl verwenden, bleibt sie lange funktionsfähig.



