Skip to content

docs: USB CDC DTR/RTS control-line semantics matrix per board family #689

Closed

Description

@zackees

TL;DR

USB CDC ACM SET_CONTROL_LINE_STATE exposes DTR and RTS to host software. What these two bits actually do on the target chip is vendor-specific — and that fact has now burned multiple sessions on different boards. There's no canonical reference inside the FastLED + fbuild stack that maps (BoardFamily, DTR-state, RTS-state) → effect. Every future board port will rediscover the wrong default unless there's somewhere to look first.

This issue asks for a docs/usb-cdc-control-line-matrix.md (or wherever fbuild docs live) that pins down the rules in one place, with links to the datasheets / app notes that back them.

Where this came from

The audit on FastLED/fbuild#684 and the LPC845-BRK bring-up incident (FastLED/FastLED#3300 / #3325) exposed that:

  • DTR=False is fine on ESP32 native USB CDC (post-reset idle).
  • DTR=False is fatal on the LPC11U35 USB-VCOM bridge (LPC845-BRK), CP2102 in CDC mode with some firmware revs, and FTDI FT232 in some modes — the bridge treats DTR as a "host ready" gate and silently drops every transmit byte from the target.
  • Nothing in either repo documents this matrix. The fact lives in scattered comments inside esp_reset.rs, three Python sites that just got fixed, and one short paragraph in the docstring of the new ci/util/serial_probe.py helper. None of these are a reference a future contributor would find.

Proposed shape

A single doc — docs/usb-cdc-control-line-matrix.md in this repo (or crates/fbuild-serial/docs/, maintainer preference) — structured as:

Per-chip matrix

+----------------------------+--------------+----------------+------------------------+
| USB bridge / native CDC     | DTR effect   | RTS effect     | Source                |
+----------------------------+--------------+----------------+------------------------+
| ESP32-S3 native USB CDC    | → BOOT pin   | → EN/RESET pin | esptool reset.py +     |
|                            | (via SOC USB | (via SOC USB   | ESP-IDF USB CDC docs   |
|                            | peripheral)  | peripheral)    |                        |
+----------------------------+--------------+----------------+------------------------+
| LPC11U35 USB-VCOM bridge   | host-ready   | (ignored on    | mbed DAPLink docs;     |
|   (LPC845-BRK, LPCXpresso) | gate; False  | most rev's)    | NXP UM10473 §USB-      |
|                            | drops bytes  |                | to-Serial bridge       |
+----------------------------+--------------+----------------+------------------------+
| FTDI FT232R / FT232H       | exposed as   | exposed as     | FTDI AN232B-04         |
|                            | RS-232 DTR;  | RS-232 RTS;    | "Data Throughput"      |
|                            | board-      | board-         |                        |
|                            | dependent    | dependent      |                        |
+----------------------------+--------------+----------------+------------------------+
| CP2102 / CP2104             | routable to | routable to   | SiLabs AN144           |
|                            | any GPIO;   | any GPIO;     |                        |
|                            | not gating  | not gating    |                        |
|                            | by default  | by default    |                        |
+----------------------------+--------------+----------------+------------------------+
| CH340 / CH343               | DTR pulse  | RTS pulse     | WCH datasheet          |
|                            | usually    | unused        | (Mandarin); see        |
|                            | wired to   |               | Arduino auto-reset     |
|                            | RESET via  |               | note                   |
|                            | 100nF cap  |               |                        |
+----------------------------+--------------+----------------+------------------------+
| PJRC Teensy USB-Serial     | exposed as | exposed as    | PJRC HID specs         |
|                            | host       | host          |                        |
|                            | signal;    | signal;       |                        |
|                            | reset via  |               |                        |
|                            | 1200-bps    |               |                        |
|                            | touch       |               |                        |
+----------------------------+--------------+----------------+------------------------+
| RP2040 native USB CDC      | informational| informational | RP2040 datasheet §USB   |
|                            | only        | only          |                        |
+----------------------------+--------------+----------------+------------------------+
| SAMD21/51 native USB CDC   | host signal | host signal   | Atmel SAM USB CDC ref   |
|                            | + 1200-bps  | + 1200-bps    |                        |
|                            | touch reset | touch reset   |                        |
+----------------------------+--------------+----------------+------------------------+

Concrete rules (the "what to set when you open a port" cheat sheet)

Default safe state: DTR=True, RTS=True. Works for every CDC bridge as
"host ready" and is the ESP post-reset idle state (no reset trigger
because the CDC peripheral consumes the bits at re-enumeration, not on
live toggle).

ESP32 reset sequence (esptool ClassicReset):
    DTR=False, RTS=True   →  EN low (reset hold)
    sleep(100ms)
    DTR=True,  RTS=False  →  EN high (release reset), BOOT low (run firmware)
    Final state: DTR=False, RTS=False. [ESP-correct.]

LPC845-BRK reset:
    Use pyocd / probe-rs CMSIS-DAP SWD reset.
    Do NOT touch DTR/RTS — bridge is sensitive to DTR for data flow.
    Post-reset: re-assert DTR=True, RTS=True before monitor open.

Teensy / SAMD51 / RP2040 reset:
    Open port at 1200 baud, close. Bootloader engages on disconnect.
    Re-enumerate at the bootloader's VID:PID.
    Post-reset: DTR=True, RTS=True at app baud.

Empirical probe (the test that produces this table for a new board)

Use ci/util/serial_probe.py probe-dtr-rts <port> (FastLED side) — sweeps the 4 combinations, reports bytes seen, identifies which combo gates data and which triggers reset. Note: that subcommand was proposed during the audit; if not yet implemented in #686 or here, it's a worthy add.

Cross-links

  • crates/fbuild-serial/src/esp_reset.rs — references this doc from its module header.
  • crates/fbuild-serial/src/manager.rs::open_port — references this doc from the DTR/RTS-assertion block (lines 120-134, the fix(esp32): prevent ESP32-S3 serial reset deadlock and boot-mode lockup #532 reference).
  • Per-family override files in crates/fbuild-build/src/<family>/configs/*.json — reference this doc when adding a new family.
  • FastLED agents/docs/onboarding-a-new-board.md (if it exists; otherwise file separately) — links here too.

Acceptance criteria

  • docs/usb-cdc-control-line-matrix.md (or equivalent path) exists with the per-chip table + concrete rules.
  • At least 5 chips covered with datasheet links: ESP32 native USB CDC, LPC11U35 bridge, FTDI FT232, CP2102, CH340.
  • At least 2 native USB CDC chips: RP2040, SAMD21/51.
  • PJRC Teensy entry (because its 1200-bps touch is a recurring gotcha).
  • The 3 cross-link sites updated to point at this doc.
  • One concrete worked example in the doc: "How FastLED#3339 would have been a 15-minute investigation if this table existed."

Out of scope

Refs

  • FastLED/fbuild#684 (closed) — parent: hard_reset_blocking DTR=low fix. Hints at the matrix but doesn't pin it.
  • FastLED/fbuild#686 — BOARD_FINGERPRINTS table. This doc is the why behind those entries.
  • FastLED/FastLED#3300 / #3325 / #3339 — the LPC bring-up incident. The cost of not having this doc was multiple sessions of false-trail debugging.
  • FastLED/FastLED#3336 — agent hook + doc rule forbidding ad-hoc PowerShell SerialPort probes. The "why" is exactly the matrix this issue requests.

Filed during the audit fan-out on FastLED/FastLED#3339.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions