Four button combination lock

Hi everybody, this is my first post in this forum.

Yesterday my roommate brought an arduino kit, any of us has any idea how it works but we like this stuff so we started learning the basics. Yesterday nigth we coudn't make a led with a resistance work and today we made a 4 button combination lock, the wiring was pretty easy, and the program took a while (I know some basic C).

This is what we came up, the hardest thing was to figure out how the loop and the imput pins of the buttons work, with adding some delay we finally made it work, but I feel like the code can be much bettter. Do you guys can take a look and suggest us some ways of imporving the code, please? Thank you! :smiley:

The sequence is 3,4,2,1. If you press the wrong button while making the correct sequence it resets and the red led turns on. If you introduce the correct sequence the blue red truns on and resets and turns off when any button is pressed again.

const int b1 = 2;
const int b2 = 3;   
const int b3 = 4;   
const int b4 = 5;     
const int blu =  6;
const int red =  7;

int b1State = 0;
int b2State = 0;
int b3State = 0;
int b4State = 0;

int d1=0,d2=0,d3=0,d4=0;

void setup() {
  pinMode(blu, OUTPUT);
  pinMode(red, OUTPUT);
  pinMode(b1, INPUT);
  pinMode(b2, INPUT);
  pinMode(b3, INPUT);
  pinMode(b4, INPUT);
}

void loop() {
  b1State = digitalRead(b1);
  b2State = digitalRead(b2);
  b3State = digitalRead(b3);
  b4State = digitalRead(b4);

  if(d4==1){
    digitalWrite(blu, HIGH);
    }else{
      digitalWrite(blu, LOW);
      }

  if(b3State == HIGH){
    if(d1==0 && d2==0 && d3==0 && d4==0){d1=1;delay(300);}else{d1=0;d2=0;d3=0;d4=0;digitalWrite(red, HIGH);delay(1000);digitalWrite(red, LOW);}
    }
  if(b4State == HIGH){
    if(d1==1 && d2==0 && d3==0 && d4==0){d2=1;delay(300);}else{d1=0;d2=0;d3=0;d4=0;digitalWrite(red, HIGH);delay(1000);digitalWrite(red, LOW);}
    }
  if(b2State == HIGH){
    if(d1==1 && d2==1 && d3==0 && d4==0){d3=1;delay(300);}else{d1=0;d2=0;d3=0;d4=0;digitalWrite(red, HIGH);delay(1000);digitalWrite(red, LOW);}
    }
  if(b1State == HIGH){
    if(d1==1 && d2==1 && d3==1 && d4==0){d4=1;delay(300);}else{d1=0;d2=0;d3=0;d4=0;digitalWrite(red, HIGH);delay(1000);digitalWrite(red, LOW);}
    }
  }

Read this:

int d1=0,d2=0,d3=0,d4=0;

Getyourspacebarfixed.

How are the switches wired? Using the internal pullup resistors makes for easier wiring.

Using the state change detection example, you could have much simpler code, and record when a particular switch is pressed. Then, it is easy to determine, at the end or at any other point, whether the switches were pressed in the right order.

    if(d1==1 && d2==1 && d3==1 && d4==0){d4=1;delay(300);}else{d1=0;d2=0;d3=0;d4=0;digitalWrite(red, HIGH);delay(1000);digitalWrite(red, LOW);}

Get
your
enter
key
fixed.

Thank you! I used the state change detection metoth and It works much better, changed the input to input_pullup and removed all the resistors, also made the code a little more compact and I fixed my spacebar and enter keys :smiley:

The main problem I have with the code is that I can't use 1 button twice :confused:

const int button[] = {2,3,4,5};
const int ledpin[] = {6,7};

int ButtonState[] = {0,0,0,0};
int lastButtonState[] = {0,0,0,0};
int ledState[] = {0,0};
int password[] = {0,1,2,3};
int p = 0;

void setup() {
  pinMode(button[0], INPUT_PULLUP);
  pinMode(button[1], INPUT_PULLUP);
  pinMode(button[2], INPUT_PULLUP);
  pinMode(button[3], INPUT_PULLUP);
  pinMode(ledpin[0], OUTPUT);
  pinMode(ledpin[1], OUTPUT);
}

void loop() {
  for(int i=0; i<4; i++){
  ButtonState[i] = digitalRead(button[i]);
  }

  if(p==4) ledState[0]=1;
  else     ledState[0]=0;

  for(int i=0; i<4; i++){    
    if(ButtonState[i] != lastButtonState[i]){
      if(ButtonState[i] == 1){
        for(int j=0; j<4; j++){
          if(i==password[j]){
            if(p==j)p++;
            else    p=0;
          }
        }
      }
      lastButtonState[i] = ButtonState[i];
    }
  }

  digitalWrite(ledpin[0], ledState[0]);
  delay(20);
}

Well, I solved the problem deleting all the code and replace it with a 'switch'. Do you guys know any way of make it more compact/shorter? Thanks!

const int button[] = {2,3,4,5};
const int ledpin[] = {6,7};

int ButtonState[] = {0,0,0,0};
int lastButtonState[] = {0,0,0,0};
int password[] = {0,1,0,3};
int level = 0;

void setup() {
  pinMode(button[0], INPUT_PULLUP);
  pinMode(button[1], INPUT_PULLUP);
  pinMode(button[2], INPUT_PULLUP);
  pinMode(button[3], INPUT_PULLUP);
  pinMode(ledpin[0], OUTPUT);
  pinMode(ledpin[1], OUTPUT);
}

void loop() {
  for(int i=0; i<4; i++){
  ButtonState[i] = digitalRead(button[i]);
  }

  for(int i=0; i<4; i++){    
    if(ButtonState[i] != lastButtonState[i]){
      if(ButtonState[i] == 1){
        switch(level){
          case 0: if(i == password[0]) level++;
                  else level=5;
                  break;
          case 1: if(i == password[1]) level++;
                  else level=5;
                  break;
          case 2: if(i == password[2]) level++;
                  else level=5;
                  break;
          case 3: if(i == password[3]) level++;
                  else level=5;
                  break;
          }
      }
      lastButtonState[i] = ButtonState[i];
    }
  }

  if(level==4){
    digitalWrite(ledpin[0], HIGH);
    delay(1000);
    digitalWrite(ledpin[0], LOW);
    level=0;
    }

  if(level==5){
    for(int j=0; j<4; j++){
      digitalWrite(ledpin[1], HIGH);
      delay(50);
      digitalWrite(ledpin[1], LOW);
      delay(50);
    }
    level=0;
  }
    
  delay(20);
}

I'd maintain an index variable to the password array. Each time through loop() I would check on one switch. If a switch press is detected then I'd it check against the password. If it clears my index gets advanced otherwise it is reset. Should be easy to work the blinking leds in there.

Do you guys know any way of make it more compact/shorter?

  pinMode(button[0], INPUT_PULLUP);
  pinMode(button[1], INPUT_PULLUP);
  pinMode(button[2], INPUT_PULLUP);
  pinMode(button[3], INPUT_PULLUP);

Good thing you don't have 100 switches. Use a for loop.

  for(int i=0; i<4; i++){
  ButtonState[i] = digitalRead(button[i]);
  }

Your space bar is still sticking, or your enter key is missing some attempts to use it. Now, your tab key seems to be having problems. Maybe you should just get a new keyboard.

  for(int i=0; i<4; i++)
  {
     ButtonState[i] = digitalRead(button[i]);
  }

Each time that you detect that a switch has become pressed, regardless of which one it is, store the pin array index in another array. If the ith switch is pressed, store i in the next position in the array.

At the end of loop(), compare the array of indices to an array of indices in the correct order. If the lengths are the same, and the contents match, the correct sequence of switches was pressed.

I'd maintain an index variable to the password array. Each time through loop() I would check on one switch. If a switch press is detected then I'd it check against the password. If it clears my index gets advanced otherwise it is reset. Should be easy to work the blinking leds in there.

Isn't that exactly the same way I'm doing the code in my previous post? Sorry If I'm wrong.

Each time that you detect that a switch has become pressed, regardless of which one it is, store the pin array index in another array. If the ith switch is pressed, store i in the next position in the array.

At the end of loop(), compare the array of indices to an array of indices in the correct order. If the lengths are the same, and the contents match, the correct sequence of switches was pressed.

That's exactly the system my friend made the first day, we coded separately to see who came up with the better way. The main problem with this is that you have to press a n number of buttons before comparing and consequently reseting the array. If the user presses a wrong number and he hasn't keep the count of on what digit he won't know when the password will reset.

Isn't that exactly the same way I'm doing the code in my previous post?

I don't see that. Where do you store any information about the switch that has become pressed?

The main problem with this is that you have to press a n number of buttons before comparing and consequently reseting the array. If the user presses a wrong number and he hasn't keep the count of on what digit he won't know when the password will reset.

That's why your phone has an oops key AND it provides feedback showing how many switch presses it has recognized.

For the buttons one could use a library, like so

// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

enum {
  noKey,
  oneKey,
  twoKeys,
  threeKeys,
  openLock,
  ignoreKeys,
  errorState
};

const byte buttons[] = {2, 3, 4, 5};
const byte ledPins[] = {6, 7};

Bounce keys[sizeof(buttons)];

const unsigned int de_Duration = 1000;

bool displaySuccess = false;
byte ds_Round;
unsigned long ds_Start;
unsigned int ds_currCycle;
const unsigned int ds_Repeats = 4;
const unsigned int ds_DurationOn = 50;
const unsigned int ds_DurationOff = 50;

byte password[] = {0, 1, 0, 3};

byte state = noKey;
byte eatKeys;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Keylock 4 keys, 2 leds"));
  for (byte idx = 0; idx < sizeof(buttons); idx++) {
    keys[idx].attach(buttons[idx], INPUT_PULLUP);
  }
  for (byte idx = 0; idx < sizeof(ledPins); idx++) {
    pinMode(ledPins[idx], OUTPUT);
  }
}

bool keyCheck(byte idx, byte thenState) {
  bool ret = keys[password[idx]].fell();
  Serial.print(F(" key checked "));
  Serial.print(password[idx]);
  if (ret) {
    state = thenState;
    Serial.print(F(" ok"));
  } else {
    eatKeys = 3 - idx;
    state = ignoreKeys;
    Serial.print(F(" bad, eat "));
    Serial.print(eatKeys);
  }
  Serial.print(F(" next "));
  pState(state);
  return ret;
}

void loop() {
  static unsigned long timedStart;
  unsigned long topLoop = millis();
  bool keyPress = false;

  for (byte idx = 0; idx < sizeof(buttons); idx++) {
    keys[idx].update();
    keyPress |= keys[idx].fell();
  }
  if (keyPress || (state == errorState)) {
    if (keyPress) {
      Serial.print(F("key in "));
      pState(state);
    }
    switch (state) {
      case noKey:
        keyCheck(0, oneKey);
        break;
      case oneKey:
        keyCheck(1, twoKeys);
        break;
      case twoKeys:
        keyCheck(2, threeKeys);
        break;
      case threeKeys:
        if (!keyCheck(3, openLock)) {
          state = errorState;
          timedStart = topLoop;
          Serial.print(F(" last key bad, enter "));
          pState(state);
          break;
        }
        Serial.print(F("Fall into "));
        pState(state);
      case openLock:
        displaySuccess = true;
        ds_Round = ds_Repeats;
        ds_Start = topLoop;
        ds_currCycle = ds_DurationOn;
        digitalWrite(ledPins[0], HIGH);
        state = noKey;
        Serial.print(F(" back to "));
        pState(state);
        break;
      case errorState:
        if (topLoop - timedStart > de_Duration) {
          state = noKey;
          Serial.print(F(" error time elapsed, back to "));
          pState(state);
        }
        break;
      case ignoreKeys:
        Serial.print(F(" to eat "));
        Serial.println(eatKeys);
        if (eatKeys-- == 1) {
          Serial.print(F(" last key consumed"));
          digitalWrite(ledPins[1], HIGH);
          timedStart = topLoop;
          state = errorState;
          Serial.print(F(" next "));
          pState(state);
        }
        break;
    }
  }
  digitalWrite(ledPins[1], state == errorState);
  if (displaySuccess) {
    if (topLoop - ds_Start > ds_currCycle) {
      ds_Start = topLoop;
      if (digitalRead(ledPins[0])) {
        ds_currCycle = ds_DurationOff;
      } else {
        if (ds_Round-- != 1) {
          ds_currCycle = ds_DurationOn;
        } else {
          displaySuccess = false;
          digitalWrite(ledPins[0], HIGH);
        }
      }
      digitalWrite(ledPins[0], !digitalRead(ledPins[0]));
    }
  }
}

void pState(byte value) {
  switch (value) {
    case noKey:
      Serial.println(F("noKey"));
      break;
    case oneKey:
      Serial.println(F("oneKey"));
      break;
    case twoKeys:
      Serial.println(F("twoKeys"));
      break;
    case threeKeys:
      Serial.println(F("threeKeys"));
      break;
    case openLock:
      Serial.println(F("openLock"));
      break;
    case ignoreKeys:
      Serial.println(F("ignoreKeys"));
      break;
    case errorState:
      Serial.println(F("errorState"));
      break;
  }
}