Atomated plant watering using esp32

Hallo,
thank your for your example. It helped me a lot!
It didnt worked perfectly because it had still the issue that if the Sensor value was still in the same range it didnt triggered the condition e.g. the pumping.
I modified it and now i have a solution witch is also still triggering if the value doesnt change the range and it is also in deep_sleep after pumping.

deep_sleep:
  run_duration: 30s
  sleep_duration: 2h
  id: deep_sleep1

switch:
  - platform: gpio
    pin: 22
    name: "pumping"
    id: pump1
    restore_mode: ALWAYS_OFF
    icon: "mdi:water-pump"

sensor:
  - platform: adc # airdry: 2,8V; waterwet: 1,58V
    pin: 34
    name: "soil Humidity"
    id: moisturesensor
    update_interval: 10s
    attenuation: auto
    unit_of_measurement: "%"
    filters:
    - median:
        window_size: 7
        send_every: 4
        send_first_at: 1
    - calibrate_linear:
        - 2.80 -> 0.00
        - 1.58 -> 100.00
    - lambda: |
        if (x < 0) return 0; 
        else if (x > 100) return 100;
        else return (x);
    accuracy_decimals: 0
    on_value:
      then:
        - if:
            condition:
              lambda: 'return id(moisturesensor).state < 50;'
            then:
              - deep_sleep.prevent: deep_sleep1
              - switch.turn_on: pump1
              - delay: 5s
              - switch.turn_off: pump1
              - deep_sleep.enter:
                  id: deep_sleep1
                  sleep_duration: 60s
            else:
              - deep_sleep.enter: deep_sleep1

Now i try to find a solution how i can change in the HA UI the Moisture threshold and the Sleep time. My current idea ist this but it doesnt work correctly:

deep_sleep:
  run_duration: 30s
  sleep_duration: 60s
  id: deep_sleep1

switch:
  - platform: gpio
    pin: 22
    name: "pumping"
    id: pump1
    restore_mode: ALWAYS_OFF
    icon: "mdi:water-pump"

  - platform: gpio
    pin: 23
    name: "running"
    id: LED1
    inverted: true

  - platform: gpio
    pin: 14
    name: "dry"
    id: LED2
    on_turn_on:
    - delay: 2s
    - switch.turn_off: LED2

sensor:
  - platform: homeassistant
    name: "Moisture Set Point"
    entity_id: input_number.desired_moisture1
    id: moisture_set
    
  - platform: homeassistant
    name: "Sleep Time"
    entity_id: input_number.desired_sleeptime1
    id: sleeptime_set

  - platform: adc # airdry: 2,8V; waterwet: 1,58V
    pin: 34
    name: "soil Humidity"
    id: moisturesensor
    update_interval: 10s
    attenuation: auto
    unit_of_measurement: "%"
    filters:
    - median:
        window_size: 7
        send_every: 4
        send_first_at: 1
    - calibrate_linear:
        - 2.80 -> 0.00
        - 1.58 -> 100.00
    - lambda: |
        if (x < 0) return 0; 
        else if (x > 100) return 100;
        else return (x);
    accuracy_decimals: 0
    on_value:
      then:
        - logger.log:
            level: DEBUG
            format: "value %.1f"
            args: ['id(moisture_set).state']
        - if:
            condition:
              lambda: 'return id(moisturesensor).state < id(moisture_set).state;'
            then:
              - deep_sleep.prevent: deep_sleep1
              - switch.turn_on: LED2
              - switch.turn_on: pump1
              - delay: 5s
              - switch.turn_off: pump1
              - deep_sleep.enter:
                  id: deep_sleep1
                  sleep_duration: 10s
            else:
              - delay: 30s
              - deep_sleep.enter: deep_sleep1

I still trying to figuring it out where the problem is. Becaus of this there are so many status LEDs and logging code. I just wanted to show my current status. :slight_smile:

The below compiles fine for me. I am guessing it’s the sleep component is the problem, it’s showing that the duration is not templatable. I don’t know anyway to get around that, you’ll have to update the sketch to change that value.

Hello Mikefila,
i could narrow it down to the comparing:

lambda: 'return id(moisturesensor).state < id(moisture_set).state;'

It seems like it doesnt do its job.
For example, this works well:

lambda: 'return id(moisturesensor).state < 50;'

It compiles flawlessly but the esp doesnt trigger the pumping when i use the HA UI to set the threshold higher then the moisture value.

I’m not sure why that doesn’t work, you could try putting that portion in a separate binary sensor.

      - platform: template
        id: dry
        name: "soil dry"
        lambda: |-    
          if (return id(moisturesensor).state < id(moisture_set).state){
            return true;}
            else  {
            return false;
          }

and use the condition

binary_sensor.is_on: dry

Hello,
i think we solved it now. :slight_smile:
I used your idea but at the end i figured it out that there was an issue to update the threshold value from HA. For this i added a condition to check for this value.
Here the code:

deep_sleep:
  run_duration: 30s
  sleep_duration: 60s
  id: deep_sleep1

sensor:
  - platform: adc # airdry: 2,8V; waterwet: 1,58V
    pin: 34
    name: "soil Humidity"
    id: moisturesensor
    attenuation: auto
    unit_of_measurement: "%"
    accuracy_decimals: 0
    update_interval: 5s
    filters:
    - median:   #median over the last 7 values
        window_size: 7
        send_every: 4
        send_first_at: 1
    - calibrate_linear: #setting the voltage values to equalent percantages
        - 2.80 -> 0.00
        - 1.58 -> 100.00
    # limit the value to 0 and 100%
    - lambda: |-
        if (x < 0) return 0; 
        else if (x > 100) return 100;
        else return (x);
    on_value:
      then:
        - if:
            condition:  #check that the threshold value from HA UI is imported
              sensor.in_range:
                id: moisture_set
                below: 100.0
            then:
              - if:
                  condition:  #compare value from sensor with thresholdvalue
                    lambda: 'return id(moisturesensor).state < id(moisture_set).state;'
                  then:
                  - deep_sleep.prevent: deep_sleep1
                  - switch.turn_on: pump1
                  - delay: 10s
                  - switch.turn_off: pump1
                  - deep_sleep.enter:
                      id: deep_sleep1
                      sleep_duration: 30s
                  else:
                  - switch.turn_on: LED2
                  - delay: 15s
                  - switch.turn_off: LED2
                  - deep_sleep.enter: deep_sleep1

  - platform: homeassistant
    name: "Moisture Set Point"
    entity_id: input_number.desired_moisture1
    id: moisture_set
    accuracy_decimals: 0

  - platform: homeassistant
    name: "Sleep Time"
    entity_id: input_number.desired_sleeptime1
    id: sleeptime_set

switch:
  - platform: gpio
    pin: 22
    name: "pumping"
    id: pump1
    restore_mode: ALWAYS_OFF
    icon: "mdi:water-pump"

  - platform: gpio
    pin: 23
    name: "running"
    id: LED1
    inverted: true

  - platform: gpio
    pin: 14
    name: "status"
    id: LED2
    restore_mode: ALWAYS_OFF
1 Like

Hello,
i think i solved it. For the community here my Solution. In the future i will provide a description about the hole setup.
The HA UI that i use is this:
Unbenannt

The esp32 wakes up after the set sleep time and is doing this:

  1. measuring the moisture and sending the value to HA
  2. If its under the set Threshold it pumps for 10s
  3. Sending the last time pumped to HA
  4. If pumped it goes just for a few minutes to deep sleep
  5. Check if it is allowed to go to deep sleep (I have this to be able to update the code)
  6. Only if no pumping is needed it goes to the set duration into deep sleep

In HA i had to make two input.number for the moisture threshold and the Sleep time duration, as well as one input.boolean for the prevention of deep sleep.

The code for the esp32 ist here:
Note: i commented the interesting parts that where complicated.

esphome:
  name: esp32-gieskanne1

esp32:
  board: az-delivery-devkit-v4
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "xxxx"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32-Gieskanne1"
    password: "xxx"

captive_portal:

time:
  - platform: homeassistant
    id: homeassistant_time

deep_sleep:
  run_duration: 30s
  sleep_duration: 5min
  id: deep_sleep1

sensor:
  - platform: adc # airdry: 2,8V; waterwet: 1,58V
    pin: 34
    name: "soil Moisture"
    id: moisturesensor
    attenuation: auto
    unit_of_measurement: "%"
    accuracy_decimals: 0
    update_interval: 5s
    filters:
    - median:   #median over the last 7 values
        window_size: 7
        send_every: 4
        send_first_at: 1
    - calibrate_linear: #setting the volatge values to equalent percantages
        - 2.80 -> 0.00
        - 1.58 -> 100.00
    # limit the value to 0 and 100%
    - lambda: |-
        if (x < 0) return 0; 
        else if (x > 100) return 100;
        else return (x);
    on_value:
      then:
        - if:
            condition:  #check that the threshold value from HA UI is imported
              sensor.in_range:
                id: moisture_set
                below: 100.0
            then:
              - if:
                  condition:  #compare value from sensor with threshold value, it is more complicated that "on_range" bcs it also triggers now when the value didnt changed
                    lambda: 'return id(moisturesensor).state < id(moisture_set).state;'
                  then:
                  - deep_sleep.prevent: deep_sleep1
                  - switch.turn_on: pump1
                  - delay: 10s
                  - switch.turn_off: pump1
                  - sensor.template.publish: #sending Timestamp of pumping to HA UI                                                                                                                                 
                      id: pump1_last_update                                                                                                                                                   
                      state: !lambda 'return id(homeassistant_time).now().timestamp;'
                  - if:
                      condition: #prevent esp32 from entering deep_sleep for updating purpose
                        binary_sensor.is_on: preventdeepsleep_set
                      then:
                      - deep_sleep.prevent: deep_sleep1
                      else:
                      - deep_sleep.enter:
                          id: deep_sleep1
                          sleep_duration: 2min
                  else:
                  - switch.turn_on: LED2
                  - delay: 15s
                  - switch.turn_off: LED2
                  - if:
                      condition: #prevent esp32 from entering deep_sleep for updating purpose
                        binary_sensor.is_on: preventdeepsleep_set
                      then:
                      - deep_sleep.prevent: deep_sleep1
                      else:
                      - deep_sleep.enter:
                          id: deep_sleep1
                          sleep_duration: !lambda 'return id(sleeptime_set).state*60000;' #for converting in min *60000, for converting in h *3600000

  - platform: homeassistant
    name: "Moisture Set Point"
    entity_id: input_number.desired_moisture1
    id: moisture_set
    accuracy_decimals: 0

  - platform: homeassistant
    name: "Sleep Time"
    entity_id: input_number.desired_sleeptime1
    id: sleeptime_set
  
  - platform: template
    name: "Pump1 last watered"
    device_class: timestamp
    id: pump1_last_update

binary_sensor:
  - platform: homeassistant 
    name: "prevent deep_sleep"
    entity_id: input_boolean.prevent_deep_sleep
    id: preventdeepsleep_set

switch:
  - platform: gpio
    pin: 22
    name: "pumping"
    id: pump1
    restore_mode: ALWAYS_OFF
    icon: "mdi:water-pump"

  - platform: gpio
    pin: 23
    name: "running"
    id: LED1
    inverted: true

  - platform: gpio
    pin: 14
    name: "status"
    id: LED2
    restore_mode: ALWAYS_OFF 

Following problems that i struggled with i solved here:

  1. the “on_value_range” command only triggers when the new value is inside the range and the previous value was outside the range. With this it didnt triggered the pumping if the value was still under the threshold. So i used the “on-value” and if condition.
  2. Becuse of the excessive use of deep_sleep for battery saving reasons, often on booting the script had the measured moisture value but dont had the threshold value from HA and went to completly strange conditions. For this i build an if condition to just check the threshold value if its available. (I think i abused it a bit. :slight_smile: )

The part of the code that iam talking about is this:

    on_value:
      then:
        - if:
            condition:  #check that the threshold value from HA UI is imported
              sensor.in_range:
                id: moisture_set
                below: 100.0
            then:
              - if:
                  condition:  #compare value from sensor with threshold value, it is more complicated that "on_range" bcs it also triggers now when the value didnt changed
                    lambda: 'return id(moisturesensor).state < id(moisture_set).state;'

So this is my walk of pain to this solution. I hope it helps further anonymus masochists in the future.
As i mentioned i will make a post in the future about the hole project with Part list and how it is set up in my plants.

5 Likes

@Grieche

Can you also share the YAML for the HA Lovelace card you have shown in your post above above ?

BlockquoteUnbenannt

Have a similar project for automated plant watering if it helps: https://github.com/rasclatt-dot-com/ESP32-Plant-Waterer-for-ESPHome

1 Like

Thanks @Rasclatt!

Will try it out.

Thank you very much for your work! Looks awesome on the first look and was exactly what I was looking for. As a beginner with HA and ESPHome your documentation helps a lot!

Can anyone point me to the right direction how i can integrate the moisture slider in the dashboard ?
I do not see the entities for every “sensor” with platform: homeassistant.
EDIT: I kinda understood it, it is a concept for sending Data from the HomeAssistant to ESPHome Device, so to get the code running one need to create appropriate helper and then add these on the Dashboard.

There are examples of the dashboard configs on the GitHub page

Example dashboard configs
It’s easier to just copy the yaml code rather than use the gui

Sorry for the late answer.
You mean this one?

type: entities
entities:
  - entity: sensor.soil_moisture
    icon: mdi:waves-arrow-up
  - entity: sensor.pump1_last_watered
  - entity: input_number.desired_moisture1
  - entity: input_number.desired_sleeptime1
  - entity: input_boolean.prevent_deep_sleep
title: AZDelivery esp32
1 Like

Hi @Grieche,
Thanks for the nice post !

I converted a WD-01ADE dual pump watering controller. It is equipped with 2 pumps.
Removed the old chip and installed an ESP32. Also wired 2 moisture sensors to ADC inputs.

I tried your code and works flawlessly for a single pump.

Any ideas on how to adapt it for 2 pumps ? I am getting confused with sleep and if 2 sensors can evaluate conditions in parallel.

Thanks !

Hi,
i think if you just copy the code from the adc pin:

sensor:
  - platform: adc # airdry: 2,8V; waterwet: 1,58V
    pin: 34
    name: "soil Moisture"
    id: moisturesensor
    attenuation: auto
    unit_of_measurement: "%"
    accuracy_decimals: 0
    update_interval: 5s
    filters:
    - median:   #median over the last 7 values
        window_size: 7
        send_every: 4
        send_first_at: 1
    - calibrate_linear: #setting the volatge values to equalent percantages
        - 2.80 -> 0.00
        - 1.58 -> 100.00
    # limit the value to 0 and 100%
    - lambda: |-
        if (x < 0) return 0; 
        else if (x > 100) return 100;
        else return (x);
    on_value:
      then:
        - if:
            condition:  #check that the threshold value from HA UI is imported
              sensor.in_range:
                id: moisture_set
                below: 100.0
            then:
              - if:
                  condition:  #compare value from sensor with threshold value, it is more complicated that "on_range" bcs it also triggers now when the value didnt changed
                    lambda: 'return id(moisturesensor).state < id(moisture_set).state;'
                  then:
                  - deep_sleep.prevent: deep_sleep1
                  - switch.turn_on: pump1
                  - delay: 10s
                  - switch.turn_off: pump1
                  - sensor.template.publish: #sending Timestamp of pumping to HA UI                                                                                                                                 
                      id: pump1_last_update                                                                                                                                                   
                      state: !lambda 'return id(homeassistant_time).now().timestamp;'
                  - if:
                      condition: #prevent esp32 from entering deep_sleep for updating purpose
                        binary_sensor.is_on: preventdeepsleep_set
                      then:
                      - deep_sleep.prevent: deep_sleep1
                      else:
                      - deep_sleep.enter:
                          id: deep_sleep1
                          sleep_duration: 2min
                  else:
                  - switch.turn_on: LED2
                  - delay: 15s
                  - switch.turn_off: LED2
                  - if:
                      condition: #prevent esp32 from entering deep_sleep for updating purpose
                        binary_sensor.is_on: preventdeepsleep_set
                      then:
                      - deep_sleep.prevent: deep_sleep1
                      else:
                      - deep_sleep.enter:
                          id: deep_sleep1
                          sleep_duration: !lambda 'return id(sleeptime_set).state*60000;' #for converting in min *60000, for converting in h *3600000

and just adapt to the input from the second Humidity Sensor and Pump it should work for the second pump.
If you want to get the Stats shown on the HA UI and control from there you need also to adapt the “moisture_set” and implement a second one.
I hope this helps.

Thanks !

What I was wondering is specifically about the deep sleep.
On a value change, humidity is tested, pump started (if needed) and then going to sleep.

Should I not check 2 humidity setpoints and go to sleep only once ?

I could try figure out a code out of this and test

Hm… maybe you are right :smiley:
Puhhhh but i dont have an idea how to do it in parallel or seriel but without the deep sleep setting. :thinking:

Hm…
maybe during the check of the first sensor delete all the into deep_sleep stuff?
So the esp checks the first sensor, does his thing and continues to the second one. Only in the scond one you make the deep_sleep check.

Hi. Is hard too cut deep sleep switch and switch off this function? Used esp for other things and is important for me to never deep sleep esp32. I worried to switch on this function not intentionaly.

@Grieche could you please explain the switch part in the esp32 code? You use pin 22 for pump1, pin 23 for LED1 and pin 14 for LED2, but as presented in the schematics at the top- there is only one Input to the Relay from the ESP32 (yellow wire).
Which of these pins is the yellow wire one and what are the other ones?
Can they be any PIN/GPIO?

Hi,
its long time a go and only God knows know the code but i try to explain. :smiley:
Regarding Pin22: This is the Yellow wire witch switches/triggers the relay.
Regarding Pin23 (LED1): This is just a status LED that the ESP is running. Just for myself. Its not relevant.
Regarding Pin14 (LED2): This is my visual feedback that a measuring happened and the moisture was over the threshld and so no pumping was triggered. It is also just a feedback, its not necessary for the code.
You have to look in the scematics of your esp32 witch Pins you can use as switches but you can use an switcheable Pin.
Dos this answer your questions?