Skip to content

pavlojs/esphome-epaper-dashboard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ESPHome e-Paper Dashboard

A battery-powered e-ink dashboard for Home Assistant, built with ESPHome on a LOLIN S3 Pro (ESP32-S3) and a Waveshare 7.5" e-Paper display. It wakes up every 20 minutes, pulls data from Home Assistant, renders the screen, and goes back to deep sleep — lasting about a month+ on a 2500 mAh battery.

Inspired by Madelena's Weatherman Dashboard.

Note: The on-screen UI language is Polish. All code comments are in English.


Features

The dashboard is built around a few fixed layout zones — you keep what's useful and remove what isn't. All data sources are optional except weather.

  • Current weather — large temperature + MDI condition icon, indoor temperature & humidity
  • 4-day forecast — per-day condition icons and max temperatures
  • Sensor data — any HA sensors: temperature, humidity, pressure, air quality, UV index, or anything else numeric
  • Calendar — events from one or more HA calendars, with optional keyword-based logic (work shifts, vacations, custom labels)
  • Any numeric HA data — exchange rates, fuel prices, EV status, task counters, or any sensor you have
  • Deep sleep — configurable wake/sleep cycle; ~1 month on 2500 mAh LiPo with default settings (90 s / 20 min)
  • Battery & WiFi status in the footer, with alert below 15%
  • Custom PCB available (optional) — Gerber files included in assets/

How It Works

┌─────────────┐      WiFi       ┌────────────────┐
│  ESP32-S3   │ ──────────────> │ Home Assistant │
│  (LOLIN S3) │ <────────────── │     (API)      │
└──────┬──────┘    sensors +    └────────────────┘
       │           calendar +
       │           weather data
       ▼
┌─────────────┐
│  Waveshare  │
│  7.5" e-ink │
└─────────────┘

Boot cycle:

  1. ESP32 wakes from deep sleep
  2. Powers on the e-paper display (GPIO15)
  3. Connects to WiFi and Home Assistant API (timeout: 20 s)
  4. Waits for sensor data — weather, calendar, battery (timeout: 30 s)
  5. Renders the full display layout in one pass
  6. Waits 5 s for the e-ink to finish physical rendering
  7. Enters deep sleep for 20 minutes
  8. Repeat

Display Layout

The display is 800×480 px, rotated 90° (portrait orientation: 480 wide × 800 tall). It is divided into five horizontal zones stacked top to bottom:

Zone 1 — Current weather (top, ~225 px)
Large condition icon (MDI, 96 pt) on the left, current outdoor temperature (108 pt bold) in the centre, indoor temperature and humidity on the right.

Zone 2 — 4-day forecast (~95 px)
Four columns, each showing a weekday abbreviation, a weather icon (36 pt MDI), and the forecast maximum temperature.

Zone 3 — Left column: date & events (remaining height, left 57%)
Current date (day name + numeric date), then configurable data rows (exchange rate, task count, precipitation, or anything numeric), followed by a calendar section with today's and tomorrow's events.

Zone 4 — Right column: sensor data (remaining height, right 43%)
Sensor readings with MDI icons (temperature, humidity, pressure, UV index, air quality, fuel price), then a secondary section for any additional data you want — EV status, energy usage, custom sensors, etc.

Zone 5 — Footer (bottom ~20 px)
WiFi signal strength icon + dBm value, battery icon + percent, last update timestamp — fixed to the bottom edge.


Hardware

Required

Component Notes
LOLIN S3 Pro (ESP32-S3) Development board — see Why LOLIN S3 Pro? below
Waveshare 7.5" e-Paper V2 800×480 px, monochrome (black/white), SPI interface
LiPo battery (3.7 V) or USB-C cable 2500 mAh recommended for ~1 month battery life

Optional

Component Notes
Custom PCB Simplifies wiring, adds basic reverse polarity protection via AO3401 MOSFET — Gerber files included. Without the PCB there is no reverse polarity protection — see Electrical & Hardware Safety
IKEA RÖDALM frame (13×18 cm) For a clean wall-mounted look
Outdoor/indoor sensors (e.g. BME280 + PMS5003) For environmental data — any HA-compatible sensor works

Why LOLIN S3 Pro?

The LOLIN S3 Pro was chosen specifically for battery-powered e-ink projects:

  • Built-in LiPo battery management — charging circuit with JST connector, no external TP4056 module needed
  • Battery voltage via ADC (GPIO3) — the board has a built-in voltage divider; just read the pin and multiply by 2
  • Excellent deep sleep performance — ESP32-S3 draws very low current in deep sleep
  • ~1 month on 2500 mAh — with current settings (20 min sleep / 90 s wake cycle), real-world battery life is approximately one month
  • USB-C — modern connector for both charging and programming
  • Compact form factor — fits well inside picture frames

Wiring (without Custom PCB)

Connect the Waveshare e-Paper to the LOLIN S3 Pro:

ESP32 GPIO e-Paper Pin Function
GPIO18 CLK SPI Clock
GPIO11 DIN SPI MOSI (data)
GPIO5 CS Chip Select
GPIO16 DC Data/Command
GPIO4 BUSY Busy signal (active LOW — inverted in config)
GPIO17 RST Reset (10 ms pulse)
GPIO15 Display power control (turn on/off via MOSFET or direct)

Optional pins:

ESP32 GPIO Function
GPIO3 Battery ADC — reads voltage through built-in divider (×2)
GPIO2 Deep sleep ON/OFF slide switch (INPUT_PULLDOWN, inverted)
GPIO1 Wakeup pin (keeps awake when active)

Schematic & Custom PCB

The project includes a custom PCB designed in EasyEDA (credit: WR Electro). It integrates:

  • AO3401 P-MOSFET (Q1) — routes battery power to the LOLIN board; its body diode provides basic reverse polarity protection (see Electrical & Hardware Safety)
  • SS-12D10G5 slide switch (U3) — hardware deep sleep enable/disable
  • 10 kΩ resistor (R1) — MOSFET gate pull control
  • Screw terminals (H1) — external battery connection
  • HX 2.0mm 9P connectors (H3, U4) — e-Paper ribbon cable breakout
  • JST battery connector (H2) — connects to LOLIN's built-in charger

Schematic

3D PCB render

Physical Assembly:

All components

PCB with components mounted

PCB back side

Gerber files for PCB manufacturing: assets/Gerber_PCB_epaper_dashboard.zip

You can order the PCB from JLCPCB, PCBWay, or any other manufacturer. Upload the Gerber ZIP and use default settings (2 layers, 1.6 mm thickness).


Software Requirements

Requirement Details
Home Assistant Any installation method (HAOS, Docker, Core, Supervised)
ESPHome As HA add-on or standalone CLI
Weather integration e.g. Met.no — entity: weather.forecast_home (or similar)
Calendar integration(s) Optional — up to 3 calendars for events/shifts/holidays

Optional Integrations

These are used in the default config but can be removed or replaced:

Integration Substitution variable Purpose
Outdoor sensor ha_outdoor_temp/humidity/pressure/air_entity Temperature, humidity, pressure, PM2.5
Indoor sensor ha_indoor_temp_entity, ha_indoor_humidity_entity Indoor temp & humidity
EV / car integration ha_ev_battery_entity, ha_ev_range_entity, ha_fuel_range_entity EV battery, electric range, fuel range
Fuel price sensor ha_fuel_price_entity Local fuel price (€)
Currency exchange ha_currency_entity Exchange rate
Task tracker ha_tasks_entity Open household tasks

Quick Start

For users already familiar with Home Assistant and ESPHome.

  1. Clone this repository:

    git clone https://github.com/pavlojs/esphome-epaper-dashboard.git
  2. Copy fonts to your ESPHome config directory:

    cp -r fonts/ /config/esphome/fonts/
    # or ~/.config/esphome/fonts/ for CLI users
  3. Create secrets.yaml (if not already present) in your ESPHome config directory:

    wifi_ssid: "YourWiFiSSID"
    wifi_password: "YourWiFiPassword"
  4. Edit dashboard.yaml — fill in the substitutions: block at the top of the file with your own entity IDs and labels (see Customization)

  5. Flash the LOLIN S3 Pro:

    • Via ESPHome Dashboard: upload dashboard.yaml, click Install
    • Via CLI: esphome run dashboard.yaml
  6. Add template sensors — copy the contents of templates.yaml into your Home Assistant configuration.yaml under the template: key:

    template:
      - trigger:
          # ... (paste contents of templates.yaml here)
  7. Restart Home Assistant to activate the template sensors


Beginner Guide

New to Home Assistant or ESPHome? Here's a step-by-step walkthrough.

What is ESPHome?

ESPHome is a tool that lets you configure ESP32/ESP8266 microcontrollers using simple YAML files. It integrates natively with Home Assistant — no custom firmware coding required.

Step 1: Install the ESPHome Add-on

  1. In Home Assistant, go to Settings → Add-ons → Add-on Store
  2. Search for ESPHome and install it
  3. Start the add-on and open the Web UI

Step 2: Set Up Your Weather Integration

  1. Go to Settings → Devices & Services → Add Integration
  2. Search for Meteorologisk institutt (Met.no) (or your preferred weather provider)
  3. Configure it with your home coordinates
  4. Note the entity name (e.g., weather.forecast_home)

Step 3: Find Your Entity IDs

Entity IDs are how Home Assistant identifies each sensor, switch, or device.

  1. Go to Developer Tools → States (in your HA sidebar)
  2. Use the search/filter to find sensors you want to display
  3. The entity ID looks like sensor.living_room_temperature
  4. Set your entity IDs in the substitutions: block at the top of dashboard.yaml

Step 4: Prepare the Hardware

  1. Connect the Waveshare e-Paper to the LOLIN S3 Pro using the pin mapping table
  2. Connect via USB-C to your computer
  3. If using a battery, connect it to the LOLIN's JST battery connector

Step 5: Flash the Firmware

  1. In the ESPHome Dashboard, click + New Device
  2. Instead of creating from scratch, upload the dashboard.yaml file
  3. Make sure fonts/ is in the correct directory
  4. Click Install → Plug into this computer (for first flash via USB)
  5. Subsequent updates can be done wirelessly (OTA)

Step 6: Add Template Sensors to HA

The templates.yaml file creates sensors that process weather forecast data into a format the dashboard can display.

  1. Open your Home Assistant configuration.yaml
  2. Add a template: section (if it doesn't exist)
  3. Paste the contents of templates.yaml under it
  4. Go to Developer Tools → YAML → Template Entities → Reload

Customization

Entity ID Reference

All entity IDs and display labels are configured in the substitutions: block at the top of dashboard.yaml. You only need to edit that one block — no need to touch individual sensor definitions.

Substitution variable Example value Purpose Required?
ha_weather_entity weather.forecast_home Weather entity (temperature, condition, UV) Yes
ha_outdoor_temp_entity sensor.garden_temperature Outdoor temperature No
ha_outdoor_humidity_entity sensor.garden_humidity Outdoor humidity No
ha_outdoor_pressure_entity sensor.garden_pressure Atmospheric pressure No
ha_outdoor_air_entity sensor.garden_pm2_5 PM2.5 air quality No
ha_indoor_temp_entity sensor.living_room_temperature Indoor temperature No
ha_indoor_humidity_entity sensor.living_room_humidity Indoor humidity No
ha_tasks_entity sensor.open_tasks Open tasks count No
ha_fuel_price_entity sensor.fuel_price_super Fuel price (€) No
ha_currency_entity sensor.eur_pln Exchange rate No
ha_ev_battery_entity sensor.ev_battery_level EV battery (%) No
ha_ev_range_entity sensor.ev_range EV electric range (km) No
ha_fuel_range_entity sensor.fuel_driving_range Fuel range (km) No
label_outdoor_sensor "Outdoor sensor" Label shown on display No
label_fuel_station "Fuel station" Station name on display No
label_fuel_closed "Station closed" Text when no fuel price data No
label_car_name "Car:" Car section label on display No

How to Replace an Entity

All entity IDs are set in the substitutions: block at the top of dashboard.yaml:

substitutions:
  ha_outdoor_temp_entity: sensor.garden_temperature   # ← put YOUR entity ID here
  ha_indoor_temp_entity: sensor.living_room_temperature
  # ... etc.

You do not need to touch the individual - platform: homeassistant sensor blocks — they all reference the substitution variables.

How to find your entity ID in Home Assistant:

  1. Go to Developer Tools → States
  2. Filter by type (e.g., type "temperature")
  3. Copy the entity_id value (e.g., sensor.garden_temperature)
  4. Paste it as the value of the corresponding substitution variable in dashboard.yaml

How to Add a New Sensor

To display a new piece of data, you need to do three things:

1. Add the sensor block in the sensor: section of dashboard.yaml:

  - platform: homeassistant
    id: my_new_sensor              # pick a unique ID
    entity_id: sensor.your_entity  # your HA entity
    internal: true
    unit_of_measurement: "°C"      # optional — match your sensor's unit

2. Add rendering code in the display lambda: section. Find a spot where you want it and add:

      // My new sensor
      if (!std::isnan(id(my_new_sensor).state))
        it.printf(x, y, id(font_small_bold), TextAlign::TOP_LEFT, "%.1f°C", id(my_new_sensor).state);
      else
        it.printf(x, y, id(font_small_bold), TextAlign::TOP_LEFT, "b/d");

Replace x, y with pixel coordinates (the display is 480 wide × 800 tall after rotation).

3. For text sensors (non-numeric), use text_sensor: instead of sensor::

text_sensor:
  - platform: homeassistant
    id: my_text_sensor
    entity_id: sensor.your_text_entity
    internal: true

And in the lambda:

      it.printf(x, y, id(font_small), TextAlign::TOP_LEFT, "%s", id(my_text_sensor).state.c_str());

How to Remove a Section

To remove something you don't need (e.g., EV/car section, fuel price, tasks), delete three things:

1. The sensor definition — find and delete the whole - platform: homeassistant block:

  # Delete this entire block:
  - platform: homeassistant
    id: ev_battery
    entity_id: ${ha_ev_battery_entity}
    internal: true
    unit_of_measurement: "%"

2. The rendering code — find the corresponding section in the display lambda: and delete it. Look for the id(ev_battery) reference. For the car section, delete from the separator line (it.line(right_col_x, ...)) through the fuel_range printf.

3. Optionally, remove the now-unused substitution variables from the substitutions: block at the top of the file.

Tip: Use your editor's search to find all references to the sensor's id (e.g., search for ev_battery) to make sure you delete everything.

How to Customize Calendars

Calendars are configured in two places:

1. templates.yaml — defines which HA calendars to fetch events from:

  entity_id:
    - calendar.home          # ← replace with your main calendar
    - calendar.vacations     # ← replace or remove
    - calendar.public_holidays # ← replace or remove

To add a calendar, add another - calendar.your_calendar line and add a matching variable:

  - variables:
      events_home: "{{ agenda['calendar.home']['events'] | default([]) }}"
      events_vacations: "{{ agenda['calendar.vacations']['events'] | default([]) }}"
      events_holidays: "{{ agenda['calendar.public_holidays']['events'] | default([]) }}"
      events_new: "{{ agenda['calendar.your_calendar']['events'] | default([]) }}"  # ← add
      events_all: "{{ events_home + events_vacations + events_holidays + events_new }}"  # ← include

To remove a calendar, delete its entity_id line, its variable, and remove it from events_all.

2. dashboard.yaml — the calendar rendering code in the display lambda has special logic for:

  • Work shifts — events containing "früh"/"spät"/"nacht" are mapped to Polish shift names
  • Vacations — events containing "urlop"/"urlaub" suppress shift display and show "Urlop" instead
  • Trash collection — events containing "wywóz"/"śmieci" get a trash icon

To change these keywords, search for them in the analyze_day lambda function and modify to match your calendar event names.

Adjusting Timings

In dashboard.yaml, you can modify:

deep_sleep:
  run_duration: 90s       # How long the ESP stays awake
  sleep_duration: 20min   # How long between wake-ups

Shorter sleep_duration = more frequent updates but shorter battery life.


Home Assistant Template Sensors

The templates.yaml file provides template sensors that:

  1. Forecast sensors — extract daily forecast data (temperature, conditions, date) from your weather integration. The dashboard reads these via the ESPHome HA API.

  2. Calendar aggregator — merges events from 3 calendars into a single sensor.upcoming_events entity with a structured events attribute. It fetches 2 days of events to reliably cover both today and tomorrow.

Calendar Setup

Replace these calendar entity IDs in templates.yaml with your own:

entity_id:
  - calendar.home          # ← Your main calendar
  - calendar.vacations     # ← Vacation/leave calendar
  - calendar.public_holidays # ← Holidays calendar

Weather Integration

Replace the weather entity in templates.yaml (two occurrences — in the trigger and in the action):

entity_id: weather.forecast_home   # ← replace with your weather entity

And update all forecast references (forecast['weather.forecast_home']) to match:

state: "{{ forecast['weather.your_entity'].forecast[0].temperature }}"

Power Management

Parameter Value
Wake duration 90 seconds
Sleep duration 20 minutes
Wakeup pin GPIO1 (keeps awake when active)
Display power GPIO15 (turned off before sleep)
Battery ADC GPIO3 (×2 multiplier, 12 dB attenuation)
Battery range 3.0 V (0%) — 4.2 V (100%)
Deep sleep switch GPIO2 (slide switch, optional)

The display power is managed via GPIO15 — it is turned on at boot and turned off on shutdown to minimize current draw during sleep.

Deep Sleep Switch (GPIO2)

The SS-12D10G5 slide switch connected to GPIO2 controls whether the device is allowed to enter deep sleep:

Switch position Effect
ON Normal operation — device enters deep sleep for 20 minutes after each update
OFF KEEP_AWAKE — deep sleep is disabled; device stays awake indefinitely

The OFF position is useful during development, serial monitoring, or OTA firmware updates — when the device is in deep sleep most of the time, OTA is only possible within the 90 s wake window. Flipping the switch to OFF keeps it reachable permanently. Flip back to ON for normal battery-powered operation.


Troubleshooting

Problem Solution
Blank display after boot Check SPI wiring. Verify busy_pin is inverted. Check GPIO15 power output.
Display shows no data / all fields "b/d" The device booted but couldn't receive data from HA within the timeout. Check WiFi credentials in secrets.yaml. Verify HA is reachable and the API is enabled. Check ESPHome logs for connection errors.
Some data shows "b/d" (no data) The corresponding HA sensor entity doesn't exist or has no state. Check entity IDs.
Battery drains fast Reduce run_duration. Check that the display power is off during sleep. Verify deep sleep is actually entering (check logs).
Compilation error about missing fonts Make sure the fonts/ directory is in your ESPHome config folder alongside dashboard.yaml.
OTA update fails The device is in deep sleep most of the time. Use the wakeup pin or catch it during the 90 s wake window. Flash via USB if needed.

Credits

License

This project is licensed under the MIT License.

About

Battery-powered e-ink dashboard for Home Assistant — weather, calendar, sensors — built with ESPHome on LOLIN S3 Pro + Waveshare 7.5" e-Paper

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors