Skip to content

deploy/port: accept a USB serial where --port takes a node path (unattended benches renumber) #1428

Description

@zackees

Problem

fbuild deploy -p (and by extension bash autoresearch --upload-port) accepts only an OS node path — /dev/ttyACM2, COM17. Node paths are assigned by enumeration order and are not stable identities.

On a Linux bench running long unattended FastLED HIL campaigns, an attached ESP32-C6 silently moved from /dev/ttyACM2 to /dev/ttyACM1. It never left the bus — still 303a:1001 at 3-1.3.3, still healthy — it just re-enumerated. Four consecutive unattended runs then died at deploy:

ERROR: A fatal error occurred: Could not open /dev/ttyACM2, the port is busy or doesn't exist.
([Errno 2] No such file or directory: '/dev/ttyACM2')
esptool failed (exit code 2)
❌ Deploy failed (fbuild)

Two problems with that outcome:

  1. The message misattributes the cause. "port is busy or doesn't exist" reads as a wedged or dead board. Nothing was wedged; both boards were healthy the whole time. On an unattended bench this is the difference between "retry" and "someone go look at the hardware".
  2. The benign case is the lucky one. If two boards swap node numbers rather than one vanishing, a hardcoded path deploys one board's firmware onto the other. FastLED tracks deploy isolation as an explicit acceptance criterion (feat(rp2350w): complete pin-free AutoResearch and ESP32-C6 peer-network bring-up FastLED#3832: "Deploying to COM17 never touches COM9"), and a node path cannot guarantee it.

Why fbuild is the right layer

FastLED's own guidance is that runtime USB identities and environment-aware port selection come from FastLED/boards through fbuild, and that FastLED must not reintroduce identity handling locally. fbuild port scan already resolves and prints the stable identifier:

/dev/ttyACM0  2E8A:F00F  Pico 2W                     ser=2DCB876B587EA334
/dev/ttyACM1  303A:1001  USB JTAG/serial debug unit  ser=8C:BF:EA:CF:87:B4

So the data is already there; only the selector is missing. Implementing this in FastLED would mean re-deriving identity outside the registry, which is exactly what that rule forbids.

Request

Let -p/--port accept a USB serial as well as a node path, resolved at use time — or add an explicit --port-serial / SER= selector if overloading -p is undesirable.

Suggested semantics:

  • Resolve the serial to a node immediately before opening it, not at parse time.
  • If no attached device carries that serial, fail loudly naming the serial — never fall back to "the port that used to have it".
  • If more than one device matches, fail rather than pick.

Current workaround

Callers are resolving it themselves before invoking fbuild, which is the duplication this would remove:

resolve() {  # $1 = USB serial -> /dev/ttyACMn
  for d in /sys/bus/usb/devices/*/; do
    [ -f "$d/serial" ] || continue
    if [ "$(cat "$d/serial")" = "$1" ]; then
      t=$(ls "$d"/*/tty/ 2>/dev/null | head -1); [ -n "$t" ] && echo "/dev/$t" && return 0
    fi
  done; return 1
}

That is Linux-only and duplicates identity logic fbuild already owns — hence this request rather than upstreaming the shell.

Context

🤖 Generated with Claude Code

https://claude.ai/code/session_01KkufoNxfnNRU9psT3R9F51

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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