Getting Started with OpenZen
Overview
OpenZen allows to access many different sensor models which are connected via different transports layers with one unified programming interface. This allows to develop applications which support a wide range of sensors and allows to easily adapt your application to new or additional sensors.
The following section will walk you through the process of accessing, configuring and streaming data from
a sensor connected to OpenZen. If you prefer to study and run a full code example, please have a look
at this example source file.
IO Systems
To communicate with the sensor hardware, OpenZen provides multiple so-called IO systems. Each of these can establish the communication to sensors and can list the available sensor hardware. Here are some examples of IO systems available in OpenZen:
USB virtual COM port
USB Express
Bluetooth
Network streaming
You can find the full list of available IO systems in the section [io systems].
Sensor Components
A sensor can have multiple components which supply various types of sensor data. Once the connection
to a sensor has been established, the available components can be queried and their properties can
be access and modified. Furthermore, the streaming of measurement data can be started for each component.
At this time OpenZen supports the following components:
Inertial Measurement Unit (IMU)
Provides accelerometer and gyroscope measurements and the orientation result from the sensor fusion running on the connected sensor.
Global Navigation Satellite System (GNSS)
...
Getting Started with OpenZen
OpenZen allows to access many different sensor models which are connected via different transports layers with one unified programming interface. This allows to develop applications which support a wide range of sensors and allows to easily adapt your application to new or additional sensors.
A list of the must-have parts of an OpenZen client program is:
Creating an OpenZen client
Connecting to a sensor
Accessing sensor components
Receiving events from OpenZen (for retrieving available sensors, or accessing sensor data)
⚠️ Do not add wait or sleep here as it would cause OpenZen client to read in outdated data.Closing the sensor connection
Optional parts:
Listing available sensors (rely on Receiving events from OpenZen)
Reading and modifying sensor properties
The best way to learn how OpenZen client program works is to take a look at our example source file.
For this page, the example codes are written in C++. We also have Python, C and C# examples in our repo, please check the side navbar for more details (compilations and programming languages support).
IO Systems
To communicate with the sensor hardware, please specify through which IO systems OpenZen should access your sensor.
Note |
---|
Permissions and settings required for Linux userCOM port: In the terminal execute Bluetooth: To be able to connect to any Bluetooth sensor, it first needs to be paired via the operating system’s device manager. |
Sensor Names | IG1 / IG1P Except RS485 | U3 / U2 CU / URS / UTTL | B2 | IG1 / IG1P RS485 | NAV3 | BE / ME |
---|---|---|---|---|---|---|
Connection Baudrate | 921600 | 115200 |
USB virtual COM port
Code Block // Windows auto sensorPair = client.obtainSensorByName("WindowsDevice", "//./COM40", 921600); // Linux auto sensorPair = client.obtainSensorByName("LinuxDevice", "devicefile:/dev/ttyUSB0", 921600);
USB Express (on Windows only)
Code Block auto sensorPair = client.obtainSensorByName("SiUsb", "ig1pcan000028", 921600);
Bluetooth (only for B2 sensor)
Code Block auto sensorPair = client.obtainSensorByName("Bluetooth", "00:04:3E:53:E9:9F", 115200);
Network streaming
You can find the full list of available IO systems in the section io-systems.
Sensor Components
A sensor can have multiple components which supply various types of sensor data. Once the connection
to a sensor has been established, the available components can be queried. Sensor data and their properties can be access / modified.
Inertial Measurement Unit (IMU)
Provides accelerometer and gyroscope measurements and the orientation result from the sensor fusion running on the connected sensor. All our sensors provide IMU components in OpenZen.
Global Navigation Satellite System (GNSS)
Provides global position measurements via GPS, BeiDou, GLONASS and Galileo, the associated error estimates and information on the quality and mode of the GNSS fix. Only IG1P sensor has GNSS component.
Keys for Sensor Data Access
Sensor data are collected as events in by the client program. Most of the keys listed in imuData and gnssData are available for access. Note that there are a few limitations on specific data fields:
“o”: available, “x”: not available
Field name (explanation) | IG1 / IG1P | U3 | NAV3 | U2 | B2 | BE / ME |
---|---|---|---|---|---|---|
g1 low range | o 3 axes high-precision gyro | x | o vertical axis high-precision gyro | o | o | x |
g2 general-purpose high range | o | o | x | x | x | o |
Info |
---|
To debug your OpenZen program, we have a Windows GUI client to work with sensors. There are 2 communications that our sensors follows. They are categorized as follows:
LPMSControl2 for LPMS3 sensors, OpenMAT for LPMS2 sensors. For detailed information of all our sensors, please visit our Knowledge Base. |
...
Note |
---|
Information above should have already given you a pretty nice illustration of the OpenZen library’s compositions. You can skip reading the following sections if you understand the sample program. The old OpenZen docs can be found at https://lpresearch.bitbucket.io/openzen/latest/getting_started.html |
Creating an OpenZen Client
The following description will use the C++ interface to OpenZen but other language bindings use the same concepts
described here. To see the description how to use the other language bindings, please have a look at the respective
section for the language you are interested in.
In order to acquire sensor access via OpenZen, you first need to create an OpenZen client. This can be done with the
utility method zen::make_client
. This call returns an instance of the ZenClient class:
Code Block |
---|
auto clientPair = zen::make_client(); auto& clientError = clientPair.first; auto& client = clientPair.second; if (clientError) // error when creating OpenZen client return clientError; |
Receiving Events from OpenZen
Every ZenClient instance contains its own event queue which accumulates events
from all sensors that were obtained on that client. Events can either be polled
using ZenClient::pollNextEvent
or waited for using ZenClient::waitForNextEvent
.
The only way to terminate a client that is waiting for an event, is by destroying
the client or preemptively calling ZenClient::close
.
.. this maybe explain how to setup an event loop here ?
Listing Available Sensors
The IO systems available on your platform can automatically list all available
sensors connected to the system. In order to do this, you need to call the method
ZenClient::listSensorsAsync
to start the query for available sensors. Depending
on the IO systems, it can take a couple of seconds for the listing to be complete.
Code Block |
---|
if (auto error = client.listSensorsAsync())
{
// error while listing available senors
return error;
} |
The ZenClient::listSensorsAsync
method will return immediately and
the information on the found sensors will be send to ZenClient's event queue and
can be retrieved with calls to ZenClient::pollNextEvent
or
ZenClient::waitForNextEvent
. You can either do this on your applications main
thread or use a background thread to retrieve the event listing data.
The following code snippets shows an example how to receive the OpenZen events
which contain the information during the sensor discovery process.
If the event.component.handle
is 0, the event is not specific to a sensor
but an event which is related by the OpenZen client itself, like the sensor
discovery events. Next, we can check the event.eventType
field for theZenSensorEvent_SensorFound
and ZenSensorEvent_SensorListingProgress
event types. For the ZenSensorEvent_SensorFound
event, the field
event.data.sensorFound
is of type ZenSensorDesc
which contains the
sensor's name, serial number and all information required to connect to the sensor.
Code Block |
---|
bool listingComplete = false;
while (!listingComplete) {
const auto pair = client.waitForNextEvent();
const bool success = pair.first;
auto& event = pair.second;
if (!success)
break;
if (!event.component.handle)
{
switch (event.eventType)
{
case ZenEventType_SensorFound:
std::cout << "Found sensor " << event.data.sensorFound.name << std::endl;
break;
case ZenEventType_SensorListingProgress:
std::cout << "Sensor listing progress " << event.data.sensorListingProgress.progress
<< " %" << std::endl;
if (event.data.sensorListingProgress.complete) {
listingComplete = true;
}
break;
}
}
} |
Connecting to a Sensor
A sensor found by the ZenClient::listSensorsAsync
call can be connected via the OpenZen
client using the ZenSensorDesc
which is contained in the event.data.sensorFound
of
the ZenSensorEvent_SensorFound
. The method call to ZenClient::obtainSensor
returns a
std::pair
where the first entry is a possible error code and the second entry is an
instance of the object zen::ZenSensor
which can be used to access the Sensor's
properties.
Code Block |
---|
auto sensorPair = client.obtainSensor(sensorDesc);
auto& obtainError = sensorPair.first;
auto& sensor = sensorPair.second;
if (obtainError)
{
// error while obtaining the sensor
return obtainError;
} |
Sensors can also connected directly if the IO system they are connected too and their
name is known already. Here, the method ZenClient::obtainSensorByName
can be called
with the name of the IO system and the name of the sensor:
Code Block |
---|
// connect the sensor with the name lpmscu2000573 via the SiLabs USB IO System
auto sensorPair = client.obtainSensorByName("SiUsb", "lpmscu2000573", 921600);
auto& obtainError = sensorPair.first;
auto& sensor = sensorPair.second;
if (obtainError)
{
// error while obtaining the sensor
return obtainError;
} |
Please check the documentation in the section :ref: io-system-label. for the available
IO systems and which naming conventions they use to identify connected sensors.
You can connect multiple sensor via one ZenClient
and the events of all
sensor will be available on the event queue of the ZenClient
instance.
Reading and Modifying Sensor Properties
OpenZen allows to read an modify the properties of connected sensors. Which properties
are available depends on the concrete sensor connected. You can find more information
on sensor properties in the section ref: supported-sensors-label.
Each sensor property in OpenZen has a specific data type and the respective method needs
to needsto be used on the zen::ZenSensor
instance.
Code Block |
---|
auto sensorModelPair = sensor.getStringProperty(ZenSensorProperty_SensorModel);
auto & sensorModelError = sensorModelPair.first;
auto & sensorModelName = sensorModelPair.second;
if (sensorModelError) {
// error while reading the string property from the sensor
return sensorModelError;
}
std::cout << "Sensor Model: " << sensorModelName << std::endl; |
Accessing Sensor Components
To access a specific component of a sensor, the method call ZenSensor::getAnyComponentOfType
can be used to retrieve the component of a specific type. If this component is not available
on this sensor, an error will be returned.
Code Block |
---|
auto imuPair = sensor.getAnyComponentOfType(g_zenSensorType_Imu);
auto& hasImu = imuPair.first;
auto imu = imuPair.second;
if (!hasImu)
{
// error, this sensor does not have an IMU component
return ZenError_WrongSensorType;
} |
As with the ZenSensor
class the ZenSensorComponent
returned by ZenSensor::getAnyComponentOfType
call can be used to access and modify the properties of the sensor component.
Reading Sensor Values
To start receiving data from a connected sensor, you need to ensure that the sensor
is in streaming mode to send out data on its own:
Code Block |
---|
if (auto error = imu.setBoolProperty(ZenImuProperty_StreamData, true))
{
// cannot set sensor into streaming mode
return error;
} |
Now, sensor events with measurement data will be available on the event queue of the OpenZen client.
You can use the previously introduced methods ZenClient::pollNextEvent
or
ZenClient::waitForNextEvent
to retrieve the sensor data of the inertial measurement unit:
Code Block |
---|
const auto pair = client.waitForNextEvent();
const bool success = pair.first;
auto& event = pair.second;
if (!success)
break;
// ensure the event is from the IMU component
if (event.component.handle == imu.component().handle)
{
switch (event.eventType)
{
case ZenEventType_ImuData:
std::cout << "> Acceleration: \\t x = " << event.data.imuData.a[0]
<< "\\t y = " << event.data.imuData.a[1]
<< "\\t z = " << event.data.imuData.a[2] << std::endl;
// take note that in some sensors the gyro data is stored in g2
// check the table below for details
std::cout << "> Gyro: \\t\\t x = " << event.data.imuData.g[0]
<< "\\t y = " << event.data.imuData.g1[1]
<< "\\t z = " << event.data.imuData.g1[2] << std |
...
.. list-table:: Sensors and its gyro key name
:header-rows: 1
Field name
IG1 / IG1P
LPMS3
NAV3
BE / ME
g1
o
x
o
x
g2
o
o
x
o
::endl;
break;
}
} |
To process the GNSS data streamed from the sensor, you can filter for events coming from
the GNSS component like this:
Code Block |
---|
if (event.component.handle == g_gnssHandle)
{
switch (event.eventType)
{
case ZenEventType_GnssData:
std::cout << "> GPS Fix Type: \\t = " << event.data.gnssData.fixType << std::endl;
std::cout << "> Longitude: \\t = " << event.data.gnssData.longitude
<< " Latitude: \\t = " << event.data.gnssData.latitude << std::endl;
break;
}
} |
...
Closing the Sensor Connection
Once you are done with sampling sensor values, you can release the connection to the
sensor and close the connection with the client:
Code Block |
---|
sensor.release();
client.close(); |