Arduino PIN number Lock with Timeout, Temporary block

Arduino keypad lock is a simple PIN number-based authentication that can be used for many applications. It can be also used to execute additional programs or operate devices when an unlock attempt is either detected as authenticated or unauthenticated by the locking system.

Basic PIN Lock

A basic keypad lock compares the input keys with the existing stored PIN number. In code 1 the pin number is declared in a char array. Each input character key is compared in sequence with the characters in the array. Each time if the char matches the variable pin_match increments by 1. So when the “pin_match” value becomes equal to the pin length after the number of keys equal to the pin length is entered, then the PIN is correct. Else the input PIN doesn’t match with the stored PIN, hence it is considered as the wrong PIN.

Basic code 1


const int pinlength = 5;//PIN length
int i = 0, pin_match = 0;
char pin_number[pinlength] = "12345";//PIN

void setup() {
  Serial.begin(9600);
  reset();
}

void loop() {
  while (Serial.available()) {
    Serial.print("*");
    char key = Serial.read();
    i++;
    int i_key = i - 1;
    if (pin_number[i_key] == key) {
      pin_match++;
    }
    if (pin_match == pinlength) {
      unlock();
    } else if (i_key >= (pinlength - 1)) {
      wrong_pin_number();
    }
  }
}

void lock() {
  Serial.println("Locked");
  /*
    Code for Any other operations.
  */
}
void unlock() {
  Serial.println("\nSuccess Unlocked, Locks in 5sec");

  /*
    Code for Any other operations.
  */
  delay(5000);
  //Lock after 5 Second
  lock();
  reset();
}
void wrong_pin_number() {
  Serial.println("\nSorry Wrong PIN, try again");
  /*
    Code for Any other operations.
  */
  delay(2000);
  reset();
}
void reset() {
  pin_match = 0; i = 0;
  Serial.print("\nEnter PIN : ");
  /*
    Code for Any other operations.
  */
}

In basic code 2, the Arduino inbuilt function equals() is used to compare the equality of PIN numbers. Here the PIN is stored as a string and the entered PIN will be stored in another string. Then both are compared using the equals() function. The equals function return true if both are equal hence the lock will unlock and if it returns false strings are not equal hence the Pin number is wrong.

Basic code 2

String pin_number = "12345", inputkey = "#####";
int i = 0, pin_match = 0;

void setup() {
  Serial.begin(9600);
  reset();
}

void loop() {
  while (Serial.available()) {
    Serial.print("*");
    char key = Serial.read();
    i++;
    int i_key = i - 1;
    inputkey[i_key] = key;
    if (pin_number.equals(inputkey)) {
      unlock();
    } else if (i_key >= 4) {
      wrong_pin_number();
    }
  }
}

void lock() {
  Serial.println("Locked");
}
void unlock() {
  Serial.println("\nSuccess Unlocked, Press lockpin to lock");
  const int lockpin = 2;
  pinMode(lockpin, INTPUT);
  while (1) {
    if (digitalRead(lockpin) == HIGH) {
      lock(); // Enable Lock when the switch at pin 2 is pressed.
      break;
    }
    delay(50);
  }
  reset();
}
void wrong_pin_number() {
  Serial.println("\nSorry Wrong PIN, try again");
  delay(2000);
  reset();
}
void reset() {
  inputkey = "#####"; i = 0;
  Serial.print("\nEnter PIN : ");
}

The above basic code 1, has a time delay between the unlock and lock, that is the lock will be enabled after a short period of time after unlocking. In basic code 2, it has shown with additional external input to enable the lock after unlocking; a high state-input needs to be applied at Pin2 to enable the lock. Both time-based auto-lock and the manual lock can be added to the circuit.

This basic code can be added with many additional functionalities and features. Any codes can be used in the area for success-code or fail-code in the sketch to execute any operation following a successful authentication or failed authentication. Like an LCD display with the status, LED indications, Relays, operate a servo motor or other motor-based lock mechanism, SMS alerts, etc.

Number Lock with Keypad and LCD display

Here a keypad for PIN input and LCD display output is added to the basic code.

Refer: LCD and keypad interfacing with arduino

Code

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const int pinlength = 5;
int i = 0, pin_match = 0;
char pin_number[pinlength] = "12345";

void setup() {
  lcd.init();
  lcd.backlight();
  reset();
}

void loop() {
  char key = keypad.getKey();
  if (key) {
    lcd.print("*");
    i++;
    int i_key = i - 1;
    if (pin_number[i_key] == key) {
      pin_match++;
    }
    if (pin_match == pinlength) {
      unlock();
    } else if (i_key >= (pinlength - 1)) {
      wrong_pin_number();
    }
  }
}

void lock() {
  /*
    Code for Any other operations.
  */
}
void unlock() {
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Unlocked");
  lcd.setCursor(0, 1);
  lcd.print("Press D to Lock");
  /*
    Code for Any other operations.
  */
  while (1) {
    char lock_key = keypad.getKey();
    if (lock_key == 'D') {
      lock();
      break;
    }
    delay(50);
  }
  reset();
}
void wrong_pin_number() {
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Wrong PIN");
  /*
    Code for any other operations.
  */
  delay(2000);
  reset();
}
void reset() {
  pin_match = 0; i = 0;
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Enter PIN");
  lcd.setCursor(1, 1);
  /*
    Code for any other  operations.
  */
}

Keypad with PIN input Timeout

In previous codes, if a key is pressed then the next number can be pressed at any time. Irrespective of the time taken between keys the system waits as long as the next input is received. A timeout option adds the feature that the circuit waits only for a short interval of time after the last key is pressed. If no input has been pressed within the timeout period, it clears the keys registered up to that moment and needs to renter the complete PIN again from the beginning.

code

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const int pinlength = 5;
int i = 0, pin_match = 0;
char pin_number[pinlength] = "12345";
long time_start = 0;

void setup() {
  lcd.init();
  lcd.backlight();
  reset();
}

void loop() {
  if ((millis() - time_start) >= 5000 && time_start > 0) {
    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("Time out");
    delay(1000);
    reset();
  }
  char key = keypad.getKey();
  if (key) {
    if (time_start == 0) {
      time_start =  millis();
    }
    lcd.print("*");
    i++;
    int i_key = i - 1;
    if (pin_number[i_key] == key) {
      pin_match++;
    }
    if (pin_match == pinlength) {
      unlock();
    } else if (i_key >= (pinlength - 1)) {
      wrong_pin_number();
    }
  }
}

void lock() {
  /*
    Code for Any other operations.
  */
}
void unlock() {

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Unlocked");
  lcd.setCursor(0, 1);
  lcd.print("Press D to Lock");
  /*
    Code for Any other operations.
  */
  while (1) {
    char lock_key = keypad.getKey();
    if (lock_key == 'D') {
      lock();
      break;
    }
    delay(50);
  }
  reset();
}
void wrong_pin_number() {
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Wrong PIN");
  /*
    Code for any other operations.
  */
  delay(2000);
  reset();
}
void reset() {
  pin_match = 0; i = 0; time_start = 0;
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Enter PIN");
  lcd.setCursor(1, 1);
  /*
    Code for any other  operations.
  */
}

Block for 3 consecutive wrong attempts

In the code, if the PIN number is entered as wrong for 3 consecutive attempts, then the lock will be temporarily disabled for a fixed period of time preventing further attempts during the period. Extra functions like alerts, warnings, etc. can be also used with this feature to respond when the number of wrong attempts exceeds a limit.

Code

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const int pinlength = 5;
int i = 0, pin_match = 0, attempt = 0;
char pin_number[pinlength] = "12345";
long timedelay = 0;

void setup() {
  lcd.init();
  lcd.backlight();
  reset();
}

void loop() {
  char key = keypad.getKey();
  if (key) {
    if (attempt < 3) {
      lcd.print("*");
      i++;
      int i_key = i - 1;
      if (pin_number[i_key] == key) {
        pin_match++;
      }
      if (pin_match == pinlength) {
        unlock();
      } else if (i_key >= (pinlength - 1)) {
        wrong_pin_number();
      }
    }
  }
  if (millis() - timedelay > 30000 && attempt >= 3) {
    attempt = 0;
    reset();
  }
  else if (attempt >= 3) {
    int c = 30 - (millis() / 1000) + (timedelay / 1000);
    lcd.setCursor(0, 0);
    lcd.print("Too may attempts");
    lcd.setCursor(0, 1);
    lcd.print("wait ");
    lcd.print(c);
    lcd.print(" seconds");
    delay(1000);
  }
}

void lock() {
  /*
    Code for Any other operations.
  */
}
void unlock() {
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Unlocked");
  lcd.setCursor(0, 1);
  lcd.print("Press D to Lock");
  /*
    Code for Any other operations.
  */
  attempt = 0;
  while (1) {
    char lock_key = keypad.getKey();
    if (lock_key == 'D') {
      lock();
      break;
    }
  }
  reset();
}
void wrong_pin_number() {
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Wrong PIN");
  /*
    Code for any other operations.
  */
  attempt++;
  delay(2000);
  timedelay = millis();
  reset();
}
void reset() {
  pin_match = 0; i = 0;
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Enter PIN");
  lcd.setCursor(1, 1);
  /*
    Code for any other  operations.
  */
}

Keypad Lock with PIN changing option

In previous codes, there was no option to change the PIN once the sketch is uploaded, the code has to be re-uploaded with a new PIN in order to change the existing PIN.

  1. Press key C to enable the PIN changing function.
  2. Enter the existing PIN, then
  3. Enter the New PIN
  4. Press D to save a new PIN, Success.

Here the PIN is storing in the EEPROM memory of the Arduino. Every time when the Arduino starts to run, it reads the PIN stored in the EEPROM memory. So it can be rewritten any time to modify the PIN.

Refer more: Save data on Arduino EEPROM memory.

Code

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

char pin_number[5], temp[5];
int i = 0, pin_match = 0, address = 190, tempi = 0;
char pineprom[5] = {'1', '2', '3', '4', '5'};
String changepassword = "disable";

void setup() {
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  for (int er = 0; er <= 4; er++) {
    //  char pin_number[er] = EEPROM.read(address + er);
    pin_number[er] = pineprom[er];
    Serial.println(pin_number[er]);
  }
  reset();
}

void loop() {
  char key = keypad.getKey();
  if (changepassword == "disable" && key && key != 'C') {
    lcd.print("*");
    Serial.print("*");
    i++;
    int i_key = i - 1;
    //pin_number checking - start
    if (pin_number[i_key] == key) {
      pin_match++;
    }
    if (pin_match == 5) {
      unlock();
    } else if (i_key >= 4) {
      wrong_pin_number();
    }
    //pin_number checking - end
  } else if (changepassword == "enable" && key && key != 'D') {
    if (tempi <= 4) {
      temp[tempi] = key;
      tempi++;
      lcd.setCursor(tempi, 1);
      lcd.print(key);
      Serial.print(tempi);
    }
  } else if (key == 'C') {
    changepassword = "enable";
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Set New password");
    lcd.setCursor(1, 1);
  }
  if (key == 'D' && tempi == 5) {
    for (int ew = 0; ew <= 4; ew++) {
      //  EEPROM.write(address + ew, temp[ew]);
      pin_number[ew] = temp[ew];
      Serial.println(pineprom[ew]);
    }
    changepassword = "disable";
    tempi = 0;
    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("Success");
    delay(2000);
    reset();
  } else if (key == 'D') {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Something wrong");
  }
}

void unlock() {
  /*
    Code for Any other operations.
  */
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Unlocked");
  lcd.setCursor(0, 1);
  lcd.print("Press D to Lock");
  while (1) {
    char lock_key = keypad.getKey();
    if (lock_key == 'D') {
      lock();
      break;
    }
  }
  reset();
}
void wrong_pin_number() {
  /*
    Code for any other operations.
  */
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Wrong PIN");
  delay(3000);
  reset();
}
void reset() {
  /*
    Code for any other  operations.
  */
  pin_match = 0;
  i = 0;
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Enter PIN");
  lcd.setCursor(1, 1);
}
void lock() {

}

Leave a Reply

Your email address will not be published. Required fields are marked *