2-Axis Potentiometer Joystick:
Integration With Flightgear Flight Sim

This is how I managed to make an analogue joystick / control column with potentiometers work with Flightgear flight Simulator (FGFS) over serial data comms, using an Arduino as the interface. Getting this to work was a great learning curve!

This was carried out with a Linux Mint PC, but shouldn't take too much head-scratching to use it for Mac or Windows.

This guide can be used for any home-made / home-brew / diy joystick or control column, rudder pedals or throttles - Just watch out for cheap carbon tracked potentiometers / variable resistors; go for high quality (if possible, precision potentiometers) to avoid "jumpy" controls.

I've tried to keep this guide technically as simple as possible, but have written it in a way that hopefully fully explains each step. I've split the guide into two main chunks, one for Arduino programming, the other for Flightgear programming.

Overview

Flightgear Flight Simulator (FGFS) allows modification of internal parameters (or Properties) by several means, the method used here is serial data arriving at the PC from a stick and some electronics.

This involves configuring Flightgear to make use of the received data, and setting up an external microprocessor (in this case an Arduino Uno, but also tested with an Arduino Micro) to send the data in the format and order that Flightgear is expecting to receive it.

The process flow is such:

Hardware > Arduino inputs > Serial over USB > PC > XML config > Property Tree

Joystick

I was lucky, as a friend gave me this joystick / control column. It is ex-Belgian Air Force, and was used for F16 pilot training / simulation, so is a nice piece of history.

It is made by Measurement Systems Inc (Ultra Electronics), and contains two precision potentiometers on a very nicely made 2-axis gimble.

The stick also has a trim hat and trigger switches - these were not used for this article.

Arduino

Circuits

This wiring (and the code below) works seamlessly with the Arduino Uno and Micro boards.

To supply 5Vdc to one end of the potentiometer and ground to the other end, these wires are connected to the 0V and 5V available on the Arduino boards.

The potentiometer wiper is connected to an analogue input of the Arduino (A0), giving it a variable voltage between 0V and 5V. This was repeated for the second axis (on pin A1).

I connected the X axis (roll / aileron) to A0, and the Y axis (pitch / elevator) to A1.

Arduino Program Code

The following code defines the input pins, and defines the variables used as outputs as float data type.

I modified the analogRead function to include an adjustable scaling factor (divide by 512), and an adjustable offset (subtract 0.99), these figures will be different for each stick and probably each axis depending on the hardware used, more on this under Calibration further on.

I've tried to annotate the code to explain how it works:

/*
Flightgear hardware integration 01: Stick X and Y only so far.

Scott Bouchard UK www.scottbouch.com 14-06-2017
*/

const int stickxio = A0; //Define stick aileron (x) input
const int stickyio = A1; //Define stick elevator (y) input

float stickx = 0;        //Start aileron (x) central
float sticky = 0;        //Start elevator (y) central

void setup() {
  Serial.begin(9600);    //Open up serial communication to PC
}

void loop() {
  stickx  = (analogRead(stickxio)/512.0)-0.99; //Calibration span and offset
  sticky = (analogRead(stickyio)/512.0)-0.99; //Calibration span and offset

  Serial.print(stickx);  //Send aileron position
  Serial.print(",");     //Variable (var) separator
  Serial.print(sticky);  //Send elevator position
  Serial.print("\n");    //Line separator
  
}

At this point, you can test the Arduino using the Arduino IDE serial monitor, and should see line after line of two sets of figures to two decimal places separated by a comma, for example with the stick moving, you should see something like this:

0.05,0.13
0.07,0.10
0.09,0.08

etc...

These figures shoud change as the stick is moved around. If they don't, go check the code and wiring!

The next step is to calibrate the controls so that the figures seen on the serial monitor are of the order of magnitude that Flightgear expects them.

Calibration

Calibration of the stick was achieved by adding gain and offset to the Arduino code, making sure when the joystick is at rest against the springs, the aircraft flies flat and level, and also making sure the mechanical full scale to physical end stops matches up with the software range.

To have no affect over Flightgear's aileron and elevator, the serial data should read 0.00,0.00 with the stick in it's central / at rest position. Full deflection is -1.00 to 1.00.

Using the Arduino serial monitor, the gain and offset can be adjusted with an aim to achieve close to the following:

Position Aileron Elevator
Central 0.00 0.00
Left -1.00 0.00
Right 1.00 0.00
Forward 0.00 1.00
Backward 0.00 -1.00

In reality, it's best to adjust the span and offset just so that they stop around 0.97 or 0.98, as if your hardware end-stops move, allowing the Arduino to send less than -1.00, or greater than 1.00; Flightgear gets confused, and you crash your aircraft.

You could always put a limit in the Arduino code to limit the output between -1.00 and 1.00, but this uses up program space (better used for more inputs), where by calibrating it so the mechanical end stop equates to nearly -1.00 and 1.00 saves this overhead, and simplifies the code.

If the potentiometers travel fully form end to end, the arduino will read the 0V to 5V range as 0 to 1024. The scaling I've used in this example (0 to 512) is just using half of the full range, as in reality a joystick will not move through the full range of the potentiometer, just using half was about right for my stick's travel.

If you find the axes are operating backwards (ie: right is left) reverse the polarity of the potentiometer by swapping the 5V and 0V connections over.

My joystick's central position is almost exactly in the middle of the reange of the potentiometers, only slightly off, so an offset of 0.99 was used to correct it. With having some wasted travel at either end of the potentiometer's scale, the mid point does not need to be so accurately aligned with the stick's central position, as the offset can easily correct for the difference.

Emphasis should however, be put on getting the joystick's physical central position to read 0.00,0.00 on the serial monitor, so if you let go of the stick, the aircraft will carry on the same path without veering off - ie: like a well trimmed real aircraft.

Flightgear

Flightgear Protocol Code

Flightgear needs some guidance as to what to do with the lovely serial data. And there is a very convenient way of telling it. You create a text file, containing "chunks" in order of the data you are receiving over serial - so the first chunk will relate to the first digits of serial data (before the first comma) received, and so on. In this example we are sending two parameters, so we need two chunks.

I created a file called hardware.xml (you can call it whatever you like), and saved it to /usr/share/games/flightgear/Protocol directory. This xml file contained the following lines:

Note how the line separator (\n) and var separator (,) match the Arduino code under Serial.print, this is how flightgear knows what data to expect in what order. The order of the chunks has to match the order of the data being made available on the serial connection from the Arduino, so that flightgear assigns the correct data to the correct chunk.

Within the Chunks, the Nodes are the properties within flightgear that your data is affecting. Plenty more information on this is provided in the program's documentation in the flightgear directory (see References).

Make Flightgear use your .xml file to read the serial data

So, now we have the Arduino making the serial data available, and we have told flightgear that when it receives the data, how to deal with it - all that's left is to tie the pieces together, ie: tell flightgear where in the computer to look for the serial data, and tell flightgear which .xml file corresponds to the serial data.

Firstly find out the serial TTY (or COM for Windows) port that the Arduino board is on; an easy way is to look at the Arduino IDE / Tools / Serial Port. If you are using the on board USB serial converter, it will appear in Linux prefixed as ttyACM, if you are using your computers RS232 serial port, then it will appear prefixed with ttyS.

Tie it all together

So, we have an Arduino spitting serial data at the serial port of the PC, we have an xml file configured to make sense of the data received, but there is one more step, we need to tell flightgear which XML file to apply to which serial port.

We used to use FGRUN to configure these settings, however now they are expressed as a command options in the Flightgear Launcher > Settings > Additional Settings box as:

--generic=serial,in,30,/dev/ttyACM0,9600,hardware

Breaking this command down into chunks:

Protocol:generic
Medium:Serial
Direction:in
Sampling frequency (Hz):30
Serial port path:/dev/ttyACM0
Baud Rate:9600
XML file name:hardware (no ".xml" needed)

Note: if starting Flightgear from the command line, these above command options can be appeneded there.

Enjoy!

That should be all you need for a simple 2 axis Joystick and Flightgear!

Further Development

Using this model, it will be straightforward to expand to rudder pedals, throttles, and then try out digital signals for switches etc... and then move into the realm of taking outputs from FGFS and controlling real physical indicators!

References

Documentation on your PC (under a Linux install):

wiki.flightgear.org/Howto:Use_Arduino_with_FlightGear on the Flightgear Wiki was good for a point in the right direction, but the Arduino software is a little over-complicated for what's needed, and it doesn't cover calibration. It also has a warning about unplugging and reconnecting the Arduino each time, I found this was not necessary with a Rev 3 Arduino Uno.o

Thank-You's

Thanks go to my friend Mike for giving me the joystick, please see his site: