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
from zaber_motion.ascii import Connection
with Connection.open_serial_port("COM3") as connection:
connection.enable_alerts()
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)
{
using (var connection = Connection.OpenSerialPort("COM3")) {
connection.EnableAlerts();
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, ascii: { Connection } } = require('@zaber/motion');
async function main() {
const connection = await Connection.openSerialPort('COM3');
try {
await connection.enableAlerts();
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.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)
{
try (Connection connection = Connection.openSerialPort("COM3")) {
connection.enableAlerts();
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;
connection = Connection.openSerialPort('COM3');
try
connection.enableAlerts();
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";
LENGTH_MILLIMETRES = java_get("zaber.motion.Units", "LENGTH_MILLIMETRES");
connection = javaMethod("openSerialPort", CONNECTION_CLASS, "COM3");
try
connection.enableAlerts();
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");
connection.enableAlerts();
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.
Opening a Connection
with Connection.open_serial_port("COM3") as connection:
connection.enable_alerts()
# ...
using (var connection = Connection.OpenSerialPort("COM3")) {
connection.EnableAlerts();
// ...
}
const connection = await Connection.openSerialPort('COM3');
try {
await connection.enableAlerts();
// ...
} finally {
// Close the port to allow Node.js to exit
await connection.close();
}
try (Connection connection = Connection.openSerialPort("COM3")) {
connection.enableAlerts();
// ...
}
connection = Connection.openSerialPort('COM3');
try
connection.enableAlerts();
% ...
connection.close();
catch exception
connection.close();
rethrow(exception);
end
connection = javaMethod("openSerialPort", CONNECTION_CLASS, "COM3");
try
connection.enableAlerts();
% ...
connection.close();
catch exception
connection.close();
rethrow(exception);
end
Connection connection = Connection::openSerialPort("COM3");
connection.enableAlerts();
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.
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.
Calling the @method EnableAlerts
method allows devices to communicate back without preceding requests.
This allows more timely detection of the end of movement when using ZML.
If you are planning to implement your own low-level communication (e.g. using Arduino),
alert handling may increase the complexity of your implementation.
Additionally, some of Zaber's legacy software may not be able to handle alerts.
In such cases, you can omit this line or later set comm.alert
setting back to 0
.
Detecting devices
Calling the @method DetectDevices
method searches the connection for all the present devices and performs their identification.
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
var deviceList = connection.DetectDevices();
Console.WriteLine($"Found {deviceList.Length} devices.");
const deviceList = await connection.detectDevices();
console.log(`Found ${deviceList.length} devices.`);
Device[] deviceList = connection.detectDevices();
System.out.println(String.format("Found %d devices.", deviceList.length));
}
deviceList = connection.detectDevices();
fprintf('Found %d devices.\n', deviceList.length);
deviceList = connection.detectDevices();
fprintf("Found %d devices.\n", length(deviceList));
std::vector<Device> deviceList = connection.detectDevices();
std::cout << "Found " << deviceList.size() << " devices." << std::endl;
During the identification, the library connects to the internet to retrieve information about Zaber devices. No personal or identifying information is exchanged with Zaber servers. The library stores all the information in the operating system cache folder to later allow for offline use. For more information about the behaviour see Device Database.
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.
Exception "string too long"
If you observe "string too long" exception (std::length_error
) consistently on every library call,
it's likely caused by loading an incorrect DLL.
The library comes with both Debug DLL and Release DLL. The Debug DLL is located in C:/Program Files/Zaber Motion Library/bin/Debug
,
and the Release DLL is located in C:/Program Files/Zaber Motion Library/bin
.
To resolve the issue, configure your project to load the appropriate DLL.
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.