====== Water Level Sensor ======
===== Motivation =====
Our water supply here in Thailand comes from a deep well (120m) and is stored in two tanks (1000l + 2000l). There's also a 1000l tank acting as a balance reservoir for the pool.
The fill levels of all tanks are controlled by mechanical float switches that turn on the respective pumps as needed. That works reliably most of the time, but a live fill-level display would be genuinely useful — especially once you've already started moving towards a smart home with [[https://www.home-assistant.io|Home Assistant]]. A level sensor was needed.
{{ :media:tech:solarvilla:water_tanks.png?nolink&200 |water tanks}}
===== Approach =====
Ready-made solutions are, as always, quite expensive, and besides, the journey is the destination. I ruled out a mechanical solution from the start to stay maintenance-free.
I also didn't want a solution that requires drilling into the tanks below the maximum water level, where sealing could become a problem.
Since the tank geometry is known, an ultrasonic distance measurement of the water surface is a good fit. Even during pumping the water surface isn't rough enough to cause significant measurement errors.
Examples of DIY solutions can be found here too (some published after my own implementation in 2021):
- [[https://markus-haack.com/watertank-esphome/|Water tank monitoring with ESPHome]]
- [[https://community.home-assistant.io/t/water-tank-level-and-water-volume-with-esphome/192666|Water Tank Level and Water Volume with ESPHome]]
Since we're dealing with water here, the common, cheap HC-SR04 ultrasonic module is out. But there's a similar, waterproof module: the AJ-SR04M.
// AJ_SR04M_Sensor serial format:
// Trigger: 0x00
// Response: Byte1 Byte2 Byte3 Byte4 Byte5
// Start Byte=FF MSB LSB Checksum (LSB+MSB) 00
The result is in millimeters. Thanks to the serial approach the sensor can now be read out with any PC (via USB-serial adapter) or any controller that has a UART.
So far we're only measuring the distance to the water surface. With a bit of math we can convert that distance into liters or a percentage value.
Thanks to [[https://community.home-assistant.io/t/water-tank-level-and-water-volume-with-esphome/192666|Omar Al-Janabi]], who already described the calculation in detail. For the calculation we need the tank depth
esphome:
name: water-level-serial
comment: "Measure water level of water tanks by ultrasonic wave"
includes:
- AJ_SR04M_Sensor.h
Next we add the UART interface. If we don't use pins from a hardware UART, a software UART is automatically implemented. Since I'm connecting a total of 3 modules in my solution, I run out of hardware UARTs anyway.
Finally the sensor implementation. The sensor with ID ''dist1'' provides the measured distance in meters. Using //template sensors// we can then convert that value into a percentage and a liter value as described above.
uart:
- id: uart_dist1
tx_pin: GPIO14
rx_pin: GPIO12
baud_rate: 9600
sensor:
- platform: custom
lambda: |-
auto my_sensor = new AJ_SR04M_Sensor(id(uart_dist1));
App.register_component(my_sensor);
return {my_sensor};
sensors:
unit_of_measurement: m
accuracy_decimals: 3
name: "Water Tank Distance"
id: dist1
- platform: template
name: 'Water Tank Level'
unit_of_measurement: '%'
accuracy_decimals: 0
update_interval: 10s
icon: mdi:water-percent
lambda: |-
return (((175-20)-((id(dist1).state*100)-20))/(175-20))*100;
- platform: template
name: 'Water Tank Volume'
unit_of_measurement: 'l'
icon: mdi:cup-water
accuracy_decimals: 0
update_interval: 10s
lambda: |-
return (((175-20)-((id(dist1).state*100)-20))/(175-20))*1000;
===== Mechanics =====
The sensor head has clip tabs around its edge and is suited for an opening of about 20mm.
{{ :media:tech:waterlevel:size.png?direct&400 |Dimensions}}
In thin-walled tank housings, a matching conduit connector lets you mount the sensor securely while also sheathing the cable with a cable sleeve.
{{ :media:tech:waterlevel:aj_conduit.png?nolink&300 |PVC Conduit}}
Here's the mounting on top of our well tank. This protects the sensor well against mechanical stress and environmental influences. It has now survived more than a year without a single fault.
{{ :media:tech:waterlevel:aj_mounted.jpeg?direct&200 |Mounted on top of the tank}}
In most cases the 2.5m sensor cable will probably be long enough. In my case, unfortunately, the distance to two of the tanks was greater. Attempts to extend the sensor cable introduced measurement inaccuracies. So instead I moved the AJ control board off the sensor and housed it in a small, waterproof enclosure. The photo only shows the inner 3D-printed housing.
{{ :media:tech:waterlevel:aj-adapter.png?direct&200 |3D printed inner housing}}
The ESPHome module sits in a somewhat larger, waterproof enclosure. Its internals sit on a 3D-printed carrier. For two water tanks the serial signal arrives at the screw terminals via a 4-wire cable (with the adapter shown above in between), and the closer water tank is connected directly to the measuring module. As a power supply I always use these Apple-like [[https://www.amazon.de/gp/product/B082XG9Z31/ref=ppx_yo_dt_b_asin_image_o08_s00?ie=UTF8&psc=1|USB power supplies]]. I've modified the input side to use screw terminals. This solution is quite reliable, very cheap, and safe since it's fully enclosed.
{{ :media:tech:waterlevel:aj_esp_module.png?direct&300 |ESPHome module}}
===== Results =====
With the work done, the tank fill levels can now be monitored and actions derived from them. Here's a look at the tidy Home Assistant panel for the outdoor area, showing the tank fill levels live.
{{ :media:tech:waterlevel:aj_panel.png?direct&600 |Panel screen outdoor}}
Of course you can also look at the history:
{{ :media:tech:waterlevel:aj_history.png?direct&600 |Water level history}}
The uneven fill level is due to manual topping-up, since the mechanical limit switch was broken at the time this article was written.
=====Links=====
* [[https://www.home-assistant.io]] Home Assistant
* [[https://esphome.io]] ESPHome
* [[https://airbnb.com/h/villarollokohsamui]] You can also come stay with us...
===== Donate =====
If you'd like to support my work, feel free to buy me a cappuccino or so: .