When starting this project a lot of test code for Arduino was written, to clarify all aspects of data exchange between Arduino and X-Plane, using inbuilt UDP protocol of X-Plane. Here are some of these code samples - Evolution of Arduino I/O interface.
In the earlier test code the "DATA" type message protocol was used, and the size of the program code was huge.
Later versions gradually become more compact, the "CMND" and "DREF" type messages are now used as primary data protocol. The "DATA" type is used only for reading data from X-Plane. (More info about X-Plane's UDP data input structure here: http://www.nuclearprojects.com/xplane/xplaneref).
Later, since summer 2014 all this work and test sketches were revised and organized in a single Arduino library that includes functions for network interaction between Arduino and X-Plane, functions of receiving and decoding any of X-Plane incoming data, functions for transmitting data to X-Plane. It makes Arduino programming for users as simple as possible - you only need to define the pin number and its function (button, switch, encoder, axis, etc.).
The Arduino program monitors all inputs in each cycle, but if no changes were detected, it does not transmit anything to X-Plane. if the program detects that the position of any of the controls was changed, it immediately sends the specific packet via Ethernet. In general, the whole process of data sending (in newest B58 code editions) takes 1-5 milliseconds in one cycle.
In the last versions of the Baron code the XPData library is used and data from X-Plane is received using ARDref plugin (no need to define data groups for output in the "Data Input/Output" X-Plane menu).
Each UDP packet to be transmitted to X-Plane has a Header. It consists of 5 bytes. The first four bytes are ASCII (decimal) code for one of the key words: DATA, CHAR, DREF (and many others types of data described in the documentation). The fifth byte should be "0" (decimal 48) when sending to X-Plane, or "<" (decimal 60) char when used by X-Plane (though both are working when sending).
68 65 84 65 48(60) | header DATA< | Used when X-Plane sends data selected in the "Data Input/Output screen" |
68 82 69 70 48(60) | header DREF | |
67 72 65 82 48(60) | header CHAR | |
77 79 85 83 48(60) | header MOUS | |
77 79 85 83 48(60) | header MENU | |
-- | ...and so on |
sending/receiving)
Header | Index | 32 bytes of Data - 8 sim parameters in the format of 4-byte floating point numbers | Next index | |||||||
68-65-84-65-60 | 131-0-0-0 | 166-138-16-71 | 193-114-2-195 | 58-143-139-70 | 244-238-135-59 | 61-207-225-58 | 0-192-121-196 | 0-192-121-196 | 0-192-121-196 | 133-0-0-0 |
The next four bytes following the header are the index of the Group in the Data Input & Output screen in decimal from 0 to 133 (only the first byte is used):
13 00 00 00 | group index #13 - trim/flap/slat/s-brakes |
The next 32 bytes represent the data of 8 parameters of one group (4 bytes x8 ) . Each 4-byte block is a floating point value of one parameter.
Even if this parameter is not such numeric value as speed, but only a boolean 0 or 1, it is also represented as 32-bit floating point number split into 4 bytes.
Three useful numbers in the 4-byte f/p
0 0 0 0 0 | "0" | can be used as a numeric 0 and a boolean "0" |
0 0 128 63 | "1" | can be used as a numeric 1 and a boolean "1" |
0 192 121 196 | "-999" | the number -999, send when the parameter in this group should not to be changed |
See the - IEEE 754 floating point converter
To control X-Plane using this DATA method, Arduino should send data to X-Plane in the same format as the simulator sends it (program should form the same packets of data, i.e. [header DATA0]+[Number of the group]+[8 float Data parameters] ). That is a highly inconvenient way.To be correctly received by X-Plane, the whole string length should be 504 bytes (4-bytes Value + dataref string + ending string)
I.e. for dataref for com1 - "sim/cockpit2/radios/actuators/com1_standby_frequency_hz[0]" (58 bytes) we need to add 500-58=442 "space" char(char 32).
501st byte should be "ending NULL char" (char(0)).
DREF Header | Value (10920Hz) | Dataref string | ending fields |
"DREF0" | 0, 160, 42, 70 | "sim/cockpit2/radios/actuators/com1_standby_frequency_hz[0]" | " " |
When using UDP DREF method, Arduino sends data in the format: [header DREF0]+[Value in float]+[X-Plane dataref string]+[End sequence].
If you're using our XPData Arduino library, you don't need to add all the ending "space" characters manually - the library function will do it for you.
CHAR is easiest way of sending commands to sim, but this method is limited by the number of keyboard symbols and we need to assign each command a key in X-Plane.
CHAR Header | character |
"CHAR0" | "D" |
Another very convenient way to send control parameters to X-Plane. After all, I've chosen this method for most of the controls, after reading the article "DataRefs Vs. Commands" on "X-Plane Developer". CMND combines both simplicity of CHAR method and usability of DREFs
CMND Header | Command string |
"CMND0" | "sim/flight_controls/landing_gear_down" |
When DATA format was used to receive data from X-Plane for this Baron-58 simulator Arduino received 18 parameters ( 11 groups of DATA chosen in the "Data Input & Output" menu of X-Plane), to output them to indicators/annunciators and servos (fuel and bus gauges).
A listing of the UDP data groups is located at the Data Input & Output screen in X-Plane. Currently there are 133 channels/groups, each of them may contain up to 8 parameters, or up to 1064 data refs altogether.
To find out what of the parameters each channel includes, select some groups for output to the Cockpit View when flying. Using the Datarefs Editor, we can correlate this parameters with corresponding datarefs.
DATA groups received from X-Plane by UDP (checked in the Data Input & Output Screen):
Parameters in the DATA groups used by Arduino for :
(In the latest versions of the code the XPData library is used and all DataRef values from X-Plane are received using ARDref plugin (no need to define data groups for output in "Data Input/Output" X-Plane menu).
UDP protocol is targeted to the transfer byte (char) stream, so, in order to transmit a 32-bit floating point value, before submitting it just splits into 4 binary bytes and then transmitted as 4 separate symbols.
Take for example the number 4500 feet. Its floating point representation is the next sequence of bits (first left is the sign bit):
01000101100011001010000000000000 | - floating point number +4500 | |||
01000101 | 10001100 | 10100000 | 00000000 | - split into 4 bytes |
69 | 140 | 160 | 0 | - ASCII (decimal) representation |
So, X-Plane will send the value 4500 as a 4-byte block 0-160-140-69.
Upon being received by the controller these 4 bytes must be read back as a floating point value. This does not need to be a transformation of variables, we only need to unite 4 bytes back to the 32 bits, which must be a variable of floating point type.
The best way to do it is to use a special data type structure of C, that called union.
The union is building such a memory structure where two or more members can share the same location in the memory. It means the program can access it as different data types, e.g. assign as bytes and read as floating, and vice-versa. A union variable represents the value of only one of its members at a single moment.
union { char name1[4]; // --- first variable - char or byte array float name2; // ---- second variable - float } proc; // ---- put the 4 bytes in proc.name1, get the float from proc.name2 (or backwards)
As written above, if a parameter is presented as boolean 0 or 1, it is also transmitted as a 32-bit floating point number. But in this case we may omit the union use. All we need is just to check if the 3rd or 4th byte is 0 or not.