Home ยป Back-to-basics: toggle/rocker switches

Back-to-basics: toggle/rocker switches

by Bits and Droids
3 comments
toggle switch group photo

The toggle switch. They occupy a prominent place in the heart of most flight simmers. From controlling the lights with a few rocker switches in a light aircraft to controlling complex systems of airliners. Do you want more advantages? They can be bought on the cheap, they are easy to install, and they are a piece that is easily fitted onto any material. Just drill a hole, add two wires, and we are good to go.

In a previous post, I went into full detail on how you can use a momentary pushbutton with Microsoft Flight Simulator 2020. If we strip it all down, the toggle/rocker switch functions the same as a momentary push button. The only difference is that we don’t have to physically hold on for our dear life when we want to keep the lights on. The code, however, requires some tweaks to work properly in MFS2020.

I will cover two methods:

  • Using the HID-project library (gamepad). This method makes it easy to create a setup usable in any flight sim (or any other game for that matter). Write your code, bind the keys from the menu, and you are good to go.
  • Using my Bits and Droids flight connector. Bind the keys directly on your Arduino and send your commands through the connector. This will also give you more freedom to change the function mid-game without having to alter any keybindings in-game (more on this later)

Used in this article

How to wire a toggle/rocker switch

Most toggle switches require either soldering or the use of connectors to connect them to your projects. They aren’t breadboard-friendly out of the box.

There are various switches available that can differ slightly on how to wire them properly. Some have 2 pins, some have 3 pins, and some even have 6 pins (there are more options available). Depending on your needs, you might look for some keywords. Usually, the switches are accompanied by a description like on-off, on-off-on, on-off-momentary, or on-on. On-off and on-on switches have 2 states and are usually easily recognizable by the 2 physical pins on the switch. The on-off-on, on-off-momentary have 3 states, and you guessed it, they are often found holding 3 pins. To make your life easier, you can often find the markings on the switches themselves as well (see the cute group photo below).

Toggle switches, 2 pins and 3 pins.
3 different switch types

In this article, we will wire these puppies up like normal pushbuttons utilizing the internal pull-down resistors of our boards (if you want to learn more about this, head over to our back-to-basics: momentary pushbuttons article). Whether we have 2 or 3 pins, we always need a pin running to our ground line. To keep things concise, I like to use the pin in the middle of the switch (with 2 pins, there is no middle, but one of the 2 will be closer to the absolute middle of the component). The more concise we work, the easier it becomes to check our circuit for faults or breakages. The remaining pin will each run towards a free IO slot on your board.

An example with (left) an on-off switch and (right) an on-off-on switch

The ground line can be daisy-chained through multiple switches (convenient when soldering). This will result in a cleaner, clutter-free, and most importantly, headache-free experience. There are ways to minimize the number of pins used, but we’re going to stick to the basics for now.

The logic

Let’s start by simplifying the logic. We begin with the on-off toggle. Eventhough it says on-off that doesn’t mean we have to treat it like an off state. To simplify it even further we just have state A and state B. If the toggle is in state A we want to trigger and event and if the toggle is in state B we want to trigger another. Now the events could be anything. Strobes on and strobes off, fuel pump on and fuel pump off, etc.

Switch with 2 states

The switches with 3 pins give us the possibility to use an extra state. State A, state B and state C (on-off-on). Just as the previous switch we can freely bind any event to either state.

switch with 3 states

Now We can’t completely ignore the on-off logic. It becomes rather important when determining whether we are in state A or state B. If the signal is LOW, we are in state A and if the signal is HIGH, the switch is in state B. Same goes for the switch with 3 pins. The difference is that state A is wired to a different pin on the board than state C. This logic is visualized in the table below.

Pin 1Ground pinPin 2
State ALOWHIGHLOW
State BHIGHLOWLOW
State CLOWLOWHIGH
state overview

Using the Gamepad library

Let us start with a universal approach to code our switches. You can use this approach in any game that lets you bind controls through the menu. For this, we specifically need an Arduino Pro-micro or Leonardo.

The first thing we need to do is install the HID-Project library. This can either be done through the build-in library manager in your Arduino IDE or by downloading the ZIP file directly from their Github page. There are other options available, but this one always struck home because of the ease of use.

To start using our gamepad, we have to include the library at the top of our sketch and start a Gamepad object in the setup block.

#include <HID-Project.h>

void setup() {
  Gamepad.begin();
}

If we run this sketch nothing will happen. This makes absolute sense since the board doesn’t know where we connected the switches. We do this by defining the pins underneath the library initialization for each switch. Because we make use of the internal pullup resistors of our board we need to initialize these in the setup block as well.

#include <HID-Project.h>

//Define the pin belonging to our on-off switch
const byte switchAPin1 = 3;

//Define the location of 2 pins belonging to our on-off-on switch
const byte switchBPin1 = 4;
const byte switchBPin2 = 5;

void setup() {
  Gamepad.begin();

  //Initialize the internal pullup resistors off all our switches
  pinMode(switchAPin1, INPUT_PULLUP);
  pinMode(switchBPin1, INPUT_PULLUP);
  pinMode(switchBPin2, INPUT_PULLUP);
}

The last piece of the puzzle we miss is logic. It now knows where the pins are but still has no clue what to do with them. For each pin, we want to check the current state to determine the button we press. Our gamepad can support up to 31 buttons in Windows numbered 1 – 31. If we combine the press of a Gamepad button with one of our switch states, we can create all kinds of cool stuff. To spice things up, there are some options we can still choose from. If we flip the switch, we can hold the Gamepad button; short press it, or anything in between. Depending on the game you use it for, this could be important to keep in mind. It’s essential to include the line Gamepad.write() whenever you want to update the Gamepad. Without this command, you will never register a keypress on the Gamepad.

  #include <HID-Project.h>

//Define the pin belonging to our on-off switch
const byte switchAPin1 = 3;

//Define the location of 2 pins belonging to our on-off-on switch
const byte switchBPin1 = 4;
const byte switchBPin2 = 5;

void setup() {
  Gamepad.begin();

  //Initialize the internal pullup resistors off all our switches
  pinMode(switchAPin1, INPUT_PULLUP);
  pinMode(switchBPin1, INPUT_PULLUP);
  pinMode(switchBPin2, INPUT_PULLUP);
}

void loop(){
  //----!!SWITCH A LOGIC!!----
  //read the pin state
  bool switchAPin1State = digitalRead(switchAPin1);

  //if the switchAPin1State == true
  //this is an alternative approach because HIGH = 1 and true = 1 as well
  //the next example will contain the traditional approach 

  if(switchAPin1State){
    Gamepad.press(1);
    Gamepad.release(11);
  } 

  //if this statement == false == LOW == 0
  else if(!switchAPin1State){
    Gamepad.release(1);
    Gamepad.press(11);
  }

  //----!!SWITCH B LOGIC!!----
  //read the pin state
  byte switchBPin1State = digitalRead(switchBPin1);
  byte switchBPin2State = digitalRead(switchBPin2);


  if(switchBPin1State == LOW){
    Gamepad.press(2);
    Gamepad.release(4);
  } 

  if(switchBPin1State == HIGH){
    Gamepad.release(2);
  }

  if(switchBPin1State == LOW){
    Gamepad.press(3);
    Gamepad.release(4);
  } 

  if(switchBPin1State == HIGH){
    Gamepad.release(3);
  }

  //Both are off
  if(switchBPin2State == LOW && switchBPin1State == LOW){
    Gamepad.press(4);
  }
  

  Gamepad.write();
}

This is a fundamental approach and demonstrates that there are many options available to utilize. By just focussing on 1 switch, I’ll demonstrate some alternative approaches down below. There are games out there that have widely different keybinds. Toggle to start an engine, another game might have you hold a button to keep the engine running, and perhaps another game has a sequence you need to toggle through. All valid options that require custom solutions.

#include <HID-Project.h>
byte oldBtnState;

...

void loop(){  

//----!!SWITCH A LOGIC!!----
  //read the pin state
  byte switchAPin1State = digitalRead(switchAPin1);

//----- 2 Button presses depending if the state = HIGH or LOW
  if(switchAPin1State == LOW){
    Gamepad.press(1);
    Gamepad.release(11);
  } 

  //if this statement == false == LOW == 0
  if(switchAPin1State == HIGH){
    Gamepad.release(1);
    Gamepad.press(11);
  }

//----- 1 Button press if the state = LOW
  if(switchAPin1State == LOW){
    Gamepad.press(1);
  } 

  //if this statement == false == LOW == 0
  if(switchAPin1State == HIGH){
    Gamepad.release(1);
  }


//----- 2 Button presses momentary
  if(switchAPin1State == LOW && oldBtnState == HIGH){
    Gamepad.press(1);
    delay(200);
    Gamepad.write();
    delay(20);
    Gamepad.release(1);
    delay(20);
    Gamepad.write();
    oldBtnState = LOW;
  } 

  //if this statement == false == LOW == 0
  if(switchAPin1State == HIGH){
    Gamepad.press(2);
    delay(200);
    Gamepad.write();
    delay(20);
    Gamepad.release(2);
    delay(20);
    Gamepad.write();
    oldBtnState = HIGH;
  }
}

Now once again, there are cleaner ways to code this, but I want you to get a basic understanding of what happens under the hood without too many sparkles. We can always add sprinkles to our cake at a later point.

Coding toggles with the Bits and Droids connector

Using my connector, you can directly send commands to the connector without binding any keys ingame. The most basic approach would look like this. Another plus is that you can use any Arduino (or microcontroller that can communicate over serial) with this approach.

#include <BitsAndDroidsFlightConnector.h>

//Create a connector object
BitsAndDroidsFlightConnector connector(false);

//Define the pin
const byte switchAPin1 = 3;

//To check against the current state
byte oldState;

void setup(){
  //With the connector it's important to initialize the Serial line
  Serial.begin(115200);
  pinMode(switchAPin1, INPUT_PULLUP);
}

void loop(){
  byte currentState = digitalRead(switchAPin1);
  if(currentState != oldState){
    if(currentState == HIGH){
      //Send a command to send Strobes on
      Serial.println(connector.sendStrobesOn());
    } else{
      //Send a command to send Strobes off
      Serial.println(connector.sendStrobesOff());
    }
    oldState = currentState;
  }
}

Now you might wonder why someone would prefer this over a Gamepad. One of the reasons might be the flexibility to switch between outputs on the fly. Let’s take a look at the code below.

#include <BitsAndDroidsFlightConnector.h>

//We add a variable to track the current mode
byte mode = 0;

...

void loop(){
  byte currentState = digitalRead(switchAPin1);
  if(currentState != oldState){
    if(currentState == HIGH){
      //send output based on 
      switch(mode){
        case 0: 
           Serial.println(connector.sendStrobesOn());
           break;
       case 1:
           Serial.println(connector.sendApuGeneratorSwitchToggle());
           break;
      }
    
    } 

...

    oldState = currentState;
  }
}

In this example, the output differs depending on a certain mode variable. We could easily add a pushbutton or rotary encoder to switch between modes. This way, we can have 1 controller that performs differently with each mode (without having to go into the key-binds menu and rebind everything or having to switch pre-sets).

The sky is the limit

Now with this, the possibilities are endless. Want to make a combination lock to start your plane? Go ahead! Just want a plain old toggle box? Go for it! As with all of these articles, you can use the acquired knowledge to mix and match the components to your needs. Combine some encoders, led panels with some toggle switches to create something truly yours, or keep it simple with just some toggles.

When you made it this far, I want to compliment you on your attention span bigger than mine. If you have any questions, suggestions, or spot a mistake, let me know in the comments below. Thanks for reading, and I hope to see you at the next one.

You may also like

3 comments

Csaba Kafka July 15, 2021 - 9:22 pm

I tried this code, for two switches:
#include

//Create a connector object
BitsAndDroidsFlightConnector connector(false);

const byte Battery = 2;
const byte AvionicsMaster = 3;
byte oldState;

void setup(){
//With the connector it’s important to initialize the Serial line
Serial.begin(115200);
pinMode(Battery, INPUT_PULLUP);
pinMode(AvionicsMaster, INPUT_PULLUP);
}

void loop(){
byte currentState = digitalRead(Battery);
if(currentState != oldState){
if(currentState == HIGH){
//Send a command to send Battery on
Serial.println(connector.sendToggleMasterBattery1());
} else{
//Send a command to send Battery off
Serial.println(connector.sendToggleMasterBattery1());
}
oldState = currentState;
}
}

{
byte currentState = digitalRead(AvionicsMaster);
if(currentState != oldState){
if(currentState == HIGH){
//Send a command to send AvionicsMaster on
Serial.println(connector.sendAvionicsMaster1On());
} else{
//Send a command to send AvionicsMaster off
Serial.println(connector.sendAvionicsMaster1Off());
}
oldState = currentState;
}
}

But I get this error:
Arduino: 1.8.13 (Windows 10), Board: “Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)”

msfs_toggle_switch_test:40:1: error: expected unqualified-id before ‘{‘ token
{
^
exit status 1
expected unqualified-id before ‘{‘ token

Bits and Droids July 16, 2021 - 7:24 am

You misplaced an } which closed the loop function before the second toggle.

#include

//Create a connector object
BitsAndDroidsFlightConnector connector(false);

const byte Battery = 2;
const byte AvionicsMaster = 3;
byte oldState;

void setup(){
//With the connector it’s important to initialize the Serial line
Serial.begin(115200);
pinMode(Battery, INPUT_PULLUP);
pinMode(AvionicsMaster, INPUT_PULLUP); }

void loop(){
byte currentState = digitalRead(Battery);
if(currentState != oldState){
if(currentState == HIGH){
//Send a command to send Battery on
Serial.println(connector.sendToggleMasterBattery1());
} else{
//Send a command to send Battery off
Serial.println(connector.sendToggleMasterBattery1());
}
oldState = currentState;
}

{
byte currentState = digitalRead(AvionicsMaster);
if(currentState != oldState){
if(currentState == HIGH){
//Send a command to send AvionicsMaster on
Serial.println(connector.sendAvionicsMaster1On());
} else{
//Send a command to send AvionicsMaster off
Serial.println(connector.sendAvionicsMaster1Off());
}
oldState = currentState;
}
}
}

Csaba Kafka July 17, 2021 - 8:47 am

Thanks a lot, it works

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept