Main Content

Custom HID Devices in CircuitPython

What is HID?
HID stands for “Human Interface Device”. Keyboards, mice, digitizer tablets, joysticks, and game controllers are all HID devices. CircuitPython can emulate three standard HID devices by default: mouse, keyboard and consumer control. These are described in more detail in CircuitPython Essentials Guide and the Customizing USB Devices Guide.

All operating systems (e.g., Windows, macOS, or Linux) have built-in support for certain standard devices like keyboards and mice. But they vary about whether they support other less common devices. Often you need to install a driver to support a particular HID device, such as a specific game controller or a tablet.

Custom HID Devices
There are so many kinds of devices you might want to implement in CircuitPython, it cannot provide built-in support for them all. So CircuitPython lets you define and implement your own custom HID devices . With the right device definition and built-in OS support or a compatible driver, you can implement an existing HID device, or define your own custom variation.

Report Descriptors
Report Descriptors
To define an HID device, you need to supply an HID report descriptor. When you plug in an HID device, it sends its report descriptor(s) to the host computer. The report descriptor is binary data that specifies the device type and the details of the reports that the device sends and receives.

A report is binary data. A report sent from the device to the host computer is called an IN report. A report sent from the host to the device is an OUT report. IN and OUT are named from the perspective of the host.

For instance, a mouse report descriptor will declare that it is a device of type Mouse, with a certain number of Buttons and possibly a scroll Wheel. Every time you move the mouse, push a button, or move its scroll wheel, the mouse will send an IN report with data describing which buttons that are currently pushed, how far the mouse has moved in X and Y directions, and how much the scroll wheel has been turned.

Similarly, a keyboard will send IN reports saying which regular keys are pressed and which modifier keys (Shift, Ctrl, etc.) are pressed at the same time. In addition, most keyboards can receive OUT reports back from the host computer, which tell the keyboard to turn on and off its LEDs, such as the shift-lock indicator.

A Sample Report Descriptor
Below is a CircuitPython boot.py file that includes an example of a gamepad report descriptor.

The descriptor is a bytes string named GAMEPAD_REPORT_DESCRIPTOR. Note how the descriptor specifies a Usage Page, which is the general class of device, in this case, Generic Desktop Controls, and then a particular Usage, which is Game Pad. Then the descriptor specifies a Report ID, which here is 4. The report ID can be any value from 1 to 255, but must be unique for this report among all the reports in the devices presented by CircuitPython.

The descriptor then declares 16 on/off (0 or 1) Buttons, which fit in one bit each (Report Count of 16 and Report Size of 1), and four joystick axes, which are Usages X, Y, Z, and Rz. Each joystick value varies from -127 to 127, and fits in 8 bits.

The rest of the code creates a Device based on the descriptor, and includes it in a list of devices that also includes the default keyboard, mouse, and consumer control devices that CircuitPython usually presents. The Device constructor specifies the Report ID’s used, and how many bytes are in the IN and OUT reports for each Report ID. In this case the IN report is 6 bytes long, and there is no OUT report. See the Customizing USB Devices Guide for more details about why this code needs to be in boot.py, and what usb_hid.enable() is doing.”

Link to article