Repository navigation
Description
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:
- 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".
- 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
- Observed across ~250 unattended HIL cycles; the paths were stable for 241 cycles before they were not, so anything that only tests briefly will not see this.
- Related FastLED issues: Unattended RP2040 deploy & recovery: bench host setup instructions (follow-up to #3713) FastLED#3714 (unattended deploy & recovery), feat(rp2350w): complete pin-free AutoResearch and ESP32-C6 peer-network bring-up FastLED#3832 (deploy isolation criterion).
🤖 Generated with Claude Code
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTriage
Activity
feat(cli): accept -p ser=<USB serial> for deploy/monitor/reset (#1428) (
feat(fetch,serial): env-tunable retry, jitter, mirror fallback (#1463)…
zackees commentedon Sep 30, 2026
Done in #1596 and #1598 (merged):
-p/--port ser=<USB serial>for deploy/monitor/reset, resolved in the daemon right before the port is opened (after the build); no match names the serial and lists the attached ones, several matches fail, phantom/non-USB ports are never selected. A requested/dev/...node that no longer exists now fails with a clear re-enumeration message (naming the attached serials) instead of the flasher's "port is busy or doesn't exist". Resolution was tested against synthetic port lists, not real hardware.