Adventures with e-ink & ESP32

December 1, 2025

A while ago I came across people who had created E-ink dashboards [1], and thought they looked great! I ordered an ESP32 and a Waveshare E-ink display with grand ambitions of building something useful.

That was over a year ago. The display sat collecting dust. Initially, all I got was an image to display. I’m not well versed in writing Arduino/C/device code.

Fast-forward to after I moved to a new apartment. AI tools improved significantly, and I decided to revisit and see if I could use this hardware. With AI’s help, I was able to get this working pretty well!

Weather Dashboard V1

But this really wasn’t something we looked at much and wasn’t very useful.

As the new light rail stations were opening up in Federal Way, I saw someone’s transit display [2,3] and got inspired. If not more useful, this was at least something cooler than a simple weather display (everyone has a weather app, not everyone has a light rail display).

Light Rail Map:

The project turned out better than expected. Having real-time transit data on the wall is just cool and sometimes useful when you want to get on the train. It’s also been great to see the train scale up & down to handle commuting traffic.

All the configuration files and Home Assistant automations are in the GitHub repo if you want to copy this setup.

Some technical details

Moving from the V1 weather dashboard to V2 Light rail display, we ran into some fun times:

Background:

We’re using ESP32 chips, with ESPHome that integrates seamlessly with Home Assistant.

One Bus Away API Data

I’m pulling data from OneBusAway for the train locations. To migrate to a working LR display, AI (again, super useful) broke the problem down and worked towards a solution piece by piece. This was first starting out with displaying the stations, then adding trains, then integrating data, etc.

All went great thanks to Claude until integration real life data. I checked my API key (which btw, you have to email Sound Transit for), and that all worked on my computer, so I had no idea why my ESP 32 kept crashing.

Eventually, I realized that it was running out of memory processing the full API response. Fun.

Thankfully this was already configured with EspHome, so with some Home Assistant configurations later we’re off to the races with API parsing via Home Assistant:

rest:
  - resource: !include ONEBUSAWAY_URL.json
    scan_interval: 30
    sensor:
      - name: "Link API"
        value_template: "{{ value_json.data.list | length }}"

      - name: "Link Positions"
        value_template: >
          {% set data = value_json.data %}
          {%- for trip in data.list -%}
            {%- if not loop.first -%},{%- endif -%}
            {{ (trip.status.distanceAlongTrip / trip.status.totalDistanceAlongTrip) | round(3) }}
          {%- endfor -%}

      - name: "Link Directions"
        value_template: >
          {% set data = value_json.data %}
          {%- for trip in data.list -%}
            {%- for ref_trip in data.references.trips -%}
              {%- if trip.tripId == ref_trip.id -%}
                {%- if not loop.first -%},{%- endif -%}
                {{ ref_trip.directionId }}
                {%- break -%}
              {%- endif -%}
            {%- endfor -%}
          {%- endfor -%}

As part of this move to the light rail, I fixed some other small bugs too. There are different versions of the 7.5” Waveshare display and I was using settings for the wrong one. There were also a ton of “Timeout” errors on display updates. Somehow I eventually came across the fact that the BUSY pin was inverted, so it never got the signal that the update completed.

How to build one

If you want to try this, here’s what you actually need:

Hardware:

  • Any basic ESP32 dev board ($10-15)
  • Waveshare 7.5” E-Ink Display V2 (not the original version - $60-80)

For my specific hardware:

  • Use 7.50inV2alt display type, not the default 7.50inV2
  • Set BUSY pin as inverted
  • Standard SPI pin connections (see my config for exact mappings)