/*
  Voltaat learn (http://learn.voltaat.com)
  Link for full tutorial: 
  LiquidCrystal I2C Library:
  Wire Library:
  Keypad Library:
  Servo Library: 
  Tutorial: Build a Secure Safe with Password Protection
  Connections from the keypad to Arduino:
  • Keypad pin 1 → Arduino pin 2
  • Keypad pin 2 → Arduino pin 3
  • Keypad pin 3 → Arduino pin 4
  • Keypad pin 4 → Arduino pin 5
  • Keypad pin 5 → Arduino pin 6
  • Keypad pin 6 → Arduino pin 7
  • Keypad pin 7 → Arduino pin 8
  • Keypad pin 8 → Arduino pin 9
  Connections from the servo motor:
  • Servo GND pin→ breadboard GND line
  • Servo 5V pin→ breadboard 5V line
  • Servo signal pin → Arduino pin 10
  Connections from the LCD:
  • I2C module GND pin → Breadboard GND line
  • I2C module 5V pin → breadboard GND line
  • I2C module SDA pin → Arduino pin A4
  • I2C module SCL pin → Arduino pin A5
*/
// Define libraries 
#include "Keypad.h"
#include "Wire.h"
#include "LiquidCrystal_I2C.h"
#include "Servo.h"
// Define the 4x4 keypad pins
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; // Connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; // Connect to the column pinouts of the keypad
// Initialize the keypad, LCD, and servo
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD I2C address and dimensions
Servo myServo; // Create a Servo object
// Set password and other variables
String password = "1234B"; // Set the password to open the safe
const int MAX_PASSWORD_LENGTH = 4; // maximum password length, change accordingly
String enteredPassword = ""; // Store the entered password
bool safeOpen = false; // Store the state of the safe (open or closed)
// Commands inside void setup run once
void setup() {
  Serial.begin(9600); // Initialize serial communication
  lcd.init(); // Initialize the LCD
  lcd.backlight(); // Turn on the LCD backlight
  myServo.attach(10); // Attach the servo to pin 10
  myServo.write(0); // Initialize the servo to closed position
  lcd.setCursor(1, 0);
  lcd.print("Voltaat Learn"); // Display "Security Safe" on the first line
  lcd.setCursor(1, 1);
  lcd.print("Security Safe"); // Clear the second line
}
// Commands inside void loop run forever
void loop() {
  static bool showEnterPassword = false;
  static unsigned long prevMillis = 0;
  const unsigned long delayTime = 5000; // 5 seconds
  static bool showSafeClosed = false;
  static unsigned long safeClosedMillis = 0;
  const unsigned long safeClosedDelayTime = 2000; // 2 seconds
  // Show "Enter Password" message after 5 seconds
  if (!showEnterPassword && millis() - prevMillis >= delayTime) {
    showEnterPassword = true;
    prevMillis = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Enter Password:");
  }
  // Read keypad input and handle password entry
  char key = keypad.getKey();
  if (showEnterPassword && key != NO_KEY) {
    if (key == '#') { // Check if enter key is pressed
      if (enteredPassword == password) { // Compare entered password with the correct password
        if (safeOpen) { // If safe is already open, close it and ask to enter password again
          safeOpen = false;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Press C to close");
          enteredPassword = ""; // Clear entered password
        } else { // If safe is closed, open it and ask to close it
          safeOpen = true;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Safe is open");
          lcd.setCursor(0, 1);
          lcd.print("Press C to close");
          myServo.write(90); // Open the safe by turning the servo to 90 degrees
          enteredPassword = ""; // Clear entered password
        }
      } else { // Incorrect password entered
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Wrong Password!");
        delay(2000);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Enter Password:");
        enteredPassword = ""; // Clear entered password
      }
    } else if (key == '*') { // Check if clear key is pressed
      enteredPassword = ""; // Clear entered password
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Enter Password:");
    } else if (key == 'C' || key == 'c') { // Check if C key is pressed
      if (safeOpen) { // If safe is open, close it
        safeOpen = false;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Safe is closed");
        myServo.write(0); // Close the safe by turning the servo to 0 degrees
        showSafeClosed = true;
        safeClosedMillis = millis();
        enteredPassword = ""; // Clear entered password
      }
    } else if (key == 'D' || key == 'd') { // Check if D key is pressed
      if (enteredPassword.length() > 0) { // If password is not empty, delete last character
        enteredPassword = enteredPassword.substring(0, enteredPassword.length() - 1);
        lcd.setCursor(0, 1);
        lcd.print("            "); // Clear the password field on the LCD screen
        lcd.setCursor(0, 1);
        // Print asterisks instead of the actual password characters
        for (int i = 0; i < enteredPassword.length(); i++) {
          lcd.print("*");
        }
      }
    } else { // Append entered key to the entered password
      if (enteredPassword.length() < 5 && !safeOpen) { // Limit password to 5 characters
        enteredPassword += key;
        lcd.setCursor(0, 1);
        // Print asterisks instead of the actual password characters
        for (int i = 0; i < enteredPassword.length(); i++) {
          lcd.print("*");
        }
      }
    }
  }
  // show "Safe Closed" message for 2 seconds after the safe is closed
  if (showSafeClosed && millis() - safeClosedMillis >= safeClosedDelayTime) {
    showSafeClosed = false;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Enter Password:");
  }
}