Skip to content

feat(core): board-profile catalog and versioned blob serializer - #35

Merged
tburkhalterr merged 1 commit into
mainfrom
feat/board-profile-catalog
Aug 4, 2026
Merged

feat(core): board-profile catalog and versioned blob serializer#35
tburkhalterr merged 1 commit into
mainfrom
feat/board-profile-catalog

Conversation

@tburkhalterr

Copy link
Copy Markdown
Contributor

Slice G of the runtime-board-config pivot (CANShift/canshift-firmware#68): the shared source of truth for BoardProfile. The tuner writes the blob, firmware reads it. src/board-profile/ = types + catalog + serializer.

Field list I settled on (cross-checked against firmware include/board_profile.h)

I mirrored the current C++ struct field-for-field (not #68's prose, which diverges) so a future struct↔TS parity check stays green. snake_case blob keys ↔ camelCase TS domain via boardProfileFromWire / boardProfileToWire (the boundary-mapper convention).

  • top: board_id, board_name, chip_family, lcd, backlight, touch, can, storage, conn
  • lcd: driver, pin_mosi, pin_miso, pin_sclk, pin_cs, pin_dc, pin_rst, pin_bl, freq_write_hz, panel_width, panel_height, memory_width, memory_height, default_rotation, rgb_order_bgr, invert, bus_shared_with_touch, readable, color_depth
  • backlight: present, pwm_channel, pwm_freq_hz, default_duty, invert
  • touch: driver, pin_cs, pin_irq, freq_hz, needs_calibration, pin_sda, pin_scl
  • can: controller, pin_tx, pin_rx, default_speed_kbps
  • storage: spiffs_present, spiffs_size_kb, sd_present, sd_pin_cs
  • conn: wifi_supported, ble_supported, psram_present

Enums (string slugs): chip_family esp32|esp32s3; lcd driver ili9341|st7789|ili9488|gc9a01; touch driver none|xpt2046|ft6336|gt911|cst816s; can controller none|esp_twai.

Three points to reconcile with firmware slice #70 (flagged so you can align the struct):

  1. chip_family is not in the current struct. #68 requires it (picks the universal firmware per chip) and the struct becomes the runtime type in #70 — I added it top-level; the struct needs the matching field.
  2. The struct uses lcd/conn (not display/connectivity), bus_shared_with_touch (not bus_shared), pin_sda/pin_scl for I2C touch (not an i2c_addr), and backlight present/default_duty (no pin — the backlight pin is lcd.pin_bl). I matched the struct; these differ from the task/#68 prose.
  3. Kept the struct's readable and color_depth fields even though #68's prose omitted color_depth.

Blob envelope

Versioned magic envelope, gated before shape (magic + formatVersion), reusing the #21 __proto__ hardening:

{ "magic": "CANSHIFT_BOARD", "schema": "board-profile", "formatVersion": 1, "profile": { …snake_case… } }
  • serializeBoardProfile(profile): string — validates via the wire schema, wraps, pretty-prints; deterministic (Zod canonical key order → byte-stable round-trip).
  • parseBoardProfile(raw): BoardProfileResult — discriminated result mirroring parseUsbStatus/parseCanshiftFile: ok / invalid_json / not_an_object / unsupported_blob_version / wrong_shape. Pipeline: JSON.parse with a forbidden-key reviver → magic gate (wrong magic → wrong_shape with a ["magic"] issue) → formatVersion too-new gate → strict envelope+profile validation. Numeric fields are range-checked to the struct's C widths (int8 pins, uint16 dims, uint32 freqs).

Catalog

BOARD_PROFILES + getBoardProfile(id):

  • crowpanel_28, generic_ili9341, generic_ili9341_gt911exact pin maps copied from the firmware board headers (include/boards/*.h).
  • waveshare_s3_28 (incoming — ST7789T3 SPI + CST816 I2C, esp32s3, 240×320): all pins are placeholder -1 and freq_write_hz is a provisional 40 MHz. TODO: fill the real Waveshare pin map when the board arrives / slice-B stabilizes — the round-trip test still validates the shape, so a later pin correction is a one-line data change.

Schema version

No CURRENT_SCHEMA_VERSION bump — a board profile is device-provisioning data with its own independent blob formatVersion, not persisted DashboardConfig. The dashboard-schema migration rule does not apply.

Test plan

  • npm run build / npm run lint / npm run format:check — clean
  • npm test — 1054 passed, 3 skipped (unrelated); firmware-parity suites green with the sibling checkout. 15 new tests:
    • catalog lists the four boards; getBoardProfile hit/miss
    • every catalog profile serialize→parse round-trips back to itself; wire mapper round-trips; snake_case wire keys mirror the struct order
    • byte-equivalent blob round-trip; magic envelope shape
    • rejection: invalid JSON, non-object, wrong magic → wrong_shape, newer formatVersionunsupported_blob_version, out-of-range pin, unknown driver enum, strict extra key
    • __proto__ stripped, Object.prototype never polluted

Closes #34. Part of CANShift/canshift-firmware#68 (slice G). Do not merge — ready for review; the field list above is the proposal to reconcile with firmware slice #70.

@tburkhalterr

Copy link
Copy Markdown
Contributor Author

Note captured from review (not blocking — the waveshare_s3_28 profile is an intentional placeholder to fill when the board is in hand):

  • The touch controller identity is UNCERTAIN: product listings say CST816D, an automated review suggested CST328, and Waveshare's own schematic is the authority. This matters twice — the touch.driver enum currently lists cst816s (neither cst816d nor cst328), and LovyanGFX must actually have a driver for whichever it is (slice C / #71). Verify the exact controller + its LovyanGFX support against the real board before filling pins.
  • CodeRabbit proposed LCD GPIO45/40/42/41/39/5 and touch SDA/SCL/INT GPIO1/3/4 — treat as UNVERIFIED starting points, confirm against the schematic.
  • Keeping pins at -1 keeps the round-trip test valid; filling real pins is a one-line data change.

@tburkhalterr
tburkhalterr merged commit 5fe1fa3 into main Aug 4, 2026
4 checks passed
@tburkhalterr
tburkhalterr deleted the feat/board-profile-catalog branch August 4, 2026 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(core): board-profile TS catalog + versioned blob serializer

1 participant