E-Ink Display | ESPHome-TRMNL 7.5"
Files
Printables Thingiverse Creality Cloud MakerWorld github ☕︎ Treat the author to a cup of tea 🡥
Description
This is an electronic paper (E-Ink) screen running on ESPHome firmware, designed to display information from your Inker server and managed by Home Assistant. The project was not created by the ESPHome or TRMNL team and is not affiliated with them.
flowchart LR
HA["Home <br> Assistant"] -- JSON API --> Inker["Inker"]
RSS["RSS Feeds"] --> Inker
Inker -- PNG --> ESPH["ESPHome <br> E-Ink Display"]
HA <--> MQTT_Int["MQTT <br> Integration"]
HA --> BP["blueprint"]
BP --> MQTT_Int
MQTT_Int <--> Mosq["Mosquitto <br> broker"]
Mosq <-- MQTT --> ESPH
Features:
- Energy Efficiency: The screen pauses updates when you are not at home or in the room.
- Extensibility: Thanks to ESPHome, the device supports a huge number of additional sensors.
- MQTT Communication: Instant data exchange with your server.
Case:
- Compactness: Minimalist case (175.6 x 116.6 x 11.25 mm) with thin bezels, focusing all attention on the screen.
- Ergonomics: Convenient buttons are located in plain sight—no more fumbling for them on the back panel.
- Three Mounting Options: kickstand (desktop), hanger (wall-mounted), magnets (wall-mounted).
- Simple Assembly: The case is assembled with reliable latches—no extra screws or complex mounting required.
Components
| Image | Component | Link | Qty | Note |
|---|---|---|---|---|
![]() |
7.5" ePaper Display | Aliexpress | 1 | This is a 3-color B/W/R display, but only B/W will work due to RAM limitations. Supported displays Max dimensions: 171.20 x 112.20 x 1.50 mm |
![]() |
USB C plug | Aliexpress | 1 | |
![]() |
USB C socket | Aliexpress | 1 | |
![]() |
Slide switch TLZWLA SS12F15VG4 | Aliexpress | 1 | |
![]() |
Tactile button 6X6X10 | Aliexpress | 1 | |
![]() |
Waveshare E-Paper ESP32 Driver Board (V3) | Aliexpress | 1 | |
| — | Li-ion Battery | — | 1 | Max thickness 7.4 mm. Updating once per hour, a 1400 mAh (5.18 Wh) battery lasts about a month. |
![]() |
TP4056 charging module | Aliexpress | 1 | |
| — | 100 kOhm Resistor | — | 2 | |
![]() |
8x3mm Neodymium magnet | — | 5 | 5 magnets securely hold the 150g display on a metal door. |
| — | T-7000/B-7000 Glue | — | 1 |
Estimated cost per screen (including 20% VAT and shipping): ~$50.
Assembly
-
Remove the red PWR LED from the Waveshare E-Paper ESP32 Driver Board to save power.
-
To make the ESP32 driver board thinner, desolder the pins on its bottom side. (Tip: lift and remove the plastic spacer between pins with a screwdriver first).
-
Set switch #2 on the ESP32 to the "ON" position. This switch controls power to the UART module via USB. When the module is not in use, you can manually turn it off to save energy (note: if switch 2 is "OFF", firmware cannot be uploaded).
-
Solder the components according to the diagram.
Project Link -
Apply T-7000/B-7000 glue around the magnets to prevent the case from sliding on smooth metal surfaces.
Firmware Installation
-
Download the configuration file esphome-trmnl.yaml and modify the values to your own:
Variable Value Description fw_version1.0.1 esphome-trmnlFirmware version server_urlhttp://192.168.1.123:80Inker server URL mqtt_broker_ip192.168.1.100Mosquitto broker IP address manual_ip_static_ip192.168.1.222Static IP for the display manual_ip_gateway192.168.1.1Network gateway manual_ip_subnet255.255.255.0Network subnet nametrmnlDevice system name (lowercase, numbers, hyphens, up to 24 chars) friendly_nameTRMNLDisplay name in Home Assistant model7.50in-bV3E-ink display model wifi_output_power8.5dBWi-Fi transmit power (8.5 to 20.5 dB) img_typeBINARYInternal image encoding method img_formatPNGEncoded image format img_url_setup_logohttp://raw.github.../setup-logo.bmpPlaceholder image deep_sleep_delay1000msDelay before entering sleep mode sleep_time3600Default sleep time (sec) error_sleep_duration1200Sleep time if an error occurs (sec) reset_duration200 msDisplay reset operation duration wifi_connect_delay15sWi-Fi connection timeout mqtt_connect_delay500msDelay after successful MQTT connection buttonGPIO32Control button pin adc_pinGPIO34ADC pin (for battery monitoring) deep_sleep_pinGPIO32Pin to wake up from deep sleep clk_pinGPIO13SPI Clock pin mosi_pinGPIO14SPI MOSI pin cs_pinGPIO15SPI Chip Select pin dc_pinGPIO27Data/Command pin busy_pinGPIO25Busy pin (To prevent irreversible damage to the display, it is necessary to invert the BUSY pin on models gdew0154m09, Waveshare 7.30in-f, and Waveshare 7.50in V2 (and likely on V3 and other newer models). Set the BUSY pin to inverted: truein the settings.)reset_pinGPIO26Hardware reset pin -
Add to ESPHome
secrets.yaml:mqtt_password: # password for MQTT connection wifi_ssid: # your wifi ssid wifi_password: # your wifi password - Upload the edited yaml to ESPHome and install the firmware on the ESP32.
Installing Inker
Important Note
This guide assumes you already have Home Assistant installed. You will also need Docker with the Docker Compose plugin. Instructions are based on Debian OS.
-
Create a directory
Create a directory (e.g., ./inker) and enter it.mkdir ./inker cd ./inker -
Create
docker-compose.yml:Create docker-compose.ymlcat <<EOF > docker-compose.yml services: inker: image: wojooo/inker:latest container_name: inker restart: unless-stopped ports: - "80:80" volumes: - postgres_data:/var/lib/postgresql/17/main - redis_data:/data - uploads_data:/app/uploads environment: TZ: UTC ADMIN_PIN: "1111" volumes: postgres_data: redis_data: uploads_data: EOF -
Start the container
Run the following command in the directory:
docker compose up -d
Updating Inker
To update and restart:
docker compose pull && docker compose up -d
docker image prune
Home Assistant Configuration
-
Install Mosquitto broker
In the "Configuration" tab, enter the "name" and "mqtt_password" used in the firmware. -
Install MQTT Integration
Turn on the screen; it should appear in the MQTT integration.
Battery Voltage Sensor Calibration (ADC Calibration)
-
Record readings at battery voltages of 4.20, 4.19, 3.75, 3.50, and 3.00 V.
-
Enter the data into the filter. To the left of (->) is the sensor value, to the right is the true value:
sensor: - platform: adc ... id: battery_voltage ... - calibrate_linear: - 3.22 -> 3.75 - 3.64 -> 4.19 - 3.89 -> 4.20 ...
Creating Custom Widgets
Example 1. Home Assistant "Time and Date"
Phase 1. Data Source Setup. Telling Inker where to get data from HA.
- Create a new Data Source in Inker.
- Basic Information:
- Name: e.g.,
HA Time. - Type: JSON API.
- Name: e.g.,
- Connection:
- URL:
http://<HA-IP-Address>:<port>/api/states/<sensor_name> - HTTP Method: GET.
- Custom Headers: Add
Authorizationheader.- Header name:
Authorization - Header value:
Bearer YOUR_TOKEN(replace with your Long-Lived Access Token from HA profile).
- Header name:
- URL:
- Click Test URL. If successful, available fields will appear on the right.
- Settings:
- Refresh Interval (seconds): Set desired update interval.
- Save Changes.
Phase 2. Widget Setup. Configuring the display.
- Create a new Custom Widget in Inker.
- Select the Data Source created above.
- Choose Display Type: JavaScript
- Widget Name: e.g.,
HA time. - JavaScript Transform:
- Output Mode: Single Value.
- JavaScript Code:
const state = $.state;
const dateObj = new Date(state);
const formatter = new Intl.DateTimeFormat('en-US', {
weekday: 'short', // Mon, Tue...
day: 'numeric', // 15
month: 'short', // Mar
hour: '2-digit', // 22
minute: '2-digit' // 13
});
const result = formatter.format(dateObj);
return result;
- Live Output will show the formatted time (e.g.,
Tue, May 5, 11:45). - Save Changes.
Example 2. Home Assistant "Calendar"
This widget collects events from multiple HA calendars and displays them as a single line.
- Configured similarly to Example 1, but with a different URL that returns the state of all entities:
http://<HA_IP_ADDRESS>:<PORT>/api/states
const entities = ["calendar.test1", "calendar.test2"];
const groupedEvents = {}; // Object for grouping: { "Today (05.05)": ["Event 1", "Event 2"] }
entities.forEach(entityId => {
const calendar = $.find(item => item.entity_id === entityId);
if (calendar && calendar.attributes && calendar.attributes.start_time) {
const eventName = calendar.attributes.message;
const startTime = calendar.attributes.start_time;
const eventDate = new Date(startTime.replace(' ', 'T'));
const today = new Date();
eventDate.setHours(0, 0, 0, 0);
today.setHours(0, 0, 0, 0);
const daysUntilEvent = Math.round((eventDate - today) / (1000 * 60 * 60 * 24));
if (daysUntilEvent === 0 || daysUntilEvent === 1) {
const dayStr = daysUntilEvent === 0 ? "Today" : "Tomorrow";
const day = String(eventDate.getDate()).padStart(2, '0');
const month = String(eventDate.getMonth() + 1).padStart(2, '0');
const label = `${dayStr} (${day}.${month})`;
if (!groupedEvents[label]) {
groupedEvents[label] = [];
}
groupedEvents[label].push(eventName);
}
}
});
const result = Object.keys(groupedEvents).map(label => {
const events = groupedEvents[label].join(", ");
return `${label} ${events}`;
});
return result.join(" | ");
- In the Live Output block below, you will immediately see the result of your code execution. If data is being received correctly from HA, it will display calendar data for the current day or the next (for example:
"Today (05.05)": ["Event 1", "Event 2"]).
FAQ
- How to change Wi-Fi SSID and password? Connect via USB open web.esphome.io CONNECT () Configure Wi-Fi (ESP32 must be active).
Help! Something is not working.
-
Incorrect display: if image quality is low, try toggling switch #1 on the ESP32.
-
Nothing works: open web.esphome.io, connect via USB, and check the logs.
-
ESPHome won't build: delete
C:\Users\<user>\.platformio\and\.esphome\buildfolders.
Comments





















