|
|
(32 intermediate revisions by 4 users not shown) |
Line 1: |
Line 1: |
| [[Category:Programming]] | | [[Category:Programming]]{{Recommended_Flow_Links|{{Flow Page Number|{{PAGENAME}} }} }} |
| __TOC__
| | <div class="ProgrammingBasicsIntro"> |
| | {| |
| | | |
| | ==Welcome!== |
| | Welcome to the Phidget Programming Basics guide! |
|
| |
|
| Please refer to [[What is a Phidget?]] for an introduction to the concept of Phidgets.
| | By following this guide, you will learn the important concepts behind how Phidgets are used in any software application. |
|
| |
|
| The Phidget software library matches a Phidget device channel with a software channel that is created by a user.
| | ==Before You Start== |
| Software channels are each of a class, where that class defines a set of methods, properties, events and errors. Phidget devices are aware of the channel classes, and expose access to features of the device through the class interface.
| | We recommend having a few things set up before starting this guide on how to program with Phidgets: |
|
| |
|
| To use a Phidget, you'll want to:
| | '''1.''' You will need the Phidget22 libraries installed on your machine. To get the Phidgets libraries for your operating system, check out our page on [[Operating System Support]]. |
| # '''[[#Creating a Channel|Create]]''' an instance of the channel class that provides access to the device you want to control.
| |
| # '''[[#Opening a Channel|Open]]''' the channel.
| |
| # Detect when the channel is '''[[#Attaching a Channel|attached]]'''.
| |
| # Use '''[[#Do Things with a Channel|methods and properties]]''' of the attached channel.
| |
| # '''[[#Close a Channel|Close]]''' the channel.
| |
|
| |
|
| Small code snippets are provided for each step in the sections below. [[Language - Java|Java]] and [[Language - C/C++|C]] were selected because Java is a high-level language and C is low level language. The best reference to the ''features'' each channel class is the {{Phidget22API}} for your specific language. This page is intended to be a high-level introduction.
| | '''2.''' We recommend knowing which programming language you intend to use before starting this guide, so you can follow along. We provide a list of [[Programming Resources|programming languages]] we support, and instructions of how to set them up for use with Phidgets. |
|
| |
|
| == Creating a Channel == | | '''3.''' This guide will work best if you have a Phidget to work with, and have already tried it out using the '''Phidget22 Control Panel'''. If you haven't yet, navigate to the [[Product_Page_Links|product page]] for your Phidget and follow the instructions in the '''User Guide''' tab to try it out. |
| | | |
| | {|class="ProgrammingBasicsTOC" style="color: black; width: 100%; border-style: solid; border-width: 2px;" cellpadding="10" |
| | |<font size=5>'''Table of Contents'''</font> |
| | <font size=4> |
| | #'''[[{{Flow Page Number Name| 1 }}]]''' |
| | #'''[[{{Flow Page Number Name| 2 }}]]''' |
| | #'''[[{{Flow Page Number Name| 3 }}]]''' |
| | #'''[[{{Flow Page Number Name| 4 }}]]''' |
| | #'''[[{{Flow Page Number Name| 5 }}]]''' |
| | #'''[[{{Flow Page Number Name| 6 }}]]''' |
| | #'''[[{{Flow Page Number Name| 7 }}]]''' |
| | #'''[[{{Flow Page Number Name| 8 }}]]''' |
| | #'''[[{{Flow Page Number Name| 9 }}]]''' |
| | #'''[[{{Flow Page Number Name| 10 }}]]''' |
| | #'''[[{{Flow Page Number Name| 11 }}]]''' |
| | #'''[[{{Flow Page Number Name| 12 }}]]''' |
| | #'''[[{{Flow Page Number Name| 13 }}]]''' |
| | #'''[[{{Flow Page Number Name| 14 }}]]''' |
| | #'''[[{{Flow Page Number Name| 15 }}]]''' |
| | #'''[[{{Flow Page Number Name| 16 }}]]''' |
| | </font> |
| | |} |
| | |} |
|
| |
|
| A Phidget device exposes one or more device channels where each channel is an interface to a feature of the hardware. A channel class defines an interface to control and query a channel of the Phidget device. For example, a 1018 - Phidget InterfaceKit device includes support
| | {|class="ProgrammingBasicsTOCsmall" style="color: black; width: 100%; border-style: solid; border-width: 2px;" cellpadding="10" |
| for digital input and digital out, and therefore exports <code>DigitalInput</code> and <code>DigitalOutput</code> channels.
| | |<font size=5>'''Table of Contents'''</font> |
| | | <font size=4> |
| Phidget devices are controlled by creating an instance of a Phidget channel class. Each channel class has a function that allows you to '''create''' an instance, and a function to later '''delete''' the instance.
| | #'''[[{{Flow Page Number Name| 1 }}]]''' |
| | | #'''[[{{Flow Page Number Name| 2 }}]]''' |
| For example, in Java:
| | #'''[[{{Flow Page Number Name| 3 }}]]''' |
| | | #'''[[{{Flow Page Number Name| 4 }}]]''' |
| <syntaxhighlight lang=java>
| | #'''[[{{Flow Page Number Name| 5 }}]]''' |
| // Create a new Accelerometer channel
| | #'''[[{{Flow Page Number Name| 6 }}]]''' |
| Accelerometer accel = new Accelerometer();
| | #'''[[{{Flow Page Number Name| 7 }}]]''' |
| </syntaxhighlight>
| | #'''[[{{Flow Page Number Name| 8 }}]]''' |
| <syntaxhighlight lang=java>
| | #'''[[{{Flow Page Number Name| 9 }}]]''' |
| // Create a new RFID channel
| | #'''[[{{Flow Page Number Name| 10 }}]]''' |
| RFID rfid = new RFID();
| | #'''[[{{Flow Page Number Name| 11 }}]]''' |
| </syntaxhighlight>
| | #'''[[{{Flow Page Number Name| 12 }}]]''' |
| | | #'''[[{{Flow Page Number Name| 13 }}]]''' |
| Or in C:
| | #'''[[{{Flow Page Number Name| 14 }}]]''' |
| | | #'''[[{{Flow Page Number Name| 15 }}]]''' |
| <syntaxhighlight lang=c>
| | #'''[[{{Flow Page Number Name| 16 }}]]''' |
| // Create a new Accelerometer channel
| | </font> |
| PhidgetAccelerometerHandle accel;
| | |} |
| PhidgetAccelerometer_create(&accel);
| | </div> |
| </syntaxhighlight>
| | {{Flow_Navigation_Buttons|{{Flow Page Number|{{PAGENAME}} }} }} |
| <syntaxhighlight lang=c>
| |
| // Create a new RFID channel
| |
| PhidgetRFIDHandle rfid;
| |
| PhidgetRFID_create(&rfid);
| |
| </syntaxhighlight>
| |
| | |
| A channel class will have properties and methods specific to the feature they belong to. For example, the <code>VoltageInput</code> class has a <code>Voltage</code> property that lets you access the current voltage measured by the device, the <code>Accelerometer</code> class has properties to set the sensitivity on each axis and the <code>RFID</code> has a method that writes to a writable RFID tag.
| |
| | |
| == Opening a Channel == | |
| | |
| After you have created a [[#Creating a Software Channel| channel instance]], you can call the <code>open()</code> method to begin the process of matching the channel to a Phidget device channel.
| |
| | |
| For example, with the <code>Accelerometer</code> channel in Java:
| |
| | |
| <syntaxhighlight lang=java> | |
| accel.open();
| |
| </syntaxhighlight>
| |
| | |
| Or in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| Phidget_open((PhidgetHandle) accel);
| |
| </syntaxhighlight>
| |
| | |
| To see how to use <code>open()</code> in other languages, please refer to the {{Phidget22API}}.
| |
| | |
| The <code>open()</code> function begins the process of matching the channel handle you created to a channel of a Phidget device, and does not actually ''open'' the Phidget itself. An open channel may match a new Phidget device when it is plugged in if that channel has not already found a Phidget device channel that met its criteria.
| |
| | |
| ===Details for Open()===
| |
| | |
| Because <code>open()</code> simply enables the process of matching a channel to a Phidget device channel, <code>open()</code> returns immediately and does not wait for the channel to attach to a Phidget device channel. The channel changes state to ''open'', but is not necessarily ''attached''. Until the channel actually becomes ''attached'' to a Phidget device channel, most of the channel class interface will not be available.
| |
| | |
| As long as the channel is open (and not ''attached''), the system will continue to try to match it with a
| |
| Phidget device channel that has not already been matched with a channel. The channel can only be matched once, and the Phidget device channel can only be matched once. For example, if you connected a single Phidget Accelerometer to a computer, and you created and opened two <code>Accelerometer</code> channels, only one of the channels would attach while the other would remain ''open'' but not ''attached''. If the ''attached'' channel were to be closed, the other channel would then attach to the Accelerometer.
| |
| | |
| When the channels is matched with a Phidget device channel, the channel state is changed to ''attached'' and an event is fired.
| |
| | |
| '''Note:''' Once a Phidget device channel is matched to a channel, it will not be available to match another channel until the first channel is closed. The one exception is if the Phidget device channel is ''attached'' over the network through the [[Phidget Network Server]]. In that case, multiple channels are allowed to match and attach to the Phidget device channel. Some Phidgets, such as motor controllers will never match more than one channel at a time. | |
| | |
| ===Channel Matching===
| |
| | |
| <code>open()</code> can be used without specifying any matching properties. In this case the system will match a channel with the first Phidget device channel that matches the channel class. The channel class itself is an implied matching parameter.
| |
| | |
| If there is more than one Phidget connected to the computer that supports the channel class, the <code>DeviceSerialNumber</code> property must be set determine which Phidget will match.
| |
| | |
| If the Phidget connected to the computer has more than one channel of the same classes, the <code>Channel</code> property must be set to determine which Phidget device channel will match.
| |
| | |
| If the Phidget Network Server is enabled on the computer, the <code>IsRemote</code> and <code>IsLocal</code> properties can be used to determine if the channel will match the Phidget device channel or the network channel.
| |
| | |
| Please refer to the {{Phidget22API}}, and [[Best Phidgets Practices|Best Practices]] for more information on matching channels to Phidget device channels.
| |
| | |
| ==== Opening a Channel on a USB Phidget ====
| |
| | |
| [[Image:channel-matching-1.jpg|link=|500px]] | |
| | |
| In this example, a [{{SERVER}}/products.php?product_id=1018 1018 PhidgetInterfaceKit 8/8/8] is connected to a computer. A [{{SERVER}}/products.php?product_id=1108 1108 Magnetic Sensor] is connected to analog port 3 on the 1018. Here's how you would open the channel for the magnetic sensor in Java:
| |
| | |
| <syntaxhighlight lang=java>
| |
| VoltageRatioInput ch = new VoltageRatioInput();
| |
| ch.setDeviceSerialNumber(324781);
| |
| ch.setChannel(3);
| |
| ch.open();
| |
| </syntaxhighlight>
| |
| | |
| If you wanted to open digital input 5 in the same example, it would look like this:
| |
| | |
| <syntaxhighlight lang=java>
| |
| DigitalInput ch = new DigitalInput();
| |
| ch.setDeviceSerialNumber(324781);
| |
| ch.setChannel(5);
| |
| ch.open();
| |
| </syntaxhighlight>
| |
| | |
| ==== Opening a VINT Hub Port as a Channel ====
| |
| | |
| The ports on a [[What is VINT?|VINT]] Hub can be opened as Digital Inputs, Digital Outputs, Voltage Inputs, or Voltage Ratio Inputs. Suppose you had a [{{SERVER}}/products.php?product_id=HUB0000 HUB0000 VINT Hub] with a [{{SERVER}}/products.php?product_id=1133 1133 Sound Sensor] connected to its sixth port.
| |
| | |
| [[Image:channel-matching-2.jpg|link=|500px]]
| |
| | |
| Here's how you would open it in Java:
| |
| | |
| <syntaxhighlight lang=java>
| |
| VoltageInput ch = new VoltageInput();
| |
| ch.setDeviceSerialNumber(370181);
| |
| ch.setIsHubPortDevice(true);
| |
| ch.setHubPort(6);
| |
| ch.open();
| |
| </syntaxhighlight>
| |
| | |
| ==== Opening a Channel on a VINT Device ====
| |
| | |
| In this example, we have a [{{SERVER}}/products.php?product_id=TMP1101 TMP1101 4x Thermocouple Phidget] and a [{{SERVER}}/products.php?product_id=HUB0000 HUB0000 VINT Hub].
| |
| | |
| [[Image:channel-matching-3.jpg|link=|500px]]
| |
| | |
| If we wanted to open both the thermocouple connected to port 0 and the integrated temperature sensor on the board, we first need to figure out which channels those are. Go to the [{{SERVER}}/products.php?product_id=TMP1101 product page for the TMP1101] and click on the API tab. From the table at the top of the tab, we can see that the integrated temperature sensor is TemperatureSensor channel 4. In Java, opening these channels would look like this:
| |
| | |
| <syntaxhighlight lang=java>
| |
| TemperatureSensor tc = new TemperatureSensor(); // Handle for the thermocouple
| |
| TemperatureSensor ic = new TemperatureSensor(); // Handle for the integrated temperature chip
| |
| | |
| tc.setDeviceSerialNumber();
| |
| ic.setDeviceSerialNumber();
| |
| tc.setHubPort(2);
| |
| ic.setHubPort(2);
| |
| tc.setChannel(0);
| |
| ic.setChannel(4);
| |
| | |
| tc.open();
| |
| ic.open();
| |
| </syntaxhighlight>
| |
| | |
| ==== Opening a Channel on a Remote Phidget ====
| |
| | |
| Suppose you wanted to open a locally connected Phidget remotely over the Network Service, so that you could have multiple programs connecting to it at the same time. In this example, we have a [{{SERVER}}/products.php?product_id=1024 1024 PhidgetRFID] connected to a computer that has the Phidget Network Server enabled.
| |
| | |
| [[Image:channel-matching-4.jpg|link=|600px]]
| |
| | |
| You could open the RFID reader channel remotely with the following code in Java:
| |
| | |
| <syntaxhighlight lang=java>
| |
| RFID ch = new RFID();
| |
| ch.setDeviceSerialNumber(388624);
| |
| ch.setIsRemote(true);
| |
| ch.open();
| |
| </syntaxhighlight>
| |
| | |
| == Attaching a Channel ==
| |
| | |
| When a channel is open (and not ''attached''), the system will continue to try to match it with a
| |
| Phidget device channel until either a match is found, or the channel is closed. When the channels is matched with a Phidget device channel, the channel state is changed to ''attached'' and an event is fired.
| |
| | |
| An event handler can be registered to catch the ''attach event'' through the channel's <code>Attach</code> event. Each channel also has a <code>Attached</code> property that can be read to determine if the class has
| |
| been matched to a Phidget device channel.
| |
| | |
| To set an event handler to detect the attach in Java:
| |
| | |
| <syntaxhighlight lang=java>
| |
| //create a Phidget accelerometer channel:
| |
| Accelerometer accel = new Accelerometer();
| |
| accel.addAttachListener((AttachEvent ae) -> {
| |
| Accelerometer accel = (Accelerometer) ae.getSource();
| |
| // Do things after attachment (i.e. read data, control the device)
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| Or in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| // Define the attach handler function
| |
| void CCONV onAttachedEventHandler(PhidgetHandle channel, void *userPtr) {
| |
| printf("A channel has been attached\n");
| |
| // Do things after attachment (i.e. read data, control the device)
| |
| }
| |
| | |
| int main() {
| |
| // .....Then, in the main code create the channel and set the attach handler:
| |
| PhidgetAccelerometerHandle accel;
| |
| PhidgetAccelerometer_create(&accel);
| |
| Phidget_setOnAttachHandler((PhidgetHandle)accel, OnAttachedEventHandler, NULL);
| |
|
| |
| // other stuff in main
| |
| }
| |
| </syntaxhighlight>
| |
| | |
| You should set the attach event handler '''before''' you <code>open()</code> the channel; otherwise, you may miss attach event if it occurs between opening the channel and setting the handler.
| |
| | |
| == Do Things with a Channel ==
| |
| | |
| After a channel has been ''opened'' and ''attached'', software can control the Phidget device via the channel class interface.
| |
| | |
| The examples below are for a.
| |
| | |
| Like setting an event handler that executes [[#Event Attachment|when the channels is attached]], event handlers can be set for many Phidget devices when a state change occurs, or sensor data is received.
| |
| | |
| For example, for a Voltage Input channel like the ones supported by the [{{SERVER}}/products.php?product_id=1018 1018 Phidget Interface Kit] you can set an event handler that gets called at a defined interval.
| |
| | |
| In Java, this would look like:
| |
| | |
| <syntaxhighlight lang=java>
| |
| // After creating and opening a VoltageInput channel called "sensorIn":
| |
| sensorIn.addVoltageChangeListener((VoltageInputVoltageChangeEvent de) -> {
| |
| System.out.println("Voltage: " + de.getVoltage());
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| Or in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| void CCONV onVoltageChangeHandler(PhidgetVoltageInputHandle voltageInput, void *userPtr, double voltage) {
| |
| printf("\nVoltage: %lf V\n", voltage);
| |
| }
| |
| | |
| // .....Then, in the main code:
| |
| // After creating and opening a VoltageInput channel called "sensorIn":
| |
| PhidgetVoltageInput_setOnVoltageChangeHandler(sensorIn, onVoltageChangeHandler, NULL);
| |
| </syntaxhighlight>
| |
| | |
| The voltage value delivered in the above snippets could also be accessed via the <code>Voltage</code> property of the channel. Properties that have related events are automatically updated each time the event is received by the channel, and calls between events always return the value set by the last event.
| |
| | |
| <syntaxhighlight lang=java>
| |
| double val = sensorIn.getVoltage();
| |
| }
| |
| </syntaxhighlight>
| |
| | |
| Or, in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| double val;
| |
| PhidgetVoltageInput_getVoltage(sensorIn, &val);
| |
| }
| |
| </syntaxhighlight>
| |
| | |
| ===Sensors, Input, and Output===
| |
| | |
| Often, your Phidget will be something like an [{{SERVER}}/products.php?product_id=1018 1018 Phidget Interface Kit] which has voltage inputs (black plug holes), digital inputs and outputs (green screw attachments). You can learn about the channel classes your Phidget uses and how to attach to them by visiting its product page and the {{Phidget22API}}. For InterfaceKits like the 1018:
| |
| | |
| * To the voltage 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 simple indicators like LEDs, buzzers, or relays.
| |
| | |
| You use all of these things in software entirely through the channel class that each one belongs to. For example, to turn off an LED connected to output 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:
| |
| | |
| <syntaxhighlight lang=cpp>
| |
| // Create the DigitalOutput channel:
| |
| PhidgetDigitalOutput led_out;
| |
| PhidgetDigitalOutput_create(&led_out);
| |
| Phidget_setChannel((PhidgetHandle)led_out, 1);
| |
| // Open and handle the attachment of the DigitalOutput handle
| |
| ....
| |
| | |
| // Then, turn the LED off, passing first the digitalOutput handle, then the new state:
| |
| PhidgetDigitalOutput_setState(led_out, 0);
| |
| </syntaxhighlight>
| |
| | |
| Or in Java, this would be:
| |
| | |
| <syntaxhighlight lang=java>
| |
| // Create the DigitalOutput software handle
| |
| DigitalOutput ledOut = new DigitalOutput();
| |
| ledOut.setChannel(1);
| |
| // Open and handle the attachment of the digital output handle
| |
| ....
| |
| | |
| // Then, turn the LED off, passing first the output number, then the new state:
| |
| ledOut.setState(0);
| |
| </syntaxhighlight>
| |
| | |
| Getting a digital input would follow a similar pattern, except you would use the getState function and store the result in a variable instead of passing the function a new output state.
| |
| | |
| Getting sensor data from a voltage input is a little more complicated because:
| |
| * You must declare the sensor as one of two types ('''ratiometric''' or '''non-ratiometric'''). If it is ratiometric, use the VoltageRatioInput class to open the device channel. If it is non-ratiometric, use the VoltageInput class instead. To find out which your sensor is, read the product information for your specific sensor on our [{{SERVER}} main web site].
| |
| * You must translate the 0-5V reading that you get from the input into the proper units you need (temperature, luminosity, decibels, etc.)
| |
| If the sensor comes from Phidgets, you can use the <code>setSensorType</code> method in order to have the conversion done for you.
| |
| | |
| For example, to obtain the lux from the [{{SERVER}}/products.php?product_id=1127 - PrecisionLightSensor], a '''non-ratiometric''' sensor plugged into voltage input 5, you would do this in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| // Use the VoltageInput class because the 1127 is non-ratiometric
| |
| PhidgetVoltageInputHandle voltageInput;
| |
| PhidgetVoltageInput_create(&voltageInput)
| |
| | |
| // Set the input channel to 5 and open
| |
| Phidget_setChannel(voltageInput, 5);
| |
| Phidget_open(voltageInput);
| |
| | |
| // Set the sensor type. For a complete list of Phidgets sensors, see the Sensor Type enum in the API documentation.
| |
| PhidgetVoltageInput_setSensorType(voltageInput, SENSOR_TYPE_1127);
| |
| | |
| // Get the sensor value; since the library knows it's an 1127, it will automatically convert from volts to lux.
| |
| int lightLevel;
| |
| PhidgetVoltageInput_getSensorValue(voltageInput, &lightLevel);
| |
| | |
| </syntaxhighlight> | |
| | |
| | |
| Or in Java:
| |
| | |
| <syntaxhighlight lang=java> | |
| // Use the VoltageInput class because the 1127 is non-ratiometric
| |
| VoltageInput voltageInput = new VoltageInput();
| |
|
| |
| // Set the input channel to 5 and open
| |
| voltageInput.setChannel(5);
| |
| voltageInput.open();
| |
| | |
| // Set the sensor type. For a complete list of Phidgets sensors, see the Sensor Type enum in the API documentation.
| |
| voltageInput.setSensorType(SENSOR_TYPE_1127);
| |
| | |
| // Get the sensor value; since the library knows it's an 1127, it will automatically convert from volts to lux.
| |
| int lightLevel = voltageInput.SensorValue();
| |
| </syntaxhighlight>
| |
| | |
| ===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 view the complete list of functions for your device in the {{Phidget22API}}. Select your device at the top of the screen, select your preferred programming language, and then select which channel class you want to view the documentation for.
| |
| | |
| The API documentation is broken up into sections. The first section is a list of methods (or if you're using an object-oriented language, properties), after that there'll be a list of events and event handlers. After that, there may be sections for enumerations (which are like names that let you select options for certain functions, like SensorType in the earlier example), error events, or structs, depending on the language.
| |
| | |
| == Close a Channel ==
| |
| | |
| When you are finished with a channel, you should close and (in some languages) delete it. Once closed, the Phidget device channel the channel was matched to will be released and made available for another channel to match.
| |
| | |
| For example, in Java:
| |
| | |
| <syntaxhighlight lang=java>
| |
| device.close();
| |
| device = null;
| |
| </syntaxhighlight>
| |
| | |
| Or, in C:
| |
| | |
| <syntaxhighlight lang=c>
| |
| Phidget_close((PhidgetHandle)channel);
| |
| Phidget_release((PhidgetHandle)&channel); // will NULL the pointer
| |
| </syntaxhighlight>
| |
| | |
| == Further Reading ==
| |
| | |
| [[Data Interval/Change Trigger]] - Learn about these two properties that control how much data comes in from your sensors.
| |
| | |
| [[Using Multiple Phidgets]] - How to know you are attaching to the Phidget you want.
| |
| | |
| [[Polling vs. Events]] - Your program can gather data in either a polling-driven or event-driven manner. Learn the difference to determine which is best for your application.
| |
| | |
| [[Logging, Exceptions, and Errors]] - Learn about all the tools you can use to debug your program.
| |
| | |
| [[Phidget Network Server]] - Phidgets can be controlled and communicated with over your network- either wirelessly or over ethernet.
| |
| | |
| [[Best Phidgets Practices]] - Good programming habits that will save you from common problems when writing code for your Phidgets.
| |