40. Password Protected Locker System

The given task is to design a password-protected locker system using a microcontroller that secures access through two types of PINs — a Master PIN and a User PIN.

Requirements

  • During the first-time setup, the system prompts the user to set a User PIN.
  • The User PIN is used to unlock or access the locker.
  • The user can update the PIN after verifying the current User PIN.
  • The Master PIN provides administrative access to reset the User PIN if it is forgotten.
  • All PINs should be stored in EEPROM to retain them even after a reset or power loss.

Why Use EEPROM?

  • EEPROM (Electrically Erasable Programmable Read-Only Memory) is a non-volatile memory that preserves stored data even when power is turned off.
  • It is ideal for this application because it allows secure storage and retrieval of PINs across power cycles, ensuring the system remains functional and secure after restarts.

This task will  be implemented with the following microcontrollers:

  1. ESP32
  2. Arduino UNO

We are using the ESP32 DevKit v4 development board and programming it using the Arduino IDE.

  • Before uploading, make sure to select “ESP32 Dev Module” as the board to ensure correct settings and compatibility.

ESP32 does not have a real EEPROM chip inside. Instead, part of its Flash memory is used to simulate EEPROM behaviour.

Hardware Connection

Connect the ESP32  development board to your computer using a USB cable.

Code

#include <EEPROM.h>

#define EEPROM_USER_PIN_ADDR 0  // Starting address for user PIN in EEPROM
#define EEPROM_SIZE 32          // >= 4 bytes for the PIN; adjust as needed
#define MASTER_PIN "1234"       // Master PIN for reset (string literal)

String pin12;

void setup() {
  Serial.begin(115200);
  Serial.println("Password-Protected Locker System Started!");

  // REQUIRED on ESP32: start EEPROM emulation before any read/write
  EEPROM.begin(EEPROM_SIZE);

  // Check if a user PIN is already set (0xFF means erased/uninitialized)
  if (EEPROM.read(EEPROM_USER_PIN_ADDR) == 0xFF) {
    setInitialPIN();
  } else {
    Serial.println("User PIN is already set. Enter your PIN to access.");
  }
}

void loop() {
  Serial.println("\nMain Menu:");
  Serial.println("1. Enter PIN to Unlock");
  Serial.println("2. Update User PIN");
  Serial.println("3. Reset PIN with Master PIN");
  Serial.print("Choose an option (1-3): ");
  while (Serial.available() == 0)
    ;  // Wait for user input

  int option = Serial.parseInt();
  Serial.println(option);

  switch (option) {
    case 1: verifyPIN(); break;
    case 2: updateUserPIN(); break;
    case 3: resetPINWithMaster(); break;
    default: Serial.println("Invalid option. Please try again."); break;
  }

  // flush any trailing characters for cleanliness
  while (Serial.available()) Serial.read();
}

void setInitialPIN() {
  Serial.println("No PIN found. Set a new 4-digit PIN:");
  String newPIN = getValidatedPIN();

  for (int i = 0; i < 4; i++) {
    EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
  }
  // REQUIRED on ESP32: persist writes to Flash
  EEPROM.commit();

  Serial.println("User PIN set successfully!");
}

void verifyPIN() {
  Serial.println("Enter your 4-digit PIN to unlock:");
  String enteredPIN = getValidatedPIN();
  String storedPIN = readStoredPIN();

  if (enteredPIN == storedPIN) {
    Serial.println("Access Granted!");
  } else {
    Serial.println("Incorrect PIN. Access Denied.");
  }
}

void updateUserPIN() {
  Serial.println("Enter your current 4-digit PIN:");
  String currentPIN = getValidatedPIN();
  String storedPIN = readStoredPIN();

  if (currentPIN == storedPIN) {
    Serial.println("Enter your new 4-digit PIN:");
    String newPIN = getValidatedPIN();

    for (int i = 0; i < 4; i++) {
      EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
    }
    // Commit ONCE after all bytes
    EEPROM.commit();

    Serial.println("User PIN updated successfully!");
  } else {
    Serial.println("Incorrect current PIN. Update failed.");
  }
}

void resetPINWithMaster() {
  Serial.println("Enter the Master PIN to reset user PIN:");
  String enteredMasterPIN = getValidatedPIN();

  if (enteredMasterPIN == MASTER_PIN) {
    Serial.println("Enter a new 4-digit User PIN:");
    String newPIN = getValidatedPIN();

    for (int i = 0; i < 4; i++) {
      EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
    }
    // Single commit after the loop (more efficient and correct)
    EEPROM.commit();

    Serial.println("User PIN reset successfully!");
    pin12 = readStoredPIN();
    Serial.println("Verifying stored PIN:");
    Serial.println(pin12);
  } else {
    Serial.println("Incorrect Master PIN. Reset failed.");
  }
}

String getValidatedPIN() {
  while (true) {
    while (Serial.available() == 0)
      ;                                          // Wait for user input
    String recv = Serial.readStringUntil('\n');  // Read until newline
    recv.trim();                                 // Remove CR/LF and spaces

    // Must be exactly 4 digits
    if (recv.length() == 4 && isDigits(recv)) {
      return recv;
    }

    if (recv.length() > 0) {
      Serial.println("Invalid input. Please enter a 4-digit PIN:");
    }
  }
}

bool isDigits(String str) {
  for (int i = 0; i < str.length(); i++) {
    if (!isDigit(str[i])) return false;
  }
  return true;
}

String readStoredPIN() {
  char pin[5];  // 4 digits + null
  for (int i = 0; i < 4; i++) {
    pin[i] = EEPROM.read(EEPROM_USER_PIN_ADDR + i);
  }
  pin[4] = '\0';
  return String(pin);
}

Code Explanation

  • setup();
    • Runs once at startup. Initialises serial communication, starts EEPROM emulation with 32 bytes, and checks if a user PIN exists.
    • If EEPROM is empty, it calls setInitialPIN() to create a new 4-digit PIN.
  • loop();
    • Continuously shows the main menu. Reads the user’s menu choice and calls the respective function.
  • setInitialPIN();
    • Executes only when no PIN is stored. Prompts the user to enter a valid 4-digit PIN, writes it byte-by-byte into EEPROM.
  • verifyPIN();
    • Asks the user to enter their 4-digit PIN, reads the stored PIN from EEPROM, and compares them. Prints “Access Granted” if they match or “Access Denied” if not.
  • updateUserPIN();
    • Verifies the current PIN before allowing an update. If correct, it prompts for a new 4-digit PIN, overwrites the old one in EEPROM.
  • resetPINWithMaster();
    • Allows resetting the user PIN using a predefined master PIN (1234). If entered correctly, it accepts a new user PIN, saves it to EEPROM.
  • getValidatedPIN();
    • Reads Serial input from the user until a newline. Ensures the entered value is exactly four digits long and numeric. Keeps prompting until a valid PIN is entered.
  • isDigits(String str);
    • Helper function that checks whether all characters in a given string are digits (0–9).
  • readStoredPIN();
    • Reads 4 bytes from EEPROM starting at address 0, converts them into a string, and returns the stored 4-digit PIN for comparison or display.

Precautions when Allocating Flash to EEPROM

  1. Don’t exceed Flash partition size
    • The emulated EEPROM uses the “app data” area of Flash and is safe up to about 4096 bytes. Allocating more (e.g., EEPROM.begin(8000)) can corrupt other Flash data or cause runtime errors.
  2. Write infrequently
    • Each Flash cell supports a limited number of writes (~100k).
      • Avoid continuous writing in loop().
      • Use EEPROM.commit() only when values actually change.
  3. Call EEPROM.commit() after writing
    • Without this, your changes stay only in RAM and disappear after a restart.
  4. Avoid overlapping with other Flash uses

We are using the Arduino UNO development board and programming it using the Arduino IDE.

  • Before uploading, make sure to select “Arduino UNO” as the board to ensure correct settings and compatibility.

Hardware Connection

Connect the Arduino UNO  development board to your computer using a USB cable.

Firmware Implementation

We are going to use EEPROM.h library to implement this task. In Arduino UNO, there is a separate EEPROM chip available of 1KB.

Code

#include <EEPROM.h>

#define EEPROM_USER_PIN_ADDR 0  // Starting address for user PIN in EEPROM
#define MASTER_PIN "1234"       // Master PIN for reset (stored as a string)

void setup() {
  Serial.begin(9600);
  Serial.println("Password-Protected Locker System Started!");

  // Check if a user PIN is already set
  if (EEPROM.read(EEPROM_USER_PIN_ADDR) == 255) {  // Uninitialized EEPROM value
    setInitialPIN();                               // Prompt user to set initial PIN
  } else {
    Serial.println("User PIN is already set. Enter your PIN to access.");
  }
}


void loop() {
  Serial.println("\nMain Menu:");
  Serial.println("1. Enter PIN to Unlock");
  Serial.println("2. Update User PIN");
  Serial.println("3. Reset PIN with Master PIN");
  Serial.print("Choose an option (1-3): ");
  while (Serial.available() == 0)
    ;  // Wait for user input
  int option = Serial.parseInt();
  Serial.println(option);

  switch (option) {
    case 1:
      verifyPIN();
      break;
    case 2:
      updateUserPIN();
      break;
    case 3:
      resetPINWithMaster();
      break;
    default:
      Serial.println("Invalid option. Please try again.");
  }
}


void setInitialPIN() {
  Serial.println("No PIN found. Set a new 4-digit PIN:");
  String newPIN = getValidatedPIN();
  for (int i = 0; i < 4; i++) {
    EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
  }
  Serial.println("User PIN set successfully!");
}


void verifyPIN() {
  Serial.println("Enter your 4-digit PIN to unlock:");
  String enteredPIN = getValidatedPIN();
  String storedPIN = readStoredPIN();

  if (enteredPIN == storedPIN) {
    Serial.println("Access Granted!");
  } else {
    Serial.println("Incorrect PIN. Access Denied.");
  }
}


void updateUserPIN() {
  Serial.println("Enter your current 4-digit PIN:");
  String currentPIN = getValidatedPIN();
  String storedPIN = readStoredPIN();

  if (currentPIN == storedPIN) {
    Serial.println("Enter your new 4-digit PIN:");
    String newPIN = getValidatedPIN();
    for (int i = 0; i < 4; i++) {
      EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
    }
    Serial.println("User PIN updated successfully!");
  } else {
    Serial.println("Incorrect current PIN. Update failed.");
  }
}

void resetPINWithMaster() {
  Serial.println("Enter the Master PIN to reset user PIN:");
  String enteredMasterPIN = getValidatedPIN();

  if (enteredMasterPIN == MASTER_PIN) {
    Serial.println("Enter a new 4-digit User PIN:");
    String newPIN = getValidatedPIN();
    for (int i = 0; i < 4; i++) {
      EEPROM.write(EEPROM_USER_PIN_ADDR + i, newPIN[i]);
    }
    Serial.println("User PIN reset successfully!");
  } else {
    Serial.println("Incorrect Master PIN. Reset failed.");
  }
}


String getValidatedPIN() {
  while (true) {
    while (Serial.available() == 0)
      ;                                          // Wait for user input
    String recv = Serial.readStringUntil('\n');  // Read input until newline
    recv.trim();                                 // Remove leading/trailing whitespace and CR/LF


    // Check if input is exactly 4 digits
    if (recv.length() == 4 && isDigits(recv)) {
      return recv;  // Return valid 4-digit PIN
    }

    if (recv.length() > 1)
      Serial.println("Invalid input. Please enter a 4-digit PIN:");
  }
}


bool isDigits(String str) {
  for (int i = 0; i < str.length(); i++) {
    if (!isDigit(str[i])) {
      return false;  // Return false if any character is not a digit
    }
  }
  return true;
}


String readStoredPIN() {
  char pin[5];  // Buffer for 4 digits + null terminator
  for (int i = 0; i < 4; i++) {
    pin[i] = EEPROM.read(EEPROM_USER_PIN_ADDR + i);
  }
  pin[4] = '\0';  // Null-terminate the string
  return String(pin);
}


Code Explanation

  • setup();
    • Initializes Serial communication and checks if a PIN exists in EEPROM; if not, asks the user to set a new one.
  • loop();
    • Displays the main menu, reads the user’s choice, and calls the related function to verify, update, or reset the PIN.
  • setInitialPIN();
    • Prompts the user to enter a new 4-digit PIN and stores it byte by byte in EEPROM.
  • verifyPIN();
    • Takes user input and compares it with the stored EEPROM PIN to grant or deny access.
  • updateUserPIN();
    • Verifies the old PIN first, then allows setting and saving a new 4-digit PIN in EEPROM.
  • resetPINWithMaster();
    • Resets the user PIN only if the correct master PIN is entered, then stores the new one in EEPROM.
  • getValidatedPIN();
    • Reads and validates Serial input to ensure it’s exactly four numeric digits before returning it.
  • isDigits(String str);
    • Checks whether all characters in a string are numeric digits (0–9).
  • readStoredPIN();
    • Reads 4 bytes from EEPROM, converts them into a string, and returns the stored user PIN.

Precautions:

  • Be careful while writing to the EEPROM, as it has limited write cycles.
  • Writing time is 3.3ms per byte; consider this while writing your code.

Output

Output Video