Many axis joystick

PaulStoffregen

Well-known member
Moderator Edit: the latest many-axis source code is attached to reply #36.

------------

Teensyduino currently implements a 6 axis joystick. When I wrote the Teensy 3.0 version, I briefly tried 8 axes, but ran into trouble. Windows seemed to detect the device, but it would not show up in the Joystick control panel as 8 axes. With limited time, I reverted to the 6 axis HID descriptor from Teensy 2.0.

Robert Ferguson send me a link to this project which seems to work with 7 axes.

http://www.imaginaryindustries.com/blog/?p=80

This product implements 8 and has screenshots of the Windows control panel.

http://www.leobodnar.com/products/BU0836/

This forum topic is my reminder to again investigate 8 (or more) axis support. I need the actual HID descriptors from these devices, or some other way of figuring out how to write the descriptors so Windows will recognize the device and show it properly.

I'm also hoping anyone interested in many-asix joystick support will subscribe to this thread. I *really* need your help with testing, when I work on this. In fact, I probably will wait to work on it until at least a couple people have volunteered here to help test.
 
Last edited:
Here is another project using an Arduino that supports 40 buttons and 8 axes... HID descriptor is listed on the project web page at http://hunt.net.nz/users/darran/weblog/15f92/:

HID Descriptor

This is the HID descriptor for the joystick. You can learn more about them from the USB HID Usage Tables.




0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x04, /* Usage (Joystick) */

0xa1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */

/* 8 axes, signed 16 bit resolution, range -32768 to 32767 (16 bytes) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x32, /* Usage (Analog1) */
0x09, 0x33, /* Usage (Analog2) */
0x09, 0x34, /* Usage (Analog3) */
0x09, 0x35, /* Usage (Analog4) */
0x09, 0x36, /* Usage (Analog5) */
0x09, 0x37, /* Usage (Analog6) */
0x16, 0x00, 0x80, /* Logical Minimum (-32768) */
0x26, 0xff, 0x7f, /* Logical Maximum (32767) */
0x75, 16, /* Report Size (16) */
0x95, 8, /* Report Count (8) */
0x81, 0x82, /* Input (Data, Variable, Absolute, Volatile) */
0xc0, /* End Collection */

/* 40 buttons, value 0=off, 1=on (5 bytes) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 1, /* Usage Minimum (Button 1) */
0x29, 40, /* Usage Maximum (Button 40) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 1, /* Report Size (1) */
0x95, 40, /* Report Count (40) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0xc0 /* End Collection */
 
I'm very interested in this Paul. I've been trying to add more axis (throttle type not x,y,z) and have been toying with descriptors and the like but I haven't been able to write a HID for it as I don't seem to have the software capable. The hid tool from USB.org always crashes when I try to save one. I'm a nooby but still trying and willing to assist and test.
I have 2 teensy 3's and a setup with 12 or 16 pots so I can easily test 8 plus axis.

I'll check out those link provided too.
 
Last edited:
Other desc examples I've looked at only call 1 each of a number of types of axis like Zr, Yr, slider, dial, knob, throttle etc. (Like the lufa example above) Where as I've been trying to include multiples of one type or 2 types only, like slider and throttle which seems to be less successful.
I edited to include a similar setup as the lufa eg. And managed to see the 8 axis in windows but haven't tested properly yet as nothing was connected to my board.
 
I wonder if more than 8 can work in Windows?

Even if only 8 show up in the control panel, can Windows give more to programs reading the joystick?
 
I'm using the very basic and free version of visual studio to edit the descriptor and arduino to program the joystick. I can change the desc. file all day long and see various different results but do i need to change any other files? What files get loaded to the chip other than the descriptor and .ino. do new axis need to be defined in USB_joystick.c or .h? I'm kinda stuck at this point. Also in desc should we using 8 16 or 32 bit ints for logical min and Max etc.
 
I wonder if more than 8 can work in Windows?

Even if only 8 show up in the control panel, can Windows give more to programs reading the joystick?

I'm working on a 16-20 bit joystick interface.

I had (IIRC) 9 axes at one point in my testing but never checked the windows control panel. I had X, Y, Z, Rx, Rz and 4 sliders.

I'll play with my HID Descriptor tonight and see if it 1, actually works and 2, what happens when there are that many axes available to windows.
 
What software are you using to test the joystick?

I was using iRacing, a simulation racing game.

When calibrating wheels/pedals it displays the current device being tracked (based on largest diff in min/max registered across all device axes) min, max, and current vals. It also lists the device number and axis number.

I had a 9 axis HID being read by windows, I'm not positive I could use all 9 as I only tested 1 as a proof of concept.

Testing 1st 20 bit axis on the 2nd usb joystick device registered by windows.
qA8FsjA.jpg

I'll get that device up and running again, verify I can use all the axes and post my HID Descriptor. I'm at work right now.
 
Well, it doesn't error when there are more than 8 axes available at any point in development. However the axes are not shown in the Windows descriptor nor are their values visible in any way I can test. I can only access up to the 8th axis.

My Descriptor
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, 0x18, // REPORT_COUNT (24)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x18, // USAGE_MAXIMUM (Button 24)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x08, // REPORT_COUNT (8)
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, 0x40, // USAGE (Vx)
0x09, 0x41, // USAGE (Vy)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x09, 0x36, // USAGE (Slider)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0 // END_COLLECTION


Windows 'joy.cpl':
neTwhHN.png
 
Here is the device descriptor for the BU0836A:

Connection Status Device connected
Current Configuration 1
Speed Full (12 Mbit/s)
Device Address 1
Number Of Open Pipes 1

Device Descriptor BU0836A Interface
Offset Field Size Value Description
0 bLength 1 12h
1 bDescriptorType 1 01h Device
2 bcdUSB 2 0200h USB Spec 2.0
4 bDeviceClass 1 00h Class info in Ifc Descriptors
5 bDeviceSubClass 1 00h
6 bDeviceProtocol 1 00h
7 bMaxPacketSize0 1 40h 64 bytes
8 idVendor 2 16C0h VOTI
10 idProduct 2 05BAh
12 bcdDevice 2 0122h 1.22
14 iManufacturer 1 01h "Leo Bodnar"
15 iProduct 1 02h "BU0836A Interface"
16 iSerialNumber 1 03h "B31071"
17 bNumConfigurations 1 01h

Configuration Descriptor 1 Bus Powered, 100 mA
Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 02h Configuration
2 wTotalLength 2 0022h
4 bNumInterfaces 1 01h
5 bConfigurationValue 1 01h
6 iConfiguration 1 00h
7 bmAttributes 1 80h Bus Powered
4..0: Reserved ...00000
5: Remote Wakeup ..0..... No
6: Self Powered .0...... No, Bus Powered
7: Reserved (set to one)
(bus-powered for 1.0) 1.......
8 bMaxPower 1 32h 100 mA

Interface Descriptor 0/0 HID, 1 Endpoint
Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 04h Interface
2 bInterfaceNumber 1 00h
3 bAlternateSetting 1 00h
4 bNumEndpoints 1 01h
5 bInterfaceClass 1 03h HID
6 bInterfaceSubClass 1 00h
7 bInterfaceProtocol 1 00h
8 iInterface 1 00h

HID Descriptor
Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 21h HID
2 bcdHID 2 0110h 1.10
4 bCountryCode 1 00h
5 bNumDescriptors 1 01h
6 bDescriptorType 1 22h Report
7 wDescriptorLength 2 0067h 103 bytes

Endpoint Descriptor 81 1 In, Interrupt, 4 ms
Offset Field Size Value Description
0 bLength 1 07h
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 81h 1 In
3 bmAttributes 1 03h Interrupt
1..0: Transfer Type ......11 Interrupt
7..2: Reserved 000000..
4 wMaxPacketSize 2 0020h 32 bytes
6 bInterval 1 04h 4 ms

Interface 0 HID Report Descriptor Joystick
Item Tag (Value) Raw Data
Usage Page (Generic Desktop) 05 01
Usage (Joystick) 09 04
Collection (Application) A1 01
Usage (Pointer) 09 01
Collection (Physical) A1 00
Usage (X) 09 30
Usage (Y) 09 31
Usage (Z) 09 32
Usage (Rx) 09 33
Usage (Ry) 09 34
Usage (Rz) 09 35
Usage (Dial) 09 37
Usage (Slider) 09 36
Logical Minimum (0) 15 00
Logical Maximum (4095) 26 FF 0F
Report Size (16) 75 10
Report Count (8) 95 08
Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
End Collection C0
Usage Page (Button) 05 09
Usage Minimum (Button 1) 19 01
Usage Maximum (Button 32) 29 20
Logical Minimum (0) 15 00
Logical Maximum (1) 25 01
Report Size (1) 75 01
Report Count (32) 95 20
Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
Usage Page (Generic Desktop) 05 01
Usage (Hat Switch) 09 39
Logical Maximum (7) 25 07
Physical Minimum (0) 35 00
Physical Maximum (315) 46 3B 01
Unit Exponent (0) 55 00
Unit (Eng Rot: deg^4) 65 44
Report Size (4) 75 04
Report Count (1) 95 01
Input (Data,Var,Abs,NWrp,Lin,Pref,Null,Bit) 81 42
Unit (None) 65 00
Report Count (1) 95 01
Report Size (4) 75 04
Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 03
Collection (Logical) A1 02
Usage Page (Vendor-Defined 1) 06 00 FF
Usage (Vendor-Defined 1) 09 01
Logical Minimum (0) 15 00
Logical Maximum (255) 26 FF 00
Report Size (8) 75 08
Report Count (17) 95 11
Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) B1 02
End Collection C0
End Collection C0
 
OK, i want to work on this in earnest now, but I'm having trouble getting started...
I have built a prototype of my cockpit controller with 36 digital inputs and 8 analog controls, but even after reading the Teensy documentation on the main website and perusing these forums for hours, I haven't a clue how to edit the firmware for this teensy 2++. I see how to edit/load/run sketches, but obviously there's a lot more underlying code that handles the hardware. For example, where do I find the USB descriptor and how do I change it? There are obviously libraries that are being called when a sketch is compiled, but where are they located (Ubuntu)?
 
Last edited:
@RobF - my knowledge on this is still forming since I've just started trying to sort this all out myself. But I'll describe what I do know.

I'm not sure of the exact location on Ubuntu, but the location of the Teensy libs might follow a similar pattern as OSX or Windows.

On OSX you can find all of the Teensy libraries here:

Code:
/Applications/Arduino.app/Contents/Resources/Java/hardware/teensy/cores

On Windows:
Code:
/Program Files (x86)/Arduino/hardware/teensy/cores

Assuming you have the latest Teensyduino installed you'll have the following directories:

Code:
teensy
teensy3
usb_disk
usb_flightsim
usb_hid
usb_midi
usb_rawhid
usb_serial
usb_serial_hid

Depending on the board (Teensy 3.x vs Teensy 2, etc) and device USB type (Serial, Serial+Joystick, etc), you'll find what you're looking for in different places. You mentioned the Teensy 2.0++. Assuming you're creating a "Serial + Keyboard + Mouse + Joystick" device, you'll want to look at these files (starting at [root Arduino path]/hardware/teensy/cores):

Code:
usb_serial_hid/usb_private.h
usb_serial_hid/usb.c

In usb_private.h you can adjust the device name, manufacturer name, serial number, vendor ID and product ID.
In usb.c you'll find all the HID report descriptions. There's a ton of stuff to tweak here. I learned a lot about USB HID Reports from Frank Zhao. Before you touch anything in usb.c make sure you read his tutorial on HID reports.

You'll probably want to pore over the other files within usb_serial_hid as there's more to tweaking stuff than the two files I mentioned.
 
I can help with the usb interface. I have a hardware monitor that can capture the initialization sequence.
 
Hi, I've managed to get the 16 bit 8 axes + 4 hats working with Teensy 3.0.

Attached are the 4 changed files from the teensy3 dir. I'm new, so I'm not sure where i could submit patches?

Couple notes: instead of 16 bytes, now the JOYSTICK_SIZE is 24, and the usb_joystick_data has 5 array items. Packet is being sent at 22 bytes (176bits).
The breakdown of the packet is 32bit(buttons) + 16 bit (4 Hats) + 128 bits (8 axes). The extra 3 hats will not be visible to Windows default game controller panel, but is visible to joystick testers such as the one from: http://www.planetpointy.co.uk/joystick-test-application/

Here is the new HID descriptor:
Code:
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 (Button #32)
        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, 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)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x01,                     // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
        0x15, 0x00,                     //   Logical Minimum (0)
        0x27, 0xFF, 0xFF, 0x00, 0x00,   //   Logical Maximum (65535)
        0x75, 0x10,                     //   Report Size (16)
        0x95, 0x08,                     //   Report Count (8)
	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)
        0x81, 0x02,                     //   Input (variable,absolute)
        0xC0,                           // End Collection
        0xC0                            // End Collection
};

Old packet structure:
old_packet.jpg


New packet structure:
new_packet.jpg

Changed files from hardware/teensy/cores/teensy3
View attachment new_teensy_joystick.zip
 
Hi, I've managed to get the 16 bit 8 axes + 4 hats working with Teensy 3.0.

Attached are the 4 changed files from the teensy3 dir. I'm new, so I'm not sure where i could submit patches?

Couple notes: instead of 16 bytes, now the JOYSTICK_SIZE is 24, and the usb_joystick_data has 5 array items. Packet is being sent at 22 bytes (176bits).
The breakdown of the packet is 32bit(buttons) + 16 bit (4 Hats) + 128 bits (8 axes). The extra 3 hats will not be visible to Windows default game controller panel, but is visible to joystick testers such as the one from: http://www.planetpointy.co.uk/joystick-test-application/

Here is the new HID descriptor:
Code:
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 (Button #32)
        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, 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)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x01,                     // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
        0x15, 0x00,                     //   Logical Minimum (0)
        0x27, 0xFF, 0xFF, 0x00, 0x00,   //   Logical Maximum (65535)
        0x75, 0x10,                     //   Report Size (16)
        0x95, 0x08,                     //   Report Count (8)
	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)
        0x81, 0x02,                     //   Input (variable,absolute)
        0xC0,                           // End Collection
        0xC0                            // End Collection
};

Old packet structure:
View attachment 1403


New packet structure:
View attachment 1404

Changed files from hardware/teensy/cores/teensy3
View attachment 1407

Nice work recoilfx.

I have been messing about with the RawInput today, I am interested to see if extra axis are supported. The obvious limitation are the usage tables, there is still the same 6 axes as in DirectX ...

X, Y, Z, Rx, Ry & Rz

It may be though, that you can then have any number of Sliders, Dials, Wheels and Hat Switches up to the maximum packet size.

However not all games would support these, especially if they just rely on Direct Input.

I still have a little way to go but have some basic code working, I just have a few bugs to iron out. I will then try to incorporate it into the Joystick test application.

Regards,

Les
 
It may be though, that you can then have any number of Sliders, Dials, Wheels and Hat Switches up to the maximum packet size.

I started fiddling with code here to add 12 sliders, after the 6 axes. But it's kind of pointless if no software on Windows can read it...
 
I started fiddling with code here to add 12 sliders, after the 6 axes. But it's kind of pointless if no software on Windows can read it...

To be honest I would be very surprised if games just relied upon Direct Input for game controllers, I am involved in an alpha test of a commercial game so maybe I'll ask the developers.

If you have the code done for 12 sliders then please either post it here or email me a copy so I can test it with RawInput.

Regards,

Les
 
If you have the code done for 12 sliders then please either post it here or email me a copy so I can test it with RawInput.

It wasn't done, but I wrapped it up this morning and tested on Linux. I ended up with 128 buttons, 6 axes, 15 sliders, and 4 hat switches with 1 degree resolution.

Here's a screenshot with "jstest-gtk". Linux considers each hat switch as 2 axes. I don't know if the 64 button limit is coming from jstest or the Linux kernel.

extreme_joystick_screenshot.jpg
(click for full size)

Here's the code, and a simple test sketch.
 

Attachments

  • ExtremeJoystickTest.ino
    3.2 KB · Views: 4,768
  • extreme_joystick_test.zip
    12.9 KB · Views: 5,263
All 6 axes and 15 sliders are 16 bits. In the test sketch, I just multiplied analogRead() output by 16.

Linux seems to be confused by the hat switches offering more than 8 directions. If Windows can't use them either, I'll change them back to 4 bits and use the extra space to add a couple more sliders (as if 15 isn't enough....)
 
I can't get it to work on window 7 here. Doesn't show up as a game controller, and trying to access it through rawInput is showing some weird results.(My rawInput code is far from complete, so may be bugged)

I need to have a proper play when I get some more time.

Regards,

Les
 
I tried with Windows 7 and fiddled with the report descriptor until Windows recognized it as a joystick.

This one has 128 buttons, 6 axes, 17 sliders, and 4 hat switches.
 

Attachments

  • extreme_joystick_test2.zip
    12.9 KB · Views: 5,791
Back
Top