Bits and Droids logo
Mail icon

Create A Flight Sim Arduino airspeed steam gauge

3/30/2024 | by Bits and Droids

Hi guys,

Today, I wanted to show you how I created my fuel Gauge. The same concept can be applied to many metrics, so I'll probably return to this topic in the future.

Update!

I've made some improvements to the code:

cpp
#include<BitsAndDroidsFlightConnector.h> #include<Servo.h> BitsAndDroidsFlightConnector connector(false); Servo myServo; int servoPos = 0; int speedVal = 0; int positionServo = 0; int servoPin = 13; int led1 = 12; int led2 = 10; int led3 = 8; int btn = 5; bool lightState = false; void setup() { myServo.attach(servoPin); pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); pinMode(btn, INPUT_PULLUP); Serial.begin(115200); Serial.setTimeout(15); } void loop() { if(digitalRead(btn) == LOW){ toggleLights(); delay(300); } connector.dataHandling(); speedVal = connector.getIndicatedAirspeed().toInt(); if(speedVal > 200){ positionServo = 180; } //0-40 40 = 0 - 15 if(speedVal < 40){ positionServo = map(speedVal, 0, 40, 0, 15); } //40-100 = 15 - 80 if(speedVal >= 40 && speedVal <= 100){ positionServo = map(speedVal, 40, 100, 15, 80); } //100-160 = 80 - 150 if(speedVal > 100 && speedVal <= 160){ positionServo = map(speedVal, 100, 160, 80, 150); } //160-200 = 150 - 180 if(speedVal > 160){ positionServo = map(speedVal, 160, 200, 150, 180); } myServo.write(positionServo); } void toggleLights(){ if(lightState){ lightState = false; } else{ lightState = true; } if (lightState){ digitalWrite(led1, HIGH); digitalWrite(led2, HIGH); digitalWrite(led3, HIGH); } else{ digitalWrite(led1, LOW); digitalWrite(led2, LOW); digitalWrite(led3, LOW); } }

Needed supplies

First things first, what do we need?:

  • A servo (I got an MG996R)
  • An Arduino (any will do)
  • Our flight connector which can be downloaded from the downloads page or at the bottom of this article
  • Some wires to connect everything

The MG996R servo's rotational axis is 180 degrees, which impacts how we can use the arm. Creating a gauge that could rotate more than 180 degrees without any modifications wouldn't be impossible.

Convert 180 rotation to 360 degrees.

The approach I love the most is the use of gears. There is just something about them that gets me excited. To convert our rotational range from 180 to 360, we have to make an incremental power transfer.

cad gears

In the image above, we can see a gear with 10 teeth and a gear with 30 teeth. If we put a servo on the big gear and start spinning, we can calculate the rotation of the smaller one using the following equation.

The number of teeth of the driving gear (the gear that gets rotated by the servo) / the number of teeth on the other gear. If we power the big gear, this would be 30/10 = 3. So, the rotational ratio would be 1:3. If we drive the smaller gear, the rate would become 10/30 = 0.33, which is a ratio of 3:1.

Now, let's get back to our servo. Our servo can rotate 180 degrees, so we want to double this movement. We could either find a gear with 20 teeth or 10 teeth or calculate the number of turns needed for the gear in the example above. If our ratio is 1:3, we can divide 360 by 3 to get 120. This concludes that our big gear has to turn 120 degrees to make the smaller gear rotate 360 degrees.

As always, change these values to match your needs. The same formula can be applied if you have gears of different sizes than those mentioned here.

Program our servo

Our final code will look as follows:

cpp
#include <BitsAndDroidsFlightConnector.h> #include <Servo.h> #include<Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,20,4); BitsAndDroidsFlightConnector connector(false); //create servo object Servo myservo; //Change this to your servo pin int servoPin = 35; int value; int oldVal = -1; void setup() { //attach servo to pin myservo.attach(servoPin); //lcd for testing lcd.init(); lcd.backlight(); //serial for handling the incomming data Serial.begin(115200); Serial.setTimeout(15); // put your setup code here, to run once: } void loop() { connector.dataHandling(); //get Value from the library and convert it to int (will make this possible from the getgo in the future). value = connector.getFuelTotalPercentage().toInt(); //calculate percentage of 180 in degrees. i.e. Incoming val is 50 so percentagerot will be 180*0.5 = 90 degrees //My servo is upside down so perhaps you don't need the first 180 - int percentageRot = (180 - (180 * (value / 100.0))); //if value differs from prev val if(value != oldVal){ //Send the 180 * percentage myservo.write(percentageRot); //newval becomes the old val oldVal = value; //for debugging print the values if desired lcd.setCursor(0,0); lcd.print((String)percentageRot+" "); lcd.setCursor(0,1); lcd.print((String)value+" "); } }

One important part is only sending data to our server when the incoming data differs from the old data. My servo keeps ticking when I skip this step even though I send the same position.

This can be done by a simple check like this (already included in the code above)

cpp
int val; int oldVal; void loop(){ val = connector.getFuelTotalPercentage().toInt(); if(val != oldVal){ //do your code magic here oldval = val; } }

The servo.h library can be downloaded directly from the Arduino IDE. Go to sketch -> libraries -> manage libraries and search for the servo library. I already had it installed. The Bitsanddroids library can be downloaded from the downloads page or at the bottom of this article. We send our servo the incoming value (a percentage, i.e., 50) of the desired rotation. If the fuel is at 50% we multiply 180 (the rotational freedom) by 0,5 and the arm would stop at 90 degrees.

the result

My result looked like this:

speed gauge

Now, remember you can apply these same concepts to many metrics.

Feel free to experiment, and I would love to see your creations.

As always, thanks for the support!