HID stands for "Human Interface Device". Keyboards, mice, digitizer tablets, joysticks, and game controllers are all examples of HID devices.

Standard HID Devices

CircuitPython provides three HID devices by default. They are defined in usb_hid.Devices:

  • KEYBOARD - A standard keyboard, including five (virtual) LED indicators.
  • MOUSE - A standard mouse supporting five buttons and a mouse wheel.
  • CONSUMER_CONTROL - A USB Consumer Control device: multimedia controls, browser shortcut keys, etc.

Consumer Control

You may not have heard of Consumer Control devices, but you probably have one on your desk. Your keyboard may have volume control, play, and pause keys, and perhaps also keys to do browser operations, open a calculator, etc. Those keys are not actually regular keyboard device keys. Instead, key presses for those keys are sent to the host computer via a Consumer Control device, not the keyboard device.

For instance, on the keyboard below, the marked functions are sent via Consumer Control keys. Those physical keys send both regular keyboard presses and Consumer Control presses. For example, you can send the regular keyboard keycode F4, or the Consumer Control code MUTE depending on whether you press the Fn key or not.

Consumer Control codes are defined in this USB standards document (page 85)

Choosing HID Devices

You can choose which HID devices CircuitPython provides using code like this in boot.py.

import usb_hid

# These are the default devices, so you don't need to write
# this explicitly if the default is what you want.
usb_hid.enable(
    (usb_hid.Device.KEYBOARD,
     usb_hid.Device.MOUSE,
     USB_hid.Device.CONSUMER_CONTROL)
)

usb_hid.enable((usb_hid.Device.KEYBOARD,))   # Enable just KEYBOARD.

usb_hid.disable()       # Disable all HID devices.

usb_hid.enable(())      # Another way to disable all the devices.

Note that usb_hid.enable() always takes a tuple of devices, even if there is just one device, or zero devices.

Advanced Topics

Composite HID Devices

Multiple HID devices can be grouped together into a single composite HID device, which contains all the devices at once. They share a single endpoint pair (see here for details), and each device uses a distinct report ID to distinguish it from the other devices in the composite device. In the code above, all the devices in the tuple are in a single composite device. If there is only one device listed, it does not need a separate report ID, and will not use one.

Right now CircuitPython only allows a single HID composite device, but this may change in the future.

Custom HID Devices

Besides the devices listed above, you can also define custom HID devices. You need to supply an HID report descriptor, which is a binary string of bytes that defines the kind of device and the details of the reports that it sends and receives. For instance, a mouse report will contain 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. A keyboard report will report which regular keys are pressed and which modifier keys (Shift, Ctrl, etc.) are pressed.

Writing your own HID report descriptors from scratch requires a lot of detailed knowledge, and is beyond the scope of this guide. However, you can often get the report descriptor for an existing HID device you want to emulate, and just use it as is, or modify it slightly for your purposes. There are many tutorials about HID report descriptors available, such as this one. And here's an online tool that can decipher existing report descriptors.

You will also need to write a CircuitPython driver to handle your new device. There are examples in the adafruit_hid library.

As an example of what is possible, below is some code that defines a particular gamepad controller HID device and adds to the standard set of HID devices.  The sample descriptor given here may not work with your operating system. You must understand how report descriptors are defined to make sure it suits your needs.

import usb_hid

# This is only one example of a gamepad descriptor, and may not suit your needs.
GAMEPAD_REPORT_DESCRIPTOR = bytes((
    0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
    0x09, 0x05,  # Usage (Game Pad)
    0xA1, 0x01,  # Collection (Application)
    0x85, 0x04,  #   Report ID (4)
    0x05, 0x09,  #   Usage Page (Button)
    0x19, 0x01,  #   Usage Minimum (Button 1)
    0x29, 0x10,  #   Usage Maximum (Button 16)
    0x15, 0x00,  #   Logical Minimum (0)
    0x25, 0x01,  #   Logical Maximum (1)
    0x75, 0x01,  #   Report Size (1)
    0x95, 0x10,  #   Report Count (16)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x05, 0x01,  #   Usage Page (Generic Desktop Ctrls)
    0x15, 0x81,  #   Logical Minimum (-127)
    0x25, 0x7F,  #   Logical Maximum (127)
    0x09, 0x30,  #   Usage (X)
    0x09, 0x31,  #   Usage (Y)
    0x09, 0x32,  #   Usage (Z)
    0x09, 0x35,  #   Usage (Rz)
    0x75, 0x08,  #   Report Size (8)
    0x95, 0x04,  #   Report Count (4)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,        # End Collection
))

gamepad = usb_hid.Device(
    report_descriptor=GAMEPAD_REPORT_DESCRIPTOR,
    usage_page=0x01,           # Generic Desktop Control
    usage=0x05,                # Gamepad
    report_ids=(4,),           # Descriptor uses report ID 4.
    in_report_lengths=(6,),    # This gamepad sends 6 bytes in its report.
    out_report_lengths=(0,),   # It does not receive any reports.
)

usb_hid.enable(
    (usb_hid.Device.KEYBOARD,
     usb_hid.Device.MOUSE,
     usb_hid.Device.CONSUMER_CONTROL,
     gamepad)
)

Boot Keyboard and Mouse

The keyboard and mouse provided by CircuitPython are not marked as "boot" devices. This is a special feature of USB HID devices, used when you need to talk a computer when it's booting or to its BIOS. The report descriptor for a boot keyboard or mouse is standard. Any descriptor you supply is ignored if the device is used in boot mode.

CircuitPython does not yet support boot devices, but this may change in the future.

Cleaning up Windows HID Devices

If you are developing a new HID device on Windows, and change the report descriptor in the process of development, you may find that the device does not seem to work properly. Windows remembers the old HID information, and can have trouble with a changed report descriptor. To fix this, you can remove the previously installed device, with the board not plugged in. You can do this from the Windows Device Manager, but it's much easier to use Uwe Sieber's Device Cleanup Tool. For more details see this section on the Troubleshooting page in the Welcome to CircuitPython guide.

This guide was first published on May 20, 2021. It was last updated on 2021-07-20 14:26:58 -0400.

This page (HID Devices) was last updated on Dec 08, 2021.

Text editor powered by tinymce.