Home Garden Automation using ESP32 and Servos

I have been running Home Assistant for more than 6 years. I have always wanted to automate my watering systems, but I have always put off because I needed to fix my rainwater tanks. This year I decided I am finally going to do it. After research I found a few videos that I inspired me and gave me ideas that I put together. See the bottom of the article. Here is a video of working.

I made a design first :grin:. The idea is to use an ESP32 to control servos that will open and close water valves. I am using waterproof 20kg servos (see the link below).

I power the ESP32 with a Mini USB power supply. The ESP32 cannot power the servos directly so I am using a 16 Channel PWM Servo Driver Controller Module I2C PCA9685 12bit to power them. Some people say you can power 1 servo with ESP but I found that not true. My ESP32 crashed every time I tried to run 1 servo directly from it. NOTE: you need to ensure you are supplying the right Ampere for both the ESP32 and PCA9685.

Here is the wiring between the ESP32 board and the PCA9685. It is very simple and it worked from the first time for me. NOTE: the servo’s three wires must match the colours of the terminals on PCA9685.

I used ESPHome Builder in Home Assistant to write the code. It took me a few YouTube videos and a few tweaks to get the code right but it is pretty straightforward.

esphome:
  name: esphome-web-54dc14
  friendly_name: Smart Garden Controller
  min_version: 2025.9.0
  name_add_mac_suffix: false


esp32:
  variant: esp32
  framework:
    type: arduino

logger:
api:
ota:
- platform: esphome

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

i2c:
  sda: GPIO18
  scl: GPIO19
  scan: true
  id: bus_a

pca9685:
  id: pca9685_hub1
  i2c_id: bus_a
  address: 0x40
  frequency: 50

output:
  - platform: pca9685
    pca9685_id: pca9685_hub1
    id: pca_channel_0
    channel: 0
  - platform: pca9685
    pca9685_id: pca9685_hub1
    id: pca_channel_1
    channel: 12

servo:
  - id: servo_front_garden
    transition_length: 0s
    auto_detach_time: 5s
    output: pca_channel_0
    min_level: 5%   # Optional - Default 3%
    idle_level: 7.3%  # Optional - Default 7.5%
    max_level: 12.5%  # Optional - Default 12.5%
  - id: servo_back_lawn
    transition_length: 0s
    auto_detach_time: 5s
    output: pca_channel_1
    min_level: 2.5%   # Optional - Default 3%
    idle_level: 7.3%  # Optional - Default 7.5%
    max_level: 12.5%  # Optional - Default 12.5%

cover:
  - platform: template
    name: "Front Garden Valve"
    id: front_garden_valve
    device_class: damper
    has_position: true
    optimistic: true
    assumed_state: false

    open_action:
      - servo.write:
          id: servo_front_garden
          level: 0.0
      - cover.template.publish:
          id: front_garden_valve
          position: 0.0

    close_action:
      - servo.write:
          id: servo_front_garden
          level: 1.0
      - cover.template.publish:
          id: front_garden_valve
          position: 1.0

    stop_action:
      - servo.detach: servo_front_garden
    
    position_action:
      - lambda: |-
          float angle = 0.0 + (pos * 90.0);
          // Add a small “extra close” offset near fully closed
          if (pos < 0.05) {
            angle += 20.0;   // extra 5 degrees beyond closed
          }
          id(servo_front_garden).write(angle / 180.0);
      - delay: 1s
      - servo.detach: servo_front_garden

  - platform: template
    name: "Back Lawn Valve"
    id: back_lawn_valve
    device_class: damper
    has_position: true
    optimistic: true
    assumed_state: false

    open_action:
      - servo.write:
          id: servo_back_lawn
          level: 0.0
      - cover.template.publish:
          id: back_lawn_valve
          position: 0.0

    close_action:
      - servo.write:
          id: servo_back_lawn
          level: 1.0
      - cover.template.publish:
          id: back_lawn_valve
          position: 1.0

    stop_action:
      - servo.detach: servo_back_lawn
    
    position_action:
      - lambda: |-
          float angle = 0.0 + (pos * 90.0);
          // Add a small “extra close” offset near fully closed
          if (pos < 0.05) {
            angle += 5.0;   // extra 5 degrees beyond closed
          }
          id(servo_back_lawn).write(angle / 180.0);
      - delay: 1s
      - servo.detach: servo_back_lawn

I created two covers for the servos that I can control from my HA Dashboard.

Now for the fun part. I was inspired by ericinventor for this. I asked a friend to print some 3D parts to get the servos placed correctly on the water valves.

However, the plastic did not last long

So I modified the design to use the servo metal arms

Once this is working, I got a 4 way water manifold that will connect the pump water supply to the servo valves.

I have installed this where my current water tap is so it is easy to connect to the existing watering systems.

And here is the final working product. I installed an electric junction box to house the ESP32 board and the PCA9685.

I also added an automation to water the Front Garden and the Back Lawn every second day.

alias: Water Front Garden Every Second Day
description: Water front garden every 2 days at 5 AM for 10 minutes
triggers:
  - at: "05:00:00"
    trigger: time
conditions:
  - condition: template
    value_template: "{{ (now().timetuple().tm_yday | int) % 2 == 0 }}"
actions:
  - target:
      entity_id: cover.esphome_web_54dc14_front_garden_valve
    action: cover.open_cover
  - delay:
      hours: 0
      minutes: 10
      seconds: 0
      milliseconds: 0
  - target:
      entity_id: cover.esphome_web_54dc14_front_garden_valve
    action: cover.close_cover
  - action: notify.mobile_app_abdul_iphone
    data:
      message: The front garden has been watered.
      title: Front Garden Watered
mode: single

I would like to create better automation that will take the weather forecast and seasons into account. For example if the rain probability is > 50% then do not water the lawn. Or water only twice a week during winter but every second day during summer.

I would love to hear from other people on how they automate their gardens.

Other preparations:
I had 3 rainwater tanks (a total of 13,000L) in my property that were not being used for years and there were not connected to a water pump. I had to do major work to re-install the tanks first as they have slid down the hill in my back yard over the years. I built a platform for them using recycled retaining wall bricks and crusher dust. This took me weeks to do by hand :sweat_smile:

Then I installed a new pump and connected the outlet to the manifold with the servo valves.

Credit and links:


https://www.ebay.com.au/itm/226315087078
https://www.ebay.com.au/itm/356380928523
https://www.ebay.com.au/itm/286748676049
https://www.ebay.com.au/itm/371778903333

1 Like

Hello,

That’s a fantastic project! Given your success in building the hardware and ESPHome configuration.