Water tank level meter

Hello,
I have a water tank to harvest rain water used for my toilets and washing machine.
I wanted to have a visual display and the info in my HA to save me opening the cover.
So I started buying some stuff to start with my project:

The code is used:

esphome:
  name: esphome-web-863f1b

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-863F1B"
    password: "zen0qxS0Jec0"

captive_portal:
    
i2c:
  sda: 4
  scl: 5

font:
  - file: "gfonts://Roboto"
    id: my_font
    size: 16

# https://esphome.io/cookbook/display_time_temp_oled.html
# https://gist.github.com/tubalainen/19103e725c1d7331bc16eae130a6757d
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: my_display
    pages:
      - id: page1
        lambda: |-
          it.printf(0, 0, id(my_font), "WIFI: %.0f", id(puissance_wifi).state);
          it.printf(0, 16, id(my_font), "Eau: %.2f / %.0f", id(distance).state, id(distance).state);
          it.printf(0, 32, id(my_font), "T: %.1f°C H: %.0f", id(temp).state, id(hum).state);

sensor:
      - platform: sht4x
        temperature:
          name: "Temperature"
          unit_of_measurement: "°C"
          device_class: "temperature"
          state_class: "measurement"
          icon: "mdi:thermometer"
          accuracy_decimals: 1
          id: temp
        humidity:
          name: "Relative Humidity"
          unit_of_measurement: "%"
          device_class: "humidity"
          state_class: "measurement"
          icon: "mdi:water-percent"
          accuracy_decimals: 0
          id: hum
      - platform: ultrasonic
        trigger_pin: 12
        echo_pin: 14
        name: "Hauteur d'eau cuve"
        update_interval: 10s
        timeout: 3m
        id: distance
        filters:
          - lambda: return (2.32-x);
          - filter_out: nan
      # Qualité du signal
      - platform: wifi_signal
        name: "Signal Wifi"
        update_interval: 60s
        id: puissance_wifi
      # Temps de fonctionnement
      - platform: uptime
        name: "Allumé depuis (s)"
        id: uptime_sec

binary_sensor:
    # statut
      - platform: status
        name: "Statut"

switch:
    # Bouton de redémarrage
      - platform: restart
        name: "Redémarrage"

    # Transformation des secondes en jours
text_sensor:
      - platform: template
        name: "Allumé depuis (j)"
        lambda: |-
          int seconds = (id(uptime_sec).state);
          int days = seconds / (24 * 3600);
          seconds = seconds % (24 * 3600); 
          int hours = seconds / 3600;
          seconds = seconds % 3600;
          int minutes = seconds /  60;
          seconds = seconds % 60;
          return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
        icon: mdi:clock-start
        update_interval: 60s 

My current challenge is being able to convert the water tank height into liter.
My tank looks like this:
citerne

Can anynody help with the formula to calculate the volume knowing the diameter and lenght of the tank and the height of the water?

Thank you!

1 Like

The equation is lower down on this page:

https://www.mathopenref.com/cylindervolpartial.html

awesome!
Thank you for your help! I will try to put this into practice

I took me some time to figure out how to convert the formula into ESP code but finally it’s done and it works as wished. I post it here if anyone is interested:

      - platform: ultrasonic
        trigger_pin: 12
        echo_pin: 14
        name: "Volume d'eau cuve"
        update_interval: 10s
        timeout: 3m
        id: volumecuve
        unit_of_measurement: "l"
        filters:
          - lambda: return (28.5 * (9.75 * 9.75 * acos((9.75 - (23.2 - x * 10)) / 9.75) - (9.75 - (23.2 - x * 10)) * sqrt(2 * 9.75 * (23.2 - x * 10) - (23.2 - x * 10) * (23.2 - x * 10))));
          - filter_out: nan

Initial formula:
Formula

Thanks for your support

PS: if someone reading knows what the formula is for the square (^2) I could simplify it a little.

1 Like

You could simplify it to the number:

return (28.5 * (95.0625 * acos(...

That’s right, anyhow I prefer to see the single entries to cope with the formula, makes it easier if I need to rework something.

BTW, just finalized the display:
Capture d’écran 2022-10-23 234000

Thanks again, I coulnd’t have done it w/o your help!

2 Likes

Hello,
I again need some help from the community. I’m still optimizing the accuracy of my water level meter.
Fact is that when my cylinder is full, there is a small cylinder on top where the overflow gets evacuated which changes my formula.
I’m stucking at the point where I want to use a if loop in a filter, which seems not to work.
Is something wrong with my syntax or is this just impossible as is?
If impossible how would you recommend to change my formula / filter?
Thanks for your help!

As soon as I remove the # i get an error pointing to “filter_out: nan” which makes me think the if condition is the trouble:

      - platform: ultrasonic
        trigger_pin: 12
        echo_pin: 14
        name: "Volume d'eau cuve"
        update_interval: 1800s
        timeout: 3m
        id: volumecuve
        unit_of_measurement: "l"
        filters:
          - lambda:
#              if (x > 1.95) {
                return (8511 + ((2.32-x) - 1.95) * 100 * 4.5);
#              }
#              if (x <= 1.95) {
#                return (28.5 * (9.75 * 9.75 * acos((9.75 - (23.2 - x * 10)) / 9.75) - (9.75 - (23.2 - x * 10)) * sqrt(2 * 9.75 * (23.2 - x * 10) - (23.2 - x * 10) * (23.2 - x * 10))));
#              }
          - filter_out: nan

I also tried this way:

      - platform: ultrasonic
        trigger_pin: 12
        echo_pin: 14
        name: "Volume d'eau cuve"
        update_interval: 1800s
        timeout: 3m
        id: volumecuve
        unit_of_measurement: "l"
        filters:
          - lambda:  |-
              if (x > 1.95) {
                return (8511 + ((2.32-x) - 1.95) * 100 * 4.5);
              }
              if (x <= 1.95) {
                return (28.5 * (9.75 * 9.75 * acos((9.75 - (23.2 - x * 10)) / 9.75) - (9.75 - (23.2 - x * 10)) * sqrt(2 * 9.75 * (23.2 - x * 10) - (23.2 - x * 10) * (23.2 - x * 10))));
              }
          - filter_out: nan

without more success →

Compiling /data/esphome-web-863f1b/.pioenvs/esphome-web-863f1b/src/main.cpp.o
/config/esphome/esphome-web-863f1b.yaml: In lambda function:
/config/esphome/esphome-web-863f1b.yaml:149:3: error: control reaches end of non-void function [-Werror=return-type]
  149 |           - filter_out: nan
      |   ^
cc1plus: some warnings being treated as errors
*** [/data/esphome-web-863f1b/.pioenvs/esphome-web-863f1b/src/main.cpp.o] Error 1
========================= [FAILED] Took 12.73 seconds =========================

so amazing, can you tell me how to creat the fill tank icon, bros

Hey sorry, I was not really focussed on HA in the last monthes.
If still required please find my yaml:

# Mesure niveau de cuve d'eau de pluie
# Garage double
# ESP8266 + Capteur temperature/humidite sht40 + capteur distance hc-sr04 + ecran OLED 09" 128x64 ssd1306

esphome:
  name: esphome-web-863f1b
esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
ota:
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-863F1B"
    password: "xxxxxxxxxxxxxxxx"
captive_portal:

i2c:
  sda: 4
  scl: 5

font:
  - file: "gfonts://Roboto"
    id: my_font_24
    size: 24
  - file: "gfonts://Roboto"
    id: my_font_32
    size: 32
  - file: 'BebasNeue-Regular.ttf'
    id: my_font_48
    size: 48
  - file: 'arial.ttf'
    id: my_font_14
    size: 14

time:
  - platform: homeassistant
    id: esptime

# https://esphome.io/cookbook/display_time_temp_oled.html
# https://gist.github.com/tubalainen/19103e725c1d7331bc16eae130a6757d

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: my_display
    pages:
      - id: page1
        lambda: |-
          if(id(puissance_wifi).has_state()) {
            if (id(puissance_wifi).state >= -55) {
              it.filled_rectangle(127, 0, 2, 15);            
            }
            if (id(puissance_wifi).state >= -65) {
              it.filled_rectangle(124, 3, 2, 12);
            }
            if (id(puissance_wifi).state >= -74) {
              it.filled_rectangle(121, 6, 2, 9);
            }
            if (id(puissance_wifi).state >= -80) {
              it.filled_rectangle(118, 9, 2, 6);
            }
            it.filled_rectangle(115, 12, 2, 3);
          }
          it.strftime(0, 60, id(my_font_48), TextAlign::BASELINE_LEFT, "%H:%M", id(esptime).now());
          it.printf(127, 23, id(my_font_14), TextAlign::TOP_RIGHT , "%.1f°", id(temp).state);
          it.printf(127, 60, id(my_font_14), TextAlign::BASELINE_RIGHT , "%.0f%%", id(hum).state);
      - id: page2
        lambda: |-
          if(id(puissance_wifi).has_state()) {
            if (id(puissance_wifi).state >= -55) {
              it.filled_rectangle(127, 0, 2, 15);            
            }
            if (id(puissance_wifi).state >= -65) {
              it.filled_rectangle(124, 3, 2, 12);
            }
            if (id(puissance_wifi).state >= -74) {
              it.filled_rectangle(121, 6, 2, 9);
            }
            if (id(puissance_wifi).state >= -80) {
              it.filled_rectangle(118, 9, 2, 6);
            }
            it.filled_rectangle(115, 12, 2, 3);
          }
          it.strftime(2, 0, id(my_font_14), "%H:%M", id(esptime).now());
          it.rectangle(0, 16, 40, 64);
          it.filled_rectangle(0, 64 - 48 * id(pourcentcuve).state /100, 40, 64);
          it.printf(120, 13, id(my_font_32), TextAlign::TOP_RIGHT , "%.0f%%", id(pourcentcuve).state);
          it.printf(120, 40, id(my_font_24), TextAlign::TOP_RIGHT , "%.0f l", id(volumecuve).state);

interval:
  - interval: 5s
    then:
      - display.page.show_next: my_display
      - component.update: my_display

sensor:
      - platform: sht4x
        temperature:
          name: "Temperature"
          unit_of_measurement: "°C"
          device_class: "temperature"
          state_class: "measurement"
          icon: "mdi:thermometer"
          accuracy_decimals: 1
          id: temp
        humidity:
          name: "Relative Humidity"
          unit_of_measurement: "%"
          device_class: "humidity"
          state_class: "measurement"
          icon: "mdi:water-percent"
          accuracy_decimals: 0
          id: hum
      - platform: ultrasonic
        trigger_pin: 12
        echo_pin: 14
        name: "Hauteur d'eau cuve"
        update_interval: 1800s
        timeout: 3m
        id: distance
        filters:
          - lambda: return (2.32-x);
          - filter_out: nan
      - platform: template
        name: "Volume d'eau cuve"
        id: volumecuve
        unit_of_measurement: "l"
        lambda: |-
              if (id(distance).state > 1.95) {
                return (8511 + (id(distance).state - 1.95) * 100 * 4.5);
              } else {
                return (28.5 * (9.75 * 9.75 * acos((9.75 - (id(distance).state * 10)) / 9.75) - (9.75 - (id(distance).state * 10)) * sqrt(2 * 9.75 * (id(distance).state * 10) - (id(distance).state * 10) * (id(distance).state * 10))));
              }
        update_interval: 1800s
      - platform: template
        name: "Remplissage cuve"
        update_interval: 1800s
        id: pourcentcuve
        unit_of_measurement: "%"
        lambda: return (id(volumecuve).state / 8600 * 100);

# Qualité du signal

      - platform: wifi_signal
        name: "Signal Wifi"
        update_interval: 60s
        id: puissance_wifi

# Temps de fonctionnement

      - platform: uptime
        name: "Allumé depuis (s)"
        id: uptime_sec

binary_sensor:
    # statut
      - platform: status
        name: "Statut"

switch:
    # Bouton de redémarrage
      - platform: restart
        name: "Redémarrage"

# Transformation des secondes en jours
text_sensor:
      - platform: template
        name: "Allumé depuis (j)"
        lambda: |-
          int seconds = (id(uptime_sec).state);
          int days = seconds / (24 * 3600);
          seconds = seconds % (24 * 3600);
          int hours = seconds / 3600;
          seconds = seconds % 3600;
          int minutes = seconds /  60;
          seconds = seconds % 60;
          return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
        icon: mdi:clock-start
        update_interval: 60s
1 Like