Flip Switch Password

Hello all, I am fairly new to coding/arduino but have some (very basic) knowledge on programming logic due to some very low level games ive created.

At this current time I have done all the research i can before i posted on here and i KNOW that others have done similar things and literally ONE step away from completing this task but due to my lack of programming knowledge im getting stuck.

So the design is straight forward. A bunch of flip switches in succession that must be flipped up in the correct order in order to have "lunch".

I have successfully programmed the switches to the corresponding LED to light up and i am using the Serial.println command to get a value when said switch is toggled as well as a gate to close the loop and have only one value pop up every time the switch is flipped. The only thing i haven't done yet is when the wrong switch is flipped for all values to return to "false" but that is fairly simple if i knew how to get the sequence in.

For now its 3 switches (to keep things simple).

Here is the code:

int SW1 = 2;
int SW2 = 3;
int SW3 = 4;
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;
int LED4_BINGO = 8;


boolean SW1g = true;
boolean SW2g = true;
boolean SW3g = true;

void setup() {
  Serial.begin(9600);
  pinMode(SW1, INPUT);
  pinMode(SW2, INPUT);
  pinMode(SW3, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
}

void loop() {

  // FOR TOGGLE SWITCH 1 //
  if (SW1g) {
    if (digitalRead(SW1) == HIGH) {
      digitalWrite(LED1, HIGH);
      Serial.print("SW1:");
      Serial.println(0);
      SW1g = false;
    }
  }
  if (!SW1g) {
    if (digitalRead(SW1) == LOW) {
      digitalWrite(LED1, LOW);
      Serial.print("SW1:");
      Serial.println(1);
      SW1g = true;
    }
  }
  // FOR TOGGLE SWITCH 2 //
  if (SW2g) {
    if (digitalRead(SW2) == HIGH) {
      digitalWrite(LED2, HIGH);
      Serial.print("SW2:");
      Serial.println(0);
      SW2g = false;
    }
  }
  if (!SW1g) {
    if (digitalRead(SW2) == LOW) {
      digitalWrite(LED2, LOW);
      Serial.print("SW2:");
      Serial.println(2);
      SW2g = true;
    }
  }
    // FOR TOGGLE SWITCH 3 //
  if (SW3g) {
    if (digitalRead(SW3) == HIGH) {
      digitalWrite(LED3, HIGH);
      Serial.print("SW3:");
      Serial.println(0);
      SW3g = false;
    }
  }
  if (!SW3g) {
    if (digitalRead(SW3) == LOW) {
      digitalWrite(LED3, LOW);
      Serial.print("SW3:");
      Serial.println(3);
      SW3g = true;
    }
  }
  delay(15);
}

Now the goal is to get the Serial.println value to match a sequence of "3, 1, 2" but I haven't a clue how to do it.

If there already a topic on this, please direct me to it because ive scoured the entire internet for this answer and ive been getting to the same place with different methods haha

You might want to review State Change Detection (Edge Detection)

Scan your switches every ≈ 20ms to remove switch bounce.

Act on switch state changes to advance a counter.

When the counter reaches a certain value, the password is met.

If a wrong switch is detected, the counter is disabled.

If switch operation is out of sequence, reset the counter.

Flags can also be used to show when a proper sequence is met.

Debouncing Switches in Arduino has a DebounceSwitch class that will do the scanning and has methods to check the state of the switch.

  /**
   * Returns true when the debounced state changes
   * this returns TRUE until the next time update() is called
   */
  bool isChanged();

  /**
   * Returns true when the debounced state of the switch is DOWN (connected to GND)
   */
  bool isDown();

Your strategy is very common for a learner…
Everything is very logical and linear, but in the long term, this ‘paints you into a corner’.

As noted above, state-change is important, but also look into arrays (and sequencing/iterating through those arrays).

Your current code has defined a ‘hard’ relationship between the switches and the sequence.

With a bit more thought, you can easily implement a way to change the ‘secret’ order of switches in one line, and add as many switches as you want with an extra added line) as well as significantly shortening the existing program!)

This is the beauty of ‘abstraction’, and using variables in your program… pointers and indexes also help, but this is a great project to get started with improving your coding techniques.

Hello,
did you ever used "structs" and arrary's in your projects?
The usage of "structs" and arrary's will simplify the sketch to a few line of codes.

Hey all! Thank you so much for such a quick response and appeasing my novice nature in thinking about this stuff. All of you had some really great suggestions and I'll be looking into them.

@LarryD I actually didn't know about that. Is there a source that you can search with a brief description and a list of "commands" that closely relate to what you need available? Im sure the list is endless on codes but for a newbie like me it would be helpful to possibly have something like that at my disposal. I do have a C++ Book for Dummies i bought many years back, i should look back into that as well. Im looking into your suggestion and going to give it a try

@drmpf interesting, so in this case you don't need to specify that the switch is an INPUT in the void setup()? This automatically will dictate the switch as an input?

@lastchancename I figured my way of thinking was common, it only makes sense when i know minimal codes or whats even possible with the arduino. I have more projects in store for it for sure but for now im starting.....simple? haha probably not simple i always throw myself into the fire when learning new things. Ill have to look how to do arrays. I saw a bunch of code before that used buttons instead of toggles and it used an array but I have 0 clue at what i am looking at. Would you have a good source that could show me how simple and complex arrays are set?

@paulpaulson i haven't unfortunately. I don't know what structs are, might you have a source where i can learn about that as well?

Hello
I will take a view into my sketch box and will see to find a proper example.

1 Like

From the Arduino Digital Pins reference
"Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs with pinMode() when you're using them as inputs"

But if you don't have an external pullup resistor that your switch is grounding, then set
pinMode(pin,INPUT_PULLUP);
in setup() (I will clarify that on the webpage)

I should read my own code :frowning:
The pinMode(pin, INPUT_PULLUP) is done for you in the DebouncedSwitch(sw); constructor.
So no you don't need to do it in setup(), but any pinMode(sw,... ) in setup will override the what DebouncedSwitch(sw) did

I would mash the three switch states into a 3-bit binary value:

   int value = (SW3g * 4) | (SW2g * 2) | (SW1g * 2);

The low bit represents Switch 1.

Now you can list the valid combinations:

0b000,  // No switches on
0b100,  // Switch 3 on
0b101,  // Switches 3 and 1 on
0b111,  // All three switches on

Any other combination means they were turned on out of order or a switch was turned off before the sequence was completed. You can test in a switch/case statement:

  switch (value)
  {
  case 0b000,  // No switches on
  case 0b100,  // Switch 3 on
  case 0b101,  // Switches 3 and 1 on
    // This combination is valid
    break;

  case 0b111,  // All three switches on
    // This combination is valid and complete!
    // OPEN THE LOCK
    break;

  default: // Any other pattern
    // Invalid combination
    // SOUND THE ALARM
    break;
  }

@johnwasser I like this idea quite abit but i have some questions. Bare with me because i doing my train of thought through typing so a may be wrong at the start but understand it at the end.

It seems that it would do the same thing that my original code does. It takes the 3 switches, it identifies them, and causes an action. Though what i will say is i may be reading this wrong because it still seems to me that there is "required sequence" to be performed here.

It just opens when the code reaches 0b111 where all 3 switches have been turned on.

Unless "case" is dictating that case 0b000 must happen first, then if 0b100 happens it is good to go, but if 0b110 happens it goes to "default:" and will reset things. Right? I think that is whats going on....

though the one thing i am confused on is
" I would mash the three switch states into a 3-bit binary value:

int value = (SW3g * 4) | (SW2g * 2) | (SW1g * 2);"

Not sure what you mean here, could you elaborate?

The code assumes that the switches are being checked every time through loop(), thousands of times per second. If someone can switch all of the switches on in a few microsecond then they have a chance of bypassing the sequence check. If any of the switches bounce out of sequence, that would violate the sequence check.

Oops. That should be:
int value = (SW3g * 4) | (SW2g * 2) | (SW1g * 1);
or
int value = (SW3g * 4) | (SW2g * 2) | (SW1g);

Code to try:

//Forum URL  https://forum.arduino.cc/t/flip-switch-password/888111/13

//Password lock push switch 1 then 2 then 3
//Reset switch cancels sequence

// Version    YY/MM/DD  Description
// 1.00       21/07/23

#define LEDon              HIGH
#define LEDoff             LOW

#define switchIsClosed     LOW
#define switchIsOpen       HIGH

#define SET                true
#define RESET              false

#define enabled            true
#define disabled           false

byte lastSW1State        = switchIsOpen;
byte lastSW2State        = switchIsOpen;
byte lastSW3State        = switchIsOpen;
byte lastResetState      = switchIsOpen;

byte counterPW           = 0;

const byte SW1           = 2; //+5V----[50k INPUT_PULLUP]---[pin]----[switch]----GND
const byte SW2           = 3;
const byte SW3           = 4;
const byte ResetSwitch   = 5;

const byte LED3          = 13; //[pin]----[220R]----[>|]----GND
const byte LED2          = 12;
const byte LED1          = 11;
const byte LED4_BINGO    = 8;

const byte heartbeatLED  = 7;

//timing stuff
unsigned long heartbeatMillis;
unsigned long switchMillis;

//************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(SW1, INPUT_PULLUP);
  pinMode(SW2, INPUT_PULLUP);
  pinMode(SW3, INPUT_PULLUP);
  pinMode(ResetSwitch, INPUT_PULLUP);

  pinMode(LED1, OUTPUT);
  digitalWrite(LED1, LEDoff);

  pinMode(LED2, OUTPUT);
  digitalWrite(LED2, LEDoff);

  pinMode(LED3, OUTPUT);
  digitalWrite(LED2, LEDoff);

  pinMode(LED4_BINGO, OUTPUT);
  digitalWrite(LED4_BINGO, LEDoff);

  pinMode(heartbeatLED, OUTPUT);

} //END of setup()


//************************************************************************
void loop()
{
  //***********************
  //time to toggle the heartbeat LED (every 1/2 second) ?
  if (millis() - heartbeatMillis >= 500)
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***********************
  //time to check the switches (every 50ms) ?
  if (millis() - switchMillis >= 50)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

  //***********************
  //other non-blocking code goes here
  //***********************

} //END of loop()


//************************************************************************
void checkSwitches()
{
  byte state;

  //*****************************************       SW1
  state = digitalRead(SW1);

  //***********************
  //has the switch changed state ?
  if (lastSW1State != state)
  {
    //update to the new state
    lastSW1State = state;

    //************
    //was the switch pushed at count 0 ?
    if (state == switchIsClosed)
    {
      if (counterPW == 0)
      {
        digitalWrite(LED1, LEDon);
        Serial.print("SW1: ");
        Serial.println("Enabled");

        counterPW = 1;
      }

      //wrong sequence
      else
      {
        systemOFF();
      }

    } //END of  if (state == switchIsClosed)
  } //END of  if (lastSwitchState != state)

  //*****************************************       SW2
  state = digitalRead(SW2);

  //***********************
  //has the switch changed state ?
  if (lastSW2State != state)
  {
    //update to the new state
    lastSW2State = state;

    //************
    //was the switch pushed at count 1 ?
    if (state == switchIsClosed)
    {
      if (counterPW == 1)
      {
        digitalWrite(LED2, LEDon);
        Serial.print("SW2: ");
        Serial.println("Enabled");

        counterPW = 2;
      }

      //wrong sequence
      else
      {
        systemOFF();
      }

    } //END of  if (state == switchIsClosed)
  } //END of  if (lastSwitchState != state)

  //*****************************************       SW3
  state = digitalRead(SW3);

  //***********************
  //has the switch changed state ?
  if (lastSW3State != state)
  {
    //update to the new state
    lastSW3State = state;

    //************
    //was the switch pushed at count 2 ?
    if (state == switchIsClosed)
    {
      if (counterPW == 2)
      {
        digitalWrite(LED3, LEDon);
        Serial.print("SW3: ");
        Serial.println("Enabled");

        digitalWrite(LED4_BINGO, LEDon);

        counterPW = 255;
      }

      //wrong sequence
      else
      {
        systemOFF();
      }

    } //END of  if (state == switchIsClosed)
  } //END of  if (lastSwitchState != state)

  //*****************************************       ResetSwitch
  state = digitalRead(ResetSwitch);

  //***********************
  //has the switch changed state ?
  if (lastResetState != state)
  {
    //update to the new state
    lastResetState = state;

    //************
    //was the switch pushed ?
    if (state == switchIsClosed)
    {
      systemOFF();
    }

  } //END of  if (lastSwitchState != state)


} //END of  checkSwitches()


//************************************************************************
void systemOFF()
{
  digitalWrite(LED1, LEDoff);
  digitalWrite(LED2, LEDoff);
  digitalWrite(LED3, LEDoff);
  digitalWrite(LED4_BINGO, LEDoff);

  counterPW = 0;

} //END of  systemOFF()

//************************************************************************

Here is a sketch that will allow for up to 16 switches easily, will report each time any switch changes, will require each change to result in a valid pattern, and will report success when all switches in the pattern are on.

const byte SwitchCount = 3;  // Note: If this goes above 16, increase the pattern size

const byte SwitchPins[SwitchCount] = {2, 3, 4};
const byte LEDPins[SwitchCount] = {11, 12, 13};
const byte BINGOPin = 8;

int SwitchValues[SwitchCount];

// Note: Not all switches have to be on at the end of the pattern
const byte ValidOrder[] = {3, 1, 2};  // Note: Subtract 1 to get bit index.
const byte OrderCount = sizeof ValidOrder / sizeof ValidOrder[0];  // Number of switches in pattern

const byte PatternCount = OrderCount + 1;  // Add one for the empty pattern
unsigned int ValidPatterns[PatternCount];

unsigned int LastPattern = 0;

void setup()
{
  Serial.begin(9600);
  for (int i = 0; i < SwitchCount; i++)
  {
    pinMode(SwitchPins[i], INPUT);
    pinMode(LEDPins[i], OUTPUT);
  }
  pinMode(BINGOPin, OUTPUT);
  digitalWrite(BINGOPin, LOW);

  // Generate the list of valid patterns
  unsigned int pattern = 0;
  ValidPatterns[0] = pattern;  // The empty pattern
  for (int i = 0; i < OrderCount; i++)
  {
    pattern |= 1 << (ValidOrder[i] - 1);
    ValidPatterns[i + 1] = pattern;
  }
}

void loop()
{
  unsigned int pattern = 0;

  for (int i = 0; i < SwitchCount; i++)
  {
    SwitchValues[i] = digitalRead(SwitchPins[i]);
    digitalWrite(LEDPins[i], SwitchValues[i]);
    pattern |=  SwitchValues[i] << i;
  }

  // Examine the pattern if it has changed
  if (pattern != LastPattern)
  {
    LastPattern = pattern;
    Serial.print("New pattern: ");
    Serial.println(pattern, BIN);

    boolean isValid = false;
    byte validIndex = 0;
    for (int i = 0; i < PatternCount; i++)
    {
      if (pattern == ValidPatterns[i])
      {
        isValid = true;
        validIndex = i;
      }
    }

    if (isValid)
    {
      Serial.print("Valid Pattern.  Match at step ");
      Serial.println(validIndex);
      if (validIndex == PatternCount - 1)
      {
        Serial.println("FULL MATCH.  OPEN THE LOCK.");
        digitalWrite(BINGOPin, HIGH);
      }
    }
    else // not valid
    {
      Serial.println("INVQALID PATTERN.  SOUND THE ALARM!");
    }
  }
}

@LarryD wow! Thank you so much for taking the time to do all that!

Just so you know, I am using tinkercad to mess about with the code to see if things will work before i even try it on the actual arduino.

I set everything up and used the code that you provided and it seems to work just about right but it works for a button push rather than a flip switch which is one action "UP" for "ON". Within the code it works if i flip the switch twice (UP, DOWN) and will complete the first combination.

The only thing that i didn't add is the extra switch which was the reset switch that you added for pin5.

Is there way to adjust it for toggle switches rather than pushbutton switches?

@johnwasser i am having the same issue with this code that i am having with Larry's code :confused:

Can you copy and paste the Serial Monitor output? Did you make sure all the switches were off before starting the sketch?

What kind of toggle switch ?

SPST
SPDT
ON/OFF/ON ?


Show us a good schematic of your circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.

Does this game start with all the toggle switches in the “off” position or is the objective simply to change the state of the toggle switches, from their current position , in the correct order.

Depending on the game, push button switches may be a better choice.

Is there a timeout to cancel a half finished game so the next player starts from a clean position?

@johnwasser so i got home and started to play with the board and wired everything up. Using your written code it all works, for the most part. The BINGO light doesn't turn off when there is an error. So i did a few tweeks after studying the code you wrote. Check it out.

The next part is to force a restart on the first error. So that all switches MUST be toggled off in order to attempt to re-enter the correct password.

const byte SwitchCount = 3;  // Note: If this goes above 16, increase the pattern size

const byte SwitchPins[SwitchCount] = {2, 3, 4};
const byte LEDPins[SwitchCount] = {11, 12, 13};
const byte BINGOPin = 8;
const byte ERRORPin = 7;

int SwitchValues[SwitchCount];

// Note: Not all switches have to be on at the end of the pattern
const byte ValidOrder[] = {3, 1, 2};  // Note: Subtract 1 to get bit index.
const byte OrderCount = sizeof ValidOrder / sizeof ValidOrder[0];  // Number of switches in pattern

const byte PatternCount = OrderCount + 1;  // Add one for the empty pattern
unsigned int ValidPatterns[PatternCount];

unsigned int LastPattern = 0;

void setup()
{
  Serial.begin(9600);
  for (int i = 0; i < SwitchCount; i++)
  {
    pinMode(SwitchPins[i], INPUT);
    pinMode(LEDPins[i], OUTPUT);
  }
  pinMode(BINGOPin, OUTPUT);
  pinMode(ERRORPin, OUTPUT);
  digitalWrite(BINGOPin, LOW);

  // Generate the list of valid patterns
  unsigned int pattern = 0;
  ValidPatterns[0] = pattern;  // The empty pattern
  for (int i = 0; i < OrderCount; i++)
  {
    pattern |= 1 << (ValidOrder[i] - 1);
    ValidPatterns[i + 1] = pattern;
  }
}

void loop()
{
  unsigned int pattern = 0;

  for (int i = 0; i < SwitchCount; i++)
  {
    SwitchValues[i] = digitalRead(SwitchPins[i]);
    digitalWrite(LEDPins[i], SwitchValues[i]);
    pattern |=  SwitchValues[i] << i;
  }

  // Examine the pattern if it has changed
  if (pattern != LastPattern)
  {
    LastPattern = pattern;
    Serial.print("New pattern: ");
    Serial.println(pattern, BIN);

    boolean isValid = false;
    byte validIndex = 0;
    for (int i = 0; i < PatternCount; i++)
    {
      if (pattern == ValidPatterns[i])
      {
        isValid = true;
        validIndex = i;
      }
    }

    if (isValid)
    {
      Serial.print("Valid Pattern.  Match at step ");
      Serial.println(validIndex);
      if (validIndex == PatternCount - 1)
      {
        Serial.println("FULL MATCH.  OPEN THE LOCK.");
        digitalWrite(BINGOPin, HIGH);
        delay(1500);
        reset();
      }
    }
    else // not valid
    {
      Serial.println("INVALID PATTERN.  SOUND THE ALARM!");
      {
        void loop();
        digitalWrite(ERRORPin, HIGH);
        delay(150);
        digitalWrite(ERRORPin, LOW);
        delay(150);
        reset();
      }
    }
  }
}



void reset()
{
  LastPattern = 0;
  digitalWrite(BINGOPin, LOW);
}

@LarryD three prong toggle switches. (Im not sure if links are allowed but this is what i am coding for) Amazon.com: DaierTek 12V Blue LED Lighted Toggle Switch with Safety Cover Guard 3 Pin ON Off Rocker Heavy Duty for Motorcycle Racing Car Automotive RV ATV Truck -2pack: Industrial & Scientific

Good news! The code that @johnwasser wrote works with a couple tweeks i gave it. See above

@6v6gt well, for the most part it would be strictly a "find the right order, if error occurs you lost try again from the start"