Skip to content

Code

The following complete example demonstrates the basic usage of the library. Running the program will make your Zaber device move.

Operating system:

Before running the code, replace the placeholder port name, COM3, with name of your port. If you are uncertain about the name, see our guide to finding the right port.

Before running the code, replace the placeholder port name, COM3, with name of your port (for example /dev/ttyUSB0). If you are uncertain about the name, see our guide to finding the right port.

Before running the code, replace the placeholder port name, COM3, with name of your port (for example /dev/tty.usbserial-A4017DXH). If you are uncertain about the name, see our guide to finding the right port.

from zaber_motion import Units, Library
from zaber_motion.ascii import Connection

Library.enable_device_db_store()

with Connection.open_serial_port("COM3") as connection:
    device_list = connection.detect_devices()
    print("Found {} devices".format(len(device_list)))

    device = device_list[0]

    axis = device.get_axis(1)
    if not axis.is_homed():
      axis.home()

    # Move to 10mm
    axis.move_absolute(10, Units.LENGTH_MILLIMETRES)

    # Move by an additional 5mm
    axis.move_relative(5, Units.LENGTH_MILLIMETRES)
using System;
using Zaber.Motion;
using Zaber.Motion.Ascii;

namespace csharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Library.EnableDeviceDbStore();

            using (var connection = Connection.OpenSerialPort("COM3")) {
                var deviceList = connection.DetectDevices();
                Console.WriteLine($"Found {deviceList.Length} devices.");

                var device = deviceList[0];

                var axis = device.GetAxis(1);
                if (!axis.IsHomed())
                {
                    axis.Home();
                }

                // Move to 10mm
                axis.MoveAbsolute(10, Units.Length_Millimetres);

                // Move by an additional 5mm
                axis.MoveRelative(5, Units.Length_Millimetres);
            }
        }
    }
}
const { Length, Library, ascii: { Connection } } = require('@zaber/motion');

async function main() {
    Library.enableDeviceDbStore();

    const connection = await Connection.openSerialPort('COM3');
    try {
        const deviceList = await connection.detectDevices();
        console.log(`Found ${deviceList.length} devices.`);

        const device = deviceList[0];

        const axis = device.getAxis(1);
        if (!await axis.isHomed()) {
            await axis.home();
        }

        // Move to 10mm
        await axis.moveAbsolute(10, Length.mm);

        // Move by an additional 5mm
        await axis.moveRelative(5, Length.mm);

    } finally {
        // close the port to allow Node.js to exit
        await connection.close();
    }
}

main();
package zaber_example;

import zaber.motion.Library;
import zaber.motion.Units;
import zaber.motion.ascii.Axis;
import zaber.motion.ascii.Connection;
import zaber.motion.ascii.Device;

/**
 * Example Java Zaber application
 */
public class App
{
    public static void main(String[] args)
    {
        Library.enableDeviceDbStore();

        try (Connection connection = Connection.openSerialPort("COM3")) {
            Device[] deviceList = connection.detectDevices();
            System.out.println(String.format("Found %d devices.", deviceList.length));

            Device device = deviceList[0];

            Axis axis = device.getAxis(1);
            if (!axis.isHomed())
            {
                axis.home();
            }

            // Move to 10mm
            axis.moveAbsolute(10, Units.LENGTH_MILLIMETRES);
            // Move by an additional 5mm
            axis.moveRelative(5, Units.LENGTH_MILLIMETRES);
        }
    }
}
import zaber.motion.ascii.Connection;
import zaber.motion.Units;
import zaber.motion.Library;

Library.enableDeviceDbStore();

connection = Connection.openSerialPort('COM3');
try
    deviceList = connection.detectDevices();
    fprintf('Found %d devices.\n', deviceList.length);

    device = deviceList(1);

    axis = device.getAxis(1);
    if ~axis.isHomed()
        axis.home();
    end

    % Move to 10mm
    axis.moveAbsolute(10, Units.LENGTH_MILLIMETRES);
    % Move by an additional 5mm
    axis.moveRelative(5, Units.LENGTH_MILLIMETRES);

    connection.close();
catch exception
    connection.close();
    rethrow(exception);
end
javaaddpath("motion-library-jar-with-dependencies.jar");

CONNECTION_CLASS = "zaber.motion.ascii.Connection";
LIBRARY_CLASS = "zaber.motion.Library";
LENGTH_MILLIMETRES = java_get("zaber.motion.Units", "LENGTH_MILLIMETRES");

javaMethod("enableDeviceDbStore", LIBRARY_CLASS);

connection = javaMethod("openSerialPort", CONNECTION_CLASS, "COM3");
try
    deviceList = connection.detectDevices();
    fprintf("Found %d devices.\n", length(deviceList));

    device = deviceList(1);

    axis = device.getAxis(1);
    if (!axis.isHomed())
        axis.home();
    endif

    % Move to 10mm
    axis.moveAbsolute(10, LENGTH_MILLIMETRES);
    % Move by an additional 5mm
    axis.moveRelative(5, LENGTH_MILLIMETRES);

    connection.close();
catch exception
    connection.close();
    rethrow(exception);
end
#include <iostream>
#include <vector>
#include <zaber/motion/ascii.h>

using namespace zaber::motion;
using namespace zaber::motion::ascii;

int main() {
    Library::enableDeviceDbStore();

    Connection connection = Connection::openSerialPort("COM3");

    std::vector<Device> deviceList = connection.detectDevices();
    std::cout << "Found " << deviceList.size() << " devices" << std::endl;

    Device device = deviceList[0];

    Axis axis = device.getAxis(1);
    if (!axis.isHomed()) {
        axis.home();
    }

    // Move to 10mm
    axis.moveAbsolute(10, Units::LENGTH_MILLIMETRES);

    // Move by an additional 5mm
    axis.moveRelative(5, Units::LENGTH_MILLIMETRES);

    return 0;
}

Detailed Explanation

The following section provides detailed explanation of the example code along with some additional information about Zaber devices.

Initialization

Library.enable_device_db_store()
Library.EnableDeviceDbStore();
Library.enableDeviceDbStore();
Library.enableDeviceDbStore();
Library.enableDeviceDbStore();
javaMethod("enableDeviceDbStore", LIBRARY_CLASS);
Library::enableDeviceDbStore();

The library connects to the internet to retrieve information about Zaber devices. Calling the @method EnableDeviceDbStore method makes the library store the downloaded information to later allow for offline use. For more information about the behaviour see Device Database.

Opening a Connection

with Connection.open_serial_port("COM3") as connection:
    device_list = connection.detect_devices()
    print("Found {} devices".format(len(device_list)))

    # ...
using (var connection = Connection.OpenSerialPort("COM3")) {
    var deviceList = connection.DetectDevices();
    Console.WriteLine($"Found {deviceList.Length} devices.");

    // ...
}
const connection = await Connection.openSerialPort('COM3');
try {
    const deviceList = await connection.detectDevices();
    console.log(`Found ${deviceList.length} devices.`);

    // ...
} finally {
    // Close the port to allow Node.js to exit
    await connection.close();
}
try (Connection connection = Connection.openSerialPort("COM3")) {
    Device[] deviceList = connection.detectDevices();
    System.out.println(String.format("Found %d devices.", deviceList.length));

    // ...
}
connection = Connection.openSerialPort('COM3');
try
    deviceList = connection.detectDevices();
    fprintf('Found %d devices.\n', deviceList.length);

    % ...

    connection.close();
catch exception
    connection.close();
    rethrow(exception);
end
connection = javaMethod("openSerialPort", CONNECTION_CLASS, "COM3");
try
    deviceList = connection.detectDevices();
    fprintf("Found %d devices.\n", length(deviceList));

    % ...

    connection.close();
catch exception
    connection.close();
    rethrow(exception);
end
Connection connection = Connection::openSerialPort("COM3");

std::vector<Device> deviceList = connection.detectDevices();
std::cout << "Found " << deviceList.size() << " devices." << std::endl;

The first step towards communicating with the device is opening a connection. In the example we create a connection by opening a serial port but the library supports other ways such as TCP. If you encounter a problem at this step, head to the Troubleshooting section below.

Afterwards, calling of the @method DetectDevices method searches the connection for all the present devices and performs their identification.

The code also demonstrates use of a with statement to ensure that the serial port closes after the program runs.

The code also demonstrates use of a using statement to ensure that the serial port closes after the program runs.

The code also demonstrates use of a try, finally statement to ensure that the serial port closes after the program runs. Closing the port is crucial because an open port prevents Node.js from exiting.

The code also demonstrates use of a try-with-resources statement to ensure that the serial port closes after the program runs.

The code also demonstrates use of a try-catch statement to ensure that the serial port closes in case of an exception.

The serial port is closed on object deconstruction. No additional try-catch or explicit close() call is needed.

Homing the Device

After detecting the devices, we pick the first device from the list and make sure it's homed.

device = device_list[0]

axis = device.get_axis(1)
if not axis.is_homed():
    axis.home()
var device = deviceList[0];

var axis = device.GetAxis(1);
if (!axis.IsHomed())
{
    axis.Home();
}
const device = deviceList[0];

const axis = device.getAxis(1);
if (!await axis.isHomed()) {
    await axis.home();
}
Device device = deviceList[0];

Axis axis = device.getAxis(1);
if (!axis.isHomed()) {
    axis.home();
}
device = deviceList(1);

axis = device.getAxis(1);
if ~axis.isHomed()
    axis.home();
end
device = deviceList(1);

axis = device.getAxis(1);
if (!axis.isHomed())
    axis.home();
endif
Device device = deviceList[0];

Axis axis = device.getAxis(1);
if (!axis.isHomed()) {
    axis.home();
}

Homing a device moves it so that it finds a reference position. A reference position is necessary in order to perform accurate absolute position movements. The device retains the reference position until it's powered off, so repeated homing is unnecessary as long as power is maintained.

Note that homing is performed using the @var axis object rather than the @var device object. Some devices have multiple axes, so the @class Axis class allows control of a specific axis on the device. Axes are indexed from 1. Therefore the code above homes the first axis of the device.

Moving the Device

Once homed, we can start moving the device by calling @method MoveAbsolute and @method MoveRelative methods.

# Move to 10mm
axis.move_absolute(10, Units.LENGTH_MILLIMETRES)
# Move by an additional 5mm
axis.move_relative(5, Units.LENGTH_MILLIMETRES)
// Move to 10mm
axis.MoveAbsolute(10, Units.Length_Millimetres);
// Move by an additional 5mm
axis.MoveRelative(5, Units.Length_Millimetres);
// Move to 10mm
await axis.moveAbsolute(10, Length.mm);
// Move by an additional 5mm
await axis.moveRelative(5, Length.mm);
// Move to 10mm
axis.moveAbsolute(10, Units.LENGTH_MILLIMETRES);
// Move by an additional 5mm
axis.moveRelative(5, Units.LENGTH_MILLIMETRES);
% Move to 10mm
axis.moveAbsolute(10, Units.LENGTH_MILLIMETRES);
% Move by an additional 5mm
axis.moveRelative(5, Units.LENGTH_MILLIMETRES);
% Move to 10mm
axis.moveAbsolute(10, LENGTH_MILLIMETRES);
% Move by an additional 5mm
axis.moveRelative(5, LENGTH_MILLIMETRES);
// Move to 10mm
axis.moveAbsolute(10, Units::LENGTH_MILLIMETRES);
// Move by an additional 5mm
axis.moveRelative(5, Units::LENGTH_MILLIMETRES);

At a low level, the devices operate in native units related to their drive type, such as microsteps for a stepper driver or encoder counts for a direct drive device. The library allows you to specify real-world units like millimetres, and handles the conversion to the specific native units. Use native units directly by omitting the second argument in the move methods, or by specifying the units to be native. To read more about native units refer, to the ASCII Protocol manual.

The above movements will execute one after the other, with the program waiting for the movement to finish before continuing. This behaviour can be controlled by including a third argument in the move method, with a boolean false indicating that the method should not wait.

If you have a rotary device use Units.ANGLE_DEGREES in the code above.

If you have a rotary device use Units.Angle_Degrees in the code above.

If you have a rotary device use Angle.DEGREES in the code above.

If you have a rotary device use Units.ANGLE_DEGREES in the code above.

If you have a rotary device use Units.ANGLE_DEGREES in the code above.

If you have a rotary device use Units::ANGLE_DEGREES in the code above.

Troubleshooting

Connection Failed Exception

Ensure that you have specified the correct serial port name. Check also that other applications, such as Zaber Console, do not have the port open by either selecting Close or exiting the application. Using the Zaber Launcher application does not cause a conflict over the serial port.

No Device Found Exception

Check that the device is connected correctly and powered on. Also ensure that you have specified the correct serial port name. You can use the Zaber Launcher application to verify communication with the devices.

Device Address Conflict Exception

If you encounter @class DeviceAddressConflictException, open the port in the Zaber Launcher application which will automatically reassign unique addresses to your devices.

Header Conflict

If your compilation fails in Microsoft Visual Studio, it may be due to a conflict between macros in the WinAPI C headers and the C++ headers of the library. To resolve the conflict, undefine the colliding macros before including the library.

#undef ERROR
#undef min
#include <zaber/motion/ascii.h>

This is a known issue that may be resolved in a major version update.

Permission denied

In some cases you may encounter:

Cannot open serial port: open /dev/ttyUSB0: permission denied

This problem occurs when the current user is not allowed to use serial ports on the computer. To resolve this issue open a console and enter the following:

sudo adduser $USER dialout

Logout and login again or reboot your computer and you should have access to the serial ports.

Port Locking on Unix

To avoid conflict with other software accessing the serial port, the library locks the port using the flock system call. If the lock cannot be acquired without blocking, the @method OpenSerialPort method throws a @class SerialPortBusyException exception. The library also puts the port into exclusive mode using the TIOCEXCL command of the ioctl system call to prevent further attempts to open the port. The lock is released and exclusive mode is disabled when the connection is closed.

Project Download

Additionally, we provide a download link to a complete project for each programming language.