Flightgear Yoke and Panel🔗
Posted by Médéric Ribreux 🗓 In projects/ flightgear/
Introduction
When I was a child (probably in 1990), I discovered the fantastic world of flight simulation with Flight Simulator 3. For a geek kid, it was really interesting because it mixed computers and another scientific and enthusiasts domain: how to fly an aircraft!
I even persuaded my parents to buy me a book about Flight Simulator, just to be able to learn how to use this software. I remembered to try to land to Chicago Meigs and to explore San Francisco. I even really enjoyed one long simulation during the night (from San Francisco to Los Angeles) with a Learjet. It was fantastic!
Years ago, I discovered Flightgear which is the free software reference in flight simulation. But whenever I tried to use it with my keyboard, it was really difficult to fly any craft with it. Flightgear offers far more controls and outputs than Flight Simulator 3 or 4. Managing them with a keyboard is nearly impossible.
I was really decided to buy a yoke some years ago but I forgot to do so because I started a new project. This year (2019), I decided to improve my electronic skills a bit and one of my project was to try to build a yoke for Flightgear, something really cheap, just a prototype.
Finaly I found that it could be interresting to add more controls to the yoke, thanks to VRInsight Flight Master Yokes. The idea of adding switches to a panel was really interesting.
With time and hard work, I managed to build this yoke and its panel. Here is a summary of what I have done. I tried to put the maximum of details.
Be careful that this production is far from being more than just a prototype. There are a lot of ways to improve it. Furthermore, I have never fled a true aircraft nor have used a flight simulation commercial yoke system.
So, do not think that this yoke is what you need to build to master Flightgear. But if you would like to reproduce it, you are free to do so.
Some pictures
What is emulated?
Here is the list of controls:
- Rudder and ailerons.
- Handbrake.
- Magnetos and starter.
- Pitot heat.
- Avionics.
- Master Alternator.
- Master Battery.
- Strobes.
- Nav lights.
- Beacon lights.
- Landing lights.
- Taxi lights.
- Gear Up/Down.
- Throttle.
- Mixture.
- Flaps.
- Primer.
- Fuel pump.
- Carburetor heater.
- Fuel tank selector.
Build it
Bill of materials
Component | Number | Manufacturer | Datasheet | Price (Total) |
---|---|---|---|---|
Mechanics | ||||
MDF 810x405x9mm | 1 | China | Datasheet | 4.1€ |
MDF 1150x610x9mm | 1 | China | - | 8.5€ |
Steel corner braces | 10 | ? | Datasheet | 3.9€ |
Angle bracket W40mmx60mmx60mm | 1 | Simpson Strong-Tie | Datasheet | 1.05€ |
Nail plate NP15/40/120 | 1 | Simpson Strong-Tie | Datasheet | 1.05€ |
Table chrome leg L250mm, Ø30mm | 1 | ? | Datasheet | 4.61€ |
Plummer block bearing UCP206 Ø30mm | 1 | TRI | Datasheet | 9.96€ |
Ball bearing drawer slide L=251mm | 2 | Handix | Datasheet | 3.56€ |
Rack CMLN1-150 | 1 | TRI | Datasheet | 4.32€ |
Pinion 15 tooth EM1015 | 1 | TRI | Datasheet | 1.2€ |
Electronics | ||||
Rocker Switch R5ABLKBLKFF0 | 2 | E-Switch | Datasheet | 2.54€ |
Rocker Switch R4ABLKREDIF0 | 2 | E-Switch | Datasheet | 1.7€ |
Rocker Switch R4FBLKBLKEFO | 1 | E-Switch | Datasheet | 1.0€ |
5 positions 30° Rotary Switch A10505RNZQ | 1 | C&K | Datasheet | 4.44€ |
4 positions 30° Rotary Switch A10405RNZQ | 1 | C&K | Datasheet | 4.21€ |
4 positions 90° Rotary Switch A12405MNZQ | 1 | C&K | Datasheet | 4.17€ |
Knob 450-SL2612 | 2 | Eagle Plastic Devices | Datasheet | 2.46€ |
Knob MPKES70B1/4 | 3 | Apem | Datasheet | 3.36€ |
Toggle Switches 2FA53-73/.187 | 9 | Carling | Datasheet | 15.84€ |
Rotary Potentiometer PDB181-E420K-502B | 1 | Bourns | Datasheet | 0.935€ |
Rotary Encoder PEC11R-4030F-N0024 | 1 | Bourns | Datasheet | 1.14€ |
Linear Potentiometer PTB0143-2010BPB103 | 2 | Bourns | Datasheet | 8.22€ |
Arduino Mega 2560 | 1 | Arduino LLC | Datasheet | 31.57€ |
Connectors and cables | ||||
Terminal Tabs Connectors 94512-0101 | 8 | Molex | Datasheet | 0.656€ |
Terminal Tabs Connectors 35718-1010 | 18 | Molex | Datasheet | 1.64€ |
Crimp Male Terminal (x100) 70021 16-02-0107 | 1 | Molex | Datasheet | 4.5€ |
Crimp Female Terminal (x100) 70058 16-02-0086 | 1 | Molex | Datasheet | 5.2€ |
Crimp Housing 70066 50-57-9002 | 11 | Molex | Datasheet | 3.069€ |
Crimp Housing 70066 50-57-9003 | 2 | Molex | Datasheet | 0.74€ |
Crimp Housing 70066 50-57-9004 | 2 | Molex | Datasheet | 0.74€ |
Crimp Housing 70067 50-57-9008 | 5 | Molex | Datasheet | 2.305€ |
22 AWG Black cable | 8m | Alphawire | Datasheet | 2.0€ |
22 AWG Blue cable | 8m | Alphawire | Datasheet | 2.0€ |
22 AWG Red cable | 8m | Alphawire | Datasheet | 2.0€ |
22 AWG Yellow cable | 8m | Alphawire | Datasheet | 2.0€ |
Required tools (WIP)
Tool | Image | Used for |
---|---|---|
Metallic Framing Square | Measurements | |
Ruler | Measurements | |
Pen | Measurements | |
Hand saw | Wood cutting | |
Coping saw | Wood cutting | |
Drill | Drilling | |
Drill bits Ø6/8/10/12/32 | Wood cutting/Drilling | |
Box Cutter | Wood Cutting and adjustments | |
File | Wood cutting and adjustments | |
Rasp | Wood cutting and adjustments | |
Screwdriver | Bolt fixing | |
Sockets | Bolt fixing | |
Pliers crimp | Cabling |
Building guide
The most "complicated" part of the yoke and panel is the panel. I tried to use FreeCAD (v0.18 from Debian) to build this technical drawing with dimensions:
If you are a FreeCAD user, you can download my work on this panel. I have also generated a PDF file for printing. If you use an A2 printer, you should have a 1:1 scale paper guide to help you drill holes in the panel.
Assembly guide
TODO
Calculations
Keyboard precision in Flightgear
- Throttle: 30 discrete values with keyboard (PgUp).
- Mixture: 20 discrete values with mouse.
- Elevator: 20 + 20 = 40 discrete positions with keyboard (Up/Down keys).
- Ailerons: 20 + 20 = 40 discrete positions with keyboard (Left/Right keys).
Rack and pinion
- z: teeth number.
- mod: module.
Module 1 means 3.14mm per tooth.
- 150mm long rack ⇒ 48 teeth for a whole turn.
- 125mm long rack ⇒ 40 teeth for a whole turn.
- 110mm long rack ⇒ 35 teeth for a whole turn.
- 100mm long rack ⇒ 32 teeth for a whole turn.
Encoder precision
For a 24 pulses per rotation (ppr).
Rack Lenght | z for 1 turn | z for pinion | number of turns | Precision |
---|---|---|---|---|
110mm | 35 | 10 | 3.5 | 84 levels |
100mm | 32 | 10 | 3.2 | 77 levels |
150mm | 48 | 15 | 3.2 | 77 levels |
125mm | 40 | 15 | 2.67 | 64 levels |
110mm | 35 | 15 | 2.33 | 56 levels |
100mm | 32 | 15 | 2.13 | 51 levels |
150mm | 48 | 16 | 3.0 | 72 levels |
125mm | 40 | 16 | 2.5 | 60 levels |
100mm | 32 | 16 | 2.0 | 48 levels |
150mm | 48 | 21 | 2.28 | 54 levels |
125mm | 40 | 21 | 1.9 | 45 levels |
100mm | 32 | 21 | 1.52 | 36 levels |
Yoke course
- 30mm clearance full pushed.
- 10mm of panel thickness.
- 30mm clearance for panel switches.
- 10mm of bearing support board.
- 190mm of course from inner bearing.
- MAX course: 110mm
- Aimed course: 100mm.
Interfacing with Flightgear
Introduction
Flightgear has an interesting feature called generic protocol. With it, you can connect nearly everything in input or output for or from Flightgear.
The generic protocol is a way to build an interface with your hardware and Flightgear. It is sufficiently opened to be able to use the input of your choice. For example, you can connect a simple switch from a microcontroler and use it to operate a "virtual switch" in Flightgear.
Finding the property tree nodes
Flightgear uses heavily property tree for nearly everything inside the simulation engine. So, there is a huge number of nodes to explore. Fortunately, there is a property tree browser inside Flightgear.
You just have to press '/' to open it and explore the tree.
Designing a protocol file for flightgear
The protocol file is just an XML configuration file. Once designed, you just have to put it under $FG_ROOT/Protocol/ directory (which is in /usr/share/games/flightgear/Protocol in Debian).
You can use either ASCII protocol or binary protocol. ASCII uses strings to communicate. If you have lots of values, it can be tough to read a lot of things in ASCII mode, because it is far from being compact. But on the other side, ASCII mode is really easiest to implement and to debug. Let's start with it.
<?xml version="1.0"?> <PropertyList> <generic> <input> <line_separator>\n</line_separator> <var_separator>,</var_separator> <!-- Left panel upper group --> <chunk> <name>Avionics</name> <node>/controls/switches/master-avionics</node> <type>bool</type> </chunk> <chunk> <name>Master Alt</name> <node>/controls/switches/master-alt</node> <type>bool</type> </chunk> <chunk> <name>Master Bat</name> <node>/controls/switches/master-bat</node> <type>bool</type> </chunk> <chunk> <name>Instruments lightning</name> <node>/controls/lighting/instruments-norm</node> <type>float</type> </chunk> <!-- Left Panel middle group --> <chunk> <name>Magnetos</name> <node>/controls/switches/magnetos</node> <type>int</type> </chunk> <chunk> <name>Starter</name> <node>/controls/switches/starter</node> <type>bool</type> </chunk> <chunk> <name>Pitot Heat</name> <node>/controls/anti-ice/pitot-heat</node> <type>bool</type> </chunk> <!-- Left Panel lower group --> <chunk> <name>Parking Brakes</name> <node>/controls/gear/brake-parking</node> <type>bool</type> </chunk> <!-- Lights group --> <chunk> <name>Strobe</name> <node>/controls/lighting/strobe</node> <type>bool</type> </chunk> <chunk> <name>Landing</name> <node>/controls/lighting/landing-lights</node> <type>bool</type> </chunk> <chunk> <name>Taxi</name> <node>/controls/lighting/taxi-light</node> <type>bool</type> </chunk> <chunk> <name>Beacon</name> <node>/controls/lighting/beacon</node> <type>bool</type> </chunk> <chunk> <name>Nav</name> <node>/controls/lighting/nav-lights</node> <type>bool</type> </chunk> <!-- Right panel upper group --> <chunk> <name>Primer</name> <node>/controls/engines/engine/primer-lever</node> <type>bool</type> </chunk> <!-- There is no fuel pump control in Cessna 172p for the moment --> <chunk> <name>Fuel Pump</name> <node>controls/engines/engine[0]/fuel-pump</node> <type>bool</type> </chunk> <chunk> <name>Carb Heat</name> <node>/controls/engines/current-engine/carb-heat</node> <type>bool</type> </chunk> <chunk> <name>Fuel Selector Left</name> <node>/consumables/fuel/tank[0]/selected</node> <type>bool</type> </chunk> <chunk> <name>Fuel Selector Right</name> <node>/consumables/fuel/tank[1]/selected</node> <type>bool</type> </chunk> <!-- Right Panel lower group --> <chunk> <name>Flaps</name> <node>/controls/flight/flaps</node> <type>float</type> </chunk> <chunk> <name>Gear Down</name> <node>/controls/gear/gear-down</node> <type>bool</type> </chunk> <chunk> <name>Throttle</name> <node>/controls/engines/engine/throttle</node> <type>float</type> </chunk> <chunk> <name>Mixture</name> <node>/controls/engines/engine/mixture</node> <type>float</type> </chunk> </input> </generic> </PropertyList>
Arduino code
Now that we have defined a protocol for communication, we need the Arduino to send correct values towards the computer. We need to build some code that will:
- read all the values from the physical switches and components.
- transform them to adjusted values for Flightgear.
- transfert by serial line the values, conforming to our generic protocol specifications.
Here is what I have produced in C++ for Arduino. You have three files:
- The header file
yoke_and_panel.h
. - The real code file:
yoke_and_panel.cpp
. - The Makefile:
Makefile
.
Here is the content of yoke_and_panel.h
:
/* Pin definition */ /* Digital pins */ #define AVIONICS 41 #define MASTER_ALT 50 #define MASTER_BAT 39 #define MAGNETO_OFF 45 #define MAGNETO_RIGHT 49 #define MAGNETO_LEFT 47 #define MAGNETO_BOTH 51 #define STARTER 53 #define PITOT 43 #define PARKING_BRAKES 52 #define STROBE 48 #define NAV 36 #define BEACON 46 #define LANDING 34 #define TAXI 44 #define PRIMER 32 #define FUEL_PUMP 42 #define CARB_HEAT 22 #define FUEL_LEFT 35 #define FUEL_RIGHT 24 #define FUEL_BOTH 33 #define FUEL_OFF 30 #define FLAPS_0 28 #define FLAPS_10 26 #define FLAPS_20 38 #define FLAPS_30 37 #define GEAR_UP 40 /* Analog pins */ #define AILERONS A0 #define THROTTLE A2 #define MIXTURE A1 #define ENCPINA 2 #define ENCPINB 3 /* Global variables */ extern boolean avionics; extern boolean masterAlt; extern boolean masterBat; extern boolean gearUp; extern boolean pitotHeat; extern boolean parkingBrakes; extern boolean strobeLight; extern boolean navLight; extern boolean taxiLight; extern boolean beaconLight; extern boolean landingLight; extern boolean fuelPump; extern boolean carbHeat; extern boolean fuelLeft; extern boolean fuelRight; extern boolean primerLeverState; extern boolean starterState; extern float flaps; extern float throttle; extern float mixture; extern float instrumentBrightness; extern float ailerons; extern unsigned int magnetos; extern unsigned int primer; extern unsigned long starterEndTimer; /* Encoder variables */ extern volatile byte aFlag; extern volatile byte bFlag; extern volatile byte elevatorPos; extern volatile byte oldElevatorPos; extern volatile byte elevatorRaw; /* Functions declarations */ void setup(); void EncPinA(); void EncPinB(); void fuelTanksValue(); void magnetosValue(); void flapsValue(); void primerValue(); void loop();
Here is the content of the main code:
/* * This file is part of flightgear_yoke_and_panel * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfred is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with flightgear_yoke_and_panel. * If not, see <https://www.gnu.org/licenses/>. * * Copyright (C) 2018 Médéric Ribreux <mederic.ribreux@medspx.fr> *--------------------------------------------------------------------- * yoke_and_panel.cpp: Main Arduino source code for yoke and panel. */ #include <Arduino.h> #include "yoke_and_panel.h" /* Global variables */ boolean avionics = false; boolean masterAlt = false; boolean masterBat = false; boolean gearUp = false; boolean pitotHeat = false; boolean parkingBrakes = false; boolean strobeLight = false; boolean navLight = false; boolean taxiLight = false; boolean beaconLight = false; boolean landingLight = false; boolean fuelPump = false; boolean carbHeat = false; boolean fuelLeft = false; boolean fuelRight = false; boolean primerLeverState = false; boolean starterState = false; float flaps = 0.0; float throttle = 0.0; float mixture = 0.0; float ailerons = 0.0; float elevator = 0.0; /* float instrumentBrightness = 0.0; */ unsigned int magnetos = 0; unsigned int primer =0; unsigned long starterEndTimer = 0; static int pinA = 2; // Encoder first pin static int pinB = 3; // Encoder second pin volatile byte aFlag = 0; volatile byte bFlag = 0; volatile byte elevatorPos = 30; volatile byte elevatorRaw = 0; // Elevator raw value /* Initial setup */ void setup() { /* Pins assignment */ /* Digital Pins */ pinMode(AVIONICS, INPUT_PULLUP); pinMode(MASTER_ALT, INPUT_PULLUP); pinMode(MASTER_BAT, INPUT_PULLUP); pinMode(MAGNETO_OFF, INPUT_PULLUP); pinMode(MAGNETO_RIGHT, INPUT_PULLUP); pinMode(MAGNETO_LEFT, INPUT_PULLUP); pinMode(MAGNETO_BOTH, INPUT_PULLUP); pinMode(PARKING_BRAKES, INPUT_PULLUP); pinMode(STARTER, INPUT_PULLUP); pinMode(PITOT, INPUT_PULLUP); pinMode(STROBE, INPUT_PULLUP); pinMode(NAV, INPUT_PULLUP); pinMode(TAXI, INPUT_PULLUP); pinMode(BEACON, INPUT_PULLUP); pinMode(LANDING, INPUT_PULLUP); pinMode(PRIMER, INPUT_PULLUP); pinMode(LANDING, INPUT_PULLUP); pinMode(FUEL_PUMP, INPUT_PULLUP); pinMode(CARB_HEAT, INPUT_PULLUP); pinMode(FUEL_LEFT, INPUT_PULLUP); pinMode(FUEL_RIGHT, INPUT_PULLUP); pinMode(FUEL_BOTH, INPUT_PULLUP); pinMode(FUEL_OFF, INPUT_PULLUP); pinMode(FLAPS_0, INPUT_PULLUP); pinMode(FLAPS_10, INPUT_PULLUP); pinMode(FLAPS_20, INPUT_PULLUP); pinMode(FLAPS_30, INPUT_PULLUP); pinMode(GEAR_UP, INPUT_PULLUP); /* pinMode(INSTR_BRIGHT, INPUT); */ /* Analog Pins */ pinMode(AILERONS, INPUT); /* pinMode(RUDDER, INPUT); */ pinMode(THROTTLE, INPUT); pinMode(MIXTURE, INPUT); /* Encoder */ pinMode(pinA, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP); /*attachInterrupt(digitalPinToInterrupt(pinA), EncPinA, RISING); attachInterrupt(digitalPinToInterrupt(pinB), EncPinB, RISING);*/ attachInterrupt(0, EncPinA, RISING); attachInterrupt(1, EncPinB, RISING); /* Open serial line to computer */ Serial.begin(115200); } /* Interruption function for encoder */ void EncPinA(){ noInterrupts(); elevatorRaw = PINE & 0x30; if(elevatorRaw == 0b00110000 && aFlag) { elevatorPos --; bFlag = 0; //reset flags for the next turn aFlag = 0; //reset flags for the next turn } else if (elevatorRaw == 0b00010000) { bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation } interrupts(); } /* Interruption function for encoder */ void EncPinB(){ noInterrupts(); //elevatorPos = 20; elevatorRaw = PINE & 0x30; if (elevatorRaw == 0b00110000 && bFlag) { elevatorPos ++; bFlag = 0; //reset flags for the next turn aFlag = 0; //reset flags for the next turn } else if (elevatorRaw == 0b00100000) { aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation } interrupts(); } /* Fuel tank logic */ void fuelTanksValue() { if (!digitalRead(FUEL_LEFT)) { fuelRight = 0; fuelLeft = true; } else if (!digitalRead(FUEL_RIGHT)) { fuelRight = true; fuelLeft = 0; } else if (!digitalRead(FUEL_BOTH)) { fuelRight = true; fuelLeft = true; } else if (!digitalRead(FUEL_OFF)) { fuelRight = 0; fuelLeft = 0; } } /* Magneto logic */ void magnetosValue() { if (!digitalRead(MAGNETO_OFF)) { magnetos = 0; } else if (!digitalRead(MAGNETO_LEFT)) { magnetos = 1; } else if (!digitalRead(MAGNETO_RIGHT)) { magnetos = 2; } else if (!digitalRead(MAGNETO_BOTH)) { magnetos = 3; } } /* Flaps logic */ void flapsValue() { if (!digitalRead(FLAPS_0)) { flaps = 0.0; } else if (!digitalRead(FLAPS_10)) { flaps = 0.33; } else if (!digitalRead(FLAPS_20)) { flaps = 0.66; } else if (!digitalRead(FLAPS_30)) { flaps = 1.0; } } /* Primer logic */ void primerValue() { /* You push primer button */ if (!digitalRead(PRIMER)) { /* Set lever state to true if required*/ if (primerLeverState == false) { primerLeverState = true; } } /* You release primer button */ else { if (primerLeverState == true) { primer = primer + 1; primerLeverState = false; } } /* delay primer value for 5 seconds after starter stop.*/ if (starterEndTimer > 0) { unsigned long currentMillis = millis(); if (currentMillis - starterEndTimer > 5000) { primer = 0; starterEndTimer = 0; } } } /* Starter and primer value reset */ void starterValue() { /* Starter is set starterState */ if (!digitalRead(STARTER)) { if (starterState == false) { starterState = true; starterEndTimer = 0; } } else { /* Starter is stopped */ if (starterState == true) { starterState = false; starterEndTimer = millis(); } } } /* Main loop */ void loop() { /* we read everything */ avionics = !digitalRead(AVIONICS); masterAlt = !digitalRead(MASTER_ALT); masterBat = !digitalRead(MASTER_BAT); pitotHeat = !digitalRead(PITOT); parkingBrakes = !digitalRead(PARKING_BRAKES); strobeLight = !digitalRead(STROBE); navLight = !digitalRead(NAV); taxiLight = !digitalRead(TAXI); beaconLight = !digitalRead(BEACON); landingLight = !digitalRead(LANDING); fuelPump = !digitalRead(FUEL_PUMP); carbHeat = !digitalRead(CARB_HEAT); flapsValue(); magnetosValue(); fuelTanksValue(); primerValue(); starterValue(); /* instrumentBrightness = (analogRead(INSTR_BRIGHT)/512.0); */ throttle = analogRead(THROTTLE)/1024.0; mixture = analogRead(MIXTURE)/1024.0; /* ailerons smoothing */ ailerons = 0.99 - (analogRead(AILERONS)/512.0); /* An example of smoothing for centering ailerons if (abs(ailerons) < 0.02) { ailerons = 0.0; }*/ /* elevator smoothing */ elevator = (30 - elevatorPos) / 60.0; if (abs(elevator) < 0.02) { elevator = 0.0; } /* We send everything towards serial */ Serial.print(avionics); Serial.print(","); Serial.print(masterAlt); Serial.print(","); Serial.print(masterBat); Serial.print(","); /*Serial.print(instrumentBrightness); Serial.print(",");*/ Serial.print(magnetos); Serial.print(","); Serial.print(starterState); Serial.print(","); Serial.print(pitotHeat); Serial.print(","); Serial.print(parkingBrakes); Serial.print(","); Serial.print(strobeLight); Serial.print(","); Serial.print(landingLight); Serial.print(","); Serial.print(taxiLight); Serial.print(","); Serial.print(beaconLight); Serial.print(","); Serial.print(navLight); Serial.print(","); Serial.print(primerLeverState); Serial.print(","); Serial.print(primer); Serial.print(","); Serial.print(fuelPump); Serial.print(","); Serial.print(carbHeat); Serial.print(","); Serial.print(fuelLeft); Serial.print(","); Serial.print(fuelRight); Serial.print(","); Serial.print(flaps); Serial.print(","); Serial.print(gearUp); Serial.print(","); Serial.print(throttle); Serial.print(","); Serial.print(mixture); Serial.print(","); Serial.print(ailerons); Serial.print(","); Serial.print(elevator); Serial.print("\n"); //delay(3050); }
Connect Flightgear to the Arduino
You just have to launch Flightgear with the following option:
--generic=serial,in,60,/dev/ttyACM0,9600,fg_yoke_and_panel.xml
With this option we:
- tell to Flightgear to open the serial port:
serial
. - use to listen something:
in
. - the device will be read at the frequency of 30Hz (30 times per second) in Flightgear.
- the system device is /dev/ttyACM0 which is our Arduino serial port.
- Speed for serial is 9600 bauds.
- We use the fg_yoke_and_panel.xml for generic protocol.