Water Tank Level and Water Volume with ESPHome

Is it possible to extend the length of this cable to 6m?

not a good idea. I tried and the reading is all over the place. shielded cable might work but haven’t tried it :thinking:
I work around it by extending the connection from the nodemcu to the sr04t with a network cable. I have 3 sensors; one in basement and two on the roof, but I still use only 1 nodemcu

Hi, TD and EX is in CM (Centimeters)? I just blindly ordered a sensor :slight_smile:

You think the sensor will work in the middle of such a barrel:

?

I tried setting this up and I keep getting the following message:

Distance measurement timed out!

I connected it to a nodemcu.
Any ideas on this?

Hi
Apologies I really am a newby at this. Do you just need those 2 components? In the photo it seems like you have more?

I have had the same sensors working with rpi3 and use 23AWG ethernet cable over 10m. Make sure you are using copper and not CCA.

Re: not getting ‘fuzzy measurements’, it took some tinkering but the key is in the delays between measurements (add sleep between ping and echo reading as well) – and then given the possibility of random echo, take several measurements.

Measurements are taken every minute and then hass will trigger the powering of wells to fill tanks based on threshold levels (i.e. once tank is below 85%, start pumping).
But to save on power costs, it calculates how long it expects that it will need to pump to fill and then begins pumping such that it will maximize the amount of pumping on cheapest power timeslots (with the goal of being full by next business day)

Also using blitzortung integration, if there have been more than two lightning strikes within 20km in the past 30 minutes it will remove power from wells and actuate a physical disconnect of mains (we are in a remote area and a burnt well with a golf course to water is not fun or cheap)

rpi communicates over MQTT back to hass. implementation has been flawless for year+

Example code for rpi to hass mqtt:

#%%Sensor
#Libraries
import RPi.GPIO as GPIO
import time
import statistics
 
#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
 
#set GPIO Pins
GPIO_TRIGGER = 18
GPIO_ECHO = 24
 
#set GPIO direction (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)
 
def distance():
    # set Trigger to HIGH
    GPIO.output(GPIO_TRIGGER, True)
 
    # set Trigger after 0.01ms to LOW
    time.sleep(0.00005)
    GPIO.output(GPIO_TRIGGER, False)
 
    StartTime = time.time()
    StopTime = time.time()
 
    # save StartTime
    while GPIO.input(GPIO_ECHO) == 0:
        StartTime = time.time()
        # wait for real bounce
        time.sleep(0.0005)
 
    # save time of arrival
    while GPIO.input(GPIO_ECHO) == 1:
        StopTime = time.time()
 
    # time difference between start and arrival
    TimeElapsed = StopTime - StartTime
    # multiply with the sonic speed (34300 cm/s)
    # and divide by 2, because there and back
    distance = (TimeElapsed * 34300) / 2
 
    return distance

def middis():
    runs=[]
    for i in range(11):
        runs.append(distance())
        time.sleep(1)
    #print(runs)
    midrun = statistics.median(runs)
    return midrun

def cisternlevel(dist_to_water):
    #dist = middis()
    cisterndepth = 270 #set cistern total depth
    bufferheight = 25 #set height of sensor above max waterline + safety buffer
    cisternlevel = 100 * (cisterndepth + bufferheight - dist_to_water) / cisterndepth
    return cisternlevel
#%% MQTT
import paho.mqtt.client as mqtt
def bigmqtt():
    def on_connect(client, userdata, flags, rc):
        if rc==0:
            cisternclient.connected_flag = True
            print("connected ok returned code=",rc)
        else:
            cisternclient.bad_connection_flag=True
            print("bad connection returned code=",rc)
        
        
    def on_publish(client,userdata,result):
            print("data published \n")
            pass
    
    def on_disconnect(client,userdata,rc):
        logging.info("disconnected reason " +str(rc))
        cisternclient.connected_flag = False
        cisternclient.disconnect_flag = True

    # configure mqtt connection

    broker = "192.168.xx.xx"
    port = xxx
    username = "xxx"
    password = "xxx"
    mqtt.Client.connected_flag=False
    mqtt.Client.bad_connection_flag=False
    cisternclient=mqtt.Client("cistern1")
    cisternclient.username_pw_set(username, password)
    cisternclient.connected_flag = False
    cisternclient.on_connect = on_connect
    cisternclient.on_publish = on_publish
    cisternclient.will_set("borgo/cistern/health",payload="Disconnected",qos=0,retain=False)

    cisternclient.loop_start()
    try:
        cisternclient.connect(broker, port, keepalive=10)
        while not cisternclient.connected_flag and not cisternclient.bad_connection_flag:
            print("in wait loop")
            time.sleep(1)
        if cisternclient.bad_connection_flag:
            cisternclient.loop_stop()
            exit() #exit where?
        print("in Main loop")
        if __name__ == '__main__':
            try:
                while True:
                    dist = middis()
                    percentlevel=cisternlevel(dist)
                    print ("distance to top = %.1f cm" % dist)
                    print ("cistern level = %.1f percent" % percentlevel)
                    cisternclient.publish("borgo/cistern/level",payload=percentlevel,qos=0,retain=True)
                    cisternclient.publish("borgo/cistern/dist",payload=dist,qos=0,retain=True)
                    cisternclient.publish("borgo/cistern/health",payload="Connected",qos=0,retain=True)
                    time.sleep(10)
            except:
                print("smth broke")
                exit(1)
    except:
        print("main loop connection failed")
        exit(1)
    cisternclient.loop_stop()
    cisternclient.disconnect()
    

if __name__ == '__main__':
    try:
        while True:
            print("alors on danse")
            bigmqtt()
            time.sleep(10)
        # Reset by pressing CTRL + C
    except KeyboardInterrupt:
        print("Measurement stopped by User")
        GPIO.cleanup()


And then in hass config yaml mqtt sensor:

sensor:
    - platform: mqtt
      name: 'cistern_level'
      state_topic: 'borgo/cistern/level'
      unit_of_measurement: '%'
      value_template: "{{ value | round(1) }}"

    - platform: mqtt
      name: 'cistern_dist'
      state_topic: 'borgo/cistern/dist'
      unit_of_measurement: 'cm'
      value_template: "{{ value | round(1) }}"

    - platform: mqtt
      name: 'cistern_health'
      state_topic: 'borgo/cistern/health'
      unit_of_measurement: '%'
3 Likes

Waaaw, amazing, finally I got both sensors work on same NodeMCU (water flow & ultrasonic distance sensor waterproof) , thank you Omer and other brothers for your kind support :smiley:

Can you please share link of water flow sensor you are using?

I am using : YF-S201 Hall Effect Water Flow Meter / Sensor

2 Likes

Would this work for just measuring the water level in a 60 gallon rain barrel? We have one that is used to feed water to our chickens. I’d ideally just like to know when the water level gets low so I can refill it.

2 Likes

Hello @nawafbana its possible to share the code?

I am using this water flow:
https://www.tomsonelectronics.com/products/1-inch-water-flow-sensor-yf-g1-dn25

Code:

esphome:
name: water_level
platform: ESP8266
board: nodemcuv2

wifi:
ssid: “xxxxxxxxxxxx”
password: “xxxxxxxxxxxx”

#Optional manual IP
manual_ip:
static_ip: 192.168.1.39
gateway: 192.168.1.1
subnet: 255.255.255.0

#Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: “Water Level Fallback Hotspot”
password: “xxxxxxxxxxxxxx”

captive_portal:

#Enable logging
logger:

#Enable Home Assistant API
api:
password: “xxxxxxxxxxxxx”

ota:
password: “xxxxxxxxxxxxx”

sensor:

  • filters:
    lambda: |-
    static float total_pulses = 0.0;
    total_pulses += x * 1 / 60.0;
    id(lifetime_counter).publish_state(total_pulses / 27);
    return x ;
    id: water_pulse
    internal: true
    name: “Pulse Counter”
    pin: GPIO14
    platform: pulse_counter
    update_interval: 5s

  • id: lifetime_counter
    name: “Water Total”
    platform: template
    unit_of_measurement: L
    accuracy_decimals: 4

  • accuracy_decimals: 4
    icon: “mdi:water”
    id: water_flow_rate
    lambda: “return (id(water_pulse).state /27);”
    name: “Water Flow Rate”
    platform: template
    unit_of_measurement: L/min
    update_interval: 5s

  • platform: ultrasonic
    trigger_pin: GPIO1
    echo_pin: GPIO3
    name: “Water Tank Level”
    unit_of_measurement: “%”
    accuracy_decimals: 0
    update_interval: 5s
    filters:

    • lambda: return ((((x100)-20)-(140-20))/(140-20))-100;
    • filter_out: nan
  • platform: ultrasonic
    trigger_pin: GPIO1
    echo_pin: GPIO3
    update_interval: 5s
    name: “Water Tank Volume”
    unit_of_measurement: “l”
    accuracy_decimals: 0
    filters:

    • lambda: return ((((x100)-20)-(140-20))/(140-20))-3000;
    • filter_out: nan
3 Likes

still facing some problems in measuring the exact flow, need support from the experts…

*I have it working, but readings go from 1.2m to 1.02m in height. with results in 38% to 51% in the readings. *

*Anyway to do an average sensor or something like that? *

How do you calibrate the screw on the ultrasound PCB?

any other thing to improve readings?

.
.
.

Finding the real problem, my sensor won’t read past 1.1 meeter. any advice? it is a JSN-SR04T

Does the voltage level match the input voltage for the ESP8266 or do you need a voltage divider? I see you wired it directly so was curious…

Edit: Did some research and found this. Will apply for the ESP8266 also.

3 Likes

Hi Nawafbana, can you share GPIO configuration with NodeMCU please?

Hello, im not sure, what numbers i need. Can you help me pls?
My tank:
Tank diameter 2,65m
Height from the bottom of the tank at which the sensor is 2m

Thank you
Ivica

hello I am soooo close,
I have been trying to find the right way to do this for a while now and i found your code yesterday. My results seems promising but i am positive the results are off.
I think something is wrong in my lambda filters but I can’t figure what part.

  • im using the HC-sr04 for testing but will use the waterfproof for realworld (automatic drip irrigation system)
  • from sensor to bottom of “test bucket”: 68cm
  • from sensor to max water level: 33cm (22cm away plus safety)

Here’s the important bit of my code, hope you or someone can help me:

sensor:     
  - platform: ultrasonic
    trigger_pin: GPIO 5
    echo_pin: GPIO 4
    name: 'Water tank Level'
    unit_of_measurement: '%'
    accuracy_decimals: 0
    update_interval: 5s
    icon: mdi:water
    filters:
      - lambda: return ((((x*100)-33)-(68-33))/(68-33))*-100;
      - filter_out: nan
  - platform: ultrasonic
    trigger_pin: GPIO 5
    echo_pin: GPIO 4
    update_interval: 5s
    name: 'Water Tank Volume'
    unit_of_measurement: 'l'
    accuracy_decimals: 2
    icon: mdi:water
    filters:
      - lambda: return ((((x*100)-33)-(68-33))/(68-33))*-1000;
      - filter_out: nan

I am getting 105% and 955.5l: Testing in a food grade 20litres plastic bucket which has about 1 liter of water in the bottom…

I really can’t figure out what’s wrong with the code… trying different sensor placement while waiting for a response.

Thanks in advance.

lambda: return ((((x100)-33)-(68-33))/(68-33))-1000
I finally understand, the problem is that you left the value 1000l in the code. It is the value between the maximum amount of water and the bottom of the tank. So you have a 20 liter bucket set up as a 1000 liter tank. Try set -20.

1 Like

Hi Nawafbana

Thank you for posting this. I’m a bit of a newbie and while I have a level sensor set up and measuring I have tried to compile the sketch you posted but fail.

Can you please tell me how you compiled the sketch.

Thanks in advance

Tony