IoT-Automatisierung

Wasserleck-Erkennung mit IoT: ESP32-Sensoren, MQTT-Alarme und automatisches Absperrventil

Bauen Sie ESP32-Lecksensoren, die retained MQTT-Alarme veröffentlichen, und eine Ventilsteuerung, die die Wasserzufuhr automatisch absperrt und verriegelt bleibt, bis jemand sie zurücksetzt.

IoT-Aufbau zur Wasserleck-Erkennung mit einer ESP32-Sonde unter der Spüle und einem motorisierten Absperrventil an der Hauptleitung
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

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:

  1. Lecksensor-Knoten sitzen überall dort, wo Wasser auftreten kann: unter Spülen, hinter der Waschmaschine, neben dem Warmwasserbereiter. Jeder veröffentlicht einen retained Zustand dry oder wet.
  2. Der Broker verteilt Alarme an alle Interessierten.
  3. Eine Ventilsteuerung in der Nähe der Hauptzuleitung abonniert mit einer einzigen Wildcard alle Leck-Topics und schließt beim ersten wet einen motorisierten Kugelhahn.
  4. 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

  1. Ein Sensor verliert die Stromversorgung. Sein Last Will veröffentlicht offline. Alarmieren Sie auch darauf; ein toter Sensor schützt nichts.
  2. 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.
  3. 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.
  4. Fehlalarme. Kalibrieren Sie WET_THRESHOLD anhand des Raw-Topics und behalten Sie die Bestätigungszählung bei.
  5. 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:

  1. Lokaler Summer am Sensor, sofort und ganz ohne Netzwerk.
  2. Push-Benachrichtigung von Home Assistant, Node-RED oder einem kleinen Python-Subscriber, sobald ein wet-Zustand eintrifft.
  3. Das Ventil schließt automatisch, und der geschlossene Zustand wird als zweite Benachrichtigung gesendet, damit Sie wissen, dass die Zufuhr abgesperrt ist.
  4. Wiederholte Erinnerungen alle fünfzehn Minuten, solange ein Sensor nass ist oder das Ventil verriegelt bleibt.
  5. Offline-Alarme, wenn ein Sensor oder die Ventilsteuerung offline verö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.

Verwandte Artikel

Zigbee2MQTT einrichten: Vom Koordinator bis zum Dashboard

Installieren Sie Zigbee2MQTT mit Docker, konfigurieren Sie Koordinator und MQTT-Verbindung, lernen Sie Geräte an und benennen Sie sie um, steuern Sie sie per MQTT und zeigen Sie ihre Zustände auf einem Dashboard an, zuerst getestet auf einem kostenlosen öffentlichen MQTT-Broker.

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.