Flip Switch Password

Hello and good morning
I have made a quick homebrewed sketch to handle the flipswitches and associated leds, and keep in mind the flipswitches are connected to GND. You still have to add your application for password handling. I have commented the place for it in the sketch accordingly.

//BLOCK COMMENT
// https://forum.arduino.cc/t/flip-switch-password/888111/4
#define ProjectName "Flip Switch Password"
// CONSTANT DEFINITION
// you have to arrange the pin addresses to you hardware setup
const byte Leds[] {2, 3, 4};
const byte FlipSwitches[] {A0, A1, A2};
const unsigned long scanTime = 10;
// VARIABLE DECLARATION
enum {One, Two, Three};
// specify a block containing the variables
struct BLOCK {
  int name; // of the block
  String text; //  to be displayed
  byte led;  // pin address of LED
  byte flip; // pin address of flip switch
  bool state; // memory for last flip switch state
};
// now fill the block with relevant data
BLOCK block_ [] {
  {One, "Sw1", Leds[One], FlipSwitches[One], 1},
  {Two, "Sw2", Leds[Two], FlipSwitches[Two], 1},
  {Three, "Sw3", Leds[Three], FlipSwitches[Three], 1},
};
// now you have three blocks available
// e.g. the variable "block_[One].led" contains the pin address of the led etc
// this greatly simplifies the processing for the same data content, see blow

void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);
  //  range-based for loop instruction to set the needed pinModes for the leds and flip switches
  for (auto led : Leds) pinMode(led, OUTPUT);
  for (auto flipSwitches : FlipSwitches) pinMode(flipSwitches, INPUT_PULLUP);
}
void loop () {
  unsigned long currentTime = millis();
  static unsigned long myTime;
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  // every 10msec we check the states of the flip switches
  if (currentTime - myTime >= scanTime) {
    myTime = currentTime;
    // by using the range-based for loop instruction  again
    for (auto &block : block_) {
      bool stateNew = digitalRead(block.flip);
      // any state change detected?
      if (block.state != stateNew) {
        // yes, lock the next call
        block.state = stateNew;
        // switch led
        digitalWrite(block.led, !block.state);
        // and generate the text output as specified above
        Serial.print(block.text);
        Serial.println(!block.state ? " ON" : " OFF");
        
        // space to implement your processing
        switch (block.name) {
          // flip switch named One to Three either pressed or released
          case One:

            break;
          case Two:

            break;
          case Three:

            break;
        }
      }
    }
  }
}

Have fun and enjoy the weekend.

Using this code the only thing it does it blink the LED on pin # 13. Flipping switches doesn't change its state. Unless im understanding your code incorrectly.

Hello
Did you read this comment, didn´t you?

I grazed over it and was looking for just that to make sense of it. Thank you for pointing it out!

Those are simple SPST switches with a light.

Putting a declaration of the loop() function in the middle of your code doesn't do anything. Did you want to repeat the blinking for a while? How about we blink the ERROR LED when we are waiting for all of the switches to go back to OFF:

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;
boolean Ready = false;

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;
  }

  if (!Ready && pattern != 0)
  {
    // Until all of the switch go to OFF we just
    // blink the error LED.
    digitalWrite(ERRORPin, HIGH);
    delay(150);
    digitalWrite(ERRORPin, LOW);
    delay(150);
    return;
  }

  Ready = true;

  // 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!");
      reset(); 
    }
  }
}

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

If this is to be a combination lock, it really shouldn't sound an alarm on the first mistake. It should wait until the final pattern is reached and then sound the alarm if any mistakes were made along the way. With the current method of reporting any error it would take at most two wrong guesses to determine the correct first switch and at most one wrong guess to get the second and third switches.

With the code below, someone trying to break in would have to turn on all three switches to know if any were out of order. That could take as many as 6 guesses.

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;
boolean Ready = false;
boolean MistakeMade = false;

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;
  }

  // Reset the MistakeMade flag when all switches are off
  if (pattern == 0)
    MistakeMade = false;

  if (!Ready && pattern != 0)
  {
    // Until all of the switch go to OFF we just
    // blink the error LED.
    digitalWrite(ERRORPin, HIGH);
    delay(150);
    digitalWrite(ERRORPin, LOW);
    delay(150);

    if (MistakeMade)
    {
      // NOTE: If the MistakeMade flag is set,
      // the last attempt was a failure and
      // this might be a good time for an audible
      // alarm
    }

    return;
  }

  Ready = true;

  // 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)
      {
        // Reached the final pattern
        if (!MistakeMade)
        {
          // No mistakes made!  Unlock.
          Serial.println("FULL MATCH.  OPEN THE LOCK.");
          digitalWrite(BINGOPin, HIGH);
          delay(1500);
          digitalWrite(BINGOPin, LOW);
        }
        // Just start blinking the error light until
        // all the switches are off again
        reset();
      }
    }
    else // not a valid pattern
    {
      // Flag the mistake and let the person
      // keep guessing.  The lock will now
      // not open until going back to all
      // switches off and running the sequence
      // without error.
      MistakeMade = true;
    }
  }
}

void reset()
{
  Ready = false;
  LastPattern = 0;
}

@johnwasser so i studied your code for the past day or so and i did a few modifications to it to add one more LED which is the individual RESET LED and then a dedicated "ERROR" LED.

There was a hickup though in the code under if (MistakeMade). This would initiate on the first mistake instead of letting the pattern happen and THEN cause the LED to flash. After studying the code in depth and understanding arrays and what not (and after LOTS of trial and error) its all working.

Thank you to everyone who has helped me thus far. I wouldn't of been able to even come close to this without the help of you all. :slight_smile: