Skip to content

serial: consult PlatformIO-style upload.* hints from board JSON when dispatching reset #906

Description

@zackees

Background

Ecosystem survey (documented below) shows that PlatformIO's data-driven per-board upload.* schema is the frontrunner for encoding reset semantics. fbuild already extracts most of these fields into its board JSONs (see crates/fbuild-config/src/bin/enrich_boards.rs::UPLOAD_FIELDS), but the runtime reset dispatch in crates/fbuild-serial/src/boards.rs ignores them — it falls back purely to VID/PID lookup.

This is a real gap for unknown-VID boards. The board JSON tells us exactly what the reset primitive should be via upload.protocol (arduino, esptool, sam-ba, picotool, teensy-gui, cmsis-dap, etc.) and modifier flags (use_1200bps_touch, native_usb, wait_for_upload_port). We already parse this data, we should also USE it.

Ecosystem evidence

Tool How reset is configured Data-driven per-board?
PlatformIO boards/*.jsonupload.protocol + use_1200bps_touch + wait_for_upload_port + disable_flushing + native_usb + require_upload_port Yes — full schema
Arduino CLI boards.txtupload.tool + upload.protocol + upload.use_1200bps_touch + upload.wait_for_upload_port + upload.disable_flushing Yes — 3 flag keys
esptool.py CLI flag --before/--after + VID/PID dispatch inside esptool/loader.py::_construct_reset_strategy_sequence. Custom mini-language via ESPTOOL_CUSTOM_RESET_SEQUENCE env No — vendor-scoped
avrdude Reset intrinsic to programmer name (-c arduino = DTR-cap auto-reset) No — programmer implies protocol
probe-rs Reset embedded in per-chip-family Rust code, not YAML target definitions No — hardcoded per family
OpenOCD reset_config in per-target .cfg TCL scripts Yes — per-chip TCL
fbuild Hardcoded BoardFamily enum + VID/PID lookup (+ OS kernel-class fallback via #895) No — enum-based, but has a novel OS-class layer

fbuild's OS-kernel-class fallback is unique — nobody else does that. Keep it. But we're behind the ecosystem on data-driven per-board config, and the fix is small because the data is already in our JSON.

Proposed enhancement

Add a lightweight UploadHint struct to crates/fbuild-serial/src/boards.rs and a mapping function that turns it into a BoardFamily. The hint becomes the highest-priority signal in the detection chain:

family_for_port_with_hint(port, Some(hint))
  ├─ if hint has upload.protocol → family_from_upload_hint (NEW)
  ├─ else fall through to family_for_port(port)
  │       ├─ family_for_vid_pid (existing)
  │       ├─ family_for_port_via_kernel_class (added by #895)
  │       └─ None
  └─ Some(family) or None (caller uses universal default)

Protocol → family map (from ecosystem research)

upload.protocol BoardFamily Rationale
esptool / esptool_py + native_usb: true Esp32NativeUsbCdc Native USB CDC (S3/C3/C6/H2/P4)
esptool / esptool_py + native_usb: false (or missing) Esp32ExternalUart CP2102/CH340/FTDI behind classic ESP DevKit
arduino ArduinoAutoReset avrdude -c arduino = DTR-cap auto-reset
sam-ba NativeUsbCdcReset1200Bps BOSSA + 1200-bps touch
picotool NativeUsbCdcReset1200Bps Pico + 1200-bps touch
teensy-gui / teensy-cli Teensy HalfKay + 1200-bps touch
cmsis-dap / jlink / stlink / raspberrypi-swd / atmel-ice CdcAcmBridge SWD reset, DTR-true idle for the VCOM bridge

Fallback: if upload.protocol is missing but use_1200bps_touch: true, treat as NativeUsbCdcReset1200Bps (matches PIO's use_1200bps_touch semantics — every ecosystem that ships this flag means "open port at 1200 baud, close, wait for re-enum, then upload").

Acceptance

  • family_from_upload_hint correctly maps every documented upload.protocol value to a BoardFamily.
  • family_for_port_with_hint treats the hint as highest priority — returns the hint-derived family even when the VID/PID table would return a different one, if a caller has explicit board context.
  • Unit tests cover every protocol string, missing-protocol + touch fallback, and the priority order (hint wins over VID/PID).
  • Deploy path is NOT modified in this PR — that's a follow-up (needs BoardConfig to grow the extra upload fields).

Follow-ups (not this PR)

  1. Extend BoardConfig (crates/fbuild-config/src/board/types.rs) with upload_use_1200bps_touch, upload_wait_for_upload_port, upload_native_usb, upload_disable_flushing — mirror the PlatformIO fields we already extract in enrich_boards.rs::UPLOAD_FIELDS.
  2. Wire the deploy path to build an UploadHint from BoardConfig and pass it to family_for_port_with_hint.
  3. Add PlatformIO-style protocols: [] array support for multi-protocol boards (Pico: [cmsis-dap, jlink, raspberrypi-swd, picotool]).
  4. Optional: probe-based ESP_SYNC detection — for a truly unknown port, send an ESP32 SLIP-framed SYNC frame and classify as Esp32* if we get a SYNC_RESP. Risky (writes to running firmware), needs a --probe-unknown flag.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions