Project tutorial
Arduino ESP32 DIY Water Level Sensor and DIY Level Indicator

Arduino ESP32 DIY Water Level Sensor and DIY Level Indicator © GPL3+

A self-made (cheap) water level sensor and water level indicator to measure 5 water levels in a reservoir tank.

  • 4 respects

Components and supplies

Water Level Sensor
DIY, for details see project description
Water Level Indicator
DIY, for details see project description
NodeMCU ESP 32
A000066 iso both
Arduino UNO & Genuino UNO
As an alternative to the ESP 32
Resistor 680 k Ohm
Capacitor 1 nF
Capacitor 220 nF

Apps and online services

About this project

Water reservoir tanks are used for many purposes, e.g. in a Caravan or Camper or in Plant Watering installations, etc. Often there is no direct visibility of the available amount of water in the tank. There are various ways to measure water levels in reservoir tanks, you can buy them at significant prices or make one yourself!

As opposed to: Bring your Own Device -> MAKE YOUR OWN DEVICE!

I decided to make a Water Level Sensor and also a Water Level Indicator with 5 levels.

Here you can watch the result:

Sensor going down and up, the indicator shows the level (speed 3*)

Step 1: Making the Water Level Sensor

The DIY Sensor is made with simple materials:

  • 6 pieces of electricity wire (1, 5 mm copper with black PVC cladding) stripped approx 1 cm at both ends
  • a piece of electricity pipe 3/4 inch with a length that matches the depth of your reservoir
  • 1 PVC 3/4 inch pipe extension
  • 6 * 680KOhm resistors
  • a 1 nF capacitor (for noise suppression)
  • a cheap audio cable
  • a piece of double sided prototype PCB board

Note that drilling some 5mm holes in the pipe is essential.

The principle of operation of the Water Level Sensor is based on the conductive properties of the water. The higher the salinity, the higher the conductivity, equivalent to lower resistance. The average conductivity value of the tap water supplied in my area is approx 35 mS/m. The more copper probes are in the water, the lower the resistance of the ladder network is. This lower resistance value results in a higher input voltage to the 12 bit ADC at the GPIO pin of the ESP32. With this set up it is possible to measure 5 different waterlevels.

The electronic scheme of the sensor is as follows:

All parts are assembled and soldered together:

The assembly is put in the 3/4 inch pipe extension and filled with transparant silicone to make it waterproof.

The cable used for connecting the sensor to the Arduino is a cheap audio cable from the local ACTION store

The fully assembled Water Level Sensor seen below:

Step 2: Making the Water Level Indicator

The components used for the indicator are:

  • a piece of dark grey cutting board (from ACTION) with holes drilled as can be seen in the next picture and using a file to make the holes square
  • a piece of LED strip consisting of 5 WS2812 LEDs (cut from a longer strip)
  • photo paper with the 5 levels printed (1%, 25%, 50%, 75%, 100%)
  • a piece of transparant foil for protection of the print
  • Aluminium foil as a cover (not very easy to handle, but this is what I used)

The final result of the assembly work can be seen below:

Step 4: The Electronic set up

The breadboard set up is shown in the following picture:

In this set up I used an ESP 32s (Node MCU). The reason for this choice is the fact that I plan to expand this set up with functionality for making a complete plant watering system with on-line monitoring and warnings. (the ESP 32 has WiFi and Bluetooth on board).

The circuit diagram (made with Fritzing) is as follows:

The ESP 32 is powered with 5 V from the USB port and has a 3, 3V regulator on board. One output GPIO pin is directly connected to the Water Level Sensor and sends a 3, 3 V signal to the sensor with a duration of just 200 milliseconds. This is to avoid electrolysis effects in the water and consequently corroding of the copper ends.

The output from the Sensor is connected to two 1 M Ohm resistors that form a "virtual Wheatstone bridge" with the Sensor and a comparator array in the software.

I used this set up, because the behaviour of ladder network in the water as a function of the waterlevel is non-linear (see below diagram):

Theoretically, when the sensor is not in the water the measured value at the sensor pin is approx 1,0855 V which would be equivalent to 1347 bits. The deviation with the graph can be explained with tolerances of the resistors, the resistance in the sensor wire, deviations in the 3,3 Volt supply of the ESP 32 and some non-linearities in the 12 Bits ADC of the ESP32 as well as some noise in the circuit.

The 5V power for the 5 LEDs used in the Level Indicator is derived directly from the 5V pin (connected to the 5V input from the USB port);

During my experiments, it turned out that using a 5V telephone charger gave different results than the 5V USB port of the PC or from a 5V power bank. The power bank, although providing stable results, switches off (sleeping mode), however, after a short while, because of the low current drawn by the system.

Step 3: Making the Software

The loop function in the Arduino Sketch is simple and consists only of the calling 2 functions:

void loop(){
 delay(1000);                            // Check for new value every 1 sec;

The 1 second measurements are just for experimentation and demonstration purposes. In practical applications this will be far less frequent (depending on how quickly the water in the reservoir is used)

The plot monitor of the ARDUINO IDE as well as the serial monitor have been very useful during experiments. A view of the plot monitor graph is given below.

The Graph shows the (1 second) measurements of the sensor going down and up into the water (green line); the red line represents the reference values as set in the LEVELarray []. These values have been determined by experimentation and clearly show the non-linear behaviour of the sensor.

//                      0    1    2    3    4    5
int LEVELarray [6] = {1125,1245,1450,1720,2080,2630} ; 

The "0" level is applicable when there is no water in the reservoir, the "5" level is applicable when the reservoir is full.

The non-linear behaviour of the sensor could be corrected by making a ladder network with non-uniform distributed resistor values.

I concluded that it is much easier to deal with this in the software.

Also the natural jitter that exists on the measured sensor values can easily be eliminated in the software by a allowing a tolerance on the measured values of e.g. 4%, (also note the 220nF capacitor in parallel with the two 1 M Ohm resistors)

for (int i = 0; i < 6 ; i++)
if ((WaterLevelValue > (LEVELarray[i] * 0.96)) && (WaterLevelValue < (LEVELarray[i] * 1.04))) 
// allow a margin of 4% on the measured values to eliminate jitter and noise
     level = i;

Finally the level as determined above, is used to display the water level on the Level Indicator with a color, which is very easy thanks to the <Adafruit_NeoPixel.h> library:

redVal = color_scheme[level*3];   
greenVal = color_scheme[level*3 + 1];
blueVal = color_scheme[level*3 + 2];
strip.setPixelColor(level-1, strip.Color(redVal, greenVal, blueVal) );;

For downloading the sketch through the USB port onto the ESP 32, it is required to hold down the Boot button.


When using a regular ARDUINO (e.g. UNO) instead of the ESP 32, a number of changes need to be made in the code such as:

  • the pin allocations
  • The values in the LEVELarray, taking into account that the UNO has a 10 bit ADC (on pin A0); first best guess is dividing the values by 4
  • The 5V trigger pulse to the Sensor (instead of 3, 3V) should not have an impact

You may discover an extra 680 kOhm resistor in the practical set up on the breadboard compared to the Fritzing diagram. This is because initially, I wanted to have the "0" sensor wire be disconnected from the ladder network (for experimentation reasons), so I used also a 3 wire sensor cable, whereas in the final design only 2 are needed.

I am interested to hear if someone is actually going to reproduce this project.

Developed and produced by Pierre Pennings (December 2018).


ARDUINO Sketch for DIY Water Level Sensor and DIY Water Level Indicator with 5 levels running on a ESP 32 or ARDUINO UNO or similar (with small modifications)
  This code for a WaterLevel sensor and a WaterLevel Indicator has been developed and produced by Pierre Pennings (December 2018)
  This application can be used for various situations where information about the level of water in a reservoir tank is required
  e.g. in automatic plant watering systems or in caravans or campers where there is no direct visibility on the water reserve in the tank.  
  The DIY WaterLevelSensor uses 6 pieces of copper electricity wire connected to a ladder network of 680K Ohm resistors
  The DIY WaterLevel Indicator is made with 5 (Neopixel) SMD5050 LEDs with WS2812B controller chips powered with 5 V
  Every individual LED is adressed from one ARDUINO output pin and the control adress determined by the measured WaterLevel
  The WaterLevel is measured periodically (during only 200 mili seconds, to avoid corrosion due to electrolysis effects)
  The WaterLevel Indicator is set to the measured water level
  The measured WaterLevelValues are not distributed linearly but follow a second grade polynomial
  Reference values for the measurements are stored in an Array called LEVELarray[] consisting of 6 positions
  The actual measured WaterLevelValues are compared with the values in the Array and consquently the Water level is determined
  For this Project, which is part of a bigger plan, an ESP 32 (NodeMCU) is used with 12 Bits ADCs, however an normal ARDUINO UNO (or almost any other model) will do the job
  (of course the settings in the code will need to be adjusted, e.g. due to 10 Bits ADC and different Pin allocations
  The ESP 32 device works at 3.3 Volt levels, while the WaterLevel Indicator runs on 5 V
  The 5 Level LEDs have been built in a separate indicator Display (indicating 1%, 25%, 50%, 75% and 100% levels)
  The ESP 32 is fed with 5 V power (from a 5V adaptor or 5v powerbank), it has an on-board 3.3V voltage regulator
  The 5 indicator LEDs get the 5V supply directly from the 5 volt pin of the ESP 32 
  This code is licensed under GPL3+ license.

#include <Adafruit_NeoPixel.h>
#define NUM_LEDS  5

///////////////////////////////////////////////// initialise the GPIO pins
const int output27 = 27;                       // pin 27 sends a "0"  or "1" (0 -3.3 V) to the waterlevel measurement circuit
const int LevelSensorPin = 34;                 // 12 bits ADC pin 34 senses the voltage level of the waterlevel sensor (values 0 - 4095)
const int IndicatorPin = 16;                   // pin 16 sends the control data to the LED WaterLevel Indicator

int WaterLevelValue = 0;                        // Variable to store the value of the Waterlevel sensor
int level = 0;                                  // Variable of the WaterLevel

//                      0    1    2    3    4    5
int LEVELarray [6] = {1125,1245,1450,1720,2080,2630} ;    // Array with the level reference values to determine the waterlevel
                                                          // the "0" level is applicable when there is no water in the reservoir
                                                          // the "5" level is applicable when the reservoir is full 

byte color_scheme[] = {
  0, 0, 0,                  // no color/ off
  0, 200, 0,                // green
  100, 200, 0,              // yellow
  250, 150, 0,              // orange
  0, 0, 200,                // blue
  200, 0, 0                 // red

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, IndicatorPin, NEO_RGB + NEO_KHZ800);

/////////////////////////////////////////////////// the setup code that follows, will run once after "Power On" or after a RESET
void setup() {
  pinMode(output27, OUTPUT);                    // Initializes the power output pin (3.3 V) for the WaterLevel Sensor circuit
  digitalWrite(output27, LOW);                  // Set output27 to LOW; this will send 0 V to the measuring circuit

  pinMode(LevelSensorPin, INPUT);               // Initializes the water level sensorpin
  pinMode(IndicatorPin, OUTPUT);                // Initializes the output pin for the WaterLevel Indicator

  strip.begin();                                 // Initialize all LEDs to "off"
  for (int t = 0; t < 5 ; t++)
    strip.setPixelColor(t, 100, 100, 100);       // After Power On the WaterLevel Indicator LEDs are tested once;                                // note that the order of colors of the WS2812 LED strip is R,G,B 
    delay (200);
    strip.setPixelColor(t, 0, 0, 0);             // And back to off
  for (int k = 4; k > -1 ; k--)
    strip.setPixelColor(k, 100, 100, 100);       // blink for 0,2 seconds going top down and then off;
    delay (200);
    strip.setPixelColor(k, 0, 0, 0);             

/////////////////////////////////////////////////// the loop code that follows, will run repeatedly until "Power Off" or a RESET
void loop(){

  delay(1000);                            // Check for new value every 1 sec;
                                          //this value is just for demonstration purposes and will in a practical application be far less frequent
//////////////////END of LOOP////////////////////////////////////////////////////////////  

/////////////////////////////////////////////////// Hereafter follows the Function for measuring the WaterLevel (called from within the loop)

  digitalWrite(output27, HIGH);           // make pin 27 HIGH
  delay(200);                             // allow the circuit to stabilize
  WaterLevelValue = analogRead(LevelSensorPin);  //Read data from analog pin and store it to WaterLevelvalue variable

   for (int i = 0; i < 6 ; i++)
      if ((WaterLevelValue > (LEVELarray[i] * 0.96)) && (WaterLevelValue < (LEVELarray[i] * 1.04)))              // allow a margin of 4% on the measured values to eliminate jitter and noise
      level = i;
   digitalWrite(output27, LOW);           // make pin 27 LOW
   Serial.print(" LEVELarray: "); Serial.print(level); Serial.print(" = "); Serial.print(LEVELarray[level]);Serial.print("   WaterLevelValue: "); Serial.print(WaterLevelValue); Serial.print("  Level: "); Serial.println(level);
   // uncomment this code for determining the values to be put in the LEVELarray [] using the serial plotter and/or serial monitor or the ARDUINO IDE

/////////////////////////////////////////////////// Hereafter follows the Function for Indicating the WaterLevel (called from within the loop)


for (int t = 0; t < 6 ; t++)
    strip.setPixelColor(t, 0, 0, 0);           // turn off all LEDs on the WaterLevel Indicator;                                  
int redVal, greenVal, blueVal;                // Set the WaterLevel Indicator LED with a color defined in the Array color_scheme
    redVal = color_scheme[level*3];
    greenVal = color_scheme[level*3 + 1];
    blueVal = color_scheme[level*3 + 2];
    strip.setPixelColor(level-1, strip.Color(redVal, greenVal, blueVal) );;

Custom parts and enclosures

template for Water Level Indicator
template for drilling holes and printing text for the Water Level Indicator


Water Level Sensor and Level Indicator
This diagram shows the set up of a system for measuring waterlevels with a DIY sensor and a display for level indication in combination with an ARDUINO


Similar projects you might like

DIY Plant Moisture Sensor

Project tutorial by millerman4487

  • 23 respects

Smoke Detection using MQ-2 Gas Sensor

by Aritro Mukherjee

  • 133 respects

DHT11 Humidity + Temperature Sensor with 16x2 LCD display

Project showcase by onatto22

  • 1 comment
  • 17 respects

Arduino Control AC Water Heater temperature

Project tutorial by Mohannad Rawashdeh

  • 6 respects

Arduino-Based Automatic Water Tap Using IR Sensor

Project tutorial by Creatjet3D R&D Team

  • 19 respects

Water Plant System for Plants in Vases

Project tutorial by zioalex

  • 10 respects
Add projectSign up / Login