Giving your Arduino the ability to pretend to be a keyboard, mouse, or joystick opens up a whole world of possibilities because it means your Arduino can now interact with software that was never intended for automated control by a smart device. This could be desktop software, such as a game or a web browser. For example, your Arduino could “type” into a web form and submit it on your behalf, or act as a custom controller for a game.
You could also use an Arduino to connect a custom input device to your computer so that it is seen as a regular keyboard or joystick. The custom input device could be a chording keyboard, for example, or even something such as a virtual-reality glove or head tracking system that controls the cursor location in joystick mode. The required parts are shown in Figure 4-1, and the complete schematic is in Figure
4-2.
Parts Required
1 Arduino Duemilanove, Arduino Pro, Seeeduino, or equivalent
1 Prototyping shield
1 PCB-mount female USB “B” connector
1 USB A-to-B cable (commonly used as a printer cable)
2 3.6V Zener diodes (must be rated at 0.5W or less, 1W won’t work)
1 2.2K 0.25W or 0.5W resistor
2 68R 0.25W or 0.5W resistor
4 SPST push buttons (optional)
Source code available from www.practicalarduino.com/projects/virtual-usb-keyboard.
Instructions
Populate Prototyping Shield
While there’s not much that can go drastically wrong, before beginning construction you should consider using a USB hub to connect your virtual USB keyboard shield to your computer the first few times. Even though they have excellent internal protection already, we wouldn’t want a fault in the shield to fry a USB port in your expensive computer—much better to sacrifice a cheap USB hub.
There aren’t many parts in this project so the layout isn’t particularly critical. You can rearrange components to suit yourself if you have particular requirements for fitting it inside a case and still getting access to the USB connector.
Start by mounting the USB connector on one edge of the prototyping shield. It’s important to mount it in such a way that you will be able to plug the USB lead into the connector while it is mounted on the board without any other components getting in the way. PCB-mount USB connectors have two tabs sticking out of the body to provide physical stability since the force of plugging and unplugging a cable can be quite large, so it’s important to use the tabs to hold the socket in place rather than rely on the four tiny pins used for electrical connections. As a general principle it’s not a good idea to have mechanical support (“strain relief”) provided by signal-carrying electrical connections if you can avoid it, and many parts designed to be subjected to physical force provide mechanical mounts separate from the pins.
The tabs on the bottom of the USB socket are kinked so they can clip into appropriately sized holes, but that provides very weak support and we certainly wouldn’t rely on it. Instead we used a pair of pliers to straighten the tabs and then drilled two holes through the prototyping shield so they could slide down neatly with the four pins aligned with existing holes in the shield. Then, with the socket pushed hard against the shield, we bent the tabs inward and soldered them onto pads on the shield to give it a very strong physical mount that won’t budge when a USB cable is inserted or removed. Make sure you keep the tabs away from the pads used to connect the pins to prevent any short circuits.
Before adding any more parts to the shield it’s a good idea to use the USB cable to connect it to the computer and use a multimeter to verify the 0V and +5V connections on the socket pins. Then disconnect the USB lead and fit the 2K2 resistor linking the D– line (pin 2) to Arduino digital I/O pin 5. This allows the UsbKeyboard library to reset the USB connection under software control.
If you’re curious about how USB works, it can be interesting at this point to temporarily connect digital I/O pin 5 to the +5V pin and plug the shield back into the cable connected to your computer. If you watch the system log on your computer while you do it, you’ll discover that even the basic shield with nothing on it but the connector and one resistor will be identified by the computer as a low-speed USB device! Obviously it can’t actually send or receive data because there’s no intelligence in it yet, but it demonstrates that device presence detection is an electrical operation and has nothing to do with data flowing on the bus.
Disconnect the temporary connection between digital I/O pin 5 and +5V if you performed the previous experiment and proceed to fitting the 68R resistors that connect the D– and D+ USB data lines to the Arduino digital I/O lines. D– (USB pin 2) connects via one resistor to Arduino digital I/O pin 4, while D+ (USB pin 3) connects via the other resistor to digital I/O pin 2 (see Figure 4-3).
Note that the use of Arduino digital I/O pins 2 and 4 is hard-coded into the UsbKeyboard library itself and can’t be changed in your program. The use of digital I/O pin 2, in particular, is critical because the library relies on the “interrupt” associated with that pin to detect events on the USB connection.
Fit the 3.6V Zener diodes that link the D– (USB pin 2) and D+ (USB pin 3) USB data lines to ground, being careful with orientation of the diodes: the ends with the bands connect to the data lines with the other ends connected to ground.
The purpose of the Zener diodes may seem a bit cryptic at first, but they are absolutely critical to the operation of the circuit. The USB standard specifies that even though the power supply line is +5V, the communication lines themselves run at a nominal voltage of 3.3V. It’s also a little different to what you may have seen on other serial connections, such as RS232, which have “TX” (transmit) and “RX” (receive) lines. The D– and D+ lines are not independent TX/RX lines, as you may expect, but are actually what is known as a “half-duplex differential signalling pair.” This approach helps USB run at very high data rates by reducing the effect of electrical noise.
A Zener diode has a special property in that it “clamps” a voltage to around the same voltage level as the diode is specified for. In the forward direction it acts just like a normal diode, conducting with the common 0.6V drop. However, in the reverse direction, unlike a normal diode which is basically open circuit until it breaks down, the Zener diode will start conducting at its specified voltage. This is commonly called “clamping.”This makes them very useful for regulating and setting voltage levels in low-power circuits, and also invaluable as voltage reduction and protection devices.
In this circuit the Zener diodes clip the 5V supplied by the Arduino I/O pins down to the 3.3V USB standard.
But wait. Why use a 3.6V Zener to achieve a 3.3V limit?
That’s because, in this particular application, the electrical characteristics of the circuit mean that the voltage actually achieved will be a little below the rating on the Zener. Using a 3.6V Zener results in the voltage on the data lines ending up clipped to approximately the correct 3.3V level. Since the USB standard specifies that the voltage must be in the range of 2.8 to 3.6V, it doesn’t matter if we exceed 3.3V a little and it all works out just nicely.
One final word about the Zener diodes: power rating is critical, but not in the way you might expect. Power rating on components is based on how much energy they can safely dissipate before the magic smoke comes out, so most of the time it’s perfectly safe to overrate your parts and use a component with a higher rating than required for the particular circuit. However, in this case that approach can actually prevent the circuit from working because the trade-off in Zener diode design is that as its power rating increases it also exhibits more capacitance—not only will it behave like a Zener, but it will also behave like a tiny capacitor! For simple power-regulation requirements that’s just fine. However, in this case, the data lines need to change state very fast and any capacitance added to the line will effectively “damp” the data lines and prevent them from moving between low and high values fast enough. Capacitance on a high-speed data line is very bad and needs to be avoided or the circuit simply won’t work. In practice, a 1/4W Zener diode should work fine; a 1/2W Zener should work, but is a bit on the borderline; and a 1W Zener almost certainly won’t work—it will simply have too much capacitance.
Finding stock of through-hole (leaded) Zeners below 1W can be quite tricky now because many electronics shops only stock the 1W versions, but if you’re lucky you may find a shop with old stock of 1/4W or 1/2W diodes. If not, you may need to resort to using surface-mount diodes: SMD Zeners are commonly available in low power ratings. With a steady hand and a small soldering iron tip you should be able to solder them between adjacent pads on a prototyping shield without too much difficulty, particularly if you can find them in a larger package size such as 1206 or 0805.
Zener diodes are truly bizarre components with some very unusual characteristics. If you want to find out more about them, read the Wikipedia article at en.wikipedia.org/wiki/Zener_diode.
Finally, install jumper leads from the GND pin on the USB connector (pin 4) to the Arduino’s ground connection on the shield, and VCC (USB connector pin 1) to the Arduino’s +5V on the shield. This can’t be seen in the photograph of our prototype (see Figure 4-4) because the connection was made directly underneath the shield from one pad to another. The connection from USB VCC to Arduino +5V is optional, and if you are going to power your Arduino from some other power source you should leave it off. However, with that connection in place the Arduino can draw its power from the USB port on the host and doesn’t need any other connections at all. It allows you to just plug your USB shield/Arduino combination into a host using the socket on the shield and the Arduino will power up automatically.
Prepare the UsbKeyboard Library
The example sketch simply emulates a USB keyboard and reads the value of four digital input lines and sends characters to the host computer whenever one of the inputs is pulled low. On our shield we installed four PCB-mount push buttons that connect Arduino inputs 8, 9, 10, and 11 to ground when pressed, but you could just as easily use an external sensor such as the output from a motion detector to pull one of the inputs low and trigger transmission of characters.
The program relies on the UsbKeyboard Arduino library, created by Philip Lindsay, that incorporates a generic USB library created by Objective Development (www.obdev.at). The UsbKeyboard library is available for download from Lindsay’s site at code.rancidbacon.com/ProjectLogArduinoUSB.
Unfortunately, at the time of writing the library won’t compile under Arduino 0017 or 0018. You’ll need to download and install version 0016 (still available from the Arduino web site) for this project.
The library download is a compressed tarball called arduinousb_release_002.tar.gz. Download and extract it. Inside you’ll find a directory called libraries/UsbKeyboard. With Arduino 0017 and later you can install libraries inside the libraries directory inside your sketchbook, but that location doesn’t work with Arduino 0016 so instead you’ll need to move the UsbKeyboard directory into the libraries directory inside your actual Arduino 0016 installation.
Compile and Upload Sketch
The very first thing the sketch does is include the UsbKeyboard library.
#include "UsbKeyboard.h"
It then specifies which digital inputs to use for the four buttons. Note that the labels for the buttons could have been anything you like and doesn’t have any correlation to what characters might be sent when that button is pushed: we just used those names so it would be easy to remember what each one represents. The sketch also specifies a pin for a status LED at this point.
#define BUTTON_A 8
#define BUTTON_B 9
#define BUTTON_MSG 10
#define BUTTON_ENTER 11
byte ledPin = 13;
The setup function has to do a few different things, starting with setting up the status LED and setting the pins for the button connections to inputs, as follows.
void setup()
{
pinMode (ledPin, OUTPUT);
digitalWrite (ledPin, HIGH);
pinMode (BUTTON_A, INPUT);
pinMode (BUTTON_B, INPUT);
pinMode (BUTTON_MSG, INPUT);
pinMode (BUTTON_ENTER, INPUT);
To save some external components, it then enables the CPU’s internal pull-up resistors on the pins being used for the buttons. By doing this, we don’t need to connect external pull-up resistors, just the buttons themselves.
digitalWrite (BUTTON_A, HIGH);
digitalWrite (BUTTON_B, HIGH);
digitalWrite (BUTTON_MSG, HIGH);
digitalWrite (BUTTON_ENTER, HIGH);
Now for the USB setup. Because USB is extremely time-critical we need to mess with the interrupts a bit to ensure the Arduino will enumerate itself properly with the host computer. Notice that while forcing re-enumeration there is a 250 millisecond delay using a function called delayMs(), which is not a built-in Arduino function. Because timer0 has been disabled at that point in the code we can’t use functions like delay() and must define our own.
TIMSK0&=!(1<<TOIE0);
The sketch then clears the interrupt flags before it performs time-critical operations.
cli();
To make the host detect that the Arduino is present, the program uses functions in the UsbKeyboard library to disconnect and then reconnect. This forces it to be re-enumerated by the host.
usbDeviceDisconnect();
delayMs(250);
usbDeviceConnect();
The interrupts then need to be enabled again.
sei();
}
The main program loop is very simple and repetitive. It just loops as fast as possible and calls the update() function in the UsbKeyboard library each time through, then checks each of the digital inputs to see if any of them have been pulled low by a push button or other device connecting it to ground. If they have, it calls sendKeyStroke() to send an appropriate keypress event to the host.
void loop()
{
UsbKeyboard.update();
if (digitalRead(BUTTON_A) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_A);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_B) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_B);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_MSG) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_H, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_E);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_SPACE);
UsbKeyboard.sendKeyStroke(KEY_W, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_R);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_D);
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_ENTER) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
}
As you can see from the sequence sent when BUTTON_MSG is asserted you aren’t limited to sending one keypress event per input: you can also send sequences of characters.
Finally, the sketch defines its own delay function that can be used inside setup() while timer0 is disabled.
void delayMs(unsigned int ms)
{
for (int i = 0; i < ms; i++) {
delayMicroseconds(1000);
}
}
One thing to note about the example sketch is that it doesn’t introduce any delays inside the main program loop. It’s critical that the loop executes quickly so just about the only thing you can do inside the loop is read digital inputs: if you try to do anything that will slow down the program, the host computer may fail to get a response at a critical time and decide the device is misbehaving. If that happens the host will de-enumerate the device and your fake keyboard will stop working.
Once you’ve compiled the sketch and uploaded it to your Arduino, open a new document in a text editor or word processor on the host computer so it’s all ready for your Arduino to begin typing into it.
Then disconnect the USB lead from the normal USB socket on the Arduino board and plug it instead into the additional USB socket on the prototyping shield. This will cause the Arduino to power down and then power up again using power from the USB connection coming in via the shield. It will also attempt to enumerate itself with the host computer as a Human Interface Device (HID) so if you’re curious about what happens you could open the system log on your computer and watch it while plugging the USB cable into the shield.
If all goes well your Arduino will now behave like an extra keyboard plugged into your computer, so try pressing one of the buttons or connecting one of the inputs to ground to trigger a keypress event. You should see corresponding characters appear in the text document on your computer—and, as far as it knows, that’s just you typing the letters on a regular keyboard!
For a more complete list of available characters supported by the library, have a look in the library header file (hardware/libraries/UsbKeyboard/UsbKeyboard.h) using a text editor.
For even more information have a look at the USB HID to PS/2 scan code translation table document published by Microsoft at download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf. Note that although the letters are defined using uppercase names such as KEY_J for the letter j, the character sent through will actually be the lowercase version unless you
apply a modifier to the key. The modifier is sent through as a second parameter to the sendKeyStroke() function, so to send an uppercase J you would call the following:
sendKeyStroke(KEY_J, MOD_SHIFT_LEFT);
The example program includes a couple of modifiers so you can see it in action.
Just for fun, we mapped the four buttons to characters used to control the game Frozen Bubble (left, right, fire, and centre) so we could use an Arduino as a custom game controller (see Figure 4-5).
We also connected a Nintendo DS touch screen to another Arduino as described in the Touch Control Panel project in Chapter 8 and ran a program that defined different regions on the screen for left, right, and fire. We then linked the two Arduinos together with the digital outputs from the touch screen Arduino connected to digital inputs on the USB keyboard Arduino to give touch screen control of Frozen Bubble. There are videos of both systems in action on the Practical Arduino site.
Variations
Chording Keyboard
A chording keyboard is a special type of input device that has a very small number of buttons compared to the number of characters it can send: rather than having one button for each letter of the alphabet like a normal keyboard, a chording keyboard allows you to press a combination (“chord”) of keys at once to select the letter you want. A five-button chording keyboard can be comfortably held in a one-handed grip and allow the operator to type a huge number of different characters by simultaneously pressing several keys, with the added advantage that it doesn’t need to be placed on a surface to be used: you can even walk around with a chording keyboard in one hand and type while on the move.
You could also use an Arduino to connect a custom input device to your computer so that it is seen as a regular keyboard or joystick. The custom input device could be a chording keyboard, for example, or even something such as a virtual-reality glove or head tracking system that controls the cursor location in joystick mode. The required parts are shown in Figure 4-1, and the complete schematic is in Figure
4-2.
Parts Required
1 Arduino Duemilanove, Arduino Pro, Seeeduino, or equivalent
1 Prototyping shield
1 PCB-mount female USB “B” connector
1 USB A-to-B cable (commonly used as a printer cable)
2 3.6V Zener diodes (must be rated at 0.5W or less, 1W won’t work)
1 2.2K 0.25W or 0.5W resistor
2 68R 0.25W or 0.5W resistor
4 SPST push buttons (optional)
Source code available from www.practicalarduino.com/projects/virtual-usb-keyboard.
Instructions
Populate Prototyping Shield
While there’s not much that can go drastically wrong, before beginning construction you should consider using a USB hub to connect your virtual USB keyboard shield to your computer the first few times. Even though they have excellent internal protection already, we wouldn’t want a fault in the shield to fry a USB port in your expensive computer—much better to sacrifice a cheap USB hub.
There aren’t many parts in this project so the layout isn’t particularly critical. You can rearrange components to suit yourself if you have particular requirements for fitting it inside a case and still getting access to the USB connector.
Start by mounting the USB connector on one edge of the prototyping shield. It’s important to mount it in such a way that you will be able to plug the USB lead into the connector while it is mounted on the board without any other components getting in the way. PCB-mount USB connectors have two tabs sticking out of the body to provide physical stability since the force of plugging and unplugging a cable can be quite large, so it’s important to use the tabs to hold the socket in place rather than rely on the four tiny pins used for electrical connections. As a general principle it’s not a good idea to have mechanical support (“strain relief”) provided by signal-carrying electrical connections if you can avoid it, and many parts designed to be subjected to physical force provide mechanical mounts separate from the pins.
The tabs on the bottom of the USB socket are kinked so they can clip into appropriately sized holes, but that provides very weak support and we certainly wouldn’t rely on it. Instead we used a pair of pliers to straighten the tabs and then drilled two holes through the prototyping shield so they could slide down neatly with the four pins aligned with existing holes in the shield. Then, with the socket pushed hard against the shield, we bent the tabs inward and soldered them onto pads on the shield to give it a very strong physical mount that won’t budge when a USB cable is inserted or removed. Make sure you keep the tabs away from the pads used to connect the pins to prevent any short circuits.
Before adding any more parts to the shield it’s a good idea to use the USB cable to connect it to the computer and use a multimeter to verify the 0V and +5V connections on the socket pins. Then disconnect the USB lead and fit the 2K2 resistor linking the D– line (pin 2) to Arduino digital I/O pin 5. This allows the UsbKeyboard library to reset the USB connection under software control.
If you’re curious about how USB works, it can be interesting at this point to temporarily connect digital I/O pin 5 to the +5V pin and plug the shield back into the cable connected to your computer. If you watch the system log on your computer while you do it, you’ll discover that even the basic shield with nothing on it but the connector and one resistor will be identified by the computer as a low-speed USB device! Obviously it can’t actually send or receive data because there’s no intelligence in it yet, but it demonstrates that device presence detection is an electrical operation and has nothing to do with data flowing on the bus.
Disconnect the temporary connection between digital I/O pin 5 and +5V if you performed the previous experiment and proceed to fitting the 68R resistors that connect the D– and D+ USB data lines to the Arduino digital I/O lines. D– (USB pin 2) connects via one resistor to Arduino digital I/O pin 4, while D+ (USB pin 3) connects via the other resistor to digital I/O pin 2 (see Figure 4-3).
Note that the use of Arduino digital I/O pins 2 and 4 is hard-coded into the UsbKeyboard library itself and can’t be changed in your program. The use of digital I/O pin 2, in particular, is critical because the library relies on the “interrupt” associated with that pin to detect events on the USB connection.
Fit the 3.6V Zener diodes that link the D– (USB pin 2) and D+ (USB pin 3) USB data lines to ground, being careful with orientation of the diodes: the ends with the bands connect to the data lines with the other ends connected to ground.
The purpose of the Zener diodes may seem a bit cryptic at first, but they are absolutely critical to the operation of the circuit. The USB standard specifies that even though the power supply line is +5V, the communication lines themselves run at a nominal voltage of 3.3V. It’s also a little different to what you may have seen on other serial connections, such as RS232, which have “TX” (transmit) and “RX” (receive) lines. The D– and D+ lines are not independent TX/RX lines, as you may expect, but are actually what is known as a “half-duplex differential signalling pair.” This approach helps USB run at very high data rates by reducing the effect of electrical noise.
A Zener diode has a special property in that it “clamps” a voltage to around the same voltage level as the diode is specified for. In the forward direction it acts just like a normal diode, conducting with the common 0.6V drop. However, in the reverse direction, unlike a normal diode which is basically open circuit until it breaks down, the Zener diode will start conducting at its specified voltage. This is commonly called “clamping.”This makes them very useful for regulating and setting voltage levels in low-power circuits, and also invaluable as voltage reduction and protection devices.
In this circuit the Zener diodes clip the 5V supplied by the Arduino I/O pins down to the 3.3V USB standard.
But wait. Why use a 3.6V Zener to achieve a 3.3V limit?
That’s because, in this particular application, the electrical characteristics of the circuit mean that the voltage actually achieved will be a little below the rating on the Zener. Using a 3.6V Zener results in the voltage on the data lines ending up clipped to approximately the correct 3.3V level. Since the USB standard specifies that the voltage must be in the range of 2.8 to 3.6V, it doesn’t matter if we exceed 3.3V a little and it all works out just nicely.
One final word about the Zener diodes: power rating is critical, but not in the way you might expect. Power rating on components is based on how much energy they can safely dissipate before the magic smoke comes out, so most of the time it’s perfectly safe to overrate your parts and use a component with a higher rating than required for the particular circuit. However, in this case that approach can actually prevent the circuit from working because the trade-off in Zener diode design is that as its power rating increases it also exhibits more capacitance—not only will it behave like a Zener, but it will also behave like a tiny capacitor! For simple power-regulation requirements that’s just fine. However, in this case, the data lines need to change state very fast and any capacitance added to the line will effectively “damp” the data lines and prevent them from moving between low and high values fast enough. Capacitance on a high-speed data line is very bad and needs to be avoided or the circuit simply won’t work. In practice, a 1/4W Zener diode should work fine; a 1/2W Zener should work, but is a bit on the borderline; and a 1W Zener almost certainly won’t work—it will simply have too much capacitance.
Finding stock of through-hole (leaded) Zeners below 1W can be quite tricky now because many electronics shops only stock the 1W versions, but if you’re lucky you may find a shop with old stock of 1/4W or 1/2W diodes. If not, you may need to resort to using surface-mount diodes: SMD Zeners are commonly available in low power ratings. With a steady hand and a small soldering iron tip you should be able to solder them between adjacent pads on a prototyping shield without too much difficulty, particularly if you can find them in a larger package size such as 1206 or 0805.
Zener diodes are truly bizarre components with some very unusual characteristics. If you want to find out more about them, read the Wikipedia article at en.wikipedia.org/wiki/Zener_diode.
Finally, install jumper leads from the GND pin on the USB connector (pin 4) to the Arduino’s ground connection on the shield, and VCC (USB connector pin 1) to the Arduino’s +5V on the shield. This can’t be seen in the photograph of our prototype (see Figure 4-4) because the connection was made directly underneath the shield from one pad to another. The connection from USB VCC to Arduino +5V is optional, and if you are going to power your Arduino from some other power source you should leave it off. However, with that connection in place the Arduino can draw its power from the USB port on the host and doesn’t need any other connections at all. It allows you to just plug your USB shield/Arduino combination into a host using the socket on the shield and the Arduino will power up automatically.
Prepare the UsbKeyboard Library
The example sketch simply emulates a USB keyboard and reads the value of four digital input lines and sends characters to the host computer whenever one of the inputs is pulled low. On our shield we installed four PCB-mount push buttons that connect Arduino inputs 8, 9, 10, and 11 to ground when pressed, but you could just as easily use an external sensor such as the output from a motion detector to pull one of the inputs low and trigger transmission of characters.
The program relies on the UsbKeyboard Arduino library, created by Philip Lindsay, that incorporates a generic USB library created by Objective Development (www.obdev.at). The UsbKeyboard library is available for download from Lindsay’s site at code.rancidbacon.com/ProjectLogArduinoUSB.
Unfortunately, at the time of writing the library won’t compile under Arduino 0017 or 0018. You’ll need to download and install version 0016 (still available from the Arduino web site) for this project.
The library download is a compressed tarball called arduinousb_release_002.tar.gz. Download and extract it. Inside you’ll find a directory called libraries/UsbKeyboard. With Arduino 0017 and later you can install libraries inside the libraries directory inside your sketchbook, but that location doesn’t work with Arduino 0016 so instead you’ll need to move the UsbKeyboard directory into the libraries directory inside your actual Arduino 0016 installation.
Compile and Upload Sketch
The very first thing the sketch does is include the UsbKeyboard library.
#include "UsbKeyboard.h"
It then specifies which digital inputs to use for the four buttons. Note that the labels for the buttons could have been anything you like and doesn’t have any correlation to what characters might be sent when that button is pushed: we just used those names so it would be easy to remember what each one represents. The sketch also specifies a pin for a status LED at this point.
#define BUTTON_A 8
#define BUTTON_B 9
#define BUTTON_MSG 10
#define BUTTON_ENTER 11
byte ledPin = 13;
The setup function has to do a few different things, starting with setting up the status LED and setting the pins for the button connections to inputs, as follows.
void setup()
{
pinMode (ledPin, OUTPUT);
digitalWrite (ledPin, HIGH);
pinMode (BUTTON_A, INPUT);
pinMode (BUTTON_B, INPUT);
pinMode (BUTTON_MSG, INPUT);
pinMode (BUTTON_ENTER, INPUT);
To save some external components, it then enables the CPU’s internal pull-up resistors on the pins being used for the buttons. By doing this, we don’t need to connect external pull-up resistors, just the buttons themselves.
digitalWrite (BUTTON_A, HIGH);
digitalWrite (BUTTON_B, HIGH);
digitalWrite (BUTTON_MSG, HIGH);
digitalWrite (BUTTON_ENTER, HIGH);
Now for the USB setup. Because USB is extremely time-critical we need to mess with the interrupts a bit to ensure the Arduino will enumerate itself properly with the host computer. Notice that while forcing re-enumeration there is a 250 millisecond delay using a function called delayMs(), which is not a built-in Arduino function. Because timer0 has been disabled at that point in the code we can’t use functions like delay() and must define our own.
TIMSK0&=!(1<<TOIE0);
The sketch then clears the interrupt flags before it performs time-critical operations.
cli();
To make the host detect that the Arduino is present, the program uses functions in the UsbKeyboard library to disconnect and then reconnect. This forces it to be re-enumerated by the host.
usbDeviceDisconnect();
delayMs(250);
usbDeviceConnect();
The interrupts then need to be enabled again.
sei();
}
The main program loop is very simple and repetitive. It just loops as fast as possible and calls the update() function in the UsbKeyboard library each time through, then checks each of the digital inputs to see if any of them have been pulled low by a push button or other device connecting it to ground. If they have, it calls sendKeyStroke() to send an appropriate keypress event to the host.
void loop()
{
UsbKeyboard.update();
if (digitalRead(BUTTON_A) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_A);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_B) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_B);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_MSG) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_H, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_E);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_SPACE);
UsbKeyboard.sendKeyStroke(KEY_W, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_R);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_D);
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_ENTER) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
}
As you can see from the sequence sent when BUTTON_MSG is asserted you aren’t limited to sending one keypress event per input: you can also send sequences of characters.
Finally, the sketch defines its own delay function that can be used inside setup() while timer0 is disabled.
void delayMs(unsigned int ms)
{
for (int i = 0; i < ms; i++) {
delayMicroseconds(1000);
}
}
One thing to note about the example sketch is that it doesn’t introduce any delays inside the main program loop. It’s critical that the loop executes quickly so just about the only thing you can do inside the loop is read digital inputs: if you try to do anything that will slow down the program, the host computer may fail to get a response at a critical time and decide the device is misbehaving. If that happens the host will de-enumerate the device and your fake keyboard will stop working.
Once you’ve compiled the sketch and uploaded it to your Arduino, open a new document in a text editor or word processor on the host computer so it’s all ready for your Arduino to begin typing into it.
Then disconnect the USB lead from the normal USB socket on the Arduino board and plug it instead into the additional USB socket on the prototyping shield. This will cause the Arduino to power down and then power up again using power from the USB connection coming in via the shield. It will also attempt to enumerate itself with the host computer as a Human Interface Device (HID) so if you’re curious about what happens you could open the system log on your computer and watch it while plugging the USB cable into the shield.
If all goes well your Arduino will now behave like an extra keyboard plugged into your computer, so try pressing one of the buttons or connecting one of the inputs to ground to trigger a keypress event. You should see corresponding characters appear in the text document on your computer—and, as far as it knows, that’s just you typing the letters on a regular keyboard!
For a more complete list of available characters supported by the library, have a look in the library header file (hardware/libraries/UsbKeyboard/UsbKeyboard.h) using a text editor.
For even more information have a look at the USB HID to PS/2 scan code translation table document published by Microsoft at download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf. Note that although the letters are defined using uppercase names such as KEY_J for the letter j, the character sent through will actually be the lowercase version unless you
apply a modifier to the key. The modifier is sent through as a second parameter to the sendKeyStroke() function, so to send an uppercase J you would call the following:
sendKeyStroke(KEY_J, MOD_SHIFT_LEFT);
The example program includes a couple of modifiers so you can see it in action.
Just for fun, we mapped the four buttons to characters used to control the game Frozen Bubble (left, right, fire, and centre) so we could use an Arduino as a custom game controller (see Figure 4-5).
Pin Name Cable Color Description
1 VCC Red +5 VDC
2 D– White Data –
3 D+ Green Data +
1 VCC Red +5 VDC
2 D– White Data –
3 D+ Green Data +
We also connected a Nintendo DS touch screen to another Arduino as described in the Touch Control Panel project in Chapter 8 and ran a program that defined different regions on the screen for left, right, and fire. We then linked the two Arduinos together with the digital outputs from the touch screen Arduino connected to digital inputs on the USB keyboard Arduino to give touch screen control of Frozen Bubble. There are videos of both systems in action on the Practical Arduino site.
Variations
Chording Keyboard
A chording keyboard is a special type of input device that has a very small number of buttons compared to the number of characters it can send: rather than having one button for each letter of the alphabet like a normal keyboard, a chording keyboard allows you to press a combination (“chord”) of keys at once to select the letter you want. A five-button chording keyboard can be comfortably held in a one-handed grip and allow the operator to type a huge number of different characters by simultaneously pressing several keys, with the added advantage that it doesn’t need to be placed on a surface to be used: you can even walk around with a chording keyboard in one hand and type while on the move.
No comments:
Post a Comment