# Andrew Villeneuve 2026/05 # Waveshare 2.9inch Touch ePaper HAT integration demo # # Platform: https://www.waveshare.com/2.9inch-Touch-e-Paper-HAT.htm # # Pin mappings # --- # |Function |ESP Pin |HAT Pin |Wire | # |--- |--- |--- |--- | # |5V |VIN |Pin2/5V |Red | # |GND |GND |Pin6/GND |Blk | # |Display SCLK |GPIO18 |Pin23/GPIO11/SPI0_SCLK |Blue | # |Display MOSI |GPIO23 |Pin19/GPIO10/SPI0_MOSI |Green | # |Display CS |GPIO19 |Pin24/GPIO8/SPI0_CE0 |Mgnta | # |Display DC |GPIO17 |Pin22/GPIO25 |White | # |Display Busy |GPIO4 |Pin24/GPIO24 |Brown | # |Display RST |GPIO5 |Pin11/GPIO17/SPI1_CE1 |Orange | # |Touch SDA |GPIO21 |Pin3/GPIO2/I2C1_SDA |Gray | # |Touch SCL |GPIO22 |Pin5/GPIO3/I2C1_SCL |Purple | # |Touch RST |GPIO15 |Pin13/GPIO27 |Blue | # |Touch INT |GPIO16 |Pin15/GPIO22 |White | # # Exposed Entities # --- # ### Boilerplate ### esphome: name: waveshare-epaper-test esp32: board: nodemcu-32s framework: type: esp-idf # Enable logging logger: level: DEBUG # Enable Home Assistant API api: encryption: key: !secret enckey ota: - platform: esphome password: !secret apipw wifi: ssid: !secret wifi_ssid password: !secret wappw # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: ${fallbackssid} password: !secret hotspotpw captive_portal: ### Setup Interfaces ### # For the display spi: clk_pin: GPIO18 mosi_pin: GPIO23 # For the touchscreen i2c: id: i2c_bus sda: GPIO21 scl: GPIO22 scan: false touchscreen: platform: icnt86x id: touchscreen0 address: 0x48 reset_pin: GPIO15 update_interval: 50ms calibration: x_min: 0 x_max: 295 y_min: 0 y_max: 127 transform: mirror_x: true mirror_y: true ### Okay, now the Good Stuff ### # Load some fonts for the display renderer (we don't neeed these with LVGL) font: - file: "FreeSans.ttf" id: font1 size: 26 bpp: 2 - file: "FreeSans.ttf" id: headerfont size: 80 - file: "FreeSans.ttf" id: console size: 30 # Pull in some icons from the material design library image: defaults: transparency: chroma_key invert_alpha: false resize: 54x54 binary: - file: "mdi:home-lock-open" id: home_lock_open - file: "mdi:home-lock" id: home_lock - file: "mdi:ceiling-fan" id: ceiling_fan - file: "mdi:ceiling-fan-light" id: ceiling_fan_light - file: "mdi:fan-off" id: fan_off - file: "mdi:fan-speed-1" id: fan_speed_1 - file: "mdi:fan-speed-2" id: fan_speed_2 - file: "mdi:fan-speed-3" id: fan_speed_3 - file: "mdi:floor-lamp-torchiere" id: floor_lamp - file: "mdi:home-off" id: away_button # Force a full display refresh when we press the "boot" button on the devboard binary_sensor: - platform: gpio pin: number: GPIO0 mode: input: true pullup: true inverted: true id: boot_button internal: true on_click: then: - component.update: lvgl0 - delay: 1s - component.update: display0 display: - platform: waveshare_epaper_2bit id: display0 cs_pin: GPIO19 dc_pin: GPIO17 busy_pin: GPIO4 reset_pin: GPIO5 model: 2.90inv2-r2-2bpp full_update_every: 30 rotation: 270° auto_clear_enabled: false update_interval: never # show_test_card: true # lambda: |- # it.print(0, 0, id(headerfont), "Bleat!"); lvgl: - id: lvgl0 displays: - display0 touchscreens: - touchscreen0 # on_draw_end: # - component.update: display0 bg_color: 0xFFFFFF # Black: 0x000000 # Dark Grey: 0x555555 # Light Grey: 0xAAAAAA # White: 0xFFFFFF theme: label: text_color: 0x000000 button: # bg_color: 0xAAAAAA bg_color: 0xFFFFFF text_color: 0x000000 height: 64 width: 64 pad_all: 0 border_width: 2 border_color: 0x000000 checked: bg_color: 0x555555 obj: bg_color: 0xFFFFFF widgets: - obj: width: 100% height: 100% pad_all: 0 # pad_row: 0 # pad_column: 0 border_width: 0 bg_color: 0xFFFFFF layout: type: GRID grid_rows: [fr(1), fr(1)] grid_columns: [fr(1), fr(1), fr(1), fr(1)] widgets: - button: grid_cell_row_pos: 0 grid_cell_column_pos: 0 on_click: - logger.log: "ceiling_fan" widgets: - image: src: ceiling_fan - button: grid_cell_row_pos: 0 grid_cell_column_pos: 1 on_click: - logger.log: "ceiling_fan_light" - lambda: 'id(display0).display_partial();' checkable: true widgets: - image: src: ceiling_fan_light - button: grid_cell_row_pos: 0 grid_cell_column_pos: 2 on_click: - logger.log: "fan_off" - component.update: display0 checkable: true widgets: - image: src: fan_off - button: grid_cell_row_pos: 0 grid_cell_column_pos: 3 checkable: true on_click: - logger.log: "fan_speed_1" - lvgl.widget.update: id: fan_speed_2 state: checked: false - lvgl.widget.update: id: fan_speed_3 state: checked: false - component.update: display0 widgets: - image: src: fan_speed_1 - button: grid_cell_row_pos: 1 grid_cell_column_pos: 0 on_click: - logger.log: "fan_speed_2" - component.update: display0 checkable: true widgets: - image: src: fan_speed_2 - button: grid_cell_row_pos: 1 grid_cell_column_pos: 1 on_click: - logger.log: "fan_speed_3" - component.update: display0 checkable: true widgets: - image: src: fan_speed_3 - button: grid_cell_row_pos: 1 grid_cell_column_pos: 2 on_click: - logger.log: "floor_lamp" - component.update: display0 checkable: true widgets: - image: src: floor_lamp - button: grid_cell_row_pos: 1 grid_cell_column_pos: 3 on_click: - logger.log: "away_button" checkable: true widgets: - image: src: away_button # - label: # text: 'Test 2bpp 1636' # align: TOP_LEFT # text_font: font1 # - button: # id: button0 # align: TOP_RIGHT # on_click: # - logger.log: "button0 clicked (unlock)" # widgets: # - image: # src: home_lock_open # outline_width: 1 # - button: # id: button1 # align: BOTTOM_RIGHT # on_click: # - logger.log: "button1 clicked (lock)" # widgets: # - image: # src: home_lock # outline_width: 1 # - slider: # id: slider0 # align: LEFT_MID # width: 180 # height: 16 # min_value: 0 # max_value: 100 # value: 50 # on_value: # - logger.log: # format: "Slider: %.0f" # args: ['x'] # - lambda: 'id(display0).display_partial();' # - label: # text: "0xFFFFFF" # text_color: 0xFFFFFF # x: 0 # y: 15 # - label: # text: "0xAAAAAA" # text_color: 0xAAAAAA # x: 0 # y: 30 # - label: # text: "0x555555" # text_color: 0x555555 # x: 0 # y: 45 # - label: # text: "0x000000" # text_color: 0x000000 # x: 0 # y: 60 # - obj: # align: BOTTOM_LEFT # width: 33% # height: 30% # bg_color: 0x000000 #Black # - obj: # align: BOTTOM_MID # width: 33% # height: 30% # bg_color: 0x555555 #Dark Grey # - obj: # align: BOTTOM_RIGHT # width: 33% # height: 30% # bg_color: 0xAAAAAA #Light Grey # - obj: # align: TOP_RIGHT # width: 33% # height: 30% # bg_color: 0xFFFFFF #White # - obj: # align: LEFT_MID # width: 66% # height: 30% # bg_color: 0x000000 # bg_grad_color: 0xFFFFFF # bg_grad_dir: HOR