Skip to main content

Plesty Thorlabs Elliptec Device

A Plesty device for Thorlabs Elliptec resonant piezo modules — an ELL14 rotation mount by default, and the rest of the family through the same ASCII protocol. Modules share one serial bus and are told apart by a hex address, so a port alone does not identify a device.

Installation

A lab PC often has no system git at all. plesty-server ships its own, so put that on PATH before anything else — the clone needs it and so does the sync:

$env:PATH = "$env:USERPROFILE\.plesty\server\tools\git\cmd;$env:PATH"
git clone https://gitlab.com/plesty/hub/devices/thorlab/plesty-thorlabs-elliptec.git
cd plesty-thorlabs-elliptec
uv sync

The sync needs git for a second reason, and fails differently without it. The build backend is versioningit, which derives the version by shelling out to git describe from the build subprocess — so an install driven through plesty-server succeeds while the identical command in a bare shell fails with:

NotSdistError: ... does not contain a PKG-INFO file
You may be installing from a shallow clone, in which case you need to
unshallow it first.

The message blames a shallow clone; the cause is the missing executable, and the clone is fine.

A plain git clone checks out main. The ELL14 work — homing in particular — is on exp.

Configuration

Copy .env.example to .env and fill in DEVICE_PORT — the serial port the bus is on, COM3 on Windows or /dev/ttyUSB0 on Linux. The bus interface enumerates as an FTDI USB-serial port. DEVICE_ADDRESS defaults to 0, which is what modules ship at.

The same .env drives the standalone server and the real-device test.

Finding the port instead of configuring it

A port is host-specific and can change across reboots, so it is the setting people most often get wrong. Leave DEVICE_PORT unset and the server searches for the module instead:

uv run python -m plesty.thorlabs_elliptec --tcp-port 5556
from plesty.thorlabs_elliptec import discover_modules, Device

for found in discover_modules():
    print(found)  # ELL14 (SN …, 143360 pulses/unit) at address 0 on COM3

stage = Device.discover()  # first module found, ready to connect()

Only in is sent, so nothing moves. Only ports with a USB vendor ID are probed — a motherboard's legacy COM1 and a Bluetooth serial profile both enumerate as serial ports, neither can be an Elliptec bus, and opening one disturbs whatever is there for no possible gain. FTDI adapters are tried first, since that is what the bus interface is, so the usual case opens one port and stops. Pass include_non_usb=True (or --include-non-usb) to widen that deliberately.

The default asks address 0 only. A bus whose modules have been re-addressed needs addresses=protocol.VALID_ADDRESSES to sweep all sixteen.

Run as TCP server

Make sure the module is connected and powered on.

uv run python -m plesty.thorlabs_elliptec --port COM3 --address 0 --tcp-port 5556

Give the port as the argument or as DEVICE_PORT; with neither, the server searches for the module as described above. Run one server per module when several share a bus, each on its own TCP port.

Use as a library

from plesty.thorlabs_elliptec import Device

with Device(port="COM3", address="0") as stage:
    stage.home()
    stage.move_absolute(degrees=45.0)
    stage.get_position()  # deg

A port whose name contains mock binds the simulator instead of a serial line, which is what the test suite runs against.

Before commanding motion, check that the mount can actually turn — that whatever it carries is clear of the bench and of neighbouring optics. A stage fouling on something still reports status OK and still answers every move: it simply arrives a few counts short, which reads as backlash rather than as an obstruction. Positions read at rest alternate between two adjacent encoder counts, so treat anything within a couple of counts as measurement noise.

Testing

uv run pytest              # simulator only — never opens a port
uv run pytest --hardware   # adds the tests that talk to a real module

--hardware is deliberately a flag and not a config file. A bench sets DEVICE_PORT in .env once and forgets it, so gating on that setting alone made a bare pytest a hardware operation on every machine wired to an instrument — harmless while the only such test connected and identified, and not harmless the moment someone adds one that moves the stage. The flag says may; DEVICE_PORT still says where, and both are required.

Two ways a green suite lies

A green suite here does not mean the driver can talk to a module. There are two distinct reasons, and they need different defences.

A mock that agrees with the driver. The simulator has three times certified a command that did nothing on hardware:

  • it accepted a mode="query" keyword the real serial transport rejects, so no command could be sent at all;
  • it reported an ELL14 resolution of 262144, a figure no ELL14 has;
  • it answered a home that carried no direction byte, which a rotary module ignores completely.

One cause underneath all three: the simulator was written from the driver rather than from the protocol manual, so it encodes the driver's misunderstanding and then certifies it. A mock built that way cannot fail in the direction of the instrument, only in the direction of the code.

So when adding to _simulator.py, take the frame from the manual, and make it refuse what the hardware refuses — a handler that ignores its payload will accept a command the module will not.

A path no development machine can reach. Port discovery shipped with a 0.5 s probe window, which the serial transport truncates to int(0.5) == 0, so it found nothing on any bench. Every test passed — not because anything was mocked wrongly, but because a machine with no candidate serial port never enters the probe at all, and an unexecuted path is indistinguishable from a passing one in the test output.

This is the worse of the two. A wrong mock can be corrected by reading the manual; a path that only a bench with the hardware attached can execute cannot be reasoned about from a laptop at all. Anything about framing, timing or the transport needs a real module in front of it before it is believed.

Documentation

uv run plesty docs serve builds and serves the reference pages — parameters, operations and standard methods are generated from the device itself.

License

LGPL-3.0-or-later — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plesty_thorlabs_elliptec-0.1.0.tar.gz (182.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl (42.3 kB view details)

Uploaded Python 3

File details

Details for the file plesty_thorlabs_elliptec-0.1.0.tar.gz.

File metadata

  • Download URL: plesty_thorlabs_elliptec-0.1.0.tar.gz
  • Upload date:
  • Size: 182.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plesty_thorlabs_elliptec-0.1.0.tar.gz
Algorithm Hash digest
SHA256 119d802e48fc4521ffc87953ad987813d2113f7b5700671d04252ecf3236d739
MD5 3e7166131ddc1521e9215682eb008b94
BLAKE2b-256 0f86a64a15f2e45bb69fd907fc70c506d7c4dfc10c18e1744300ab75e7fba15f

See more details on using hashes here.

File details

Details for the file plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 42.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7933fec911264620b42f0110165fa80bbe5451ec7cf15eec627fb83419cf63c
MD5 bd76c0c3adb271f83fd7b688aacda709
BLAKE2b-256 34adeeacd8afd432f502ff2d13cc58dad07c925751436ecd0e1b70fe43f3b259

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page