A detecção de vazamento de água com IoT transforma um gotejamento silencioso embaixo da pia em um alarme no seu celular e, melhor ainda, em uma válvula fechada antes que o piso seja arruinado. Neste tutorial você vai construir sensores de vazamento de baixo custo com ESP32 que publicam alarmes via MQTT e um controlador de válvula separado que assina todos os sensores e corta o abastecimento de água automaticamente, permanecendo fechado até que uma pessoa o rearme.
Visão geral do sistema
O design separa deliberadamente a detecção da ação:
- Os nós sensores de vazamento ficam onde a água pode aparecer: embaixo das pias, atrás da máquina de lavar, ao lado do aquecedor de água. Cada um publica um estado retido
dryouwet. - O broker distribui os alarmes para todos os interessados.
- Um controlador de válvula perto da entrada principal assina todos os topics de vazamento com um único curinga e fecha uma válvula esfera motorizada no primeiro
wet. - Seu celular, o Home Assistant ou o Node-RED também assinam, para notificações e histórico.
Separar os papéis permite adicionar sensores sem mexer no código da válvula, e qualquer número de aplicativos pode acompanhar o mesmo alarme. Também significa que o controlador de válvula precisa falhar de forma segura, o que abordamos mais adiante.
Onde instalar os sensores de vazamento
Os vazamentos começam em lugares previsíveis. Coloque os sensores no ponto mais baixo perto de cada um deles, porque a água escorre para baixo e se acumula antes de se espalhar:
| Local | Causa típica | Dica de instalação |
|---|---|---|
| Embaixo da pia da cozinha | Sifão solto, mangueira flexível rompida | No fundo do piso do armário, embaixo dos registros |
| Atrás da máquina de lavar | Mangueira de entrada estourada, ralo entupido | No piso, logo abaixo das conexões das mangueiras |
| Ao lado do aquecedor de água | Tanque corroído, descarga da válvula de alívio | Dentro da bandeja coletora ou na sua borda mais baixa |
| Lava-louças | Vedação da porta, válvula de entrada | Embaixo do rodapé frontal, se for acessível |
| Vasos sanitários e gabinetes de banheiro | Válvula de enchimento, flexível rachado | Atrás da base do vaso, dentro do gabinete |
| Porão ou área de serviço | Entrada principal, falha da bomba de drenagem | Perto do ralo, no canto mais baixo |
Nomeie cada sensor pelo local, não por um número. Quando um alarme chega às 3 da manhã, laundry diz para onde correr; sensor-4, não.
Lista de componentes
| Componente | Quantidade | Finalidade |
|---|---|---|
| ESP32 DevKit | 1 por sensor + 1 para a válvula | Wi-Fi e MQTT |
| Dois parafusos de aço inox ou uma sonda em PCB | 1 par por sensor | Eletrodos que a água conecta |
| Resistor de 100 kΩ | 1 por sensor | Pull-down para a leitura analógica |
| Válvula esfera motorizada, 12 V, com fins de curso | 1 | Corta o abastecimento principal |
| Módulo de relé de 2 canais | 1 | Aciona os fios de abrir/fechar da válvula |
| Fonte de 12 V | 1 | Alimentação da válvula |
| Buzzer opcional | 1 por sensor | Alarme local sem rede |
Sondas de vazamento prontas, com duas placas de contato, funcionam tão bem quanto parafusos atravessando uma pequena caixa plástica. O que importa é que os eletrodos fiquem um ou dois milímetros acima do piso, para que uma fina película de água os conecte.
Ligando um sensor de vazamento
A água conduz mal em comparação com um fio, e uma tensão contínua constante nos eletrodos causa corrosão ao longo dos meses. A solução é alimentar a sonda apenas durante a medição:
| Conexão | Pino do ESP32 |
|---|---|
| Eletrodo A | GPIO 25 (excitação, em HIGH apenas durante a amostragem) |
| Eletrodo B | GPIO 34 (entrada ADC) |
| Resistor de 100 kΩ | GPIO 34 ao GND |
| Buzzer opcional | GPIO 26 |
Seco, o ADC lê perto de zero. Molhado, a corrente passa do GPIO 25 pela água até o GPIO 34 e a leitura sobe. A água da torneira normalmente dá um salto nítido; a água destilada quase não conduz, mas vazamentos reais raramente são de água destilada.
Sketch do sensor para detecção de vazamento de água IoT
O nó faz uma amostragem a cada dois segundos, exige três leituras molhadas consecutivas antes de alarmar (para ignorar um respingo do pano de chão) e publica um estado retido para que novos assinantes o vejam na hora.
#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"; // seu próprio prefixo aleatório
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, calibre com um pano molhado
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 seco antes de limpar
wet = false;
mqtt.publish(tState, "dry", true);
}
digitalWrite(BUZZER_PIN, wet ? HIGH : LOW); // o alarme local funciona 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); // útil para calibração e deriva
}
}
O buzzer é acionado localmente, então um vazamento é audível mesmo com o Wi-Fi fora do ar. Esse tipo de fallback local é um bom hábito em qualquer projeto relacionado à segurança.
O controlador da válvula
O nó da válvula assina <base>/+/leak/state, que corresponde a todos os sensores de todos os cômodos. No primeiro wet, ele fecha a válvula e trava: a válvula permanece fechada até que alguém envie OPEN e nenhum sensor ainda esteja informando molhado.
#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; // confira o datasheet da sua válvula
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();
}
Como os estados dos sensores são retidos, o controlador da válvula fica sabendo de qualquer vazamento ocorrido enquanto ele reiniciava no momento em que assina de novo. Esse é o principal motivo para reter os estados de alarme em vez de enviar eventos avulsos.
Testando o sistema
Observe tudo e depois simule um vazamento com um pano molhado ou publicando manualmente:
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
Depois do teste, limpe o topic retido simulado com uma retained message vazia (-r -n). O servidor MQTT público (gratuito) é ideal para essa fase de bancada, mas seus topics podem ser lidos por qualquer pessoa e ele não é feito para dados de produção, então não deixe uma válvula real escutando nele.
Modos de falha para os quais se preparar
- O sensor perde a alimentação. Seu last will publica
offline. Gere um alerta para isso também; um sensor morto não protege nada. - O controlador da válvula perde o Wi-Fi. Ele não consegue ouvir os alarmes. Considere ligar o sensor mais crítico (aquecedor de água) diretamente a um GPIO do controlador da válvula como backup cabeado.
- A válvula trava. Válvulas esfera que nunca se movem podem emperrar. Exercite a válvula mensalmente com um CLOSE/OPEN agendado durante o dia.
- Alarmes falsos. Calibre o
WET_THRESHOLDusando o topic raw e mantenha a contagem de confirmação. - Queda de energia da rede elétrica. Um pequeno nobreak mantém o roteador e o controlador da válvula funcionando; sensores a bateria podem usar deep sleep com despertar ext0 pela sonda.
Escalonamento de alertas que as pessoas realmente percebem
Um alarme que ninguém vê não é um alarme. Uma escada de escalonamento simples funciona bem:
- Buzzer local no sensor, imediatamente e sem nenhuma rede.
- Notificação push do Home Assistant, do Node-RED ou de um pequeno subscriber em Python assim que chegar um estado
wet. - A válvula fecha automaticamente, e o estado fechado é enviado como uma segunda notificação para você saber que o abastecimento foi cortado.
- Lembretes repetidos a cada quinze minutos enquanto algum sensor estiver molhado ou a válvula estiver travada.
- Alertas de offline quando um sensor ou o controlador da válvula publicar
offline, porque o silêncio pode esconder um problema.
Mantenha a lógica de notificação fora dos sensores. Os dispositivos publicam fatos; os assinantes decidem quem avisar e com que frequência.
Migrando para um broker privado
Para uma instalação real, crie um broker no portal e depois mude os dois sketches para WiFiClientSecure, porta 8883, a CA ISRG Root X1 e os seus YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID gerados. Cada dispositivo precisa do seu próprio client ID. No portal do ConnectMQ, você pode acompanhar todos os topics de vazamento no explorador de topics ao vivo e criar regras de alerta por limite, por exemplo sobre o valor bruto da sonda.
Para notificações no celular, o Home Assistant pode assinar o mesmo curinga; nosso guia de automações MQTT no Home Assistant mostra a sintaxe dos gatilhos. Se também quiser identificar vazamentos ocultos pelo consumo durante a noite, combine isso com um medidor de água via MQTT. O guia de estrutura de topics MQTT para casa inteligente explica as convenções de /set e de estado retido usadas aqui.
Proteja sua casa com o ConnectMQ
Um sistema contra vazamentos precisa estar online quando importa, o que faz de um broker MQTT gerenciado uma base sensata. O ConnectMQ oferece TLS, credenciais geradas e regras de alerta em um único portal. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e migre seus sensores em minutos. Mais projetos residenciais estão reunidos no guia de automação IoT.
Perguntas frequentes
Por que a válvula continua fechada depois que o piso secou?
Isso é intencional. Um vazamento geralmente significa que algo quebrou, então a válvula fica travada até que uma pessoa verifique e envie OPEN, o que só funciona quando todos os sensores informam seco.
Posso alimentar os sensores de vazamento com bateria?
Sim. Use deep sleep com despertar periódico, ou desperte pela sonda via ext0, e conecte apenas quando o estado mudar. Conte com ajustes no circuito, porque a sonda analógica precisa estar alimentada para detectar água.
Por que usar retained messages para alarmes de vazamento?
Um estado wet retido é entregue a qualquer client no momento em que ele assina, então um controlador de válvula ou um aplicativo que reinicie durante um vazamento fica sabendo dele imediatamente.
A sonda vai corroer?
Manter tensão contínua nos eletrodos o tempo todo acelera a corrosão. Alimentar a sonda apenas por alguns milissegundos por amostra, como o sketch faz, e usar eletrodos de aço inox a mantém funcionando por muito tempo.



