Today, we will use the latest update to create our master warning and master caution buttons. This article is the first entry for the gear lever handle unit.
These are affiliate links that help support the channel.
https://amzn.to/3FZFnNN
https://amzn.to/3n1u7Yx
https://amzn.to/3BS9jst
Tools needed:https://amzn.to/3aPP6rn
https://amzn.to/3jdckfG
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.
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.
These buttons have 5 poles. Our LED is connected to the pin marked with 5V and the ground pin. To reduce the number 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.
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 must add a resistor between our board and the pin. I'd recommend a 470 ohm resistor. You might try using a 220 ohm (way too 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 result will look something like the image below.
Let's begin by opening a new sketch in our Arduino IDE. At the 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 to our microcontroller. The data is sent to our microcontroller over the serial line. It's essential that the baud rate of the connector and our controller match (by default, this is 115200).
cpp#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); }
When buying these buttons, there are usually two options: latching and momentary. Latching means the button locks in place when pressed, while 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 of which one you've bought.
We want to register an action for the latching buttons each time we change state (HIGH -> LOW, LOW -> HIGH). 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 current state by calling the digitalRead(pinOfTheButton) function. This returns either LOW or HIGH (0 or 1).
cpp#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 triggers 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 when 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 timer window of 250 to be safe (there isn't a real need to press the caution button rapidly anyway).
cpp#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; } } }
For those of you who use momentary buttons, we need a slightly different approach. The button's default state is HIGH. The moment you press the button, the state changes to LOW. We can apply the same debounce approach as in the latching example.
cpp#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; } } }
Coding up the LED is one of the most easy parts. The logic comes down to the following: 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.
cpp#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; } } }
This completes everything we need for our master caution button. You can copy the same approach with different pin numbers for the master warning button. As always, feel free to ask your questions in the comment section or on our Discord server.