Skip to content

feat(teensyrom): cross-platform USB-serial auto-detection#72

Merged
kfox merged 2 commits into
mainfrom
feat/tr-serial-autodetect-cross-platform
Jul 7, 2026
Merged

feat(teensyrom): cross-platform USB-serial auto-detection#72
kfox merged 2 commits into
mainfrom
feat/tr-serial-autodetect-cross-platform

Conversation

@kfox

@kfox kfox commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What

autodetect_serial_port() was macOS-only: it shelled out to system_profiler
for the board's USB serial number, then globbed /dev/cu.usbmodem<serial>* for
the node. Off macOS it hard-returned None, so tr:// (auto-detect) required an
explicit [teensyrom].serial_port on Linux and Windows.

This replaces that with a single pyserial list_ports.comports() path that
works on macOS, Linux, and Windows. The board is identified by its USB
(VID 0x16C0, PID 0x0489) — the only stable, driver-independent key
across platforms — with a case-insensitive "teensyrom" product/description
substring accepted as a fallback where the OS surfaces the USB product string.
Each comports() entry already carries the resolved device path, so there's no
per-platform node resolution: it returns COMn / /dev/ttyACM* /
/dev/cu.usbmodem* directly. pyserial is already the tr extra dependency.

The generic Windows usbser.sys driver exposes no product string (product is
None, description is the bare USB Serial Device (COMn)), which is why the
(VID, PID) key leads and the product-name match is only a fallback.

Changes

  • teensyrom_dma.py: rewrite autodetect_serial_port() on list_ports; add a
    pure, duck-typed _is_teensyrom_port() matcher + _TEENSY_USB_VID /
    _TEENSYROM_USB_PID constants; isolate the pyserial call behind
    _list_comports() (returns [] when pyserial is missing or enumeration
    fails); drop the dead system_profiler/glob machinery and its
    glob/json/subprocess/sys imports.
  • backend.py: drop the "macOS only" wording from the auto-detect log comment
    and the missing-serial_port error message.
  • scripts/diags/tr_serial_probe.py: new diagnostic — runs the same
    enumeration and prints the environment, each port's fields, and the match
    decision, so a failed auto-detect explains itself instead of a bare None.
  • Tests: replace the profiler/glob unit tests with matcher cases (VID/PID with
    no product string, product-string fallback, rejection) and _list_comports-
    mocked autodetect tests incl. the multi-board warning. The tests patch
    _list_comports, so they need no pyserial (the tr extra) installed.
  • CLAUDE.md: note auto-detection is now cross-platform via USB VID/PID.

Verification

  • ruff check, ruff format, mypy --strict, pyright, full unittest suite
    (1902 tests) all pass — including with pyserial absent (the CI extra set).
  • Auto-detect confirmed resolving the real board's device path on both macOS and
    Windows (via the new diagnostic); the resolved path feeds the unchanged
    SerialTransport.

kfox added 2 commits July 7, 2026 10:10
Replace the macOS-only system_profiler + /dev/cu.usbmodem globbing with a
single pyserial list_ports.comports() path that works on macOS, Linux, and
Windows. Identify the board by its USB (VID 0x16C0, PID 0x0489) with a
"teensyrom" product-string fallback, and return the enumerated device path
directly (COMn / /dev/ttyACM* / /dev/cu.usbmodem*) — no per-platform node
resolution. Add scripts/diags/tr_serial_probe.py to make a failed detect
explain itself (env, per-port fields, match decision) instead of a bare None.
…xtra

CI installs only the midi extra, so pyserial is absent and the tests that
patched serial.tools.list_ports.comports errored at patch time, while ruff
format also wanted two files reflowed. Isolate the pyserial call behind
_list_comports() (returns [] when pyserial is missing or enumeration fails) so
the auto-detect tests patch that helper instead of the serial module — no
pyserial needed. Apply ruff format.
@kfox kfox merged commit 4c0c297 into main Jul 7, 2026
6 checks passed
@kfox kfox deleted the feat/tr-serial-autodetect-cross-platform branch July 7, 2026 15:28
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.65%. Comparing base (63da329) to head (387d19d).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
c64cast/teensyrom_dma.py 80.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #72      +/-   ##
==========================================
+ Coverage   79.56%   79.65%   +0.08%     
==========================================
  Files          76       76              
  Lines       15426    15403      -23     
  Branches     2321     2315       -6     
==========================================
- Hits        12274    12269       -5     
+ Misses       2622     2607      -15     
+ Partials      530      527       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant