IoT ile su kaçağı tespiti, lavabonun altındaki sessiz bir damlamayı telefonunuzda bir alarma ve daha da iyisi, zemin mahvolmadan kapanmış bir vanaya dönüştürür. Bu eğitimde, alarmları MQTT üzerinden yayınlayan düşük maliyetli ESP32 kaçak sensörleri ve her sensöre abone olan, su beslemesini otomatik olarak kesen ve biri sıfırlayana kadar kapalı kalan ayrı bir vana kontrolcüsü oluşturacaksınız.
Sisteme Genel Bakış
Tasarım, tespiti eylemden bilinçli olarak ayırır:
- Kaçak sensörü düğümleri suyun ortaya çıkabileceği her yerde durur: lavaboların altında, çamaşır makinesinin arkasında, şofbenin yanında. Her biri retained bir
dryveyawetdurumu yayınlar. - Broker alarmları ilgilenen herkese dağıtır.
- Ana besleme hattının yakınındaki bir vana kontrolcüsü tek bir wildcard ile tüm kaçak topic'lerine abone olur ve ilk
wetdurumunda motorlu bir küresel vanayı kapatır. - Telefonunuz, Home Assistant veya Node-RED de bildirimler ve geçmiş için abone olur.
Rolleri ayırmak, vana koduna dokunmadan sensör ekleyebileceğiniz ve istediğiniz sayıda uygulamanın aynı alarmı izleyebileceği anlamına gelir. Aynı zamanda vana kontrolcüsünün güvenli tarafta arızalanması (fail safe) gerektiği anlamına da gelir; bunu aşağıda ele alıyoruz.
Kaçak Sensörleri Nereye Yerleştirilmeli?
Kaçaklar öngörülebilir yerlerde başlar. Su aşağı doğru akıp yayılmadan önce biriktiği için sensörleri bu noktaların her birinin yakınındaki en alçak noktaya koyun:
| Konum | Tipik neden | Yerleştirme ipucu |
|---|---|---|
| Mutfak lavabosunun altı | Gevşek sifon, arızalı spiral hortum | Dolap tabanının arkasında, besleme vanalarının altında |
| Çamaşır makinesinin arkası | Patlamış giriş hortumu, tıkalı tahliye | Zeminde, doğrudan hortum bağlantılarının altında |
| Şofbenin yanı | Korozyona uğramış tank, emniyet ventili tahliyesi | Damlama tavasının içinde veya en alçak kenarında |
| Bulaşık makinesi | Kapı contası, giriş valfi | Erişilebiliyorsa alt süpürgeliğin altında |
| Banyo klozetleri ve lavabo dolapları | Doldurma valfi, çatlak besleme hattı | Klozet tabanının arkasında, lavabo dolabının içinde |
| Bodrum veya tesisat odası | Ana besleme, drenaj pompası arızası | Yer süzgecinin yakınında, en alçak köşede |
Her sensörü bir numarayla değil, konumuyla adlandırın. Gece saat 3'te bir alarm geldiğinde laundry nereye koşacağınızı söyler; sensor-4 söylemez.
Malzeme Listesi
| Parça | Adet | Amaç |
|---|---|---|
| ESP32 DevKit | Sensör başına 1 + vana için 1 | Wi-Fi ve MQTT |
| İki paslanmaz çelik vida veya bir PCB probu | Sensör başına 1 çift | Suyun köprülediği elektrotlar |
| 100 kΩ direnç | Sensör başına 1 | Analog okuma için pull-down |
| Uç durdurmalı, 12 V motorlu küresel vana | 1 | Ana beslemeyi keser |
| 2 kanallı röle modülü | 1 | Vananın açma/kapama kablolarını sürer |
| 12 V güç kaynağı | 1 | Vana gücü |
| İsteğe bağlı buzzer | Sensör başına 1 | Ağ olmadan yerel alarm |
İki temas pedi olan hazır kaçak probları, küçük bir plastik kutudan geçirilmiş vidalar kadar iyi çalışır. Önemli olan, ince bir su tabakasının onları köprüleyebilmesi için elektrotların zeminden bir iki milimetre yukarıda durmasıdır.
Bir Kaçak Sensörünün Bağlantıları
Su, bir kabloya kıyasla elektriği zayıf iletir ve elektrotlardaki sabit bir DC gerilimi aylar içinde korozyona yol açar. Çözüm, probu yalnızca ölçüm yaparken beslemektir:
| Bağlantı | ESP32 pini |
|---|---|
| Elektrot A | GPIO 25 (uyarma, yalnızca örnekleme sırasında HIGH yapılır) |
| Elektrot B | GPIO 34 (ADC girişi) |
| 100 kΩ direnç | GPIO 34'ten GND'ye |
| İsteğe bağlı buzzer | GPIO 26 |
Kuruyken ADC sıfıra yakın okur. Islakken akım GPIO 25'ten suyun içinden GPIO 34'e akar ve okuma yükselir. Musluk suyu genellikle belirgin bir sıçrama verir; damıtılmış su neredeyse hiç iletmez, ancak gerçek kaçaklar nadiren damıtılmış sudur.
IoT ile Su Kaçağı Tespiti için Kaçak Sensörü Sketch'i
Düğüm iki saniyede bir örnekleme yapar, alarm vermeden önce art arda üç ıslak okuma bekler (paspas sırasında sıçrayan suyu yok saymak için) ve yeni abonelerin anında görmesi için retained bir durum yayınlar.
#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
}
}
Buzzer yerel olarak sürülür; böylece Wi-Fi kapalıyken bile bir kaçak duyulabilir. Bu tür bir yerel yedek, güvenlikle ilgili her projede iyi bir alışkanlıktır.
Vana Kontrolcüsü
Vana düğümü, her odadaki her sensörle eşleşen <base>/+/leak/state topic'ine abone olur. İlk wet durumunda vanayı kapatır ve kilitlenir (latch): vana, biri OPEN gönderene ve hiçbir sensör hâlâ ıslak bildirmeyene kadar kapalı kalır.
#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();
}
Sensör durumları retained olduğundan, vana kontrolcüsü yeniden başlatılırken meydana gelen herhangi bir kaçağı yeniden abone olduğu anda öğrenir. Alarm durumlarını tek seferlik olaylar göndermek yerine retained yapmanın asıl nedeni budur.
Sistemi Test Etmek
Her şeyi izleyin, ardından ıslak bir bezle veya elle yayın yaparak bir kaçağı simüle edin:
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
Testten sonra simüle edilen retained topic'i boş bir retained mesajla (-r -n) temizleyin. Herkese açık MQTT test broker'ı bu tezgâh aşaması için idealdir, ancak topic'leri herkes tarafından okunabilir; bu yüzden gerçek bir vanayı orayı dinler hâlde bırakmayın.
Hazırlıklı Olunması Gereken Arıza Durumları
- Sensörün gücü kesilir. Last will mesajı
offlineyayınlar. Bunun için de alarm verin; çalışmayan bir sensör hiçbir şeyi korumaz. - Vana kontrolcüsü Wi-Fi'ı kaybeder. Alarmları duyamaz. En kritik sensörü (şofben) kablolu bir yedek olarak doğrudan vana kontrolcüsünün GPIO'suna bağlamayı düşünün.
- Vana sıkışır. Hiç hareket etmeyen küresel vanalar tutukluk yapabilir. Vanayı gündüz zamanlanmış bir CLOSE/OPEN ile ayda bir çalıştırın.
- Yanlış alarmlar.
WET_THRESHOLDdeğerini raw topic'ini kullanarak kalibre edin ve doğrulama sayısını koruyun. - Şebeke elektriği kesintisi. Küçük bir UPS, modemi ve vana kontrolcüsünü ayakta tutar; pilli sensörler prob üzerinde ext0 uyandırmalı deep sleep kullanabilir.
İnsanların Gerçekten Fark Ettiği Alarm Eskalasyonu
Kimsenin görmediği bir alarm, alarm değildir. Basit bir eskalasyon basamağı iyi çalışır:
- Sensör üzerinde, hemen ve hiçbir ağ olmadan çalan yerel buzzer.
- Bir
wetdurumu geldiği anda Home Assistant, Node-RED veya küçük bir Python abonesinden anlık bildirim (push notification). - Vana otomatik olarak kapanır ve kapalı durum ikinci bir bildirim olarak iletilir; böylece beslemenin kesildiğini bilirsiniz.
- Herhangi bir sensör ıslakken veya vana kilitliyken on beş dakikada bir tekrarlanan hatırlatmalar.
- Bir sensör veya vana kontrolcüsü
offlineyayınladığında çevrimdışı alarmları, çünkü sessizlik bir sorunu gizleyebilir.
Bildirim mantığını sensörlerin dışında tutun. Cihazlar olguları yayınlar; kime ve ne sıklıkla haber verileceğine aboneler karar verir.
Özel Bir Broker'a Geçiş
Gerçek bir kurulum için portalda bir broker oluşturun, ardından her iki sketch'i de WiFiClientSecure, 8883 portu, ISRG Root X1 CA ve oluşturulan YOUR_USERNAME, YOUR_PASSWORD ve YOUR_CLIENT_ID değerlerine geçirin. Her cihazın kendi client ID'si olmalıdır. ConnectMQ portalında her kaçak topic'ini canlı topic gezgininde izleyebilir ve örneğin ham prob değeri üzerinde eşik tabanlı alarm kuralları oluşturabilirsiniz.
Telefon bildirimleri için Home Assistant aynı wildcard'a abone olabilir; Home Assistant MQTT otomasyonları rehberimiz tetikleyici sözdizimini gösteriyor. Gece tüketiminden gizli kaçakları da fark etmek istiyorsanız bunu bir MQTT üzerinden su sayacı ile eşleştirin. Akıllı ev MQTT topic yapısı rehberi, burada kullanılan /set ve retained durum kurallarını açıklıyor.
Evinizi ConnectMQ ile Koruyun
Bir kaçak sisteminin önemli olduğu anda çevrimiçi olması gerekir; bu da barındırılan bir MQTT broker'ı mantıklı bir temel hâline getirir. ConnectMQ size TLS'i, otomatik oluşturulan kimlik bilgilerini ve alarm kurallarını tek bir portalda sunar. Kredi kartı gerektirmeden app.connectmq.com adresinde ücretsiz bir Serverless broker oluşturun ve sensörlerinizi dakikalar içinde taşıyın. Daha fazla ev projesi IoT otomasyon rehberinde bir araya getirildi.
Sık Sorulan Sorular
Zemin kuruduktan sonra vana neden kapalı kalıyor?
Bu bilinçli bir tercihtir. Bir kaçak genellikle bir şeyin bozulduğu anlamına gelir; bu yüzden vana, bir kişi kontrol edip OPEN gönderene kadar kilitli kalır ve bu da ancak her sensör kuru bildirdiğinde işe yarar.
Kaçak sensörlerini pille çalıştırabilir miyim?
Evet. Periyodik uyandırmalı deep sleep kullanın veya ext0 aracılığıyla prob üzerinden uyandırın ve yalnızca durum değiştiğinde bağlanın. Analog probun suyu algılamak için beslenmesi gerektiğinden devrede ince ayar yapmayı bekleyin.
Kaçak alarmları için neden retained mesajlar kullanmalı?
Retained bir wet durumu, abone olduğu anda her client'a iletilir; böylece bir kaçak sırasında yeniden başlayan bir vana kontrolcüsü veya uygulama bundan hemen haberdar olur.
Prob korozyona uğrar mı?
Elektrotlarda sürekli DC tutmak korozyonu hızlandırır. Sketch'te olduğu gibi probu her örnek için yalnızca birkaç milisaniye beslemek ve paslanmaz çelik elektrotlar kullanmak, uzun süre çalışmasını sağlar.



