Solved: Eight axis joystick HID descriptor for Teensy LC

Status
Not open for further replies.

avisgut

Member
I've seen a few posts about this, but not many answers, so I thought I'd provide a little bit about what I've learned in the past two days trying to add on an extra two axes to the joystick we have already in the Teensyduino library (or, more accurately, subtracting 15 axes from the larger one).

Before I get too far, let me emphasize that the only thing I've done so far is get Windows to recognize the device as an eight-axis joystick with 32 buttons. I don't know if it will work yet, but navigating the byzantine structure of HID descriptors and all the different files Arduino provides to set up a USB device and interface is way harder than working through a localized programming problem.

First, let me give you some background reading material:
For a good introduction to how all the files work: https://blog.hamaluik.ca/posts/making-a-custom-teensy3-hid-joystick/

Before you work your way through it, though, I recommend you download Notepad++. It makes editing multiple files very much easier and has some nice formatting automatically applied.

The information is a little outdated, and one of the anachronisms is discussed here: https://forum.arduino.cc/index.php?topic=651250.0 I still couldn't get it to work, but you can pursue it if you're interested. The other thing is boards.txt includes a teensylc section, so don't make changes to the teensy3 section as in the guide. I also had a question if Teensy LC used the Teensy 3 directory, and the answer is yes, it does.

Next, check out this great introduction to HID descriptors and how they're put together:
https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/

So now on to editing the files:
Go into usb_desc.h, find the section beginning

Code:
#elif defined(USB_HID)
  #define VENDOR_ID		0x16C0
  #define PRODUCT_ID		0x0482
  #define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME		{'K','e','y','b','o','a','r','d','/','M','o','u','s','e','/','J','o','y','s','t','i','c','k'}

Go down to where it says "define JOYSTICK_SIZE = 12" and change it to 14. The joystick size is how many bytes are in the datastream that the device sends.
32 buttons = 32 bits
8 axes at 10 bit resolution = 80 bits
Total: 92 bits. Divide by eight to get a total of 14 bytes.
(If you're not on a whole byte and have a few extra bits, you'll need to round up to the nearest byte and padd out your descriptor with fluff as shown in HID link above).

With joystick size changed, hop into usb_desc.c.

Look for the section beginning
Code:
#ifdef JOYSTICK_INTERFACE
#if JOYSTICK_SIZE == 12

Directly below that, you're going to add

Code:
#elif JOYSTICK_SIZE == 14 
// 32 buttons, 8 axis, no hat
static uint8_t joystick_report_desc[] = {
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x04,                     // Usage (Joystick)
        0xA1, 0x01,                     // Collection (Application)
        0x15, 0x00,                     // Logical Minimum (0)
        0x25, 0x01,                     // Logical Maximum (1)
        0x75, 0x01,                     // Report Size (1)
        0x95, 0x20,                     // Report Count (32(
        0x05, 0x09,                     // Usage Page (Button)
        0x19, 0x01,                     // Usage Minimum (Button #1)
        0x29, 0x20,                     // Usage Maximum 
        0x81, 0x02,                     // Input (variable,absolute)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x01,                     // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
        0x15, 0x00,                     // Logical Minimum (0)
		0x26, 0xFF, 0x03,               //     Logical Maximum (1023)
        0x75, 0x0A,                     // Report Size (10)
        0x95, 0x08,                       // Report Count ()
        0x09, 0x30,                     // Usage (X)
        0x09, 0x31,                     // Usage (Y)
        0x09, 0x32,                     // Usage (Z)
        0x09, 0x33,                     // Usage (Rx)
        0x09, 0x34,                     // Usage (Ry)
        0x09, 0x35,                     // Usage (Rz)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
/*         0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider)
        0x09, 0x36,                     // Usage (Slider) */
        0x81, 0x02,                     // Input (variable,absolute)
        0xC0,                           // End Collection
	//hat switches
/*         0x15, 0x00,                     // Logical Minimum (0)
        0x25, 0x07,                     // Logical Maximum (7)
        0x35, 0x00,                     // Physical Minimum (0)
        0x46, 0x3B, 0x01,               // Physical Maximum (315)
        0x75, 0x04,                     // Report Size (4)
        0x95, 0x04,                     // Report Count (4)
        0x65, 0x14,                     // Unit (20)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)
        0x09, 0x39,                     // Usage (Hat switch)
        0x81, 0x42,                     // Input (variable,absolute,null_state) */
        0xC0                            // End Collection
};

This code is the 64-bit joystick code with the hats and most of the sliders commented out. I've also dropped the resolution down to ten bits.

Note: If your game controller isn't showing up in Windows' Set Up Game Controller, it's because you did something wrong with the HID. Go to Control Panel, Device Manager, HID Devices (?) and look for an icon with a yellow exclamation point. The two errors I got most often were "not byte-aligned", meaning my description was short of an entire byte, or "main function declared out of collection" (or something), which I'm still not entirely clear on, but I think it means I had some usages that weren't declared with a collection.

Next, you have to go into joystick.h to make it work with with your new joystick size. That's my next step.

Good luck.
 
Status
Not open for further replies.
Back
Top