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