Skip to content

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

img

INKER - create interfaces without a single line of code

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
ePaper 7.5 ePaper 7.5 bp 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 USB C plug Aliexpress 1
USB C socket USB C socket Aliexpress 1
Slide switches Slide switch TLZWLA SS12F15VG4 Aliexpress 1
img Tactile button 6X6X10 Aliexpress 1
img 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.
img TP4056 charging module Aliexpress 1
100 kOhm Resistor 2
img 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

  1. Remove the red PWR LED from the Waveshare E-Paper ESP32 Driver Board to save power.

  2. 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).

  3. Set switch #1 on the ESP32 to position A.

    img

  4. 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).

  5. Assemble all housing parts in the specified order.

    img

  6. Solder the components according to the diagram.

    img
    Project Link

  7. Apply T-7000/B-7000 glue around the magnets to prevent the case from sliding on smooth metal surfaces.

    img


Firmware Installation

  1. Install ESPHome Device Builder
    Open your Home Assistant instance and show the add-on dashboard.

  2. Download the configuration file esphome-trmnl.yaml and modify the values to your own:

    Variable Value Description
    fw_version 1.0.1 esphome-trmnl Firmware version
    server_url http://192.168.1.123:80 Inker server URL
    mqtt_broker_ip 192.168.1.100 Mosquitto broker IP address
    manual_ip_static_ip 192.168.1.222 Static IP for the display
    manual_ip_gateway 192.168.1.1 Network gateway
    manual_ip_subnet 255.255.255.0 Network subnet
    name trmnl Device system name (lowercase, numbers, hyphens, up to 24 chars)
    friendly_name TRMNL Display name in Home Assistant
    model 7.50in-bV3 E-ink display model
    wifi_output_power 8.5dB Wi-Fi transmit power (8.5 to 20.5 dB)
    img_type BINARY Internal image encoding method
    img_format PNG Encoded image format
    img_url_setup_logo http://raw.github.../setup-logo.bmp Placeholder image
    deep_sleep_delay 1000ms Delay before entering sleep mode
    sleep_time 3600 Default sleep time (sec)
    error_sleep_duration 1200 Sleep time if an error occurs (sec)
    reset_duration 200 ms Display reset operation duration
    wifi_connect_delay 15s Wi-Fi connection timeout
    mqtt_connect_delay 500ms Delay after successful MQTT connection
    button GPIO32 Control button pin
    adc_pin GPIO34 ADC pin (for battery monitoring)
    deep_sleep_pin GPIO32 Pin to wake up from deep sleep
    clk_pin GPIO13 SPI Clock pin
    mosi_pin GPIO14 SPI MOSI pin
    cs_pin GPIO15 SPI Chip Select pin
    dc_pin GPIO27 Data/Command pin
    busy_pin GPIO25 Busy 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: true in the settings.)
    reset_pin GPIO26 Hardware reset pin
  3. Add to ESPHome secrets.yaml:

    mqtt_password: # password for MQTT connection
    wifi_ssid: # your wifi ssid
    wifi_password: # your wifi password
    
  4. 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.

  1. Create a directory

    Create a directory (e.g., ./inker) and enter it.
    mkdir ./inker
    cd ./inker
    
  2. Create docker-compose.yml:

    Create docker-compose.yml
    cat <<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
    
  3. Start the container

Run the following command in the directory:

Start container
docker compose up -d

Updating Inker

To update and restart:

Update and restart Inker.
docker compose pull && docker compose up -d
To free up space:
Remove unused Docker images.
docker image prune

Home Assistant Configuration

  1. Install Mosquitto broker
    Open Mosquitto broker panel in Home Assistant.
    In the "Configuration" tab, enter the "name" and "mqtt_password" used in the firmware.

  2. Install MQTT Integration
    MQTT
    Turn on the screen; it should appear in the MQTT integration.

  3. Install blueprint
    Open in Home Assistant


Battery Voltage Sensor Calibration (ADC Calibration)

  1. Record readings at battery voltages of 4.20, 4.19, 3.75, 3.50, and 3.00 V.

  2. 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.

img

  1. Create a new Data Source in Inker.
  2. Basic Information:
    • Name: e.g., HA Time.
    • Type: JSON API.
  3. Connection:
    • URL: http://<HA-IP-Address>:<port>/api/states/<sensor_name>
    • HTTP Method: GET.
    • Custom Headers: Add Authorization header.
      • Header name: Authorization
      • Header value: Bearer YOUR_TOKEN (replace with your Long-Lived Access Token from HA profile).
  4. Click Test URL. If successful, available fields will appear on the right.
  5. Settings:
    • Refresh Interval (seconds): Set desired update interval.
  6. Save Changes.

Phase 2. Widget Setup. Configuring the display.

img

  1. Create a new Custom Widget in Inker.
  2. Select the Data Source created above.
  3. Choose Display Type: JavaScript
  4. Widget Name: e.g., HA time.
  5. 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;
  1. Live Output will show the formatted time (e.g., Tue, May 5, 11:45).
  2. 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

img

JavaScript Code
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"]).

img


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\build folders.

Treat the author to a cup of tea ☕︎

Share with friends
Link copied!

Comments