So the ESP is going into deep sleep as expected and wakes up when i press the button - so far so good.
i did set up a Automation where i increment a Counter for every press of the button. Works fine when ESP is awake, but desn’t recognize the press that woke up the ESP.
If it’s an ESP32, you can use wakeup_cause to determine if the button push did the wake. You would then increment your counter. If it’s an ESP8266 then I don’t know of a way.
If the ESP is in deep sleep, it will not be connected to HA, and nothing you do in HA will wake it. You either have to wake it with a physical thing connected to the ESP (i.e. the thing you have on GPIO2) or via a schedule that is set on the ESP in the deep sleep config.
Well, that was somewhat embarrassing. I had a situation where my ESP32 rebooted immediately on entering deep_sleep. I trimmed test yaml code down and documented many steps with full logs - that’s why I deleted those 3 posts … too long !
Deep-sleep was OK
Binary_sensor: was OK for GPIO7
… but put them together to wake from deep_sleep when the first bucket of rain tips … well no.
Long story short …
get the sensor working correctly,
then copy the entire binary_sensor: pin: section (including inverted: and pullup mode if used) to deep_sleep: wakeup_pin: section,
remember to add allow_other_uses: true to both.
note that the pulse which triggered the wakeup wasn’t counted, but you can add it from the on_boot: If there was more than one tip in the second it takes ESP32 to wake up, well sorry.
I forgot one thing … In the post above I used a binary_sensor so I could see each tip of the rain gauge as it happened, and got that working with deep_sleep … but in the greenhouse I don’t want to record each individual bucket tip - instead I planned to use pulse_counter and measure the number of tips per minute. So does the same code from binary_sensor above work the same in a pulse_counter ? Not quite.
Curiously during on_boot: GPIO7 is now reported as ON - however that is not triggering immediate wakeup from deep_sleep … so OK.
I have previously worked out code to keep the ESP32 awake while rain is happening and for 5 minutes after. This works well, and I have added it here.
When the rain gauge bucket tips while the ESP32 is in deep_sleep it doeswake up with wakeup_cause = 2. However the pulse did not get counted in the pulse_counter. Fortunately we can detect this scenario in on_boot: and do something about it ourselves - hopefully before more pulses are detected.
Pulses which happen after the ESP32 wakes but before Wi-fi is established are counted.
FYI this is my test YAML code:
#####################################################################
#
# The ESP32-S3-mini board is CURRENTLY used for debugging parts of the greenhouse code
#
#####################################################################
substitutions:
devicename: "s3-test" # last octet of the IP Address
deviceIP: "123" # last octet of the IP Address
wifi_ssid: !secret upstairs_ssid ### which wi-fi to connect to
wifi_password: !secret upstairs_password
update_interval_sensor: "1 min" # How often to measure and report sensor values
update_interval_network: "5 min" # How often to measure and report network values
rain_after_timeout: 5 # how many minutes to wait after rain stops
esphome:
name: $devicename
platformio_options:
board_build.flash_mode: dio
on_boot:
- priority: -100.0
then:
- logger.log: "########## ON_BOOT starts ##########"
# Was wakeup for reason 2 - ESP_SLEEP_WAKEUP_EXT0: Wakeup caused by external signal using RTC_IO
- if:
condition:
lambda: 'return int(esp_sleep_get_wakeup_cause()) == 2;'
then:
# process the rain gauge pulse which woke us up
- lambda: 'id(rain_gauge).publish_state(1.0);'
- logger.log: " >>>> RAIN detected while asleep. <<<<"
- logger.log:
format: ">>>>>> on_boot: initial values pin GPIO7=%s, esp_sleep_get_wakeup_cause()=%d "
args: [ 'id(rain_gauge).state ? "ON" : "OFF"', esp_sleep_get_wakeup_cause() ]
on_shutdown:
- logger.log:
format: ">>>>>> on_shutdown: logger values pin GPIO7=%s, wakeup_cause=%d "
args: [ 'id(rain_gauge).state ? "ON" : "OFF"', esp_sleep_get_wakeup_cause() ]
- logger.log: "##### on_shutdown -100 finished. "
- logger.log: "---------------------------------------------------------------------"
- logger.log: " "
sensor:
# rain gauge hardware sends pulses as the bucket tips.
# this rain_gauge component returns the number of pulses in the preceding update_interval (1 minute)
- platform: pulse_counter
name: Rain Gauge
id: rain_gauge
pin:
number: GPIO7
allow_other_uses: true
inverted: true
mode: # prevent lots of ON/OFF events
input: true
pullup: true
update_interval: 60s # check the rain_gauge every minute
on_value:
then:
# - logger.log: " ********** RAIN GAUGE on_value *****"
- if:
condition:
lambda: 'return x > 0.0;'
then:
# there are rain pulses, so currently raining. Keep re-starting the counter
- logger.log:
format: ">>>>>> NEW RAIN DETECTED counter started, x=%f pulses"
args: [ 'x' ]
- number.set:
id: rain_after_time
value: 1.0
# don't go into deep_sleep, because that will stop the rain counting
- deep_sleep.prevent: deep_sleep_1
else:
- if:
condition:
lambda: 'return id(rain_after_time).state >= $rain_after_timeout;'
then:
# now 5 minutes since last rain detected. reset
- number.set:
id: rain_after_time
value: 0.0
# - logger.log:
# format: ">>>>>> RAIN TIMED OUT <<<<<< Back to normal. reset id(rain_after_time).sta>
# args: [ 'id(rain_after_time).state' ]
- script.execute: check_deep_sleep
else:
# rain has recently stopped ... wait for timeout
- if:
condition:
lambda: 'return id(rain_after_time).state > 0.0;'
then:
# recently stopped raining, so increment timer
- number.increment:
id: rain_after_time
# - logger.log:
# format: ">>>>>> waiting for RAIN TIMEOUT. Currently at %f minutes "
# args: [ 'id(rain_after_time).state' ]
number:
- platform: template
# this is actually a counter of minutes since rain was last detected:
# 0 means no rain detected (can go back to deep_sleep)
# 1 is set whenever rain_gauge detects any rain
# 2-99 is minutes since rain was last detected
id: rain_after_time
name: Rain_after_time
optimistic: true
min_value: 0
max_value: 10
step: 1
script:
- id: check_deep_sleep
then:
# no outstanding reason to prevent deep_sleep, so...
- logger.log: "<<<<< check-deep_sleep >>>>> No active reasons to postpone deep_sleep, so activated"
- logger.log: ""
- deep_sleep.allow: deep_sleep_1
deep_sleep:
id: deep_sleep_1
run_duration: 120000 ms # how long to run (be awake) = 2 minutes
sleep_duration: 60000 ms # how long to sleep for
wakeup_pin: # wake up if a rain gauge pulse is detected
number: GPIO7
allow_other_uses: true
inverted: true
mode: # prevent lots of ON/OFF events
input: true
pullup: false
esp32:
board: esp32-s3-devkitm-1 # ESP32-S3-zero from Core Electronics
variant: esp32s3
framework:
type: esp-idf
version: recommended
logger:
level: DEBUG # VERBOSE
packages:
common_wifi: !include _common_wifi.yaml # uses the deviceIP: substitution
Now to worry about how much keeping the ESP32 awake for the duration of rain will run down the battery
UPDATE: I found 2 problems with the interaction of deep_sleep and pulse_counter:
on_boot: priority -100 is performed after all the setting up is done, including after wi-fi is established (which at my test bench can be a while) … but I want to capture that first bucket tip asap before the pulse_counter starts up. Moving that code to priority 600 seems to do the trick, even if I can’t see my debugging statements.
the updating of pulse_counter and timing of the deep_sleep awake period are not synchronised, so there could be bucket tips (pulses) which have been counted but not yet published when the ESP32 shuts down. Github’s AI copilot gave several solutions, and apologised every time it admitted that it’s “solution” was based on invalid code.
It is not possible to publish the pulse_counter sensor in on_shutdown; so I think I will have to begin deep_sleep just after the pulse-counter is published.
Beginning deep_sleep just after the pulse_counter is published will mean not using the deep-sleep run_duration.
I have been testing this, and present my example code.
I have created a number template awake_timer to count down the time before beginning deep_sleep. In on_boot it is set to 2 minutes, so that I default to take 2 samples (with update_interval 60 seconds) before going back to deep_sleep.
My rain_gauge uses a pulse_counter so that I don’t have to count each individual pulse. Every 60 seconds it publishes the number of pulses that it has counted. To minimise the chance of deep_sleep starting while there are pulses which have been counted but not published, I begin deep_sleep from the on_value: of the rain_gauge.
If there are pulses, it is raining, and so I reset the awake_time … actually I choose to leave the ESP32 awake for 5 minutes to make sure the rain really has stopped.
The GPIO pin which the pulse_counter is connected to can also be used to wake the ESP32. Make sure you have the same pin: parameters and allow_other_uses: true on both the rain_gauge and deep_sleep components. Note that the pulse which wakes the ESP32 - and any other pulses which happen before the ESP is sufficiently awake - will not be counted, and so I have moved it earlier in the on_boot priority. If you need better accuracy the suggestion is to use a hardware counter.
#####################################################################
#
# The ESP32-S3-mini board is CURRENTLY used for debugging parts of the greenhouse code
#
#####################################################################
substitutions:
devicename: "s3-test" # last octet of the IP Address
deviceIP: "123" # last octet of the IP Address
wifi_ssid: !secret upstairs_ssid ### which wi-fi to connect to
wifi_password: !secret upstairs_password
update_interval_sensor: "1 min" # How often to measure and report sensor values
update_interval_network: "5 min" # How often to measure and report network values
rain_after_timeout: 5 # how many minutes to wait after rain stops
awake_time_limit: 2 # how many minutes (rain_gauge cycles) before going back to sleep
esphome:
name: $devicename
platformio_options:
board_build.flash_mode: dio
on_boot:
- priority: 600 # most sensors setup, but before wi-fi connected
then:
# 2 - ESP_SLEEP_WAKEUP_EXT0: Wakeup caused by external signal using RTC_IO
- lambda: |-
id(awake_timer).state = $awake_time_limit; // set default time to be awake (in minutes)
if ( int(esp_sleep_get_wakeup_cause()) == 2 ) {
// process the rain gauge pulse which woke us up asap so we don't miss more tips
ESP_LOGD("", " >>>> RAIN detected while asleep. <<<<" );
id(rain_gauge).publish_state(1.0);
// hopefully the next tip will be recorded by the pulse_counter
ESP_LOGD(" ", " ");
};
on_shutdown:
- logger.log: "##### on_shutdown -100 finished. "
- logger.log: "---------------------------------------------------------------------"
- logger.log: " "
sensor:
# rain gauge hardware sends pulses as the bucket tips.
# this rain_guage component returns the number of pulses in the preceeding update_interval (1 minute)
- platform: pulse_counter
name: Rain Gauge
id: rain_gauge
pin:
number: GPIO7
allow_other_uses: true
inverted: true
mode: # prevent lots of ON/OFF events
input: true
pullup: true
update_interval: 60s # check the rain_gauge every minute
on_value:
then:
- lambda: |-
if ( x > 0.0 ) {
// YES, rain was detected !
ESP_LOGD(" ", ">>>>>> NEW RAIN DETECTED counter started, x=%f pulses", x );
id(deep_sleep_1).prevent_deep_sleep(); // don't go into deep_sleep, because that will stop the rain counting
id(awake_timer).state = $rain_after_timeout; // how many minutes to wait after rain stops
} else if ( id(awake_timer).state < 0.5 ) {
// we have timed out, so go to sleep
ESP_LOGD(" ", ">>>>>> RAIN TIMED OUT <<<<<< Back to normal. " );
// there was no rain this iteration, so doesn't matter if we sleep before the publish
// is sent to HA
id(check_deep_sleep).execute();
} else {
// one more iteration through the loop
id(awake_timer).state-- ;
};
- logger.log: " "
number:
- platform: template
# This is actually a counter of minutes to stay awake - counting DOWN.
# By default it is set in on_boot:; but is reset when rain is detected:
# 0 means it is time to go back to deep_sleep
id: awake_timer
optimistic: true
min_value: 0
max_value: 10
step: 1
script:
- id: check_deep_sleep
then:
# no outstanding reason to prevent deep_sleep, so... start deep_sleep
- logger.log: "<<<<< check-deep_sleep >>>>> No active reasons to postpone deep_sleep, so activated"
- deep_sleep.enter:
id: deep_sleep_1
sleep_duration: 5min
deep_sleep:
id: deep_sleep_1
# timings for deep_sleep moved to be driven by rain_gauge
wakeup_pin: # wake up if a rain gauge pulse is detected
number: GPIO7
allow_other_uses: true
inverted: true
mode: # prevent lots of ON/OFF events
input: true
pullup: false
esp32:
board: esp32-s3-devkitm-1 # ESP32-S3-zero from Core Electronics
variant: esp32s3
framework:
type: esp-idf
version: recommended
logger:
level: DEBUG # VERBOSE
packages:
common_wifi: !include _common_wifi.yaml # uses the deviceIP: substitution