Skip to content

Discover generic USB/RS-232 adaptors and stop auto-probing devices behind them#102

Merged
dccote merged 2 commits into
masterfrom
find-generic-ports
Jul 6, 2026
Merged

Discover generic USB/RS-232 adaptors and stop auto-probing devices behind them#102
dccote merged 2 commits into
masterfrom
find-generic-ports

Conversation

@dccote

@dccote dccote commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Instruments behind a generic RS-232/USB converter (FTDI, Prolific, Silicon Labs CP210x, WCH CH34x) inherit the bridge's anonymous VID/PID and have no identity of their own — the Coherent FieldMaster is one. This branch adds (1) a way to discover those adaptors and (2) a mechanism so the framework treats such devices correctly instead of blindly probing them.

1. Discovery — SerialPort

  • genericSerialConverterVendors — class attribute mapping USB idVendor → chip name (extensible): {0x0403: "FTDI", 0x067b: "Prolific", 0x10c4: "Silicon Labs (CP210x)", 0x1a86: "WCH (CH340/CH341)"}.
  • isGenericSerialConverter(idVendor) -> bool — pure predicate (handles None).
  • genericSerialConverterPorts() — connected ports from a known converter chip, as pyserial ListPortInfo objects (open via .device, disambiguate via .serial_number).

2. Mechanism — PhysicalDevice.usesGenericSerialConverter

The real problem: FieldMasterDevice, OscilloscopeDevice, EchoDevice and IntelliDriveDevice all declare 0x0403:0x6001. On every FTDI cable, DeviceManager tried initializeDevice() on each candidate — sending arbitrary protocol bytes to an unknown instrument (the same blind probing that can wedge a device).

  • New class flag usesGenericSerialConverter (default False).
  • When set, vidpids() expands to every generic-converter vendor with the product id wildcarded (sourced from SerialPort.genericSerialConverterVendors — one source of truth). So the device is discoverable behind any generic cable, not just FTDI, and is identified by serial number.
  • isCompatibleWith treats a None product id in a pair as a wildcard (no change for concrete pairs).
  • DeviceManager.candidateClassesForAutoDiscovery() drops generic-converter classes, so they are never auto-probed — they are constructed explicitly (FieldMasterDevice(serialNumber="FTFDLOTS")).
  • Flagged generic: FieldMaster, oscilloscope, Echo, IntelliDrive. Not flagged: DebugFieldMasterDevice (own fake identity) and Thorlabs (custom-EEPROM FTDI PID 0xfaf0 = a unique identity).
  • connectedUSBDevices omits the product id from usb.core.find when it is None (VID-only matching end to end).

Design note

Matching is by VID only, restricted to four vendors that make essentially nothing but serial bridges. Microchip (0x04D8) / Cypress (0x04B4) are intentionally excluded to avoid false positives; add them as specific (VID, PID) pairs if needed.

Test plan (all hardware-free)

  • testSerialPortConverters.py — 5 passed (mocks comports())
  • testGenericSerialConverter.py — flag, vidpids() expansion, wildcard compatibility, non-generic unaffected, and the auto-discovery guard (generic classes match but are excluded)
  • testFieldMasterDevice.py — debug tests pass; hardware tests also passed live (FieldMaster still reads through the flagged path)
  • Live: genericSerialConverterPorts() detects the FTDI adaptor and excludes an STM32 VCP and Bluetooth
  • Not run here (device in use): testPhysicalDevice.py monitoring tests open connected USB devices; the DeviceManager change is unit-tested in isolation instead

🤖 Generated with Claude Code

dccote and others added 2 commits July 6, 2026 13:13
Problem: Instruments behind a generic RS-232/USB converter (FTDI, Prolific,
Silicon Labs CP210x, WCH CH34x) inherit the bridge's anonymous VID/PID and
have no identity of their own. SerialPort could match a known VID/PID/serial
(matchPorts) but had no way to list the generic-converter ports so a caller
could discover the candidate adaptors.

Solution: add genericSerialConverterVendors (VID -> chip name) plus two
classmethods: isGenericSerialConverter(idVendor) and
genericSerialConverterPorts(), which returns the connected ports whose vendor
is a known converter chip as pyserial ListPortInfo objects (open via .device,
disambiguate via .serial_number). Adds testSerialPortConverters.py, a
hardware-free test that mocks comports().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: Many instruments sit behind a stock FTDI 0x0403:0x6001 cable
(FieldMaster, oscilloscope, Echo, IntelliDrive), so they all share the same
VID/PID. DeviceManager could not tell them apart and, on every FTDI cable
plugged in, tried initializeDevice() on each candidate class -- sending
arbitrary protocol bytes to an unknown instrument (the same kind of blind
probing that can wedge a device).

Solution: add PhysicalDevice.usesGenericSerialConverter (default False). When
set, vidpids() expands to every generic converter vendor (FTDI/Prolific/
CP210x/CH34x) with the product id wildcarded, sourced from
SerialPort.genericSerialConverterVendors (single source of truth), and
isCompatibleWith treats a None product id in a pair as a wildcard. So such a
device is discoverable behind any generic cable, not just FTDI, and is
identified by serial number rather than VID/PID. DeviceManager gains
candidateClassesForAutoDiscovery(), which drops generic-converter classes so
they are never auto-probed; they are constructed explicitly instead.

FieldMaster, oscilloscope, Echo and IntelliDrive are flagged generic;
DebugFieldMasterDevice keeps its own fake identity (flag False), and
Thorlabs (custom-EEPROM FTDI PID 0xfaf0) stays a specific, unique identity.
connectedUSBDevices omits the product id from usb.core.find when it is None so
VID-only matching works end to end. Adds testGenericSerialConverter.py
(hardware-free).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dccote dccote changed the title Add SerialPort.genericSerialConverterPorts to discover generic USB/RS-232 adaptors Discover generic USB/RS-232 adaptors and stop auto-probing devices behind them Jul 6, 2026
@dccote
dccote merged commit 0e1e996 into master Jul 6, 2026
14 checks passed
@dccote
dccote deleted the find-generic-ports branch July 6, 2026 17:53
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