It always starts with a prototype...
Some days ago, while crafting a quick prototype on top of an Arduino Leonardo board, we noticed that the USB HID emulation part of the core (Arduino) libraries was always enabled.
In short, the above means that:
- Whether you need to use the USD HID emulation or not, the related code is always compiled and "active", wasting some of your precious Flash memory.
- The Arduino core libraries will always report the same hardwired USB HID report descriptor to the USB host.
- The hardwired HID report descriptor identifies the Leonardo itself as both a mouse and a keyboard.
For our prototype, the described behaviour was problematic, not because of the wasted resources, but because the host, an Nexus 7 Android tablet, was recognizing the Leonardo as a keyboard capable USB device. To our surprise, when Android detects an external USB keyboard, it disables by default its own virtual keyboard (!). The result is a quite unusable tablet.
As there is no officially documented way to disable the USB HID keyboard emulation in Arduino, I made a quick patch to the core libraries. Hurrah for the open source software!
What is the purpose of the USB HID report descriptor?
According to the official USB documentation "Device Class Definition for Human Interface Devices (HID) Version 1.11",
A Report descriptor describes each piece of data that the device generates and what the data is actually measuring.
The report descriptor itself is a compact list (array) of bytes, which the USB device sends to the USB host during the enumeration process. Unless you really need to work at very low level or want to create your own custom HID peripheral, you should no require to understand or modify the individual bytes composing the descriptor.
In case you need to know about the very specific meaning of each item in the report descriptor, don't be afraid to go straight to the source, the official USB HID documentation. It is openly available (check the URL below) and easier to understand that you may have initially thought. In contrast with other standards documentation, it is a fairly short and well written document.
Disabling a section of the report descriptor
This patch works with the Arduino core libraries included with the latest version of the software, 1.6.5-r3 at the time of this writing. However, it should work also with older versions (1.5.8 or higher).
We will be slightly modifying a single file from the core libraries, Hid.cpp
, located under Arduino/hardware/arduino/avr/cores/arduino/
. If you open this file with a text editor, you will notice a constant array of bytes named _hidReportDescriptor
, which is where the actual USB HID report descriptor is defined.
The array has three sections, each one of them starting with a comment, corresponding to Mouse, Keyboard and raw data.
const u8 _hidReportDescriptor[] = {
// Mouse
[...some lines omitted...]
// Keyboard
[...some lines omitted...]
#ifdef RAWHID_ENABLED
// RAW HID
[...some lines omitted...]
#endif
};
In the code above, you can clearly see that the Raw section can be enabled by defining the macro constant RAWHID_ENABLED
. If you check the beginning of the HID.cpp
file, you can read:
//#define RAWHID_ENABLED
Which means that, by default, your Arduino core library does not define the constant RAWID_ENABLED
.
You can check the original Arduino file here.
Hands on making the actual changes
Under OS X, if you installed the Arduino software as a bundled (packaged) application, you can find the file HID.cpp
by executing these steps:
- Open a Finder and go to your Applications folder.
- Right click on the file
Arduino.app
and click on "Show Package Contents". - Navigate to
Contents/Java/hardware/arduino/avr/cores/arduino/HID.cpp
Under Windows, the path should look similar to
...\arduino-1.6.0\Arduino\hardware\arduino\avr\cores\arduino\HID.cpp
Now we are ready to make the appropriate changes to the HID.cpp
file. First, we will add a pair of #ifdef
/#endif
delimiters around the Keyboard section of the constant, like this:
const u8 _hidReportDescriptor[] = {
// Mouse
[...some lines omitted...]
#ifdef KEYBOARD_ENABLED
// Keyboard
[...some lines omitted...]
#endif
#ifdef RAWHID_ENABLED
// RAW HID
[...some lines omitted...]
#endif
};
Then, we will add a line near the top of the file, undefining the macro constant KEYBOARD_ENABLED
. With this we are effectively disabling the USB HID Keyboard emulation of our Arduino.
//#define KEYBOARD_ENABLED
You can check the updated file at our github. To enable the keyboard again, you just need to uncomment this last line.
Beware that:
- You will have to modify the file
HID.cpp
each time that you want to change the USB HID behaviour and before you compile your Arduino code. - It is very easy to forget about this file and the changes you made to it and then, starting a new USB HID Arduino project and not getting the results you were expecting...
The Arduino community comes to the rescue
You can bet many other Arduino users were annoyed with this issue: USB HID emulation not being easily configurable from your sketch code.
When I was pushing the patch above to the official Arduino code base, I was informed by the community developers that a new pluggable USB emulation component was in development. It is scheduled (hopefully) for the next-to-come version 1.6.6.
In the meantime, you can check how the issue is being worked out at the Arduino project github... and even premier the new code!
In the future version, should you want to enable only the Mouse USB HID emulation, for example, you will simply add at the beginning of your sketch,
#include "Mouse.h"
#include "HID.h"
To know more
You can check out some useful references:
- USB human interface device class, at the wikipedia.
- A typical sequence of the HID report descriptor of a mouse, dissected and explained in depth, byte by byte.
- Another tutorial about USB HID report descriptors, by Frank Zhao.
- The USB HID technical documentation page, by the USB Implementers Forum.
- "Device Class Definition for Human Interface Devices (HID) Version 1.11", by the USB Implementers Forum.
- "HID Usage Tables 1.12 docu", defines constants that can be interpreted by an application to identify the purpose and meaning of a data field in a HID report descriptor.
If you liked this content and don't want to miss future articles, you can subscribe to our mailing list and get notified!