Products for USB Sensing and Control

Products for USB Sensing and Control

Addressing Many Phidgets

Getting your project running with many channels to deal with.


by James

Source Code

Download (Includes source code for C, Java and Python versions of the example covered in this article)

Introduction

In this article we'll discuss how to connect and manage many Phidget channels in your program.

Given that every channel of every Phidget is its own software object, it can be daunting to try to figure out how to organize them all. This guide should help you find ways to organize Phidgets within your own programs.

Hardware

For this setup, we used twenty 1142 light sensors. We connected some directly to a HUB0000_0 VINT Hub, and others through DAQ1000 8x Voltage Input Phidgets.

Software

Setup

With the specifics of our setup covered, now we can dive into effective techniques to organize these Phidgets in software. While the code we will be describing is written using the C language, most of the concepts covered are still applicable for use in Java and Python as well.

To start, we created a structure to keep track of the pertinent addressing information for each channel. This includes the device serial number, hub port and channel for each sensor, as well as whether or not the sensor should use the Voltage Input on the hub port itself.

            
typedef struct {
    int serialNumber;
    int hubPort;
    int channel;
    int isHubPort;
} Address;
            
            

We then made an array of such entries, which serves as a reference to keep track of all of our channels in one place. Knowing this array, we can use the index of the channels within this array to serve as a unique identifier to keep track of each sensor more easily. Alternatively, you could add your own unique identifier to the parent structure and use that to identify your Phidgets.

            
Address addressMap[NUM_CHANNELS] = {
    { -1, 0, 0, 0 },  //// 
    { -1, 0, 1, 0 },    //
    { -1, 0, 2, 0 },    // These eight are the ones connected
    { -1, 0, 3, 0 },    // to the DAQ1000 on Hub port 0
    { -1, 0, 4, 0 },    //
    { -1, 0, 5, 0 },    //
    { -1, 0, 6, 0 },    //
    { -1, 0, 7, 0 },  ////
    { -1, 1, 0, 0 },        ////
    { -1, 1, 1, 0 },          //
    { -1, 1, 2, 0 },          //
    { -1, 1, 3, 0 },          // These eight are connected to the
    { -1, 1, 4, 0 },          // DAQ1000 on Hub port 1
    { -1, 1, 5, 0 },          //
    { -1, 1, 6, 0 },          //
    { -1, 1, 7, 0 },        ////
    { -1, 2, 0, 1 },  ////
    { -1, 3, 0, 1 },    // These four are directly connected to
    { -1, 4, 0, 1 },    // hub ports 2, 3, 4, and 5
    { -1, 5, 0, 1 }   ////
};
            
            

For each of these entries, the serial number is "-1" which will match any serial number. If we wanted to map these to specific hubs, we would use the actual serial number here.

Similarly, we created an array of structures to keep the important information from all the channels in one place.

            
typedef struct {
    int index;
    double value;
    int isReady;
} ChannelInfo;

ChannelInfo channelInfoList[NUM_CHANNELS] = {0};
            
            

Now, in the main block of your program, you can create an array of Phidget handles to contain all your Phidgets of the same type. You may then address and open them sequentially in a loop.

            
void addressChannel(PhidgetHandle ch, int channelIndex);

int
main(int argc, char **argv) {
    //Declare the array of PhidgetVoltageInputHandles
    PhidgetVoltageInputHandle ch[NUM_CHANNELS];
    PhidgetReturnCode res;
    const char *errs;

    //Open address and open each device
    for (int i = 0; i < NUM_CHANNELS; i++) {
        res = PhidgetVoltageInput_create(&ch[i]);
        if (res != EPHIDGET_OK) {
            fprintf(stderr, "failed to create voltage input channel\n");
            exit(1);
        }

        //Specify channel addressing info
        addressChannel((PhidgetHandle)ch[i], i);

        //Initialize channel info
        channelInfoList[i].index = i;
        channelInfoList[i].isReady = 0;
        channelInfoList[i].value = PUNK_DBL;

        /*
        *  ... Event Handler Code Here ...
        */

        /*
        * Open the channel synchronously: waiting a maximum of 5 seconds.
        */
        res = Phidget_openWaitForAttachment((PhidgetHandle)ch[i], 5000);
        if (res != EPHIDGET_OK) {
            if (res == EPHIDGET_TIMEOUT) {
                printf("Channel did not attach after 5 seconds: ");
                printf("please check that the device is attached\n");
            } else {
                Phidget_getErrorDescription(res, &errs);
                fprintf(stderr, "failed to open channel:%s\n", errs);
            }
            goto done;
        }
    }

    // ... More Code Here ...
}

void addressChannel(PhidgetHandle ch, int channelIndex) {
    //Set the properties used to address the channel from the values in the 
    //addressMap table
    Phidget_setDeviceSerialNumber(ch, addressMap[channelIndex].serialNumber);
    Phidget_setHubPort(ch, addressMap[channelIndex].hubPort);
    Phidget_setChannel(ch, addressMap[channelIndex].channel);
    Phidget_setIsHubPortDevice(ch, addressMap[channelIndex].isHubPort);
}
            
            

We set up all the classes with the same event handlers for simplicity. In this case we also used the context (ctx) pointer to pass our supplementary channel info to the event to keep that information linked to the channel when it fires events.

            
res = PhidgetVoltageInput_setOnSensorChangeHandler(ch[i], onSensorChangeHandler,
                                                    &channelInfoList[i]);
if (res != EPHIDGET_OK) {
    Phidget_getErrorDescription(res, &errs);
    fprintf(stderr, "failed to set sensor change handler: %s\n", errs);
    goto done;
}

res = Phidget_setOnAttachHandler(ch[i], onAttachHandler, &channelInfoList[i]);
if (res != EPHIDGET_OK) {
    fprintf(stderr, "failed to assign on attach handler\n");
    return (res);
}

res = Phidget_setOnDetachHandler(ch[i], onDetachHandler, &channelInfoList[i]);
if (res != EPHIDGET_OK) {
    fprintf(stderr, "failed to assign on detach handler\n");
    return (res);
}

res = Phidget_setOnErrorHandler(ch[i], errorHandler, &channelInfoList[i]);
if (res != EPHIDGET_OK) {
    fprintf(stderr, "failed to assign on error handler\n");
    return (res);
}
            
            

We used the attach handler to initialize each Voltage Input channel to use the 1142 sensor type to match the sensor attached to each input.

At the top of the handler, you can see how we extract the supplementary channel information out of the context pointer we gave each event handler. Here we're using it to get our unique index for the channel that fired the event.

            
static void CCONV
onAttachHandler(PhidgetHandle ch, void *ctx) {
    ChannelInfo *channelInfo = (ChannelInfo*)ctx;
	int index = channelInfo->index;
    PhidgetReturnCode res;
    
    printf("Sensor [%d] attached\n", index);
    
    res = PhidgetVoltageInput_setSensorType(ch, SENSOR_TYPE_1142);
    if (res != EPHIDGET_OK) {
        fprintf(stderr, "failed to set sensor type\n");
        return;
    }
}
            
            

Next, we used the sensor change and error handlers together to track the values coming in from all the sensors.

            
static void CCONV
errorHandler(PhidgetHandle ch, void *ctx, Phidget_ErrorEventCode errorCode, const char *errorString) {
    ChannelInfo *channelInfo = (ChannelInfo*)ctx;
    int index = channelInfo->index;
    
    //indicate the latest measurement is not in range
    if (errorCode == EEPHIDGET_SATURATION ||
      errorCode == EEPHIDGET_OUTOFRANGE) {
        channelInfo->value = PUNK_DBL;
        channelInfo->isReady = 1;

        tryToPrint();
    }
    else {
        fprintf(stderr, "Error: %s (%d)\n", errorString, errorCode);
    }
}

static void CCONV
onSensorChangeHandler(PhidgetVoltageInputHandle ch, void *ctx, double sensorValue) {
    ChannelInfo *channelInfo = (ChannelInfo*)ctx;
    int index = channelInfo->index;

    channelInfo->value = sensorValue;
    channelInfo->isReady = 1;

    tryToPrint();

    printf("\n\n");
}
            
            

Once data has been collected on all channels, the new values are printed onscreen.

            
void tryToPrint() {
    //Check if all channels have gathered new data yet
    for (int i = 0; i < NUM_CHANNELS; i++)
        if (!channelInfoList[i].isReady)
            return;

    //If all channels have new data, print the values onscreen
    printf("Values:");
    for (int i = 0; i < NUM_CHANNELS; i++) {
        channelInfoList[i].isReady = 0;
        if (i % 8 == 0)
            printf("\n");
        if (channelInfoList[i].value != PUNK_DBL)
            printf("%7.2f |", channelInfoList[i].value);
        else
            printf("  SAT   |");
    }
}
            
            

Which results in a final output like the following:

Conclusion

The above is intended as a starting point to getting a large number of Phidgets working in your project.

If you have any questions, let us know!