Files
planar-EL512.256-H/README.md
T

270 lines
14 KiB
Markdown
Raw Normal View 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](https://github.com/adafruit/Adafruit-GFX-Library)
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](https://esphome.io) 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](docs/D1_mini_esp32_schema.jpg)
2026-10-05 21:13:36 +02:00
- 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](docs/datasheet/EL512.256-H3%20FRB.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](include/pins.h).
## Timing
2026-10-05 21:13:36 +02:00
| 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/](docs/datasheet/EL512.256-H3%20FRB.pdf)) 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
2026-10-18 23:46:24 +02:00
konosuba.* animated KonoSuba pages; konosuba_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
2026-10-18 23:46:24 +02:00
tools/ import_still_alive.py, konosuba_sprites.py
test/ unit tests (placeholder)
docs/ datasheet and notes
```
## Build and flash
Requires [PlatformIO](https://platformio.org/) (CLI or the VS Code extension).
The upload and monitor port is set to COM20 in `platformio.ini`.
```bash
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](esphome/el512-panel.yaml) turns the panel into a Home
2026-10-18 23:46:24 +02:00
Assistant display with sixteen pages: clock and weather, forecast, agenda, a sensor
dashboard with a message line, now playing, Plex, solar, Still Alive, and eight
animated themed pages ([Cyberpunk](#cyberpunk-pages), [KonoSuba](#konosuba-pages)). Pages
rotate on an interval; 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:
```yaml
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](components/el512/el512_helpers.h)).
Setup:
1. Copy [home_assistant/packages/el512_panel.yaml](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](docs/images/still_alive.jpg)
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](components/el512/still_alive_data.h) come from
[SDSkyKlouD/still-alive-web](https://github.com/SDSkyKlouD/still-alive-web) and can
be regenerated with:
```bash
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](https://store.steampowered.com/legal/)); the web recreation they
are imported from is by [SD SkyKlouD](https://github.com/SDSkyKlouD/still-alive-web).
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
Four animated ESPHome pages in a Cyberpunk look, drawn by
[components/el512/cyberpunk.cpp](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.
2026-10-18 23:46:24 +02:00
## KonoSuba pages
Four animated pages around the KonoSuba party, drawn by
[components/el512/konosuba.cpp](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](tools/konosuba_sprites.py),
which writes `components/el512/konosuba_sprites.h`; `--preview sprites.png` renders it
magnified for checking:
```bash
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.
## Using the driver
```cpp
#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](docs/README.md) for design notes and the plan.