Bits and Droids logo
Mail icon

Create your own master caution and warning buttons

by Bits and Droids | 21-03-2023

Today we will dive into the latest update to create our master warning and master caution buttons. This article is the first entry for the gear lever handle unit.

Needed for this tutorial:

These are affiliate links that help support the channel.

  • Arduino Leonardo https://amzn.to/3FZFnNN
  • Or an Arduino pro micro https://amzn.to/3n1u7Yx
  • Pushbuttons https://amzn.to/3BS9jst

    Tools needed:
  • Soldering station https://amzn.to/3aPP6rn
  • Wire strippers https://amzn.to/3jdckfG
  • Wire https://amzn.to/3AYmlUk

For this tutorial to work,, you'll need to use the latest version of the connector, WASM module, and library (v0.9.7 or higher). Everything required can be obtained from the downloads page.

Wiring

There are a multitude of wiring options available. Most of these options have enable or disable our LED based on the state of the button. We don't want this for our internal LED. We want to control our button and LED as separate components. We want the LED to match the game state instead.

button pinout

These buttons have 5 poles. Our LED is connected to the pin marked with 5V and the ground pin. To reduce the amount of wires running towards our board we want to connect the ground of our LED to the ground of our buttons (NC to Gnd). A small piece of wire suffices in this case.

button pinout grounds connected

From our button we can route three cables to our processor. From the C to pin six, the 5v to pin seven and the ground to the ground line on our Arduino. We can't wire the 5v LED pin directly to our board. To save our LED from burning out instantly we have to add a resistor between our board and the pin. I'd recommend a 470 ohm resistor. You might try using a 220 ohm (way to bright in my opinion) resistor and work your way up from there to see what you like best. The higher the resistance the dimmer the light will be. The end result will look something like the image below.

The coding part

Let's begin by opening a new sketch in our Arduino IDE. At the very top of the file, we want to include the BitsAndDroidsFlightConnector. This enables us to access all the features the library has to offer. The library and connector work in unison. Microsoft Flight Simulator 2020 sends data to the connector, passing it on to our microcontroller. The sending of data to our microcontroller happens over the Serial line. It's essential that the baud rate of the connector and our controller match (by default, this is 115200).

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

void setup(){
  Serial.begin(115200);
}

The button and LED have been wired to our Arduino through pins six and seven. We could add an external pull-up/pull-down resistpull-upd this article if you want to learn all about pushbuttons). To make our lives easier, we'll use internal pull-up resistors. This ensures we don't have to add any components to our setup. Our LED has to be defined as an output (since our light never sends data back).

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

const byte ledPin = 7;
const byte btnPin = 6;

void setup(){
  Serial.begin(115200);

//Define the behavior of our pins
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
}

Coding the button

When buying these buttons there are usually 2 options: latching and momentary. Latching means the button locks in place when pressed while the momentary buttons reset to the starting position. If you've got a choice I'd recommend going with the momentary variants for the master warning/caution buttons. I'll explain both types in this article so please take note which one you've bought.

Latching

For the latching buttons we want to register an action each time we change state (HIGH -> LOW, LOW -> HIGH). In order to make this possible we'll have to keep track of the previous state and current state of the button to determine if the state has changed. We can read the currenstate by calling the digitalRead(pinOfTheButton) function. This returns either LOW or HIGH (0 or 1).

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

const byte ledPin = 7;
const byte btnPin = 6;

//Here we store the old button state
//Thanks to theindiantechsupport on Youtube for pointing out
//bools take up less space
bool lastBtnState = LOW;

void setup(){
  Serial.begin(115200);

//Define the behavior of our pins
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
}

void loop(){

  //In order to check if something changed we need to check what the current state is
  bool currentBtnState = digitalRead(btnPin);

  //if statement only excecutes the code in between the {...} when the statement between the (...) is true
  //int his case if the currenstate is not equal to the old state -> do something
  if(currentBtnState != lastButtonState){
    //Sends the command to the connector to disable the caution light
    connector.send(sendMasterCaution)

    //In the next loop we want to check against the new state 
    //This line is essential to stay up to date
    lastButtonState = currentBtnState;
  }
}

When you press a button the signal will change to HIGH or LOW. The downside is that this won't happen instantly. There is a high chance the signal bounces around slightly. Why is this an issue? When the signal bounces it flickers between the HIGH and LOW state. According to our logic each switch would trigger an action. We could add a delay to freeze the code execution for a certain amount of time (basically we wait till the bounce ends before proceeding). In small applications this works just fine but there is a downside. The entire code freezes during the delay. To keep things flowing it might be a better idea to use a timer. We note the time our button press starts and check if the current time minus the start time is bigger than our bouncy time window. Let's start with a timerwindow of 250 to be safe (there isn't a real need to press the caution button rapidly anyway).

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

const byte ledPin = 7;
const byte btnPin = 6;

//these variables hold our timer
long timerStart = 0;
long currentTime;


bool lastBtnState = LOW;

void setup(){
  Serial.begin(115200);

//Define the behavior of our pins
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
}

void loop(){

 
  bool currentBtnState = digitalRead(btnPin);

  
  if(currentBtnState != lastButtonState){

    //we only want to set the timer once. If the timer has expired we set the start back to 0
    if(timerStart = 0){

      //the milis() function returns the time the processor is active in milli seconds
      timerStart = millis();

    }
    currentTime = millis();

    //If our timewindows has passed
    if(currentTime - timerStart > 250){

      connector.send(sendMasterCaution);

      lastButtonState = currentBtnState;
     
      //we reset our timer to 0
      //next time the button state changes a new timer can be started
      timerStart = 0;
    }
   
  }
}

Momentary

For those of you that use momentary buttons we need a slightly different approach. The default state of the button is a HIGH state. The moment you press the button the state changes to LOW. We can apply the same debounce approach as we did in the latching example.

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

const byte ledPin = 7;
const byte btnPin = 6;

//these variables hold our timer
long timerStart = 0;
long currentTime;


void setup(){
  Serial.begin(115200);

//Define the behavior of our pins
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
}

void loop(){

 
  bool currentBtnState = digitalRead(btnPin);

  //almost the same as the latching example except we check if the state == LOW instead of changed  
  if(currentBtnState == LOW){

    //we only want to set the timer once. If the timer has expired we set the start back to 0
    if(timerStart = 0){

      //the milis() function returns the time the processor is active in milli seconds
      timerStart = millis();

    }
    currentTime = millis();

    //If our timewindows has passed
    if(currentTime - timerStart > 250){

      connector.send(sendMasterCaution);
     
      //we reset our timer to 0
      //next time the button state changes a new timer can be started
      timerStart = 0;
    }
   
  }
}

The LED

Coding up the LED is one of the most easy parts. The logic comes down to: if the game light is on we want to turn our LED on and vice versa. In order to receive the game data we need to call the connector.dataHandling() function.

#include <BitsAndDroidsFlightConnector.h>

BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector();

const byte ledPin = 7;
const byte btnPin = 6;

long timerStart = 0;
long currentTime;


void setup(){
  Serial.begin(115200);

//Define the behavior of our pins
  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
}

void loop(){
  connector.dataHandling();
  
  //Why does this line work while a boolean returns true or false?
  //In the end it all gets converted to 1s and 0s
  //LOW = 0 and false = 0, HIGH = 1 and true = 1
  //The moment our boolean changes our light changes as well
  digitalWrite(ledPin, connector.getMasterCautionOn());
 
  bool currentBtnState = digitalRead(btnPin);

  
  if(currentBtnState == LOW){

    
    if(timerStart = 0){
      timerStart = millis();
    }

    currentTime = millis();

    if(currentTime - timerStart > 250){
      connector.send(sendMasterCaution);
      timerStart = 0;
    }
   
  }
}

Its a wrap

This completes everything we need for our master caution button. For the master warning button you can just copy the same approach with different pin numbers. As always feel free to ask your questions in the comment section or on our Discord server.