Let my inputs go!
The Gadgeteer system is great; there are loads of modules that you can easily use to do a range of interesting things. There’s a ‘but’ coming here, of course, and it is this: there’s no obvious way of connecting things which are not already Gadgeteer modules. For example, suppose you want to put a switch on your fridge door, so that you can trigger a Gadgeteer camera to take a picture of the person who is surreptitiously drinking all your milk. Or maybe you want to connect multiple switches, perhaps to determine the ammunition remaining in your USB missile launcher. The Gadgeteer buttons are not really suited to the task. Even if you can physically fit them where you want them, and you’ve got long enough cables, each module only has one switch on it. There’s a solution to this, and that’s what this post will start to cover – adding your own devices to the Gadgeteer system.
In order to add new stuff to a Gadgeteer system, there are three things which must be considered: the physical connections, the electrical interface and the software drivers. Don’t panic, because for simple things like switches, this is all very easy. The physical connections might seem like a problem, because of the tiny little socket that the Gadgeteer main board uses to connect all its devices. Fortunately, there is a cheap breakout board available which can be plugged in to the main board using a standard cable and exposes all the wires as pins you can solder to or use to plug into a breadboard:
You’ll notice that this board actually has two sockets on it. That’s because you can also use it as a pass-through connector between a functional module and the mainboard. This is useful for debugging and diagnostics, as you can measure voltage levels at all the pins even while the connected device is doing its stuff. The pins on the bottom edge fit perfectly into a standard breadboard. So that’s the physical hardware interface taken care of.
The next thing to consider is the electrical interface, and this is a little more complex. Although all the Gadgeteer devices use the same 10-wire connector, the actual electrical functions of these wires vary and that’s why each socket is labelled with one or more letters denoting its function(s). There is a full list of these on the Gadgeteer CodePlex site. The one we are going to use is type Y. This socket type is the most general-purpose. In common with all socket types, it has three pins devoted to power supply (+5V, +3.3V and GND). The remaining seven are general-purpose digital IO (Input/Output) pins, which can be configured as inputs to the mainboard or outputs from it. It’s important to note that these are designed to generate or accept 3.3V signals. If you put 5V into them, you may permanently damage the mainboard. In fact, it’s worth bearing in mind that using the breakout board plugged in to any socket is effectively giving you a direct electrical connection to the main board’s processor, and will not prevent you destroying it with the wrong voltages. Just remember to use the 3.3V line and not the 5V line in your projects.
In our test system, we are going to start by using one IO pin as an input connected to a switch, and one as an output connected to an LED. The circuit for the switch looks like this:
It’s pretty simple. A 10k resistor is connected between pin 4 and the 3.3V line. Pin 4 is thus normally high. The push-to-make switch connects pin 4 to ground; when the switch is closed, pin 4 is low. We are also going to connect an LED to one of the pins and configure that as an output. This circuit is even simpler (but remember that both are connected at the same time):
In fact, you don’t really need the resistor – most LEDs will cope with 3.3V – but it’s better to be safe than sorry. This way even if the LED is faulty it can’t draw enough current to damage the Gadgeteer main board. Building this on a breadboard looks something like this:
Using white wires doesn’t really stand out against the white breadboard, but I’m sure you get the idea of how simple it is. You may notice that I’m not using the Hydra mainboard. Just for a change, the mainboard is a Sytech Nano. it’s smaller and slightly less powerful than the Hydra, but for our purposes we can treat it exactly the same. With the circuit set up, the remaining part of the system is the software. Even this is not particularly complex.
Start up Visual Studio and create a new Gadgeteer project. If the system puts a Hydra board on the design surface for you, you can delete it and add a Nano board. Or you can use the Hydra, it doesn’t really make a difference. Now comes the interesting part. You can’t just drag a new component from the toolbox to represent the breakout board, because it isn’t actually a component – it’s just a fancy cable. The breakout board doesn’t have any functionality. That is provided by our circuits, and there are no predefined drivers for them. We have to write our own. This is of course exactly what the developers of every Gadgeteer module do. To make a module driver that you can add via the toolbox adds a little complexity, so we are going to settle for simply defining a module that we can use in our code. We are going to do this by creating a subclass of the existing class Gadgeteer.Modules.Module
. We’ll call it BreakoutModule
, because creativity is a rare talent. Here’s the code for our new class:
public class BreakoutModule: Gadgeteer.Modules.Module { public BreakoutModule(int socketNumber) { GT.Socket socket = GT.Socket.GetSocket( socketNumber, true, this, null); this.input1 = new GT.Interfaces.DigitalInput( socket, GT.Socket.Pin.Four, GT.Interfaces.GlitchFilterMode.On, GT.Interfaces.ResistorMode.PullUp, this); this.output1 = new GT.Interfaces.DigitalOutput( socket, GT.Socket.Pin.Nine, false, this ); } private GT.Interfaces.DigitalInput input1; private GT.Interfaces.DigitalOutput output1; public bool Input1 { get { return input1.Read(); } } public bool Output1 { set { output1.Write(value); } get { return output1.Read(); } } }
As you can see, the class defines a constructor, which takes as its single parameter the number of the socket into which it is plugged on the mainboard. It also exposes two properties: Input1, which is read-only, and returns the current state of the pin to which we have wired our button and Output1, which is connected to our LED. These are represented internally by objects of type DigitalInput
and DigitalOutput
respectively. Using these classes allows a layer of abstraction between our module and the actual hardware. We don’t need to know anything about the actual processor on the mainboard, and the mapping between physical pins and our objects is taken care of for us. All we have to do is some initialisation in the constructor. This uses the Gadgeteer.Socket
class (nothing to do with network sockets) to get a reference to the underlying connection, then uses it to instantiate the input and the output members.
The constructor for a DigitalInput
takes five parameters. the first two are a reference to the socket and the number of the pin to which we want to connect. The third parameter is the ‘Glitch Filter’ setting. This is important because mechanical switches and buttons have a habit of ‘bouncing’; that is, when you press them, they don’t always simply switch on. Sometimes, the mechanism swiches on, then off, then on. These extra state changes are usually fleeting, but can still confuse systems which don’t expect them. The glitch filter ‘debounces’ the switch, (probably) by requiring a minimum time between changes of state. This results in an input with much less noise on it. The slight loss of responsiveness is not usually noticeable in a manual switch application. the fourth parameter (Gadgeteer.Interfaces.ResistorMode
) lets the system know how the pin is connected electrically. We have built our circuit using a ‘pull-up’ resistor; that is, the pin is connected to +3.3V unless the button is pressed. The final parameter is simply a reference to our module.
The DigitalInput
constructor is simpler: apart from the socket reference, the pin number and the module reference, all that is required is the default state of the output. Setting it to false ensures that our LED is off when the system boots up.
Having defined the BreakoutModule
, we now need to create an instance of it in our program, and then write code to make use of it. Previously, this has been done for us by Visual Studio, and the resulting code has ended up in the Program.generated.cs file. We are doing it manually, however, and it’s not hard. Before we do, however, consider how we are going to use it. The BreakoutModule
provides us with a property which tells us the state of our push button. There are no events associated with this, so we need to poll it regularly and respond when it changes. To do this, we’ll use a GT.Timer. Ten times a second, we’ll check the button status, and if the button is pushed, we’ll invert the state of the LED. The upshot of this will be that while the button is pressed, the LED will flash. Here’s the code:
public partial class Program { BreakoutModule bm; void ProgramStarted() { bm = new BreakoutModule(3); GT.Timer updateTimer = new GT.Timer(100); updateTimer.Tick += new GT.Timer.TickEventHandler(updateTimer_Tick); updateTimer.Start(); } void updateTimer_Tick(GT.Timer timer) { if (!bm.Input1) bm.Output1 = !bm.Output1; else bm.Output1 = false; } }
As you can see, we define a BreakoutModule
object in the class, and then instantiate it in the ProgramStarted
method. It’s then really simple to get the value of its input and change the value of its output. I have posted an exciting video of the result on YouTube, if you really want to see it. Adding more inputs or outputs is a simple matter of modifying our custom module’s definition. With the ability to add your own electronics through this breakout interface, there’s really no limit to what can be done.
Next time, I’m going to look at some wireless communications for the Gadgeteer – but will it be WiFi, GPRS or Something Else? The excitement is almost detectable!