Skip to content

ShaneOnChain/ledger-phosphor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LEDGER PHOSPHOR boot splash glowing on a 1983 Apple Monitor II green CRT

Ledger Phosphor

A live XRP Ledger ticker rendered on a 1983 Apple green-phosphor CRT.
An ESP32 synthesizes composite video straight from its DAC pin — no PC, no video card.
The microcontroller is the video source.

build status platform: ESP32-WROOM-32 composite NTSC license: MIT


An ESP32-WROOM-32 drives a vintage Apple Monitor II (12" green monochrome CRT, 1983) over a single RCA cable, showing the live XRP/USD price and the XRP Ledger closing in real time.

ESP32 GPIO25 (DAC1) ──yellow──> RCA center pin ─┐
ESP32 GND ───────────black────> RCA shield ─────┴─> Apple Monitor II composite in

That is the entire interface — one signal wire and one ground. The ESP32's DAC generates a real NTSC composite signal; the monitor just paints it. Wiring detail: media/wiring.svg · docs/HARDWARE.md.

The display

Live green-phosphor dashboard (price is read from the on-chain XRPL DEX over the same WebSocket as the ledger feed — no API key, no second connection):

┌──────────────────────────────────────────────────────┐
│ [X]  XRP / USD                              XRPL DEX  │
│      $2.1416                                          │
│ ──────────────────────────────────────────────────── │
│ LEDGER INDEX                                          │
│ #105,088,566                                          │
│                                                       │
│ TXNS 60     TPS 17.1                                  │
│ CLOSE 3.8s     SEEN 1,204                             │
│ OPEN FOR                                       2.3s   │
│ ====================|                                 │
│ IN-FLIGHT                                       247   │
│ ####  ####  ##/                                       │
│ ##############                                        │
│ ──────────────────────────────────────────────────── │
│ * LIVE · s1.ripple.com                     14:29:57   │
└──────────────────────────────────────────────────────┘

At power-on it plays a one-shot, filmable boot sequence — a CRT warm-up, a stacked LEDGER PHOSPHOR / by ShaneOnChain splash, a real self-test that ticks each phase to [OK] as the live connection comes up, a READY hold, and a channel-change wipe into the dashboard. Full layout and data-source mapping: docs/DESIGN.md.

First-light composite test pattern on the CRT   The build on the bench — ESP32 driving the Apple Monitor II

Left: the first_light composite test pattern. Right: the build on the bench.

Why this was hard

The dashboard is the easy part. The interesting problem is making a no-PSRAM microcontroller be a stable composite-video source and a live blockchain client at the same time — on two cores, in ~45 KB of contiguous heap.

  • Composite NTSC from a bare DAC pin. GPIO25 swings the 0–1 V luma of a real NTSC signal directly out of the ESP32's DAC (via the legacy I²S→DAC path). No display controller, no scaler.
  • A dual-core jitter fix. The composite line-refill ISR installs at the lowest interrupt level, and any FreeRTOS critical section masks it — so a busy TLS/WebSocket loop on the same core starved it and scanlines wobbled under load. The fix: pin all networking to core 0 and leave core 1 for video + render alone, so no network critical section can ever mask the line ISR. Cross-core state is lock-free (volatile atomic scalars — a portMUX would re-introduce the very critical sections we moved off-core).
  • Fitting WSS + TLS + a video framebuffer in a no-PSRAM WROOM-32. A persistent TLS WebSocket, WiFi/lwIP, and a double composite framebuffer leave ~45 KB of contiguous heap. The network-task stack is allocated before TLS so the TLS arena lands around it, not squeezed by it.
  • Price from the on-chain DEX over the existing WebSocket. A second HTTPS/TLS context for a price API doesn't fit beside the firehose, so XRP/USD is derived from the XRP/RLUSD DEX order book on the socket that's already open — zero extra TLS, no API key.
  • An anti-burn pixel orbiter. To protect the 40-year-old phosphor, the whole UI drifts a few pixels along a slow precessing Lissajous — invisible in normal viewing, held dead-center during the filmed boot intro.
  • Self-healing networking. A wedged public-node firehose is cleared by a core-0 WiFi-stack bounce without a reboot or an intro replay; a stale watchdog is the last-resort backstop.

Dual-core architecture: networking on core 0, video on core 1, sharing lock-free state

Full design rationale and the data-source mapping live in docs/DESIGN.md.

Hardware

Part Notes
Apple Monitor II (A2M2010) 12" green-phosphor CRT, 1983. Composite in via a single RCA jack. Monitor /// behaves the same.
ESP32-WROOM-32 dev board Must be the original ESP32 — the composite I²S→DAC path runs only on the classic chip, not the S2/S3/C3.
Slim RCA pigtail (RCA → bare wires) The whole connection. Slim barrel seats in the recessed Apple jack.

Wiring: ESP32 GPIO25 to RCA center, GND to RCA shield, into the Apple Monitor II composite in, plus an optional attenuator

Two wires: GPIO25 → RCA center, GND → RCA shield — that's the whole connection. A direct wire usually syncs fine; if the picture is washed out or unstable, add the small attenuator shown above. Bill of materials, connector notes, and the signal-level math: docs/HARDWARE.md.

⚠️ You never need to open the monitor for this project — it's all on the external RCA jack. A CRT stores a dangerous high-voltage charge even unplugged; never go inside one unless you know how to discharge it.

Build & flash

Composite video uses the ESP32's legacy I²S→DAC path, which Espressif removed in Arduino core 3.x — so the esp32 core is pinned to 2.0.14. The sketch also relies on two non-static globals in ESP_8_BIT_composite 1.3.2 to lift the video ISR to LEVEL3. Both are pinned below, and the CI workflow rebuilds both sketches from a clean toolchain on every push.

Prerequisite: arduino-cli (brew install arduino-cli).

# 1. One-time toolchain (pinned). esp32 core 2.0.14 + the three registry libraries.
arduino-cli config set library.enable_unsafe_install true
arduino-cli config add board_manager.additional_urls \
  https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli core update-index
arduino-cli core install esp32:esp32@2.0.14
arduino-cli lib install "Adafruit GFX Library@1.12.6" "ArduinoJson@7.4.3" "WebSockets@2.7.2"

# 2. ESP_8_BIT_composite is not in the Library Manager — install from GitHub at the 1.3.2 tag.
arduino-cli lib install --git-url "https://github.com/Roger-random/ESP_8_BIT_composite.git#v1.3.2"
PORT=/dev/cu.usbserial-0001
FQBN=esp32:esp32:esp32

# Stage 1 — first light: a test pattern + big text, no networking. Proves GPIO25 → RCA →
# monitor actually paints pixels, and calibrates the overscan safe area.
arduino-cli compile --fqbn $FQBN firmware/01_first_light
arduino-cli upload  --fqbn $FQBN -p $PORT firmware/01_first_light

# Stage 2 — the live ticker. Copy the config template and add your 2.4 GHz WiFi first.
cp firmware/phosphor_ticker/config.h.example firmware/phosphor_ticker/config.h
#   edit config.h: set WIFI_SSID / WIFI_PASSWORD. config.h is gitignored.
arduino-cli compile --fqbn $FQBN firmware/phosphor_ticker
arduino-cli upload  --fqbn $FQBN -p $PORT firmware/phosphor_ticker
arduino-cli monitor -p $PORT -c baudrate=115200

Configuration

Everything tunable lives in config.h (copied from config.h.example): WiFi, the WebSocket host, the NTSC/PAL toggle, the overscan margins, TXNS_PER_BLOCK (the block-grid calibration knob), and the BOOT_* wordmark/credit. BOOT_SHOW_SSID defaults to off so your network name stays out of any filmed clip.

Be a good citizen

The default config points at Ripple's free public cluster (s1.ripple.com) and subscribes to the full transactions_proposed firehose. That's shared community infrastructure — if you run this continuously or build several units, point WS_HOST at your own rippled/Clio node (the config supports plain ws:// to a node on your LAN).

Beyond XRPL

The composite + UI engine is data-source-agnostic — the same hardware could just as easily drive a morning news ticker, a weather board, or a home-automation panel. See Possible extensions in docs/DESIGN.md.

Credits

Phosphor's XRPL streaming + blocks-landing engine grew out of an ESP32-C3 LED-matrix build, which is based on Little Ledger by Andy (Handy4ndy) / Xspence — an educational open-source project for real-time XRP Ledger monitoring on embedded hardware. Thanks to Andy for the groundwork and for sharing it openly.

Built on Roger Cheng's ESP_8_BIT_composite (composite video + Adafruit GFX on the ESP32 DAC; signal path after Peter Barrett's ESP_8_BIT), ArduinoJson, and the arduinoWebSockets library. Full attribution and license details: NOTICE.md.

License

MIT © ShaneOnChain. Use it, fork it, put a green CRT on your desk.

About

A live XRP Ledger ticker rendered on a 1983 Apple green-phosphor CRT — composite video synthesized straight from an ESP32 DAC pin.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages