An opinionated library that enhances working with openDAQ modules from MicroStrain by HBK.
Add the library to your project using CMake FetchContent, replacing <version> with the desired release tag:
include(FetchContent)
FetchContent_Declare(
MicroStrainDaqUtils
GIT_REPOSITORY https://github.com/HBK-MicroStrain/microstrain-daq.git
GIT_TAG <version>
SOURCE_SUBDIR microstrain-daq-utils/daq_utils/cpp
)
FetchContent_MakeAvailable(MicroStrainDaqUtils)
target_link_libraries(your_target PRIVATE microstrain::daq_utils)To import the library into your project:
#include <daq_utils/daq_utils.h>
#include <daq_utils/wireless.h>pip install microstrain-daq-utils
To import the library into your project:
import daq_utilsdotnet add package MicroStrain.DaqUtils
To import the library into your project:
using Daq.Utils;See Usage for examples of how to use the library.
The Python library bindings can be used with JupyterLab, and a separate package provides notebook templates pre-configured for various use cases.
This is ideal for exploration, prototyping, and testing.
pip install microstrain-daq-jupyter
Note: Notebook templates require the Library to be installed.
Navigate to the directory where you would like to save your notebooks and launch JupyterLab:
jupyter lab
To create a new notebook from a template, click the new launcher button:
Then, click the Template tile in the Notebook section:
Then, select the desired template:
| Template | Description |
|---|---|
| Starter | Pre-configured openDAQ and library setup ready to use |
See the openDAQ documentation for a full reference on the openDAQ API. For wireless-specific usage and examples, see the Wireless guide.
By default, openDAQ loads modules from its installation directory. To load modules from a different location, set the OPENDAQ_MODULE_PATH environment variable to the desired directory.
For example, to set it to the Downloads directory:
Windows
setx OPENDAQ_MODULE_PATH C:\Users\username\Downloads
Note:
setxtakes effect in new terminal sessions, not the current one. Restart your terminal after running this command.
Linux
touch ~/.bashrc && \
sed -i '/^export OPENDAQ_MODULE_PATH=/d' ~/.bashrc && \
echo 'export OPENDAQ_MODULE_PATH=~/Downloads' >> ~/.bashrc && \
source ~/.bashrc
Mac
touch ~/.bashrc && \
sed -i '' '/^export OPENDAQ_MODULE_PATH=/d' ~/.bashrc && \
echo 'export OPENDAQ_MODULE_PATH=~/Downloads' >> ~/.bashrc && \
source ~/.bashrc
Note: If multiple versions of the same module exist in the directory, the behavior is undefined. Remove the old version before adding the new one.
Then pass the path when creating your openDAQ instance:
C++
#include <opendaq/opendaq.h>
daq::InstanceBuilderPtr builder = daq::InstanceBuilder();
if (const char* modulePath = std::getenv("OPENDAQ_MODULE_PATH")) {
builder.setModulePath(modulePath);
}
daq::InstancePtr instance = builder.build();Python
import os
import opendaq as daq
builder = daq.InstanceBuilder()
if module_path := os.environ.get('OPENDAQ_MODULE_PATH'):
builder.module_path = module_path
instance = builder.build()C#
using Daq.Core.OpenDAQ;
var builder = OpenDAQFactory.InstanceBuilder();
var modulePath = Environment.GetEnvironmentVariable("OPENDAQ_MODULE_PATH");
if (modulePath != null)
builder.ModulePath = modulePath;
var instance = builder.Build();This code snippet will display a list of all currently available devices:
C++
for (daq::DeviceInfoPtr deviceInfo : instance.getAvailableDevices()) {
std::cout << "Name: " << deviceInfo.getName() << " Connection string: " << deviceInfo.getConnectionString() << "\n";
}Python
for device_info in instance.available_devices:
print('Name:', device_info.name, 'Connection string:', device_info.connection_string)C#
foreach (var deviceInfo in instance.AvailableDevices)
Console.WriteLine($"Name: {deviceInfo.Name} Connection string: {deviceInfo.ConnectionString}");Add a device using its connection string:
C++
daq::DevicePtr device = instance.addDevice("microstrain-wireless://COM46:3000000");Python
device = instance.add_device('microstrain-wireless://COM46:3000000')C#
var device = instance.AddDevice("microstrain-wireless://COM46:3000000");Connection strings are in the format: prefix://address.
When you are ready to remove the device:
C++
instance.removeDevice(device);Python
instance.remove_device(device)C#
instance.RemoveDevice(device);This will disconnect the device so you can use it in other applications.
Get a reference to a channel using it's index:
C++
daq::ChannelPtr channel = device.getChannels()[0];Python
channel = device.get_channels()[0]C#
var channel = device.GetChannels()[0];Properties are organized into groups. To print available property groups for a device, channel, group, or other root:
C++
daq_utils::PrintGroups(channel);Python
daq_utils.print_groups(channel)C#
DaqUtils.PrintGroups(channel);To get the groups as a list instead:
C++
std::vector<std::string> groups = daq_utils::Groups(channel);Python
daq_utils.groups(channel)C#
DaqUtils.Groups(channel);To print all properties across every group:
C++
daq_utils::PrintProperties(channel);Python
daq_utils.print_properties(channel)C#
DaqUtils.PrintProperties(channel);To filter to a specific group:
C++
daq_utils::PrintProperties(channel, "Setup.Configure.Sampling");Python
daq_utils.print_properties(channel, 'Setup.Configure.Sampling')C#
DaqUtils.PrintProperties(channel, "Setup.Configure.Sampling");To get the properties as a list instead:
C++
std::vector<daq_utils::PropertyInfo> props = daq_utils::Properties(channel);
std::vector<daq_utils::PropertyInfo> props = daq_utils::Properties(channel, "Setup.Configure.Sampling");Python
daq_utils.properties(channel)
daq_utils.properties(channel, 'Setup.Configure.Sampling')C#
DaqUtils.Properties(channel);
DaqUtils.Properties(channel, "Setup.Configure.Sampling");If you know a property name but not its full path, use find to get its full dot-notation path:
C++
std::string path = daq_utils::Find(channel, "LostBeaconTimeout");Python
daq_utils.find(channel, 'LostBeaconTimeout')C#
DaqUtils.Find(channel, "LostBeaconTimeout");This can also be used for finding groups:
C++
std::string path = daq_utils::Find(channel, "Sampling");Python
daq_utils.find(channel, 'Sampling')C#
DaqUtils.Find(channel, "Sampling");Properties can be accessed using dot-notation paths:
C++
daq::BaseObjectPtr timeout = channel.getPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout");Python
timeout = channel.get_property_value('Setup.Configure.Sampling.LostBeaconTimeout')C#
var timeout = channel.GetPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout");They can also be set:
C++
channel.setPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout", 7);Python
channel.set_property_value('Setup.Configure.Sampling.LostBeaconTimeout', 7)C#
channel.SetPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout", (IntegerObject)7);To view a function property's description, arguments, and return type, read the description field from the property:
C++
std::cout << channel.getProperty("Capabilities.MaxSweeps").getDescription() << "\n";Python
print(channel.get_property('Capabilities.MaxSweeps').description)C#
Console.WriteLine(channel.GetProperty("Capabilities.MaxSweeps").Description);Function properties can be called directly through the openDAQ API, but the wrapper simplifies the syntax. To call a function with no arguments using the wrapper:
C++
daq::BaseObjectPtr result = daq_utils::Call(channel, "Setup.Configure.Apply");Python
result = daq_utils.call(channel, "Setup.Configure.Apply")C#
var result = DaqUtils.Call(channel, "Setup.Configure.Apply");To call a function with arguments:
C++
daq::BaseObjectPtr result = daq_utils::Call(channel, "Capabilities.InputRangesWithVoltage", {0xFF, 5000});Python
result = daq_utils.call(channel, "Capabilities.InputRangesWithVoltage", 0xFF, 5000)C#
var result = DaqUtils.Call(channel, "Capabilities.InputRangesWithVoltage", (IntegerObject)0xFF, (IntegerObject)5000);The result object can then be queried for any returned properties. For example:
C++
daq::BaseObjectPtr success = result.asPtr<daq::IPropertyObject>().getPropertyValue("Success");Python
success = result.get_property_value('Success')C#
var success = result?.Cast<PropertyObject>()?.GetPropertyValue("Success");To view what properties are available for a device, channel, or group, create a DaqPropertyInspector:
C++
daq_utils::DaqPropertyInspector propInspector(instance);Python
prop_inspector = daq_utils.DaqPropertyInspector(instance)C#
var propInspector = new DaqPropertyInspector(instance);To inspect a property:
C++
propInspector.Describe(node, "Setup.Configure.CommunicationProtocol");Python
prop_inspector.describe(node, 'Setup.Configure.CommunicationProtocol')C#
propInspector.Describe(node, "Setup.Configure.CommunicationProtocol");To view what fields/values are available for openDAQ Enumeration and Struct types, create a DaqTypeInspector:
C++
daq_utils::DaqTypeInspector typeInspector(instance);Python
type_inspector = daq_utils.DaqTypeInspector(instance)C#
var typeInspector = new DaqTypeInspector(instance);To inspect a type:
C++
typeInspector.Describe("AutoCalCompletionFlag");Python
type_inspector.describe('AutoCalCompletionFlag')C#
typeInspector.Describe("AutoCalCompletionFlag");To create openDAQ typed values such as Enumerations and Structs, use DaqTypeFactory. It handles the type manager and string conversion automatically:
C++
daq_utils::DaqTypeFactory daqTypes(instance);Python
daq_types = daq_utils.DaqTypeFactory(instance)C#
var daqTypes = new DaqTypeFactory(instance);C++
daq::EnumerationPtr voltage = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_3000mV");Python
voltage = daq_types.enum("mscl_WirelessTypes_Voltage", "voltage_3000mV")C#
var voltage = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_3000mV");C++
daq::StructPtr cmdInfo = daqTypes.MakeStruct("mscl_ShuntCalCmdInfo",
{
{"UseInternalShunt", daq::Boolean(true)},
{"NumActiveGauges", daq::Integer(1)},
{"GaugeResistance", daq::Integer(350)},
{"ShuntResistance", daq::Integer(100000)},
{"GaugeFactor", daq::Float(2.0)},
{"InputRange", daqTypes.MakeEnum("mscl_WirelessTypes_InputRange", "range_14_545mV")},
{"HardwareOffset", daq::Integer(0)},
{"ExcitationVoltage", daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_1500mV")}
});Python
cmd_info = daq_types.struct(
"mscl_ShuntCalCmdInfo",
{
"UseInternalShunt": True,
"NumActiveGauges": 1,
"GaugeResistance": 350,
"ShuntResistance": 100000,
"GaugeFactor": 2.0,
"InputRange": daq_types.enum("mscl_WirelessTypes_InputRange", "range_14_545mV"),
"HardwareOffset": 0,
"ExcitationVoltage": daq_types.enum("mscl_WirelessTypes_Voltage", "voltage_1500mV")
}
)C#
var cmdInfo = daqTypes.MakeStruct("mscl_ShuntCalCmdInfo", new Dictionary<string, object>
{
["UseInternalShunt"] = true,
["NumActiveGauges"] = 1,
["GaugeResistance"] = 350,
["ShuntResistance"] = 100000,
["GaugeFactor"] = 2.0,
["InputRange"] = daqTypes.MakeEnum("mscl_WirelessTypes_InputRange", "range_14_545mV"),
["HardwareOffset"] = 0,
["ExcitationVoltage"] = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_1500mV")
});To read a field from a struct by name:
C++
daq::StructPtr struct_val = result.asPtr<daq::IStruct>();
daq::BaseObjectPtr field = struct_val.get("FieldName");Python
field = struct_val.FieldNameC#
var field = struct_val.Cast<Struct>()?.Get("FieldName");To iterate over all fields:
C++
daq::StructPtr struct_val = result.asPtr<daq::IStruct>();
for (const daq::StringPtr& name : struct_val.getFieldNames()) {
std::cout << name << ": " << struct_val.get(name) << "\n";
}Python
for name in struct_val.struct_type.field_names:
print(name, getattr(struct_val, name))C#
var struct_val = result.Cast<Struct>();
foreach (string name in struct_val.FieldNames)
Console.WriteLine($"{name}: {struct_val.Get(name)}");Note: To see what fields a struct type contains, see Inspecting types.
To access an item by index:
C++
daq::ListPtr<daq::IBaseObject> list = result.asPtr<daq::IList<daq::IBaseObject>>();
daq::BaseObjectPtr first = list[0];Python
first = result[0]C#
var list = result.Cast<IListObject<BaseObject>>();
var first = list[0];To iterate over all items:
C++
daq::ListPtr<daq::IBaseObject> list = result.asPtr<daq::IList<daq::IBaseObject>>();
for (const daq::BaseObjectPtr& item : list) {
std::cout << item << "\n";
}Python
for item in result:
print(item)C#
var list = result.Cast<IListObject<BaseObject>>();
foreach (var item in list)
Console.WriteLine(item);If a device isn't being detected, run this to check whether the module is loaded:
for (const daq::ModulePtr& module : instance.getModuleManager().getModules()) {
daq::ModuleInfoPtr info = module.getModuleInfo();
daq::VersionInfoPtr v = info.getVersionInfo();
std::cout << info.getName() << " (" << info.getId() << ") v" << v.getMajor() << "." << v.getMinor() << "." << v.getPatch() << "\n";
}for module in instance.module_manager.modules:
info = module.module_info
v = info.version_info
print(f"{info.name} ({info.id}) v{v.major}.{v.minor}.{v.patch}")foreach (var module in instance.ModuleManager.Modules)
{
var info = module.ModuleInfo;
var v = info.VersionInfo;
Console.WriteLine($"{info.Name} ({info.Id}) v{v.Major}.{v.Minor}.{v.Patch}");
}- Module appears — the module loaded, but something is preventing device detection
- Module missing — the module itself is not being loaded


