Skip to content

FTDI: FTDI's D2XX library beside libftdi, so the Mantis runs on Windows without Zadig - #1614

Open
BioCam wants to merge 2 commits into
PyLabRobot:mantis-v1-connect-clampfrom
BioCam:ftdi-d2xx
Open

BioCam wants to merge 2 commits into
PyLabRobot:mantis-v1-connect-clampfrom
BioCam:ftdi-d2xx

Conversation

@BioCam

@BioCam BioCam commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1612: only the last two commits are this PR.

The Problem

On Windows, an FTDI device comes bound to FTDI's own driver, installed by Windows Update or with the device's software. PyLabRobot's FTDI transport talks through libftdi and libusb, which can't open a device bound to that driver. Today the only way in is to rebind the device with Zadig. After that, every other program that uses the device can't reach it until FTDI's driver is restored by hand.

FTDI's driver comes with its own library, D2XX (ftd2xx.dll). Through it, PyLabRobot can open the device with the driver Windows already has: no Zadig, and the device's own software keeps working whenever PyLabRobot isn't connected.

PR Content

  • FTDI(..., ftdi_library=...):
    • "libftdi": the default, unchanged.
    • "d2xx": FTDI's own D2XX library (ftd2xx.dll, libftd2xx.dylib/.so), loaded with ctypes. There's nothing new to pip-install, and PyLabRobot doesn't bundle the library: it comes with FTDI's driver.
    • None: D2XX on Windows when the library loads, else libftdi.
  • Finding a channel: D2XX names each channel by serial number plus interface letter, e.g. <serial>A and <serial>B on an FT2232H. device_id stays the base serial number and interface_select picks the letter. usb_address is refused under D2XX, which has no USB address, and the error lists the channels it found.
  • A channel another program holds is refused before anything is sent. The error says what to do: close that program or stop its service, or use ftdi_library="libftdi" for a device rebound with Zadig. D2XXError carries D2XX's status code.
  • Every FTDI call has its D2XX counterpart: baud rate, line properties, flow control, RTS/DTR, latency timer, purge, reset and modem status. Reads don't block, as with libftdi.
  • Captures: the D2XX path records one setup entry naming the library. libftdi captures are unchanged, so existing captures still replay.
  • Only the Mantis uses it: Mantis(..., ftdi_library=None) picks D2XX on Windows. Every other FTDI device keeps libftdi until it has been checked on D2XX itself.
  • Tests: against a fake D2XX library: the automatic choice (Windows only), channel resolution and its refusals, every call, short writes, and the setup record replaying with and without it.

Usage

  1. On Windows with FTDI's driver, the Mantis picks D2XX by itself: no Zadig, nothing to set
from pylabrobot.formulatrix import Mantis

mantis = Mantis(name="mantis", declared_configuration_json="my_mantis.json")
await mantis.setup()  # log: "Successfully opened FTDI device: … (d2xx)", once per interface
  1. A Mantis already rebound with Zadig keeps libftdi
mantis = Mantis(name="mantis", declared_configuration_json="my_mantis.json", ftdi_library="libftdi")
  1. Any FTDI device can choose D2XX explicitly
from pylabrobot.io.ftdi import FTDI

io = FTDI(human_readable_device_name="my device", device_id="FT12AB34", ftdi_library="d2xx")
  1. While another program holds the Mantis, setup refuses before sending anything
RuntimeError: No FTDI channel found through D2XX for 'Formulatrix Mantis' (device_id None, interface B). … 2 held by another program list no serial number: close the vendor software or stop its service.

On the device

A Mantis v4 on a Windows PC with FTDI's driver and the vendor software installed, plus the same steps on macOS:

result
automatic choice on Windows d2xx, from C:\Windows\System32\ftd2xx.dll; both channels opened, no Zadig
every #1612 step (connect, reads, clamp at levels 1–3, release, declared file agreeing / claiming v3, RFID tags, no folder) pass; the bytes written are identical to macOS over libftdi
setup while the vendor software holds the Mantis refused; a USB capture shows nothing sent
the vendor software closed, then setup connects
the vendor software after PyLabRobot starts up, passes its pressure test, scans the chips
macOS, libftdi every step's bytes unchanged

🤖 Generated with Claude Code

@BioCam
BioCam requested a review from a team as a code owner November 11, 2026 20:48
@BioCam

BioCam commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

when this helps, on a windows pc that keeps FTDI's own driver:

  • sharing a pc with the vendor software: plr and the vendor software take turns on one instrument, no driver swap in between
  • locked-down or validated pcs: no admin rights and no driver change needed, unlike zadig
  • no silent breakage: windows update or a vendor update reinstalling FTDI's driver can't undo anything
  • first run: install, plug in, run; no zadig step
  • clearer errors: a channel held by another program is refused before anything is sent, saying what to close

not covered: running plr and the vendor software at the same time (one program per channel), and devices already rebound with zadig (keep ftdi_library="libftdi"). other FTDI devices can opt in with ftdi_library="d2xx" once checked on it; only the Mantis defaults to it here.

@rickwierenga

Copy link
Copy Markdown
Member

this is awesome! please merge (mantis should be separated though)

BioCam and others added 2 commits November 11, 2026 22:43
- `ftdi_library="libftdi" | "d2xx" | None`; the default stays "libftdi", so no device changes; None picks D2XX on Windows when FTDI's D2XX library loads, else libftdi
- D2XX is loaded with ctypes from FTDI's driver install; no new dependency, nothing bundled
- channels resolve by serial number plus interface letter; device_id stays the serial without it; without interface_select a multi-interface chip opens channel A, as libftdi does; usb_address is refused under D2XX
- every call mapped to its D2XX call with libftdi's values; 1.5 stop bits refused; reads return what has arrived without waiting; a short write raises
- errors name the likely cause: a channel held by another program, or bound to another driver
- a D2XX setup is recorded in captures; FTDIValidator.setup takes that record when present

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `ftdi_library` on `Mantis()`, `MantisDevice` and `MantisDriver`, passed to both links (interfaces A and B); None picks FTDI's D2XX library on Windows when it loads, else libftdi
- a simulated Mantis refuses `ftdi_library`, as it refuses any link argument
- the model-file test names the meshed parts instead of probing for a mesh attribute

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BioCam
BioCam changed the base branch from main to mantis-v1-connect-clamp November 11, 2026 22:44
@BioCam

BioCam commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

stacked on #1612 now, so this diff is just the ftdi change (3 io files) plus the small mantis switch that uses it (4 files, ~35 lines). #1612 merges first, then this one.

This branch has not been deployed

No deployments
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.

2 participants