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.
Metadata
Release files for plesty-thorlabs-elliptec 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| plesty_thorlabs_elliptec-0.1.0.tar.gz | 182.7 kB | Without provenance | Details for plesty_thorlabs_elliptec-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl |
Total release size: 224.9 kB
Release files / plesty_thorlabs_elliptec-0.1.0.tar.gz
| Download URL | plesty_thorlabs_elliptec-0.1.0.tar.gz |
|---|---|
| Size | 182.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
119d802e48fc4521ffc87953ad987813d2113f7b5700671d04252ecf3236d739
|
|
BLAKE2b-256 checksum How to use checksums |
0f86a64a15f2e45bb69fd907fc70c506d7c4dfc10c18e1744300ab75e7fba15f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl
| Download URL | plesty_thorlabs_elliptec-0.1.0-py3-none-any.whl |
|---|---|
| Size | 42.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f7933fec911264620b42f0110165fa80bbe5451ec7cf15eec627fb83419cf63c
|
|
BLAKE2b-256 checksum How to use checksums |
34adeeacd8afd432f502ff2d13cc58dad07c925751436ecd0e1b70fe43f3b259
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Security for plesty-thorlabs-elliptec 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.