Skip to content

fbuild-serial: BoardFamily enum + polymorphic ResetMethod dispatch registry #687

Description

@zackees

TL;DR

Companion structural piece to #684 (closed) and #686. Today fbuild-serial has board-specific behaviour scattered across esp_reset.rs, manager.rs, boot_mode.rs, and the deploy paths. The next board family port (LPC8xx is on-hand; STM32, RP2040, SAMD51, Teensy, BMP all queued) will multiply that scatter unless there's a single point of consultation for "what does this board want for DTR/RTS, reset, idle state, line-control semantics?"

Proposed structural addition: a BoardFamily enum + a ResetMethod enum + a small dispatch table.

Where this came from

The audit on #684 + #686 identified that:

  • Per-board DTR/RTS idle state is named but not encoded. (#684 requested idle_dtr_rts() API; not landed as a concrete enum.)
  • Reset method per family has no abstraction. ESP32 → DTR/RTS pulse (esp_reset.rs). LPC845-BRK → pyocd CMSIS-DAP SWD (handled outside fbuild-serial entirely). Teensy / SAMD / RP2040 → 1200-bps-touch trick (unimplemented). UNO → DTR pulse via auto-reset cap.
  • Today's reset dispatch is implicit: callers in fbuild-deploy/esp32_native call esp_reset directly; LPC callers must do their own thing. New families will keep adding ad-hoc paths.

Proposed shape

// In a new file: crates/fbuild-serial/src/board_family.rs

pub enum BoardFamily {
    /// ESP32 native USB CDC (S3/C3/C6/H2/P4). Reset via DTR/RTS pulse.
    /// Idle: (false, false) — BOOT high, EN high, chip running.
    Esp32NativeUsbCdc,

    /// ESP32 via external USB-UART chip (CP2102/CH340/FTDI). Same DTR/RTS
    /// semantics as native (chip exposes lines), so same idle + reset.
    Esp32ExternalUart,

    /// Bridge-based VCOM (LPC11U35 on LPC845-BRK, mbed DAPLink boards).
    /// Reset via SWD/CMSIS-DAP, NOT DTR/RTS.
    /// Idle: (true, true) — host-ready signal to the bridge.
    CdcAcmBridge { vid: u16, pid: u16 },

    /// PJRC Teensy (3.x/4.x). Reset via 1200-bps touch.
    /// Idle: (true, true).
    Teensy,

    /// SAMD51 / RP2040 with native USB CDC. Reset via 1200-bps touch.
    /// Idle: (true, true).
    NativeUsbCdcReset1200Bps,

    /// Classic Arduino (UNO / Mega) with capacitor-coupled DTR auto-reset.
    /// Reset via DTR=true→false transition.
    /// Idle: (true, true) — but be aware of capacitor-charge timing.
    ArduinoAutoReset,
}

impl BoardFamily {
    /// Universal post-attach / post-reset idle state for this family.
    /// Set by `manager::open_port` after the port handle is acquired.
    pub fn idle_dtr_rts(&self) -> (bool, bool) {
        use BoardFamily::*;
        match self {
            Esp32NativeUsbCdc | Esp32ExternalUart => (false, false),
            CdcAcmBridge { .. } | Teensy | NativeUsbCdcReset1200Bps
                | ArduinoAutoReset => (true, true),
        }
    }

    pub fn reset_method(&self) -> ResetMethod {
        use BoardFamily::*;
        use ResetMethod::*;
        match self {
            Esp32NativeUsbCdc | Esp32ExternalUart => DtrRtsPulse,
            CdcAcmBridge { .. } => SwdViaCmsisDap,
            Teensy | NativeUsbCdcReset1200Bps => TouchBaud1200,
            ArduinoAutoReset => DtrPulse,
        }
    }
}

pub enum ResetMethod {
    /// esptool ClassicReset (esp_reset.rs).
    DtrRtsPulse,
    /// Single DTR=true→false transition (Arduino auto-reset cap).
    DtrPulse,
    /// Open port at 1200 baud, close → SAMD/RP2040/Teensy bootloader.
    TouchBaud1200,
    /// CMSIS-DAP probe via pyocd / `probe-rs` — out of `fbuild-serial`'s
    /// jurisdiction; caller dispatches.
    SwdViaCmsisDap,
}

Why a single enum is the right shape

Acceptance criteria

  • crates/fbuild-serial/src/board_family.rs exists with the enum + idle_dtr_rts() + reset_method().
  • manager::open_port accepts an optional BoardFamily and consults idle_dtr_rts(); default (when None) is the current (true, true) which is the most-common safe state.
  • esp_reset::hard_reset_blocking renamed to esp_dtr_rts_reset_blocking (since BoardFamily now disambiguates) AND has a doc-comment + debug-assert that the caller passed BoardFamily::Esp32*.
  • A dispatch_reset(family, port) helper in fbuild-serial picks the right method (calls esp_dtr_rts_reset_blocking for ESP families; returns a typed "not handled here, use SWD" for CdcAcmBridge).
  • Doc-comment in board_family.rs links to the [BLOCKER] LPC845-BRK AutoResearch bring-up echo returns empty RX (suspected HardFault) — blocks #3102 FastLED#3300 incident.

Out of scope

  • Implementing TouchBaud1200 (SAMD51 / RP2040 / Teensy) and DtrPulse (Arduino UNO) — variants can be unimplemented!() initially; track each implementation in its own follow-up.
  • The SWD/CMSIS-DAP reset path itself — that lives outside fbuild-serial. The dispatch just routes to "caller must do this elsewhere."

Refs

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