Qmix SDK  20180626
The software libraries for integration of all CETONI devices.
Controller Library


This controller library defines a common interface for control device access and configuration.


The way how you include the library functions in your own windows program, depends on the compiler and on the programming language you use. In order to access the controller API, you have to include the library labbCAN_Controller_API.dll to your programming environment. You have to copy this file to the working directory of your system or to the application directory that contains your application EXE file. Use the calling convention LCC_CALL for this library. This convention is managing how the parameters are put on the stack and who is responsible to clean the stack after the function execution.

The interface of the library, constant definitions and declarations of the library functions, is defined in the header file interface/labbCAN_Controller_API.h

Controller API Usage

Retrieve device handle

To work with a controller channel, you need a valid channel handle. A channel handle is simply an opaque pointer to the internal channel object created by the labbCAN environment. So the first thing you need do to is obtaining a valid control channel handle. There are two ways to get a valid channel handle. The first method is to get a channel object via its channel name.

To do this, use the function LCC_LookupChanByName().

dev_hdl ControlChannelHandle;
Result = LCC_LookupChanByName("QmixTC_1_Ctrl1", &ControlChannelHandle);

You can get the channel names from the device configuration files (see Device Configuration Files). To search for the control channel names, open the file qmixcontroller.xml and search in the channel list for Lcl::CLocalControllerChannel items. The following example shows the device configuration of one single Qmix TC control channel. In the first line you will find the channel name - QmixTC_1_Ctrl1 in this case.

1 <Channel Name="QmixTC_1_Ctrl1">
2  <Type>Lcl::CQmixChipF40Controller</Type>
3  <PwmOutputEnable>QmixTC_1_DO0_INA</PwmOutputEnable>
4  <ControlLoop>
5  <Type>Lcl::CPIDControlLoop</Type>
6  <Enabled>false</Enabled>
7  <PIDParam Name="Qmix TC 1 Default" K="3" Td="0" Ti="260" Tt="250" DerivativeGainLimit="20" MinU="0" MaxU="100" DisabledU="0" SampleTime_ms="500" Setpoint="0"/>
8  <Input>
9  <Type>Lcl::CAnalogChannelLoopIn</Type>
10  <Channel>QmixTC_1_AI0_PT100</Channel>
11  </Input>
12  <Output>
13  <Type>Lcl::CAnalogChannelLoopOut</Type>
14  <Channel>QmixTC_1_AO0_PWM</Channel>
15  </Output>
16  </ControlLoop>
17 </Channel>

The second method is, to get the channel name by its index. All control channels are number from 0 to n-1 channels. To get a channel by its index, use the function LCC_GetChannelHandle().

dev_hdl ControlChannelHandle;
Result = LCC_GetChannelHandle(0, &ControlChannelHandle);

If you want to know, how many channels are available, simply call the function LCC_GetNoOfControlChannels(). The following example shows how to use both functions to enumerate all control channels and output their names.

1 long ChannelCount = LCC_GetNoOfControlChannels();
3 for (long i = 0; i < ChannelCount; ++i)
4 {
5  dev_hdl Channel;
6  Result = LCC_GetChannelHandle(i, &Channel);
8  char ChannelName[128];
9  Result = LCC_GetChanName(Channel, ChannelName, sizeof(ChannelName));
11  std::cout << "Channel[" << i << "]: " << ChannelName << std::endl;
12 }
long long dev_hdl
generic device handle
Definition: labbCAN_Bus_API.h:359
LCC_Func long LCC_CALL LCC_GetNoOfControlChannels()
Query number of available controller channels.
LCC_Func long LCC_CALL LCC_GetChanName(dev_hdl hChan, char *pNameStringBuf, int StringBufSize)
Query name of specific channel.
LCC_Func long LCC_CALL LCC_GetChannelHandle(unsigned char Index, dev_hdl *pChanHdl)
Get controller channel handle by its index.

Using Controller Channels

To use the control channel, you simply need to write a setpoint and enable the channel control loop.

Result = LCC_WriteSetPoint(ChannelHandle, 37);
Result = LCC_EnableControlLoop(ChannelHandle, 1);

Now you can use the function LCC_ReadActualValue() to read the actual control channel value. The following example shows the minimum number of steps to get a control channel running.

1 int main(void)
2 {
3  dev_hdl ChannelHandle;
5  // Open the labbCAN bus, get a control channel handle and start the
6  // devic communication
7  const char* ConfigPath = "config/testconfig_qmixsdk";
8  Result = LCB_Open(ConfigPath, 0);
9  Result = LCC_LookupChanByName("QmixQplus_1_ReactorZone", &ChannelHandle);
10  Result = LCB_Start();
12  // Write the setpoint and enable the control loop
13  Result = LCC_WriteSetPoint(ChannelHandle, 37);
14  Result = LCC_EnableControlLoop(ChannelHandle, 1);
16  // now the control loop is running and we can read the actual value
17  double ActualValue;
18  Result = LCC_ReadActualValue(ChannelHandle, &ActualValue);
20  // We are finished and can stop communication and close the bus
21  Result = LCB_Stop();
22  std::this_thread::sleep_for(2s);
23  Result = LCB_Close();
24 }
LCC_Func long LCC_CALL LCC_WriteSetPoint(dev_hdl ChanHdl, double fSetPointValue)
Write setpoint value to controller device.
LCB_Func long LCB_CALL LCB_Open(const char *pDeviceConfigPath, const char *PluginSearchPath)
Initialize LabCanBus instance.
LCC_Func long LCC_CALL LCC_LookupChanByName(const char *pChannelName, dev_hdl *pChanHdl)
Lookup for a controller channel by its name.
long long dev_hdl
generic device handle
Definition: labbCAN_Bus_API.h:359
LCB_Func long LCB_CALL LCB_Stop()
Stop network communication.
LCB_Func long LCB_CALL LCB_Start()
Start network communication.
LCC_Func long LCC_CALL LCC_ReadActualValue(dev_hdl ChanHdl, double *pfActualValue)
Read actual value from device.
LCC_Func long LCC_CALL LCC_EnableControlLoop(dev_hdl ChanHdl, int Enable)
Enables / disables a control loop.
LCB_Func long LCB_CALL LCB_Close()
Close LabCanBus instance.


See the labbCAN Controller API module for a detailed reference of all controller control, status and configuration functions.

Dynamically created control channels

The controller library supports the creation of dynamic control channels. A dynamic control channel is a control channel, where the control loop input, control loop output and the controller PID parameters are provided in source code. This enables the dynamic creation of special control loops and thus enables things like pressure controlled dosage with syringe pumps. Dynamic control channels are created with the function LCC_CreatePIDControlChannel(). The following code shows how to create a pressure control loop with an analog input channel that measures the pressure and a syringe pump that generates the flow and the pressure.

dev_hdl ControlLoopIn;
dev_hdl Pump;
dev_hdl ControlChannel;
LCAIO_GetInChanHandle(0, &ControlLoopIn);
LCC_CreatePIDControlChannel(ControlLoopIn, Pump, PUMP_FLOW, &ControlChannel);

To build the control loop you need the device handles for the control loop input and the control loop output. Then you can call the function LCC_CreatePIDControlChannel() to wire input and output together to build up a control loop.

Programming Interface - API

See the labbCAN Controller API module for a detailed reference of the Controller I/O library application programming interface