In the first part of this series, I explained the Microcontroller hardware system choice which is the ESP32 provides the best cost and features combo. The programming language for this project will be MicroPython, for this to run on the ESP32 we will need to install the MicroPython bootloader and use an appropriate IDE, both explained in the first part as well.
Types of water leak sensors
In this second part we will add the water leak sensor and start playing with code to connect our ESP32 to the sensor and report its status on a terminal screen (locally on the ESP32).
Binary sensor![]() | Multi-level sensor![]() |
| Acts as 2 poles, if water is present it closes the circuit between the poles and the Microcontroller will read a the high voltage from a simple pull-up circuit | Can detect depth of water along the sensor length, provides different reading (resistance) on ESP-32 ADC according to level of water creating a different resistance level |
| Pro: simple and can be screwed in baseboard or drywall with one screw. Can keep the ESP-32 far from the potential wet area. Con: is really 2 wires connected to 2 metal prongs, that’s it. | Pro: allows more granular water level detection Con: integrating it in a real use case might be challenging, might be more suitable as a soil moisture detector for plants, but not in a washroom leak |
| Supporting circuit: requires a pullup circuit which is basically a pullup to Vcc (power supply 3.3V) when the water is present | Supporting circuit: nothing connects to ADC pin on ESP-32 to readout the voltage/resistance |
| suitable for water leak sensor application in a home | suitable for water detection where the level of water up to 8 cm depth is relevant |
Based on the application, the binary sensor provides adequate capability to sense the presence of water or not, a simple binary Wet/Dry output is enough. The binary sensor is basically a simple 2 wires that when water is present, it closes the circuit between the 2 leads and when there is no water there is an open circuit.
The advantage of this format for the binary sensor is that it can be installed at ground level with a screw to the wall or baseboard through the center or using double faced tape and the ESP module can be mounted higher up from the ground and the potential moisture.
Connecting the sensor to the ESP32
To drive the input pin of the ESP32 to recognize this change we will use a simple pull up circuit sketched below:

- Pin 13 of the ESP32 (GPIO) is used to connect to one lead from the sensor and also connects via a resistor to the GND.
- The other sensor lead is connected to VCC (3.3 Volts).
- When the sensor is dry, the circuit is open and the ADC should see 0V on pin 13
- Once the sensor is wet (both terminals in water) it will close the circuit and cause the ESP32 pin 13 to detect VCC instead of GND signaling a change.

First version of the code
in your Micropythin IDE, I used uPyCraft, you can save the following code in a main.py file
from machine import Pin, ADC
import time
POWER_PIN = 17 # The ESP32 pin GPIO17 connected to the VCC pin of water sensor
SIGNAL_PIN = 13 # The ESP32 pin GPIO36 (ADC0) connected to the S pin of water sensor
THRESHOLD = 100 # Threshold of water detection, needs calibration with the ADC sensor values
# Setup power pin
power = Pin(POWER_PIN, Pin.OUT)
#power.value(0) # Initially turn off the sensor
# Setup ADC for reading the water sensor
signal = ADC(Pin(SIGNAL_PIN))
# Set the ADC width (resolution) to 12 bits
signal.width(ADC.WIDTH_12BIT)
# Set the attenuation to 11 dB, allowing input range up to ~3.3V
signal.atten(ADC.ATTN_11DB)
while True:
#power.value(1) # Turn on power to the sensor
time.sleep_ms(10) # Short delay to allow sensor to stabilize
value = signal.read() # Read the analog value from sensor
#power.value(0) # Turn off power to the sensor
if value > 500: # Check if the water level exceeds the threshold
print("Water detected")
else:
print("All good, no leaks")
time.sleep(3) # Delay for a second before the next reading
The code is pretty simple and straight forward, a simple endless loop turns on the power on pin 17 and reads the value on pin 13, if there is a leak the circuit will be closed and we will read a high value on pin 13, if there is no leak the circuit is open and you will be read a low value on pin 13, the threshold used for this value is 500, knowing that the ADC pin has a range from 0 to 1400.
Testing the first version of the code
Once the code is downloaded to the ESP32, the terminal will start printing the status of the sensor, we will test by dunking the sensor in a small beaker and removing it and we can see the sensor is responsive
![]() | ![]() |

Vibe coding your way around
You can definitely get a similar result using Gen AI tools like Claude.ai, ChatGPT etc. , here is a simple prompt:
you are an experienced embedded systems developer with experienc working with ESP32-WROOM using Micropython. Your task is to write python code for an ESP32 connected to a simple binary water sensor with 2 leads. The first lead is connected to pin 17 of the ESP32 and the second lead connected to the pin 13 of the ESP32 which is also pulled down with a resistor to the ground voltage.
The code should implement a simple timer loop to measure the signal on the pin 13, if the value is high signifying a short due to water leak is established the ESP32 should print a message on the terminal "Water Detected", if the value is low signifying the leads are dry it should print "All good, no leaks"
This is the code generated from that prompt with Claude.AI:
from machine import Pin
import time
# Pin configuration
POWER_PIN = 17 # Output pin to provide voltage to sensor
SENSOR_PIN = 13 # Input pin to read sensor state (with pull-down resistor)
# Initialize pins
power_pin = Pin(POWER_PIN, Pin.OUT)
sensor_pin = Pin(SENSOR_PIN, Pin.IN, Pin.PULL_DOWN)
# Set power pin HIGH to provide voltage to sensor circuit
power_pin.value(1)
# Configuration
SAMPLE_INTERVAL = 1.0 # Check sensor every 1 second
DEBOUNCE_COUNT = 3 # Number of consistent readings needed to confirm state change
STARTUP_DELAY = 2 # Wait 2 seconds after boot before starting
print("ESP32 Water Leak Detection System")
print("=====================================")
print(f"Power Pin: {POWER_PIN}")
print(f"Sensor Pin: {SENSOR_PIN}")
print(f"Sample Interval: {SAMPLE_INTERVAL}s")
print(f"Starting in {STARTUP_DELAY} seconds...")
print()
# Wait for system to stabilize
time.sleep(STARTUP_DELAY)
# State tracking variables
last_state = None
current_reading = None
consecutive_readings = 0
try:
while True:
# Read sensor state
current_reading = sensor_pin.value()
# Check if state has changed or is consistent
if current_reading == last_state:
consecutive_readings += 1
else:
consecutive_readings = 1
# Only act on stable readings (debouncing)
if consecutive_readings >= DEBOUNCE_COUNT:
if current_reading == 1: # Water detected
print("⚠️ Water Detected")
else: # No water detected
print("✅ All good, no leaks")
# Reset counter after reporting
consecutive_readings = 0
# Update last state
last_state = current_reading
# Wait before next reading
time.sleep(SAMPLE_INTERVAL)
except KeyboardInterrupt:
print("\nSystem stopped by user")
except Exception as e:
print(f"Error occurred: {e}")
finally:
# Cleanup: turn off power pin
power_pin.value(0)
print("Cleanup complete - power pin turned OFF")
I think Vibe coding can be very powerful for quick prototyping as long as you know what you’re doing and can get out of small issues yourself quickly.
All that is good, we now have an ESP32 that can read the status of a water sensor and report it on a terminal, this is a good start but we are not yet in a “connected home” domain, we will see in the part 3 how to make this setup connected and able to report its status over Wifi.





Leave a comment