Deep sleep and multiple buttons

Hi,

I have two specific questions about ESPHome deep sleep. I’m trying to make a remote that has three buttons and ESP32. And naturally it should wake up from sleep on any of those and then send mqtt event to Home Assistant.

Currently it sleeps well and wakes up just fine. But I haven’t found a way to tell if it woke up by timer or button. And which button it was. Is there a way to know what input it was?

Second question is about multiple wake up sources. There’s advanced option esp32_ext1_wakeup and it has option pins. But there isn’t any example how multiple pins should be entered in yaml.

Thanks in advance!

It specifies

pins ( Required , list of pin numbers): The pins to wake up on.

So, a list :slight_smile:

Thanks, found specs but didn’t manage to enter multiple values in correct format.
Now understood to look at yaml specs about list. So it should be like pins: [25, 27] ?

I think so, or

pins:
  - 25
  - 27

I find yaml confusing at times. I constantly have to find documentation and remind myself. Either looks OK according to YAML Syntax — Ansible Documentation which was the first google hit on the search term.

lists in yaml

Ok, thanks again!

I tried to search further about deep sleep and it’s wake up reason. There seems to be function esp_sleep_get_wakeup_cause() but didn’t find a reference to it in ESPHome. Maybe it’s not implemented? There is some possibility to use external C++ code as a custom component. Can someone comment if it possible with that?

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.