#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
// --- LCD Configuration ---
// Find your I2C LCD address. Common addresses: 0x27, 0x3F.
// Use an I2C scanner sketch if you're unsure.
LiquidCrystal_I2C lcd(0x27, 16, 2); // Address, columns, rows
// --- Keypad Configuration ---
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'}
};
// Define GPIO pins for rows and columns
byte rowPins[ROWS] = {19, 18, 5, 17}; // Connect to R1, R2, R3, R4
byte colPins[COLS] = {16, 4, 0, 2}; // Connect to C1, C2, C3, C4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// --- LED Configuration ---
const int RED_LED_PIN = 13;
const int GREEN_LED_PIN = 12;
const int BLUE_LED_PIN = 14;
// For Common Anode LED (HIGH = OFF, LOW = ON)
#define LED_ON LOW
#define LED_OFF HIGH
// --- Button Configuration ---
const int BUTTON_PIN = 15;
// --- Password and State ---
#define PASSWORD "1234"
const int PASSWORD_LENGTH = strlen(PASSWORD);
typedef enum {
STATE_IDLE,
STATE_WAITING_FOR_START,
STATE_ENTERING_PASSWORD,
STATE_PASSWORD_CORRECT_GET_NUMBER,
STATE_SHOW_WELCOME
} SystemState_t;
SystemState_t currentState = STATE_IDLE;
int passwordIndex = 0;
char enteredPassword[PASSWORD_LENGTH + 1];
char doorNumberChar = '\0';
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
int lastButtonState = HIGH;
int buttonState;
void setup() {
Serial.begin(115200); // For debugging output to Serial Monitor
// --- Initialize LCD ---
lcd.init();
lcd.backlight();
lcd.clear();
// --- Initialize LEDs ---
pinMode(RED_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(BLUE_LED_PIN, OUTPUT);
setLed(LED_OFF, LED_OFF, LED_OFF); // All off
// --- Initialize Button ---
pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up
// --- Initial State ---
displayPrompt("System Ready", "Press Button");
currentState = STATE_WAITING_FOR_START;
Serial.println("System Initialized. Press button to start.");
}
void loop() {
char key = keypad.getKey();
// --- Handle Button Press to Start ---
if (currentState == STATE_WAITING_FOR_START) {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == LOW) { // Button pressed (active LOW due to PULLUP)
currentState = STATE_ENTERING_PASSWORD;
resetPasswordEntry();
displayPrompt("Enter Code:", "");
setLed(LED_OFF, LED_OFF, LED_ON); // Blue: entry mode
Serial.println("Password entry started.");
}
}
}
lastButtonState = reading;
}
// --- Process Keypad Input based on State ---
if (key != NO_KEY) { // Keypad library returns NO_KEY if nothing pressed
Serial.print("Key Pressed: "); Serial.println(key);
if (currentState == STATE_ENTERING_PASSWORD) {
if (passwordIndex < PASSWORD_LENGTH) {
lcd.setCursor(passwordIndex, 1); // Display on second line
lcd.print(key); // Or print '*' for security
if (key == PASSWORD[passwordIndex]) {
enteredPassword[passwordIndex] = key;
passwordIndex++;
setLed(LED_OFF, LED_ON, LED_OFF); // Green: correct digit
delay(200);
setLed(LED_OFF, LED_OFF, LED_ON); // Blue: back to entry
if (passwordIndex == PASSWORD_LENGTH) {
currentState = STATE_PASSWORD_CORRECT_GET_NUMBER;
setLed(LED_OFF, LED_ON, LED_OFF); // Green: password correct
displayPrompt("Correct Code!", "Enter Door #:");
Serial.println("Password correct! Enter door number.");
}
} else {
setLed(LED_ON, LED_OFF, LED_OFF); // Red: wrong digit
Serial.print("Wrong digit at index "); Serial.print(passwordIndex);
Serial.print(". Expected "); Serial.print(PASSWORD[passwordIndex]);
Serial.print(", got "); Serial.println(key);
delay(1000);
setLed(LED_OFF, LED_OFF, LED_ON); // Blue: back to entry
// For "index didn't increase, next user input will be checked by the same index"
// We just show error and wait for next key press for the same index.
// To make it more obvious, re-prompt and clear the wrongly entered digit.
displayPrompt("Enter Code:", "");
lcd.setCursor(0,1);
for(int i=0; i < passwordIndex; i++) lcd.print(PASSWORD[i]); // Or '*'
}
}
} else if (currentState == STATE_PASSWORD_CORRECT_GET_NUMBER) {
if (isDigit(key)) { // Check if the key is a digit '0'-'9'
doorNumberChar = key;
currentState = STATE_SHOW_WELCOME;
setLed(LED_OFF, LED_ON, LED_OFF); // Green
char welcomeMsg[17]; // 16 chars + null
sprintf(welcomeMsg, "Welcome to %c", doorNumberChar);
displayPrompt(welcomeMsg, "");
Serial.print("Door "); Serial.print(doorNumberChar); Serial.println(" rung.");
delay(3000);
setLed(LED_OFF, LED_OFF, LED_OFF); // All off
displayPrompt("System Ready", "Press Button");
currentState = STATE_WAITING_FOR_START;
Serial.println("System reset to waiting state.");
} else {
setLed(LED_ON, LED_OFF, LED_OFF); // Red: invalid door #
displayPrompt("Invalid Door #", "Use 0-9");
delay(1000);
setLed(LED_OFF, LED_ON, LED_OFF); // Green (password was correct)
displayPrompt("Correct Code!", "Enter Door #:"); // Re-prompt
}
}
}
// A small delay to prevent loop from running too fast if not much is happening
// Keypad library handles debouncing internally. Button has its own debounce.
delay(10);
}
// --- Helper Functions ---
void setLed(int r, int g, int b) {
digitalWrite(RED_LED_PIN, r);
digitalWrite(GREEN_LED_PIN, g);
digitalWrite(BLUE_LED_PIN, b);
}
void displayPrompt(const char* line1, const char* line2) {
lcd.clear();
if (line1) {
lcd.setCursor(0, 0);
lcd.print(line1);
}
if (line2) {
lcd.setCursor(0, 1);
lcd.print(line2);
}
}
void resetPasswordEntry() {
passwordIndex = 0;
for (int i = 0; i <= PASSWORD_LENGTH; i++) {
enteredPassword[i] = '\0';
}
// Optionally clear the part of the LCD where digits were shown
lcd.setCursor(0,1);
for(int i=0; i < PASSWORD_LENGTH; ++i) lcd.print(" ");
}