Using a physical pushbutton in ePaper ESP32 driver board

Hello,
I am trying to get a Waveshare ePaper running with a waveshare driver board:

I have a printout on the screen and the refresh pin (17) and “book now” pin (16) work perfect. If I press the physically attached pushbutton, the screen gets either refreshed (thru HomeAssistant magic, because I need to get some data from HomeAssistant first) and than displayed or a slot is booked in HA and than the new data is displayed on the ePaper.

As I do not want to run it on power but on battery, my next step was to try to hook up deep sleep mode. I added:

deep_sleep:
  run_duration: 40s  # Stay awake for 40 seconds
  sleep_duration: 25min  # Sleep duration
  wakeup_pin: 
    number: GPIO4 # Wakeup when GPIO4 is pressed (pulled LOW)
    inverted: True
    mode: INPUT_PULLUP
  wakeup_pin_mode: KEEP_AWAKE
  id: ${rmgr_device_name}_deep_sleep

and the ESP goes into deep sleep and waked up after 25 minutes, get the new data and continues sleeping. So far so nice.

But I do need, that a person standing in front of the ePaper can press any physical pushbutton, than the ESP wakes up, gets the data, displays the actual status (like waking up after a certain amount of time). But I cannot get this “wakeup physical pushbotton” to work.

I tried differend GPIOs, and stuck to GPIO4 because accordingh to what I understand, it is just “close” to 16/17 so I can avoind counting the wrong pin. So now if I use my connection with IO17 it refreshed the screen, if I use the same physical connection on IO4 it simply does nothing, if I only use the deep_sleep configuration from above.

If I add

# wakeup Pin
  - platform: gpio
    name: "${rmgr_device_name}_wakeup"
    pin:
      number: GPIO4
      inverted: true
      mode: 
        input: True
        pullup: True
    filters:
      - delayed_on: 10ms

down at the binary section definitions which work with GPIO16 and 17, I get the following error from the compiler:

INFO ESPHome 2025.2.2
INFO Reading configuration /config/esphome/framery1.yaml...
WARNING GPIO15 is a strapping PIN and should only be used for I/O with care.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
Failed config

deep_sleep: [source /config/esphome/common/calendar_free_busy_display.yaml:51]
  
  Pin 4 is used in multiple places.
  run_duration: 40s
  sleep_duration: 25min
  wakeup_pin: 
    number: 4
    inverted: True
    mode: 
      input: True
      pullup: True
      output: False
      open_drain: False
      pulldown: False
    ignore_pin_validation_error: False
binary_sensor.gpio: [source /config/esphome/common/calendar_free_busy_display.yaml:256]
  
  Pin 4 is used in multiple places.
  platform: gpio
  name: framery1_wakeup
  pin: 
    number: 4
    inverted: True
    mode: 
      input: True
      pullup: True
      output: False
      open_drain: False
      pulldown: False
    ignore_pin_validation_error: False

if I do not add it, I get “no reaction” from ppushing the button.

I have absolutely no idea, what I have to do. Found some “esp_deep_sleep” like

https://community.home-assistant.io/t/deep-sleep-and-multiple-buttons/
or
https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/

but do not get what I do different than what is shown there.

Thanks you for any help
Juergen

Here my full ESPHome Yaml.

# Calendar / Free Busy DASHBOARD
#  For Home Assistant and ESPHome
# Inspired by WEATHERMAN by Madelena Mak 2022 - https://mmak.es
#
#######################
# VARIABLES passed into the system:
# - rmgr_device_name: the common prefix for sensors etc (still a bad name)
#
# ESPHome for battery:
# https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/

# Core Configuration Section: https://esphome.io/components/esphome.html
esphome:
  # https://esphome.io/components/esphome.html#changing-esphome-node-name
  name: "${rmgr_device_name}"
  # name is defined in specific instance config (e.g. framery1.yaml etc)

  # https://esphome.io/components/esphome.html#on-boot
  # on_boot is an *Automation* (https://esphome.io/automations/#automation)
  on_boot: # == "Trigger"
    priority: 200.0
    then: # == "Actions"
      - component.update: epaper_screen_print
      - logger.log: "Forcing API reconnect after boot..."
      - delay: 5s

        # this triggers a reload in Home Assistant (which pushes the data down to the device)
#        - homeassistant.event:
#            event: "esphome.${rmgr_device_name}.booted"

# https://esphome.io/components/esp32.html
esp32:
  # "if unsure choose a generic board from Espressif such as esp32dev."
  board: esp32dev
  framework:
    type: arduino


# Enable logging
# https://esphome.io/components/logger.html
logger: 
#  level: NONE
#  level: ERROR
#  level: WARN
#  level: INFO
  level: DEBUG
#  level: VERBOSE
#  level: VERY_VERBOSE

# Deep Sleep Configuration
deep_sleep:
  run_duration: 40s  # Stay awake for 40 seconds
  sleep_duration: 25min  # Sleep duration
  wakeup_pin: 
    number: GPIO4 # Wakeup when GPIO4 is pressed (pulled LOW)
    inverted: True
    mode: INPUT_PULLUP
  wakeup_pin_mode: KEEP_AWAKE
  id: ${rmgr_device_name}_deep_sleep



# Global variables for detecting if the display needs to be refreshed
# and other things :-)

globals:
  - id: initial_data_received
    type: bool
    restore_value: no
    initial_value: 'false'

  - id: headline_text
    type: std::string
    restore_value: no
  - id: subheadline_text
    type: std::string
    restore_value: no

  - id: timeslots
    type: std::vector<std::string>
    restore_value: no
  - id: is_occupied
    type: std::vector<bool>
    restore_value: no
  - id: rmgr_booking_slot_length_minutes
    type: std::string
    restore_value: no
  - id: last_display_refresh
    type: std::string
    restore_value: no


# Enable Home Assistant API
# https://esphome.io/components/api.html


api:
#   on_client_connected:
#     then:
#       - logger.log: "Connected to HomeAssistant. -> booted"
#       - homeassistant.event:
#           event: esphome.${rmgr_device_name}.booted
#       - logger.log: "simulte refresh to refresh the screen on first connect"
#       - homeassistant.event:
#          event: esphome.${rmgr_device_name}_refresh_clicked

  services:
    - service: "draw_schedule"
      variables:
        param_headline_text: string
        param_subheadline_text: string
        # !!! you need the same number of entries in param_timeslots and param_is_occupied
        param_timeslots: string[]
        param_is_occupied: bool[]
        param_rmgr_booking_slot_length_minutes: string
        param_last_display_refresh: string

      then:
        - lambda: 'id(initial_data_received) = true;'
        - lambda: 'id(headline_text) = param_headline_text;'
        - lambda: 'id(subheadline_text) = param_subheadline_text;'
        - lambda: 'id(timeslots) = param_timeslots;'
        - lambda: 'id(is_occupied) = param_is_occupied;'
        - lambda: 'id(rmgr_booking_slot_length_minutes) = param_rmgr_booking_slot_length_minutes;'
        - lambda: 'id(last_display_refresh) = param_last_display_refresh;'
        - logger.log: ".-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. calfreebusy_draw_schedule Data"
        - logger.log:
            format: "param_headline_text has value >%s<"
            args: [ 'id(headline_text).c_str()' ]
        - logger.log:
            format: "param_subheadline_text has value >%s<"
            args: [ 'id(subheadline_text).c_str()' ]
        - logger.log:
            format: "param_timeslots has value >%i<"
            args: [ 'id(timeslots)' ]
        - component.update: epaper_screen_print

# https://esphome.io/components/ota/esphome
ota:
  - platform: esphome

# ota turns safe_mode on, which we do not want.
safe_mode:
  boot_is_good_after: 30s

# Wifi information
wifi:
  networks:
#    channel: 11
    ssid: !secret wifi_ssid
    password: !secret wifi_password
  manual_ip:
    static_ip: 172.16.0.62
    gateway: 172.16.0.1
    subnet: 255.255.0.0
    dns1: 172.16.0.1
    dns2: 0.0.0.0



# Enable fallback hotspot (captive portal) in case wifi connection fails
# does not work -> error before compiling
# ap:
#   ssid: ${rmgr_device_name}_ssid
#   password: !secret wifi_password


# Include custom fonts
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 18
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_large_bold
    size: 108
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_title
    size: 54
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', 'u', 'p', 'd', 't', '%']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_medium_bold
    size: 30
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
  - file: 'fonts/GothamRnd-Bold.ttf'
    id: font_small_bold
    size: 18
    glyphs: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']

  # Include Material Design Icons font
  # Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medlarge
    size: 18
    glyphs:
      - "\U000F05A9" # mdi-wifi
      - "\U000F0928" # mdi-wifi-strength-4
      - "\U000F0925" # mdi-wifi-strength-3 
      - "\U000F0922" # mdi-wifi-strength-2
      - "\U000F091F" # mdi-wifi-strength-1
      - "\U000F092B" # mdi-wifi-strength-alert-outline


# Check if epaper should be refreshed

sensor:
  
  - platform: wifi_signal
    name: "${rmgr_device_name} - WiFi Signal Strength"
    id: "${rmgr_device_name}_wifisignal"
    unit_of_measurement: "dBm"
    entity_category: "diagnostic"
    update_interval: 600s

binary_sensor:
# refresh durch HomeAssistant
  - platform: homeassistant
    entity_id: input_boolean.${rmgr_device_name}_refresh_clicked
    id: ${rmgr_device_name}_update_epaper_display
# book durch Button am ESP
  - platform: gpio
    name: "${rmgr_device_name}_book_now"
    pin:
      number: GPIO16
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on: 10ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_doubleclicked
      - timing: # INFO: this is currently not used functionally.
          - ON for 1s to 2s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Long Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_longclicked
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Short Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_booknow_clicked
# wakeup Pin
  - platform: gpio
    name: "${rmgr_device_name}_wakeup"
    pin:
      number: GPIO4
      inverted: true
      mode: 
        input: True
        pullup: True
    filters:
      - delayed_on: 10ms

# refresh durch Button am ESP
  - platform: gpio
    name: "${rmgr_device_name}_refresh"
    pin:
      number: GPIO17
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on: 10ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_refresh_doubleclicked
      - timing: # INFO: this is currently not used functionally.
          - ON for 1s to 2s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Long Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_refresh_longclicked
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "Single Short Clicked"
          - homeassistant.event:
              event: esphome.${rmgr_device_name}_refresh_clicked

  - platform: status
    name: "${rmgr_device_name} Connected"
    id: ${rmgr_device_name}_connected

# Pins for Waveshare ePaper ESP Board
spi:
  clk_pin: GPIO13                 # SCLK 	P13 	SPI CLK pin, clock signal input
  mosi_pin: GPIO14                # DIN 	P14 	SPI MOSI pin, data input

# Now render everything on the ePaper screen.
display:
  - platform: waveshare_epaper
# models from https://esphome.io/components/display/waveshare_epaper
#    model: 7.50inV2   # s/w board   evtl. älterer Treiber ???
#    model: 7.50in-nV2   # s/w board   
    model: 7.50in-bV3  # r/s/w board

    id: epaper_screen_print
    cs_pin: GPIO15                # CS 	P15 	Chip selection, low active
    dc_pin: GPIO27                # DC 	P27 	Data/command, low for commands, high for data
    busy_pin: 
      number: GPIO25              # BUSY 	P25 	Busy status output pin (means busy) 
      inverted: true
    reset_pin: GPIO26             # RST 	P26 	Reset, low active
    reset_duration: 2ms
    update_interval: never
    rotation: 90°

    lambda: |-
      int offsetY = 40;
      int offsetX = 0;
      
      //# Print loading screen before data is received.
      if (id(initial_data_received) == false) {
        it.printf(240, 390, id(font_small_bold), TextAlign::TOP_CENTER, "WARTE AUF DATEN");
        ESP_LOGD("display","Warte auf Daten");
      } else { 

        // headline
        int lineHeight = 35;
        it.printf(50, offsetY, id(font_medium_bold), TextAlign::TOP_LEFT, "%s", id(headline_text).c_str());
        offsetY += lineHeight;
        
        // subheadline
        lineHeight = 22;
        it.printf(150, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(subheadline_text).c_str());
        offsetY += lineHeight;

        lineHeight = 22;
        for (size_t i = 0; i < id(timeslots).size(); ++i) {
          offsetY += lineHeight;
          it.printf(50, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(timeslots)[i].c_str());
          if (id(is_occupied)[i]) {
            it.filled_rectangle(120, offsetY, 100, lineHeight);
          }
        }
      }



      //# decide for the WiFi-signal Symbol
      //# is printed printed below
      std::string wifiSymbol = "";
      offsetX = it.get_width() - 50;
      offsetY = it.get_height() - 150;
      if(id(${rmgr_device_name}_wifisignal).has_state ()) {
        if (id(${rmgr_device_name}_wifisignal).state >= -50) {
            // Excellent
            wifiSymbol = "\U000F0928";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -60) {
            //Good
            wifiSymbol = "\U000F0925";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -67) {
            //Fair
            wifiSymbol = "\U000F0922";
        } else if (id(${rmgr_device_name}_wifisignal).state  >= -70) {
            //Weak
            wifiSymbol = "\U000F091F";
        } else {
            //Unlikely working signal
            wifiSymbol = "\U000F092B";
        }
      }

      // Print booking slot length + Last refresh + Wifi
      // we write from bottom to top 
      offsetX = it.get_width() - 50;
      offsetY = it.get_height() - 130;
      int lineHeight = 22;
      // Print DBI and Wifi Signal
      it.printf(offsetX - 25, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%.0f dB", id(${rmgr_device_name}_wifisignal).state);
      it.printf(offsetX, offsetY, id(font_mdi_medlarge), TextAlign::TOP_RIGHT, "%s", wifiSymbol.c_str());
      // 
      it.printf(offsetX - 150, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(rmgr_booking_slot_length_minutes).c_str());
      offsetY -= lineHeight;
      it.printf(offsetX, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(last_display_refresh).c_str());


captive_portal:

I’n not able to follow your description…
Anyway, if you use same pin with multiple components

you need to add thist to pin configurations:
allow_other_uses: true

1 Like

Hi @Karosm
you are my hero! Made my day. I have had this before, but got other errors, so I forgot about it. It works. I am so happy!
Many thanks!!!

Juergen

You are welcome!