Source code for joystick_xl.hid

"""
Initial USB configuration tools for use in ``boot.py`` setup.

This module provides the necessary functions to create a CircuitPython USB HID device
with a descriptor that includes the configured type and quantity of inputs.
"""

import usb_hid  # type: ignore (this is a CircuitPython built-in)

from joystick_xl import __version__


[docs] def create_joystick( axes: int = 4, buttons: int = 16, hats: int = 1, report_id: int = 0x04, ) -> usb_hid.Device: """ Create the ``usb_hid.Device`` required by ``usb_hid.enable()`` in ``boot.py``. .. note:: JoystickXL will add an entry to the ``boot_out.txt`` file on your ``CIRCUITPY`` drive. It is used by the ``Joystick`` module to retrieve configuration settings. :param axes: The number of axes to support, from 0 to 8. (Default is 4) :type axes: int, optional :param buttons: The number of buttons to support, from 0 to 128. (Default is 16) :type buttons: int, optional :param hats: The number of hat switches to support, from 0 to 4. (Default is 1) :type hats: int, optional :param report_id: The USB HID report ID number to use. (Default is 4) :type report_d: int, optional :return: A ``usb_hid.Device`` object with a descriptor identifying it as a joystick with the specified number of buttons, axes and hat switches. :rtype: ``usb_hid.Device`` """ _num_axes = axes _num_buttons = buttons _num_hats = hats # Validate the number of configured axes, buttons and hats. if _num_axes < 0 or _num_axes > 8: raise ValueError("Axis count must be from 0-8.") if _num_buttons < 0 or _num_buttons > 128: raise ValueError("Button count must be from 0-128.") if _num_hats < 0 or _num_hats > 4: raise ValueError("Hat count must be from 0-4.") _report_length = 0 # Formatting is disabled below to allow the USB descriptor elements to be # grouped and annotated such that the descriptor is readable and maintainable. # fmt: off _descriptor = bytearray(( 0x05, 0x01, # : USAGE_PAGE (Generic Desktop) 0x09, 0x04, # : USAGE (Joystick) 0xA1, 0x01, # : COLLECTION (Application) 0x85, report_id, # : REPORT_ID (Default is 4) )) if _num_axes: for i in range(_num_axes): _descriptor.extend(bytes(( 0x09, min(0x30 + i, 0x36) # : USAGE (X,Y,Z,Rx,Ry,Rz,S0,S1) ))) _descriptor.extend(bytes(( 0x15, 0x00, # : LOGICAL_MINIMUM (0) 0x26, 0xFF, 0x00, # : LOGICAL_MAXIMUM (255) 0x75, 0x08, # : REPORT_SIZE (8) 0x95, _num_axes, # : REPORT_COUNT (num_axes) 0x81, 0x02, # : INPUT (Data,Var,Abs) ))) _report_length = _num_axes if _num_hats: for i in range(_num_hats): _descriptor.extend(bytes(( 0x09, 0x39, # : USAGE (Hat switch) ))) _descriptor.extend(bytes(( 0x15, 0x00, # : LOGICAL_MINIMUM (0) 0x25, 0x07, # : LOGICAL_MAXIMUM (7) 0x35, 0x00, # : PHYSICAL_MINIMUM (0) 0x46, 0x3B, 0x01, # : PHYSICAL_MAXIMUM (315) 0x65, 0x14, # : UNIT (Eng Rot:Angular Pos) 0x75, 0x04, # : REPORT_SIZE (4) 0x95, _num_hats, # : REPORT_COUNT (num_hats) 0x81, 0x42, # : INPUT (Data,Var,Abs,Null) ))) _hat_pad = _num_hats % 2 if _hat_pad: _descriptor.extend(bytes(( 0x75, 0x04, # : REPORT_SIZE (4) 0x95, _hat_pad, # : REPORT_COUNT (_hat_pad) 0x81, 0x03, # : INPUT (Cnst,Var,Abs) ))) _report_length += ((_num_hats // 2) + bool(_hat_pad)) if _num_buttons: _descriptor.extend(bytes(( 0x05, 0x09, # : USAGE_PAGE (Button) 0x19, 0x01, # : USAGE_MINIMUM (Button 1) 0x29, _num_buttons, # : USAGE_MAXIMUM (num_buttons) 0x15, 0x00, # : LOGICAL_MINIMUM (0) 0x25, 0x01, # : LOGICAL_MAXIMUM (1) 0x95, _num_buttons, # : REPORT_COUNT (num_buttons) 0x75, 0x01, # : REPORT_SIZE (1) 0x81, 0x02, # : INPUT (Data,Var,Abs) ))) _button_pad = _num_buttons % 8 if _button_pad: _descriptor.extend(bytes(( 0x75, 0x01, # : REPORT_SIZE (1) 0x95, 8 - _button_pad, # : REPORT_COUNT (_button_pad) 0x81, 0x03, # : INPUT (Cnst,Var,Abs) ))) _report_length += ((_num_buttons // 8) + bool(_button_pad)) _descriptor.extend(bytes(( 0xC0, # : END_COLLECTION ))) # fmt: on # write configuration data to boot.out using 'print' print( "+ Enabled JoystickXL", __version__, "with", _num_axes, "axes,", _num_buttons, "buttons and", _num_hats, "hats for a total of", _report_length, "report bytes.", ) return usb_hid.Device( report_descriptor=bytes(_descriptor), usage_page=0x01, # same as USAGE_PAGE from descriptor above usage=0x04, # same as USAGE from descriptor above report_ids=(report_id,), # report ID defined in descriptor in_report_lengths=(_report_length,), # length of reports to host out_report_lengths=(0,), # length of reports from host )
def _get_device() -> usb_hid.Device: """Find a JoystickXL device in the list of active USB HID devices.""" for device in usb_hid.devices: if ( device.usage_page == 0x01 and device.usage == 0x04 and hasattr(device, "send_report") ): return device raise ValueError("Could not find JoystickXL HID device - check boot.py.)")