Arduino and network interface to PMDG Data and Events

The PMDGDataEventserver has also been tested by members to work with P3D/PMDG.

NEW: There is also a version for PMDG 747 QOTS II , For the program description however, remain on this page.

NEW : CDU screen only on network interface.

NEW : PMDG Data and Events Server for NGX rev SP1D (1.1)
NEW : TCP/IP Server Interface ADDED. With this new version you can not only connect serial devices to but also connect over the network using TCP/IP.
NEW : CDU screen content availble, no screen scraping anymore!

If you already know or use the PMDGDataEventServer, Read more about the new version for PMDG NGX rev SP1D at the end of this, if not carry on reading from here.

PMDG Data Events Server for serial interface.

Although the title of this page states ‘Arduino’, all other hardware that you can build can be uses to connect to the serial interface. With the introduction of the version for PMDG NGX SDK Rev SP1D a network (TCP/IP) interface is avaiblable as well.

Programming is very easy, a simple example sketch will lead your way.

The program uses Simconnect according to the PMDG SDK.

Note: This program is independant from the other parts (hardware and software) of my FSIO project. 

This program is a spinoff from my PMDGDataEventServer that I made for users op SIOC and part of my FSIO architecture. However it is totaly independant of the FSIO hard en software architecture.

In order to use the PMDG SDK you need to activate it in PMDG. The text below is taken from the PMDG SDK. Enable the CDU only when you plan to use it. Th

// NOTE - add these lines to the 737NGX_Options.ini file: 
//
[SDK]
EnableDataBroadcast=1
//
// to enable the data sending from the NGX.
//
// Add any of these lines to the [SDK] section of the 737NGX_Options.ini file: 
//
EnableCDUBroadcast.0=1 
EnableCDUBroadcast.1=1 
//
// to enable the contents of the corresponding CDU screen to be sent to external programs.

Do not enable both the CDU if you are not going to use the CDU screen data. Just enable the one that you are going to use.

First some word about the PMDG terminology in the SDK.

Events

Elements, like buttons, encoders, switches etc. of the PMDG NGX can be operated by mouse actions on the assigned spot on the screen. Cockpit / hardware builders need some means to send these kinds of operations to the PMDG by means of software (commands).

PMDG calls these operations Events, as it are Events happening to the PMDG software. So I will keep that naming.

There are 754 events defined in the NGX SDK. In order to make programming easy I made a header file that you can refer to in your Arduino program. You find it here.

Data

We also want to access the PMDG data, like indicators, displays, switch positions etc.  The SDK makes 433 data elements available.

For a full list I refer to the PMDG NGX SDK and the header file that you need for the Arduino program, find it here.

PMDGDataEventServer program

The program allows you to connect up to 6 serial input devices to send Events and Receive data to and from PMDG. All events and data fields from the PMDG NGX are implemented.

Programming Events in Arduino software

The following code snipped show how your Arduino can send an Event to PMDG:

#include "PMDGEvents.h"
char line[64];
int Event;
…
… 
  Event = EVT_OH_LIGHTS_TAXI;
    sprintf(line, "%d=%d", Event, MOUSE_FLAG_LEFTSINGLE);
    Serial.println(line);
…

This piece of code will toggle the taxi light switch. I used the definition names of the Events 1-to-1 from the PMDG SDK.

Programming to receive PMDG data.

The program will only send PMDG data values after they are registered. So your arduino program has to ask for them.
A registration request can be done at any time, but you would normally sent it right after starting the connection. Once a data element is registered, the program will send the value of that data every time that data changes.

Note: In the SDK some data elements are defined as arrays. For the serial interface I make the array elements available per single item. This is why I had to extend the SDK defind data name with a number. E.g., In the SDK the MCP FD annunciators left and right are defined in one array defined as ‘MCP_annunFD’(with 2 elemets). For the serial interface I created 2 single definitions ‘ MCP_annunFD_0’ and ‘MCP_annunFD_1’. For all others I kept the PMDG SDK definitions.

How data members can be registered can be seen in the code snipped below. It registers 4 of the MCP displays values.

#include "PMDGData.h"
char line[64];
…
…
 sprintf(line, "REG:%d:%d:%d:%d", MCP_Course_0 , MCP_IASMach , MCP_Heading , MCP_Altitude);
  Serial.println(line);
…

The resulting message the  Arduino will send to the Server is :”REG:234:236:240:241″

The numbers 234, 236, 240 and 240 in above example are index numbers into the list of data elements. You can use the numbers direct but coding the definitions from the header file will make reading your code easier.

The DataEvenserver will register these data elements.
Right after registration the progam sends the data (if available) and after that, every time one of these data fields change, the Arduino will receive a message:

E.g. when the MCP_Course left is changed to 123 the message is: 234=123.

Look at the REG line above. The MCP_Course_0 is index 234.

Very simple. I did not create a complex protocol.

Non integer data values

The program will only send integer numbers. However there are a few values within the data from the PMDG NGX that are non-integer.

One good example is the variable: MAIN_TEFlapsNeedle_0. While the flaps are moving the value changes as decimal number from 0,000 to 40.000. Yet, without change you would only receive integer values so 1 – 40.
If you want to implement a move gauge you need the decimal value as well. To make things easy on the Arduino side, the program allows you to request a data value multiplied by 100. No need for making the Arduine recieving function to expect also floating numbers.

You do this bij placing an ‘*’ behind the request for that variable.
Below is the request line where the last request variable is followed by ‘*’

 sprintf(line, "REG:%d:%d:%d:%d:%d*", MCP_Course_0 , MCP_IASMach , MCP_Heading , MCP_Altitude, MAIN_TEFlapsNeedle_0);
 // line wille be :"REG:234:236:240:241:296*"
 Serial.println(line);

Note the ‘*’ behind the last %d* in the first line.

Now when the flaps start moving you will get integer values 0 – 4000. I would think this is accurate enough to make a nice moving plaps gauge.

Be aware that the Gauge division between stops is not liniar. So you need to make your program move 1/12 of a circle to move from 0 to 1 (values 0 – 100).

Device ID

Your Arduino can also send a device ID to the PMDGDataEventServer. This for the purpose of display. The COMMnn indication on the main screen will be replaced with the device ID you send. Best is to send this line within the  ‘register variables’ routine.

 Serial.println("ID:PMDGtest"); // Identify this (max 12 char)

 Arduino program to access PMDGData.

Your Arduino program will need a ‘receiver function’ that waits for a full line to be received. Then, process the received message. Make sure this serial receiver is non blocking. In the example program you can find how this could be done.

You will find a test / example program here.

Look at the example ino and read on.

The program send a toggle (left mouse click) to the Taxi light switch every second and display some data from the MCP and the Flaps Value to a LCD.

Within the Setup function include:

RegisterVariables();

Here you register all variables that you want to receive from the PMDG NGX when their value changes. Here you should als send your device ID to be displayed.

Within the main loop you include a part that collects serial input data until a full line (‘\n’) is recieved. This line can read either ‘Ready’, ‘Close’ or ‘vvv=nnn’ (without the quotes).

The function HandleInput() is called to analyse / parse the input received from the PMDGDataEventServer.

If you receive “Ready” it means the PMDGDataEventServer just (re-)opened the serial port. If you receive Ready you should (re)send the Registration message for variables you want to receive.

Any other message will be in the format ‘vvv=nnn’. My example program the input handler puts both values in ‘ivar’ and ‘ivalue’.

The main loop can now react on the variable just received. In the example, this is displaying the value on a LCD screen. Your program will need to handle more variables, but in fact this is very straight forward. (more of the same)

Do not forget to clear ‘ivar’ to indicate that it has been dealt with so the loop will not go and handle it a second time.

Sending input to controls, buttons, switches etc.

About Events

All controls of PMDG can be operated using some mouse inputs on the screen.

With your Arduino code you can send mouse inputs to any of the Events listed in the SDK.  In a lot of cases you can also send direct data to an event. Data is always a positive number. *)

In the PMDG SDK, mouse flags are impossible numbers. So I decided to use negative values that the program then will translate into the correct ‘impossible number’. If you use the header file there is no problem sending the correct mouse event.

A lot of Events can also be set direct data numbers.

The Taxi light switch (like most switches) can be toggles by a left_mouse click. This is what you will do on the screen. What you can do on the screen you can do progammaticaly. However, as a panel builder you want to set the switch to a defined ‘on’ of ‘off’ state direct. I better talk about set to ‘up’ or ‘down’ state. You can do this by sending a direct value to that switch Event.

In PMDG, to set a switch in the UP position you have to send a ‘0’ to the event. Regardles it this means ON of OFF for that function. For the next down position you send 1, 2 etc. Rotary switches the same, the most counterclockwise position is 0;

In our taxi light example:
to set the taxy light switch to the  ‘down’ postion you program line should read:

Event= EVT_OH_LIGHTS_TAXI;
sprintf(lijn,"%d=%d", Event, 1);
Serial.println(line);

Other examples of event that you would like to set direct are:

To set the EVT_OH_ELEC_DC_METER switch can be directly set to a position using numbers from 0-7.

EVT_MCP_COURSE_SELECTOR_L. To change the MCP Left course, you can send -1 (right click) or -3 (left_click) mouse events. But you can also set the left course value direct using event  EVT_MCP_CRS_L_SET (index 744) and send it a value between 1 and 360 to direct set its value.
Btw: I would not use this method for the MCP Course, Alt etc. Better to use left or right mouse button clicks for every rotary encode click. I used this just make the point of direct set.

Before programming you can test what event value works best for you, using the test facilities of the program.

*) If you would like to set the Vertical Speed direct ‘EVT_MCP_VS_SET’ this is how PMDG has implemented this in the SDK:
– Sets MCP VS (if VS window open) to parameter-10000 (e.g. send 8200 for -1800fpm)

Starting the program

This is the main screen of the program. For aviation enthusiasts the layout should look familiar

Schermafdruk 2016-01-02 17.42.43

The L1, L2 , R1 and R3 keys are used to select the serial port of your serial device(s).  When you press one of these keys a little window will open to select the right serial communications port and set its parameters like baud rate.

The selection is stored in the program’s settings. So next time you start the program, the ports will be automatically opend.

If opening is successful the port name will be displayed in green colour. If your Arduino code implements sending an ID. The ID will be displayed here.

L3.
Here you set of clear the Auto Connect facility. If set to ‘yes’ the program will auto connect to FSX right after start. You will want to use this once all development is done and configuration is stable.

R3.
Manually connect / disconnect from FSX. If FSX connection is established the word Connect will be displayed green. Above the word connect, the plane you loaded will be displayed.

L4.
Set / clear start hidden.  Next time you start the program will hide, and a tray icon will be set. Double click on the icon and the program will be shown.

R4.
Manually hide the program windown.

Note: as of today the PMDGDataEventServer allows to connect 6 Arduino’s. ( 6 serial interfaces). The pictures on this page show 4 ports.

Test facility

L5.
Pressing L5 will open the PMDG Data variable window. Here all available NGX data elements are shown with their definition name.

Schermafdruk 2016-01-02 17.48.03

The ‘INDEX’ column is the program’s internal index. This is the number your program needs to send to register the data element.
The ‘VALUE’ column will be filled with the Data from the NGX (when FSX is connected). The first time you open this window it will look like the program is frozen for a few seconds. This is normal behaviour.

The ‘SP_REG’ column indicates which of the serial ports has registered this particular data element. As you can see Port 4 is connected to COMM10. The COMM10 device has executed the Register command for MCP_Course_0, MCP-IASMach, MCP_Heading and MCP_Altitude. This is the command in the code snipped above.

This way you can check your registration code works and is sending the right registration request.

L5 (second press)
If you press L5 for the second time the Events window will open. Here you see a complete list of all Events possible with their index value.

Schermafdruk 2016-01-02 17.51.47

The column value can be used to test the event. Type an event value in the cell next to the event you want to test. and then Left_Click on the value. The command will be sent to FSX/PMDG.

L5 (third press)
Window will close to standard.

Input line

You can directly enter an Event=Value on the input line. Then use Send button and the Event will be executed to FSX/PMDG.

You can enter multiple Events separated by “:”
E.g: 744=90:748=280:749=28000 [Send]

Btw: Your Arduino program can also send multiple Events separated by “:”.

Logging Window

Dragging the program window to the right will open the log window. Here reived Event can be seen from the Serial port and the resulting action.

Data send to the serial port is also displayed.

The logging will only be turned on when the Logging window is opened. Logging will automatically turned off when you drag the window close.

If you are interested in the program:

Register yourself at my forum PHPJJ Forum. You will find a download link (registerd users only) in the first topic of the PMDGDataEventServer for Arduino forum. The example sketch is included here.

You can send me a message on this page.

Mouse Events

In the PMDG SDK mouse flags are impossible numbers so I decided to use negative values that the program then will translate into the correct impossible number. The table below is part of the PMDGEvent.h (header file)

#define MOUSE_FLAG_RIGHTSINGLE -1
#define MOUSE_FLAG_MIDDLESINGLE -2
#define MOUSE_FLAG_LEFTSINGLE -3
#define MOUSE_FLAG_RIGHTDOUBLE -4
#define MOUSE_FLAG_MIDDLEDOUBLE -5
#define MOUSE_FLAG_LEFTDOUBLE -6
#define MOUSE_FLAG_RIGHTDRAG -7
#define MOUSE_FLAG_MIDDLEDRAG -8
#define MOUSE_FLAG_LEFTDRAG -9
#define MOUSE_FLAG_MOVE -10
#define MOUSE_FLAG_DOWN_REPEAT -11
#define MOUSE_FLAG_RIGHTRELEASE -12
#define MOUSE_FLAG_MIDDLERELEASE -13
#define MOUSE_FLAG_LEFTRELEASE -14
#define MOUSE_FLAG_WHEEL_FLIP -15
#define MOUSE_FLAG_WHEEL_SKIP -16
#define MOUSE_FLAG_WHEEL_UP -17
#define MOUSE_FLAG_WHEEL_DOWN -18
#define THIRD_PARTY_EVENT_ID_MIN -19

continuation from top

PMDGDataEventServer for PMDG NGX Revision Sp1D

With the release of SP1D Precision manuals also released an updated SDK. I did not make a list of all the additions and corrections that were made. I noticed that one bugg I had with SP1C is the MCP AT Arm Annunciator is now solved.

Number of Data fields increased from 433 to 506 and the number of event went from 433 to 506. I noticed Electic Metering and IRS display items available.And more.

The currend PMDG Data Event Server will continu to work with SP1D.

I made an updated version of the PMDGDataEventServer incorporating all Data and Events available through the SDK of SP1D.

If you update to SP1D, you should use this new version. No need to change your SIOC scripts.

CDU SCREEN

Until now, if you want to make a CDU we had to use a LCD  panel connected as a screen to our graphics card. This was the only way to get a decent CDU screen. If you cannot or do not want this people have come up with screen capture programs, For all these solutions the CDU had to be displayed one way or the other on one of your screens,

One of the major additions to the SKD is that PMDG made the CDU screen content available. This allows us to build a CDU and build the screen on external hardware.

CDU Screen avaible through serial interface.

I extended this program with the ability to request the CDU screen to be send to you over the serial interface.

CDU can now be implemented as stand allone panel using any suitable hardware. Not restricted to Arduino e.g. Raspberry PI or system alike.

I could imagine to implement a CDU on i-Pad, or a Android pad, using the touch screen. Such a pad in portait mode would make a great CDU.

Throught the PMDGDataEvents servel all ellements are available. Events for the keyboard input, the indicaters and the CDU data.

The screen data is available as 14 lines with 24 characters. Each character is 3 bytes: Symbol, Color and Attributes. Total data per screen 1008 bytes.

TCP/IP Interface

I added a TCP/IP server interface. So any device on your network can access PMDG NGX Data and Event.

Once started the PMDGDataEventServer allows you to connect over TCP/IP. You can specify the port that the program listens too on the main screen.

The protocol is very simple and does not different from the protocol used over the serial interface.

Protocol specification:

The only exception is the start of the connection.
Using the Serial interface case, it is the program that makes the connection to the already connected device (Arduino). In the TCP/IP case it is the client device that start to seek the connection. That is why, after connection is made your program is expected to send:

PMDG:Hello

The PMDGDataEventServer will then answer with:

Ready

Now that is all the difference, now everything is the same as for the serial interface.

Now the communication is open and you should/can send registration message in the format. See above.

You can send any number of REG: messages, So one variable at a time or a logical grouping of variables, which ever makes your programming easy and readable.

After reception of a REG: message (if  the PMDGDataEventServer is connected to FSX) the server will immediately send you the values of the requested variables in the format:

vvv=nnn:vvv=nnn:

You can expect any number of nnn:vvv groeps per message. Communication is asynchronious, so you do not wait for answer. You do not need to send acknowledge from your program.

After this every time a requested PMDG value changes, you will receive a message in the same format as above, with the new value.

NOTE: I require that the REG: message spels REG in capitals.

Sending Events

Sending events does not differ from the serial interface:

eee=vvv:eee=vvv:

eee= Event number
vvv = Event Value

You can send any number of events in one message by separating by ‘:’

CDU Screen data

A special case of Data that we can request from PMDG is the CDU screen data. Both CDULeft and CDURight can be made available.

If you are going to use this facility be sure to make the folling changes to the 737NGX_Options.ini file. If you are not going to use them, or just one of them, leave the unused out.

//[SDK]
//EnableDataBroadcast=1
//
// to enable the data sending from the NGX.
//
// Add any of these lines to the [SDK] section of the 737NGX_Options.ini file:
//
//EnableCDUBroadcast.0=1
//EnableCDUBroadcast.1=1
//
// to enable the contents of the corresponding CDU screen to be sent to external programs.

Requesting the CDU screen data is no different from requesting other data.

For the left CDU send:

REG: 1000:

You will ofcause use the definitions from the PMDGData.h file that I provide.

#define CDU0Screen  1000
#define CDU1Screen  1001

CDU Screen data is then send to your program when available.

CDU screen data message does not differ from other data messages, except for its size. You will receive:

1000=scfscfscfscfscf….

The screen data is 14 lines, 24 characters each. For each character you receive 3 bytes.

s = Symbol.
c = Color
f = flag

Here is the part of the PMDG NGX SDK explaining the meaning of s,c, and f

struct PMDG_NGX_CDU_Cell
{
unsigned char Symbol;
unsigned char Color; // any of PMDG_NGX_CDU_COLOR_ defines
unsigned char Flags; // a combination of PMDG_NGX_CDU_FLAG_ bits
};
// NGX CDU Screen Data Structure
#define CDU_COLUMNS 24
#define CDU_ROWS    14
// NGX CDU Screen Cell Colors
#define PMDG_NGX_CDU_COLOR_WHITE        0
#define PMDG_NGX_CDU_COLOR_CYAN          1
#define PMDG_NGX_CDU_COLOR_GREEN        2
#define PMDG_NGX_CDU_COLOR_MAGENTA      3
#define PMDG_NGX_CDU_COLOR_AMBER        4
#define PMDG_NGX_CDU_COLOR_RED          5
//
//NGX CDU Screen Cell flags
#define PMDG_NGX_CDU_FLAG_SMALL_FONT    0x01   // small font, including that used for line headers
#define PMDG_NGX_CDU_FLAG_REVERSE       0x02   // character background is highlighted in reverse video
#define PMDG_NGX_CDU_FLAG_UNUSED        0x04   // dimmed character color indicating inop/unused entries

So you will get a message of 1008 bytes . Symbols are ASCII characters, except for 0XA1, 0XA1 (left and right arrow). The message is coded with 8 bits per byte.

The PMDG software sometimes throws about  6-8 screens to the software in quick succession to its Symconnect interface.  Test showed that if I would send every update to the client, (even on localhost) the buffers of the receiving TCP/IP socket got overflow. This lead to missing other messages.
Therefore the PMDGDataServer maintains the actual CDU screen accurate internally and send the actual status every 250ms to the client. This has proven to be fast enough for a good feel. I could eventually change it to every  200 or 100 mS.

Virtual CDU

I have created a CDU program on Windows using C#. It is fully functioning on my Windows pad laptop with detachable touch screen. This is a full functioning screen based CDU.

Now also available a CDU screen only that you can run on any network PC. You now can get you CDU screen of any PC in your FSX network, so you do not need to get the screen from a detached CDU screen on the FSX PC. The screen is borderless and fully sizable. The program is available in the downloads.

I hereby invite Android programmers to create a virtual CDU for Android pads. Would be wonderful.

With this facility you can create your own hardware CDU without the need for screenscraping for the display. The demo it put on YouTube was recorded with connection over WiFi.

Test Facility

In version 1.26.ST.D uploaded on April 4, 2017 I created an extra test facility using the input line.

What ever you type in the input line will be handled as if it was send by Port 99. Port 99 is not a physical port, just a test number.

Sending Events:

Like your arduino would do:

64=-3:  now Press the Send Button, and observe that the Taxi Light Toggles every time you press [Send].
64=1: [Send] observe that the taxi light switch goes into the UP position.
64=0; [Send] Taxi light switch in DOWN position.

With this you can test what your arduino code should send to get the correct effect you want. Look up the Index in the Event Window (press L6 twice).

Variables:

As whatever you type and send in the input line is send to the program as if it was send from Arduino on Port 99 you also can test the REG: command.

If you type: REG:234:240:[Send]  The program will send you the value of the MCP_Course (left) and MCP_Heading whenever its value(s) changes.
Notice that the SP_REG field in the Variables window (press L6 once) now record 99, as the port that requested that value.