Deep sleep and multiple buttons

Ok, now I found that ESPHome lambda is C++ code and should be possible to just use that function that way. So writing on_boot trigger next…

How did you go with this? I have an application that needs to know if it woke via pin or timer. Any luck with lambda function?

Hi, thanks for asking. Didn’t get it to work completely. Below script works as detecting input reason and input that caused wakeup. But I could not figure out how to bring that out of lambda. Then went to other projects. So code below should work on ESP32 except those publish.state lines. I think those logging lines worked just fine.

esphome:
  name: $devicename
  platform: ESP32
  board: lolin32
  on_boot:
    priority: -500
    then:
      - lambda: |-
          int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
          if (((log(GPIO_reason))/log(2)) == 25) {
            id(btn25).publish_state(true);
            ESP_LOGD("main", "Button 3 wakeup");
          }
          if (((log(GPIO_reason))/log(2)) == 27) {
            id(btn27).publish_state(true);
            ESP_LOGD("main", "Button 1 wakeup");
          }


Would something like this work?

sensor:
  - platform: template
    name: "Remote Control Wake Reason"
    lambda: |-
      int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
      if (((log(GPIO_reason))/log(2)) == 25) {
        return 25;
        ESP_LOGD("main", "Button 3 wakeup");
      }

      if (((log(GPIO_reason))/log(2)) == 27) {
        return 27;
        ESP_LOGD("main", "Button 1 wakeup");
      }

Then just use the state change on that sensor in HA accordingly to fire off your autos?

…perhaps this simpler version may also work if logging isn’t needed (sensor state changes are logged by default already)?

sensor:
  - platform: template
    name: "Remote Control Wake Reason"
    lambda: |-
      int GPIO_reason = esp_sleep_get_ext1_wakeup_status();
      return ((log(GPIO_reason))/log(2));

Of course, you could make binary templates for each button, but I figure that’s just more complicated vs having a single integer to track.

Thanks @truglodite & @drogfild, you have given me a few things to try.

If I have no success with ESPhome I’ll just code the whole thing in C++,

I’ve cracked it! I have been trying to get this working on and off for about 6 months. Thanks @drogfild
& @truglodite for pointing me in the right direction.

The solution was to use a Global variable in the on_boot and add a sensor template that returns the Global variable. I also ended up using a priority of 900 but haven’t tried changing it to see if it makes a difference.

I’m using ext0 so use esp_sleep_get_wakeup_cause() but it should work for ext1 with esp_sleep_get_ext1_wakeup_status()

My configuration is:

  on_boot:
    priority: 900
    then:
      lambda: |-
        id(wake_up_reason) = esp_sleep_get_wakeup_cause();

deep_sleep:
  run_duration: 2min
  sleep_duration:
    days: 1
  wakeup_pin:
    number: GPIO32
    inverted: True
    mode: INPUT_PULLUP
  wakeup_pin_mode: KEEP_AWAKE
  id: deep_sleep_1

globals:
   - id: wake_up_reason
     type: int
     restore_value: no
     initial_value: '0'

sensor:
  - platform: template
    name: "Mail Wake Reason"
    accuracy_decimals: 0
    lambda: |-
      return (id(wake_up_reason));

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO32
      inverted: True
      mode: INPUT_PULLUP
    name: "Mail Notification"
    id: mail_notification
    device_class: occupancy

3 Likes

Nice! Thanks for sharing!

Ok - trying this tomorrow. Thanks.

Ran into this as well, I simply needed to distinguish between IO and RTC, here is some intermediate code to build from:

esphome:
  name: ${plug_name}
  platform: ESP32
  board: esp-wrover-kit
  on_boot:
    priority: -100
    then: 
     - mqtt.publish:
              topic: blah/test2
              payload: !lambda |-
                     esp_sleep_wakeup_cause_t wakeup_reason;
                     wakeup_reason = esp_sleep_get_wakeup_cause();
                        
                     switch(wakeup_reason)
                          {
                            case ESP_SLEEP_WAKEUP_EXT0 : return "Wakeup caused by external signal using RTC_IO"; break;
                            case ESP_SLEEP_WAKEUP_EXT1 : return "Wakeup caused by external signal using RTC_CNTL"; break;
                            case ESP_SLEEP_WAKEUP_TIMER : return "Wakeup caused by timer"; break;
                            case ESP_SLEEP_WAKEUP_TOUCHPAD : return "Wakeup caused by touchpad"; break;
                            case ESP_SLEEP_WAKEUP_ULP : return "Wakeup caused by ULP program"; break;
                            default : return "Wakeup was not caused by deep sleep"; break;
                          }
2 Likes

Thanks! Looks good. This works right after startup and posts MQTT message once? Have you tried same but with API?

When your device wakes up for deepsleep, home assistant needs to initiate the connection to the api, and this can, in my experience take up to a minute. This is why i am using mqtt for this particular device (as advised on the esphome discord)

This should be workable on the api too though, if home assistant has had time to connect through the api. might be a bit tricky to reliably detect if home assistant has connected (there is a api.connected condition, but that checks connected clients and the esphome log viewer is a client as well…

Maybe try this for starters and go from there? (I am just writing this up here, likely has some issues)

esphome:
  name: ${plug_name}
  platform: ESP32
  board: esp-wrover-kit
  on_boot:
    priority: -100
    then: 
     #wait for api to come up
     - delay: 60s
     - lambda: |-
                     esp_sleep_wakeup_cause_t wakeup_reason;
                     wakeup_reason = esp_sleep_get_wakeup_cause();
                        
                     switch(wakeup_reason)
                          {
                           //let's update state of a sensor when wake up from RTC_IO
                            case ESP_SLEEP_WAKEUP_EXT0 : return "Wakeup caused by external signal using RTC_IO"; 
                            id(my_binary_sensor).publish_state(true);
                            break;
                            case ESP_SLEEP_WAKEUP_EXT1 : return "Wakeup caused by external signal using RTC_CNTL"; break;
                            case ESP_SLEEP_WAKEUP_TIMER : return "Wakeup caused by timer"; break;
                            case ESP_SLEEP_WAKEUP_TOUCHPAD : return "Wakeup caused by touchpad"; break;
                            case ESP_SLEEP_WAKEUP_ULP : return "Wakeup caused by ULP program"; break;
                            default : return "Wakeup was not caused by deep sleep"; break;
                          }
- mqtt.publish:
              topic: blah/test2
              payload: !lambda |-
                     

you can insert any lambda into that logic switch case statement, for example manuallly publish a new status to a sensor component. I really wouldn’t do that delay 60s myself (other than for basic troubleshooting) not sure what the best way is to ensure home assistant is connected

API is not recommended in combination with deep sleep.

I realize this is an old topic, but it was very useful for me - and practically the only helpful Google hit I got. Thanks, all.

I will note that to get this to work with mode: ANY_HIGH and esp_sleep_get_ext1_wakeup_status(), I had to add external pulldown resistors (pin to ground; I used 330 ohm for now) to each of the GPIO pins I was using. Otherwise the chip woke immediately from deep sleep, presumably because the pin value was floating. If there’s a way to use the ESP32’s internal pulldown resistors with ANY_HIGH I couldn’t find it.

I happened to hit the same issue yesterday.
I was able to use the internal pulldown resistors with ext1 wakeup using the following:

  on_boot:
    - priority: 900
      then:
        - lambda: esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
2 Likes

Ah, excellent; that would simplify the system. I’ll give it a try on my next build.

Was wondering if you’d share your full yaml for point of reference for a novice users like myself.

1 Like

I’m still trying to figure out how to trigger the wake-up from multiple pins (32 & 26). Would anyone be able to share a snippet of their deep_sleep yaml on a configuration with multiple pins?

Hello,

I’m taking some inspiration from this project, but I want to make mine wireless and thus use a battery which limits powered on time. I have looked through a couple threads, but still not making progress.

I’m using an ESP32 with 6 capacitive touch sensors and I want to know which one woke up the ESP32 so I can perform different actions.

Here is my code so far - granted this only has 2 touch buttons, but once I figure out two I can add more. :

esphome:
  name: esp-touch
  on_boot:
    priority: 900
    then:
      lambda: |-
        id(wake_up_reason) = esp_sleep_get_wakeup_cause();


esp32:
  board: esp32dev
  framework:
    type: arduino


# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "6cbeb276258987c189ca430f8a73ad1d"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp-Touch Fallback Hotspot"
    password: "X5MyBdidHG69"


captive_portal:

deep_sleep:
  run_duration: 10s
  sleep_duration: 10min
  esp32_ext1_wakeup:
    pins:
      - 4
      - 15
    mode: ANY_HIGH

globals:
   - id: wake_up_reason
     type: int
     restore_value: no
     initial_value: '0'  

sensor:
  - platform: template
    name: "Touch Reason"
    accuracy_decimals: 0
    lambda: |-
      return (id(wake_up_reason));     


# Example configuration entry
esp32_touch:
  setup_mode: false

binary_sensor:
  - platform: esp32_touch
    name: "ESP32 Touch Pad GPIO4"
    pin: GPIO4
    threshold: 1000

  - platform: esp32_touch
    name: "ESP32 Touch Pad GPIO15"
    pin: GPIO15
    threshold: 1000    

Any help would be really appreciated and I’m sure others will benefit as I have seen this question asked in many places.

Hello,

I’m also trying to figure out how to trigger wake-up from multiple pins (32 + 35) using a esp32 d1 mini.

GPIO 32 => PIR Motion Dedector
GPIO 35 => Push Button with external pull down resistor

I’m using the following the following configuration:

deep_sleep:
  id: deep_sleep_1
  run_duration: 120s
  esp32_ext1_wakeup:
    pins: 
      - 35
      - 32
    mode: ANY_HIGH

Boot:

  on_boot:
    priority: -100
    then:
      - mqtt.publish:
              topic: wakeup/test2
              payload: !lambda |-
                     return "Reason: " + esp_sleep_get_ext1_wakeup_status();

The wakeup it self is working fine on both pins. But when I check the MQTT Message “esp_sleep_get_ext1_wakeup_status();” returns no value.

Nachricht 10 empfangen auf wakeup/test2 um 9:21:
Reason: 

ok, the reason seems to be:
if you are using GPIO’s > 30 you can’t use an int as variable, because it would overflow the range.

you need to use unit64_t