It is indeed possible and also without the requirement to modify the Arduino environment itself. Just register an HID descriptor for the "report ID" for "Consumer Control" (the code is 4 (constant kHID_ReportID_ConsumerControl
), where the "report ID" code for keyboard and mouse are 2 and 1, respectively):
static HIDSubDescriptor node(HID_ReportDescriptor_ConsumerControl,
sizeof(HID_ReportDescriptor_ConsumerControl));
HID().AppendDescriptor(&node);
After the registration, HID().SendReport()
now works for "report ID" equal to 4 (the first parameter), and the audio volume can be changed, etc.
The registration needs to done once and can be put into the init() function, for instance, after the Keyboard.begin();
and Mouse.begin();
calls.
Usage
Example usage, increasing the audio volume by one step:
uint8_t data[2];
data[0] = kVolumeUp;
data[1] = 0;
HID().SendReport(kHID_ReportID_ConsumerControl, data, 2);
The constants are at the end.
Notes
"Consumer Control" has the multimedia controls Volume up, Volume down, Mute, Play, etc.
The registration is similar to the way the constructors for the built-in keyboard and mouse libraries do it. HID_ReportDescriptor_ConsumerControl
and constants are listed at the end because it is quite long, but they must come first (for instance, in the beginning of the file).
All this could be packaged into a reusable form. Here I have demonstrated it in a paste-and-forget form to keep it short.
HID().SendReport()
looks strange, but HID()
is a global function that returns an instance of the class HID_
(all built into the standard Arduino libraries, presumably to avoid some initialisation order problems with global/static objects - I thought Scott Meyers had solved that a long time ago (47. in the first book)).
HID().SendReport()
can also be used to circumvent the limitation of a presumption of a US keyboard. E.g. using code 100, page 56 (and kHID_ReportID_Keyboard, like in the standard class keyboard_
) to emulate the 102th key on European keyboards (e.g. a Spanish keyboard).
Only volume up and volume down were tested.
Definition of HID_ReportDescriptor_ConsumerControl
and constants:
// For direct use of USB HID (yes, 3 is left out)
const uint8_t kHID_ReportID_Mouse = 1;
const uint8_t kHID_ReportID_Keyboard = 2;
const uint8_t kHID_ReportID_ConsumerControl = 4;
const uint8_t kHID_ReportID_SystemControl = 5;
const uint8_t kHID_ReportID_Gamepad = 6;
const uint8_t kHID_ReportID_MouseAbsolute = 7;
const uint8_t kHID_ReportID_NKRO_keyboard = 8;
const uint8_t kHID_ReportID_TeensyKeyboard = 9;
const uint8_t kHID_ReportID_SurfaceDial = 10;
// For use in "Consumer Control". They are bit coded.
const uint8_t kVolumeUp = 1 << 0; // Bit 0
const uint8_t kVolumeDown = 1 << 1; // Bit 1
const uint8_t kVolumeMute = 1 << 2; // Bit 2
static const uint8_t HID_ReportDescriptor_ConsumerControl[] PROGMEM =
{
0x05, 0x0c, // Usage Page ("Consumer Devices")
0x09, 0x01, // Usage (Consumer Control)
0xa1, 0x01, // Collection (Application)
0x85, kHID_ReportID_ConsumerControl, // "Report ID"
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x09, 0xe9, // Usage (Volume Up)
0x09, 0xea, // Usage (Volume Down)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xe2, // Usage (Mute)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb0, // Usage (Play)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb1, // Usage (Pause)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb7, // Usage (Stop)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb5, // Usage (Next)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb6, // Usage (Previous)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb3, // Usage (Fast Forward)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x09, 0xb4, // Usage (Rewind)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data, Variable, Relative)
0x95, 0x06, // Report Count (6) Number of bits remaining in byte
0x81, 0x07, // Input (Constant, Variable, Relative)
0xc0 // End Collection
}; // Table for USB HID "Consumer Control" (for audio volume control, etc.)
Platform
This was tested with:
- Arduino Leonardo (already used as a macro keyboard, using an old PS/2 keyboard. Two new macro keys were assigned for testing audio volume up / down).
- The Arduino Leonardo connected to an extra current port of a D-Link USB hub (necessary, for unknown reasons - probably a local problem)
- Arduino IDE v. 1.18.10
- Linux, Ubuntu 19.10 (Eoan Ermine), but with the buggy GNOME replaced by Cinnamon.