Work-in-progress “IP KVM” for a Macintosh Classic using an RP2040 (Pico W): Tap raw 1-bpp video + sync, capture with PIO+DMA, and stream to a host UI.
Above output is current project output as of PR #19.
- RP2040 PIO captures 512 pixels per line (1 bpp) on PIXCLK edges.
- Lines are queued and streamed over the USB vendor bulk interface with a compact per-line header (optional RLE).
- Host test helper (
src/host_recv_frames.py) reconstructs frames into PGM images (default).
GPIO0— PIXCLK (input, PIO)GPIO1— VSYNC (input, SIO GPIO, active-low, IRQ on falling edge)GPIO2— HSYNC (input, PIO, active-low)GPIO3— VIDEO (input, PIO, 1 bpp data)GPIO6,GPIO12— Reserved for future SPI to external ADB controllerGPIO9— ATXPS_ON(output via ULN2803, GPIO high asserts PSU on)
- Active video: 512×342 (1 bpp)
- Horizontal offset: 175 PIXCLK cycles after the selected HSYNC edge (PIO skip + 18-cycle delay after phase-lock)
- Vertical offset: 28 HSYNCs after VSYNC fall
- Capture window: 370 HSYNCs total (28 VBL + 342 active)
- Line capture begins on the selected HSYNC edge.
The firmware exposes one vendor bulk interface for video plus one CDC ACM function for control/debug:
- BULK0 (vendor): binary line packets (video data).
- CDC ACM (control/debug): ASCII commands + status text.
Note: a single CDC ACM function appears as two USB interfaces in lsusb -t (Communication + Data). That is normal and still maps to one /dev/ttyACM* control/debug port.
See docs/protocol/usb_cdc_stream.md for details on interfaces and commands.
Each line is emitted as a packet with a compact header:
0..1 magic 0xEB 0xD1
2..3 frame_id little-endian
4..5 line_id little-endian (0..341)
6..7 payload_len little-endian (bit 15 indicates RLE)
8..N line payload (64 bytes raw, or up to 128 bytes RLE)
RLE payloads expand to 64 bytes on the host; firmware may still emit raw lines if RLE does not compress.
python3 src/host_recv_frames.py frames --ctrl-device=/dev/ttyACM0This test script:
- Uses USB bulk for video ingest and CDC for debug/control (
--ctrl-device=overrides CDC autodetect). - Optionally asserts
P(power on) before capture; use--no-bootto skip. - Sends
Xto stop any prior run before arming (use--no-stopto skip). - Resets counters and arms capture by sending
RthenS(use--no-resetto skip). - Adjust the boot wait with
--boot-wait=SECONDSif needed. - Use
--diag-secs=SECONDSto briefly print ASCII status before arming capture. - Reassembles lines into full 512×342 frames.
- Writes PGM files to
frames/(0/255 grayscale) by default; use--pbmfor packed 1-bpp PBM. - Optionally emits a continuous 8-bit raw stream with
--stream-rawor--stream-raw=/path/to/pipe(runs until you stop it). - Firmware defaults to continuous ~60 fps capture; send
Mto toggle to the ~30 fps test cadence. - Edge toggles for testing: send
Hto flip HSYNC edge,Kto flip PIXCLK edge,Vto flip VSYNC edge (capture stops/clears when toggled).
Example: stream raw 512×342 8-bit frames to ffplay on stdout:
python3 src/host_recv_frames.py frames --stream-raw \
| ffplay -f rawvideo -pixel_format gray -video_size 512x342 -framerate 60 -src/firmware sources (Pico SDK)classic_line.pio: PIO program for per-line capture.main.c: USB bulk video + CDC control firmware.host_recv_frames.py: host-side test program for reassembling frames.
docs/PROJECT_STATE.md: living status summary.protocol/: wire format documentation.agent/: running notes + decisions for Codex/agents.- PDFs: helper datasheets used during bring-up.
This project relies on documentation and reference implementations from the following sources.
- Classic Macintosh Video Signals Demystified, Designing a Mac-to-VGA Adapter with LM1881 (Big Mess o' Wires) — monitor ID pins, composite sync behavior, and sync-on-green details for classic Macs.
- Control a Macintosh Classic CRT with a BeagleBone Black (Part 1) — Classic CRT HSYNC/VSYNC/DATA timing reference.
- Mac SE/30 video interface — SE/30 timing notes and capture window context.
- Macintosh Classic II Developer Note — 512×342 timing chart and dot clock reference.
- Mac Plus Analog Board — sync polarity + line rate notes for compact Macs.
- Datasheets: SP3222E, SN74LS245, ULN2803A — electrical interface references used during bring-up.
- /opt/MacDevDocs — Apple developer notes + hardware references used to cross-check classic Mac timing and connector pinouts (see the Apple Developer Documentation Archive).
- /opt/Pico-SDK — RP2040 PIO/DMA/USB SDK structure and build expectations.
- /opt/PicoHTTPServer — Pico W HTTP server, captive portal flow, and incremental response patterns for future on-device UI work.
- /opt/SigrokPico — USB CDC transport patterns and RLE compression ideas for low-entropy line data.
- /opt/picovga — multi-core video pipeline patterns for RP2040 video workloads.
- /opt/adb/hootswitch — RP2040 ADB bus implementation (PIO + DMA + state machine) used as the reference for our ADB device emulation plan.
Use
scripts/setup_opt_references.shto install the above corpora under/optif you need local copies.
Set your Pico SDK path, then build out-of-tree in build/.
