Python library for Thorlabs Elliptec (ELLx) resonant piezo motor modules
Project description
tl_elliptec
Python library for Thorlabs Elliptec (ELLx) resonant piezo motor modules — rotary stages, linear stages, multi-position sliders, filter wheels, and the motorized iris. Implements the ASCII-hex serial protocol from Thorlabs' own protocol manual, including a priority-based request broker so background position polling never delays a move you're waiting on.
Full documentation: tl-elliptec.readthedocs.io
This is an unofficial, community-maintained library. It is not affiliated with, endorsed by, or sponsored by Thorlabs, Inc. "Thorlabs" and "Elliptec" are trademarks of Thorlabs, Inc., used here only to identify the hardware this library talks to.
Supported devices
ELL6, ELL6B, ELL9, ELL12, ELL14, ELL15, ELL16, ELL17, ELL18, ELL20, ELL21, ELL22.
Install
pip install tl_elliptec
Or for local development:
git clone https://github.com/TapyrLabs/tl_elliptec
pip install -e ".[test]"
Quick start
One device wired straight to its controller (no hub) — construct the model class directly from the serial port; it opens and owns the port itself (Make sure to use the correct COM port for your setup):
from tl_elliptec import ELL20
with ELL20("COM5") as stage: # "/dev/ttyUSB0" on Linux/macOS
stage.home()
stage.move_absolute(10) # 10 mm — physical units, not raw pulses
stage.move_relative(-2.5) # -2.5 mm from wherever it now is
print(stage.get_position()) # 7.5
Several devices on a shared bus
Create one ElliptecBus and hand it to each device — reading and moving
works exactly the same as above, just addressed:
from tl_elliptec import ElliptecBus, discover_devices
with ElliptecBus("COM5") as bus:
devices = discover_devices(bus) # probes addresses 0-F, builds the right class for each
for address, device in devices.items():
print(address, type(device).__name__, device.serial_number)
stage = devices["0"]
stage.home()
stage.move_absolute(45) # 45 degrees, if it's an ELL14
First-time address setup
Every ELLx module ships from the factory at address "0". Wire two or more
onto the same bus before giving them unique addresses and they all
answer to "0" at once — their replies collide on the wire, so neither
scan() nor discover_devices() finds anything. This has to be fixed once, per device, before they
share the bus. Either let the library walk you through it, always connect one device at a time:
from tl_elliptec import ElliptecBus, setup_devices
with ElliptecBus("COM5") as bus:
# Connect ONE new, unaddressed device at a time when prompted.
assigned = setup_devices(bus, count=2) # e.g. ["1", "2"]
...or do it by hand, one device connected at a time:
from tl_elliptec import ElliptecBus, ELL14
with ElliptecBus("COM5") as bus:
stage = ELL14(bus, address="0") # factory default, must be the only device on the bus
stage.change_address("2") # non-volatile -- a one-time step
Addresses persist across power cycles, so this only needs to happen once
per device — after that, discover_devices() finds everything normally,
every session.
Broker-handled communication and live position streaming
Normally, when many devices are connected to a single serial port, and with incessent polling, the communicaiton might go into conflict and brake. tl_elliptec implements a serial communication broker in the ElliptecBus class, the broker implements a read/write priority queue for safe command serialization. Explicitly issued commands (moves, reads, ...) always jump
ahead of background polling, so polling a device's position never delays a
move you're waiting on:
for position in stage.poll_position(): # a generator -- only work while it's iterated
print(position) # yields only when the position actually changes
This scales to several devices on one bus without extra locking — run one
poll_position() per device on its own thread, and issue moves from
another thread (or the main one) whenever you like.
Full command reference are all in the docs: tl-elliptec.readthedocs.io
Running the tests
pip install -e ".[test]"
pytest
No hardware required — tests run against a scripted fake bus and check values against the manual's own worked examples.
License
MIT © Matteo Michiardi
If this library saved you some time, consider buying me a coffee ☕
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tl_elliptec-1.0.0.tar.gz.
File metadata
- Download URL: tl_elliptec-1.0.0.tar.gz
- Upload date:
- Size: 41.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9f52f5d7fe84f42b2b44c3662f434665ff4e2c89a5ea8b3d269241a78e0f3c6
|
|
| MD5 |
fa3904c9cbc8bd426e942998ab71a1dd
|
|
| BLAKE2b-256 |
c3a8011c48e3b4d309574b653f9a78f44006774bccb0c6f8954008dd37f9b743
|
Provenance
The following attestation bundles were made for tl_elliptec-1.0.0.tar.gz:
Publisher:
publish.yml on TapyrLabs/tl_elliptec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tl_elliptec-1.0.0.tar.gz -
Subject digest:
f9f52f5d7fe84f42b2b44c3662f434665ff4e2c89a5ea8b3d269241a78e0f3c6 - Sigstore transparency entry: 2220391964
- Sigstore integration time:
-
Permalink:
TapyrLabs/tl_elliptec@6c717942662dfa12531fb602e42de41fcc37aeeb -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/TapyrLabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c717942662dfa12531fb602e42de41fcc37aeeb -
Trigger Event:
release
-
Statement type:
File details
Details for the file tl_elliptec-1.0.0-py3-none-any.whl.
File metadata
- Download URL: tl_elliptec-1.0.0-py3-none-any.whl
- Upload date:
- Size: 34.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74e9ad4be29a6f334fceb91b651a6e7805b615f4f8137045f1a89d06c506ce4f
|
|
| MD5 |
40c93b65ebd050b80303994e8184b528
|
|
| BLAKE2b-256 |
919f4a5c45ef3ad37fdba1745411630b25d22e4b98c324e074b43f7a2e4636f6
|
Provenance
The following attestation bundles were made for tl_elliptec-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on TapyrLabs/tl_elliptec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tl_elliptec-1.0.0-py3-none-any.whl -
Subject digest:
74e9ad4be29a6f334fceb91b651a6e7805b615f4f8137045f1a89d06c506ce4f - Sigstore transparency entry: 2220392487
- Sigstore integration time:
-
Permalink:
TapyrLabs/tl_elliptec@6c717942662dfa12531fb602e42de41fcc37aeeb -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/TapyrLabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c717942662dfa12531fb602e42de41fcc37aeeb -
Trigger Event:
release
-
Statement type: