Use ESPHome with e-ink Displays to blend in with your home decor!


This is my finished display. It is running on 2x 1.5V li ion batteries and will last for about a month. I removed the LED on the esp board to save power and added 2 small resistors to make a voltage divider so i can measure the battery level.

The screen wakes up every hour during daytime and goes into deepsleep at night. I put all the data for the screen in one homeassistant sensor (ePaperDisplay Data). Not really necessary but its how i did it. If you want to steal some code, well go ahead because i did the same :slight_smile:

2 Likes

Thank you, your post was really helpful, i have used the page you linked to and have succesfully flashed the device, i then entered the wifi password and i can now see the device in a browser, it has ESPHome Web XXXXXX and then i can choose file for ā€œOTA Updateā€

Would it be possible for you to give me a hint on one of the many tutorials you mention is out there ?

I dont assume the OTA update is where i update the YAML files through, i would guess that is for firmware updating ?

From the documentation itself: Getting Started with ESPHome and Home Assistant ā€” ESPHome
You can flash your code wirelessly (OTA). Have fun.

1 Like
esphome:
  name: kitchen-display
  friendly_name: Kitchen Display

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "somekey="

ota:
  - platform: esphome
    password: "password"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    # Set this to the IP of the ESP
    static_ip: 192.168.1.30
    # Set this to the IP address of the router. Often ends with .1
    gateway: 192.168.1.1
    # The subnet of the network. 255.255.255.0 works for most home networks.
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-9908C4"
    password: "secret"

captive_portal:
    
font:
  - file: 'fonts/GothamRnd-Book.ttf'
    id: font_small_book
    size: 44

# Pins for Waveshare ePaper ESP Board
spi:
  clk_pin: GPIO13
  mosi_pin: GPIO14

# Define colors
# This design is white on black so this is necessary.
color:
  - id: color_black
    red: 0%
    green: 0%
    blue: 0%
  - id: color_white
    red: 100%
    green: 100%
    blue: 100%
  - id: color_red
    red: 100%
    green: 0%
    blue: 0%


# Now render everything on the ePaper screen.
display:
  - platform: waveshare_epaper
    id: eink_display
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: 
      number: GPIO25
      inverted: true
    reset_pin: GPIO26
    reset_duration: 2ms
    model: 7.50in-bv3
    rotation: 270Ā°
    auto_clear_enabled: false
    lambda: |-
       it.print(180, 100, id(font_small_book), color_red, TextAlign::TOP_CENTER, "Hello Timmy!");
    

I post this here as a record of my experience so far with getting started:
Hardware:
Waveshare ESP32 e-paper driver
Waveshare 7.5 inch tri-colour (w/r/b) screen - v3

Dip switches are set:

  1. as ON
  2. as OFF (disable uart for power saving but ON works too)

Initially I had no luck getting things running - the display did not react to the device booting and it took a far bit of trial and error with the code to get anything to display. The above (rough) code gives me some display and the update of the screen works well - initially I used the v2 model type and the display went mental refreshing! v3 turns out to be very important.

At this stage I have some text and refreshing working but Iā€™ve not attempted getting red to work (not so important) as itā€™s not officially supported. For some reason color_red for the font displays something (in black as expected_ but color_black - nothing.

At this stage Iā€™m happy as I know the darn things working and now I can move on to working out what to display. Thank you for all the code snippets and posts thus far!

1 Like

I used your script and found the font online and put it in the right folder.

So now i have it working somewhat.

Says ā€œHello Timmy!ā€ in the screen, the screen seems to be updating ā€œall the timeā€ but atleast i got something onto the screen.

Now i can start figuring out how i get data from Home Assistant read by the display so that i can display some of the sensors i have on the Home Assistant.

Take a look here,

This file sends data from HomeAssistant till ESPhome

I will look at it, but me not being a programmer, i am going to struggle, tried copying it over and i get all sorts of errrors like

Component template cannot be loaded via YAML (no CONFIG_SCHEMA).

This is probably because i dont have these items but i am at a loss for how i should proceed.

I was hoping (Naively offcourse) that someone had made it so that the ESP32 could just log into HA with a special user and i could then edit a dashboard for that user there and it would pull it from HA, but it seems that the ESP32 is more or less a standalone unit that needs serious programming to work.

It seems i was a bit naive, this is probably above my abilities, so i might just end up binning the whole thing as it seems i need more skills than i posses. :frowning:

TBH jumping in with a project like this from the start isnā€™t going to be easy. My first ESPHome device was a relay - it does ON and OFF :slight_smile: This project is in orders of magnitude harder as there is a lot more involved in driving a display and fetching content by nature. Yes it can be learnt but getting your feet wet before the head isnā€™t a bad way to start! :smiley:

So far Iā€™ve got it to display the time in a nice big font. Shortly after this I realised using e-ink as a clock is a really bad idea as Iā€™m going to kill the screen with too many refreshes so itā€™s likely to go back to being weather plus other low-refresh info.

1 Like

I totally get your frustration! Taking small steps is key, and loading data from Home Assistant doesnā€™t need to be the first thing you do. My advice: take it one small step at a time. Start with something simple, like just getting the ESP32 to display text. Once youā€™ve got that working, you can build on it step-by-step.

Thatā€™s exactly how I started with my first ESP Home project. Breaking it down like this makes it much easier to learn and feel confident with each part before moving on.

No pressure to get it all perfect right awayā€”small progress is still progress!

1 Like

My initial plan was to have it display stuff like ā€œNext time the trashcan is emptiedā€

Even making it display a date and when the date has come and gone, add 2 weeks to that date and then display the next one seems not doable.

Will probably put this on the shelf and hope someone makes it easier, it would be really awesome if someone could make an integration into HA, where i could just design a dashboard in HA, and have it fetch it from there.

I guess when i saw this thread i thought this was a ā€œuser manualā€ with step by step instructions on how to get it doing what i wanted it to do.

Maybe i should have read a bit past the first couple of posts before buying the hardware :joy:

I sympathise Tim, having spent the last few nights up until 5 am trying to a) get a Waveshare 7.5" ePaper display to anything at all b) get a Waveshare 2.9" display to do what I want with ESPHome.

It would be great if HA/ESPHome could get the project to the stage you suggest i.e. a drag and drop Dashboard that one can configure visually.

The project seems a long way from that, right now. For example, there isnā€™t a proper layout engine, it seems, to do things like wrap text, align items, pad space between them etc - things that are easy in HTML/CSS.

The main problem, for me, is that a cycle of configuration change to test regularly takes 5 minutes due to the ā€œCompileā€ step. It would need to be 5 seconds to really get creative. Ideally, thereā€™d be an online sandbox where one might test ā€œdisplay lambdasā€ and see the result without having to get it working in C on oneā€™s own display.

That said, what one can achieve with this remains impressive. Hereā€™s what Iā€™ve got so far!

BitcoinEpaper

Thatā€™s the live Bitcoin price, coming in via a sensor on HA. The graph is generated by a Google Sheet, from live data updated daily (CSV import), making a chart which Google Sheets publishes to a URL, which my ESPHome device YAML imports via ā€œonline_imageā€. This is powered by a $2 ESP32-C3 from AliExpress. (The graph data is currently imaginary).

Cool, right?

If youā€™re stuck, I hope you can find the help to keep going!

Best wishes and hereā€™s my config if it helps anyone:

substitutions:
  device_name: bitcoin-price-290in

logger:
  level: DEBUG  # Set logging level (DEBUG for verbose output)
  baud_rate: 115200  # Set the baud rate for logging output

esphome:
  name: ${device_name}

  on_boot:
      priority: 200.0
      then:
        - component.update: adam_epaper1
        - wait_until:
            condition:
              lambda: 'return id(data_updated) == true;'
              # Wait a bit longer so all the items are received
        - delay: 5s
        - logger.log: "Initial sensor data received: Refreshing display..."
        - lambda: 'id(initial_data_received) = true;'
        - script.execute: update_screen

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

# Enable Home Assistant API
api:
  encryption:
    key: "redacted"

ota:
  - platform: esphome
    password: "redacted"

button:
  - platform: shutdown
    name: "Bitcoin - Shutdown"
  - platform: restart
    name: "Bitcoin - Restart"
  - platform: template
    name: "Bitcoin - Refresh Screen"
    entity_category: config
    on_press:
      - script.execute: update_screen

captive_portal:

# Global variables for detecting if the display needs to be refreshed. (Thanks @paviro!)
globals:
  - id: data_updated
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: initial_data_received
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: recorded_display_refresh
    type: int
    restore_value: yes
    initial_value: '0'
  - id: bitcoin_price_formatted
    type: std::string
    restore_value: no
    initial_value: '""'

# Script for updating screen - Refresh display and publish refresh count and time. (Thanks @paviro!)
script:
  - id: update_screen
    then:
      - logger.log: "Executing update_screen script"
      - lambda: 'id(data_updated) = false;'
      - component.update: adam_epaper1
      - lambda: 'id(recorded_display_refresh) += 1;'
      - lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);'
      - logger.log: "Screen updated and refresh count published"
      
# Check whether the display needs to be refreshed, once per minute
time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - script.execute: update_screen
          # - if:
          #     condition:
          #       lambda: 'return id(data_updated) == true;'
          #     then:
          #       - logger.log: "Sensor data updated: refreshing display..."
          #       - script.execute: update_screen
          #     else:
          #       - logger.log: "No sensors updated - skipping display refresh."

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  on_connect:
    - logger.log: "WiFi connected, updating bitcoin graph..."
    - component.update: bitcoin_graph

# Add the sensor from Home Assistant
sensor:
  - platform: homeassistant
    entity_id: sensor.exchange_rate_1_btc
    id: bitcoin_price
    on_value:
      then:
        - lambda: |-
            id(data_updated) = true;
            int price = static_cast<int>(id(bitcoin_price).state);
            std::string price_str = std::to_string(price);
            int insert_position = price_str.length() - 3;
            while (insert_position > 0) {
              price_str.insert(insert_position, ",");
              insert_position -= 3;
            }
            id(bitcoin_price_formatted) = price_str;

  # Create sensors for monitoring device remotely.
  - platform: template
    name: "adam1 - Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    
  - platform: template
    name: "adam1 - Recorded Display Refresh"
    accuracy_decimals: 0
    unit_of_measurement: "Refreshes"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh);'
  
  - platform: wifi_signal
    name: "adam1 - WiFi Signal Strength"
    id: wifisignal
    unit_of_measurement: "dBm"
    entity_category: "diagnostic"
    update_interval: 60s

# image:
#   - file: "images/mini-test.png"
#     id: bitcoin_graph
#     type: BINARY

# Enable HTTP Request component
http_request:
  useragent: esphome/1.0  # Optional: Set a custom user agent

online_image:
  - id: bitcoin_graph
    url: "http://homeassistant.local:8123/local/mini-test.png"
    # url: "https://docs.google.com/spreadsheets/d/e/2PACX-1vSUFF1Koi0kin1iIK9kEShIjQ-gSFL2s2QvDCHzids5KC3sRgT8i0-oTRnWJXBekX9f8A2o47yUVrCs/pubchart?oid=183940170&format=image"
    format: png
    # type: RGBA
    update_interval: 5min  # Fetch a new image every 24 hours >> 5 min for testing
    # resize: 296x128  # Resize the image
    on_download_finished:
      - logger.log: "Finished (downloading): Bitcoin graph online_image on_download_finished"
      - component.update: adam_epaper1
    on_error:
      - logger.log: "Error: Bitcoin graph online_image on_error"

font:
  - file:
      type: gfonts
      family: Montserrat
      weight: 400
    size: 30
    id: font_title
    extras: # Add dollar symbol $
      - file: "gfonts://Montserrat"
        glyphs: [$]

  - file:
      type: gfonts
      family: Montserrat
      weight: 400
    size: 20
    id: font_subtitle
    extras: # Add dollar symbol $
      - file: "gfonts://Montserrat"
        glyphs: [$]

  - file:
      type: gfonts
      family: Montserrat
      weight: 400
    size: 10
    id: font_footer

  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi
    size: 10
    glyphs:
      # 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

# Define colors
# These look the wrong way around but it's because HA works on both LCD and ePaper displays.
# On the ePaper display, white means no colour (000000). Black means max colour (FFFFFF).
# See https://github.com/esphome/issues/issues/3132
color: 
  - id: colour_white
    hex: "000000"
  - id: colour_black
    hex: "FFFFFF"

spi:
  clk_pin: 4       # SCK (SPI Clock) / SCL / CLK on the board - Yellow / YellowHAT
  mosi_pin: 6      # MOSI (SPI Data Input) / SDA / DIN on the board - Blue / BlueHAT

display:
  - platform: waveshare_epaper
    model: 2.90inV2
    cs_pin: 7        # CS (Chip Select) - Orange
    dc_pin: 1        # D/C (Data/Command) - Green
    busy_pin: 3      # BUSY (Busy signal) - Purple
    reset_pin: 2     # RES (Reset) - White
    reset_duration: 2ms
    rotation: 270
    full_update_every: 1
    update_interval: never
    id: adam_epaper1
    lambda: |-
      ESP_LOGI("display", ">>> DISPLAY UPDATE <<<");
      if (isnan(id(bitcoin_price).state)) {
        // Awaiting data
        it.printf(148, 65, id(font_title), id(colour_black), TextAlign::BASELINE_CENTER, "Awaiting data...");
      }
      else {
        ESP_LOGD("display", "Writing Bitgoin graph");
        if (id(bitcoin_graph) != nullptr) {
          ESP_LOGD("display", "Displaying Bitcoin graph image.");
          // Fix inverted image: https://github.com/esphome/feature-requests/issues/1463
          it.image(0, 0, id(bitcoin_graph), COLOR_OFF, COLOR_ON);
        } else {
          ESP_LOGD("display", "Bitcoin graph image not ready.");
        }

        // Draw blanking rectangle behind Bitcoin price
        it.filled_rectangle(35, it.get_height()-20-35, 245, 35, id(colour_white));

        // Draw main text with formatted price
        ESP_LOGD("bitcoin_price", "Displaying bitcoin price: $%.0f", id(bitcoin_price).state);
        ESP_LOGD("bitcoin_price", "Formatted Bitcoin price: %s", id(bitcoin_price_formatted).c_str());
        it.printf(it.get_width() / 2, it.get_height()-20, id(font_title), id(colour_black), TextAlign::BOTTOM_CENTER, "Bitcoin: $%s", id(bitcoin_price_formatted).c_str());

        /* FOOTER */
        it.strftime(it.get_width()-11, it.get_height()-10, id(font_footer), id(colour_black), TextAlign::BASELINE_RIGHT, "Updated %H:%M %d%b%y", id(homeassistant_time).now());
        
        // Draw WiFi signal strength
        if(id(wifisignal).has_state()) {
          const char* wifi_icon;
          const char* wifi_log;

          if (id(wifisignal).state >= -50) {
              wifi_icon = "\U000F0928";  // Excellent
              wifi_log = "Excellent";
          } else if (id(wifisignal).state >= -60) {
              wifi_icon = "\U000F0925";  // Good
              wifi_log = "Good";
          } else if (id(wifisignal).state >= -75) {
              wifi_icon = "\U000F0922";  // Fair
              wifi_log = "Fair";
          } else if (id(wifisignal).state >= -100) {
              wifi_icon = "\U000F091F";  // Weak
              wifi_log = "Weak";
          } else {
              wifi_icon = "\U000F092B";  // Very weak
              wifi_log = "Very weak";
          }

          it.print(it.get_width(), it.get_height()-10, id(font_mdi), id(colour_black), TextAlign::BOTTOM_RIGHT, wifi_icon);
          ESP_LOGI("WiFi", wifi_log);
        }
      }

Iā€™m really stuck and hope someone can help me?

To make my version of this project, I bought the Waveshare 7.5" (v2) ePaper and with it bought the Waveshare ESP32 epaper driver board.

I am unable to get the Waveshare ESP32 to flash from ESPHome (ā€œwebā€ or local), and turns out Iā€™m unable to do almost anything with it via the command line >> esptool.py, either.

Commands with ā€œno-stubā€ work fine, but anything more meaningful always fails:

esptool.py --port /dev/cu.usbmodem585A0850441 --baud 115200 erase_flash                     
esptool.py v4.7.0
Serial port /dev/cu.usbmodem585A0850441
Connecting......
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting.....
Detecting chip type... ESP32
Chip is ESP32-D0WDQ6 (revision v1.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 34:98:7a:51:f6:88
Uploading stub...

A fatal error occurred: Failed to write to target RAM (result was 01070000: Operation timed out)

So, uploading the stub fails, which esptool needs to do anything worthwhile, because esptool is unable to write anything to RAM.

Buttons? Iā€™ve tried every possible combination of holding down BOOT while plugging in, or holding down BOOT while running the command, holding down RESET (EN), holding down BOOT and RESET at the same time then releasing RESET.

I did maybe ONCE get the ESPHome web tool to write to the board, but it remains a mystery what magic combination of buttons helped. The board then failed to come online or do anything useful.

Cable? It could be my micro-USB >> USB-C cable, but I donā€™t think so? Iā€™ve got another brandā€™s cable on order, in case. Iā€™m using an expensive, high quality cable that promises it is data-enabled. And plugging directly into a Macbook USB-C port, no hub.

Drivers? I (re)installed the CH343 drivers on my Mac, made no difference. I can flash my ESP32-C3 boards no problem.

Faulty board? I know itā€™s not the particular Waveshare ESP32 board, because I now have three of them (!) and they all behave the same.

My question
Please, can anyone give step by step instructions (including board buttons to press) to get the Waveshare ESP32 board working with ESPHome on the latest MacOS?

Alternative: get display working with ESP32-C3?
I bought the Waveshare ESP32 because I couldnā€™t figure out the right pins to use on my ESP32-C3 to power the 7.5" display, so thatā€™s another option, if anyone can help? The ESP32-C3 flashes fine, first time, every time, with either the web tools or command line. I wired the ePaper ā€œPWRā€ to 3.3v on the ESP32-C3, and VCC to the 5v line.

I have an identical ESP32-C3 powering the Waveshare 2.9in display, via ESPHome, with no problem. I tried with ESPHome but couldnā€™t get the Waveshare display to do anything at all, even flicker once.

The answer was:

Drivers

Iā€™d installed some CH34x drivers to my Mac, as many websites advise. Turns out the inbuilt Mac OS drivers work much better, and those other ones were getting in the way. When I dragged the SiLabs app from Applications to the bin, suddenly 2 x new devices appeared:

/dev/cu.wchusbserial585A0850441
/dev/tty.wchusbserial585A0850441

These work fine. So, I can now flash my Waveshare ESP32s using my Mac. (I found this out by using the same cable and board with Windows and esptool worked fine with ā€œCOM3ā€)

Now the new bootloaderā€™s in, I can do the rest with OTA and donā€™t need a cable or /dev/ stuff at all.

Now, it works:

BItcoin-ePaper75

As before, this is a daily-updated graph (using online_image) with an hourly-updated price.

I love it. With thanks to all the contributors above.

PS Total coincidence that BTC hit all-time high as I took this photo :stuck_out_tongue_closed_eyes:

1 Like

Hi Harald.
Do you have a working code on github (or an other place), that i can use as a start of my project?