Files
planar-EL512.256-H/README.md
T
mangavalkandClaude Opus 5.5 f46f7ab6c5 Add Interstellar and Your Name pages
Interstellar: Gargantua with a time dilation clock (Earth today vs.
Miller's planet), TARS with his settings and remarks about the house,
the Endurance spinning at a solar-driven RPM while a Ranger docks, and
the tesseract's bookshelf with the time in binary dust and the Message's
first word in Morse on a watch.

Your Name: comet Tiamat splitting over Itomori's lake, and
kataware-doki, a countdown to sunset from sun.sun where two silhouettes
stand on the crater rim during the twilight hour.

HomeData gains sunset, sunrise and sun elevation (with an ISO time
parser); wrap() and typed() move to el512_pages.h. Share Tech Mono 22
gets the full Latin set. All pages are in the renderer, checked with
normal, busy and empty data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:59:00 +02:00

22 KiB
Raw Blame History

Planar EL512.256-H on ESP32

Drive a Planar EL512.256-H (512 × 256, monochrome electroluminescent) panel from an ESP-WROOM-32 (classic ESP32), with an Adafruit GFX compatible driver so the usual drawing, text and font APIs work.

The panel has no frame memory and needs a continuous video stream. The ESP32's I2S0 peripheral in 8-bit parallel (LCD) mode streams it by DMA from a small ring of encoded lines that an interrupt keeps refilling (17 KB instead of a 142 KB full frame), so there is room left for Wi-Fi.

Status: runs on hardware with a correct, tear-free image (drawing goes into a back buffer that is swapped in at the start of a frame). Usable three ways: as an Arduino / PlatformIO library with Adafruit GFX, as an ESPHome display component for Home Assistant, and as the stand-alone Still Alive demo.

Hardware

  • ESP32 board: a Wemos D1 mini ESP32 is used here (esp32dev in PlatformIO, wemos_d1_mini32 in ESPHome); pinout in docs/D1_mini_esp32_schema.jpg
  • Planar EL512.256-H, configured for two-bit parallel mode: jumper 2 (DCONFIG) of pin strip PS1 removed, jumper 1 (SELFTST) fitted (datasheet Table 3)
  • Datasheet: docs/datasheet/EL512.256-H3 FRB.pdf
  • Check the panel's logic-level and supply requirements in the datasheet before wiring.

Wiring

Panel signal Panel pin (Table 1) Function ESP32 GPIO DMA byte bit
VCLK J1-13 Video clock, data latched on rising edge 18 I2S0 BCK
VID J1-15 Pixel data, even columns (x = 1, 3, …) 19 0
TVID J1-7 Pixel data, odd columns (x = 0, 2, …) 21 1
HS J1-11 Horizontal sync 22 2
VS J1-9 Vertical sync (rising edge used) 23 3
ENABLE J2-6 Display enable, low = off 25 —
_LOWPWR J2-5 Low-power mode, low = on 26 —
GND J1-6, 8, 10, 12, 14, 16 Signal return GND —

The datasheet numbers columns 1 to 512 from the left; x in the code is 0-based, so x = 0 is column 1 (odd, TVID).

Power (Table 1, Table 4): Vcc1 +5 V logic on J1-3, 4 (4.75–5.25 V, 0.2 A max); Vcc2 +11…+30 V on J1-1, 2 (0.9 A max at 12 V). J1-5 is reserved: do not connect.

The panel inputs are HCT compatible with internal pull-ups of unstated voltage, and the ESP32 is not 5 V tolerant, so drive them through a 74HCT245 or 74AHCT541 powered from 5 V (see the plan). ENABLE is pulled high inside the panel, so the panel scans while the ESP32 is still booting and VCLK is not running yet; add a pull-down (for example 10 kΩ) on the buffer's ENABLE input so the panel stays off until begin() starts the stream. Keep the cable under 300 mm, or add 47 Ω series resistors for up to 2 m.

With Wi-Fi on, the panel can blank for a moment when the ESP32 transmits (seen at page changes, which report the new page to Home Assistant). The refresh measured clean on the ESP32 side at those moments, so the signal is disturbed on the wires: connect all six J1 GND pins, run them along the signal wires, keep the antenna away from the cable, and preferably use the 5 V buffer above. Brief blanking during boot is expected until ENABLE has its pull-down.

The pin map lives in include/pins.h.

Timing

Parameter Value Datasheet limit (Tables 5, 6)
Pixel clock (VCLK) 5 MHz, 2 pixels per clock 30 MHz max, keep running
HS high (T7) 256 clocks 256 in two-bit mode, even
HS low (T6) 16 clocks 4 clocks min
HS period (T8) 272 clocks = 54.4 µs 50 µs min
Lines per frame 261 (256 visible + 5 blanking) 256 rows + T1
Front porch (T1) 220.8 µs (last row end to VS rise) 100 µs min
Refresh 5 MHz / (272 × 261) ≈ 70.4 Hz 70 Hz typ, 75 Hz max

The datasheet (docs/datasheet/) is the source of truth; if anything here disagrees with it, the datasheet wins.

Layout

platformio.ini          build config: envs still_alive (default) and esp32dev
include/pins.h          pin map
components/el512/       shared driver + ESPHome component (one copy of the code)
  el512_panel.*         framebuffer, DMA ring, refresh interrupt (ESP-IDF API)
  still_alive.*         Portal end-credits show, 5x7 font (el512_font.h)
  still_alive_data.h    show content, generated by tools/import_still_alive.py
  cyberpunk.*           animated Cyberpunk-style ESPHome pages
  konosuba.*            animated KonoSuba pages; konosuba_sprites.h from tools/
  steins.*              animated Steins;Gate pages; steins_sprites.h from tools/
  trek.*                animated Star Trek / Lower Decks pages; trek_sprites.h from tools/
  interstellar.*        animated Interstellar pages
  yourname.*            animated Your Name pages; yourname_sprites.h from tools/
  el512_pages.h         home data and helpers shared by the themed pages
  el512_display.*       ESPHome display platform `el512`
  __init__.py, display.py   ESPHome config schema
lib/EL512/              Arduino library: Adafruit_GFX front end (EL512_GFX)
src/still_alive/        Portal-style end-credits demo (env still_alive)
src/test_images/        test sketch: cycles through 12 test images (env esp32dev)
esphome/                el512-panel.yaml (Home Assistant display), el512-smoke.yaml
home_assistant/packages/  el512_panel.yaml: helper sensors for the ESPHome pages
tools/                  import_still_alive.py, konosuba_sprites.py, steins_sprites.py,
                        trek_sprites.py, yourname_sprites.py, render_pages.py
                        (+ render/: PC renderer)
test/                   unit tests (placeholder)
docs/                   datasheet and notes

Build and flash

Requires PlatformIO (CLI or the VS Code extension). The upload and monitor port is set to COM20 in platformio.ini.

pio run -e still_alive -t upload   # end-credits demo
pio run -e esp32dev -t upload      # test images
pio device monitor

ESPHome / Home Assistant

esphome/el512-panel.yaml turns the panel into a Home Assistant display with thirty pages: clock and weather, forecast, agenda, a sensor dashboard with a message line, now playing, Plex, solar, Still Alive, and twenty-two animated themed pages (Cyberpunk, KonoSuba, Steins;Gate, Star Trek, Interstellar, Your Name). Pages rotate on an interval, in a random order that is reshuffled every hour; Home Assistant gets a page select, a pause switch, the interval, a message text, a "Play Still Alive" button, and Display / Low power mode switches (ENABLE, _LOWPWR).

The component comes straight from this repository:

external_components:
  - source:
      type: git
      url: https://mangavalk.com/mangavalk/planar-EL512.256-H.git
      ref: main
    components: [el512]

display:
  - platform: el512
    id: panel
    enable_pin: GPIO25        # optional, J2-6
    low_power_pin: GPIO26     # optional, J2-5
    still_alive_page: page_still_alive   # optional
    animated_pages: [page_cyber_hud]     # optional: redraw these while shown
    animation_interval: 66ms             # optional, default 66ms (~15 fps)
    pages: ...

Data pins default to the wiring above (vclk_pin, vid_pin, tvid_pin, hs_pin, vs_pin can be changed). Page lambdas can use helpers such as el512::split(), el512::weather_icon(), el512::fit() and el512::watts() (el512_helpers.h).

Setup:

  1. Copy home_assistant/packages/el512_panel.yaml to /config/packages/ (with homeassistant: packages: !include_dir_named packages in configuration.yaml) and reload the template entities. It adds sensor.el512_forecast, sensor.el512_agenda, sensor.el512_now_playing and sensor.el512_plex_streams.
  2. Put el512-panel.yaml in the ESPHome add-on and add el512_api_key and el512_ota_password to its secrets.yaml. The entity IDs at the top (substitutions) and in the sensor list are this installation's; adjust them.
  3. Flash once over USB (or use ESPHome's web installer), then add the device in Home Assistant; later updates go wirelessly from the add-on.

The component logs a warning when the ESP32 side of the refresh has a problem (DMA FIFO underrun, late ring refill, swap timeout); otherwise a debug summary once a minute.

For local builds, esphome/el512-panel-local.yaml (git-ignored) is the same config with source: {type: local, path: ../components}; build it with the ESPHome CLI from PowerShell or cmd (the ESP-IDF installer refuses to run under Git Bash/MSYS).

Still Alive demo

Still Alive demo running on the EL512.256-H

A recreation of Portal's ending: timed song text typed into a console on the left, credits scrolling top right, ASCII art bottom right. The same show runs as the PlatformIO demo and as a page in ESPHome. The text, timing, art and credits in components/el512/still_alive_data.h come from SDSkyKlouD/still-alive-web and can be regenerated with:

python tools/import_still_alive.py   # stops after the credits text (--until-ms 200000)
pio run -e still_alive -t upload

Start the song when the serial log says: 7.75 s after reset in the PlatformIO demo, or 7.75 s after the Still Alive page is shown in ESPHome.

Disclaimer: "Still Alive" was written by Jonathan Coulton for Portal (2007). Portal, its credits and ASCII art are © Valve Corporation (see Valve Legal Info); the web recreation they are imported from is by SD SkyKlouD. They are included only for personal, non-commercial use in this hobby project, and all rights remain with their owners. This project is not affiliated with or endorsed by Valve.

Cyberpunk pages

Cyber HUD Cyber Breach
Cyber Scan Cyber Split

Four animated ESPHome pages in a Cyberpunk look, drawn by components/el512/cyberpunk.cpp with Orbitron, Rajdhani and Share Tech Mono (Google Fonts). All of them take part in the rotation:

  • Cyber HUD: clock, outside weather, power (solar, house, grid) and four rooms in cut-corner frames, with a scrolling ticker.
  • Cyber Breach: a breach-protocol code matrix that solves itself: the cursor walks the active row or column, codes fill the buffer, daemons upload, then a new puzzle starts.
  • Cyber Scan: an optics boot log with live values, a calibration zoom, then a rotating reticle around the clock and a typed-out scan of the house.
  • Cyber Split: compact clock and readouts on the left, a small breach matrix on the right.

They are listed under animated_pages, so they redraw about 15 times a second while shown; each page starts its intro (wipe-in, glitches) when it appears. The fonts and Home Assistant values come from the globals cp_fonts and cp_home in el512-panel.yaml. To keep only some of them, remove the others from pages, animated_pages, the page select, on_page_change and the rotation loop count.

These pages are inspired by the look of Cyberpunk 2077 and use no assets from it. Cyberpunk 2077 is a trademark of CD PROJEKT S.A.; this project is not affiliated with or endorsed by CD PROJEKT RED.

KonoSuba pages

KonoSuba Explosion KonoSuba Guild
KonoSuba Card KonoSuba Battle

Four animated pages around the KonoSuba party, drawn by components/el512/konosuba.cpp with Cinzel, Nunito, Press Start 2P, Noto Sans JP and Dela Gothic One (Google Fonts), all in the rotation:

  • KonoSuba Explosion: Megumin chants under a night sky while a magic circle draws itself, then EXPLOSION (flash, shockwave, mushroom cloud, screen shake). She ends up flat on the ground, Kazuma rates the explosion, and the day's count goes up.
  • KonoSuba Guild: the Adventurers' Guild board: your next agenda events as quest notes (rank, deadline, reward in Eris, NEW! for today), the weather as a hazard notice and today's solar yield as the cabbage-harvest bounty, while cabbages fly by.
  • KonoSuba Card: Megumin's adventurer card: level from today's solar yield, mana from solar power, stamina from what the house load leaves, a daily luck roll, and the explosions cast today.
  • KonoSuba Battle: the party against a giant toad in a JRPG window; the battle log uses live values (Kazuma steals the grid import, Megumin's explosion does the solar watts in damage).

The pixel art lives as text in tools/konosuba_sprites.py, which writes components/el512/konosuba_sprites.h; --preview sprites.png renders it magnified for checking:

python tools/konosuba_sprites.py --preview sprites.png

These pages are fan art: the pixel art and all texts are original, and no material from the series is used. KonoSuba (Kono Subarashii Sekai ni Shukufuku wo!) is by Natsume Akatsuki and Kurone Mishima, © its rights holders; this project is not affiliated with or endorsed by them.

Steins;Gate pages

Divergence Meter D-Mail
Gadget Lab World Lines

Four animated pages from the Future Gadget Lab, drawn by components/el512/steins.cpp with Nixie One, Special Elite and Noto Sans JP (plus fonts shared with the other themed pages), all in the rotation:

  • Divergence Meter: eight Nixie tubes roll and lock onto a world line (Steins Gate first), then show the time; every change ripples like a Reading Steiner.
  • D-Mail: a flip phone types the panel's Message text (36 characters, or a default) as a D-mail, the Phone Microwave (name subject to change) spins a banana and discharges, and the world line shifts. Upa sits on top; Kurisu keeps watch.
  • Gadget Lab: the lab inventory, where every gadget is a live sensor (#008, the Phone Microwave, draws the house load), with a detail card and Okabe in his lab coat.
  • World Lines: the alpha, beta and Steins Gate attractor fields with today's solar power plotted as the world line, futures branching off "you are here". The history is kept on the device in 15-minute slots (HomeData::record()), so it fills during the day and starts over at midnight or after a restart.

The pixel art lives in tools/steins_sprites.py (same --preview option as the KonoSuba sprites).

These pages are fan art: the pixel art and all texts are original, and no material from the series is used. Steins;Gate is © MAGES. and its other rights holders; this project is not affiliated with or endorsed by them.

Star Trek pages

LCARS Status Duty Roster
Warp Core Captain's Log

Four animated pages aboard a Lower Decks-era ship, drawn by components/el512/trek.cpp with Antonio (standing in for the LCARS typeface), all in the rotation:

  • LCARS Status: elbows, blocks and pills around the ship's systems: power distribution (solar, house, grid), environmental (four rooms), long range sensors (weather), the fabrication bay (3D printer state), a stardate and a data cascade.
  • Duty Roster: the U.S.S. Cerritos roster: Mariner, Boimler, Tendi and Rutherford each get one of your next agenda events as their assignment (or a chore of their own), with a scrolling shipboard notice.
  • Warp Core: plasma pulses rush into the reaction chamber faster as solar power rises; engineering readouts for core output, containment (what the house load leaves) and the plasma injectors (grid), and Rutherford with advice. A grid import above 3 kW is a core breach (it's the oven).
  • Captain's Log: a viewscreen at warp that drops out next to a planet, over a captain's log typed from the weather, power and the next agenda event.

Red alert: when the 3D printer (printer substitution, the Moonraker current_print_state sensor) goes to paused or error, the panel jumps to LCARS Status in red alert and holds the rotation until the printer carries on.

The pixel art lives in tools/trek_sprites.py.

These pages are fan art: the pixel art, layouts and texts are original, and no material from the series is used. Star Trek and Star Trek: Lower Decks are trademarks of CBS Studios Inc.; this project is not affiliated with or endorsed by them.

Interstellar pages

Gargantua TARS
Endurance Tesseract

Four animated pages drawn by components/el512/interstellar.cpp with Michroma and Share Tech Mono, all in the rotation:

  • Gargantua: the black hole with its lensed accretion disk turning, and time dilation: today's Earth time next to today on Miller's planet (one hour there is seven years here), with a bar filling towards the next Miller second, 17 Earth hours away.
  • TARS: the robot's four slabs stepping in turn, his settings (humor, honesty, discretion, trust) next to the home's readings, and dry remarks about the house.
  • Endurance: the ring spins at an RPM set by solar power while a Ranger flies in, matches the spin and docks in the hub.
  • Tesseract: the bookshelf from behind with a book falling out, the time as binary bars of dust, and the first word of the Message (or STAY) in Morse code on a watch's second hand.

These pages are fan art: drawings and texts are original. Interstellar is © Paramount Pictures and Warner Bros.; this project is not affiliated with or endorsed by them.

Your Name pages

Comet Tiamat Kataware-doki

Two animated pages drawn by components/el512/yourname.cpp with Yuji Syuku, Caveat and Cormorant Garamond, both in the rotation:

  • Comet Tiamat: the comet crosses the night sky over Itomori's round lake and splits in two, shedding sparks; the shrine gate on the hill, the town's lights, the clock and the weather.
  • Kataware-doki: a countdown to sunset (at night, to sunrise) from Home Assistant's sun.sun. In the twilight hour, from 45 minutes before sunset until civil dusk, the sky glows, the sun sinks behind the crater rim and two silhouettes stand on it.

The pixel art lives in tools/yourname_sprites.py.

These pages are fan art: the pixel art, drawings and texts are original. Your Name (Kimi no Na wa) is by Makoto Shinkai, © its rights holders; this project is not affiliated with or endorsed by them.

Rendering pages on the PC

tools/render_pages.py draws the themed pages to PNG without the panel, to check layouts before flashing and to make the images above. It compiles the page code natively with g++ against ESPHome's own display and font code (from the installed esphome package) and the exact font bitmaps of the last esphome compile, so text sizes and positions match the device. Pages are drawn at chosen moments after they were shown, with sample data:

esphome compile esphome/el512-panel-local.yaml        # once, and after font changes
python tools/render_pages.py                          # all pages -> .render/out
python tools/render_pages.py --page st_lcars --at 0,2000 --scenario alert
python tools/render_pages.py --scenario busy --sheet  # long names, high values
python tools/render_pages.py --scenario empty         # no data yet
python tools/render_pages.py --examples               # docs/images/pages/*.png

It needs a C++20 g++ (MinGW-w64 works on Windows) and Pillow. The host stand-in for the display and the sample data are in tools/render/.

Using the driver

#include <EL512.h>
#include "pins.h"

EL512Panel panel({el512pins::VCLK, el512pins::VID, el512pins::TVID, el512pins::HS,
                  el512pins::VS, el512pins::ENABLE, el512pins::LOWPWR_N});
EL512_GFX display(panel);

void setup() {
  display.begin();
  display.setTextColor(1);
  display.print("Hello");
  display.display();   // push the drawing buffer to the refresh stream
}

See docs/README.md for design notes and the plan.