Documentation

Rain and Water Level Detection Sensor Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual
Documentation / Rain and Water Level Detection Sensor Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

Rain and Water Level Detection Sensor Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

shillehtek

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

Output
Analog — rises with depth
Operating Voltage
2 - 5V DC
Current
< 20 mA
Sensing Area
~40 x 16 mm trace zone
Best Practice
Power only while reading
Pins
S, + (VCC), - (GND)

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.

Water level sensor module pinout diagram showing S analog output, VCC 2-5V, and GND pins with sensing traces

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
Tip: Dip the probe to a few known depths and note the raw values — those numbers become your "low" and "high" thresholds. Readings are not linear in centimeters, but they are wonderfully repeatable for the same depth.

ESP32 Wiring

Sensor Pin ESP32 Pin Details
S GPIO 34 ADC1 channel, input-only pin
+ GPIO 25 GPIO power — on only while reading
- GND
Note: Powered from a 3.3V GPIO, the sensor's output tops out around 3.3V — inherently safe for the ESP32 ADC with no divider. Keep S on an ADC1 pin (GPIO 32-39) so readings keep working while Wi-Fi is on.

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_arduino.ino
// 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_esp32.py
# 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)

water_level_rpi.py
#!/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_pico.py
# 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)

Frequently Asked Questions

Why do all the examples power the sensor from a GPIO pin?
Because a constantly-energized probe sitting in water corrodes fast — DC current drives electrolysis that eats the copper traces, sometimes within weeks. Powering the sensor only for the ~20 ms of each reading reduces the energized time by thousands of times and is the single biggest thing you can do for its lifespan. The sensor draws well under GPIO current limits, so the trick is completely safe.
Can it tell me the water level in centimeters?
Not directly — the response is monotonic but not linear, and it varies with water conductivity. Treat it as a threshold and trend sensor: calibrate raw values at the depths you care about and compare against them. For true continuous level measurement in a tank, mount an HC-SR04 ultrasonic sensor above the water and measure the distance to the surface instead.
Is the sensor waterproof?
Only the striped sensing zone is meant to get wet. The header, transistor, and LED at the top must stay dry — submerging them kills the board. Mount vertically, pins up, with your maximum expected level safely below the component area, and add a drip loop in the wiring so water cannot run down the cables into the connector.
Why did my readings slowly drop over weeks in the same water?
Corrosion or residue on the traces. If the surface has darkened, clean it gently with isopropyl alcohol and a soft eraser to restore conductivity, and adopt the GPIO-power pattern if you were not using it. Mineral films from hard water also insulate the traces over time — periodic cleaning keeps thresholds honest.
Does the type of water matter?
Yes — the sensor measures conductivity, so mineral-rich tap water reads noticeably higher than the same depth of distilled or rainwater, and salty water higher still. Calibrate your thresholds with the same water the project will actually see, and expect distilled water to read low even when deep.
Can I leave it permanently submerged as a tank sensor?
It is not designed for continuous immersion — even unpowered, long soaks accelerate wear. For always-wet duty, better tools are a float switch (mechanical, decades of life), a non-contact capacitive sensor through the tank wall, or the ultrasonic approach from above. Use this module for rain detection, spill alarms, and periodic level checks where it spends most of its life dry or briefly dipped.
Can it detect rain?
Nicely — lay it at a slight angle outdoors and falling drops bridging the traces produce clear readings; a dry spell lets it drain and return to zero. For rain duty, the GPIO-power pattern matters even more, since the board may stay damp for hours after a storm.

Related Tutorials