Multiple joysticks on Teensy 4.1

karios_k

New member
Hello everyone,
I've been using Teensy's as joysticks for a while now and they work great. I recently came upon a project which requires a single Teensy 4.1 to present itself as multiple distinct joysticks in Windows. I was able to modify the USB descriptor by introducing unique Report IDs on each joystick like so:

static uint8_t joystick_report_desc[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 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, 0x05, // Usage Maximum (Button #5) 0x81, 0x02, // Input (variable,absolute) 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, 0x01, // Report Count (1) 0x65, 0x14, // Unit (20) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x39, // Usage (Hat switch) 0x81, 0x42, // Input (variable,absolute,null_state) 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, 0x04, // Report Count (4) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x81, 0x02, // Input (variable,absolute) 0xC0, // End Collection 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x03, // Logical Maximum (1023) 0x75, 0x0A, // Report Size (10) 0x95, 0x02, // Report Count (2) 0x09, 0x36, // Usage (Slider) 0x09, 0x36, // Usage (Slider) 0x81, 0x02, // Input (variable,absolute) 0xC0, // End Collection 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) 0x85, 0x02, // Report ID (2) 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, 0x05, // Usage Maximum (Button #5) 0x81, 0x02, // Input (variable,absolute) 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, 0x01, // Report Count (1) 0x65, 0x14, // Unit (20) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x39, // Usage (Hat switch) 0x81, 0x42, // Input (variable,absolute,null_state) 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, 0x04, // Report Count (4) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x81, 0x02, // Input (variable,absolute) 0xC0, // End Collection 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x03, // Logical Maximum (1023) 0x75, 0x0A, // Report Size (10) 0x95, 0x02, // Report Count (2) 0x09, 0x36, // Usage (Slider) 0x09, 0x36, // Usage (Slider) 0x81, 0x02, // Input (variable,absolute) 0xC0 // End Collection

When compiling against this, Windows indeed sees 2 distinct joysticks with the same name.

USBView shows only one device connected to the controller which is normal, since there is indeed only one physical device connected.

In usb_desc.h, I kept only one endpoint with JOYSTICK_INTERFACE and this is what USBView is reporting also, with the correct bInterfaceNumber.

The problem is I can't send data to a specific joystick using its Report ID, since none of the functions take a report ID as a parameter (which makes sense, since the default use case is a single joystick - which I believe has a report ID of 0 by default). Also, using the standard Joystick functions does not send the data to ANY of the connected joysticks.

What I need is to modify the usb_joystick_send() function with a report ID so I can populate the buffer as per usual (using the standard Joystick.X(), Joystick.button() etc functions) and then send the buffer to a specific joystick (using something like Joystick.send_now(REPORT_ID)).

Any help or ideas would be greatly appreciated.
 
By the way, if I set:
0x85, 0x01, // Report ID (1)
even on a single joystick, the data I send to the joystick do not go through. So maybe I have misunderstood something about the idea of the ReportID?
Any USB gurus out there (like @PaulStoffregen)? :)
 
Back
Top