//============================included libraries=======================
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <EEPROM.h>
//=====================================================================
//============================LCD messages=============================
const char txtEmpty[] PROGMEM = "";
const char txt1[] PROGMEM = "AVAILABLE";
const char txt2[] PROGMEM = "PRESS";
const char txt3[] PROGMEM = "KEY 1-9 TO START";
const char txt4[] PROGMEM = "WELCOME";
const char txt5[] PROGMEM = "WRONG PIN";
const char txt6[] PROGMEM = "TRY AGAIN";
const char txt7[] PROGMEM = "Enter your PIN";
const char txt8[] PROGMEM = "Use keypad to";
const char txt9[] PROGMEM = "Re-Enter your PIN";
const char txt10[] PROGMEM = "Create 4 digit PIN";
const char txt11[] PROGMEM = "PLEASE";
const char txt12[] PROGMEM = "THANK YOU";
const char txt13[] PROGMEM = "YOU CAN";
const char txt14[] PROGMEM = "OPEN THE DOOR";
const char txt15[] PROGMEM = "CLOSE THE DOOR";
const char txt16[] PROGMEM = "NOT AVAILABLE";
const char txt17[] PROGMEM = "ENTER YOUR PIN";
const char txt18[] PROGMEM = "TO OPEN";
const char txt19[] PROGMEM = "THE DOOR";
//=====================================================================
//=============================debug on and off=========================
#define DEBUG 1 // if this value is "1" debug messages are on
#if (DEBUG == 1)
#define debugMsg(x) Serial.println(__func__ + String("(): ") + (String(x)))
#else
#define debugMsg(x)
#endif
//=====================================================================
//========================declare EEPROM addresses========================
const byte codeAddress = 0x00; // EEPROM address for code variable (5 bytes)
//======================================================================
//========================declare MENU constants========================
const char BTN_INCREASE = '3';
const char BTN_UP = '1';
const char BTN_DOWN = '4';
const char BTN_DECREASE = '6';
const char BTN_RESET = '#';
const char BTN_DEFAULT = '*';
const char BTN_ENTER_MENU = '*';
//======================================================================
//========================declare LCD constants========================
const byte LCDAddress = 0x27;
const int LCDColumns = 20;
const int LCDRows = 4;
//======================================================================
//=================create LCD object===================================
LiquidCrystal_I2C lcd(LCDAddress, LCDColumns, LCDRows);
//=====================================================================
//======================declare keypad constants=======================
const byte numRows = 4; // variable to set keypad rows
const byte numCols = 3; // variable to set keypad columns
byte rowPins[numRows] = {9, 4, 5, 7}; // variable to set keypad row pins
byte colPins[numCols] = {8, 10, 6}; // variable to set keypad column pins
char keymap[numRows][numCols] = // variable to set keypad keymap
{
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}};
//====================================================================
//=================create Keypad object===================================
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
//=====================================================================
//==================declare project constants ===========================
const byte lockerStatusLight = 12; // available light PIN
const byte doorLock = 11; // door lock PIN
const byte doorLockLed = 3; // door lock LED for demo only
//======================================================================
//==================declare project variables===========================
unsigned long startTime;
String code; // Variable that holds the security PIN
unsigned int totalCounter; // counter to keep the total money inserted
unsigned int subTotalCounter; // counter to keep the sub-total money inserted
//=====================================================================
enum DOOROPERATION
{
NOFINAL,
FINAL,
INITIAL
};
enum LCD_ALIGN
{
LEFT,
RIGHT,
CENTER
};
enum LCD_CLEAR
{
CLEAR,
NO_CLEAR
};
//==================declare functions==================================
void initializePins();
void lcdInitialize();
void initializeVariables();
void lcd_print_2lines(const char *_txt1, const char *_txt2, const char *_txt3, const char *_txt4, bool _lcd_clear, byte _align);
void step1();
void step2();
void step3();
void step6();
void step7();
void setAvailableLight(bool option);
String pinEnter(int _x);
void updatePinEntered(String aPin);
bool actionWaitingTime(unsigned long startTime, unsigned int secondsWaiting);
void writeStringToEEPROM(int addrOffset, const String &strToWrite);
void doorLockOperation(byte _phase);
//=====================================================================
//=================================set up function ====================
void setup()
{
Serial.begin(9600);
delay(100);
initializePins(); // function for initialize the pins
initializeVariables(); // function for initialize the variables
lcdInitialize(); // function for initialize the lcd
step1();
}
//=====================================================================
//=================================loop function ======================
void loop() {}
//=====================================================================
//===================function for pins inialization========================
void initializePins()
{
pinMode(lockerStatusLight, OUTPUT);
pinMode(doorLock, OUTPUT);
pinMode(doorLockLed, OUTPUT);
}
//========================================================================
//===================function for variables inialization===================
void initializeVariables()
{
}
//===========================================================================
//===================function for LCD inialization========================
void lcdInitialize()
{
lcd.init();
lcd.backlight();
}
//============================================================================
//==========================step1===========================
void step1()
{
setAvailableLight(true);
doorLockOperation(DOOROPERATION::INITIAL);
// debugMsg("start");
lcd_print_2lines(txt1, txt2, txt3, txt4, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
step2();
}
//==============================================================================
//==========================step2==============================
void step2()
{
// debugMsg("start");
char tempChar = NULL;
// while (isButtonHigh(startButton)){}
while (!(tempChar = myKeypad.getKey()))
{
}
if (tempChar == BTN_ENTER_MENU)
{
lcd.setCursor(19, 3);
lcd.print(F("."));
// debugMsg("* pressed");
delay(100);
}
else
{
setAvailableLight(false); // available light turned OFF
step3();
}
}
//==============================================================================
//==========================step3===============================================
/* this step ask for PIN insertion and request a verification.
if verification fail 3 times then return to start */
void step3()
{
// debugMsg"step 3");
String tempCode = ""; // Variable keep the 1st entry code
String tempCode1 = ""; // Variable keep the 2nd entry code
byte verificationTimes = 3; // PIN verification tries
byte incompleteCodeTimes = 3; // PIN verification tries
tempCode = pinEnter(3); // store the 1st pin entered in a variable
// debugMsg("tempCode:" + String(tempCode));
if (tempCode != "")
{
// debugMsg("tempCode is valid");
while ((incompleteCodeTimes > 0) && (verificationTimes > 0))
{
tempCode1 = pinEnter(2); // store the 2nd pin entered in a variable
// debugMsg("tempCode1:" + String(tempCode1));
if (tempCode1 != "") // if tempCode1 is valid
{
if (!tempCode.equals(tempCode1)) // if both codes are not matching
{
verificationTimes--;
lcd_print_2lines(txt5, txtEmpty, txt11, txt6, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
delay(2000);
}
else
{
// debugMsg("both codes are equal");
break;
}
}
else if (tempCode1 == "") // if tempCode1 is not valid
{
incompleteCodeTimes--;
}
}
if (incompleteCodeTimes == 0 || verificationTimes == 0)
{
// debugMsg("incompleteCodeTimes exhausted:" + String(incompleteCodeTimes));
// debugMsg("going to step1");
step1();
}
else
{
// debugMsg("update the code");
updatePinEntered(tempCode);
// debugMsg("end");
step6();
}
}
else if (tempCode == "") // if tempCode is not valid
{
// debugMsg("tempCode is NOT valid");
// debugMsg("going to step1");
step1();
}
}
//==============================================================================
//========================= function step6 RELEASE DOOR =========================
void step6()
{
doorLockOperation(DOOROPERATION::NOFINAL);
step7();
}
//==============================================================================
//========================= function step6 RELEASE DOOR =========================
void step7()
{
char tempChar = NULL;
String tempCode = "";
lcd_print_2lines(txt16, txt17, txt18, txt19, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
// while (isButtonHigh(startButton)){}
while (!(tempChar = myKeypad.getKey()))
{
}
tempCode = pinEnter(1);
while (1)
{
if (tempCode.equals(code))
{
debugMsg("OK");
doorLockOperation(DOOROPERATION::FINAL);
break;
}
else if (!tempCode.equals(code))
{
debugMsg("NOT OK");
step7();
}
}
debugMsg("finished");
step1();
}
//==============================================================================
void lcd_print_2lines(const char *_txt1, const char *_txt2, const char *_txt3, const char *_txt4, bool _lcd_clear, byte _align)
{
const char *txt;
char myChar;
if (_lcd_clear == LCD_CLEAR::CLEAR)
{
lcd.clear();
}
for (byte i = 0; i < 4; i++)
{
if (i == 0)
{
txt = _txt1;
}
if (i == 1)
{
txt = _txt2;
}
else if (i == 2)
{
txt = _txt3;
}
else if (i == 3)
{
txt = _txt4;
}
if (_align == LCD_ALIGN::LEFT)
{
lcd.setCursor(0, i);
}
else if (_align == LCD_ALIGN::CENTER)
{
lcd.setCursor((LCDColumns - strlen_P(txt)) / 2, i);
}
else if (_align == LCD_ALIGN::RIGHT)
{
lcd.setCursor(LCDColumns - strlen_P(txt), i);
}
for (byte k = 0; k < strlen_P(txt); k++)
{
myChar = pgm_read_byte_near(txt + k);
lcd.print(myChar);
Serial.print(myChar);
}
Serial.println();
}
}
//===================function set available indicator=========================
void setAvailableLight(bool option)
{
if (option)
{
digitalWrite(lockerStatusLight, HIGH);
// debug_message("available light is ON");
}
else
{
digitalWrite(lockerStatusLight, LOW);
// debug_message("available light is OFF");
}
}
//==============================================================================
//======================= function for PIN entering ==========================
String pinEnter(int _x)
{
// debugMsg("start");
unsigned long localStartTime = millis();
byte pinLength = 4; // declare how many are the pin digits
String temp = ""; // variable to keep the pin number
char keyPressed; // variable to keep the key that has been pressed
if (_x == 1)
{
lcd_print_2lines(txt11, txtEmpty, txt7, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
}
else if (_x == 2)
{
lcd_print_2lines(txt4, txt8, txt9, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
}
else
{
lcd_print_2lines(txt4, txt8, txt10, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
}
while ((temp.length() < pinLength) && (!actionWaitingTime(localStartTime, 10)))
{
keyPressed = myKeypad.getKey();
if (keyPressed)
{
temp += keyPressed;
debugMsg(keyPressed);
if (temp.length() <= pinLength) // print an asterisk on the display for each digit
{
lcd.setCursor(8 + temp.length(), 3);
lcd.print("*");
localStartTime = millis();
}
}
}
delay(500);
if (temp.length() < pinLength)
{
temp = "";
}
localStartTime = 0;
// debugMsg("end");
return temp;
}
//==============================================================================
//===================function that update the code(PIN)=========================
void updatePinEntered(String aPin)
{
code = aPin;
writeStringToEEPROM(codeAddress, code);
delay(200);
// debugMsg("pin:" + String(code) + " has been stored to EEPROM");
delay(50);
// debugMsg("code:" + readStringFromEEPROM(codeAddress) + " has been retrieved from EEPROM.");
delay(50);
}
//==============================================================================
//================function return true if secondsWaiting passed================
bool actionWaitingTime(unsigned long startTime, unsigned int secondsWaiting)
{
if ((millis() - startTime) > secondsWaiting * 1000)
{
return true;
}
else
{
return false;
}
}
//==============================================================================
//===================function that stores string to EEPROM=======================
void writeStringToEEPROM(int addrOffset, const String &strToWrite)
{
byte len = strToWrite.length();
EEPROM.update(addrOffset, len);
for (int i = 0; i < len; i++)
{
EEPROM.update(addrOffset + 1 + i, strToWrite[i]);
}
}
//==============================================================================
//=================== function for door lock operation ===================
void doorLockOperation(byte _phase)
{
if (_phase == DOOROPERATION::NOFINAL)
{
lcd_print_2lines(txt12, txt13, txt14, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
digitalWrite(doorLock, HIGH);
digitalWrite(doorLockLed, LOW);
delay(5000);
digitalWrite(doorLock, LOW);
digitalWrite(doorLockLed, HIGH);
}
else if (_phase == DOOROPERATION::FINAL)
{
lcd_print_2lines(txtEmpty, txt14, txt12, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
digitalWrite(doorLock, HIGH);
digitalWrite(doorLockLed, LOW);
delay(5000);
digitalWrite(doorLock, LOW);
digitalWrite(doorLockLed, HIGH);
lcd_print_2lines(txtEmpty, txt11, txt15, txtEmpty, LCD_CLEAR::CLEAR, LCD_ALIGN::CENTER);
delay(5000);
}
else if (_phase == DOOROPERATION::INITIAL)
{
digitalWrite(doorLock, LOW);
digitalWrite(doorLockLed, HIGH);
}
}
//===================================================================================