Search code examples
c#.netwpfwindow

Calibrating a game controller from code


I am looking to reproduce a calibration procedure from a C# WPF App. There are similar app on the internet doing so, such as DIView:

DIView calibration procedure

and DXTweak:

DXTweak calibration procedure

However, the procedure is tedious since you are asked to pick a minimum, middle and maximum value for each axis.

I have my own joystick board that is recognized by windows as a game controller. I am able to read its values with DirectInput or with any other libraries. When using one of these two apps to calibrate my joystick, I can actually get the results when I poll the values with DirectInput. There's definitely a way to do it by code.

My goal would be to develop an app to calibrate multiple axises at the same time. I believed that DirectInput could handle this kind of task, or that the windows registries stored them somewhere. Sadly, there wasn't enough results after searching it on Google for several hours (Please, prove me wrong!).

EDIT:

There are the DeviceProperties.GetCalibrationPoints and DeviceProperties.SetCalibrationPoints functions in the DirectInput API, but I have no clue of how to use them.

Here's the documentation.

I tried getting the calibration points like this:MyDevice.GetCalibrationPoints(ParameterHow.ByUsage, MyDevice.DeviceInformation.Usage); but I guess I'm doing it wrong because I get a -2147024894 (ERROR_FILE_NOT_FOUND) Exception.


Solution

  • Alright here's what I found using registries. It's a bit long but I got it working.

    Using regedit, you can find the list of DirectInput devices using this path: Computer\HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput

    DirectInput registry

    By choosing your desired device, you eventually end up with the following path: Computer\HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput\YOUR_DEVICE_HERE\Calibration\0\Type\Axes. If you can't see anything else in that registry, it means that your axises have not been calibrated yet.

    Using C#, you can read the calibration values of each axis in the registry using the following function and by casting it as an array of Bytes:

    Byte[] Axis = Registry.GetValue(registry + '0', "Calibration", "EMPTY") as Byte[];
    

    You can change the '0' for each axis.

    Make sure to handle exceptions if your registry doesn't exist, meaning that your path is invalid or that the desired Axis has never been calibrated before (and therefore uses default values).

    I also noticed something different while using the resulted array, the values are stored in a weird way:

    • The array has a length of 12, the min value of the calibration is stored in the first 4 bytes, then the 4 middle ones for the middle value of the calibration, and the 4 last ones for the max.

    • To read the actual values for each part, you need to multiply the second byte for each part by 256, and add it to the first byte: int min = 256 * Axis[1] + Axis[0];

    Finally, if you wish to write your own calibration values, you can simply use the following function Registry.SetValue(regPath + '0', "Calibration", ByteArray);

    Don't forget to change the '0' for each axis, and to use the same format in the array:

    ByteArray[0] = (byte)(min % 256);
    ByteArray[1] = (byte)(min / 256);
    ByteArray[4] = (byte)(middle % 256);
    ByteArray[5] = (byte)(middle / 256);
    ByteArray[8] = (byte)(max % 256);
    ByteArray[9] = (byte)(max / 256);