/*
* IR Remote Password Lock System
* ================================
* - Default password: 1234
* - User can change password (stored in EEPROM)
* - Green LED = Unlocked, Red LED = Locked
* - LCD displays status and input
*
* BUTTONS:
* 0-9 : Enter password digits
* PLAY : Confirm / Submit password
* POWER : Lock the system
* MENU : Enter change-password mode (when unlocked)
* C : Clear current input
* BACK : Cancel operation
*/
#include <IRremote.hpp>
#include <LiquidCrystal.h>
#include <EEPROM.h>
// ───── Pin Definitions ─────
#define IR_RECEIVE_PIN 2
#define GREEN_LED_PIN 10 // Unlocked indicator
#define RED_LED_PIN 11 // Locked indicator
// ───── EEPROM Addresses ─────
#define EEPROM_FLAG_ADDR 0 // Flag to check if password was saved before
#define EEPROM_LEN_ADDR 1 // Password length
#define EEPROM_PASS_ADDR 2 // Password digits start here
#define EEPROM_FLAG_VALUE 42 // Magic number to detect first boot
// ───── Constants ─────
#define MAX_PASS_LEN 8
#define DEFAULT_PASS "1234"
// ───── System States ─────
enum State {
STATE_LOCKED,
STATE_UNLOCKED,
STATE_CHANGE_PASS_OLD, // Ask for old password before changing
STATE_CHANGE_PASS_NEW, // Enter new password
STATE_CHANGE_PASS_CONFIRM // Confirm new password
};
// ───── Global Variables ─────
LiquidCrystal lcd(9, 8, 7, 6, 5, 4);
State currentState = STATE_LOCKED;
char storedPassword[MAX_PASS_LEN + 1]; // Stored password
char inputBuffer[MAX_PASS_LEN + 1]; // Current input
char newPassBuffer[MAX_PASS_LEN + 1]; // New password buffer (for change)
int inputIndex = 0;
int failedAttempts = 0;
unsigned long lockoutTime = 0;
bool lockedOut = false;
// ───── Function Prototypes ─────
void loadPasswordFromEEPROM();
void savePasswordToEEPROM(const char* password);
void setLEDs(bool unlocked);
void displayLocked();
void displayUnlocked();
void displayInput();
void processDigit(char digit);
void submitPassword();
void clearInput();
void handleLockedState(int command);
void handleUnlockedState(int command);
void handleChangePassOld(int command);
void handleChangePassNew(int command);
void handleChangePassConfirm(int command);
int getDigitFromCommand(int command);
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(9600);
// Initialize pins
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);
// Initialize IR receiver
IrReceiver.begin(IR_RECEIVE_PIN);
// Initialize LCD
lcd.begin(16, 2);
// Load password from EEPROM (or set default)
loadPasswordFromEEPROM();
// Start in locked state
currentState = STATE_LOCKED;
setLEDs(false);
displayLocked();
Serial.println("IR Password Lock Ready");
Serial.print("Current password: ");
Serial.println(storedPassword);
}
// =====================================================
// MAIN LOOP
// =====================================================
void loop() {
// Handle lockout after too many failed attempts
if (lockedOut) {
if (millis() - lockoutTime >= 30000) { // 30 second lockout
lockedOut = false;
failedAttempts = 0;
lcd.clear();
lcd.print("Lockout expired");
delay(1000);
displayLocked();
} else {
// Blink red LED during lockout
digitalWrite(RED_LED_PIN, (millis() / 250) % 2);
return;
}
}
// Check for IR signal
if (IrReceiver.decode()) {
int command = IrReceiver.decodedIRData.command;
// Route to appropriate state handler
switch (currentState) {
case STATE_LOCKED:
handleLockedState(command);
break;
case STATE_UNLOCKED:
handleUnlockedState(command);
break;
case STATE_CHANGE_PASS_OLD:
handleChangePassOld(command);
break;
case STATE_CHANGE_PASS_NEW:
handleChangePassNew(command);
break;
case STATE_CHANGE_PASS_CONFIRM:
handleChangePassConfirm(command);
break;
}
IrReceiver.resume();
}
}
// =====================================================
// EEPROM FUNCTIONS
// =====================================================
void loadPasswordFromEEPROM() {
// Check if a password has been saved before
if (EEPROM.read(EEPROM_FLAG_ADDR) == EEPROM_FLAG_VALUE) {
int len = EEPROM.read(EEPROM_LEN_ADDR);
if (len > 0 && len <= MAX_PASS_LEN) {
for (int i = 0; i < len; i++) {
storedPassword[i] = EEPROM.read(EEPROM_PASS_ADDR + i);
}
storedPassword[len] = '\0';
Serial.println("Password loaded from EEPROM");
return;
}
}
// No valid password found — use default
strcpy(storedPassword, DEFAULT_PASS);
savePasswordToEEPROM(storedPassword);
Serial.println("Default password set and saved to EEPROM");
}
void savePasswordToEEPROM(const char* password) {
int len = strlen(password);
EEPROM.update(EEPROM_FLAG_ADDR, EEPROM_FLAG_VALUE);
EEPROM.update(EEPROM_LEN_ADDR, len);
for (int i = 0; i < len; i++) {
EEPROM.update(EEPROM_PASS_ADDR + i, password[i]);
}
Serial.println("Password saved to EEPROM");
}
// =====================================================
// LED CONTROL
// =====================================================
void setLEDs(bool unlocked) {
if (unlocked) {
digitalWrite(GREEN_LED_PIN, HIGH);
digitalWrite(RED_LED_PIN, LOW);
} else {
digitalWrite(GREEN_LED_PIN, LOW);
digitalWrite(RED_LED_PIN, HIGH);
}
}
// =====================================================
// LCD DISPLAY FUNCTIONS
// =====================================================
void displayLocked() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("== LOCKED ==");
lcd.setCursor(0, 1);
lcd.print("Enter Password:");
clearInput();
}
void displayUnlocked() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("** UNLOCKED **");
lcd.setCursor(0, 1);
lcd.print("MENU=ChgPw PWR=Lk");
}
void displayInput() {
// Show asterisks for entered digits on the second line
lcd.setCursor(0, 1);
lcd.print(" "); // Clear line
lcd.setCursor(0, 1);
for (int i = 0; i < inputIndex; i++) {
lcd.print('*');
}
// Show cursor blink effect
if (inputIndex < MAX_PASS_LEN) {
lcd.print('_');
}
}
void displayChangePrompt(const char* line1) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(line1);
clearInput();
displayInput();
}
// =====================================================
// INPUT HANDLING
// =====================================================
void clearInput() {
memset(inputBuffer, 0, sizeof(inputBuffer));
inputIndex = 0;
}
void processDigit(char digit) {
if (inputIndex < MAX_PASS_LEN) {
inputBuffer[inputIndex] = digit;
inputIndex++;
inputBuffer[inputIndex] = '\0';
displayInput();
// Brief feedback beep could go here
}
}
// =====================================================
// STATE: LOCKED
// =====================================================
void handleLockedState(int command) {
int digit = getDigitFromCommand(command);
if (digit >= 0) {
// Number pressed — add to input
processDigit('0' + digit);
}
else if (command == 168) { // PLAY = Submit
if (inputIndex == 0) return;
if (strcmp(inputBuffer, storedPassword) == 0) {
// Correct password!
currentState = STATE_UNLOCKED;
failedAttempts = 0;
setLEDs(true);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("ACCESS");
lcd.setCursor(2, 1);
lcd.print("GRANTED!");
delay(1500);
displayUnlocked();
} else {
// Wrong password
failedAttempts++;
setLEDs(false);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("WRONG!");
lcd.setCursor(0, 1);
lcd.print("Attempts: ");
lcd.print(failedAttempts);
delay(1500);
// Lockout after 3 failed attempts
if (failedAttempts >= 3) {
lockedOut = true;
lockoutTime = millis();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("!! LOCKOUT !!");
lcd.setCursor(0, 1);
lcd.print("Wait 30 seconds");
// Blink both LEDs rapidly for warning
for (int i = 0; i < 10; i++) {
digitalWrite(RED_LED_PIN, HIGH);
delay(100);
digitalWrite(RED_LED_PIN, LOW);
delay(100);
}
digitalWrite(RED_LED_PIN, HIGH);
} else {
displayLocked();
}
}
}
else if (command == 176) { // C = Clear
clearInput();
displayLocked();
}
}
// =====================================================
// STATE: UNLOCKED
// =====================================================
void handleUnlockedState(int command) {
if (command == 162) { // POWER = Lock
currentState = STATE_LOCKED;
setLEDs(false);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("LOCKING...");
delay(1000);
displayLocked();
}
else if (command == 226) { // MENU = Change password
currentState = STATE_CHANGE_PASS_OLD;
displayChangePrompt("Old Password:");
}
}
// =====================================================
// STATE: CHANGE PASSWORD — Enter Old Password
// =====================================================
void handleChangePassOld(int command) {
int digit = getDigitFromCommand(command);
if (digit >= 0) {
processDigit('0' + digit);
}
else if (command == 168) { // PLAY = Submit
if (strcmp(inputBuffer, storedPassword) == 0) {
// Old password correct → ask for new password
currentState = STATE_CHANGE_PASS_NEW;
displayChangePrompt("New Password:");
} else {
lcd.clear();
lcd.print("Wrong old pass!");
delay(1500);
currentState = STATE_UNLOCKED;
displayUnlocked();
}
}
else if (command == 194 || command == 176) { // BACK or C = Cancel
currentState = STATE_UNLOCKED;
displayUnlocked();
}
}
// =====================================================
// STATE: CHANGE PASSWORD — Enter New Password
// =====================================================
void handleChangePassNew(int command) {
int digit = getDigitFromCommand(command);
if (digit >= 0) {
processDigit('0' + digit);
}
else if (command == 168) { // PLAY = Submit
if (inputIndex < 1) {
lcd.clear();
lcd.print("Min 1 digit!");
delay(1000);
displayChangePrompt("New Password:");
return;
}
// Store new password temporarily
strcpy(newPassBuffer, inputBuffer);
currentState = STATE_CHANGE_PASS_CONFIRM;
displayChangePrompt("Confirm New Pw:");
}
else if (command == 194 || command == 176) { // BACK or C = Cancel
currentState = STATE_UNLOCKED;
displayUnlocked();
}
}
// =====================================================
// STATE: CHANGE PASSWORD — Confirm New Password
// =====================================================
void handleChangePassConfirm(int command) {
int digit = getDigitFromCommand(command);
if (digit >= 0) {
processDigit('0' + digit);
}
else if (command == 168) { // PLAY = Submit
if (strcmp(inputBuffer, newPassBuffer) == 0) {
// Passwords match — save!
strcpy(storedPassword, newPassBuffer);
savePasswordToEEPROM(storedPassword);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Password Changed");
lcd.setCursor(0, 1);
lcd.print("Saved to EEPROM!");
// Success LED blink
for (int i = 0; i < 5; i++) {
digitalWrite(GREEN_LED_PIN, LOW);
delay(150);
digitalWrite(GREEN_LED_PIN, HIGH);
delay(150);
}
delay(1000);
currentState = STATE_UNLOCKED;
setLEDs(true);
displayUnlocked();
} else {
lcd.clear();
lcd.print("Mismatch! Retry");
delay(1500);
currentState = STATE_CHANGE_PASS_NEW;
displayChangePrompt("New Password:");
}
}
else if (command == 194 || command == 176) { // BACK or C = Cancel
currentState = STATE_UNLOCKED;
displayUnlocked();
}
}
// =====================================================
// IR COMMAND → DIGIT MAPPING
// =====================================================
int getDigitFromCommand(int command) {
switch (command) {
case 104: return 0;
case 48: return 1;
case 24: return 2;
case 122: return 3;
case 16: return 4;
case 56: return 5;
case 90: return 6;
case 66: return 7;
case 74: return 8;
case 82: return 9;
default: return -1;
}
}