velxio/test/test_epaper/autosearch/05_velxio_existing.md

92 lines
4.0 KiB
Markdown

# 05 — How Velxio currently wires SPI/I2C displays (the pattern to follow)
We already have two display emulators that Phase 1 ePaper should mirror.
## SSD1306 — I2C OLED
- **Web Component**: `wokwi-ssd1306` (third-party, comes from `@wokwi/elements`)
- **Simulation hook**: `frontend/src/simulation/parts/ProtocolParts.ts:312`
registers as `ssd1306` and listens on the simulator's I2C bus.
- **Render path**: maintains a 1-bit GDDRAM buffer in JS, pushes it to the
Web Component via `setPixels(uint8Array, width, height)`.
## ILI9341 — SPI TFT (the closest precedent for ePaper)
- **Web Component**: `wokwi-ili9341` (built-in canvas)
- **Simulation hook**: `frontend/src/simulation/parts/ComplexParts.ts:795`
registers as `ili9341`. Plumbing:
```ts
const pinManager = (avrSimulator as any).pinManager;
const spi = (avrSimulator as any).spi;
const pinDC = getArduinoPinHelper('D/C');
pinManager.onPinChange(pinDC, (_, state) => { dcState = state; });
spi.onByte = (value) => {
if (!dcState) processCommand(value); // DC=LOW → command opcode
else processData(value); // DC=HIGH → parameter / pixel
spi.completeTransfer(0xff); // unblock the CPU immediately
};
```
The simulation maintains:
- `colStart, colEnd, rowStart, rowEnd, curX, curY` — RAM window state
- `currentCmd, dataBytes[]` — collecting parameters for the active command
- A single `ImageData` buffer batched to `requestAnimationFrame` so we don't
thrash the canvas on every WRITE_RAM byte.
- A `getArduinoPinHelper('D/C')` lookup — pulls the DC pin number from the
user's wiring in the diagram.
This is **exactly** what we need for ePaper, with these additions:
| Concern | ILI9341 today | ePaper additions |
|---|---|---|
| DC pin | yes | yes (same) |
| RST pin | optional, ignored | **mandatory** (clear framebuffer) |
| BUSY pin | n/a | **emulator drives it back to firmware** |
| Frame model | streaming pixel writes (visible immediately) | **latched** — held off-screen until 0x20 ACTIVATION |
| Refresh latency | none | configurable (default 1 s) — drive BUSY high during |
| Framebuffer planes | one (16-bit RGB) | one or two (B/W, +R) or seven (Spectra/ACeP) |
| Wire-up | Adafruit_GFX `tft.begin()` | GxEPD2/Adafruit_EPD `display.init()` |
## What "drive BUSY back to the firmware" means in code
ESP32 boards: `Esp32Bridge.sendPinEvent(busyGpio, state)` exists today
(used for DHT22/HC-SR04). For browser-side simulators (AVR, RP2040) the
PinManager already supports `triggerPinChange(gpio, state)` and the
simulator's GPIO peripheral picks it up as an INPUT read.
ePaper emulator pseudocode:
```ts
function flushFrameAndPulseBusy() {
// 1. Compose the latched RAM planes into the canvas
ctx.putImageData(composeBwAndRedPlanes(), 0, 0);
// 2. Drive BUSY HIGH so the firmware's GxEPD2 _PowerOn() blocks
pinManager.triggerPinChange(busyGpio, true);
// 3. After REFRESH_MS, drive BUSY LOW
setTimeout(() => pinManager.triggerPinChange(busyGpio, false), REFRESH_MS);
}
```
`REFRESH_MS` is 50 ms by default (snappy emulation) but settable per-panel
attribute for users who want to feel the real ~12 s Spectra 6 latency.
## Where the new files will live
```
frontend/src/components/velxio-components/EPaperElement.ts ← Web Component (per CLAUDE.md §6a)
frontend/src/components/velxio-components/EPaper.tsx ← React wrapper
frontend/src/components/simulator/BoardOnCanvas.tsx ← (no changes — components flow through ComponentRegistry)
frontend/src/services/ComponentRegistry.ts ← register the metadataId(s)
frontend/src/simulation/parts/ComplexParts.ts ← OR a new EPaperPart.ts in parts/
```
Per CLAUDE.md §6a (added 2026-04), the visual component **must** be a Web
Component with a `pinInfo` getter so wires snap to pin tips, not the
corner. We'll author one Web Component (`<velxio-epaper>`) parameterised by
a `panel-kind` attribute (`epaper-1in54-bw`, `epaper-2in13-bwr`, etc.) so
all sizes share the same code.