Products for USB Sensing and Control
Products for USB Sensing and Control

Logging with Multiple Phidgets


by Brian

Two of the most common things I get asked about through our tech support channels are how to use multiple devices at once, and how to log data to a file that is readable by a spreadsheet program like Excel. In an effort to kill two birds with one stone, let's walk through an example where we do both:

Setting Up Multiple Devices


For this project, we'll be using the following Phidgets:

In a typical program running Phidgets code the first thing you see will be a declaration of the object. For example, in C:

                
PhidgetTemperatureSensorHandle thermocouple = NULL;
PhidgetTemperatureSensor_create(&thermocouple);
                
                

After that, we register the event handlers and set channel matching parameters. Event handlers are separate functions that will trigger whenever something happens with the Phidget (e.g. successful attach, or incoming data). Channel matching parameters can be used to narrow down which channel is being opened by specifying things like serial number and hub port. After all of that, we can call 'open' on the device:

                
Phidget_setOnAttachHandler((PhidgetHandle)thermocouple, onAttachHandler, NULL);
Phidget_setOnDetachHandler((PhidgetHandle)thermocouple, onDetachHandler, NULL);
Phidget_setOnErrorHandler((PhidgetHandle)thermocouple, onErrorHandler, NULL);
PhidgetTemperatureSensor_setOnTemperatureChangeHandler(thermocouple, onTemperatureChangeHandler, NULL);

Phidget_setDeviceSerialNumber((PhidgetHandle)thermocouple, 371089);
Phidget_setHubPort((PhidgetHandle)thermocouple, 0);
Phidget_openWaitForAttachment((PhidgetHandle)thermocouple, 5000);
                
                

When working with a second or third device all you need to do is repeat all this for each device you want to use. It's as simple as that. For example, here is how I would declare and open 3 devices (2 VINT temperature sensors and an analog light sensor):

                
PhidgetTemperatureSensorHandle thermocouple = NULL; //this will be our thermocouple
PhidgetTemperatureSensorHandle ambient = NULL; //this will be our ambient sensor
PhidgetVoltageInputHandle analogLight = NULL; //this will be the 1143

PhidgetTemperatureSensor_create(&thermocouple);
PhidgetTemperatureSensor_create(&ambient);
PhidgetVoltageInput_create(&analogLight);

Phidget_setOnAttachHandler((PhidgetHandle)thermocouple, onTemperatureAttachHandler, NULL);
Phidget_setOnDetachHandler((PhidgetHandle)thermocouple, onDetachHandler, NULL);
Phidget_setOnErrorHandler((PhidgetHandle)thermocouple, onErrorHandler, NULL);
PhidgetTemperatureSensor_setOnTemperatureChangeHandler(thermocouple, onTemperatureChangeHandler, NULL);

Phidget_setOnAttachHandler((PhidgetHandle)ambient, onTemperatureAttachHandler, NULL);
Phidget_setOnDetachHandler((PhidgetHandle)ambient, onDetachHandler, NULL);
Phidget_setOnErrorHandler((PhidgetHandle)ambient, onErrorHandler, NULL);
PhidgetTemperatureSensor_setOnTemperatureChangeHandler(ambient, onTemperatureChangeHandler, NULL);

Phidget_setOnAttachHandler((PhidgetHandle)analogLight, onVoltageAttachHandler, NULL);
Phidget_setOnDetachHandler((PhidgetHandle)analogLight, onDetachHandler, NULL);
Phidget_setOnErrorHandler((PhidgetHandle)analogLight, onErrorHandler, NULL);
PhidgetVoltageInput_setOnVoltageChangeHandler(analogLight, onVoltageChangeHandler, NULL);

Phidget_setDeviceSerialNumber((PhidgetHandle)thermocouple, 371089);
Phidget_setDeviceSerialNumber((PhidgetHandle)ambient, 371089);
Phidget_setDeviceSerialNumber((PhidgetHandle)analogLight, 371089);

Phidget_setHubPort((PhidgetHandle)thermocouple, 0);
Phidget_setHubPort((PhidgetHandle)ambient, 1);
Phidget_setHubPort((PhidgetHandle)analogLight, 2);

Phidget_setIsHubPortDevice((PhidgetHandle)analogLight, 1);

Phidget_openWaitForAttachment((PhidgetHandle)thermocouple, 5000);
Phidget_openWaitForAttachment((PhidgetHandle)ambient, 5000);
Phidget_openWaitForAttachment((PhidgetHandle)analogLight, 5000);
                
                

Notice here how I have registered the same event handler for multiple devices. For example, the callback function "onTemperatureAttachHandler" is used to handle the attach event for both of the temperature devices. We could have used a separate callback function for each if we wanted to, but since they both use the TemperatureSensor object, it is easy to write one function that will handle both.

This means we need to figure out which device fired the instance of the event we are currently handling. There are a number of ways to do this, but since we have one object per VINT port, we can just check the port number. Since I am not using multiple channels per port the port number is sufficient to uniquely identify what device we are dealing with. In C, this is done by checking the HubPort property of the handle of the event. Other languages will handle this differently- click here for more information.

                
static void CCONV onTemperatureAttachHandler(PhidgetHandle ph, void *ctx) {
    int port;
    Phidget_getHubPort(ph, &port);
    switch (port)
    {
        //do some things based on the device we have opened
    }
}
                
                

That's all there is to it (in terms of Phidget-specific code): Declare the objects, set parameters, open the channels and sort out the data once the events start firing.

Logging to a File

Logging information to a file is a general task that is handled natively by the programming language you use. There are libraries available for generating actual .xls files or other similar binary file types in many languages but that requires external includes and won't necessarily be available for every language. The most basic case would be a simple .csv (comma separated values) file which is just a normal text file with some specific formatting. For this, all we need to do is write our data as strings to the file and make sure that our "cells" are separated by a comma or new line character. To write to a file, we first have to declare a file handle and then use it to open a file.

                
FILE *file = fopen("phidgetLog.csv", "w");
                
                

If the filename we open doesn't currently exist in the directory then it will be created, otherwise the existing file will be opened. We are opening it with mode "w" for write since we want to write data to the file and not read from it. The details of the fopen function in C can be found here. If you are working with another language then you'll want to search for "file IO" to get a similar function. This is a very common thing to do so there should be no shortage of information available online.

From here we just need to write the actual data. The easiest way to do this is to pass our newly created file into the event handler and write it directly in the callback function.

                
int thermocoupleSample = 0;
				
static void CCONV onTemperatureChangeHandler(PhidgetTemperatureSensorHandle ph, void *ctx, double temperature) {
	FILE *file = (FILE *)ctx;
	fprintf(file, "Thermocouple,%f,%d\n", temperature, thermocoupleSample++);
}

int main() {
    .
    .
	.
    FILE *file = fopen("phidgetLog.csv", "w");
    PhidgetTemperatureSensor_setOnTemperatureChangeHandler(thermocouple, onTemperatureChangeHandler, (void *)file);
    .
    .
    .
}
                
                

Timestamp

In the code snippet above, we're only printing the data and an index number that counts up. Most of the time, you'll want to print some kind of timestamp so that this data can be plotted with time on the X axis. In the full examples provided at the bottom of this page, you'll find that we simply use the system's built-in time function to get the timestamp. One thing to keep in mind is that the timestamp is the time when the event fired, not when the data was actually gathered. As a result, the timestamp may not be precise, especially if you're using a high data rate and have events from multiple channels firing in quick succession. The good news is that this error is usually on the order of milliseconds, and it doesn't accumulate. For some Phidgets that require high time precision, there is an accurate timestamp passed from the device to the data event. Phidget accelerometers, spatials, GPS, and encoders all have some form of time tracking.

Full Examples

Here are the complete examples in both C and Python that combine all of what has been shown. The result is a program that opens 2 temperature devices and 1 voltage input and logs data from all 3 to a .csv file. Once the data is in the spreadsheet, you can manipulate it and create plots.

Download Source Code