jenswilly.dk


Parsing USB joystick HID data

As part of using a USB joystick as input device to an MCU I needed to parse the raw HID data of the joystick into values on the various axes and button states.

This is the first time I have had to manually get from a HID descriptor and some raw data to something that makes sense.

Using the "USB Prober" tool that can be downloaded from Apple Developer downloads (search for "usb prober" – it will be part of the latest IOUSBFamily Log Release package) I got the following HID descriptor for the joystick:

Length (and contents):   109
    Raw Descriptor (hex)    0000: 05 01 09 04 A1 01 09 01  A1 00 05 01 09 30 09 31  
    Raw Descriptor (hex)    0010: 15 00 26 FF 03 35 00 46  FF 03 65 00 75 0A 95 02  
    Raw Descriptor (hex)    0020: 81 02 09 35 09 32 15 00  26 FF 01 35 00 46 FF 01  
    Raw Descriptor (hex)    0030: 65 00 75 09 95 02 81 02  75 01 95 02 81 01 09 39  
    Raw Descriptor (hex)    0040: 15 01 25 08 35 00 46 3B  01 65 14 75 08 95 01 81  
    Raw Descriptor (hex)    0050: 02 05 09 19 01 29 0C 15  00 25 01 35 00 45 01 75  
    Raw Descriptor (hex)    0060: 01 95 0C 81 02 75 01 95  04 81 01 C0 C0 

Parsed Report Descriptor:   
      Usage Page    (Generic Desktop) 
      Usage (Joystick)    
          Collection (Application)    
            Usage (Pointer)    
                Collection (Physical)    
                  Usage Page    (Generic Desktop) 
                  Usage (X)    
                  Usage (Y)    
                  Logical Minimum.........    (0)  
                  Logical Maximum.........    (1023)  
                  Physical Minimum........    (0)  
                  Physical Maximum........    (1023)  
                  Unit....................    (0)  
                  Report Size.............    (10)  
                  Report Count............    (2)  
                  Input...................   (Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bitfield) 
                  Usage (Rz)    
                  Usage (Z)    
                  Logical Minimum.........    (0)  
                  Logical Maximum.........    (511)  
                  Physical Minimum........    (0)  
                  Physical Maximum........    (511)  
                  Unit....................    (0)  
                  Report Size.............    (9)  
                  Report Count............    (2)  
                  Input...................   (Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bitfield) 
                  Report Size.............    (1)  
                  Report Count............    (2)  
                  Input...................   (Constant, Array, Absolute) 
                  Usage (Hat Switch)    
                  Logical Minimum.........    (1)  
                  Logical Maximum.........    (8)  
                  Physical Minimum........    (0)  
                  Physical Maximum........    (315)  
                  Unit....................    (20)  
                  Report Size.............    (8)  
                  Report Count............    (1)  
                  Input...................   (Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bitfield) 
                  Usage Page    (Button) 
                  Usage Minimum...........    (1)  
                  Usage Maximum...........    (12)  
                  Logical Minimum.........    (0)  
                  Logical Maximum.........    (1)  
                  Physical Minimum........    (0)  
                  Physical Maximum........    (1)  
                  Report Size.............    (1)  
                  Report Count............    (12)  
                  Input...................   (Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bitfield) 
                  Report Size.............    (1)  
                  Report Count............    (4)  
                  Input...................   (Constant, Array, Absolute) 
                End Collection     
          End Collection 

What is says is basically that the HID data consists of:

  1. 10 bits of X-axis data (for values from 0 to 1023)
  2. 10 bits of Y-axis data (for values from 0 to 1023)
  3. 9 bits of Rz-axis (yaw or rotation) data (for values from 0-511)
  4. 9 bits of Z-axis (throttle) data (for values from 0-511)
  5. 2 "constant" bits – i.e. unused padding bits
  6. 8 bits of hat switch data (though values are only from 1-8)
  7. 12 bits of button states (12 buttons of 0 or 1 values)
  8. 4 bits of padding

For a total of 64 bits or 8 bytes.

Using tool downloaded as part of the HID API library I got the following raw data as a single HID report:
e9 31 e8 ef 3f 00 00 00

Raw HID data is in least significant bit (LSB) order. So the first 10 bits (X-axis) is the first byte (B0[0:7] -> X-axis[0:7]) and then the two lowest bits in the next byte (B1[0:1] -> X-axis[8:9]).
The next 10 bits (Y-axis) are the highest 6 bits of byte two (since the lowest two is already used) (B1[2:7] -> Y-axis[0:5]) and the lowest 4 bits of byte 3 (B3[0:3] -> Y-axis[6:9]).
Followed in the same manner by 9 bits of Rz-axis (rotation) and 9 bits or Z-axis (throttle). Then two bits of "constant" data – i.e. two padding bits. Then 8 bits of hat switch data (which is actually somewhat overkill since the hat only has 9 states which is represented as a number from 0 to 8). Then 12 single bits representing the individual buttons and lastly four more padding bits. Whew...

Here's how the bits matches the axis and button data:

Raw hex: e9 31 e8 ef 3f 00 00 00

Binary  11101001 00110001 11101000 11101111 00111111 00000000 00000000 00000000
field   XXXXXXXX YYYYYYXX RRRRYYYY ZZZRRRRR --ZZZZZZ HHHHHHHH BBBBBBBB ----BBBB
bit no. 76543210 54321098 32109876 21087654 --876543 76543210 76543210 ----BA98

X = X-axis:  b0111101101 = 480
Y = Y-axis:  b1000001100 = 524
R = Rz-axis: b011111110 = 254
Z = Z-axis:  b111111111 = 511
H = hat:     b00000000 = 0 (centered)
B = buttons  b000000000000 = 0 (no buttons pressed)
- = padding

Leave a Reply