Discussion How to use X52 Pro SDK - making us of the MFD and LEDs

Did you know that you can program your X52 Pro Joystick in C/C++? I finally figured out how the SDK works and I want to share it with you.

If you want to see a working program making use of this SDK, check out my Post:
https://forums.frontier.co.uk/showthread.php/423671-WIP-LCARS-Interface-amp-X52-Pro-integration-with-Journal-API

If you prefer a video tutorial, you can watch this:
[video=youtube_share;m2rc7zAOR7w]https://youtu.be/m2rc7zAOR7w[/video]

So, now how to use the DirectOutput SDK.

First you will need the DirectOutput.dll and the DirectOutput.h files. For comfort I also created a .lib file. You can download all this here:
https://1drv.ms/u/s!Aq8Kg0iqxkdrgfxXZBc5CL-a9-SAaQ

Now create an empty C/C++ project in Visual Studio. Go to
Project > [Project Name] Properties > C/C++ > General

Make sure you are on All Configurations and x64

Then add the include folder that you downloaded into the Additional Include Directories
Then Go to
Linker > General

Add the lib folder to Additional Library Directories.
Finally go to Linker > Input

And add DirectOutput.lib; to Additional Dependencies

When you use C++ for programming you have to write
Code:
extern "C" {
#include "DirectOutput.h"
}
In order for it to work, as the SDK is written in pure C.

Then create a new const wchar_t * and assign it the name of your app, for example
Code:
const wchar_t * name = L"TestApp";
With this you can call
Code:
DirectOutput_Initialize(name);
In order to initialize the SDK.

You will then have to create two functions which will act as callback functions when a new device is added. They will look something like this:
Code:
void __stdcall DirectOutput_Device_Callback(void* hDevice, bool bAdded, void* pvContext) {}
Code:
void __stdcall DirectOutput_Enumerate_Callback(void* hDevice, void* pvContext) {}
In both these functions you are getting a void * hDevice. You will have to use this later in order to set the LEDs. To save these values, create a new global vector<void*>.
With that you can call in both callback functions [vector_name].push_back(hDevice), so that you will get these functions:
Code:
void __stdcall DirectOutput_Device_Callback(void* hDevice, bool bAdded, void* pvContext) {
    if (bAdded) {
        devices.push_back(hDevice);
    }
    else {

    }
}

void __stdcall DirectOutput_Enumerate_Callback(void* hDevice, void* pvContext) {
    devices.push_back(hDevice);
}
Notice the if statement in the first function, there is another bool indicating wether the device is added or removed.

Now that we have these two callback functions, we can pass them to the DirectOutput SDK by calling
Code:
DirectOutput_RegisterDeviceCallback(*DirectOutput_Device_Callback, nullptr);
DirectOutput_Enumerate(*DirectOutput_Enumerate_Callback, nullptr);
With that, we completed the initialization of the SDK, and now we can go on to the good stuff.

The whole system work with a page system, so we will also have to add a new page:
Code:
DWORD dwPage = 1;
const wchar_t * pageDebugName = L"TestPage";
DirectOutput_AddPage(devices[0], dwPage, pageDebugName, FLAG_SET_AS_ACTIVE);
The Debug Name has no real purpose fo us, so set it to anything you want. the DWORD for the page however will be needed for further calls on this page (setting LEDs and so on).
With FLAG_SET_AS_ACTIVE we are setting the new page as active page.

Now, first let's take a look at the LEDs.

Most buttons consists of two components, a red and a green one. Here you are controlling every LED as a individual LED. Here are all IDs ready to be copied into your project:
Code:
DWORD LED_FIRE = 0;
DWORD LED_FIRE_A_RED = 1;
DWORD LED_FIRE_A_GREEN = 2;
DWORD LED_FIRE_B_RED = 3;
DWORD LED_FIRE_B_GREEN = 4;
DWORD LED_FIRE_D_RED = 5;
DWORD LED_FIRE_D_GREEN = 6;
DWORD LED_FIRE_E_RED = 7;
DWORD LED_FIRE_E_GREEN = 8;
DWORD LED_TOGGLE_1_2_RED = 9;
DWORD LED_TOGGLE_1_2_GREEN = 10;
DWORD LED_TOGGLE_3_4_RED = 11;
DWORD LED_TOGGLE_3_4_GREEN = 12;
DWORD LED_TOGGLE_5_6_RED = 13;
DWORD LED_TOGGLE_5_6_GREEN = 14;
DWORD LED_POV_2_RED = 15;
DWORD LED_POV_2_GREEN = 16;
DWORD LED_CLUTCH_RED = 17;
DWORD LED_CLUTCH_GREEN = 18;
DWORD LED_THROTTLE = 19;
You can also turn on or off each LED by setting its state either to 1 or 0.

So, when we for example want to make our Fire A button yellow, we call:
Code:
DirectOutput_SetLed(devices[0], dwPage, LED_FIRE_A_RED, 1);
DirectOutput_SetLed(devices[0], dwPage, LED_FIRE_A_GREEN, 1);
As you can see, the function gets four parameters. Device, page, led, state.

At this point, you now can compile your program and watch the LED change its color (don't forget that the program will close immediatley, so without stopping the program at the end, you will see nothing) Tip:
Code:
int x;
std::cin >> x;
Use this so that your program stays open until you put in a number and press enter

The MFD works in a very similar way. First create a const wchar_t * with the text you want to show.
Notice that the MFD has three lines with 16 characters each. however you can put in longer text, it will then scroll through the display.
Code:
DWORD MFD_STRING_TOP = 0;
DWORD MFD_STRING_MIDDLE = 1;
DWORD MFD_STRING_BOTTOM = 2;
You will also have to pass in the length of your text (And of course device and page)
So the full call will look like this:
Code:
const wchar_t * text = L"Test App";
DWORD textLength = 8;
DirectOutput_SetString(devices[0], dwPage, MFD_STRING_MIDDLE, textLength, text);
In a similar style to how we created the callbacks above, we also can create callbacks for the so called soft buttons on the device, which basically is the right scrolling wheel, but I haven't used it so far.

My whole sample code now looks like this:
Code:
extern "C" {
#include "DirectOutput.h"
}
#include <vector>
#include <iostream>

DWORD LED_FIRE = 0;
DWORD LED_FIRE_A_RED = 1;
DWORD LED_FIRE_A_GREEN = 2;
DWORD LED_FIRE_B_RED = 3;
DWORD LED_FIRE_B_GREEN = 4;
DWORD LED_FIRE_D_RED = 5;
DWORD LED_FIRE_D_GREEN = 6;
DWORD LED_FIRE_E_RED = 7;
DWORD LED_FIRE_E_GREEN = 8;
DWORD LED_TOGGLE_1_2_RED = 9;
DWORD LED_TOGGLE_1_2_GREEN = 10;
DWORD LED_TOGGLE_3_4_RED = 11;
DWORD LED_TOGGLE_3_4_GREEN = 12;
DWORD LED_TOGGLE_5_6_RED = 13;
DWORD LED_TOGGLE_5_6_GREEN = 14;
DWORD LED_POV_2_RED = 15;
DWORD LED_POV_2_GREEN = 16;
DWORD LED_CLUTCH_RED = 17;
DWORD LED_CLUTCH_GREEN = 18;
DWORD LED_THROTTLE = 19;

DWORD MFD_STRING_TOP = 0;
DWORD MFD_STRING_MIDDLE = 1;
DWORD MFD_STRING_BOTTOM = 2;

std::vector<void*> devices;

void __stdcall DirectOutput_Device_Callback(void* hDevice, bool bAdded, void* pvContext) {
    if (bAdded) {
        devices.push_back(hDevice);
    }
    else {

    }
}

void __stdcall DirectOutput_Enumerate_Callback(void* hDevice, void* pvContext) {
    devices.push_back(hDevice);
}

int main()
{
    const wchar_t * name = L"TestApp";
    DirectOutput_Initialize(name);
    DirectOutput_RegisterDeviceCallback(*DirectOutput_Device_Callback, nullptr);
    DirectOutput_Enumerate(*DirectOutput_Enumerate_Callback, nullptr);

    DWORD dwPage = 1;
    const wchar_t * pageDebugName = L"TestPage";
    DirectOutput_AddPage(devices[0], dwPage, pageDebugName, FLAG_SET_AS_ACTIVE);

    DirectOutput_SetLed(devices[0], dwPage, LED_FIRE_A_RED, 1);
    DirectOutput_SetLed(devices[0], dwPage, LED_FIRE_A_GREEN, 1);

    const wchar_t * value = L"Test App";
    DirectOutput_SetString(devices[0], dwPage, 1, 8, value);

    int x;
    std::cin >> x;

    DirectOutput_Deinitialize();

    return 0;
}
This is it! I hope I could help you out with that tutorial and happy coding!

If you have any further questions or need help, feel free to ask me for help!
 
Love it! Command line input?

I love the potential here!

I'd like to use ED-Discovery to handle the MFD & LED management on windows. ED-Discovery can run programs and pass them arguments.

Could you produce an EXE that takes argument input for the LED control? Something like:
x52led.exe A amber (set "A" button to amber)


Is there a function to control the MFD brightness? That could be useful!
 
I love the potential here!

I'd like to use ED-Discovery to handle the MFD & LED management on windows. ED-Discovery can run programs and pass them arguments.

Could you produce an EXE that takes argument input for the LED control? Something like:
x52led.exe A amber (set "A" button to amber)


Is there a function to control the MFD brightness? That could be useful!
What you want to do is not really possible. The LEDs state says the one you set it as long as the program is running. So if EDD runs a program just to change the LED state, it will not work. What *might* be possible is using a file with the different states, so that the program can run continuously... As I saw it, EDD doesn't support any kind of extensions in C++ or C#, but only their Action language. So maby I can look into collaborating with the creators to integrate it into EDD.
 
First off thanks for sharing your insights. I was looking for a well explained example of the X52 Pro DirectOutput SDK for a while now, and here it is;)

I'm currently working on a little program that is changing the LED colors depending on the selected Mode of the X52 Pro. I'm using your code together with parts of the DirectInput Sample from Microsoft.

At the moment, I'm basically looking for the X52 two times, once with your code and then again to initialize it with directinput, which isn't very elegant. The DirectOutput.dll provides a method (DirectOutput_GetDeviceInstance) to get a GUID of the device that we should be able to use for directinput initialization but I couldn't make it work. My best guess is that there's a DirectInput version mismatch, but I really don't know for sure (I'm by no means good at coding, just gluing stuff together I don't really understand)
Do you have any experience with DirectInput and did you try to use the DirectOutput_GetDeviceInstance method?

Another interesting (meaning annoying) behavior is that the DirectInput sample doesn't really recognize the mode state (even the unaltered sample from microsoft). However, if I start the Gamecontroller properties in Windows, where you can test the joystick and change the LED colors etc, the Modes are suddenly recognized as buttons, and my little program actually does what it's supposed to... Again, no idea why this happens.
 
I love the potential here!

I'd like to use ED-Discovery to handle the MFD & LED management on windows. ED-Discovery can run programs and pass them arguments.

Could you produce an EXE that takes argument input for the LED control? Something like:
x52led.exe A amber (set "A" button to amber)


Is there a function to control the MFD brightness? That could be useful!
Just a quick update here, you will be able to access the X52 Pro SDK via the Action Language in EDD. No release date yet, but should be in the very near future. The first version will include two commands, the first one being setLED. It takes two strings, first the name of the LED and then the state ("ON"/"OFF"). The second will be setString and also takes two strings, first the line ("TOP"/"MIDDLE"/"BOTTOM") and the text you want to display. I have abandoned the page system, because it is very buggy, or better to say it does not work properly. You will also not be able to set any callbacks for the MFD Buttons due to technical limitations.

I may change the set LED system to something different, where you get an additional "apply" command, to avoid flickering when you try to make complex animations.
I may also change from using the LEDs as red and green components to giving it four states ("RED"/"GREEN"/"AMBER"/"OFF")
 
Just a quick update here, you will be able to access the X52 Pro SDK via the Action Language in EDD. No release date yet, but should be in the very near future. The first version will include two commands, the first one being setLED. It takes two strings, first the name of the LED and then the state ("ON"/"OFF"). The second will be setString and also takes two strings, first the line ("TOP"/"MIDDLE"/"BOTTOM") and the text you want to display. I have abandoned the page system, because it is very buggy, or better to say it does not work properly. You will also not be able to set any callbacks for the MFD Buttons due to technical limitations.

I may change the set LED system to something different, where you get an additional "apply" command, to avoid flickering when you try to make complex animations.
I may also change from using the LEDs as red and green components to giving it four states ("RED"/"GREEN"/"AMBER"/"OFF")
Right on! Appreciate the interest to support this functionality in the future of ED:D
 
Could this be used to control the color of the throttle slide LEDs based on forward/neutral/reverse thrust?
 
Quick update, just to let you know, I am actually making a plugin for EDDiscovery. It is still very WIP, but you can see a short demo of it here:
[video=youtube_share;Pa7Uyh-GlN4]https://youtu.be/Pa7Uyh-GlN4[/video]
 
I have to disappoint you there, the throttle does not support manual setting of color. As the main fire button it only supports turning the leds on or off.
If you are willing to void warranty, cut traces to the optocouplers and add transistors and decoupling caps - that's not strictly the case ;)
 
Hi there, maybe you missed my post (I actually registered to the forum only for this, don't even own ED) so here it is again:

First off thanks for sharing your insights. I was looking for a well explained example of the X52 Pro DirectOutput SDK for a while now, and here it is;)

I'm currently working on a little program that is changing the LED colors depending on the selected Mode of the X52 Pro. I'm using your code together with parts of the DirectInput Sample from Microsoft.

At the moment, I'm basically looking for the X52 two times, once with your code and then again to initialize it with directinput, which isn't very elegant. The DirectOutput.dll provides a method (DirectOutput_GetDeviceInstance) to get a GUID of the device that we should be able to use for directinput initialization but I couldn't make it work. My best guess is that there's a DirectInput version mismatch, but I really don't know for sure (I'm by no means good at coding, just gluing stuff together I don't really understand)
Do you have any experience with DirectInput and did you try to use the DirectOutput_GetDeviceInstance method?

Another interesting (meaning annoying) behavior is that the DirectInput sample doesn't really recognize the mode state (even the unaltered sample from microsoft). However, if I start the Gamecontroller properties in Windows, where you can test the joystick and change the LED colors etc, the Modes are suddenly recognized as buttons, and my little program actually does what it's supposed to... Again, no idea why this happens.
I solved the first problem: The DirectOutput_GetDeviceInstance Method, as far as I can tell, basically returns the GUID for the X52Pro that is defined in the DirectOutput.h file, which simply isn't the correct GUID. I just changed it to the one I got from Enumerating by DirectInput and it worked, i.e. I use it as a constant and don't enumerate at all (don't know if GUIDs are machine dependant or really globally defined, so maybe that won't work if I use the same GUID on an other PC).

The other problem, that the mode (button) states are only recieved by DirectInput when the Gamecontroller settings are open in Windows, is still a mistery to me. I actually contacted Logitech support and am waiting for an answer, but I doubt this will lead somewhere.
 
Hi there, maybe you missed my post (I actually registered to the forum only for this, don't even own ED) so here it is again:



I solved the first problem: The DirectOutput_GetDeviceInstance Method, as far as I can tell, basically returns the GUID for the X52Pro that is defined in the DirectOutput.h file, which simply isn't the correct GUID. I just changed it to the one I got from Enumerating by DirectInput and it worked, i.e. I use it as a constant and don't enumerate at all (don't know if GUIDs are machine dependant or really globally defined, so maybe that won't work if I use the same GUID on an other PC).

The other problem, that the mode (button) states are only recieved by DirectInput when the Gamecontroller settings are open in Windows, is still a mistery to me. I actually contacted Logitech support and am waiting for an answer, but I doubt this will lead somewhere.
I did miss you post, I thought I had enabled E-Mail notifications, but yeah.

I am happy I could help you with my guide.

With your second problem, to be honest, I have no idea how the system works there. If I could, I would remove the whole macro system and replace it with just buttons you can talk to. It's interesting that you were able to recieve button states from the mode button at all. I mean I struggle with getting my games work with the clutch, scrolling wheel and mous axis and button on the controller... Basically what I am saying here is that I have no idea, and the whole software side system is just a complete mess by Saitek. Remember the driver that came on the CD? Crashed without doing anything... If you find out how to use the mode switch, please let me know!
 
Right on! Appreciate the interest to support this functionality in the future of ED:D
In case you missed it, I opened a new thread for the new Plugin. https://forums.frontier.co.uk/showthread.php/434012-EDDX52-EDDiscovery-X52-Pro-Addon

The Plugin is really taking shape with a release right around the corner. It turned out way more powerful than I imagined... You can actually interact with the game through the MFD (Selecting List items on the MFD) and for example lower the Gear through selecting an item on the MFD. And secondly, you can set blink patterns on each button.

An ingame demo can be found here:
[video=youtube;-PnReJJqnlg]https://www.youtube.com/watch?v=-PnReJJqnlg[/video]
 
I love the potential here!

I'd like to use ED-Discovery to handle the MFD & LED management on windows. ED-Discovery can run programs and pass them arguments.

Could you produce an EXE that takes argument input for the LED control? Something like:
x52led.exe A amber (set "A" button to amber)


Is there a function to control the MFD brightness? That could be useful!
I released the first version of the Plugin! Download: https://github.com/wulkanat/EDDX52/releases
 
I did miss you post, I thought I had enabled E-Mail notifications, but yeah.

I am happy I could help you with my guide.

With your second problem, to be honest, I have no idea how the system works there. If I could, I would remove the whole macro system and replace it with just buttons you can talk to. It's interesting that you were able to recieve button states from the mode button at all. I mean I struggle with getting my games work with the clutch, scrolling wheel and mous axis and button on the controller... Basically what I am saying here is that I have no idea, and the whole software side system is just a complete mess by Saitek. Remember the driver that came on the CD? Crashed without doing anything... If you find out how to use the mode switch, please let me know!
Some news concerning the mode switch problem: I managed to detect all button presses (including the mode switch and both wheels (presses and up and down) and buttons below the MFD) with DirectInput. It appears that the Saitek driver for the X52 pro actually hijaked these button presses. You just have to create a new profile in the editor for the stick and delete all mode and shift state assignments (topmost X on each column) and activate it (guess uninstalling the software would also work). The obvious downside to this is that you can't use the software to define profiles and Modes anymore.
 
Top Bottom