La détection de fuite d'eau IoT transforme un goutte-à-goutte silencieux sous l'évier en alarme sur votre téléphone et, mieux encore, en vanne fermée avant que le sol ne soit abîmé. Dans ce tutoriel, vous allez fabriquer des capteurs de fuite ESP32 à bas coût qui publient des alarmes via MQTT, ainsi qu'un contrôleur de vanne distinct qui s'abonne à tous les capteurs et coupe automatiquement l'arrivée d'eau, en restant fermé jusqu'à ce qu'une personne le réarme.
Vue d'ensemble du système
La conception sépare volontairement la détection de l'action :
- Les nœuds capteurs de fuite sont placés partout où l'eau peut apparaître : sous les éviers, derrière le lave-linge, à côté du chauffe-eau. Chacun publie un état
dryouweten retained message. - Le broker distribue les alarmes à tous ceux qui sont intéressés.
- Un contrôleur de vanne situé près de l'arrivée principale s'abonne à tous les topics de fuite avec un seul wildcard et ferme une vanne à bille motorisée au premier
wet. - Votre téléphone, Home Assistant ou Node-RED s'abonnent également, pour les notifications et l'historique.
Séparer les rôles permet d'ajouter des capteurs sans toucher au code de la vanne, et un nombre quelconque d'applications peut surveiller la même alarme. Cela implique aussi que le contrôleur de vanne doit adopter un comportement sûr en cas de défaillance, ce que nous abordons plus bas.
Où placer les capteurs de fuite
Les fuites apparaissent à des endroits prévisibles. Placez les capteurs au point le plus bas près de chacun d'eux, car l'eau s'écoule vers le bas et forme une flaque avant de s'étendre :
| Emplacement | Cause typique | Conseil de placement |
|---|---|---|
| Sous l'évier de la cuisine | Siphon desserré, flexible défaillant | Au fond du meuble, sous les robinets d'arrêt |
| Derrière le lave-linge | Tuyau d'arrivée éclaté, évacuation bouchée | Au sol, directement sous les raccords des tuyaux |
| À côté du chauffe-eau | Cuve corrodée, écoulement du groupe de sécurité | Dans le bac de récupération ou à son bord le plus bas |
| Lave-vaisselle | Joint de porte, électrovanne d'arrivée | Sous la plinthe si elle est accessible |
| Toilettes et meubles de salle de bains | Robinet flotteur, flexible d'alimentation fissuré | Derrière le pied des toilettes, à l'intérieur du meuble |
| Sous-sol ou buanderie | Arrivée principale, panne de pompe de relevage | Près du siphon de sol, dans l'angle le plus bas |
Nommez chaque capteur selon son emplacement plutôt qu'avec un numéro. Quand une alarme arrive à 3 heures du matin, laundry vous dit où courir ; sensor-4, non.
Liste du matériel
| Composant | Quantité | Rôle |
|---|---|---|
| ESP32 DevKit | 1 par capteur + 1 pour la vanne | Wi-Fi et MQTT |
| Deux vis en acier inoxydable ou une sonde sur circuit imprimé | 1 paire par capteur | Électrodes que l'eau relie |
| Résistance de 100 kΩ | 1 par capteur | Pull-down pour la lecture analogique |
| Vanne à bille motorisée 12 V avec fins de course | 1 | Coupe l'arrivée principale |
| Module relais 2 canaux | 1 | Commande les fils d'ouverture et de fermeture de la vanne |
| Alimentation 12 V | 1 | Alimentation de la vanne |
| Buzzer facultatif | 1 par capteur | Alarme locale sans réseau |
Les sondes de fuite toutes faites à deux plots de contact fonctionnent aussi bien que des vis traversant un petit boîtier en plastique. L'important est que les électrodes se trouvent à un ou deux millimètres au-dessus du sol, pour qu'une fine pellicule d'eau les relie.
Câbler un capteur de fuite
L'eau conduit mal comparée à un fil, et une tension continue permanente sur les électrodes provoque de la corrosion au fil des mois. La solution consiste à n'alimenter la sonde que pendant la mesure :
| Connexion | Broche de l'ESP32 |
|---|---|
| Électrode A | GPIO 25 (excitation, mise à HIGH uniquement pendant l'échantillonnage) |
| Électrode B | GPIO 34 (entrée ADC) |
| Résistance de 100 kΩ | GPIO 34 vers GND |
| Buzzer facultatif | GPIO 26 |
À sec, l'ADC lit une valeur proche de zéro. Mouillée, la sonde laisse passer le courant du GPIO 25 vers le GPIO 34 à travers l'eau, et la valeur grimpe. L'eau du robinet produit généralement un saut net ; l'eau distillée conduit à peine, mais les vraies fuites sont rarement distillées.
Sketch du capteur pour la détection de fuite d'eau IoT
Le nœud effectue une mesure toutes les deux secondes, exige trois lectures humides consécutives avant de déclencher l'alarme (pour ignorer les éclaboussures d'un coup de serpillière) et publie un état en retained message pour que les nouveaux abonnés le voient instantanément.
#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"; // your own random prefix
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, calibrate with a wet cloth
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 dry before clearing
wet = false;
mqtt.publish(tState, "dry", true);
}
digitalWrite(BUZZER_PIN, wet ? HIGH : LOW); // local alarm works 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); // useful for calibration and drift
}
}
Le buzzer est commandé localement : une fuite reste audible même lorsque le Wi-Fi est coupé. Ce type de repli local est une bonne habitude dans tout projet lié à la sécurité.
Le contrôleur de vanne
Le nœud de la vanne s'abonne à <base>/+/leak/state, ce qui couvre chaque capteur de chaque pièce. Au premier wet, il ferme la vanne et se verrouille : la vanne reste fermée jusqu'à ce que quelqu'un envoie OPEN et qu'aucun capteur ne signale plus d'humidité.
#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; // check your valve's datasheet
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();
}
Comme les états des capteurs sont retenus, le contrôleur de vanne apprend l'existence de toute fuite survenue pendant son redémarrage dès qu'il se réabonne. C'est la principale raison de retenir les états d'alarme plutôt que d'envoyer des événements ponctuels.
Tester le système
Surveillez tout, puis simulez une fuite avec un chiffon mouillé ou en publiant manuellement :
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
Après le test, effacez le topic simulé avec un retained message vide (-r -n). Le serveur MQTT public gratuit est idéal pour cette phase sur l'établi, mais ses topics sont lisibles par tous : ne laissez pas une vraie vanne à l'écoute dessus.
Modes de défaillance à anticiper
- Un capteur perd son alimentation. Son last will publie
offline. Déclenchez aussi une alerte dans ce cas ; un capteur hors service ne protège rien. - Le contrôleur de vanne perd le Wi-Fi. Il ne peut plus entendre les alarmes. Envisagez de câbler directement le capteur le plus critique (chauffe-eau) sur une GPIO du contrôleur de vanne, comme secours filaire.
- La vanne se grippe. Les vannes à bille qui ne bougent jamais peuvent se bloquer. Manœuvrez la vanne une fois par mois avec un CLOSE/OPEN planifié en journée.
- Fausses alarmes. Calibrez
WET_THRESHOLDà l'aide du topic raw, et conservez le nombre de confirmations. - Coupure de courant secteur. Un petit onduleur maintient en vie la box et le contrôleur de vanne ; les capteurs sur batterie peuvent utiliser le deep sleep avec un réveil ext0 sur la sonde.
Une escalade des alertes que les gens remarquent vraiment
Une alarme que personne ne voit n'est pas une alarme. Une échelle d'escalade simple fonctionne bien :
- Buzzer local sur le capteur, immédiatement et sans aucun réseau.
- Notification push depuis Home Assistant, Node-RED ou un petit abonné Python dès qu'un état
wetarrive. - Fermeture automatique de la vanne, avec l'état fermé envoyé en seconde notification pour que vous sachiez que l'arrivée est coupée.
- Rappels répétés toutes les quinze minutes tant qu'un capteur est humide ou que la vanne est verrouillée.
- Alertes de déconnexion lorsqu'un capteur ou le contrôleur de vanne publie
offline, car le silence peut masquer un problème.
Gardez la logique de notification hors des capteurs. Les appareils publient des faits ; les abonnés décident qui prévenir et à quelle fréquence.
Passer à un broker privé
Pour une vraie installation, créez un broker dans le portail, puis basculez les deux sketches vers WiFiClientSecure, le port 8883, l'autorité de certification ISRG Root X1 et vos YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID générés. Chaque appareil a besoin de son propre client ID. Dans le portail ConnectMQ, vous pouvez suivre chaque topic de fuite dans l'explorateur de topics en direct et créer des règles d'alerte par seuil, par exemple sur la valeur brute de la sonde.
Pour les notifications sur téléphone, Home Assistant peut s'abonner au même wildcard ; notre guide sur les automatisations MQTT dans Home Assistant présente la syntaxe des déclencheurs. Jeedom et Domoticz savent aussi communiquer en MQTT et peuvent s'abonner aux mêmes topics. Si vous voulez aussi repérer les fuites cachées à partir de la consommation nocturne, associez ce montage à un compteur d'eau via MQTT. Le guide sur la structure des topics MQTT pour la maison connectée explique les conventions /set et d'état retenu utilisées ici.
Protégez votre maison avec ConnectMQ
Un système de détection de fuite doit être en ligne au moment où cela compte, ce qui fait d'un broker MQTT managé une base judicieuse. ConnectMQ vous fournit TLS, des identifiants générés et des règles d'alerte dans un seul portail. Créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire, et migrez vos capteurs en quelques minutes. D'autres projets pour la maison sont rassemblés dans le guide de l'automatisation IoT.
Questions fréquentes
Pourquoi la vanne reste-t-elle fermée une fois le sol sec ?
C'est voulu. Une fuite signifie généralement que quelque chose est cassé : la vanne reste donc verrouillée jusqu'à ce qu'une personne vérifie et envoie OPEN, ce qui ne fonctionne qu'une fois que tous les capteurs signalent un état sec.
Les capteurs de fuite peuvent-ils fonctionner sur batterie ?
Oui. Utilisez le deep sleep avec un réveil périodique, ou un réveil par la sonde via ext0, et ne vous connectez que lorsque l'état change. Attendez-vous à devoir ajuster le circuit, car la sonde analogique doit être alimentée pour détecter l'eau.
Pourquoi utiliser des retained messages pour les alarmes de fuite ?
Un état wet en retained message est livré à tout client dès son abonnement : un contrôleur de vanne ou une application qui redémarre pendant une fuite en est donc informé immédiatement.
La sonde va-t-elle se corroder ?
Maintenir en permanence une tension continue sur les électrodes accélère la corrosion. N'alimenter la sonde que quelques millisecondes par mesure, comme le fait le sketch, et utiliser des électrodes en acier inoxydable lui assurent une longue durée de vie.



