Problem with Joystick HID descriptor readout

Kortonox 1 Reputation point
2022-12-10T10:59:27.647+00:00

I bought a used Joystick (Speedlink Phantom Hawk), which I can't return. The Z-Rotation Axis (twist axis) has a weird offset and doesn't read out the full Motion, it is centred at readout 180, and only uses about 20-30% of the IRL range. I tried everything conventional, and everything driver related, nothing helped (Windows driver, Speedlink Joystick Driver, and alternative drivers).

From Hours of trying to fix this Problem, I got to figure out what exactly is not working correctly, however I couldn't find a way to solve it.

Using "Free Device Monitoring Studio" I am able to read out the HID descriptor and the Values send by the Joystick.

The Problem is, that the Joystick has 2 Z-Axis readouts, the first one is the actual Z-Rotation Axis with its full range, the second Z-Axis is the thrust Slider, and the Rz-Axis is again the Z-Rotation, but only part of the Range with the weird offset.

The actual Z-Axis gives values between 35-230 over the full twist range.

So it's not a faulty sensor.

The Joystick driver is build on top of the Windows driver, and only gives the ability to centre the Axis.

The Question now is, is there a way to get it working again?
Either by editing the HID descriptor, or by making it that Windows reads the actual Z-Axis instead of the false Rz-Axis?

One readout of sent packages looks like this:

Report ID:-1

X[0..255]/[0..255]: 128

Y[0..255]/[0..255]: 125

Z[0..255]/[0..255]: 146 <--- actual twist Axis

Z[0..255]/[0..255]: 127

Rz[0..255]/[0..255]: 174 <----fake/false twist Axis.

Hat Switch[0x0..0x7]/[0x0..0x13b]: 0xf

BUTTON1 pressed: 0

BUTTON2 pressed: 0

BUTTON3 pressed: 0

BUTTON4 pressed: 0

BUTTON5 pressed: 0

BUTTON6 pressed: 0

BUTTON7 pressed: 0

BUTTON8 pressed: 0

BUTTON9 pressed: 0

BUTTON10 pressed: 0

BUTTON11 pressed: 0

BUTTON12 pressed: 0

Unknown: 0

Unknown: 0

Unknown: 0

Unknown: 0

Unknown: 0

Unknown: 0

Unknown: 1

Unknown: 0

The HID Descriptor reads out this:

05 01 09 04 A1 01 09 00 A1 02 09 30 15 00 25 FF

35 00 45 FF 65 00 55 00 75 08 95 01 81 02 09 31

81 02 09 32 81 02 09 32 81 02 09 35 81 02 09 39

25 07 46 3B 01 65 14 75 04 81 42 05 09 19 01 29

0C 25 01 45 01 65 00 75 01 95 0C 81 02 95 08 81

03 C1 00 06 00 FF 09 00 A1 02 09 02 25 FF 45 FF

75 08 95 07 91 02 C1 00 C1 00

Which when Parsed gives this:

0x05, 0x01, // Usage Page (Generic Desktop Ctrls)

0x09, 0x04, // Usage (Joystick)

0xA1, 0x01, // Collection (Application)

0x09, 0x00, // Usage (Undefined)

0xA1, 0x02, // Collection (Logical)

0x09, 0x30, // Usage (X)

0x15, 0x00, // Logical Minimum (0)

0x25, 0xFF, // Logical Maximum (-1)

0x35, 0x00, // Physical Minimum (0)

0x45, 0xFF, // Physical Maximum (-1)

0x65, 0x00, // Unit (None)

0x55, 0x00, // Unit Exponent (0)

0x75, 0x08, // Report Size (8)

0x95, 0x01, // Report Count (1)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x09, 0x31, // Usage (Y)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x09, 0x32, // Usage (Z)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x09, 0x32, // Usage (Z)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x09, 0x35, // Usage (Rz)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x09, 0x39, // Usage (Hat switch)

0x25, 0x07, // Logical Maximum (7)

0x46, 0x3B, 0x01, // Physical Maximum (315)

0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)

0x75, 0x04, // Report Size (4)

0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)

0x05, 0x09, // Usage Page (Button)

0x19, 0x01, // Usage Minimum (0x01)

0x29, 0x0C, // Usage Maximum (0x0C)

0x25, 0x01, // Logical Maximum (1)

0x45, 0x01, // Physical Maximum (1)

0x65, 0x00, // Unit (None)

0x75, 0x01, // Report Size (1)

0x95, 0x0C, // Report Count (12)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x95, 0x08, // Report Count (8)

0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0xC1, 0x00, // End Collection

0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)

0x09, 0x00, // Usage (0x00)

0xA1, 0x02, // Collection (Logical)

0x09, 0x02, // Usage (0x02)

0x25, 0xFF, // Logical Maximum (-1)

0x45, 0xFF, // Physical Maximum (-1)

0x75, 0x08, // Report Size (8)

0x95, 0x07, // Report Count (7)

0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)

0xC1, 0x00, // End Collection

0xC1, 0x00, // End Collection

// 106 bytes

Windows 10
Windows 10
A Microsoft operating system that runs on personal computers and tablets.
12,078 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Jim Harris 0 Reputation points
    2024-12-22T21:54:28.6533333+00:00

    I had a similar problem with a Saitek X52 controller after I replaced the Z-axis potentiometer - the range was both offset to the left and covered about 30% of the total range.

    Within Windows 10 I found the game controller calibration wizard and I was able to get the controller to use the entire range of the Z-axis.

    You might want to try this using the offending instance of the Z-axis of your controller.

    Game controller calibration wizard:
    Go to Control Panel => Devices and Printers => right-click on the desired controller device => select "Game controller settings" to open the "Game Controllers" dialog => Highlight the correct controller and select "Properties"

    This will open up the calibration wizard. The "Test" page will show the axes and buttons on that controller. The "Settings" page will allow you to calibrate the device within Windows and store the calibration constants within the registry.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.