Water Tank Level Indicator with Low Level Warning Notifications

very interesting project , could you please show how the sensor installed in the tanks , and if you have any pictures to share that will be great.


1 Like

Sorry for bumping up an old question but I’m in the same boat of requirement.

And to my surprise there are quite a few new sensors in town:

This one has 2 transducers instead of 1 therefore has less minimum detection distance as compared to JSN SR04T. It is also completely sealed and waterproof.


Before finalizing on this I had bought another one which was a waterproof modified version of HC-SR04 from here


The 1st one is much more rugged and waterproof as compared to the latter one.

1 Like

That’s great mate. Yeah that minimum distance of around 23cm for the JSN sensor is a little painful… Thanks for sharing. You have purchased and tested the waterproof ones? Performance results?

Yes I did purchase both of them and tried and tested both of them for a personal project of mine of a water tank level controller. I wanted the water to be of potable/drinking quality, therefore I could not use float switches, the 1st sensor was like a savior because of the JSN SR04T minimum distance problem.

Personally I would say the sensor in the 1st link (https://www.ebay.com/itm/253978673298) is far better than the sensor in the 2nd one in terms of ruggedness and exposure to humid/water environments. It does have minimum distance of around 3-4cm and its working fine since a couple of weeks. It even has 2 screw mounting holes on either sides which helps to connect it to the box which contains the micro-controller (using and ESP32 btw) and power supply and stuff.

1 Like

Hi Guys,

I#m struggeling with activation my JSN-SR04t with the Blynk code.

I see:

#define echoPin 14// Echo Pin
#define trigPin 12  // Trigger Pin

I think here i have to define my Ports in the Wemos D1 Mini. I have connected the trigger to D3 and the Echo to D4 on the board. What do i have to code here?

thanks for help

best Jens

If you want to use the original code, then this is how to connect your sensor to your D1 Mini:

#define echoPin 14 // Echo Pin      <--------- Pin D5 on Wemos D1 Mini
#define trigPin 12   // Trigger Pin <--------- Pin D6 on Wemos D1 Mini

If you want to use the connections you’ve specified, then change the code to this:

#define echoPin 2  // Echo Pin      <--------- Pin D4 on Wemos D1 Mini
#define trigPin 0  // Trigger Pin   <--------- Pin D3 on Wemos D1 Mini

However, using pin D3 (GPIO0) isn’t a good idea, and pin D4 (GPIO2) is connected to the builtin LED, which also isn’t a good idea.

See this post for more info…


Hi Pete,

Thanks for help but the sensor didn‘t send Feedback. I have left the code as it is. Wifi rsi etc works and uptime also. Do you have an answer why the Sensor won‘t work?

Not without more information from you.


Ok what you do you need?

//****Virtual Pin Assignments****

// V0 - Blynk Version
// V1 - ESP Version
// V2 - Millis Uptime
// V3 - Wifi RSSI
// V4 - Volume
// V8 - Terminal
// V9 - Percent (level V widget)
//V10 - Numeric Widget - Level Alert
//V11 - Button Widget to cancel alert or turn alert (on or off)
//V12 - Distance - height from sensor to water level

//#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

#include <ESP8266mDNS.h>  // For OTA w/ ESP8266
#include <WiFiUdp.h>  // For OTA
#include <ArduinoOTA.h>  // For OTA

char auth[] = "jN8UXHOZr-Df5P9I5YrwJrtg4uQM_i-F";

char ssid[] = "Hausxxxx";
char pass[] = "Hxxxxxxx";

BlynkTimer timer;

WidgetTerminal terminal(V8);

#define echoPin 14 // Echo Pin
#define trigPin 12  // Trigger Pin

#define filterSamples   15              // filterSamples should  be an odd number, no smaller than 3

int maximumRange = 9500;
int minimumRange = 20;
int alertLevelCounter = 0; //Counter starts at zero
int volume;
int volume1; // for smoothing algorithum
int percent;
int percent1; // for smoothing algorithum
int levelAlert;
int wateralert = 0;
int sensSmoothArray1 [filterSamples];   // array for holding raw sensor values for sensor1 
int smoothDistance;                     // variables for sensor1 data

double alertInMin = 0.5;                //Alert time following alert water level being reached (0.5 = 30secs)

const int alertDuration = 2;            //Alert sent every 2 minutes

const int waterTankDia = 253;           //Water Tank Diameter (cm)
const int waterTankHt = 195;            //ie. Sensor Height (cm)

long duration, distance, distance1;     // Duration used to calculate distance

BLYNK_CONNECTED() { // runs once at device startup, once connected to server.
void MeasureCm() {
 //  The following trigPin/echoPin cycle is used to determine the
 // distance of the nearest object by bouncing soundwaves off of it. 
 digitalWrite(trigPin, LOW); 

 digitalWrite(trigPin, HIGH);
 digitalWrite(trigPin, LOW);
 duration = pulseIn(echoPin, HIGH);
 //Calculate the distance (in cm) based on the speed of sound.
 distance = duration/58.2;
   if(distance >= 20 ){
 volume = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(distance*(PI*(sq(waterTankDia/2)))))/1000;
 percent = ((float)volume/9500)*100;
 if(distance < 20 ){
   volume = 9500;
   percent = 100;

  Blynk.virtualWrite(V9, percent); // virtual pin
  Blynk.virtualWrite(V12, distance); // virtual pin
  Blynk.virtualWrite(V4, volume); // virtual pin

void UpTime() {
  Blynk.virtualWrite(2, (millis() / 1000));
  Blynk.virtualWrite(3, WiFi.RSSI());

  levelAlert = param.asInt();
  terminal.print("Water Level Alert: ");

  int alertOff = param.asInt();

  if(alertOff == 0) {
wateralert = 1;
terminal.print("Level Alert is OFF");
  else {
wateralert = 0;
terminal.print("Level Alert is ON");


void checkWaterLevel() {
  if(volume1 < levelAlert && wateralert == 0){
 alertLevelCounter += 1;

  if(alertLevelCounter > alertInMin * 60 && wateralert == 0){
Blynk.notify("Water Tank Level Alert"); // send push notification to blynk app
wateralert = 1;

void resetWaterLevelAlert() {
  alertLevelCounter = 0;
  wateralert = 0;

void MeasureCmForSmoothing() {
  /* The following trigPin/echoPin cycle is used to determine the
 distance of the nearest object by bouncing soundwaves off of it. */ 

 digitalWrite(trigPin, LOW); 

 digitalWrite(trigPin, HIGH);
 digitalWrite(trigPin, LOW);
 duration = pulseIn(echoPin, HIGH);

 //Calculate the distance (in cm) based on the speed of sound.
 distance1 = duration/58.2; //for smoothing algorithim

 smoothDistance = digitalSmooth(distance1, sensSmoothArray1);

 if(distance1 >= 20 ) {
   volume1 = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(smoothDistance*(PI*(sq(waterTankDia/2)))))/1000;
   percent1 = ((float)volume1/9500)*100;
   terminal.print("Uptime (secs): ");
   terminal.println((millis() / 1000));
   terminal.print("Distance: ");
   terminal.print("Smoothed Distance: ");
   //terminal.print("Percent: ");
   // terminal.println(percent1);
if(distance1 < 20 ){
  volume1 = 9500;
  percent1 = 100;

void UploadMeasureCmForSmoothing() {
Blynk.virtualWrite(V12, smoothDistance); // virtual pin
Blynk.virtualWrite(V4, volume1); // virtual pin
Blynk.virtualWrite(V9, percent1); // virtual pin
terminal.print("Uploaded Volume: ");
terminal.print("Uploaded Smoothed Distance: ");
//terminal.println("   "); 
//terminal.print("average = ");
//terminal.print("Uploaded Percent: ");


void setup() {
 Blynk.begin(auth, ssid, pass);

 ArduinoOTA.setHostname("Water_Tank");  // For OTA - Use your own device identifying name
 ArduinoOTA.begin();  // For OTA

 Blynk.virtualWrite(0, BLYNK_VERSION);
 Blynk.virtualWrite(1, ESP.getCoreVersion());

 pinMode(trigPin, OUTPUT);
 pinMode(echoPin, INPUT);

 timer.setTimeout(100, [](){
 timer.setInterval(5000L, UpTime);

 timer.setTimeout(400, [](){
 timer.setInterval(300000L, UploadMeasureCmForSmoothing);  //get a measurement straight up if the device was reset for some reason

 timer.setTimeout(600, [](){
 timer.setTimer(1000L, MeasureCm, 1);
 timer.setTimeout(500, [](){
 timer.setInterval(1000L, checkWaterLevel);

 timer.setTimeout(700, [](){
 timer.setInterval(15000L, MeasureCmForSmoothing);

void loop() {
  Blynk.run(); // Initiates Blynk
  ArduinoOTA.handle();  // For OTA
  timer.run(); // Initiates SimpleTimer

int digitalSmooth(int rawIn, int *sensSmoothArray){     // "int *sensSmoothArray" passes an array to the function - the asterisk indicates the array name is a pointer
int j, k, temp, top, bottom;
long total;
static int i;
// static int raw[filterSamples];
static int sorted[filterSamples];
boolean done;

i = (i + 1) % filterSamples;    // increment counter and roll over if necc. -  % (modulo operator) rolls over variable
sensSmoothArray[i] = rawIn;                 // input new data into the oldest slot

// Serial.print("raw = ");

 for (j=0; j<filterSamples; j++){     // transfer data array into anther array for sorting and averaging
 sorted[j] = sensSmoothArray[j];

   done = 0;                // flag to know when we're done sorting              
 while(done != 1){        // simple swap sort, sorts numbers from lowest to highest
  done = 1;
    for(j = 0; j < (filterSamples - 1); j++){
       if(sorted[j] > sorted[j + 1]){     // numbers are out of order - swap
         temp = sorted[j + 1];
         sorted [j+1] =  sorted[j] ;
         sorted [j] = temp;
         done = 0;

  for (j = 0; j < (filterSamples); j++){    // print the array to debug
Serial.print("   "); 
   // throw out top and bottom 15% of samples - limit to throw out at least one from top and bottom
   //  bottom = max(((filterSamples * 15)  / 100), 1); 
   //  top = min((((filterSamples * 85) / 100) + 1  ), (filterSamples - 1));   // the + 1 is to make up for asymmetry caused by integer rounding
   bottom = max(((filterSamples * 20)  / 100), 1); 
   top = min((((filterSamples * 80) / 100) + 1  ), (filterSamples - 1));   // the + 1 is to make up for asymmetry caused by integer rounding
   k = 0;
   total = 0;
  for( j = bottom; j< top; j++){
 total += sorted[j];  // total remaining indices
 //Serial.print("   "); 
 //terminal.println("   ");
   //terminal.print("average: ");
   //Serial.print("average = ");
  return total / k;    // divide by number of samples

Can you edit your post to add triple backticks before and after your code please?

<<< code goes here >>>
1 Like

As @JustBertC said, your code needs to be correctly formatted with triple backticks at the beginning and end.
My guess is that it’s a wiring issue, either incorrect connections, or a dodgy connection on a breadboard.
Photos of your setup might help, but only you will be able to judge that.


Hi Pete,

i have correct that. I have had another Wemos D1 Mini Pro active which runs with the Sensor. I have upload the Sketch to the other Wemos D1 Mini with Blynk and i got no infos. I have a 4 wired cable from the Wemos to the sensor.
Looks like this

5V (red wire) -> VCC Sensor
G (Black wire) -> GND Sensor
D5 (white Wire) -> Echo Sensor
D6 (yellow wire) -> Trig Sensor

I know that there is a poti or something like that on the sensor board. Should i adjust something on the board of the sensor? When i connect the other Wemos D1 Mini Pro the sensor delivers data.

best Jens

Remove the “//” from the beginning of this line and upload the code again. Open the serial monitor, set its baud rate to 115200 and see what it tells you.
You can copy/paste the results here if you wish, use triple backticks again at the beginning and end of the serial output.


Hi This is what i got in the serial Monitor:

18:10:27.448 -> ⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮[72] Connecting to HausBuki
18:10:57.973 -> [30580] Connected to WiFi
18:10:57.973 -> [30580] IP:
18:10:57.973 -> [30580] 
18:10:57.973 ->     ___  __          __
18:10:57.973 ->    / _ )/ /_ _____  / /__
18:10:57.973 ->   / _  / / // / _ \/  '_/
18:10:57.973 ->  /____/_/\_, /_//_/_/\_\
18:10:57.973 ->         /___/ v0.6.1 on ESP8266
18:10:57.973 -> 
18:10:57.973 -> [30587] Connecting to blynk-cloud.com:80
18:10:58.015 -> [30637] Ready (ping: 15ms).

Just to clarify what you’ve said before, you have one Wemos D1 Mini that works correctly with the sensor, and one that doesn’t?


correct the wemos d1 one mini pro works with another sketch but can read out the cistern and told me that i have xyz litters. What i want to say is the Wemos with the Sensor and another sketch works.The Wemos D1 Mini (not Pro) didn’t work with the sketch here.
This error i get when i will compile with the wemos D1 Mini Pro the one who deliver informations about the cistern.

Arduino: 1.8.9 (Mac OS X), Board: "LOLIN(WEMOS) D1 mini Pro, 80 MHz, Flash, Disabled, All SSL ciphers (most compatible), 16M (14M SPIFFS), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

Build-Optionen wurden verändert, alles wird neu kompiliert
Der Sketch verwendet 316224 Bytes (30%) des Programmspeicherplatzes. Das Maximum sind 1044464 Bytes.
Globale Variablen verwenden 30580 Bytes (37%) des dynamischen Speichers, 51340 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.
Traceback (most recent call last):
esptool.py v2.6
esptool.py v2.6
Serial port /dev/cu.usbserial-1410
  File "/Users/jenskindgen/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.2/tools/upload.py", line 25, in <module>
  File "/Users/jenskindgen/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.2/tools/esptool/esptool.py", line 2652, in main
    esp = chip_class(each_port, initial_baud, args.trace)
  File "/Users/jenskindgen/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.2/tools/esptool/esptool.py", line 222, in __init__
    self._port = serial.serial_for_url(port)
  File "/Users/jenskindgen/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.2/tools/pyserial/serial/__init__.py", line 88, in serial_for_url
  File "/Users/jenskindgen/Library/Arduino15/packages/esp8266/hardware/esp8266/2.5.2/tools/pyserial/serial/serialposix.py", line 268, in open
    raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
serial.serialutil.SerialException: [Errno 2] could not open port /dev/cu.usbserial-1410: [Errno 2] No such file or directory: '/dev/cu.usbserial-1410'
Beim Hochladen des Sketches ist ein Fehler aufgetreten

Dieser Bericht wäre detaillierter, wenn die Option
"Ausführliche Ausgabe während der Kompilierung"
in Datei -> Voreinstellungen aktiviert wäre.

Okay, I’m still a bit confused about what works and what doesn’t.

It seems that the code compiles successfully, but does not like the serial port you’re using to upload to the board.
Have you tried disconnecting everything from the board before uploading the code?


Hi Pete,

it seems it works. The Only thing which won’t work is the graph Waterlevel. Do you know the settings?

1 Like

Looking at the original post, the graph was showing the number of litres in the tank, which in your case you’ve called volume.
So, the graph should be connected to the same virtual pin as your Volume labeled display widget.


HI Pete,

Thanks for help… have change it, see now times in X axe but no graph?