ESPHome water level sensor

Thanks to advice given by @JulianDH here in this thread, I went for QDY30A sensor (25m long cable, up to 6m measurement, 0-5V output): https://www.aliexpress.com/item/4000041842214.html?spm=a2g0o.order_list.order_list_main.79.8cc51802eFJ3Fb
I used Wemos D1 mini and ADS1115 to measure voltage. I am using 24V power supply and XL4015 stepdown module (https://www.aliexpress.com/item/32675514763.html?spm=a2g0o.order_list.order_list_main.121.8cc51802eFJ3Fb) to convert 24V to 5V to feed Wemos and ADS1115. Sensor is powered directly from 24V power supply.
Below is the code I am using, aside from water level, I also average my voltage measurement (just to make it easier to calibrate sensor). I still need to calibrate in larger tank, but I do not have anything to use at the moment. I am planning to mount the sensor in the 23m deep well in 2 days.

Code:

i2c:  
  sda: 04
  scl: 05
  scan: true
  id: bus_a

ads1115:
  - address: 0x48

sensor:
  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 4.096
    name: "Vyska Hladiny"
    id: wtd
    internal: false
    unit_of_measurement: 'm'
    accuracy_decimals: 3
    icon: "mdi:water-percent"
    update_interval: 1s
    filters:
      - median:
          window_size: 30
          send_every: 30
          send_first_at: 30
      - calibrate_linear:
          - 0.189 -> 0.228
          - 5.000 -> 6

  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 4.096
    name: "Napatie"
    internal: false
    unit_of_measurement: 'V'
    accuracy_decimals: 3
    icon: "mdi:current-dc"
    update_interval: 1s
    filters:
      - median:
          window_size: 30
          send_every: 30
          send_first_at: 30

And this is how it looks so far. Plastic cap will be at the top of well (concrete plate serving as cover, there is hole in it to put the sensor in, cap will hold it on the cover)

Really good job. Be careful of supply to ADS1115. You said 24v ! Data sheet says 6v maximum. I think 5v would be fine. That means maximum measurable voltage on A0 pin will be the supply voltage ( reference voltage).

Wemos and ADS1115 are powered from the stepdown module (means they both are using 5V directly from the stepdown module).
Only QDY30A gets 24V.
I will post couple pictures once I mount it and (hopefully) it will work as expected.

Be carefull. Don’t buy sensors on the shop “SENSOR WOLRD” on AliExp. They ignore me since 2 months. I have a problem with the sensor, they read my messages but they don’t help.

I managed to mount the sensor on the well, but did not realize one thing - electric box that is near the well is switched by pressure sensor inside the house. That means it has no constant power and turns on only while pump is running. That opened another challenge - drag electric cable from inside the house. This will be done in coming days/weeks.
I have tested only by connecting it to extension cord, works OK. However I would like to be able to see the trend, etc.
In the meantime, attaching only photo of installation.
One thing that surprised me - I was expecting to be able to “feel” when the sensor hits the bottom. But I could not - I assume the weight of 25m of “hanging” cable is more than sensor itself. Just stating it, it is definitely at the bottom of the well.

One additional project is to measure water level in tank (and switch either well pump or pump in tank depending on whether there is water or no). Originally, I went for JSN-SR04T ( https://www.aliexpress.com/item/1005001431443682.html?spm=a2g0o.order_list.order_list_main.62.603e1802T3HPNJ ), however I was not able to get it to measure precisely, not to mention longer distances (it is advertised as up to 4m, I was not able to get it to measure above 2m).
Anyway, I purchased VL53L1X TOF400C ( https://www.aliexpress.com/item/1005003614683022.html?spm=a2g0o.order_list.order_list_main.50.603e1802T3HPNJ ). As seen in the picture, it does require some extra work to make it waterproof, but that should not be a big deal.
Since it is not natively supported by ESPHome (VL53L0X is), it took a moment to gather the right resources. I am using Wemos D1 mini. Wiring is relatively simple - sensor is powered by 3,3V (from wemos), GND, and SDA and SCL (see below config).

yaml for the device:

substitutions:
  devicename: laser-distance

esphome:
  name: '${devicename}'
  includes:
    - tof_vl53l1x.h
  libraries:
    - "Wire"
    - "VL53L1x"

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxx"

ota:
  password: "xxx"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Laser-Distance Fallback Hotspot"
    password: "xxx"

captive_portal:

i2c:
  sda: GPIO4
  scl: GPIO5
  scan: false
  frequency: 400kHz

sensor:
- platform: custom
  lambda: |-
    auto my_VL53L1X_sensor = new VL53L1XCustomSensor();
    my_VL53L1X_sensor->set_update_interval(2000); // define update interval
    App.register_component(my_VL53L1X_sensor);
    return {my_VL53L1X_sensor};
  sensors:
    name: "Distance"
    accuracy_decimals: 0
    unit_of_measurement: "cm"
    id: distance
    filters:
      - lambda: return x/10 ;
      - median:
          window_size: 15
          send_every: 15
          send_first_at: 15

- platform: template
  name: "Filled Tank %"
  lambda: return id(distance).state;
  unit_of_measurement: "%"
  accuracy_decimals: 0
  filters:
    - calibrate_linear:
        - 1 -> 100
        - 80 -> 0

As seen, I convert measurement to cm instead of mm. In addition, template sensor is to get tank % filled. It requires setting limit values accordingly.
In order to compile, put additional file next to yaml for the device called tof_vl53l1x.h with following content:

#include "esphome.h"

#include <Wire.h>
#include <VL53L1X.h>


class VL53L1XCustomSensor : public PollingComponent, public Sensor {

 private:
  VL53L1X tof_sensor;

 public:
  // constructor
  VL53L1XCustomSensor() : PollingComponent(15000) {} // polling every 15s

  void setup() override {
    // This will be called by App.setup()
    Wire.begin();
    Wire.setClock(400000); // use 400 kHz I2C

    tof_sensor.setTimeout(500);
    tof_sensor.setAddress(0x29);
    if (!tof_sensor.init()) {
      ESP_LOGE("VL53L1X custom sensor", "Failed to detect and initialize sensor!");
      return;
    }

    tof_sensor.setDistanceMode(VL53L1X::Long);
    tof_sensor.setMeasurementTimingBudget(250000);	// 1000us = 1ms

    ESP_LOGI("VL53L1X custom sensor", "initialised");

    //tof_sensor.startContinuous(250);	// ms
  }

  void update() override {
    //uint16_t distance_mm = tof_sensor.read(false);
    uint16_t distance_mm = tof_sensor.readSingle();
    
    if (!tof_sensor.timeoutOccurred()) {
      publish_state(distance_mm);
    } else {
      ESP_LOGE("VL53L1X custom sensor", "Timeout during read().");
    }
  }
};

I must say that after few weeks of using the sensor, I am very pleased with stability and overall reporting.
For instance, here are 10-day measurements. Critical boundary is around 1.2m, so at least I can be notified when the level hit values near that, in order to take respective actions.

Hi all,

I have been using stainless pressure sensor 4-20mA for >3-4 years with very stable results via i2c and ADS1115.

The difference maybe that I have been using this: NCD.io board which includes INA196 and voltage up to 16V.

Admittedly not super cheap at $50 but converts the 4-20mA easily into i2c, along with up the voltage to 16V grounded to get pressure sensor working, along with shunt current.

Connection really couldn’t be easier:

i2c: SCL, SDA, 5v, GND (NB is 5V) and then pressure sensor black/red connected to other side.

Read the i2c result on address - varies from 6124 to 32125 depending on pressure…

Hi all,
I’m wondering if someone smarter that I can assist me.

Due to frequent water outages in our area, we initially installed 1 x 2400l tank and I installed a submersible gravity sensor into it - with the help of others here, it’s been working great!

We’ve now had to install a second 2400l tank - both tanks are connected to one another at the bottom of the tanks via a pipe and their levels remain the same. Both are identical in shape and size, on a flat surface & next to one another (± 0.5m apart).

I was wondering how I could go about measuring the total volume in both tanks…
Would I need to purchase a second submersible sensor or is there some math that I could do to multiply the values.

This is what I’ve tried and the results have been less than favourable with lots of fluctuations:

captive_portal:

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  id: bus_a

ads1115:
 address: 0x48
 i2c_id: bus_a
 continuous_mode: off
       
sensor:
- name: Jojo Tank Height
  id: tank_height
  platform: ads1115
  multiplexer: 'A0_GND'
  update_interval: 60s
  gain: 4.096
  unit_of_measurement: "metres"
  icon: "mdi:gauge"
#  accuracy_decimals: 3


  filters:

  - median:
     window_size: 200
     send_every: 10
     send_first_at: 4    


  - calibrate_linear:

    - 0.480 -> 0
    - 0.920 -> 1.550
       
- platform: template
  name: Jojo Tank Volume
  id: tank_volume
  unit_of_measurement: "litres"
  lambda: |-
    return 2 * 3.14159265 * 0.71 * 0.71 * id(tank_height).state * 1000;

- platform: template
  name: Jojo Tank Percentage
  id: tank_percentage
  unit_of_measurement: "%"
  accuracy_decimals: 0
  lambda: |-
    return (id(tank_height).state) / 1.550 * 100;
  

- platform: wifi_signal
  name: "Jojo Volume WiFi Signal Sensor"
  update_interval: 60s

switch:
  - platform: restart
    name: tankvolume

The voltage seems to be very inconsistent and erratic and thus it seems like it’s impossible to calibrate.

Not sure if there’s a solution to this or if anyone can kindly assist :slight_smile:

Hello @mrmuttley I think you have two questions?

One tank Volume
π×r×r x height

Two tank volume
2 x π×r×r x height

Height for both is derived from your existing sensor as you have already set up the depth of the tank through linear filter. You would now set up a template sensor and create a lambada with the formula in it.

I had to do the same BUT my cylinder water tank is on its side and therefore the maths is a pig!

Did you get this problem after installing the second tank? There is no reason I can see why there should be a difference. Your sensor is a throw in pressure sensor. It only knows the height of the water above it.

1 Like

Thanks - I tried to do this as per the code above… would you mind having a look to see if it makes sense?

Problem only started after the second tank was added and updated the code to multiply the total by 2.
Was thinking of perhaps upping the voltage on the sensor but not sure it this would make any difference.

Any further input would greatly be appreciated!

Hi all,

Is anyone able to kindly assist me with adding in filters to my setup.
I’m currently getting values that are greater than the max height, max volume and greater than for 100% for tank percentage.

I’d like to display the last min/max set values and not a NAN value when filtering.

Max volume of both tanks is 4860 Liters
Max height is 1.535 Meters
and Max percentage should ignore anything above 100%

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  id: bus_a

ads1115:
 address: 0x48
 i2c_id: bus_a
 continuous_mode: off
       
sensor:
- name: Jojo Tank Height
  id: tank_height
  platform: ads1115
  multiplexer: 'A0_GND'
  update_interval: 60s
  gain: 4.096
  unit_of_measurement: "metres"
  icon: "mdi:gauge"
  accuracy_decimals: 3


  filters:

  - median:
     window_size: 200
     send_every: 10
     send_first_at: 4    


  - calibrate_linear:

    - 0.445 -> 0
    - 1.025 -> 1.535
       
- platform: template
  name: Jojo Tank Volume
  id: tank_volume
  unit_of_measurement: "litres"
  lambda: |-
    return 2 * 3.14159265 * 0.71 * 0.71 * id(tank_height).state * 1000;

- platform: template
  name: Jojo Tank Percentage
  id: tank_percentage
  unit_of_measurement: "%"
  accuracy_decimals: 0
  lambda: |-
    return (id(tank_height).state) / 1.535 * 100;
  

- platform: wifi_signal
  name: "Jojo Volume WiFi Signal Sensor"
  update_interval: 60s

switch:
  - platform: restart
    name: tankvolume

Thanks in advance :slight_smile:

Hello @mrmuttley, first thing is to check what the voltage is when the tank is at the maximum height i.e. full. You may have already done that but it clearly has moved. Easy thing to do is # out the three lines of calibrate _linear, reflash and then reread the new voltage from the logs. Remove the #’s and reenter the calibrate line with the new voltage number i.e. not 1.025

Just need to write some if statement for volume and percentages if you find the base voltage is still wondering around. I can help

Thanks so much for your help with this…

I’ve tried re-calibrating and re-measuring the actual height of the water so many times that I feel like I’m trying to hit a moving target. I’ve even taken the probe out to get a new 0 voltage.

There’s a buck/dc-dc converter installed for both the probe and ESP32 to try keep things stable as suggested by others above.
I’m wondering if perhaps the probe or the 4-20mA to voltage converter is faulty…

For a few days the voltage was 1.025 and now it’s 1.067…

Hence the reason why I’d like to try filter out the variables and set a min and max…
Thanks for the help and input- any additional help would greatly be appreciated!

Hey community,

I wanted to measure my stormwater soak-drain changes (rises quickly, falls slowly, does it overflow?), and solve a rain-water tank problem measurement for a relative.

Journey

There’s a dearth of good water/liquid depth sensors that are pre-integrated into esphome, and certainly none pre-packaged that’ll measure levels that I could find. I think this is mainly because it’s a hard problem to solve in a general sense (how deep, which liquid, etc), but also because the best sensors out there for this purpose are non-trivial to connect to (powered, analog).

I chose the 4-20mA variant sensor immersion sensors after reading way too much here and elsewhere (ultrasonic dead zones, waterproofness, ripples etc and trying to solve for both my use cases). On my research journey I found integrations for current sensors directly onto esp32 boards that required discrete circuitry, but that plus getting the (recommended for this sensor) 24v near the esp32 didn’t seem like a great idea. There are also super-cheap (like $5) industrial sensor->RS-485modbus modules though that can take 2(+) sensor inputs, and are designed to be permanently installed and robust to mistreatment/lightning/etc. So I took that option.

Then there’s RS-485 which has been around forever but I’d not appreciated it as useful for IoT until now. There also esp32 integrations/sub-boards for RS-485. And then there’s the Lilygo T-CAN485 which has an nicely isolated RS-485 interface on it already, and means I didn’t need another board. They do other boards with RS-485 too, but every single one is different.

For this project I decided that the water sensor could be remote, and the esp32 etc could be further away from the water, and then just running 12v to it would be easiest.

Materials

(no affiliate links, but links might be country-dependent)

There are many liquid sensors of this submersible kind. Not all are equal (reports are that some rust) and I believe this store is the OEM and not a re-reseller. These sensors are quite a bit bigger and more hefty than they look in the photos. Ensure the cable length is long enough as it’s got a pressure-equalisation tube inside it that needs to be kept out of the water(discussed above), I guess they can be cut down (but I didn’t) but extending could be problematic. They want 24v but will apparently run on 12v.

Pictures


Testing in my water bottle.


Sensor, 18650 for size reference :slight_smile: Sensor is quite heavy.


Installed, working, with ferrules even. Cable management could be better…

esphome configuration

Modbus configuration of the T-CAN485 board is a little annoying and was undocumented outside of the Arduino examples. It works though, just needs some pins flipped high. Feedback welcome…

Full yaml here

Got the RGB LED working properly too.

Results

It verks!

And installed and on a dashboard.

Final Thoughts

I like using these off-the-shelf components. They’re robust although not that space efficient. I am thinking about why I don’t have an RS-485/modbus bus loop around my house now too. I have no real-world experience with it yet but now I see why there is an RS-485 version of my weather station (Ecowitt), and it looks like I could loop in my aircon and solar inverter too.

The industrial 4-20mA current sensor approach is something I’d do again for remote sensors because after the “why did they make it so complicated?” thoughts subsided I realised that this range stays ~static regardless of the length of the wire to the sensor (unlike say 0-5v), and probably other good reasons too. It’s clearly not a low-power (uA) consumption approach though, so you do need a stable power source. Once 4-20ma sensor integration is solved for one type we can apply this to other types of sensor like this as well as well…

The “12 bit” RS-485 sensor converter is using less than 12 bits because it uses 2621 values for the 4-20mA range of the sensor, which is not 4096 (12 bits). Still enough values for sufficient precision for my purposes. Docs went through Google Translate fine. Can’t beat the price, and there’s space for another sensor.

The sensor is (a few days in) awesome. Came well packaged, calibrated to the values engraved, and just works. It will take some years to prove them out completely, but it looks like the real thing. I could have ordered the RS-485 version (and avoided a box), but I these are more common and replaceable. Seems ~stable across temp and barometric changes at least within 2mm change.

The case probably needs a vent as so the barometric compensation works properly, but it’s not wildly varying (my XT60 hole may be providing this). I’ve ordered some cable glands with vents…which are a thing. TIL.

2 Likes

that is about 4% variability … I am getting 0.45% … what is the rated working depth you purchased? I would have to rerun the numbers when we get to summer and tank lower. How about following:

    filters:
      - lambda: if (x > 1.067) { return 1.067; } else { return x; } 
      - calibrate_linear:
          - 0.028 -> 0
          - 1.067 -> 1.535
      - sliding_window_moving_average:
          window_size: 200
          send_every: 10

I update every second and perhaps with your longer average period that may smooth things out. Otherwise 4% is not the worst thing in the world.

Also you can use the following filter which will ignore any data less than 4% of the last number. You could use combination of filters remembering the order of the filters is important.
- delta: 4%

1 Like

Thanks so much!

I’ve tried this and it’s giving me the 100% value as expected.

Still think something is up with my setup as I re-calibrated everything yesterday and the voltages seem to be stable as per the calibration, then they start to mysteriously increase…

This is the sensor which I’ve been using.
Wondering if I should try buying a new DF Robot Current to Voltage Converter 4-20mA and seeing if this helps - it’s cheaper than buying the whole kit…

The sensor is sitting at the bottom of the tank in an upright position, should I perhaps allow it to float/be suspended mid tank?

Would an increase in temperature possibly cause this?
If so, is there a way to compensate for this…
The temps here have been hovering around 30degrees celcius

Any other suggestions would greatly help…

The raw voltage increases as reported by the ADC? (Not just the output of the filter)

If you have a reasonable meter then do some readings to see if it’s the sensor (so measure the current) or the conversion (voltage on output of the current to voltage) and try and establish which part shows an increase? ie if neither of those it’s the adc. If you’re considering changing the converter then why not get the mA to i2c converter(also DFRobot, “I2C 4-20mA DAC Module (Arduino Compatible)” and collapse one source of error?

I’ve been cycling from 20 to 40C ambient temps on my similar sensor with very little error, and I wouldn’t think that your quantity of water is changing temp that fast anyway? I did see some errors with barometric pressure changes, maybe overlay those graphs too to see if you spot a trend related to that?

1 Like

Hi,
Mr Muttley, I was having simlar issues with the 4-20mA variety. I changed esp boards and added in ADC boards -the list of pieces kept growing.
Then I found Lars Klint posted about a Shelly uni and a sensor that outputs voltage.
I flashed out on more gear and it has been installed for 6 months or more now and I have no issues with the sensor and readings. I am contemplating my power issue (currently a solar panel into a 12v car battery) this at least becomes active once per day and takes my readings required.

1 Like

If you want put a temp sensor in the water tank and then overlay over with your chart above. I would take a week chart to see if it is linked with air pressure and temperatures movements.

Can you remind me how long is the run between the ADS1115 and the current to voltage unit? And can you confirm the rated working pressure range for the pressure sensor.

I do not use current to voltage but ordered the 0-5v variety and have it running over 25 metres straight to the ADS1115. I do get variations but not that much …

You have made me look at my history and I realise my tank has been flooding!!! I am 3.8m inground and water table has been so high that the tank has been gone past the overflow level and filled up to top of turret and the surrounded lawn is now sea level!! I have now a notification from my tank pressure sensor to tell when the garden is flooded!!!

2 Likes