Overview
This water level sensor turns "is there water, and roughly how much?" into a single analog reading. The red probe carries ten interleaved copper traces — five power lines woven between five sense lines. Dry, nothing connects them and the S pin reads near zero. Dip the probe and water bridges the traces; the deeper the immersion, the more trace pairs conduct, and the higher the analog voltage on S. An onboard transistor buffers the signal, and a power LED shows when the board is energized.
It runs from 2-5V and draws under 20 mA, so it works on every hobby board: Arduino reads S directly, the ESP32 and Pico read it on their ADCs (power the board from 3.3V and the output stays ADC-safe automatically), and the Raspberry Pi reads it through an ADS1115. The output is not a calibrated ruler — it is a monotonic "more water, bigger number" signal, ideal for threshold logic: rain has started, the tank has reached the fill line, the drip tray is about to overflow.
One habit dramatically extends this sensor's life: do not leave it powered while sitting in water. Constant current through wet traces causes electrolytic corrosion that slowly eats the copper. The fix is simple and built into every code example below — power the sensor from a GPIO pin, switch it on for a few milliseconds per reading, then switch it off. With that pattern, a probe that would corrode in weeks lasts for years of periodic checks.
At a Glance
Specifications
| Parameter | Value |
| Sensing Principle | Water conductivity across interleaved copper traces |
| Trace Layout | 10 traces: 5 power + 5 sense, interleaved |
| Output | Analog voltage on S, increases with immersion depth |
| Operating Voltage | 2V - 5V DC |
| Operating Current | < 20 mA |
| Sensing Area | ~40 x 16 mm |
| Board Size | ~65 x 20 mm |
| Onboard Components | Buffer transistor + power indicator LED |
| Recommended Water Temp | 10°C - 30°C |
| Waterproofing | Trace zone only — keep the pin/component end dry |
| Longevity Practice | Energize only during readings to prevent electrolytic corrosion |
Pinout Diagram
Three pins on the header: S is the analog signal out, + is VCC (2-5V), and - is ground. Only the striped trace area is meant to touch water — mount the board vertically with the header end up, and set your "max depth" so water never reaches the components. Because the output scales with the supply, powering from 3.3V on 3.3V boards keeps S within ADC range with no divider.
Wiring Guide
All four platforms use the same anti-corrosion trick: the sensor's + pin connects to a spare GPIO instead of a power rail, so code can energize the probe only for the moment of each reading. The sensor draws well under a GPIO's current limit, making this completely safe.
Arduino Wiring
| Sensor Pin | Arduino Pin | Details |
|---|---|---|
| S | A0 | Analog input |
| + | D7 | GPIO power — on only while reading |
| - | GND |
ESP32 Wiring
| Sensor Pin | ESP32 Pin | Details |
|---|---|---|
| S | GPIO 34 | ADC1 channel, input-only pin |
| + | GPIO 25 | GPIO power — on only while reading |
| - | GND |
Raspberry Pi Wiring
The Pi has no analog inputs, so an ADS1115 reads S while a GPIO supplies power during readings.
| Wire / Pin | Connects To | Details |
|---|---|---|
| Sensor S | ADS1115 A0 | Analog channel 0 |
| Sensor + | Pin 11 (GPIO 17) | GPIO power — on only while reading |
| Sensor - | Pin 6 (GND) | |
| ADS1115 VDD / GND | Pin 1 / Pin 6 | 3.3V rail |
| ADS1115 SDA / SCL | Pin 3 / Pin 5 | I2C (GPIO 2 / GPIO 3) |
Raspberry Pi Pico Wiring
| Sensor Pin | Pico Pin | Details |
|---|---|---|
| S | GP26 (pin 31) | ADC0 input |
| + | GP16 (pin 21) | GPIO power — on only while reading |
| - | GND (pin 38) |
Code Examples
Every example uses the corrosion-saving pattern: power the probe, wait a moment to settle, read, power down, repeat every few seconds. Tune the two thresholds to your own container using the raw values you observe.
Arduino
// Water Level Sensor - Arduino Example (GPIO-powered to prevent corrosion)
// S -> A0, + -> D7, - -> GND
const int powerPin = 7;
const int sensorPin = A0;
void setup() {
Serial.begin(9600);
pinMode(powerPin, OUTPUT);
digitalWrite(powerPin, LOW); // sensor off between readings
}
int readLevel() {
digitalWrite(powerPin, HIGH); // energize the probe
delay(20); // let the reading settle
int value = analogRead(sensorPin);
digitalWrite(powerPin, LOW); // power off - no electrolysis
return value;
}
void loop() {
int level = readLevel(); // 0-1023
Serial.print("Raw: ");
Serial.print(level);
if (level < 100) {
Serial.println(" (dry)");
} else if (level < 450) {
Serial.println(" (low water)");
} else {
Serial.println(" (HIGH water!)");
// trigger your pump/alarm/notification here
}
delay(3000); // check every 3 seconds
}
ESP32 (MicroPython)
# Water Level Sensor - ESP32 MicroPython Example (GPIO-powered)
# S -> GPIO 34, + -> GPIO 25, - -> GND
from machine import ADC, Pin
import time
power = Pin(25, Pin.OUT, value=0) # sensor off between readings
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB) # full 0-3.3V range
def read_level():
power.value(1) # energize the probe
time.sleep_ms(20) # settle
value = adc.read() # 0-4095
power.value(0) # off - no electrolysis
return value
while True:
level = read_level()
if level < 400:
state = "dry"
elif level < 1800:
state = "low water"
else:
state = "HIGH water!"
print("Raw: {} ({})".format(level, state))
time.sleep(3)
Raspberry Pi (Python + ADS1115)
#!/usr/bin/env python3
# Water Level Sensor - Raspberry Pi + ADS1115 Example (GPIO-powered)
# S -> ADS1115 A0, + -> GPIO 17 (pin 11), - -> GND
# Install: pip3 install adafruit-circuitpython-ads1x15
import time
import board
import busio
import RPi.GPIO as GPIO
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
POWER_PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(POWER_PIN, GPIO.OUT, initial=GPIO.LOW)
i2c = busio.I2C(board.SCL, board.SDA)
ads = ADS.ADS1115(i2c)
ads.gain = 1
channel = AnalogIn(ads, ADS.P0)
def read_level():
GPIO.output(POWER_PIN, GPIO.HIGH) # energize the probe
time.sleep(0.02) # settle
volts = channel.voltage
GPIO.output(POWER_PIN, GPIO.LOW) # off - no electrolysis
return volts
try:
while True:
volts = read_level()
if volts < 0.3:
state = "dry"
elif volts < 1.5:
state = "low water"
else:
state = "HIGH water!"
print("Signal: {:.2f} V ({})".format(volts, state))
time.sleep(3)
except KeyboardInterrupt:
print("Stopped by user")
finally:
GPIO.cleanup()
Raspberry Pi Pico (MicroPython)
# Water Level Sensor - Pico MicroPython Example (GPIO-powered)
# S -> GP26 (ADC0), + -> GP16, - -> GND
from machine import ADC, Pin
import time
power = Pin(16, Pin.OUT, value=0) # sensor off between readings
adc = ADC(26)
def read_level():
power.value(1) # energize the probe
time.sleep_ms(20) # settle
value = adc.read_u16() # 0-65535
power.value(0) # off - no electrolysis
return value
while True:
level = read_level()
if level < 6000:
state = "dry"
elif level < 28000:
state = "low water"
else:
state = "HIGH water!"
print("Raw: {} ({})".format(level, state))
time.sleep(3)