This all started with a co-garden with our neighbor. While he had multiple rain barrels, it took a lot of time and effort to fill and carry watering cans. While I had previously made a system to automatically water a limited area (and automatically fill a small water fountain) using city water, I started to realize that this would quickly add up for our new garden (not to mention be a rather wasteful use of treated water). After doing some pondering and research, I realized that getting a large water cube, a pump and some valves would be both simple and not terribly expensive. By no means am I ‘making money’ off this system, the payback is far too long and I’ll be lucky to break-even, but it's strangely satisfying storing and using rainwater for irrigation (and surprisingly, my wife was excited at the idea of having a large quantity of water stored for emergencies, after proper treatment at least). And honestly, the process has been fun which was the primary motivator.
The first thing I did was find what appeared to be a suitable submersible pump and test it out with our neighbor’s rain barrels. Amazon yielded quite a few inexpensive options, so I did some basic research and just picked one. Being Amazon, the specs were lacking a bit but I was able to deduce what the performance ‘should’ be. Somewhat surprisingly, it ‘just worked’ and produced a reasonable amount of flow. This jump-started the rest of the project and resulted in me purchasing and installing some water cubes while I started planning out the rest of the project. As a side note, admittedly, the pump is the component I’m most concerned about when it comes to long-term operation. It won’t run for that many hours, but it will remain submerged all summer. Hopefully the internal components can handle this, but only time will tell. For control of the pump, I just used a relay inside a junction box to control whether the outlet is on, with the pump ultimately plugged into this.
As I began to plan things out, I decided that I also wanted a way to measure how much water was in the tank. After a bit of research, I landed on a water-proof (or at least resistant) ultrasonic sensor. The main drawback of this approach is that the sensor has nearly a foot minimum distance reading. I had originally hoped/planned to just print a tube to offset it, but quickly realized it created sufficient echos that this wouldn’t work. I came across a similar project using the same sensor which gave me the idea of using a cone and proceeded to design and print a mount for my tank. As I suspected, it didn’t work as-is but I was able to add some transparency sheets to make it useable. The problem I ran into before it even made it off my bench, though, was that it was quite finnicky and I couldn’t reliably get it to work on my desk, much less in the wild. Ultimately, I abandoned this approach and just made another mount that had no offset, deciding to live with the blind spot in my data: this roughly translated into me getting good readings from 0-240 gallons and then getting no further data all the way to completely full, or a little over 310 gallons. Not ideal, but it still allows me to avoid running the pump when it’s empty and know for the most part ‘how full’ the tank is.
I decided quite early on that I wanted to add soil moisture sensors, both to allow for automating the watering as well as just keep tabs on things. I have quite a few of the Mi Flora BLE sensors that I use indoors that I’ve had fantastic luck with, and couldn’t come up with any better alternatives for outdoor use. Ultimately, I designed and printed an enclosure to protect them from the rain; time will tell how effective this is, but I’m fairly confident, especially since they’ll only be outside for about half the year and I slathered them with quite a bit of hot glue.
I already had several solenoid valves and soaker hoses from my previous watering project, so I re-purposed these which was rather straight-forward. The only ‘gotcha’ I ran into, is that at least one of the valves requires water pressure to fully-seal in the off position. When I shut the pump off, this pressure obviously goes away and it leaks very slightly (talking drips). Because of the type of pump, it still works quite well as a siphon when shutoff so it CAN continue to drain my cube if I’m not careful. Luckily, the leaking through the valve is very small such that it’s negligible.
Something I had gone back and forth on initially was whether to essentially use my phone for all manual control or whether I would add some physical buttons. At least for the system watering our shared garden with our neighbor, I landed on adding some physical buttons so that he can easily use the system too (I had briefly considered using a waterproof Bluetooth button located at the hose, but even the cheap ones aren’t ‘that’ cheap). I added 2: one runs a script to toggle just the pump for 15 minutes and the other also opens the valve for the shared garden. It worked out that some buttons I had from previous projects were ‘waterproof’, so I made a small enclosure for them to mount too. They also have an integrated light, which is handy for identifying what the system is currently doing (I was quite surprised how easy it was to see these lights even outside in full-sun). This makes it as simple as walking over to the cube, pushing the ‘Garden’ button and now there’s water pressure at the spigot by the garden for 15 minutes or until the button is pressed again.
For my recent beehive project, I had bought a handful of water-proof temperature sensors. I decided to also include these in this project, partly just ‘because’ but my more rational reason was to allow me to monitor the water temperature during the shoulder seasons (early spring and late fall). We can get below-freezing temperatures both late into the spring and early in the fall, so having a way to see what the actual water temperature is will allow me to make the decision of whether I need to drain the tank or not (i.e. if it’s just barely going to drop below freezing, and I have a large amount of water a bit above freezing, then I’m probably okay).
At this point it was fairly straight-forward to put all the components together and make a functional system. I’ve gotten into the practice of milling my own PCBs for projects, so I did the same for this after designing the board. Early on, I also decided to duplicate my system to allow me to have a storage cube/system both in our back yard (for the garden) as well as front (for other small decorative garden spaces we have there). I finished the back system first before proceeding with the front, to ensure nothing unexpected popped up.
After designing, printing, milling, assembling & installing everything I had a very functional system. I recently installed an in-progress beehive monitor right next to the vegetable garden, which both allowed me to use it as a Bluetooth Proxy for reading the soil sensor(s) as well as a logical place in my Home Assistant frontend to make an ‘outdoor garden’ dashboard.
In addition to the raw data and controls for my system, I also measured up my approximate roof areas and combined this with weather forecast data (using my own weather station, of course) to give me a rough estimate of how many gallons of rainwater I’m likely to collect in the next few days. I went another step further and created an automation to alert me if it looks like the tanks might overflow, giving me a heads up that I might want to pre-emptively use up some stored water so I can maximize my rainwater capture.
While testing out the system, I had the random idea of whether I could use a pressure-washer with it. I had already purchased and installed a water filter (incidentally marketed for pressure washers) on the output of the pump, to keep from clogging any of my sprayers or soaker hoses. We already have an older and inexpensive pressure washer that’s seen better days, so I wasn’t really worried about any potential damage. Unsure if the water pump would provide enough flow or pressure, I decided to just give it a try and to my surprise it worked fantastic; literally no discernible difference compared to using city water (which is at a MUCH higher pressure). I think this will prove to be a great way to use excess stored water before a rainstorm (versus just over-watering the garden).
On the ‘coding’ side, I used ESPHome (which is really all I use anymore for these types of projects, it’s just so quick and easy). I also decided early on that I wanted all control done locally on the ESP, to ensure for instance that when I automatically start watering the garden, the pump indeed shuts off when I want instead of dropping off the network right when Home Assistant sends the ‘off’ command. As a result, I created a handful of scripts on the device to handle all my scenarios and exposed these as service calls within Home Assistant. This also made it very easy for me to add NFC tags in a handful of locations (mainly at the end of some garden hoses), so that one can simply tap their phone to run the desired script. I really like using NFC tags outside, as they’re so cheap they’re essentially free and they’re completely waterproof (not to mention, they’re completely dependent on the HA app, so no concerns of a random person triggering things). I designed a small holder for them with slots for a zip-tie to make attaching them to hoses easy.
Time will tell if any issues pop up, but thus far it’s working quite well (honestly, maybe better than expected). I originally assumed the ultrasonic distance sensor would have a lot of noise and variability, but thus far it’s been quite stable and accurate within a gallon or two (although I haven’t emptied the tanks yet, so the pump, hose and wires may prove to cause issues as it gets low).
ESPHome YAML:
esphome:
name: water-tank-back
friendly_name: Water Tank Back
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
services:
- service: fill_fountain
then:
- script.execute: fill_fountain
- service: water_garden
then:
- script.execute: water_garden
- service: garden_button
then:
- if:
condition:
- script.is_running: garden_button
then:
- script.stop: garden_button
- switch.turn_off: pump
- switch.turn_off: valve_2
- light.turn_off: button_light_2_switch
else:
- script.execute: garden_button
- service: pump_toggle
then:
- if:
condition:
- script.is_running: pump_button
then:
- script.stop: pump_button
- switch.turn_off: pump
- light.turn_off: button_light_1_switch
else:
- script.execute: pump_button
ota:
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Water-Tank--Back"
password: "xxxxxxxxxxxxxxx"
#Removed for Bluetooth Proxy feature
#captive_portal:
bluetooth_proxy:
# active: true
esp32_ble_tracker:
status_led:
pin:
number: GPIO2
inverted: true
button:
- platform: restart
name: Restart
- platform: safe_mode
name: Restart (Safe Mode)
text_sensor:
- platform: wifi_info
ip_address:
name: IP Address
sensor:
- platform: wifi_signal
name: WiFi Strength
update_interval: 60s
disabled_by_default: true
- platform: uptime
name: Uptime
disabled_by_default: true
# Distance Sensor
- platform: jsn_sr04t
name: Tank Distance
id: tank_distance
update_interval: 1s
# internal: true
- platform: template
name: Tank Volume
id: tank_volume
device_class: volume_storage
state_class: measurement
unit_of_measurement: gal
accuracy_decimals: 0
lambda: |-
return id(tank_distance).state;
update_interval: 1s
filters:
- sliding_window_moving_average:
window_size: 60
send_every: 15
send_first_at: 15
- calibrate_linear:
method: least_squares
datapoints:
- 0.37 -> 210
- 1.1684 -> 0
# Temperature Sensors
- platform: dallas
dallas_id: dallas_hub
index: 0
name: Water Temperature
id: water_temperature
# Used for distance sensor
uart:
tx_pin: GPIO4
rx_pin: GPIO13
baud_rate: 9600
# Dallas Hubs for Temp sensors
dallas:
- pin: GPIO16
id: dallas_hub
update_interval: 15s
binary_sensor:
# Buttons
# Button 1 is Pump Button
- platform: gpio
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
name: Button 1
id: button_1
on_press:
then:
- if:
condition:
- script.is_running: pump_button
then:
- script.stop: pump_button
- switch.turn_off: pump
- light.turn_off: button_light_1_switch
else:
- script.execute: pump_button
on_click:
min_length: 5s
max_length: 120s
then:
- script.execute: emergency_shutdown
# Button 2 is Garden Button
- platform: gpio
pin:
number: GPIO25
inverted: true
mode:
input: true
pullup: true
name: Button 2
id: button_2
on_press:
then:
- if:
condition:
- script.is_running: garden_button
then:
- script.stop: garden_button
- switch.turn_off: pump
- switch.turn_off: valve_2
- light.turn_off: button_light_2_switch
else:
- script.execute: garden_button
on_click:
min_length: 5s
max_length: 120s
then:
- script.execute: emergency_shutdown
light:
# Button Lights
# Button 1 is Pump Button
- platform: binary
name: Button Light 1
output: button_light_1
id: button_light_1_switch
restore_mode: ALWAYS_OFF
# internal: true
# Button 2 is Garden Button
- platform: binary
name: Button Light 2
output: button_light_2
id: button_light_2_switch
restore_mode: ALWAYS_OFF
# internal: true
output:
# Button Lights
- id: button_light_1
platform: gpio
pin: GPIO26
- id: button_light_2
platform: gpio
pin: GPIO27
switch:
# Pump Relay
- platform: gpio
pin: GPIO17
name: Pump
id: pump
device_class: switch
icon: mdi:pump
restore_mode: ALWAYS_OFF
# Valves
#Valve 1 is fountain
- platform: gpio
pin: GPIO18
name: Valve 1
id: valve_1
device_class: switch
icon: mdi:pipe-valve
restore_mode: ALWAYS_OFF
#Valve 2 is garden
- platform: gpio
pin: GPIO19
name: Valve 2
id: valve_2
device_class: switch
icon: mdi:pipe-valve
restore_mode: ALWAYS_OFF
- platform: gpio
pin: GPIO21
name: Valve 3
id: valve_3
device_class: switch
icon: mdi:pipe-valve
restore_mode: ALWAYS_OFF
- platform: gpio
pin: GPIO22
name: Valve 4
id: valve_4
device_class: switch
icon: mdi:pipe-valve
restore_mode: ALWAYS_OFF
# Scripts used to cycle pump/valves for a limited time
script:
- id: emergency_shutdown
mode: single
then:
- script.stop: garden_button
- script.stop: pump_button
- script.stop: fill_fountain
- script.stop: water_garden
- switch.turn_off: pump
- switch.turn_off: valve_1
- switch.turn_off: valve_2
- switch.turn_off: valve_3
- switch.turn_off: valve_4
- light.turn_off: button_light_1_switch
- light.turn_off: button_light_2_switch
- id: garden_button
mode: single
then:
- switch.turn_on: pump
- switch.turn_on: valve_2
- light.turn_on: button_light_2_switch
- delay: 15min
- switch.turn_off: pump
- switch.turn_off: valve_2
- light.turn_off: button_light_2_switch
- id: pump_button
mode: single
then:
- switch.turn_on: pump
- light.turn_on: button_light_1_switch
- delay: 15min
- switch.turn_off: pump
- light.turn_off: button_light_1_switch
- id: fill_fountain
mode: single
then:
- switch.turn_on: pump
- switch.turn_on: valve_1
- delay: 110s
- switch.turn_off: pump
- switch.turn_off: valve_1
- id: water_garden
mode: single
then:
- switch.turn_on: pump
- switch.turn_on: valve_2
- delay: 10min
- switch.turn_off: pump
- switch.turn_off: valve_2