Usage

Connecting to a Device

The device needs to be connected to the host computer via a RS232 style serial connection, for example, by using a USB to serial adaptor. The interface board supplied with the “kit” form is essentially just such a device. The first task is to discover which port the device is connected to. A utility function is provided to list the serial port devices available on the host.

from thorlabs_elliptec import ELLx, ELLError, ELLStatus, list_devices
print(list_devices())
# Prints something like:
# device=/dev/ttyUSB1, manufacturer=Prolific Technology Inc., product=USB-Serial Controller, vid=0x067b, pid=0x2303, serial_number=None, location=1-1.1

In this example, only a single port is available, so there is no issues with selecting the correct port. The device can be found automatically:

stage = ELLx()
print(f"{stage.model_number} #{stage.device_id} on {stage.port_name}, serial number {stage.serial_number}, status {stage.status.description}")
# Prints something like:
# ELL14 #0 on /dev/ttyUSB1, serial number 11400590, status ok

To be more selective with the port selection, several initialisation parameters are available. These are the same as those described in find_device():

stage = ELLx(vid=0x067b, pid=0x2303)
print(f"{stage.model_number} on {stage.port_name}, serial number {stage.serial_number}, status {stage.status.description}")
# Prints something like:
# ELL14 on /dev/ttyUSB1, serial number 11400590, status ok

To require a particular model number or serial number of the device, parameters are also available:

stage = ELLx(x=14, device_serial=123456789)
# Raises an exception:
# RuntimeError: Device does not have expected serial number '123456789'! (device reported '11400590')

Controlling a Device

Once the device is initialised, controlling it is straightforward:

# Move device to the home position
stage.home()
# Movements are in real units appropriate for the device (degrees, mm).
stage.move_absolute(45.0)
# The raw device encoder units can also be used with the move raw variants.
stage.move_absolute_raw(23456)

# By default, move commands are asynchronous (non-blocking) and return immediately,
# but you can manually wait for it to be in position
stage.move_absolute(45.0)
stage.wait()
stage.move_relative(-12.34)
# or test whether movement is still in progress.
print(stage.is_moving())
stage.wait()
print(stage.is_moving())
print(f"{stage.get_position()}{stage.units}")
# Prints something like:
# True
# False
# 32.655°

# Synchronous behaviour can also be achieved by setting the blocking=True parameter,
# which will perform the wait before returning from each movement command.
stage.home(blocking=True)
stage.move_absolute(1.23, blocking=True)
stage.move_relative(-0.98, blocking=True)

# When using the synchronous behaviour, any error during movement will raise an exception.
try:
    stage.move_absolute(-9999, blocking=True)
except ELLError as ex:
    if ex.status == ELLStatus.OUT_OF_RANGE:
        # Requested move beyond device limits
        print("Device can't move there!")
    else:
        # Other error, eg stage held or blocked so it can't move
        print(f"Movement error: {ex}")
else:
    print("Move completed OK")

# When using asynchronous calls, any errors won't have been detected yet,
# so instead, the is_moving() and wait() methods can raise the exception instead.
stage.move_relative(300)
try:
    print(stage.is_moving(raise_errors=True))
    stage.wait(raise_errors=True)
    print(stage.is_moving(raise_errors=True))
except ELLError as ex:
    print(f"Movement error: {ex}")

# Once done with the device, it can be specifically closed. Commands to the stage will no
# longer work until the device is re-initialised.
stage.close()

Multi-drop Feature

To use the multi-drop functionality, where multiple devices can be connected to the same serial lines, initialise the first ELLx device as usual. Subsequent devices can then be initialised by passing in a reference to a previously initialised ELLx class. For example:

from thorlabs_elliptec import ELLx
# For an ELL14 set to use device ID 1 on serial port device /dev/ttyUSB0
stage1 = ELLx(x=14, serial_port="/dev/ttyUSB0", device_id=1)
# Initialise subsequent devices by passing in a reference any previously initialised device
# This would be for an ELL8 configured for device ID 2
stage2 = ELLx(x=8, serial_port=stage1, device_id=2)
# And also an ELL20 using device ID 3
stage3 = ELLx(x=20, serial_port=stage1, device_id=3)

Note that if multiple devices are configured to share a single serial port, calling stage1.close() will stop stage1 running, but the connection to the serial port will not be released until all other devices also have their close() method called.

Further Reading

See the API documentation for the ELLx class for details on initialising and controlling devices.

For more information about the behaviour of the commands and device-specific details, see the documentation from Thorlabs on the Elliptec serial communication protocol.