Electronics and Software
Engineering Innovation
BMeasure-lib 1.1.0
BMeasure-lib
Author
Dr Terry Barnaby
Version
1.0.0
Date
2020-02-09

Introduction

The Beam BMeasure-125i unit is a flexible and powerful IoT system for data capture, data logging and control in the laboratory, industrial and remote sensing arenas. It is based around an 8 channel, fully differential, synchronous sampling, 24 bit ADC that can sample at speeds up to 128 ksps. Multiple units can be connected together to provide more synchronously sampled channels.

This reference information describes the data types and functions provided by the host API library allowing programs to be written to control the operation of a BMeasure unit and acquire the data from it. The API operates over a number of different physical interfaces including: USB 2.0, Ethernet, Wifi and RS485.

In addition there is a software manual providing an overview of using this API which should be read first. This document is available at:: https://portal.beam.ltd.uk/files/products/bmeasure-125i/doc/BMeasure-lib.pdf

Overview

The BMeasure API library, bmeasure-lib, is implemented in the C++ computer language. It has bindings layered on top of this for Python, with Matlab due to be supported soon. The API has an object orientated architecture. It has been designed as a general purpose API library for the Beam BMeasure-125i and future BMeasure products. Currently it has ports to Linux (Redhat7, Fedora29, Debian) and Microsoft Windows 7, 8 and 10.

The API provides the following functionality:

  • Find BMeasure units on the USB bus or local Ethernet and Wifi networks.
  • Connect to one or more BMeasure units.
  • Fetch information and configure the BMeasure units.
  • Start the BMeasure unit capturing and processing the sensor inputs.
  • Capture the data from all of the analogue and digital channels from one or a combined set of BMeasure units running in sync.
  • Access the data log files on the unit and download them to the host.
  • Configure the AWG to produce waveforms or set voltages on the analogue output channels.
  • Operate relays, read switches and other auxiliary operations.

The BMeasure API is implemented using the Beam BOAP (Beam Object Access protocol) communications system. It offers an BMeasureUnit API class to access an individual BMeasure unit in a relatively low level manner and an BMeasureUnits API class to access a set of BMeasure units synchronised together to operate as a single unit and with a queued data reception system..

The API supports threaded and non-threaded operation.

The referenve information provided describes the API from a C++ programming perspective. The Python and other language bindings are very similar the differences being noted under the particular language bindings section in the software manual..

API Usage

To use the API the core procedure is:

  1. Either find the available BMeasure units using: BMeasureApi::BMeasureUnit::findDevices() or use a BMeasure URL string..
  2. Choose to use the simple single unit interface BMeasureApi::BMeasureUnit or the BMeasureApi::BMeasureUnits classes.
  3. If using the simple single unit interface, connect to the unit using the BMeasureApi::BMeasureUnit::connect() function.
  4. If using the multiple unit interface, add the units using the BMeasureApi::BMeasureUnits::unitAdd() function and connect using the BMeasureApi::BMeasureUnits::unitsConnect() function.
  5. Use the interface to communicate to the unit.

See the examples below and the software manual for more details.

API Usage

There are some examples of client applications using the BMeasure API in the examples directory of the source code. Some simple client examples are listed below:

Simple example to access and read single sets of data samples in C++

/*******************************************************************************
* Example005-dataClient-single.cpp
* T.Barnaby, BEAM Ltd, 2019-10-09
*******************************************************************************
*/
#include <BMeasureUnit.h>
#include <unistd.h>
using namespace BMeasureApi;
// Function to read some data
BError test1(){
BError err;
BString device;
BMeasureUnit bmeasure;
Configuration config;
BUInt c;
printf("Start Processing Task\n");
bmeasure.start();
printf("Find BMeasure units\n");
if(err = BMeasureUnit::findDevicesUsb(devices)){
return err;
}
if(devices.number() == 0){
return err.set(1, "No USB BMeasure units found");
}
device = devices[0].device;
printf("Connect\n");
if(err = bmeasure.connect(device))
return err;
//printf("Exit\n"); return err;
printf("Get Info\n");
if(err = bmeasure.getInformation(info))
return err;
printf("NumChannels: %d\n", info.numChannels);
//printf("Exit\n"); return err;
printf("Configure measurement\n");
mc.triggerLevel = 0;
mc.triggerDelay = 0;
mc.sampleRate = 8000.0;
mc.measurePeriod = 0;
mc.numSamples0 = 1;
mc.numSamples1 = 0;
if(err = bmeasure.setMeasurementConfig(0, mc))
return err;
printf("Run single measurement\n");
if(err = bmeasure.measure(DataTypeFloat32, data))
return err;
printf("DataBlock: from: %d numChannels: %d numSamples: %d\n", data.source, data.numChannels, data.numSamples);
for(c = 0; c < data.numChannels; c++){
printf("%f ", data.data[c]);
}
printf("\n");
return err;
}
int main(){
BError err;
if(err = test1()){
printf("Error: %d %s\n", err.getErrorNo(), err.str());
return 1;
}
printf("Complete\n");
return 0;
}
BUInt32 BUInt
char data[8]
int getErrorNo() const
const char * str() const
BError & set(int errNo, BString errStr="")
unsigned int number() const
Definition: BMeasureUnit.h:31
virtual BError setMeasurementConfig(const Bool &save, const MeasurementConfig &configMeasurement)
Definition: BMeasureUnit.cpp:301
BError connect(BString device)
Connect to a device.
Definition: BMeasureUnit.cpp:77
BError measure(const DataType &dataType, DataBlock &dataBlock)
Performs a single sample measurement.
Definition: BMeasureB.cpp:595
BError getInformation(Information &info)
Definition: BMeasureB.cpp:269
Definition: BMeasureD.h:314
Data Block. Data in floating point format for all channels.
Definition: BMeasureD.h:393
Definition: BMeasureD.h:273
BUInt8 numChannels
The number of channels.
Definition: BMeasureD.h:279
Measurement config Data Block. Data packed into bytestream based on sampleTypes.
Definition: BMeasureD.h:368
MeasureMode measureMode
Definition: BMeasureD.h:372
TriggerConfig triggerConfig
Trigger config including direction, filters etc.
Definition: BMeasureD.h:377
BUInt32 numSamples0
The number of samples in a chunk for display and/or repeat.
Definition: BMeasureD.h:383
BInt32 triggerDelay
Trigger delay in samples.
Definition: BMeasureD.h:381
TriggerMode triggerMode
Definition: BMeasureD.h:376
BFloat64 sampleRate
Definition: BMeasureD.h:382
BUInt8 triggerChannel
Definition: BMeasureD.h:378
BUInt32 numSamples1
The number of samples per each data processing cycle. 0 disables this processing.
Definition: BMeasureD.h:384
BFloat32 triggerLevel
Definition: BMeasureD.h:380
BUInt32 measurePeriod
Time in seconds between measurement sample bursts. 0 is continuous.
Definition: BMeasureD.h:386
BError start()
Definition: BMeasureB.cpp:9
@ MeasureModeOneShot
Definition: BMeasureD.h:63
@ TriggerModeOff
Definition: BMeasureD.h:73
@ TriggerConfigNone
Definition: BMeasureD.h:78

Simple example to access and read single sets of data samples in Python

#!/usr/bin/python3
import sys
import time
import getopt
from threading import Thread
from bmeasure import *
# Function to read some data
def test1():
bmeasure = BMeasureUnit(True);
print("Find BMeasure units");
(err, devices) = BMeasureUnit.findDevicesUsb();
if(err):
return err;
if(devices.number() == 0):
return err.set(1, "No USB BMeasure units found\n");
print("Found", len(devices));
device = devices[0].device;
print("Start Processing Task");
bmeasure.start();
print("Connect to BMeasure");
err = bmeasure.connect(device);
if(err):
return err;
print("Get Info");
(err, info) = bmeasure.getInformation();
if(err):
return err;
print("NumChannels: ", info.numChannels);
print("Configure measurement");
mc = MeasurementConfig();
mc.measureMode = MeasureModeOneShot;
mc.triggerMode = TriggerModeOff;
mc.triggerConfig = TriggerConfigNone;
mc.triggerChannel = 0;
mc.triggerLevel = 0;
mc.triggerDelay = 0;
mc.sampleRate = 4000;
mc.numSamples0 = 1;
mc.numSamples1 = 0;
mc.measurePeriod = 0;
err = bmeasure.setMeasurementConfig(False, mc);
if(err):
return err;
print("Run single measurement");
(err, data) = bmeasure.measure();
if(err):
return err;
print("DataBlock: from: %d numChannels: %d numSamples: %d" % (data.source, data.numChannels, data.numSamples));
for c in range(0, data.numChannels):
print("Chan:", c, data.data[c]);
return err;
def main():
err = test1();
if(err):
print("Error:", err.getErrorNo(), err.getString());
return 1;
print("Complete");
return 0;
if __name__ == "__main__":
main();

Simple example to show operating the relays in Python

#!/usr/bin/python3
import sys
import time
import getopt
from threading import Thread
from bmeasure import *
# Function to set the relays on/off
def test1():
bmeasure = BMeasureUnit(True);
print("Find BMeasure units");
(err, devices) = BMeasureUnit.findDevicesUsb();
if(err):
return err;
if(devices.number() == 0):
return err.set(1, "No USB BMeasure units found\n");
print("Found", len(devices));
device = devices[0].device;
print("Start Communications Task");
bmeasure.start();
print("Connect");
err = bmeasure.connect(device);
if(err):
return err;
print("Get Info");
(err, info) = bmeasure.getInformation();
if(err):
return err;
print("NumChannels: ", info.numChannels);
# Toggle relay1
state = 0;
for i in range(0, 6):
if(state):
state = False;
else:
state = True;
print("Set relay 0: %d" % (state));
err = bmeasure.setRelay(0, state);
if(err):
return err;
time.sleep(1);
return err;
def main():
if(0):
err = find();
if(err):
print("Error:", err.getErrorNo(), err.getString());
return 1;
err = test1();
if(err):
print("Error:", err.getErrorNo(), err.getString());
return 1;
print("Complete");
return 0;
if __name__ == "__main__":
main();