Water Tank Level Indicator with Low Level Warning Notifications

@GG07 that would be great !thanks

@GG07

i guess , these are cm?

I dont get this number :sweat_smile:
what unit is this and how di you get to this number?
i have a 7000L cisterne , when its full , the are around 30 cm to the water level.

Ok…so my tank dimensions are

253cm = diameter (radius = 126.5cm)
202cm = tank height (also sensor height)

Vol = (pi * r^2)* tank height
= (3.1415926536 * (126.5 * 126.5))* 202
= 10,155,055.310263

So the calculations indicate a 10,155 litre capacity but the tank overflow is set at 9000litres.

Is your tank round?

yes , its an up standing cilinder ,approximately
160cm diameter
340cm tank height
@GG07 now i`m testing the code with a 17L bucket . so far so good. what do i have to chance in the code , if the sensor is 30cm above the bucket ?
idont know , if the is a pm funktion in this forum, but you can mail me : niklas.hallmann@yahoo.de

If the sensor is 30cm above the bucket then just add the 30cm to the height of the bucket then work out the volume based on that measurement.

1 Like

thx @GG07 .now it works ! i also managed it to send the values to thinkspeak .
now i want to implement a 0.96 Inch OLED I2C Display.
maybe you can help me with that too.

That’s good to hear. I haven’t done much with the OLED screens. There’ll be something on the Blynk forum to help you though.

@Xrapto92…I’ve updated my code with an improved formula for tank volume and added a smoothing filter. How did you go with your OLED?

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.

thanks

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.

https://www.ebay.com/itm/253978673298

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

https://www.ebay.com/itm/254113850508

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…

Pete.

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.

Pete.

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.
  Blynk.syncVirtual(V10);
  Blynk.syncVirtual(V11);
}
 
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); 
 delayMicroseconds(2); 

 digitalWrite(trigPin, HIGH);
 delayMicroseconds(10); 
 
 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;
  
   } 
 else{
 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());
  //terminal.println(levelAlert);
}


BLYNK_WRITE(V10) {
  levelAlert = param.asInt();
  terminal.print("Water Level Alert: ");
  terminal.println(levelAlert);
  terminal.println();
  terminal.flush();
} 
   
BLYNK_WRITE(V11) { 

  int alertOff = param.asInt();

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

}


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;
resetWaterLevelAlert();
  }
}

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); 
 delayMicroseconds(2); 

 digitalWrite(trigPin, HIGH);
 delayMicroseconds(10); 
 
 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.println(distance1);
   terminal.print("Smoothed Distance: ");
   terminal.println(smoothDistance);
   terminal.flush();
   //terminal.print("Percent: ");
   // terminal.println(percent1);
 } 
   else{
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.println(volume1);
terminal.print("Uploaded Smoothed Distance: ");
terminal.println(smoothDistance);
terminal.println();
terminal.flush();
//terminal.print(sorted[j]); 
//terminal.println("   "); 
//Serial.println();
//terminal.print("average = ");
//terminal.println(total/k);
//terminal.print("Uploaded Percent: ");
//terminal.print(percent1);
}

  

void setup() {
 Serial.begin(115200);
 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);

 terminal.clear();
 
 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(sorted[j]); 
Serial.print("   "); 
  }
  Serial.println();
*/
   // 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
 k++; 
 //Serial.print(sorted[j]); 
 //Serial.print("   "); 
 //terminal.print(sorted[j]); 
 //terminal.println("   ");
  
  }
   //terminal.print("average: ");
   //terminal.println(total/k);
   //terminal.flush();
   //Serial.println();
   //Serial.print("average = ");
   //Serial.println(total/k);
  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.

Pete.

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