# Plasma display replica — Adafruit Matrix Portal S3 + HUB75 A hardware replacement for the failing Babcock **PD01D221** cockpit plasma display ([../README.md](../README.md), [../FIRMWARE.md](../FIRMWARE.md)). A modern microcontroller reads the same serial command stream the game sends and renders it to a modern LED matrix — a drop-in from the host's point of view, with none of the plasma physics or high voltage. The firmware's command parser and fonts are **ported verbatim from vRIO's vPLASMA emulator** (`src/VPlasma.Core` in the vRIO repo), which is the reference oracle. Feed the replica and vPLASMA the same byte stream and they produce the same frame. ## Bill of materials | Part | Notes | |------|-------| | **Adafruit Matrix Portal S3** | ESP32-S3 controller; plugs into the HUB75 header; native USB-C = the virtual COM port | | **2 × Adafruit 64×32 RGB LED Matrix** (P-pitch to taste; 1/16 scan) | chained → **128×32**, the panel's native resolution | | **5 V power supply, ≥ 4 A** | the panels are the load; USB cannot power them | | USB-C cable | data (and logic power) from the host PC | An amber-leaning pitch and diffuser best mimic the neon-orange plasma; the firmware already renders in orange (`255,96,0` full / `110,40,0` half). ## Wiring 1. **Chain the panels**: panel A `OUT` → panel B `IN`, left-to-right, so the pair reads as one 128-wide canvas (x 0–63 = panel A, 64–127 = panel B). If the image comes out swapped, reverse the chain order. 2. **Mount the Matrix Portal S3** onto panel A's `IN` HUB75 header. 3. **Power**: 5 V ≥ 4 A into the Matrix Portal's screw terminals; run the panels' power pigtails from the same 5 V. USB-C carries data (and powers the S3 logic) — do **not** rely on USB for panel current. ## Build & flash Arduino IDE (or `arduino-cli`): 1. **Boards Manager** → install **esp32** (Espressif). Select board **"Adafruit Matrix Portal S3"**. Set **USB CDC On Boot: Enabled** (so `Serial` is the USB port the game opens). 2. **Library Manager** → install **Adafruit Protomatter** (pulls in Adafruit GFX / BusIO). 3. Open `MatrixPortalPlasma/MatrixPortalPlasma.ino` (keep `PlasmaDisplay.*`, `plasma_fonts.h`, `demo_screens.h` beside it) and Upload. The HUB75 pin arrays at the top of the `.ino` are Adafruit's published Matrix Portal S3 values — except the G and B pins, which are exchanged because these panels have the green and blue LEDs swapped (manufacturer-confirmed; symptom was orange rendering as purple). If a different panel garbles, verify the arrays against your installed Protomatter version. ## Using it with the game The Matrix Portal enumerates as a **USB CDC COM port**. In Windows Device Manager you can pin it to the COM number the host expects. Point the game's plasma output at it — under the DOSBox-X fork: ``` serial2 = directserial realport:COMx ``` Baud is cosmetic over USB CDC (the `9600` line-coding is accepted as a no-op), so the display keeps up regardless. The link is one-way (the game writes, the display listens), exactly as the cockpit drove the real panel. ## No-host testing (onboard buttons) - **UP** — toggle the built-in **firmware demonstration** (the real 10-screen PLASMADOT demo, `demo_screens.h`, extracted from the ROM). - **DOWN** — toggle the **panel test**: cycles diagnostic patterns (solid, border+grid, horizontal/vertical stripes, checkerboard) to expose dead dots and addressing faults — the multi-pattern sequence the real firmware runs. - **Power-on** briefly lights every dot (confirms both panels), then clears. ## Files | File | | |------|--| | `MatrixPortalPlasma/MatrixPortalPlasma.ino` | sketch: Protomatter init, USB serial, render loop, buttons | | `MatrixPortalPlasma/PlasmaDisplay.h/.cpp` | the PD01D221 parser + 128×32 framebuffer — a C++ port of `VPlasmaDevice` | | `MatrixPortalPlasma/plasma_fonts.h` | the 8 real ROM fonts (PROGMEM), generated from `tms27pc512.BIN` | | `MatrixPortalPlasma/demo_screens.h` | the 10 firmware demo screens (PROGMEM) | ## Keeping it faithful — the differential test `PlasmaDisplay` is a line-for-line port of `VPlasmaDevice`; keep the two in sync (same commands, fonts, orientation/`plot` mapping). Validate the replica by sending **identical byte streams** to the replica, to vPLASMA, and to the real panel, then comparing the glass. The **`VPlasma.Wire`** tool (in the vRIO repo: `tools/VPlasma.Wire`, `dotnet run --project tools/VPlasma.Wire -- …` from that repo's root) drives this: ```sh # 1) Build a repeatable stream (or capture a real one — see below). VPlasma.Wire synth --kind demo --out demo.bin # 2) The vPLASMA golden image (pixel-exact reference PNG). VPlasma.Wire render --in demo.bin --out demo-golden.png --scale 8 # 3) Replay the SAME bytes to each target, and photograph the glass: VPlasma.Wire replay --in demo.bin --port COM3 # the real panel (RS-232) VPlasma.Wire replay --in demo.bin --port COM7 # the Matrix Portal (USB-CDC) ``` Compare the two photos against `demo-golden.png`. `synth` kinds: `selftest`, `demo`, `banner`, `charset`. `render` takes `--orient v` for vertical. **Capture a real session** to build a test corpus non-intrusively — insert the tool in the path (point the game at `COM12`, tool tees on to the real display on `COM3`): ```sh VPlasma.Wire capture --port COM12 --out session.bin --tee COM3 ``` Then `render`/`replay` `session.bin` like any other stream. **Auto-compare** a photo (cropped to the glass) against the golden image, or two streams against each other — each is reduced to 128×32 and diffed: ```sh VPlasma.Wire diff --a demo-golden.png --b panel-photo.png --out delta.png # prints "differing dots=N/4096 match=XX%", writes a red-on-mismatch image, # exits 1 if they differ. Inputs may be .bin (ESC P) / .png / .txt (bitmap=). ``` ### Authoring plasma content (from TeslaSuite's plasma tools) Generate display content from text or images and push it over the wire: ```sh # Text in a Windows font, auto-sized to the panel → preview PNG + ESC P + hex: VPlasma.Wire text --text "ALERT 12" --font "Arial" --png a.png --bin a.bin --hex a.txt VPlasma.Wire replay --in a.bin --port COM7 # show it on the replica # An image → the ESC P wire stream (and/or the game's bitmap= encoding): VPlasma.Wire encode --in logo.png --bin logo.bin --hex logo.txt # A bitmap= encoding → a preview PNG (and/or ESC P): VPlasma.Wire decode --in logo.txt --png logo.png ``` Still deferred (as in vPLASMA, documented in `../FIRMWARE.md`): the 10 double-buffered pages (`ESC I`/`ESC i` are consumed but single-page) and the vector-graphics primitives (`ESC A`–`F`). **Ahead of vPLASMA (2026-07-22):** the box family is implemented here from the firmware disassembly — `ESC X l t r b` (box outline) and `ESC x m l t r b` (region blank/fill/invert), the commands **BattleTech: Firestorm**'s `CPlasma` driver draws its score panel with; `ESC Y` (screen blit) is validated and consumed as a no-op. vPLASMA needs the same additions before the differential test covers Firestorm streams — until then `../FS-plasma-bench.ps1` is the Firestorm exerciser.