Introduction

With my desk being located in a more secluded part of the house, I wanted to build something that could notify me of important things. And while having a LED light up – maybe even with different colours – is nice, I wanted something more.

So I decided to test out a few displays.

These displays would then be able to show me more than just whether someone’s at the door or that someone dropped something in the mailbox.

In this blog post, I’ll be discussing the Waveshare 2.13" ePaper display.

Requirements (shopping list)

Pretty obvious shopping list:

Waveshare 2.13 inch ePaper display
Waveshare 2.13 inch ePaper display

Waveshare ePaper Display

Waveshare has a wide range of ePaper displays and ESPHome supports most – if not all – of them.

I went with the HAT (Hardware Attached on Top) model because my plan was to mount it to my case using a 40-pin (double 20-pin) header. However, even though I believe I managed to identify the pins in the pin header matching the pins on the 8-pin connector, I was unable to get it to work using these pins.
So finally I decided to use the 8-pin connector already provided with the display.

The 2.13" model I purchased communicates over 4-wire SPI. It has a resolution of 250x122 pixels. And it supports partial refresh.

ESPHome config

The ESPHome config is pretty easy.

  1. Open the ESPHome dashboard and add your ESP32 board as a new device.
  2. Pick the correct board so that the auto-generated code has the correct values.
  3. Add the definition for SPI
  4. Add the code below to display a “Hello World!” on your display.
    Add it below the auto-generated config.
font:
  - file: "fonts/monof55.ttf"
    id: monofur
    size: 28

spi:
  clk_pin: 18
  mosi_pin: 23

display:
  - platform: waveshare_epaper
    cs_pin: 5
    dc_pin: 21
    busy_pin: 19
    reset_pin: 22
    model: 2.13in-ttgo
    rotation: 90°
    id: my_display
    lambda: |-
      it.printf(125, 6, id(monofur), TextAlign::TOP_CENTER, "%s", "Hello World!");
      it.strftime(125, 116, id(monofur), TextAlign::BOTTOM_CENTER, "%Y-%m-%d %H:%M", id(time_homeassistant).now());      

time:
  - platform: homeassistant
    id: time_homeassistant

Make sure you match the pins in the config to your setup.

Fonts

See that font section?
For the display to show text, it’ll need a font.
Find yourself a TrueType Font (.ttf) file and upload it to the /config/esphome/ folder.

Note that I created a subfolder /config/esphome/fonts/ to store my font files, so I adjusted the path accordingly.

Full configuration

Below is the full configurations you can use. Replace your existing code with this and adapt where necessary (e.g., board type and naming).

---
# ESP32 board:
# - https://s.click.aliexpress.com/e/_97FTTR
# Waveshare 2.13" ePaper Display:
# - https://s.click.aliexpress.com/e/_9JZHkB
# ESPHome docs:
# - https://esphome.io/components/display/waveshare_epaper.html
# - https://esphome.io/components/spi.html
# - https://esphome.io/components/display/index.html

# Wiring diagram
# | ePaper  | ESP32 | Description      |
# | :-----: | :---: | :--------------- |
# | VCC     | 3.3V  | 3.3V power       |
# | GND     | GND   | Ground           |
# | CLK     | 18    | Serial clock     |
# | DIN     | 23    | SPI MOSI         |
# | CS      | 5     | Chip Select      |
# | DC      | 21    | SPI DC           |
# | BUSY    | 19    | Busy (optional)  |
# | RESET   | 22    | Reset (optional) |

# Setup some variables
substitutions:
  slug: esp32_waveshare
  name: Waveshare
  description: Demo by sequr.be for ESP32 with Waveshare ePaper display

# Basic config for the board
esphome:
  name: "${slug}"
  comment: "${description}"
  platform: ESP32
  board: mhetesp32devkit

# Connect with WiFi
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_pass

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: !secret esphome_api_pass

# Enable Over-the-Air updates
ota:
  password: !secret esphome_ota_pass

# Enable HTTP webserver
web_server:
  port: 80
  auth:
    username: !secret esphome_web_user
    password: !secret esphome_web_pass

# Sync time with Home Assistant
time:
  - platform: homeassistant
    id: time_homeassistant

# TrueType Font file stored in /config/esphome/fonts/
# https://all-free-download.com/font/monospace.html
font:
  - file: "fonts/monof55.ttf"
    id: monofur
    size: 28

# Setup SPI
spi:
  clk_pin: 18
  mosi_pin: 23

# Configuration for the display
display:
  - platform: waveshare_epaper
    cs_pin: 5
    dc_pin: 21
    busy_pin: 19
    reset_pin: 22
    model: 2.13in-ttgo
    rotation: 90°
    id: my_display
    lambda: |-
      it.printf(125, 6, id(monofur), TextAlign::TOP_CENTER, "%s", "Hello World!");
      it.strftime(125, 116, id(monofur), TextAlign::BOTTOM_CENTER, "%Y-%m-%d %H:%M", id(time_homeassistant).now());      

# switches, sensors, text_sensors
switch:
  - platform: restart
    id: switch_restart
    name: "${name} Restart"

sensor:
  - platform: uptime
    id: sensor_uptime
  - platform: template
    id: sensor_wifi_signal_percentage
    name: "${name} Wi-Fi Signal Percentage"
    icon: "mdi:wifi"
    unit_of_measurement: "%"
    update_interval: never
    lambda: |-
      if (id(sensor_wifi_signal).state) {
        if (id(sensor_wifi_signal).state <= -100 ) {
          return 0;
        } else if (id(sensor_wifi_signal).state >= -50) {
          return 100;
        } else {
          return 2 * (id(sensor_wifi_signal).state + 100);
        }
      } else {
        return NAN;
      }      
  - platform: wifi_signal
    id: sensor_wifi_signal
    name: "${name} Wi-Fi Signal"
    update_interval: 300s
    on_value:
      - component.update: sensor_wifi_signal_percentage

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${name} IP Address"
      id: ip_address
    ssid:
      name: "${name} Connected SSID"
      id: ssid
    bssid:
      name: "${name} Connected BSSID"
      id: bssid
  - platform: version
    id: text_sensor_version
    name: "${name} ESPHome Version"