Flight Sim Throttle Coding

Hi all,

I have an ongoing project to build an Airbus A320 style throttle quadrant in preparation for the upcoming release of MSFS2024.
This is my first ever project using an Arduino, and while I'm fairly handy with wiring, I'm definitely not when it comes to coding. Up to this point, I have been using a software called MobiFlight which allows the assignment of commands; which are connected directly to the simulator, to any hardware wired into the Arduino therefore removing the need to code.

I am now finding this restrictive however, especially when assigning the throttle function to the potentiometers I have wired in. The following explanation is complicated, but I'll try my best.

The full throw, or movement, of the A320 throttle levers is 65 degrees.
Across this 65 degree range there are several detent positions for various throttle modes; for example, idle, climb, TOGA, etc. Well, it so happens that the idle detent actually sits at 20 degrees, not 0, as below this is the 'Reverse Thrust' function. This is causing a big issue for me because although I have managed to get the potentiometers to communicate with the game, when the pots are outputting a '0' reading; which on the physical throttle is full reverse thrust, the game is interpreting that as idle.
Is there anyway, after setting the physical throttle to the idle position, of coding the potentiometer to read '0' so that when I go below that 20 degree idle state, it can output a negative reading to active reverse thrust?

Assuming Arduino and -20 as full reverse...

int value = map (analogRead(a0), 0, 1023, -20, 65);

Using a potentiometer on analog pin A0, this demonstrates the map() function...

int oldValue;

void setup() {
  Serial.begin(115200);
  if (analogRead(A0)) // A0 must be zero
    Serial.println("Turn pot to zero.");
  while (analogRead(A0) != 0); // wait for pot = 0
}

void loop() {
  int value = map(analogRead(A0), 0, 1023, -20, 65); // 10 bit ADC
  if (value != oldValue) { // if old and new are different...
    oldValue = value; // store new value
    switch (value) {
      case 0: Serial.print("IDLE "); break;
      case 65: Serial.print("FULL "); break;
      case -20: Serial.print("RVRS "); break;
      default: Serial.print("     "); break;
    }
    if (value > -10 && value < 0) Serial.print(" ");
    if (value >= 0 && value <= 9) Serial.print("  ");
    if (value > 9) Serial.print(" ");
    Serial.println(value);
  }
}
2 Likes

Hello mr_tweedy

Welcome to the best Arduino forum ever :slight_smile:

Post the current sketch to see how we can help.

1 Like

Oh wow… that’s just mind blowing!

Thank you so much @xfpd !!!! I will upload this and post an update after testing.

I revised the sketch to use the degrees you mentioned rather than potentiometer range... and starting in full reverse afterburner : )... now it waits for "IDLE" to occur.

int oldValue;

void setup() {
  Serial.begin(115200);
  Serial.println("Turn pot to IDLE (20).");
  while (map(analogRead(A0), 0, 1023, 0, 65) != 20); // wait for pot = 20 degrees (IDLE)
}

void loop() {
  int value = map(analogRead(A0), 0, 1023, 0, 65); // 10 bit ADC
  if (value != oldValue) { // if old and new are different...
    oldValue = value; // store new value
    switch (value) {
      case 20: Serial.print("IDLE=="); break;
      case 65: Serial.print("FULL^^"); break;
      case  0: Serial.print("FREVvv"); break;
    }

    if (value < 20 && value) Serial.print("REV v ");
    if (value > 20 && value != 65) Serial.print("FWD ^ ");

    // space padding
    if (value > -10 && value < 0) Serial.print(" ");
    if (value >= 0 && value <= 9) Serial.print("  ");
    if (value > 9) Serial.print(" ");
    Serial.print(value);
    Serial.println(" degrees.");
  }
}
1 Like

That's amazing @xfpd ! I really can't thank you enough for your help.
I had a go at uploading the previous sketch to my Arduino, but then I found that the Nano isn't compatible with the joystick library. I've just ordered myself a Leonardo, so when that arrives tomorrow I'll test your new code in-game and send some feedback.

Thanks again, you're a legend!!!

I guess @mr_tweedy won't be learning to fish.

Sometimes learning to swim by not drowning works.

1 Like

Well @xfpd it works like a charm!! Studying the code you kindly wrote for me has really given me a further understanding, and after some practice, I've been able to add some of my own to it. It feels crazy to be doing all of this but it's a dream come true lol. After testing in the sim it works perfectly.

The only thing I couldn't figure out was how to replicate your code for the second potentiometer. I tried copy/paste and changing the pin number, but it kept throwing an error stating 'int redefinition'. Either way, I'm super happy and I owe a big thanks to you! I've posted the final code below. I'd love to get your feedback on my additions.


#include "Joystick.h"
Joystick_ Joystick;


int Throttle1;
int Throttle2;
int oldValue;

void setup() {

  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);

  //Initialise Joystick Library
  Joystick.begin();
  Joystick.setRxAxisRange(0, 1023);
  Joystick.setRyAxisRange(0, 1023);

  Serial.begin(115200);
  Serial.println("Turn pot to IDLE (20).");
  while (map(analogRead(A1), 0, 1023, 0, 65) != 20); // wait for pot = 20 degrees (IDLE)
  
}


void loop() {

  //Read Throttle Inputs
  Throttle1 = analogRead(A1);
  Throttle2 = analogRead(A2);

  int value = map(analogRead(A1), 0, 1023, 0, 65); // 10 bit ADC
  if (value != oldValue) { // if old and new are different...
    oldValue = value; // store new value
    switch (value) {
      case 20: Serial.print("IDLE=="); break;
      case 65: Serial.print("FULL^^"); break;
      case  0: Serial.print("FREVvv"); break;
    }

    if (value < 20 && value) Serial.print("REV v ");
    if (value > 20 && value != 65) Serial.print("FWD ^ ");

    // space padding
    if (value > -10 && value < 0) Serial.print(" ");
    if (value >= 0 && value <= 9) Serial.print("  ");
    if (value > 9) Serial.print(" ");
    Serial.print(value);
    Serial.println(" degrees.");
  }
  

//Output Controls
Joystick.setRxAxis(Throttle1);
Joystick.setRyAxis(Throttle2);

Joystick.sendState();

delay (50);

Would you post the complete error in a code block?

Are you certain your analog input pins should be INPUT, not INPUT_PULLUP?

Here is a two-throttle version of the previous sketch. It is different in that it is a constant scroll, rather than only updating with new data.

int throttle[] = {A0, A1}; // array of throttles
int throttleReading, degreesOutput, element, oldOutput;
int arraySize = sizeof(throttle) / sizeof(throttle[0]); // size of array

#define DELAY   100     // delay between analog read
#define MININ   0       // analog input minimum value
#define MAXIN   1023    // analog input maximum balue
#define MINOUT  0       // analog input re-mapped to minimum degrees
#define MAXOUT  65      // analog input re-mapped to maximum degrees

void setup() {
  Serial.begin(115200); // serial monitor communications

  for (int i = 0; i < arraySize; i++)
    pinMode(throttle[i], INPUT); // configure input pins

  Serial.println("Turn pot A0 to IDLE (20).");
  while (map(analogRead(throttle[0]), MININ, MAXIN, MINOUT, MAXOUT) != 20); // wait until A0 at "20 degrees"
}

void loop() {
  Serial.print("|");
  for (element = 0; element < arraySize; element++) {

    // Read 10 bit ADC input, 0 to 1023. Output 0 to 65 degrees
    throttleReading = analogRead(throttle[element]); // 10 bit ADC = 0 to 1023
    degreesOutput  = map(throttleReading, MININ, MAXIN, MINOUT, MAXOUT); // output 0 to 65 degrees

    Serial.print("| Trottle:"); // start trace
    Serial.print(element); // throttle number
    Serial.print(" |");

    format(degreesOutput); // degrees output
    Serial.print(degreesOutput);
    Serial.print(" Degrees | ");

    switch (degreesOutput) {
      case 20: Serial.print("IDLE =="); break;
      case 65: Serial.print("FFWD ^^"); break;
      case  0: Serial.print("FREV vv"); break;
    }

    if (degreesOutput < 20 && degreesOutput != 0) {
      Serial.print("rev   v");
    }
    if (degreesOutput > 20 && degreesOutput != 65) {
      Serial.print("fwd   ^");

    }
    Serial.print(" |"); // end of trace
    if (element) // throttle 1
      Serial.println("|"); // new line
    delay(DELAY); // keep output from running off the monitor
  }
}

void format(int i) { // prefix spaces
  if (i > -10 && i < 0) Serial.print(" ");
  if (i >= 0 && i <= 9) Serial.print("  ");
  if (i > 9) Serial.print(" ");
}

This newest (final!) edit brings back updating the data only when changes occur along with multiple throttle array. I have only tried with two throttles, but if you want to try more, add another element to the throttle array and give it a try.

// https://forum.arduino.cc/t/flight-sim-throttle-coding/1318665/
// two potentiometers mapped to 0 to 65 with 20 as the "idle" point

int throttle[] = {A0, A1}; // array of throttles
const int arraySize = sizeof(throttle) / sizeof(throttle[0]); // size of array
int throttleReading, throttleDegrees[arraySize], element, oldDegrees[arraySize];

#define DELAY   100     // delay between analog read
#define MININ   0       // analog input minimum value
#define MAXIN   1023    // analog input maximum balue
#define MINOUT  0       // analog input re-mapped to minimum degrees
#define MAXOUT  65      // analog input re-mapped to maximum degrees

void setup() {
  Serial.begin(115200); // serial monitor communications

  for (int i = 0; i < arraySize; i++)
    pinMode(throttle[i], INPUT); // configure input pins

  Serial.println("Move throttle A0 to IDLE (20 degrees).");
  // wait for throttle to be "20 degrees"
  while (map(analogRead(throttle[0]), MININ, MAXIN, MINOUT, MAXOUT) != 20);
}

void loop() {
  if (element > arraySize - 1) { // count through throttle array elements
    element = 0; // reset array counter
  }

  // Read 10 bit ADC input, 0 to 1023. Output 0 to 65 degrees
  throttleReading = analogRead(throttle[element]); // 10 bit ADC = 0 to 1023
  throttleDegrees[element]  = map(throttleReading, MININ, MAXIN, MINOUT, MAXOUT); // output 0 to 65 degrees

  if (oldDegrees[element] != throttleDegrees[element]) { // has degree changed

    for (int i = 0; i < arraySize; i++) { // local variable to print all throttle traces
      Serial.print(" |");
      oldDegrees[i] = throttleDegrees[i]; // store new degree

      Serial.print("| Trottle:"); // start trace
      Serial.print(i); // throttle number
      Serial.print(" |");

      format(throttleDegrees[i]); // degrees output
      Serial.print(throttleDegrees[i]);
      Serial.print(" Degrees | ");

      switch (throttleDegrees[i]) {
        case 65: Serial.print("FFWD ^^"); break;
        case 21 ... 64: Serial.print("fwd   ^"); break;
        case 20: Serial.print("IDLE =="); break;
        case  1 ... 19: Serial.print("rev   v"); break;
        case  0: Serial.print("FREV vv"); break;
      }

      // Serial.print(" |"); // end of trace
      if (i == arraySize - 1) // if last throttle...
        Serial.println(" ||"); // ... add new line
      delay(DELAY); // keep output from running off the monitor
    }
  }
  element++; // next throttle array elemnt
}

void format(int i) { // prefix spaces
  if (i > -10 && i < 0) Serial.print(" ");
  if (i >= 0 && i <= 9) Serial.print("  ");
  if (i > 9) Serial.print(" ");
}
diagram.json for wokwi
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-arduino-nano",
      "id": "nano",
      "top": 102.8,
      "left": 49.3,
      "rotate": 270,
      "attrs": {}
    },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": -10.9, "left": 172.6, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot2", "top": -10.9, "left": 249.4, "attrs": {} }
  ],
  "connections": [
    [ "pot1:GND", "nano:GND.1", "black", [ "v19.2", "h-48" ] ],
    [ "pot1:SIG", "nano:A0", "green", [ "v28.8", "h-0.4", "v94" ] ],
    [ "pot1:VCC", "nano:5V", "red", [ "v38.4", "h-164" ] ],
    [ "pot2:GND", "nano:GND.1", "black", [ "v19.2", "h-134.4" ] ],
    [ "pot2:VCC", "nano:5V", "red", [ "v38.4", "h-164" ] ],
    [ "pot2:SIG", "nano:A1", "green", [ "v28.8", "h-0.4", "v84.4" ] ]
  ],
  "dependencies": {}
}