Skip to content

Latest commit

 

History

History
122 lines (105 loc) · 9.29 KB

README.md

File metadata and controls

122 lines (105 loc) · 9.29 KB

ADR Index — Cardputer ADV Browser Mirror

Architecture Decision Records for mirroring / remote-controlling an M5Stack Cardputer ADV (board_M5CardputerADV, enum 24) in a web browser.

ADR Title Status
0001 Panel tee mirror (Panel_Mirror wraps Panel_ST7789) Proposed
0002 GRAM readback mirror (non-invasive) Accepted — implemented
0003 Pure browser simulator (no device) Proposed
0004 Mirror + full remote control Proposed — goal achieved via 0002+0017+0037 instead
0005 Host-side MCP server for the firmware development loop Accepted
0006 The 25% self-test was a byte-order bug, not unreliable readback Accepted
0007 Multi-profile WiFi manager with explicit failure reporting Accepted
0008 SD card manager: explicit formatting via the FATFS layer Superseded by 0036
0009 Hotspot rescan, and never show only the mDNS name Accepted
0010 On-device menu Accepted
0011 Scanning from SoftAP is not a trustworthy scan Accepted
0012 Refuse to upload while the serial port is held Accepted
0013 A draw function must not drive hardware Accepted
0014 The scan screen was matching against the wrong SSID Accepted
0015 Show the broadcast SSID, not the configured one Accepted
0016 Open networks, auth modes, and the WPA2 association floor Accepted
0017 Remote keyboard: generate the layout, send coordinates Accepted
0018 A key that does nothing is not a key that failed Accepted
0019 The ADV has dedicated arrow keys; I said it did not Accepted
0020 Every legend is a key: dual-legend caps and a solved-for layout Accepted
0021 The keyboard collapsed to one column: a self-referential CSS measure Accepted
0022 The keyboard is two parts: printed case legend + rubber dome Accepted
0023 The lowercase hotspot SSID was a real bug, but not the one we hit Accepted
0024 A redacted credentials file builds and flashes cleanly, then fails as SoftAP Accepted
0025 Test the page the device serves, not web/index.html Accepted
0026 Remove the keystroke log; fix two false claims in the note Accepted
0027 Move occasional controls behind a hamburger; keep modal state visible Accepted
0028 Device mockup with inset display, zoom pops it out Accepted
0029 BtnG0 and BtnRst move onto the case top edge, unlabelled Accepted — position/width superseded by 0030
0030 Top-face photo corrects the button sides; front-face detail measured Accepted — supersedes 0029 position/width
0031 Paint order buried three correctly-positioned details Accepted
0032 Note text removed; menu and keyboard toggle confirmed shipping Accepted
0033 Stamp-S3A label is a photograph on its own route Accepted
0034 CARDPUTER ADV label stays CSS, measured against the photo Superseded by 0035
0035 CARDPUTER ADV label becomes a photograph; CSS screws removed Accepted — supersedes 0034
0036 Remove the SD card feature entirely Accepted — supersedes 0008
0037 Why control appeared to need USB: modem sleep, and a counter that lied Accepted — implemented

This table covers only the four mirroring/control strategy options; ADRs 0005-0037 (wifi manager, on-device menu, keyboard layout, etc.) live in this directory but aren't part of that comparison.

Library API

ADR Title Status
0038 Adapter-driven begin(): IInputSink / IHostAdapter — lets a second host firmware (see launcher-adv-mirror) supply its own frame source and input path without editing this library Proposed
0039 SpiReadbackFrameSource — GRAM readback for hosts without M5Unified/M5GFX Accepted — implemented, unverified on hardware
0040 esp32async/ESPAsyncWebServer instead of the esphome fork — the esphome fork can't build against an arduino-esp32 3.x core Accepted — implemented
0041 A frame source failure doesn't take down remote control — server/WS/key injection no longer depend on _src->begin() succeeding Accepted — implemented
0044 Two ways "is the device actually there?" gave a wrong answer during dev-loop USB checks — a shell-side timing artifact, and a genuinely wedged device that even system_profiler missed Accepted — documented, not yet automated
0045 Mobile-first pass: real sizing tiers, zoom pops above the case on touch, and typing via the phone's own keyboard through a hidden input Accepted — implemented, not yet verified on a physical iPhone
0046 Landscape auto-zoom — an iPhone-only hamburger toggle that pops the display up and fills the screen at native 16:9 in landscape, for e.g. AirPlay-to-car-stereo wardriving Accepted — implemented, not yet flashed
0047 1262 HAT mockup drawn in CSS (GNSS+LoRa label centered on the device), with the top-edge buttons realigned to the ADV/S3A stickers and cut to 1/3 height Accepted — implemented, not yet flashed

Hardware facts these ADRs rest on

All verified by reading M5GFX / M5Unified / M5Cardputer sources, not datasheets.

Property Value Source
Board enum board_M5CardputerADV = 24 M5GFX/src/lgfx/boards.hpp:35
Panel Panel_ST7789, 135x240 native M5GFX.cpp autodetect
Logical screen 240x135 (rotation = 1), offset_x=52, offset_y=40 M5GFX.cpp
SPI SPI3_HOST, write 40 MHz, read 16 MHz M5GFX.cpp
Pins MOSI 35, SCLK 36, DC 34, CS 37, RST 33, BL 38 (PWM ch7) M5GFX.cpp
MISO not wired (-1), spi_3wire = true M5GFX.cpp
Readback cfg.readable = true; _read_depth = rgb888_3Byte Panel_LCD.hpp:140
Keyboard TCA8418 I2C controller, matrix(7,8), INT GPIO11 TCA8418.cpp
Keyboard seam Keyboard_Class::begin(std::unique_ptr<KeyboardReader>) Keyboard.h:153

Derived budget (see tools/verify_codec for the arithmetic):

  • Shadow framebuffer, RGB565: 240x135x2 = 64,800 B (63.3 KiB)
  • Full-frame readback at 3 B/px: 97,200 B -> 48.6 ms @ 16 MHz -> ~20.6 fps ceiling
  • Tile 60x45: 8,100 B read -> 4.05 ms/tile, 12 tiles/screen

Does starting with #1 set the base for #4?

Yes — and so does starting with #2. This was the deciding factor in the sequencing, so it is worth stating precisely.

The system splits into five layers. Only the frame source differs between options:

          +-----------------------------+
 layer 5  |  browser UI (canvas, keys)  |  shared by 1, 2, 4
 layer 4  |  wire protocol + RLE codec  |  shared by 1, 2, 4
 layer 3  |  dirty-tile scheduler       |  shared by 1, 2, 4
 layer 2  |  IFrameSource  <---- THE ONLY DIFFERENCE
          |    ReadbackFrameSource (#2) |
          |    TeeFrameSource      (#1) |
 layer 1  |  input injection            |  #4 only
          +-----------------------------+
  • #1 -> #4: #4 is #1 plus layer 1 (input injection) and a control channel. #1 builds layers 2-5; #4 adds layer 1. Nothing in #1 is thrown away.
  • #2 -> #1: swapping ReadbackFrameSource for TeeFrameSource is a one-line change at the call site. Layers 3-5 are untouched.
  • #2 -> #4: #2 builds layers 3-5, which is the majority of the work by volume. #4 then needs layer 2 (tee) + layer 1 (input).

So the chosen order #2 -> #1 -> #4 is strictly incremental: no layer is built twice, and #2 pays for the protocol/UI work that #1 and #4 both inherit. The reason to start at #2 anyway is risk: #2 answers the one question no amount of source reading can settle — does ST7789 GRAM readback actually work over 3-wire SIO on this panel? If it does not, #2 is dead and #1 becomes the only mirroring path; better to learn that from a 2-line integration than after building a full tee.

That question is why the firmware ships a boot self-test (selfTest()): it draws a known pattern, reads it back, and reports percent match before you trust a single frame.