ESPHome water level sensor

Ok, I went with two of the very cheap
image
I managed to find some spots where they could be mounted, and just misss the weir, and the basket could still be removed…

They are created with device_class: problem, so they say ‘ok’ or ‘problem’, which suits the task at hand.
The lowest one is open for ok, and the highest one os open for problem.

Looks good, the failsafe redundancy is a nice idea. Hope they do the job!

I have a thrown in level sensor from dfrobot connected through a current to voltage converter and a node MCU. I get the random voltage spikes and voltage variations throughout when no water is being added or removed see below. Can someone please help? Could this be related to the tank heating up and the water inside as the sun hits the western side of the house in the afternoon. Because it is always in the afternoon around the same time. Is anyone else seeing this?

My ESP config is:
sensor:

  • platform: adc
    pin: A0
    id: “water_tank_voltage”
    name: “Water Tank Voltage”
    icon: ‘mdi:water-percent’
    unit_of_measurement: ‘V’
    update_interval: 1s
    • multiply: 3.3
    • sliding_window_moving_average:
      window_size: 15
      send_every: 15
      send_first_at: 1
    • delta : 0.0015

I can confirm that my pressure transducer is affected by temperature. Water density does change with temperature, however, I suspect the transducers performance is what is actually being affected.

I have this type of sensor. It is externally mounted to the outflow pipe at the bottom of my tanks. I had decided to include a bmp280 temp/barrometric sensor in the enclosure for the microcontroller. At first the temperatures being reported got ridiculously high because the enclosure was a sealed black box and was sun exposed. After devising a sun-shade for the transducer and component enclosure, internal temperature held closer to ambient temperature. I was able to see that after overnight readings were stable (no water usage or major temperature changes). At around 8am when direct sunlight/ambient temperature increased, the pressure value decreased. I collected data for a week and became certain that once over 27c the pressure value started to drop. I was able to determine the pressure drop was linear to the increase in temperature, and so applied a linear calibration in firmware.


The light blue line shows after compensating for temperature.

I ended up correcting for this in firmware. I’ve been meaning to do a complete write-up on my experience with this sensor, but haven’t found the time.

esphome:
  name: water_tank_sensor
  platform: ESP8266
  board: d1_mini

i2c:
  scl: D1
  sda: D2
  scan: True

ads1115:
  - address: 0x48
    continuous_mode: on

sensor:
  - platform: bmp280
    address: 0x76
    update_interval: 1s
    pressure:
      name: "Barometer"
    temperature:
      name: "Thermometer"
      oversampling: 16x
      on_value:
        then:
          - lambda: id(temp_comp_factor).publish_state(x);

## Internal Voltage Reading
  - platform: ads1115
    id: tank_raw_voltage
    multiplexer: 'A0_GND'
    gain: 2.048 #3
    update_interval: 1s
    accuracy_decimals: 3
    on_value:
      then:
        - lambda: id(tank_voltage).publish_state(x);
        - lambda: id(tank_raw_percent).publish_state(x);

## Exposed Voltage Reading
  - platform: template
    id: tank_voltage
    name: "Water Tank Voltage"
    device_class: voltage
    accuracy_decimals: 3
    filters:
      - median:
      - exponential_moving_average:
          alpha: 0.2
          send_every: 25

## Exposed Tank Percentage
  - platform: template
    name: "Water Tank Pressure"
    id: tank_raw_percent
    update_interval: 1s
    accuracy_decimals: 0
    device_class: ""
    unit_of_measurement: "%"
    icon: "mdi:water-well"
    filters:
      - median:
      - exponential_moving_average:
          alpha: 0.2
          send_every: 25
      - calibrate_linear:
          - 0.450 -> 0.0
          - 0.820 -> 100.0
      - lambda: |-
          if (x >= 100) return 100.0;
          else if (x <= 0.0) return 0.0;
          else return x;
    on_value:
      then:
        - lambda: id(tank_comp).publish_state(x);

## Exposed Temperature Compensation
  - platform: template
    name: "Temp Comp Factor"
    id: temp_comp_factor
    accuracy_decimals: 2
    unit_of_measurement: "°C"
    device_class: ""
    filters:
      - calibrate_linear:
            - 27.1 -> 0.0
            - 31.1 -> 2.3

## Exposed Compensated Percentage
  - platform: template
    name: "Water Tank Compensated"
    id: tank_comp
    accuracy_decimals: 0
    unit_of_measurement: "%"
    icon: "mdi:water-well"
    filters:
      - lambda: |-
          if (id(temp_comp_factor).state <= 0) {
            return x;
          } else {
            return (x + id(temp_comp_factor).state);
          }
      - lambda: |-
          if (x >= 100) return 100.0;
          else if (x <= 0.0) return 0.0;
          else return x;
      # - heartbeat: 30s
      - exponential_moving_average:
          alpha: 0.1
          send_every: 5

Hope this helps.

2 Likes

Thanks @ronsum. I like how you have all your calculation are at the sensor output level in ESP home. I might use that idea and some of your code.

But what I find different is my voltage and level percentage calculation increases with temperature which is counter-intuitive I would have thought the hydrostatic pressure would decrease with increase in temperature as the density decreases or stay the same due to an equivalent increase in fluid height due to volume expansion in a fixed container volume. Note my tank is a vented non pressurised tank.

Do you think my volume expansion and hence fluid height increase greater than any density reduction hence I am seeing an increase in level/hydrostatic pressure?

I should probably say that I only have a layman’s understanding of the physics and electronics involved, all based on a few google searches regarding temperature and [water density, water volume, and pressure sensors]. Luckily I added a BMP280 sensor (thinking I may need to compensate for barometric pressure). When I noticed the unexpected fluctuations, I added the BMP280’s data to Grafana and was first able to realize the issue and then gather the data to write the compensation in firmware.

What you described seems to be in sync with the thermal effects on water. My readings weren’t, which lead me to believe that it was the sensor being affected. Either way, if you’re experiencing fluctuations that seem to coincide with the sun, it is likely a temperature issue.

As your sensor is the drop-in type (same temp as water) and the readings seem in sync with thermal effects on water, I suspect you need to monitor the water temperature in your tank. Having my sensor and other electronics all external and housed in the same environment allowed me to use the BMP280 sensor. I can’t recommend any particular in-tank water temperature sensor.

Add a temperature sensor internally (in tank) or externally (at microcontroller), and collect data on a fixed volume of water over a few days. Once you add temperature to your graph, you can verify if a spike in pressure corelates to a spike in temperature, and by how much.

The links up top should help understand the physics.

This is my tank and sensor setup:



2 Likes

I have a very similar setup (almost exact!). How do you compensate for the pump suction pressure at the sensor when it comes on? Does this affect the reading (albeit instantaneous).

Pump suction does cause a momentary downward spike. It causes the live reading to be inaccurate, and at the same time has the benefit of showing pump usage :man_shrugging:. I wanted to rectify this physically, but that would require emptying a tank.

I used the median and exponential_moving_average filters, applying the exponential_moving_average twice. I had to experiment quite a bit and can’t say if my method is optimal… but resolution and update frequency are acceptable for my application, especially when compared to an ultrasonic sensor.

This is the last 30 days of my ultrasonic sensor vs pressure transducer:

@ronsum Seems we have an almost identical setup. Here is what my sensor mounted looks like. Also I have been comparing my ultrasonic sensor with the pressure transducer just as you have. The difference would be the offset for the “dead space” on the ultrasonic sensor that I haven’t accounted for.

I also had to apply some filtering due to pump suction. My aim is to remove the ultrasonic sensor completely and just use the pressure transducer. I also included a screenshot of WASA water pressure (because Freeport na :D)

image

Hi @ronsum see below description for my liquid level sensor it says it has built in temperature compensation. Which makes me wonder if the temperature issue is related to the pressure sensor or the node mcu chip and power supply heating up with ambient temperature going up. I am wondering whether the temperature compensation should be be based on ambient temperature or water temperature.

“This throw-in type liquid level transmitter adopts high-performance pressure sensing chip, with advanced circuit processing and temperature compensation technology. The level transmitter receives different pressures at different depths of liquid, which can be converted into corresponding current signals and output through the sensor. In this way, the depth of liquid can be measured.”

Can you share your ESPHOME yaml and circuit diagram/device wiring for this set-up?

I have a throw in level sensor from dfrobot that is temperature compensated. I also have a DS18B20 temperature sensor now installed in the IP67 enclosure that houses the power supply, nodemcu and the analog current voltage converter. There seems to be a close correlation between the ambient temperature in the enclosure and the voltage reading- see below. Can someone please help with the temperature compensation formula in esphome. See attached temperature vs voltage curve.

esphome config:
#Dallas DS18B20 Temp sensor
dallas:

  • pin: GPIO5
    update_interval: 1s

Internal Voltage Reading

sensor:

  • platform: adc
    pin: A0
    id: tank_raw_voltage
    update_interval: 1s
    accuracy_decimals: 3
    filters:
    • multiply: 3.3
    • delta : 0.002
      on_value:
      then:
      • lambda: id(tank_voltage).publish_state(x);
      • lambda: id(tank_percent).publish_state(x);
      • lambda: id(tank_volume).publish_state(x);

Temperature Reading

  • platform: dallas
    id: enclosure_temp
    address: xxxxxxxxxxxx
    name: “Sensor Enclosure Temperature”
    resolution: 12
    unit_of_measurement: “°C”
    icon: “mdi:thermometer”
    device_class: “temperature”
    state_class: “measurement”
    accuracy_decimals: 1
    filters:
    • median:
    • exponential_moving_average:
      alpha: 0.2
      send_every: 15

Exposed Voltage Reading

  • platform: template
    id: tank_voltage
    name: “Water Tank Voltage”
    device_class: voltage
    accuracy_decimals: 3
    filters:
    • median:
    • exponential_moving_average:
      alpha: 0.2
      send_every: 15

Exposed Tank Percentage

  • platform: template
    name: “Water Tank Level %”
    id: tank_percent
    update_interval: 1s
    accuracy_decimals: 0
    device_class: “”
    unit_of_measurement: “%”
    icon: “mdi:water-well”
    filters:
    • median:
    • exponential_moving_average:
      alpha: 0.2
      send_every: 15
    • calibrate_linear:
      • 0.231 → 0.0
      • 0.926 → 100.0

Exposed Tank Volume

  • platform: template
    name: “Water Tank Volume”
    id: tank_volume
    update_interval: 1s
    accuracy_decimals: 0
    device_class: “”
    unit_of_measurement: “L”
    icon: “mdi:water-well”
    filters:
    • median:
    • exponential_moving_average:
      alpha: 0.2
      send_every: 15
    • calibrate_linear:
      • 0.231 → 0.0
      • 0.926 → 5000
1 Like

The relevant sections in my yaml look like this:

i2c:
  sda: 21
  scl: 22

sensor:
  - platform: ina219
    address: 0x40
    shunt_resistance: 0.1 ohm
    current:
      name: "fonteneA"
      accuracy_decimals: 5
    power:
      name: "fonteneW"
      accuracy_decimals: 5
    bus_voltage:
      name: "fonteneV"
      accuracy_decimals: 2
    shunt_voltage:
      name: "fonteneV2"
      accuracy_decimals: 5
    max_voltage: 32.0V
    max_current: 400mA
    update_interval: 10s

2 Likes

Thank you. Why do you use max_current: 400mA if the throw in sensor works in 4-20mA range? or is that just a setting for the ina219?

That’s for the INA219. It defaults to max. 3.2A and a precision of 0.8mA. Setting it to max 400mA gives a precision of 0.1mA.

2 Likes

Ahh I see. Thanks for the clarification. This is my first time using the INA219 and still learning the settings for it.

Hello to all
I try to do the same but i receive always ov on the esp, i have make this


and i use the code in this post to make this one

esphome:
  name: water-level
  platform: ESP32
  board: esp-wrover-kit

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "3819628eb5eee80ad86c901f2838e6ec"



ethernet:
  type: LAN8720
  mdc_pin: GPIO23
  mdio_pin: GPIO18
  clk_mode: GPIO0_IN
  phy_addr: 1
  power_pin: GPIO16

  # Optional manual IP
  manual_ip:
    static_ip: 10.0.0.235
    gateway: 10.0.0.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails

dallas:
  - pin: GPIO14

sensor:
  - platform: adc
    pin: GPIO39
    name: "water level voltage"
    id: watertank_voltage    
    accuracy_decimals: 3
    update_interval: 1s #the delta filter will ensure it only sends values when something changes. 
    filters:
      - sliding_window_moving_average:
          window_size: 10 #creates a moving average of the last 10 values
          send_every: 1 #sends the moving average at every measurement (but only if it passes the delta filter below)) 
          send_first_at: 1 #after startup immediately start sending the result rather than wait for the first 10 measurements
      - delta : 0.0015 #only send the result if the voltage difference with the last sent result is higher than this
        ## Exposed Voltage Reading
  - platform: template
    name: "Water Level Sensor usable CM" #first X cm are below pump inlet and thus unuseable.
    id: watertank_cm
    icon: 'mdi:water-well'
    unit_of_measurement: 'cm'
    lambda: |-
        return id(watertank_voltage).state;
    update_interval: 1s #the delta filter will ensure it only sends values when something changes. 
    filters:
      - calibrate_linear:
          # Measured value of X volt maps to y cm
          - 0.0 -> 0
          - 2.5 -> 100.0
      - delta : 0.001 #only send the result if the difference with the last sent result is higher than this
  - platform: template
    name: "Water Level Sensor usable %"
    id: watertank_percent
    icon: 'mdi:water-well'
    unit_of_measurement: '%'
    lambda: |-
        return id(watertank_cm).state / 165.0 * 100; 
      #divide by max water level height to get a percentage
    update_interval: 1s #the delta filter will ensure it only sends values when something changes. 
    filters:
      - delta : 0.001 #only send the result if the difference with the last sent result is higher than this
  - platform: template
    name: "Water Level Sensor usable liters"
    id: watertank_liter
    icon: 'mdi:water-well'
    unit_of_measurement: 'l'
    lambda: |-
        return id(watertank_cm).state / 100 * 1 * 1 * 3 * 1000.0;
      #height (meters) times Largeur times Lenght times nombers of tanks times 1000 gives liters.
    update_interval: 1s #the delta filter will ensure it only sends values when something changes. 
    filters:
      - delta : 0.001 #only send the result if the difference with the last sent result is higher than this

  - platform: dht
    pin: GPIO4
    temperature:
      name: "Cabane Temperature"
    humidity:
      name: "Cabane Humidity"
    update_interval: 60s      

  - platform: dallas
    address: 0x843C01B556471B28
    name: "watertank Temperature"

Have you an idea ???
Thanks a lot

1 Like

Hi @aherbjornsen with your setup using esp32, ina219 and TL-136 sensor do you see an drift in the TL-136 values based on ambient temperature?

I am using a TL-136 with an ESP8266 and analog current to voltage converter but the output voltage drifts with ambient temperature making the readings unusable.

@rybackisback I don’t really have much data with variable ambient temperature. I see from old graphs that I had it outdooors for a single day when the temperature varied between 14 and 28 degrees without seeing that reflect on my readings.

Thanks @aherbjornsen. Are you please able to share your wiring diagram. Ive never used an INA219 before.