//EET 411
//Project Code V4.0 code
//Lingle Jeremy
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
#include <RTClib.h>
////////////////////////////////////////////////
//#include <SPI.h>
//#include <MFRC522.h>
///////////////////////////////////////////
#define RST_PIN 5 // Configurable, see typical pin layout above
#define SS_PIN 53 // Configurable, see typical pin layout above
// Define door pins for all doors (15 lockers)
#define door1 22
#define door2 23
#define door3 24
#define door4 25
#define door5 26
#define door6 27
#define door7 28
#define door8 29
#define door9 30
#define door10 31
#define door11 32
#define door12 33
#define door13 34
#define door14 35
#define door15 36
#define SERVO 45
// Create an array to store the pin numbers for each door
const int doorPins[] = {door1, door2, door3, door4, door5, door6, door7, door8,
door9, door10, door11, door12, door13, door14, door15
};
// I2C address 0x27, 16 column and 2 rows
LiquidCrystal_I2C lcd(0x27, 16, 2);
// RTC
RTC_DS1307 rtc;
//Alert buzzer active
int buzzer = 41;
unsigned char j;
// Alert LED's
int happyLights = 50;
int angryLights = 52;
// Define constants for keypad configuration
const byte ROWS = 4;
const byte COLS = 4;
// Define the characters associated with keypad buttons
char hexaKeys[ROWS][COLS] =
{
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// Define pins for connecting the rows and columns of the keypad
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
// Initialize a Keypad instance with pin mappings
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
// Initialize a Servo instance for the lock mechanism
Servo lockServo;
// Array to store selected lockers
char locker[3];
int p = 0; // Locker choice counter
bool confirming = false; // Flag to confirm locker selection
bool codeEntered = false; // Flag to indicate whether the code was entered correctly
int lockerNum = -1; // Currently selected locker number
int tries = 2; // Number of remaining attempts to enter the combination
// Array to track locker occupancy (whether a locker is already chosen)
bool lockerChosen[15] = {false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false
};
// Define program states using an enum
enum ProgramState
{
STATE_LOCKER_SELECTION, // State for choosing a locker
STATE_ENTER_COMBO, // State for entering the combination
STATE_LOCKED_OUT // State when locked out
};
ProgramState state = STATE_LOCKER_SELECTION;
/////////////////////////////////////////
//MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
//MFRC522::MIFARE_Key key;
///////////////////////////////////////
void setup()
{
// Start serial communication for debugging
Serial.begin(9600);
// initialize the lcd
lcd.init();
lcd.backlight();
// SETUP RTC MODULE
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1);
}
// automatically sets the RTC to the date & time on PC this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
//initialize the buzzer pin as an output
pinMode(buzzer, OUTPUT);
//initialize the LED pins as an output
pinMode(angryLights, OUTPUT);
pinMode(happyLights, OUTPUT);
// Set the pinMode for door control pins
for (int i = 0; i < 15; i++)
{
pinMode(doorPins[i], OUTPUT);
}
// Attach the Servo motor to the specified pin and set its initial position
lockServo.attach(SERVO);
lockServo.write(0);
// Display a welcome message on the LCD
lcd.setCursor(4, 0);
lcd.print("Welcome!");
delay(1500);
DateTime now = rtc.now();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Current Date");
lcd.setCursor(0, 1);
lcd.print(now.year(), DEC);
lcd.setCursor(4, 1);
lcd.print('/');
lcd.setCursor(5, 1);
lcd.print(now.month(), DEC);
lcd.setCursor(6, 1);
lcd.print('/');
lcd.setCursor(7, 1);
lcd.print(now.day(), DEC);
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Current Z Time");
lcd.setCursor(0, 1);
lcd.print(now.hour(), DEC);
lcd.setCursor(2, 1);
lcd.print(':');
lcd.setCursor(3, 1);
lcd.print(now.minute(), DEC);
lcd.setCursor(5, 1);
lcd.print(':');
lcd.setCursor(6, 1);
lcd.println(now.second(), DEC);
delay(2000);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Choose A Locker");
delay(2000); // Display the welcome message for 2.5 seconds
lcd.clear(); // Clear the LCD screen
///////////////////////////////////////
/*
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
Serial.println(F("Warning: this example overwrites the UID of your UID changeable card, use with care!"));
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
*/
////////////////////////////////////////////////////////
}
void loop()
{
switch (state)
{
case STATE_LOCKER_SELECTION:
lockServo.write(0); // Reset the Servo to the initial position
lockerNum = chooseLocker(); // Let the user choose a locker
if (lockerNum != -1)
{
state = STATE_ENTER_COMBO; // Transition to combo entry state
}
break;
case STATE_ENTER_COMBO:
if (codeEntered)
{
state = STATE_LOCKER_SELECTION; // Go back to locker selection state
codeEntered = false; // Reset the codeEntered flag
}
else
{
enterCombo(lockerNum); // Let the user enter the combination
}
break;
case STATE_LOCKED_OUT:
seeAttendant(); // Notify the user to see an attendant
state = STATE_LOCKER_SELECTION; // Go back to locker selection state
break;
}
}
// Function for choosing a locker
char chooseLocker()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Locker Num(1-15)");
lcd.setCursor(0, 1);
lcd.print("# to Confirm:");
while (!confirming)
{
char lockerChoice = customKeypad.waitForKey();
if (lockerChoice && p <= 2)
{
locker[p] = lockerChoice;
lcd.setCursor(p + 14, 1);
lcd.print(lockerChoice);
p++;
delay(250); // Delay for visual feedback (can be adjusted)
if (lockerChoice == '#')
{
int lockerInt = atoi(locker);
confirming = true;
if (lockerInt >= 1 && lockerInt <= 15)
{
lockerInt = lockerInt - 1;
if (!lockerChosen[lockerInt])
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("**Locker Number**");
lcd.setCursor(0, 1);
lcd.print("----->");
lcd.setCursor(10, 1);
lcd.print("<-----");
lcd.setCursor(7, 1);
lcd.print(lockerInt + 1);
lockerChosen[lockerInt] = true; // Mark locker as chosen
digitalWrite(doorPins[lockerInt], HIGH); // Activate the locker door
delay(2000);
p = 0;
confirming = false;
return lockerInt;
}
else
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*Locker Already*");
lcd.setCursor(0, 1);
lcd.print("****Occupied****");
delay(1000);
confirming = false;
p = 0;
state = STATE_LOCKER_SELECTION;
return -1;
}
}
else
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*Locker Choice*");
lcd.setCursor(0, 1);
lcd.print("****Invalid!****");
delay(1000);
confirming = false;
lockerNum = -1;
p = 0;
state = STATE_LOCKER_SELECTION;
return -1;
}
}
delay(200);
}
}
return -1;
}
// Function for entering the combination
void enterCombo(int lockerNum)
{
// Define the combinations for each door
char lockCode[15][6] =
{
{'1', '3', '4', '5', '#'}, // Combination for Door 1
{'2', '5', '6', '8', '#'}, // Combination for Door 2
{'9', '0', '2', '1', '#'}, // Combination for Door 3
{'4', '7', '3', '6', '#'}, // Combination for Door 4
{'8', '2', '1', '0', '#'}, // Combination for Door 5
{'3', '5', '7', '2', '#'}, // Combination for Door 6
{'6', '4', '9', '8', '#'}, // Combination for Door 7
{'2', '1', '6', '4', '#'}, // Combination for Door 8
{'5', '3', '7', '0', '#'}, // Combination for Door 9
{'1', '2', '3', '4', '#'}, // Combination for Door 10
{'5', '6', '7', '8', '#'}, // Combination for Door 11
{'9', '0', '1', '2', '#'}, // Combination for Door 12
{'3', '4', '5', '6', '#'}, // Combination for Door 13
{'7', '8', '9', '0', '#'}, // Combination for Door 14
{'1', '9', '4', '7', '#'} // Combination for Door 15
};
int i = 0;
char userCode[6] = {}; // Initialize the userCode array with null characters
lcd.clear();
while (tries >= 0)
{
lcd.setCursor(0, 0);
lcd.print("**Combination***");
lcd.setCursor(0, 1);
lcd.print("# Checks: ");
char codeCheck = customKeypad.waitForKey();
if (codeCheck)
{
if (i < 5)
{
userCode[i] = codeCheck;
lcd.setCursor(i + 10, 1);
lcd.print('*');
Serial.print(codeCheck); // For debugging and verification
i++;
}
if (i == 5)
{
Serial.print('\n');
bool correct = true;
// Check if the entered combination matches the predefined one
for (int k = 0; k < 5; k++)
{
if (userCode[k] != lockCode[lockerNum][k])
{
correct = false;
break;
}
}
if (correct)
{
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Locker Combo");
///////////////////////////////
for (j = 0; j < 30; j++)
{
digitalWrite(happyLights, HIGH);
digitalWrite(buzzer, HIGH);
delay(3);//wait for 1ms
digitalWrite(buzzer, LOW);
delay(3);//wait for 1ms
}
for (j = 0; j < 40; j++)
{
digitalWrite(buzzer, HIGH);
delay(2);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(2);//wait for 2ms
}
for (j = 0; j < 50; j++)
{
digitalWrite(buzzer, HIGH);
delay(1);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(1);//wait for 2ms
digitalWrite(happyLights, LOW);
}
//////////////////////////////
lcd.setCursor(3, 1);
lcd.print("Correct!");
lockServo.write(135); // Activate the Servo lock
p = 0;
codeEntered = true; // Set the codeEntered flag
delay(1500);
break;
}
else
{
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Locker Combo");
for (j = 0; j < 50; j++)
{
digitalWrite(angryLights, HIGH);
digitalWrite(buzzer, HIGH);
delay(2);//wait for 1ms
digitalWrite(buzzer, LOW);
delay(2);//wait for 1ms
}
for (j = 0; j < 40; j++)
{
digitalWrite(buzzer, HIGH);
delay(3);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(3);//wait for 2ms
}
for (j = 0; j < 30; j++)
{
digitalWrite(buzzer, HIGH);
delay(4);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(4);//wait for 2ms
digitalWrite(angryLights, LOW);
}
lcd.setCursor(3, 1);
lcd.print("Invalid!");
delay(1000);
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("You have ");
lcd.print(tries);
lcd.setCursor(3, 1);
lcd.print("Remaining");
delay(1000);
lcd.clear();
tries--;
if (tries < 0)
{
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("LOCKED OUT");
lcd.setCursor(1, 1);
lcd.print("SEE ATTENDANT");
delay(1000);
seeAttendant();
}
i = 0;
}
}
}
}
}
// Function for notifying the user to see an attendant
void seeAttendant()
{
lcd.setCursor(0, 0);
lcd.print("***LOCKED OUT***");
lcd.setCursor(0, 1);
lcd.print("***LOCKED OUT***");
for (j = 0; j < 80; j++)
{
digitalWrite(buzzer, HIGH);
delay(1);//wait for 1ms
digitalWrite(buzzer, LOW);
delay(1);//wait for 1ms
}
for (j = 0; j < 100; j++)
{
digitalWrite(buzzer, HIGH);
delay(2);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(2);//wait for 2ms
}
for (j = 0; j < 80; j++)
{
digitalWrite(buzzer, HIGH);
delay(1);//wait for 1ms
digitalWrite(buzzer, LOW);
delay(1);//wait for 1ms
}
for (j = 0; j < 100; j++)
{
digitalWrite(buzzer, HIGH);
delay(2);//wait for 2ms
digitalWrite(buzzer, LOW);
delay(2);//wait for 2ms
}
while (1)
{
////////////////////////////////////////////
//char seeLockerBoss = customKeypad.waitForKey();
//if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
// return;
//}
/*
// Now a card is selected. The UID and SAK is in mfrc522.uid.
String UID = "";
byte letter;
// Dump UID
Serial.print(F("Card UID:"));
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
UID.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
UID.concat(String(mfrc522.uid.uidByte[i], HEX));
}
Serial.println();
UID.toUpperCase();
if (UID.substring(1) == "13 08 4A EC" || UID.substring(1) == "F3 5A 79 FA" || seeLockerBoss == '*')
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*****RESET*****");
lcd.setCursor(0, 1);
lcd.print("*****RESET*****");
delay(1000);
tries = 2;
lcd.clear();
return;
}
}
*/
char seeLockerBoss = customKeypad.waitForKey();
if (seeLockerBoss == '*')
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*****RESET*****");
lcd.setCursor(0, 1);
lcd.print("*****RESET*****");
delay(1000);
tries = 2;
lcd.clear();
return;
}
}
}