Teensy++ as USB HID (Human Interface Device)

UPDATED: Arduino's Keyboard Examples don't compile for the Teensy without modification. I blamed Arduino for this, but it's more a question of changing specs that didn't change at the same time on the Teensy.

One really nice thing about the Arduino IDE is it comes with heaps of example code. "Menu > File > Examples > ..." Last time, I was playing with "File > Examples > 01.Basics > Blink" which simply turns the Teensy++'s onboard LED on and off. I modified the loop so that the light blinked slower and slower - not difficult, but it showed that my code was going to the device and doing what I meant it to.

So tonight I wanted to establish that the Teensy++ could in fact act as a USB keyboard. Guess what - they have an example for that. Here's their original code:

/*
 Keyboard Message test

 For the Arduino Leonardo and Micro.

 Sends a text string when a button is pressed.

 The circuit:
 * pushbutton attached from pin 4 to +5V
 * 10-kilohm resistor attached from pin 4 to ground

 created 24 Oct 2011
 modified 27 Mar 2012
 by Tom Igoe
 modified 11 Nov 2013
 by Scott Fitzgerald

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/KeyboardMessage
 */

#include "Keyboard.h"

const int buttonPin = 4;          // input pin for pushbutton
int previousButtonState = HIGH;   // for checking the state of a pushButton
int counter = 0;                  // button push counter

void setup() {
  // make the pushButton pin an input:
  pinMode(buttonPin, INPUT);
  // initialize control over the keyboard:
  Keyboard.begin();
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(buttonPin);
  // if the button state has changed,
  if ((buttonState != previousButtonState)
      // and it's currently pressed:
      && (buttonState == HIGH)) {
    // increment the button counter
    counter++;
    // type out a message
    Keyboard.print("You pressed the button ");
    Keyboard.print(counter);
    Keyboard.println(" times.");
  }
  // save the current button state for comparison next time:
  previousButtonState = buttonState;
}

I looked this code over, and I thought "We don't need no stinkin' resistors." I mean, you really don't: the Arduinos have a built-in pull-up resistor for every digital I/O pin that you can activate or not, at your choice. And ... what the hell is a switch but a piece of wire touching - or not touching - a metal pad? So really, we don't need that either - a jumper wire will do fine. (I have NO electronic components around my house right at the moment. Necessity is the mother of invention and all that.) So I rewrote it (I've removed the long lead-in comment for brevity):

#include "Keyboard.h"

const int buttonPin = 17;         // input pin for pushbutton
int previousButtonState = LOW;    // for checking the state of a pushButton
int counter = 0;                  // button push counter

void setup() {
  // make the pushButton pin an input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize control over the keyboard:
  Keyboard.begin();
}

void loop() {
  // read the pushbutton:
  int buttonState = digitalRead(buttonPin);
  // if the button state has changed,
  if ((buttonState != previousButtonState)
      // and it's currently pressed:
      && (buttonState == LOW)) {
    // increment the button counter
    counter++;
    // type out a message
    Keyboard.print("You pressed the button ");
    Keyboard.print(counter);
    Keyboard.println(" times.");
  }
  // save the current button state for comparison next time:
  previousButtonState = buttonState;
}

If you try to compile this code, you'll get this:

In file included from /home/giles/arduino-1.8.2/examples/09.USB/Keyboard/KeyboardMessage/KeyboardMessage.ino:23:0:
/home/giles/arduino-1.8.2/libraries/Keyboard/src/Keyboard.h:25:17: fatal error: HID.h: No such file or directory
#include "HID.h"
                ^
compilation terminated.
Error compiling for board Teensy++ 2.0.

WTF? Your code won't compile out of the box?! (I confirmed - the unmodified code won't compile.) You didn't provide a header file?? Some research suggested that Keyboard.h "isn't needed anymore." That seemed to be the consensus. I'm building a keyboard emulator and I should comment out the keyboard header? <shrug> Well, at least it's easy to test ... and yes, without that it compiles. [This is a difference between standard Arduino and Teensy code: the Teensy version shouldn't have "Keydoard.h".] Then upload it to the Teensy++, wrap a jumper wire around the centre GND pin right next to the button, and touch the other end of the jumper to pin 17 ("C7") and voila, I received the very exciting message in my terminal "Frg lp.oo.e yd. xgyyrb 1 ycm.ov". Touch it again, and I get "Frg lp.oo.e yd. xgyyrb 2 ycm.ov"! That's fantastic!

No, I haven't decided to randomly print trash as my text messages: I didn't account for my in-computer Dvorak keyboard layout translation. Send a "Y" from your keyboard to a computer that's set for Dvorak, and you'll get a "F". "You" transates to "Frg" - so it's working fine. After a few more presses and turning off my Dvorak keyboard, I pressed it one more time and got "You pressed the button 14 times."

The final fully-stripped code (with added 100 ms LED blink on button press):

const int buttonPin = 17;
int previousButtonState = LOW;
int counter = 0;
int led = 6;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Keyboard.begin();
}

void loop() {
  int buttonState = digitalRead(buttonPin);
  if ((buttonState != previousButtonState)
      && (buttonState == LOW)) {
    counter++;
    Keyboard.print("You pressed the button ");
    Keyboard.print(counter);
    Keyboard.println(" times.");

    digitalWrite(led, HIGH);
    delay(100);
    digitalWrite(led, LOW);
  }
  previousButtonState = buttonState;
}

photo: Teensy++ 2.0 with a "switch" from ground to C7 aka 'pin 17'

Teensy++ 2.0 with a wire wrapped through GND and the other end ready to touch C7

I filed a bug report at https://github.com/arduino/Arduino/issues : 'Keyboard.h' appears in all keyboard examples, and 'Mouse.h' appears in all mouse examples - and it causes an essentially identical error. They responded (politely, I'm pleased to say) that the example code is fine for Arduino, and the problem is that the example wasn't updated for Teensy - so I'm thinking about filing a bug report with PJRC ...