+++ template = "article.html" title = "Arduino Leonardo fully-featured keyboard" date = 2014-05-04T23:03:16+02:00 description = "Building a fully-featured keyboard emulator with Arduino Leonardo, including support for modifier keys and special characters." [taxonomies] tags = ["arduino"] +++ The Leonardo has a simple [keyboard API](http://arduino.cc/en/Reference/MouseKeyboard). I needed a way to emulate a keyboard (from a joystick and arcade buttons - you see where I'm going now). Here's how I did it. ## First try Starting with an [Arduino sample](http://arduino.cc/en/Tutorial/KeyboardAndMouseControl), we can make a first attempt. The circuit is the same as the sample - simply adjust the pins to your needs. It won't need any change until the end of this post. _Basic keyboard_ ```cpp const int upButton = 2; const int downButton = 3; const int leftButton = 4; const int rightButton = 5; void setup() { pinMode(upButton, INPUT); pinMode(downButton, INPUT); pinMode(leftButton, INPUT); pinMode(rightButton, INPUT); pinMode(mouseButton, INPUT); Keyboard.begin(); } void loop() { if (digitalRead(upButton) == HIGH) { Keyboard.write(KEY_UP_ARROW); } if (digitalRead(downButton) == HIGH) { Keyboard.write(KEY_DOWN_ARROW); } if (digitalRead(leftButton) == HIGH) { Keyboard.write(KEY_LEFT_ARROW); } if (digitalRead(rightButton) == HIGH) { Keyboard.write(KEY_RIGHT_ARROW); } } ``` This however has a major issue. Each `Keyboard.write()` call generates a press/release cycle. If you keep a button pushed, instead of a single, long key press, the computer will receive a ton of press/release events. We need to keep the buttons states between `loop()` calls. ## Adding memory to the keyboard Here's a second attempt, with two modifications. First, to ease the addition/removal of a button, the code uses arrays instead of doing all steps four times. Second thing changed: each button now remember its state. _Stateful keyboard_ ```cpp // Number of buttons to handle const int buttonsCount = 4; // Arduino PINs to use const int pins[buttonsCount] = { 2, 3, 4, 5 }; // Keys to send (order has to match the pins array) const byte keys[buttonsCount] = { KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW }; bool status[buttonsCount] = {LOW}; void setup() { for (int i = 0; i < buttonsCount; ++i) { pinMode(pins[i], INPUT); } Keyboard.begin(); } void loop() { for (int i = 0; i < buttonsCount; ++i) { const int pinStatus = digitalRead(pins[i]); if (pinStatus != status[i]) { status[i] = pinStatus; if (pinStatus == HIGH) { Keyboard.press(keys[i]); } else { Keyboard.release(keys[i]); } } } } ``` So… the keyboard now remembers which buttons are pressed, and should generate a single couple of events for each button press/release. _Should_. There's still an issue: mechanical buttons are not perfect. Many events are still generated. This is due to a phenomenon called [bounce](http://en.wikipedia.org/wiki/Switch#Contact_bounce). ## Debouncing the keyboard A simple way to debounce a button is, well, really simple: ignore all changes to the state of the button during a short delay after an initial change. While it's not the most precise way and could be problematic in a more complex scenario, it's perfectly fine to do this for a keyboard, given we keep this delay short enough. Let's throw in an array to remember the last event acknowledged by the keyboard: _Debounced keyboard_ ```cpp // Number of buttons to handle const int buttonsCount = 4; // Arduino PINs to use const int pins[buttonsCount] = { 2, 3, 4, 5 }; // Keys to send (order has to match the pins array) const byte keys[buttonsCount] = { KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW }; // Debounce delay const long debounceDelay = 50; bool status[buttonsCount] = {LOW}; long lastDebounces[buttonsCount] = {0}; void setup() { for (int i = 0; i < buttonsCount; ++i) { pinMode(pins[i], INPUT); } Keyboard.begin(); } void loop() { for (int i = 0; i < buttonsCount; ++i) { const int pinStatus = digitalRead(pins[i]); if (pinStatus != status[i] && millis() - debounceDelay > lastDebounces[i]) { status[i] = pinStatus; if (pinStatus == HIGH) { Keyboard.press(keys[i]); } else { Keyboard.release(keys[i]); } lastDebounces[buttonNumber] = millis(); } } } ``` You'll maybe need to adjust the debounce delay according to your buttons. Try to keep it as short as possible. ## Conclusion And _voilà_! We now have a fully functional keyboard, to which it's easy to add/remove/change buttons. There's still room for improvement: it would be easy to allow it to send key sequences instead of single key presses, for example. You can find the full code on [GitHub](https://github.com/Kernald/gameduino).