Notice: This page contains information for the legacy Phidget21 Library. Phidget21 is out of support. Bugfixes may be considered on a case by case basis. Phidget21 does not support VINT Phidgets, or new USB Phidgets released after 2020. We maintain a selection of legacy devices for sale that are supported in Phidget21. We recommend that new projects be developed against the Phidget22 Library.
|
General Phidget Programming
This page presents the general concepts needed to write code for a Phidget.
By this point, you should have installed the drivers for your operating system and the libraries for your specific programming language.
The Basic Functions
To use your Phidget within code, you'll want to:
- Create a Phidget software
object
, which gives you access to the functions specific to that device - Open the Phidget using the
object
- Detect when a Phidget is attached (plugged in) by using the
object
- Use functions that the
object
provides, like turning on LEDs, reading sensors, triggering events on data change, etc - Close the
object
, when you are done
Small code snippets are provided for each step below. C++ and Java were selected because Java is a relatively high-level language and C++ is a relatively low level language, thereby showing how specific each language API really is. So, the most useful resource for the actual functions would be the API for your specific language. This page is a high-level introduction, by design.
Creating a Software Object
Phidget devices are controlled using software objects. All software device objects have a common API and set of functions that allow you to open it, close it, and set a few listeners to general events such as attach (plug in), detach (unplug), and errors.
But when you create an actual software object, it is a software object specific to your device.
For example, in Java:
// Create a new Accelerometer object
AccelerometerPhidget device = new AccelerometerPhidget();
// Create a new RFID device object
RFIDPhidget device = new RFIDPhidget();
Or in C:
// Create a new Accelerometer object
CPhidgetAccelerometerHandle device = 0;
CPhidgetAccelerometer_create(&device);
// Create a new RFID device object
CPhidgetRFIDHandle device = 0;
CPhidgetRFID_create(&device);
Each software object has an API and available functions which are specific to that device. For example, the RFID device API includes a function to turn on the RFID antenna. The accelerometer device API includes a function to set the sensitivity on each axis. Other APIs control all of our other Phidgets (i.e. those boards with a USB port), with applicable functions.
Opening the Phidget
Phidgets can either be opened when attached directly to a computer, or they can be opened remotely using the Phidget WebService. This section deals primarily with opening Phidgets directly.
Once you have created your software object for your specific type of device, you can call the open()
function in your language on that object. For example, in Java:
device.open();
Or in C:
CPhidget_open((CPhidgetHandle) device, -1);
All specific language calls can be found in the API documentation located on each individual language page.
The open()
function in any language opens the software object for use, not the hardware itself. Having the software "open" before the hardware means that the software can capture all events, including multiple attach (plug in) and detach (unplug) events for one open()
call.
Details for Open()
Open will return immediately once called, because it can be called even if the Phidget to be used is not attached to the system. This is known as an asynchronous call. It’s important to understand that most calls on a Phidget will fail if they are calls when the Phidget is not attached - in fact the only calls that are allowed on a detached Phidget are close()
, waitForAttachment()
and getAttached()
.
Open is also pervasive. This means that once open has been called, it will constantly try to stay attached to a Phidget. Even if the Phidget is unplugged from the computer and then plugged back in, you will simply get a Detach event, and then an Attach event. It’s a good idea to handle the Detach event in order to avoid calling the Phidget after it has detached.
The different types of open(), such as openAny(), openRemote(), etc. can be used with parameters to try and get the first device it can find, open based on its serial number, or even open across the network. The list all of the available modes that open provides, and the syntax for your language, can be found in the API for your specific language. If there are more than one of the same type of Phidget attached to a computer, and you use open() with no serial number, there is no way of knowing which Phidget will be opened first.
If you are looking to do a remote open call, to use the Phidget WebService, you usually have to change only the open() call (to a remote open call) to change your program from a locally-running one to one that can control a Phidget over the network. We give an in-depth example of using the WebService on each of our operating system pages, we have a brief overview of the WebService (with code snippets) in the Using Phidgets over a Network section, and we often have WebService code snippets on the language pages which do not easily extend from the examples on this page.
Note: Once a Phidget is opened by an application, it cannot be opened again in another application until closed by the first. When open and attached in software, no other programs or instances can read data from or change the Phidget. This includes it being open via the Windows Control Panel application! The one exception is if the Phidget is controlled only over the network with the Phidget WebService, and not directly. Then, you can use multiple remote control programs.
Attaching the Phidget
Physically, attaching a Phidget means plugging it in. The real guts behind the 'attach' command, however, occur within the software libraries. The 'attach' call is what makes the final connections between the opened software object and the corresponding thread and events. This is why all Phidget object must be attached in software, even those that are not actually plugged in with a cable. This includes Phidgets used remotely via our Phidget WebService, it includes Interface Kits on the same board as our Single Board Computer, and it even includes the Phidget Manager software object, which is a sort of meta-Phidget from which you can control other Phidgets.
In your code, you can detect an attachment either with an event in event-driven programming, or waiting for it, in logic programming.
Event Attachment
For example, to use an event to detect attachment in Java:
// After creating a Phidget object called "device":
device.addAttachListener(new AttachListener() {
public void attached(AttachEvent ae) {
System.out.println("A new device has been plugged in!");
// Do things after attachment (i.e. read data, control the device)
}
});
Or to use an event to detect attachment in C:
int AttachHandler (CPhidgetHandle device, void *userData) {
printf("A new device has been plugged in!");
// Do things after attachment (i.e. read data, control the device)
return 0;
}
// .....Then, in the main code after creating a Phidget object "device":
CPhidget_set_OnAttach_Handler((CPhidgetHandle) device, AttachHandler, NULL);
Both of the code snippets above do the same thing. The function AttachHandler(...)
is called automatically when a device is plugged in.
You will want to attach events (via addAttachListener()
above, for example) before you open the Phidget object. Otherwise, triggered events may be lost.
This method for using events to detect attachment can be expanded to other events and more complex control flow. Where possible, all example code downloads from the specific language pages shows event-driven programming.
Wait for Attachment
Waiting for attachment is a straightforward process. Your code does not handle events, it simply waits for a device to be plugged in before moving on and doing something else.
For example, in Java you wait for attachment on a created and open software object (called device) like this
// Wait until a device is plugged in
device.waitForAttachment();
// Do things after attachment (i.e. read data, control the device)
Or in C (again, device has been created and opened) :
int result;
// Wait up to 10000 ms for a device to be plugged in
if((result = CPhidget_waitForAttachment((CPhidgetHandle) device, 10000))) {
// No attachment, error
}
// Successful attachment
// Do things after attachment (i.e. read data, control the device)
So, unlike the event model above, a Phidget software object should be open before waiting for a device to be plugged in.
Do Things with the Phidget
After you have a properly created Phidget software object, you can actually call function to turn LEDs on, change output state, read data from sensors, etc.
The thing you probably want to do with your Phidget is read data from its sensors or inputs. This might be, say, a sensor plugged in to a 1018 Phidget Interface Kit used in the code snippets below. You can do this either by detecting changes via event driven code, or polling for new values via logic code.
Details about data handling:
- When a Phidget is opened, its initial state will be read before it is marked as attached. This allows polling of many properties -- including some data -- even during the Attach event, and anytime afterwards.
- Your computer can poll much faster than the Phidget can respond. If you poll in a continuous
while
loop in byte code, you will probably swamp the Phidget with requests.
- Similarly, if you set a value and then immediately read the value on the next line in your program, the Phidget may not have time to finish the set. In our examples, we use
print()
statements within loops. Print functions are relatively slow; you can also usewait()
orsleep()
depending on your language. This also applies to rapidly changing output properties. For example, if you turn on a digital output and then turn it off in the next line of code, the output will never turn on because the hardware doesn't have time to react. You need to put more instructions between the two to guarantee that both changes actually happen.
- If you are handling data using events as described below, the data event functions will fire when the device is plugged in and its initial state is read.
- Some properties have default values, but these should not be trusted. Remember: always set, don’t rely on defaults. Trying to read an uninitialized value with no default will result in an Exception.
- Usually, sensor 'sensitivity' will not be 0 (i.e. set to sense all changes) by default. This can be good because it prevents your screen from being completely overloaded with event output. But, depending on the sensor, it may make your sensor seem to have very high lag, and very poor sensitivity. To capture all events, set the sensitivity to 0.
- Often Phidgets will retain their last state unless power is lost. This can give surprising results as the previous state may not always be what you expect. For example, if you open an InterfaceKit and set an output, this output may stay set even after the Phidget is closed.
Capture Data Change with Events
To capture data changes in sensors or inputs as they happen, you need to use event driven code.
Like defining an event function that fires when the Phidget is plugged in, you can create functions that automatically run when, for example, a sensor value or input value changes.
For example, for an Interface Kit, you can create a function that gets called when a sensor changes. You would do this before the Phidget software object has been opened.
In Java, this would look like:
// After creating a Phidget object called "device":
device.addSensorChangeListener(new SensorChangeListener() {
public void sensorChanged(SensorChangeEvent sensorEvent) {
System.out.println("New Value: " + sensorEvent.getValue());
}
});
Or to use an event to detect attachment in C:
int SensorChangeHandler (CPhidgetHandle device, void *userData, int boardIndex, int newValue) {
printf("New Value %d from sensor at location %d\n", newValue, boardIndex);
return 0;
}
// .....Then, in the main code after creating a Phidget object "device":
CPhidget_set_OnSensorChange_Handler((CPhidgetHandle) device, SensorChangeHandler, NULL);
Poll for Data Change
To poll for sensor data, or output state, you usually want to look for a get...Value or get...State function available in the API for your device. (The API can be found in the user guide for your device.) Then, you simply set up a loop that get the value of a sensor continuously.
To poll your software object, the object must be open. This is in contrast to the event-driven method above, where all event functions are declared and attached before opening the object. Note though that you do not need to open and close the object each time you want to poll it. The easiest thing to do is open the device at the beginning of your program and not close it until the end of the program. You can poll it any number of times in the intervening period.
Note that when you poll the value of a sensor or another attribute, this will probably be within a loop. When you create this loop, the more code you have within a loop, the more slowly your loop will run, and the more slowly you will be sampling the value in practice. This may make you lose data, as described further in the Data Rate section.
This effect is also felt with interpreted languages (Java, Python) versus purely compiled languages, as the interpreted languages sample more slowly even within an otherwise completely empty loop.
So if you want to sample as fast as possible, and capture all of the changes that a sensor produces, you should capture data with event programming. If you choose not to use the event-driven design, you should keep the code run between polls to a minimum. This way you can sample as quickly as possible.
These code snippets assume device
is an 1018 Phidget Interface Kit 8/8/8. For example, in Java, for the Sensor at location 5 on the board:
int val;
for (int i = 0; i < 10; i++) {
val = device.getSensorValue(5);
System.out.println("Value: " + val);
}
Or, in C, for the Sensor at location 5 on the Interface Kit board:
int val;
for (int i = 0; i < 10; i++) {
CPhidgetInterfaceKit_getSensorValue(device, 5, &val);
printf("Value: %d\n", val);
}
Sensors, Input, and Output
Often, your Phidget will be something like an 1018 Phidget Interface Kit which has either analog inputs (black plug holes), or digital inputs and outputs (green screw attachments), or often both.
- To the analog inputs, you can attach various sensors, including sensors for temperature, humidity, light, sound, and so on.
- To the digital inputs, you can attach various input devices, including switches.
- To the digital outputs, you can attach status tools like LEDs.
You use these sensors in software entirely through the software object for the Phidget plugged in to your USB port. For example, to turn off an LED at output block 1 on on an RFID tag reader, you'll want to set the output at location 1 to "0" (or false). In C, this would be:
// Create the RFID software object:
CPhidgetRFIDHandle device = 0;
CPhidgetRFID_create(&device);
// Open and handle the attachment of the PhidgetRFID object
....
// Then, turn the LED off, passing first the output number, then the new state:
CPhidgetRFID_setOutputState(device, 1, 0);
Or in Java, this would be:
// Create the RFID software object:
RFIDPhidget device = new RFIDPhidget();
// Open and handle the attachment of the RFID object
....
// Then, turn the LED off, passing first the output number, then the new state:
device.setOutputState(1, 0);
Getting a digital input would follow the same pattern, except you would use the getOutputState function and you would not pass the function a new output state.
Getting an analog input is a little more complicated because:
- You must declare the sensor as one of two types (ratiometric or non-ratiometric)
- To find out which your sensor is, read the product information for your specific sensor on our main web site.
- You must translate the 1-1000 reading that you get from the input into the data that you need (temperature, etc)
- If the sensor comes from Phidgets, we provide a translation equation in the product information for your specific sensor on our main web site.
Other than that, reading an analog sensor mirrors reading a digital input. For example, to obtain the lux from the - PrecisionLightSensor, a non-ratiometric sensor plugged into analog input 5, you would do this in C:
// Change measurement to non-ratiometric style
CPhidgetInterfaceKit_setRatiometric(device, 0);
// Get the data from analog input 5
int sensorValue;
CPhidgetInterfaceKit_getSensorValue(device, 5, &sensorValue);
// In this case, the measured light lux equals the sensor value.
int lux = sensorValue;
Or in Java:
// Change measurement to non-ratiometric style
device.setRatiometric(0);
// Get the data from analog input 5
int sensorValue = getSensorValue(5);
// In this case, the measured light lux equals the sensor value.
int lux = sensorValue;
Learning Everything You Can Do
The things you can do with your particular Phidget are many and varied, so we only include general concepts on this page.
You can go one of two places for more information on what functions are available for your specific device. We provide both documentation on the raw API for each programming language as well as a language-independent description of the calls for each device.
- Read the API for your specific programming language, available as a download on each page.
- Read the API overview for your hardware, which can be found in its user guide.
Close the Phidget
When you are finished with the Phidget software object at the end of your program, you should close and (in some languages) delete it.
For example, in Java:
device.close();
device = null;
Or, in C:
CPhidget_close((CPhidgetHandle) device);
CPhidget_delete((CPhidgetHandle) device);
The close()
call removes the lock that open put on the Phidget. Make sure to close your object, so other software can use the Phidget!
The close() function also makes sure the thread associated with the Phidget close properly. Any outstanding writes will block close() until they complete, because writes are guaranteed to complete (unless a device is detached).
Also note that a device should be put into a known state before calling close. For example, if a motor controller is driving a motor and close is called, it will continue to drive the motor even though the application has exited. This may or may not be what you want. This is a result of the fact that all Phidgets boards will maintain their current state until they are powered down (physically detached from the computer), even when they are closed by the current application.
Using Multiple Phidgets
It is of course possible to use more than one Phidget within your program. The trick lies in using a unique identifier for each one. You can either hard-code the serial number in by hand (the number will be on the bottom of the board, or you can run our example code for the Phidget to obtain it on-screen), or you can use the Phidget Manager within your program to detect attached devices and return their serial numbers and Phidget types.
Using the Serial Number
Each Phidget has a unique serial number. Using this serial number, you can use a specific open call to open by serial number.
For example, in Java, this would be:
device.open(SerialNumber);
Or in C:
CPhidget_open((CPhidgetHandle) device, serialNumber);
Using the Label
If you want to have a human-readable way to reference your Phidget (as opposed to a serial number), or to have multiple different Phidgets of the same type with the same handle (for re-usable system code), you can use the Label feature of Phidgets. In many development setups, you can change the label to whatever you like, get the label within your code, and open a Phidget based on its label, for any newer generation Phidget. The disadvantage of Labels is that they are not available on all operating systems and languages.
Some limitations are:
- You cannot set a label using any language in Windows, for any language
- However, you can get the label of a Phidget on Windows, and open a Phidget by its label
- Android Java has support for only a subset of the open() functions that use labels, see the Android Java language page for details
- Older Phidgets do not support labels
- No .COM language on Windows supports opening by Label
- This includes AutoIt, Adobe Director, Delphi, and all of the Visual Basic flavours.
- LabVIEW cannot open by label
- Python cannot open by label
- LiveCode cannot open by label
When opening by label, you would use the openLabel("mylabel")
function (or similar, check the API for your language) rather than the generic open() function.
Note that setting the label should be done by a separate program. On a Mac OS computer, you can set a label through the Phidget Preference Pane. On a Linux computer, you should write a special short program specifically for setting the label. There are two reasons to not set a label within your main program:
- If you set a label every time you run that program, when not needing to, you can easily reach the 10,000 re-write limit for the flash that stores the label
- If you are using event driven programming, you cannot set the label from any function called from the attach event, as it will hang (on a mutual exclusion lock).
Because of the second point, your special program to set the label on a Phidget should use waitForAttach()
in logic code and not event-driven programming
Using the Manager
The Phidget Manager object can detect all Phidgets attached to a system and return their attributes in a list (or array, vector, etc. - depending on the language). With this, you can obtain the serial numbers and types of Phidgets currently attached. This is the preferred method for systems where you will be using multiple Phidgets and expecting the system to operate long enough (or under harsh enough conditions) that the Phidgets would need to be replaced.
This is especially true for running programs automatically at scheduled times on the Single Board Computer, where you would be replacing Phidgets without the benefit of keyboard or screen.
For example, in Java (note for Enumerations you also need to include java.util.*
):
// Create and open the manager
Manager manager;
manager = new Manager();
try {
manager.open();
} catch (PhidgetException exception) {
printError(exception.getErrorNumber(), exception.getDescription());
}
// Allow the Phidgets time to attach
Thread.sleep(1000);
// Retrieve the list of attached Phidgets from the manager
Vector phidgetList = manager.getPhidgets();
// Use an enumeration to iterate over the vector
// Vectors also have iterators in Java 2
Enumeration phidgetListEnum = phidgetList.elements();
while(phidgetListEnum.hasMoreElements()) {
Phidget phidgetElement = (Phidget)phidgetListEnum.nextElement();
System.out.print(phidgetElement.getDeviceName() + ", ");
System.out.println(phidgetElement.getSerialNumber());
// Store name and serial number into a persistent variable
....
}
// Close the manager
try {
manager.close();
} catch (PhidgetException exception) {
printError(exception.getErrorNumber(), exception.getDescription());
}
manager = null;
// Do something with names and serial numbers stored above (i.e. open and use Phidget objects)
....
Or, in C (note for sleep you also need to include unistd.h
):
// Create and open the manager
CPhidgetManagerHandle manager = 0;
CPhidgetManager_create(&manager);
CPhidgetManager_open((CPhidgetManagerHandle) manager);
// Allow the Phidgets time to attach
sleep(1);
// Retrieve the list of attached Phidgets from the manager
CPhidgetHandle* phidgetList;
int count;
CPhidgetManager_getAttachedDevices((CPhidgetManagerHandle) manager, &phidgetList, &count);
int serialNumber;
const char *name;
// Iterate over the returned Phidget data
int i;
for (i = 0; i < count; i++) {
CPhidget_getDeviceName(phidgetList[i], &name);
CPhidget_getSerialNumber(phidgetList[i], &serialNumber);
printf("%s, %d\n", name, serialNumber);
// Store name and serial number into a persistent variable
....
}
// Use the Phidget API to free the memory in the phidgetList Array
CPhidgetManager_freeAttachedDevicesArray(phidgetList);
// Close the manager
CPhidgetManager_close((CPhidgetManagerHandle) manager);
CPhidgetManager_delete((CPhidgetManagerHandle) manager);
// Do something with names and serial numbers stored above (i.e. open and use Phidget objects)
....
Distinguishing Events
If you are using event-driven code, once you have correctly opened multiple Phidgets of different types, they will have different event handlers and hence you will know what Phidget triggered which event. If you are using multiple Phidgets of the same type, or you are trying to determine within general events (such as Attach Events) which Phidget triggered the event, you can then check the serial number (or device type) of the triggering device and act accordingly.
For example, in Java, your attach event handler might look like this:
detachHandler = new DetachListener() {
public void detached(DetachEvent event) {
int serialNumber = ((Phidget)event.getSource()).getSerialNumber();
// Do something according to serialNumber
} }
Or in C:
int AttachHandler(CPhidgetHandle device, void *userptr) {
int serialNo;
CPhidget_getSerialNumber(device, &serialNo);
// Do something according to serialNumber
}
Using Phidgets over a Network
Control of a Phidget over a network uses the Phidget WebService.
We have an in-depth description of the WebService, with images to illustrate its use, on the main Phidget WebService page.
In the code on the remote computer where you are receiving Phidget data and controlling the Phidget, you would simply use a different open call, and then the Phidget software object will work as if the object were local and normal.
The different, remote open call in C would be:
int serial_number = 37299;
CPhidget_openRemoteIP ((CPhidgetHandle) device, serial_number, "127.0.0.1", 5001, NULL);
This simply uses the same computer in a 'loopback' connection (127.0.0.1) and the default port 5001. You can replace the serial number with your own Phidget's serial number.
And in Java, the different, remote open call would be:
openAny("127.0.0.1", 5001, null);
There are other options for opening remotely, including by name rather than IP. You can refer to the API for your language for options and specific syntax.
Putting It Together
User and device actions can be handled by either:
- Letting the program tell you when they happen and then doing something (event driven code)
- Polling for things to happen then doing something (logic code)
The style of programming you choose (and hence the language you might prefer) would depend on what you want to do with the Phidget. The two sections, Event Driven Code and Logic Code below give benefits, drawbacks, and general examples of each style.
The styles can also mix. For example, you can take a defined set of steps at first such as turning on an LED or antenna (logic code) and then doing nothing until an output change event is fired (event code).
With languages that support both styles, you can mix and match. For languages that support only logic code (see the Language Support Categories above) you can only use the logic paradigm style.
Examples in pseudo-code are given below for each style type so you can see how your language choice can affect your code design.
Logic Code
Logic code has use for:
- Simple, single-device applications
- Non-GUI applications (GUIs usually are event driven)
- The user driving the device rather than listening to it
Logic code is relatively easy to design well. For example, using the create, open, attach, do stuff, and close concepts introduced above, logic code to handle a Phidget might be written like this:
Although this design does not explicitly capture every event that fires when data or input changes, by polling the device often enough no data will be lost.
However, logic code cannot handle constant, asynchronous events as cleanly as event driven code can.
These designs can be mixed. So, if you find that in logic code you have a highly complex if
loop driving your program, you should consider changing some of it to event driven code. This type of awkward if-loop might look like this:
Create Device Software Object
Open Device
Loop Until Exit Requested {
if No Device Attached {
Wait For Attachment until Timeout
if Wait Timeout Reached {
break
} else {
Initialize Device
}
} else { // Device Is Attached
if Device Data Type 1 Changed {
Do Something
}
if Device Data Type 2 Changed {
Do Something Else
}
// ... More data change functions here
}
Collect User Input
}
Close Device
Delete Device
On the other hand, you can probably see that if your language does not give the option for events, you can use this structure to mimic what events would enable you to do.
Event Driven Code
Event driven code allows for clean handling of complex, asynchronous programs:
- Handling multiple Phidgets
- Handling active plugging or unplugging of the Phidget (multiple attach and detach events)
- Working behind a GUI, as many GUIs are already event driven
- Capturing all sensor data - or input and output - without constantly polling
Without event driven code, you will need to constantly poll the device to see if any state has changed. If you poll at a slower rate than your input or output changes, you will not capture all data.
However, event driven code is usually not as useful or efficient for:
- Only one open and close event
- Using only one device
- Having the user (or program) put changes onto the device (in contrast to reading data from the device)
Event driven code is relatively hard to design well. It may help to draw out a flowchart, state machine, or at least a pseudo-code outline of your system design and all events you wish to handle before writing code. One of the most important things to remember when designing event driven code is to keep the amount of code in the actual event handler to a minimum. If events are coming in at a rapid pace and you have large blocks of code in the event handlers, there is a good chance that the events coming in will overwhelm the processor and you will start missing data points. If you have a lot you need to accomplish for each event make separate functions and call them in another thread in the event handler. This way new events can continue to come in while the code executes.
The code examples given for each specific language use events if they are supported by the language.
Using the create, open, attach, do stuff, and close concepts introduced above, event code to handle a Phidget might be written like this:
Once you have written this code flow, the actual order of events that occur within the program look something like this:
Note that the device itself initiates the function call, by 'firing' the event. This allows you to update only when events fire, and capture all changes, because the low-level interface is telling you when a change occurs.
Advanced Concepts
Now that you have the basic create, open, attach, do stuff, and close concepts introduced above, there are other useful concepts which will help you design your code to be persistent and stable.
Logging
You can enable logging to get more debugging information. Turning on logging happens through a Phidget API function. This would happen at the very start of your program, before even initializing your software object or opening it. Logging lets you get feedback from the Phidget libraries about every Phidget API call you make.
In C, turning on logging to the command line would look like:
CPhidget_enableLogging(PHIDGET_LOG_DEBUG, NULL);
//... All of your Phidget calls. Events called after enable will also log.
CPhidget_disableLogging();
Or in Java:
enableLogging(PHIDGET_LOG_DEBUG, null);
//...All of your Phidget calls. Events called after enable will also log.
disableLogging();
The use of null is to indicate that the output is not to a file (and hence to the command line). Otherwise, the second argument would be a string filename.
There are six different logging levels, ranging from "Give me Everything!" to "Tell me only about critical problems". The level in the examples above - PHIDGET_LOG_DEBUG
- is a medium output level.
PHIDGET_LOG_CRITICAL = 1
- Critical error messages.
- This is the lowest logging level. Errors at this level are generally non-recoverable and indicate either hardware problems, library bugs, or other serious issues.
PHIDGET_LOG_ERROR = 2
- Non-critical error messages.
- Errors at this level are generally automatically recoverable, but may help to track down issues.
PHIDGET_LOG_WARNING = 3
- Warning messages.
- Warnings are used to log behavior that is not necessarily in error, but is nevertheless odd or unexpected.
PHIDGET_LOG_DEBUG = 4
- Debug messages.
- Debug messages are generally used for debugging at Phidgets Inc.
- Note: PHIDGET_LOG_DEBUG messages are only logged in the debug version of the library, regardless of logging level. Thus, these logs should never be seen outside of Phidgets Inc.
PHIDGET_LOG_INFO = 5
- Informational messages.
- Informational messages track key happenings within phidget21 - mostly to do with threads starting and shutting down, and the internal USB code.
PHIDGET_LOG_VERBOSE = 6
- Verbose messages.
- This is the highest logging level. Verbose messages are informational messages that are expected to happen so frequently that they tend to drown out other log messages.
Threading
Due to the use of events, the Phidget library uses threading extensively.
For example:
- Calling
open()
starts a central thread. - Closing everything will shut that central thread down (before the final close returns).
- Each device, once attached, starts its own read and write threads.
These threads provide the support to perform your typical Basic Functions:
- Triggering of data events come from the context of a device read thread.
- Attach and detach events come from the context of the central thread.
- The central thread looks for device attaches and detached, keeping track of which devices are attached internally, and sending out attach and detach events to Phidgets and Managers.
- Writes are performed asynchronously by the write thread. The write queue is only 1 deep so calling a write function while there is a write pending will block.
All Phidget libraries are thread safe, so you don’t need to do any locking on the Phidget objects. If you have a GUI, however, or another program with a separate thread, make sure to use one thread for events, and a separate thread with your GUI, and some form of mutual exclusion if they are both writing to the same thing.
Exceptions and Errors
There are two different types of errors that you can use to confirm that your program is running correctly.
One: An error generated by a function call
These errors are generated by you calling a function. For example, you might try to read analog sensor port number 150 on a board that only has eight. The function you used to read port 150 would return an error (in C/C++) or throw an error (in languages that support exceptions like Java, .NET, Python, or AS3).
So, these errors happen synchronously to function calls, that is, they are returned or thrown right after a function returns. You can handle these by checking the return values on your functions (in C/C++) or using an error catching method like try...catch
in languages that support exceptions (e.g., Java, .NET, Python, AS3).
For example, in C, you might write a LocalErrorCatcher function that you can then use on every Phidget function call to detect and deal with errors:
int LocalErrorCatcher (int errorCode) {
if (errorCode != 0) { // Everything is okay if errorCode = 0
switch (errorCode) {
default:
const char *errorDescription;
LocalErrorCatcher(
CPhidget_getErrorDescription (errorCode, &errorDescription));
printf("The description for error %d is: %s\n", errorCode, errorDescription);
break;
}
}
return 0;
}
// ... Then, later, you would use it on any Phidget function:
LocalErrorCatcher(
CPhidget_open((CPhidgetHandle) device, -1));
Note that:
- The function LocalErrorCatcher uses itself to check for errors on getting an error description.
- You can handle individual error codes as they are listed in the API. This only prints a general message.
Or in Java, you would try a Phidget function call, and catch any resulting exception that occurs:
try {
device.openAny();
} catch (PhidgetException exception) {
system.out.println("The description for error " + Integer.toString(exception.getErrorNumber()) +
" is: ", exception.getDescription());
}
Like C above, you could also use a try...catch
statement around the exception.getErrorNumber() and exception.getDescription() functions to catch any errors from those calls.
The consequences of not catching this type of error (on every function) differ by programming language. In C/C++ these errors must be explicitly checked for after each function call, otherwise the program will simply continue on in an incorrect state. In languages that support exceptions (e.g., Java, .NET, Python, AS3), errors are returned using exceptions which will leave you no choice but to catch them, or have your program terminate without warning.
Be careful when trying to determine what is causing the exception. Something that can happen depending on how your code is structured is you can have exceptions that claim to be a particular Phidget exception but are in fact to do with non-Phidgets code that is for example inside a Phidgets event handler.
Two: An error generated by an event
These errors are generated by the Phidget library during runtime. For example, the Phidget device might be experiencing too high a temperature, and trigger an error. This would not necessarily be due to any one function your program called; rather, the error would appear when the problem appears and would trigger an event.
So, these errors happen asynchronously, that is, something happens apart from any function calls and an error event is triggered. You can handle these by setting up an Error Event Handler.
In C, this would look like:
int ErrorEventHandler (CPhidgetHandle device, void *usrptr, int errorCode, const char *errorDescription) {
printf("The description for error %d is: %s\n", errorCode, errorDescription);
// Do something useful based on the error code
return 0;
}
// Actually hook the Error Event Handler in to receive events
LocalErrorCatcher(
CPhidget_set_OnError_Handler((CPhidgetHandle) device, LibraryErrorHandler, NULL));
You'll note that the LibraryErrorHandler function is what gets called when an error event occurs, and it gives you an opportunity to handle the error.
You'll also note that there is a second function called LocalErrorCatcher. This second function handles the return value from setting the error handler. We included it here because, as these are examples on how to handle errors, the example would leave a possibly unhandled error without it. This second type of error handling and the LocalErrorCatcher function are described above.
In Java, it would look like this:
try {
device.addErrorListener(new ErrorListener() {
public void error(ErrorEvent event) {
System.out.println(event);
}
});
} catch (PhidgetException exception) {
system.out.println("The description for error " + Integer.toString(exception.getErrorNumber()) +
" is: ", exception.getDescription());
}
Like C above, the act of hooking an error listener in to the device can itself generate an error, which should be caught.
These event-type errors are also how a Phidget being run over a network announces bad passwords, lost packets, and other network problems. Locally, the errors can announce incorrect temperatures, current, and power. So, it is in your best interest to set up an error event handler and take action based on the codes and error types in the API for your programming language.
Error Codes
These codes are used within the Error Event. See the API section in the User Guide for your Phidget to see which codes can be returned by which functions. These codes can be very generalized so it’s important to look at the accompanying description. These codes are broken down into errors that stem from within the library, errors which are directly reported by Phidget hardware, and errors that occur in your software.
Library Errors
EEPHIDGET_NETWORK = 0x8001
- Network Error. Usually means trouble contacting a Phidget Webservice.
EEPHIDGET_BADPASSWORD = 0x8002
- Authentication Error. The wrong password was supplied during a remote open call.
EEPHIDGET_BADVERSION = 0x8003
- Version Mismatch Error. Usually means client and host side of a webservice connection are out of sync.
Device Errors
EEPHIDGET_OVERRUN = 0x9002
- Sampling overrun. Some samples were lost in firmware because a queue filled up.
EEPHIDGET_PACKETLOST = 0x9003
- Packet(s) were lost. Usually happens when a data event is stalled by the user.
EEPHIDGET_WRAP = 0x9004
- A variable has wrapped. For example, the encoder position can wrap from 2,147,483,647 to -2,147,483,648 because of an integer overflow.
EEPHIDGET_OVERTEMP = 0x9005
- Over-Temperature condition detected. See description for more details.
EEPHIDGET_OVERCURRENT = 0x9006
- Over-Current condition detected. See description for more details.
EEPHIDGET_OUTOFRANGE = 0x9007
- Out of range condition detected. Usually an input on the board is reporting a value that is outside of the allowed range.
EEPHIDGET_BADPOWER = 0x9008
- Power supply problem detected. Either the power supply is being overloaded, or it is not powered (ie. not plugged in).
Software Errors
These codes are returned by Phidget API functions / thrown as exceptions on errors. See the API manual for your language to see which codes can be returned by which functions. In C/C++, a return code of 0 indicates success, any other return code is a failure. In languages supporting exceptions, most functions a will return void, and throw an exception on error. All Phidgets functions will return an integer value that corresponds to one of the following cases or 0 which is a success.
EPHIDGET_NOTFOUND = 1
- Phidget not found exception. “A Phidget matching the type and or serial number could not be found.”
- This exception is not currently used externally.
EPHIDGET_NOMEMORY = 2
- No memory exception. “Memory could not be allocated.”
- This exception is thrown when a memory allocation (malloc) call fails in the c library.
EPHIDGET_UNEXPECTED = 3
- Unexpected exception. “Unexpected Error. Contact Phidgets Inc. for support.”
- This exception is thrown when something unexpected happens (more unexpected than another exception). This generally points to a bug or bad code in the C library, and hopefully won’t even be seen.
EPHIDGET_INVALIDARG = 4
- Invalid argument exception. “Invalid argument passed to function.”
- This exception is thrown whenever a function receives an unexpected null pointer, or a value that is out of range. ie setting a motor’s speed to 101 when the maximum is 100.
EPHIDGET_NOTATTACHED = 5
- Phidget not attached exception. “Phidget not physically attached.”
- This exception is thrown when a method is called on a device that is not attached, and the method requires the device to be attached. ie trying to read the serial number, or the state of an output.
EPHIDGET_INTERRUPTED = 6
- Interrupted exception. “Read/Write operation was interrupted.”
- This exception is not currently used externally.
EPHIDGET_INVALID = 7
- Invalid error exception. “The Error Code is not defined.”
- This exception is thrown when trying to get the string description of an undefined error code.
EPHIDGET_NETWORK = 8
- Network exception. “Network Error.”
- This exception is usually only seen in the Error event. It will generally be accompanied by a specific Description of the network problem.
EPHIDGET_UNKNOWNVAL = 9
- Value unknown exception. “Value is Unknown (State not yet received from device, or not yet set by user).”
- This exception is thrown when a device that is set to unknown is read (e.g., trying to read the position of a servo before setting its position).
- Every effort is made in the library to fill in as much of a device’s state before the attach event gets thrown, however, many there are some states that cannot be filled in automatically (e.g., older interface kits do not return their output states, so these will be unknown until they are set).
- This is a quite common exception for some devices, and so should always be caught.
EPHIDGET_BADPASSWORD = 10
- Authorization exception. “Authorization Failed.”
- This exception is thrown during the Error event. It means that a connection could not be authenticated because of a password miss match.
EPHIDGET_UNSUPPORTED = 11
- Unsupported exception. “Not Supported.”
- This exception is thrown when a method is called that is not supported, either by that device, or by the system (e.g., calling setRatiometric on an InterfaceKit that does not have sensors).
EPHIDGET_DUPLICATE = 12
- Duplicate request exception. “Duplicated request.”
- This exception is not currently used.
EPHIDGET_TIMEOUT = 13
- Timeout exception. “Given timeout has been exceeded.”
- This exception is thrown by waitForAttachment(int) if the provided time out expires before an attach happens. This may also be thrown by a device set request, if the set times out (though this should not happen, and would generally mean a problem with the device).
EPHIDGET_OUTOFBOUNDS = 14
- Out of bounds exception. “Index out of Bounds.”
- This exception is thrown anytime an indexed set or get method is called with an out of bounds index.
EPHIDGET_EVENT = 15
- Event exception. “A non-null error code was returned from an event handler.”
- This exception is not currently used.
EPHIDGET_NETWORK_NOTCONNECTED = 16
- Network not connected exception. “A connection to the server does not exist.”
- This exception is thrown when a network specific method is called on a device that was opened remotely, but there is no connection to a server (e.g., getServerID).
EPHIDGET_WRONGDEVICE = 17
- Wrong device exception. “Function is not applicable for this device.”
- This exception is thrown when a method from device is called by another device. ie casting an InterfaceKit to a Servo and calling setPosition.
EPHIDGET_CLOSED = 18
- Phidget closed exception. “Phidget handle was closed.”
- This exception is thrown by waitForAttachment() if the handle it is waiting on is closed.
EPHIDGET_BADVERSION = 19
- Version mismatch exception. “Webservice and Client protocol versions don’t match. Update to newest release.”
- This exception is thrown in the error event when connection to a Phidget Webservice that uses a different protocol version then the client library.
Sensitivity and Filtering
Change Triggers are used to filter the number of events that are returned to an Application, by setting a minimum amount of activity before a Change Event is sent to your program.
This is a simple hysteresis - a minimum amount of change has to occur since the last event before another event will be fired.
If your application is implementing its own filtering, setting the ChangeTrigger, or sensitivity to zero will cause all events to fire. Change triggers are generally available only for sensor inputs events. Change triggers are referred to as Sensitivity in some device-specific APIs.
Data Rate
Some devices support a user-defined data rate for events.
- The data rate is set in milliseconds, with a range from up 1ms to 1000ms, depending on the Phidget.
- Data rates greater then 8ms generally need to be a multiple of 8 (8,16,24,...,496,...,996,1000).
- Data rates lower then 8ms are supported as: 4ms, 2ms, 1ms.
- Data rate is a maximum rate
- Data rate will be superseded by a non-zero sensitivity on devices that support both sensitivity and data rate.
See the main page for your device - found on its product page on our main website for more details on available data rates and specifications.
Note that this data rate is not the rate at which you will receive data. Rather, it is the amount of time over which events are averaged before being sent. It may help to think of our specification of data rate as an averaging time. This means that setting a data rate of 24 ms will try to give you data averaged over 24 ms, and send it every 24 ms. Depending on your computer, events may or may not be dropped. If events are dropped, the data points that are received still represent 24 ms (or whatever the data rate is set to). This means data is lost.
Conversely, if you are polling, and you set the data rate to longer than your polling rate, you will receive duplicate, averaged data. For example, setting the data rate to 24 ms and then polling every 12 ms will give you duplicate readings every other poll. To poll and not miss any data, the rate that your code polls must match the rate that you set for the setDataRate()
function in our API.
Using the Dictionary
The Phidget Dictionary is a service provided by the Phidget WebService. The dictionary:
- Is a centralized collection of key-value pairs
- Works only over the webservice
- Can be accessed and changed from any number of webservice clients
- Makes use of extended regular expressions (denoted between two forward slashes) for key matching
- Is also a foundation part of the webservice itself, and controls access of all Phidgets through the
openRemote
andopenRemoteIP
interfaces - Can be used as an abstracted or higher-level interface for remotely managing Phidgets
We have a more in-depth description of the Dictionary (with pictures) on the main Phidget Dictionary page.
The intended use for the dictionary is as a central repository for communication and persistent storage of data between several client applications. For example, the computer directly connected to a Phidget could create and change keys for additional events on top of (or even instead of) those already thrown by the Phidget device API. The dictionary API is very high-level and thus very flexible - essentially any application of adding keys, changing keys (and throwing events), and getting values of keys can be handled by the Dictionary.
Note that you should never add or modify a key that starts with /PSK/
or /PCK/
as these are the keys that Phidgets use. Unless, of course, you want to explicitly modify Phidget specific data - and this is highly discouraged, as it’s very easy to break things. Listening to these keys is fine if so desired.
Refer to the API for your specific language for the full Dictionary API.
Long Term Code Maintenance
Thinking long term from the start is incredibly useful. Phidgets as a company is constantly developing and inventing, and our API gets improved and changed to still work with older devices while adding support for new ones.
We release library updates - both for operating systems and languages - on a regular basis. We strongly recommend updating your own libraries as we release them. This goes for both the libraries and the WebService, as the versions must match.
This form of updating (did we mention that we strongly recommend it?) is the single most important thing you can do to reduce maintenance issues with your Phidget software down the road.
Every so often, customers come to us having run the same code for years, without updating. Then, for some reason, they have to update (say, for an operating system migration). And their code no longer works. What do to then? We can work with these customers to find out which of the many updates was the one to cause the problem - a difficult and very time consuming process. And over the course of multiple years, we may no longer stock or even manufacture that particular Phidget. You can imagine that a process with these limitations can take a very long time to resolve, if it can be resolved at all.
Conversely, if you update every time, this time frame is greatly reduced. We make a serious effort to test every library update, and although some backwards-compatibility problems do (rarely) get through, we have a fresh memory of what was changed and can work with you to quickly either debug the new libraries or get you a working version.
In addition to library updates, here are a few other tips and tricks to reduce your maintenance time:
- If you are using Logging, don't remove the logging code when you finalize your release. Simply comment it out so you can turn it on again later and debug quickly.
- Some long-term problems can be detected early by listening to library messages and errors. Handling Exceptions and Errors will allow your code to run silently until something is wrong, in which case you'll know right away.
If you are looking for more help on how to work with our library updating, please feel welcome to contact us.
Summary
This page has gone through basic to advanced uses of the Phidget API. We've used C and Java here as example languages. If you're using a different language, we include similar snippets of code (without the in-depth description) in the documentation for your language.
If you've read this far and are still hungry for more, try learning more about your hardware; we have a number of hardware learning pages as well.