Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.
Water Leak Detector: the finished build
Pico WHome automation

Water leak detector with phone alert (Pico W, MicroPython)

Buzzer plus a phone alert when water reaches the floor under the RO or washing machine.

Difficulty
Easy
Build time
1 h
Parts cost
₹875 · $10
Verified — micropython (mpy-cross v1.29.0), 3 Nov 2026Compile-only

Community draft — report a mistake

Parts list

PartQtyApprox.Buy
Raspberry Pi Pico W (MicroPython installed)1₹620Buy Raspberry Pi Pico W (MicroPython installed)
Water level / rain-drop sensor strip (S, +, −)1₹40Buy Water level / rain-drop sensor strip (S, +, −)
Active buzzer module1₹25Buy Active buzzer module
3-core wire to the sensor (1–2 m)1₹40Buy 3-core wire to the sensor (1–2 m)
5 V USB adapter + micro-USB cable1₹150Buy 5 V USB adapter + micro-USB cable
Total₹875

Affiliate disclosure: “Buy” links may earn FlashReady a small commission at no extra cost to you. Prices are indicative.

Wiring

Raspberry Pi Pico WOnly the pins used are shown · 3.3 V logicWater level sensor (exposed-trace board)Active buzzer module (high-level trigger)GNDGP153V3GP26GP22S+-I/OVCCGND
Every connection
Raspberry Pi Pico WPart · pinWireNotes
GP22Water level sensor (exposed-trace board) · +redPowered from a GPIO only while reading — the traces corrode much slower
GP26Water level sensor (exposed-trace board) · SgreenADC0, physical pin 31
GNDWater level sensor (exposed-trace board) · -black
GP15Active buzzer module (high-level trigger) · I/OorangePhysical pin 20
3V3Active buzzer module (high-level trigger) · VCCred
GNDActive buzzer module (high-level trigger) · GNDblack

Board quirks: Lay the strip flat on the floor at the lowest point under the appliance, traces facing down onto the floor (or on 1–2 mm spacers) so a thin film of water bridges them. Fix it with tape at the edges, not over the traces. The sensor is powered from GP22 only for 10 ms every 2 s. Bare copper traces with constant DC on them corrode within weeks when wet; switching the supply makes them last much longer. Keep the Pico W and its USB adapter <strong>above</strong> where water can reach — on the wall, not on the floor next to the sensor. Only the 3-core wire goes down. Phone alerts need Wi-Fi at that spot and the ntfy app subscribed to your topic. The buzzer works without Wi-Fi.

Code

main.pyMicroPythonOpen in PlaygroundOpen in PlaygroundFlash
# Water leak detector - Raspberry Pi Pico W (MicroPython).
# Water-level sensor strip on the floor under the RO purifier / washing machine / AC drain:
#   + -> GP22 (powered only while reading, so the traces corrode far slower), S -> GP26 (ADC0), - -> GND.
# Buzzer module: I/O -> GP15, VCC -> 3V3, GND -> GND.
# Wet: the buzzer sounds and a high-priority ntfy alert goes to your phone (repeated every 15 min).
from machine import ADC, Pin
import time
import network
import json

WET_ABOVE = 12000          # raw ADC (0-65535); dry strip reads near 0  <- check with your sensor
CHECK_EVERY_S = 2
REPEAT_ALERT_S = 15 * 60

WIFI_SSID = "your-wifi"
WIFI_PASS = "your-password"
NTFY_TOPIC = "your-private-topic"  # https://ntfy.sh/<topic> - pick something unguessable

# The FlashReady flasher saves your settings to /settings.json; they win over the lines above.
try:
    with open("/settings.json") as f:
        _s = json.load(f)
except (OSError, ValueError):
    _s = {}
WIFI_SSID = _s.get("wifi_ssid") or WIFI_SSID
WIFI_PASS = _s.get("wifi_pass") or WIFI_PASS
NTFY_TOPIC = _s.get("ntfy_topic") or NTFY_TOPIC
ALERTS = WIFI_SSID != "your-wifi" and NTFY_TOPIC != "your-private-topic"
if not ALERTS:
    print("Phone alerts off - no Wi-Fi name or ntfy topic set. Flash from flashready.in or type them into main.py.")

power = Pin(22, Pin.OUT, value=0)
sensor = ADC(26)
buzzer = Pin(15, Pin.OUT, value=0)
led = Pin("LED", Pin.OUT)
wlan = network.WLAN(network.STA_IF)


def wifi_up():
    if not ALERTS:
        return False
    if not wlan.isconnected():
        wlan.active(True)
        wlan.connect(WIFI_SSID, WIFI_PASS)
        for _ in range(20):
            if wlan.isconnected():
                break
            time.sleep(0.5)
    return wlan.isconnected()


def notify(message, priority="default"):
    print(message)
    if not wifi_up():
        return
    try:
        import requests
    except ImportError:
        import urequests as requests
    try:
        requests.post("https://ntfy.sh/" + NTFY_TOPIC, data=message,
                      headers={"Title": "Water leak", "Priority": priority, "Tags": "droplet"}).close()
    except OSError as e:
        print("ntfy failed:", e)


def read_wet():
    power.on()
    time.sleep_ms(10)  # let the strip settle
    raw = sum(sensor.read_u16() for _ in range(4)) // 4
    power.off()
    return raw


wet = False
wet_count = 0
last_alert = 0
notify("Leak detector started", "low")

while True:
    raw = read_wet()
    # Two wet readings in a row before alarming (a single splash or noise is ignored).
    wet_count = wet_count + 1 if raw > WET_ABOVE else 0
    now_wet = wet_count >= 2
    if now_wet and not wet:
        wet = True
        last_alert = time.time()
        notify("WATER LEAK under the RO / machine! (sensor {})".format(raw), "high")
    elif now_wet and time.time() - last_alert > REPEAT_ALERT_S:
        last_alert = time.time()
        notify("Still wet - water leak not fixed yet", "high")
    elif not now_wet and wet and raw < WET_ABOVE // 2:
        wet = False
        notify("Dry again", "low")
    print("raw={} wet={}".format(raw, int(wet)))
    led.value(1 if wet else 0)
    if wet:
        # Pulsed alarm for the 2 s between readings: 0.25 s on, 0.25 s off.
        for _ in range(CHECK_EVERY_S * 2):
            buzzer.on()
            time.sleep(0.25)
            buzzer.off()
            time.sleep(0.25)
    else:
        time.sleep(CHECK_EVERY_S)

Compiled for picow on micropython (mpy-cross v1.29.0) · 3 Nov 2026

How it works

RO purifiers, washing machines, geysers in a loft, and split-AC drain pipes all leak eventually — usually at night or while the family is away. A slow drip under a kitchen cabinet swells the plywood and seeps to the flat below before anyone notices. This detector sits on the floor and shouts the moment the floor gets wet.

How it works

The water-level sensor is a set of interleaved copper traces. Dry, the traces are open and the output S reads near 0. Water bridges the traces, current flows from + through the water and a resistor on the board, and S rises.

Every 2 seconds the Pico W switches GP22 on (which powers the strip), waits 10 ms, takes four ADC readings on GP26, and switches it off again. Two wet readings in a row (so a single splash or electrical noise doesn’t count) start the alarm:

  • the buzzer pulses on GP15,
  • the on-board LED lights,
  • an ntfy.sh alert with high priority goes to your phone (it can override “do not disturb” in the ntfy app),
  • while it stays wet, the alert repeats every 15 minutes.

When the strip reads clearly dry again (below half the threshold), it sends “Dry again” and stops beeping.

Why these parts

  • A ₹40 sensor strip — it only needs to tell wet from dry, and it is cheap enough to replace when it corrodes.
  • Pico W with MicroPython — Wi-Fi alerts in a short script you can read and change in Thonny.
  • GPIO-powered sensor — the simplest fix for the strip’s biggest weakness, electrolysis.

Setting it up

Install MicroPython on the Pico W (the UF2 from micropython.org), then flash this project from the page: the flasher writes main.py and your Wi-Fi name, password and ntfy topic. Subscribe to the same topic in the ntfy app and pour a spoon of water on the strip to test.

India notes

Many flats in Indian cities store water in lofts and run RO purifiers 24×7, with an inlet that is under mains water pressure. A high-priority alert at 3 a.m. is better than a ceiling stain in the flat below. In the monsoon, the same detector works on a balcony drain or a basement floor.

Troubleshooting

It alarms while the floor is dry

Watch raw= in the REPL/Thonny console. A dry strip should read under a few thousand; humid monsoon air or a dusty strip reads higher. Clean the strip and raise WET_ABOVE above the dry reading.

It doesn’t notice water

Put a few drops across the traces: raw should jump well above WET_ABOVE. If not, check + goes to GP22 and S to GP26 (physical pin 31), not the other way round.

No phone alert

The console prints “Phone alerts off” when the Wi-Fi name or topic is missing. Otherwise look for “ntfy failed” — the Pico W can’t reach the internet from that spot.

Upgrades

  • Close a 12 V solenoid valve on the RO inlet through a relay when a leak is detected.
  • Use several strips (one per appliance) on GP26, GP27 and GP28 and name the place in the alert.
  • Send a weekly “still alive” message so you know the detector is working.

↑↓ to move · Enter to open