#include <WiFi.h>
#include <Arduino_FreeRTOS.h>
#include <Keypad.h>
#include <queue.h>
#include <Servo.h>
#include <EEPROM.h>
#include <stdint.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <LiquidCrystal_I2C.h>
//************************************************************************************************************************************************
struct keyValue
{
char key;
};
//************************************************************************************************************************************************
// handles:
QueueHandle_t my_queue;
//************************************************************************************************************************************************
// prototypes:
void readKey(void *pvParameters);
void printKey(void *pvParameters);
//************************************************************************************************************************************************
// keypad configuration:
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}};
uint8_t colPins[COLS] = {5, 4, 3, 2}; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = {9, 8, 7, 6}; // Pins connected to R1, R2, R3, R4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
//************************************************************************************************************************************************
// declare the LCD:
LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20 columns and 4 rows
//************************************************************************************************************************************************
// CONFIGURATION THE SYSTEM:
char code[] = {'1', '2', '3', '4'}; // The default code, you can change it or make it an 'n' digits one
char check1[sizeof(code)]; // Where the new key is stored
char check2[sizeof(code)]; // Where the new key is stored again so it's compared to the previous one
short a = 0, i = 0, s = 0, j = 0; // Variables used later
int buzzerPin = 13;
int ledPin = 12;
Servo lockServo;
char ssid[] = "WOWKI-Guest";
char pass[] = "";
//************************************************************************************************************************************************
void setup()
{
WiFi.begin(ssid, pass);
// create the tasks:
xTaskCreate(readKey, "read from keypad", 128, NULL, 4, NULL);
xTaskCreate(printKey, "print from keypad", 128, NULL, 4, NULL);
// create queue:
my_queue = xQueueCreate(1, sizeof(struct keyValue));
// Initialize the LCD
lcd.init();
lcd.backlight();
// Set initial LCD content
lcd.setCursor(4, 1);
lcd.print("UOB LOCKERS");
_delay_ms(1000);
// vTaskDelay(1000); // => error!
lcd.clear(); // clear the lcd
// the user interface messages
lcd.print("Press * to Unlock.");
lcd.setCursor(0, 1);
lcd.print("Press # to Change");
lcd.setCursor(0, 2);
lcd.print("The Code. ");
lcd.setCursor(0, 3);
lcd.print("The Locker is Locked");
// setup the locker (servo)
lockServo.attach(A0);
lockServo.write(0);
// setup the led and buzzer
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
// Disable unused features
power_adc_disable();
power_spi_disable();
power_twi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
}
//************************************************************************************************************************************************
// tasks configuration:
void readKey(void *pvParameters)
{
struct keyValue x;
for (;;)
{
x.key = keypad.getKey();
// send to the queue:
xQueueSend(my_queue, &x, portMAX_DELAY);
}
}
void printKey(void *pvParameters)
{
struct keyValue x;
// TickType_t xLastWakeTime = xTaskGetTickCount();
for (;;)
{
// if receved from queue correctly:
if (xQueueReceive(my_queue, &x, portMAX_DELAY) == pdPASS)
{
if (x.key != NO_KEY)
{
// Serial.println(x.key);
switch (x.key)
{
case '*':
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter code:");
lcd.setCursor(0, 1);
lcd.print("(A to ENTER)");
ReadCode(); // Getting code function
// Serial.println(a);
// Serial.println(sizeof(code));
if (a != sizeof(code))
{
lcd.clear();
lcd.print("Wrong code"); // Message to print when the code is wrong
Alarm();
lcd.setCursor(0, 1);
lcd.print("Press * to Unlock");
}
else
{
OpenDoor(); // Open lock function if code is correct
lcd.clear();
lcd.print("Welcome:");
lcd.setCursor(0, 1);
lcd.print("the door is Open");
lcd.setCursor(0, 2);
lcd.print("Press B to close");
lcd.setCursor(0, 3);
lcd.print("the door");
}
break;
case '#':
ChangeCode();
lcd.clear();
lcd.print("Enter # to Change");
lcd.setCursor(0, 1);
lcd.print("Press * to Unlock");
break;
case 'B':
lcd.clear();
lockServo.write(0);
Closed();
lcd.print("Locked!");
lcd.setCursor(0, 1);
lcd.print("Press * to Unlock!");
break;
default:
// ...
break;
}
// // delay between lcd updates
// vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
}
}
}
}
//************************************************************************************************************************************************
void loop()
{
// putting the microcontroller into a low-power state when it is not actively processing tasks:
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set the sleep (mode) to power-down mode [power-down mode == consumes the least amount of power.]
sleep_enable(); // Enable sleep mode
// ensure the code is compatible with the specific microcontroller being used
#if defined(BODS) && defined(BODSE)
sleep_bod_disable(); //if defined == disables the Brown-out Detector during sleep
#endif
//enter and exit a critical section to prevent race conditions or conflicts that could occur when entering sleep mode
portENTER_CRITICAL();
portEXIT_CRITICAL();
sleep_cpu(); // Enter sleep mode /*allowing the microcontroller to consume minimal power while waiting for an interrupt to wake it up*/
sleep_reset(); // Ugh. Yawn... I've been woken up. Better disable sleep mode and reset the sleep mode settings.
sleep_disable(); // Wake up from sleep(disable sleep mode), continue program execution
}
//************************************************************************************************************************************************
// FUNCTIONS HERE:
void ReadCode()
{
struct keyValue x;
char keypressed = x.key;
i = 0;
a = 0;
j = 0;
while (keypressed != 'A')
{ // The user presses A to confirm the code; otherwise, they can keep typing
keypressed = keypad.getKey();
if (keypressed != NO_KEY && keypressed != 'A')
{ // If the char typed isn't A and neither "nothing"
lcd.setCursor(j, 2); // This writes "*" on the LCD whenever a key is pressed; its position is controlled by j
lcd.print("*");
j++;
if (keypressed == code[i] && i < sizeof(code))
{ // If the char typed is correct, a and i increment to verify the next character
a++;
i++;
}
else
{
a--; // If the character typed is wrong, a decrements and cannot equal the size of code[]
}
}
}
keypressed = NO_KEY;
}
void ChangeCode()
{ // Change code sequence
lcd.clear();
lcd.print("Changing the code...");
vTaskDelay(pdMS_TO_TICKS(1000)); // _delay_ms(1000);
lcd.clear();
lcd.print("Enter the old code:");
lcd.setCursor(0, 1);
lcd.print("Press A to ENTER");
vTaskDelay(pdMS_TO_TICKS(1000)); // Add a 1-second delay here
ReadCode();
if (a == sizeof(code))
{ // Again verifying the value of a
lcd.clear();
lcd.print("Changing the code");
GetNewCode1(); // Get the new code
GetNewCode2(); // Get the new code again to confirm it
s = 0;
for (i = 0; i < sizeof(code); i++)
{ // Compare codes in array 1 and array 2 from the two previous functions
if (check1[i] == check2[i])
s++; // Increment s whenever codes are matching
}
if (s == sizeof(code))
{ // Correct size is always the size of the array
for (i = 0; i < sizeof(code); i++)
{
code[i] = check2[i]; // The code array now receives the new code
EEPROM.put(i, code[i]); // And stores it in the EEPROM
}
lcd.clear();
lcd.print("Code Changed");
vTaskDelay(pdMS_TO_TICKS(2000)); // _delay_ms(2000);
}
else
{ // In case the new codes aren't matching
lcd.clear();
lcd.print("Codes are not");
lcd.setCursor(0, 1);
lcd.print("matching !!");
vTaskDelay(pdMS_TO_TICKS(2000)); // _delay_ms(2000);
}
}
else
{ // In case the old code is wrong, you can't change it
lcd.clear();
lcd.print("Wrong!");
wrong();
vTaskDelay(pdMS_TO_TICKS(500)); // _delay_ms(2000);
}
}
void GetNewCode1()
{
struct keyValue x;
char keypressed = x.key;
i = 0;
j = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter new code:"); // Tell the user to enter the new code and press A
lcd.setCursor(0, 1);
lcd.print("press A to ENTER");
vTaskDelay(pdMS_TO_TICKS(1000)); // Add a 1-second delay here
// _delay_ms(1000);
// lcd.clear();
// lcd.setCursor(0, 1);
// lcd.print("and press A"); // "Press A" keeps showing while the top row prints ***
while (keypressed != 'A')
{ // A confirms and exits the loop
keypressed = keypad.getKey();
if (keypressed != NO_KEY && keypressed != 'A')
{
lcd.setCursor(j, 2); // new code style
lcd.print("*"); // On the new code, you can show * as I did or change it to keypressed to show the keys
check1[i] = keypressed; // Store characters in the array
i++;
j++;
}
}
keypressed = NO_KEY;
}
void GetNewCode2()
{
// This is exactly like the GetNewCode1 function, but this time the code is stored in another array
// confirmation
struct keyValue x;
char keypressed = x.key;
i = 0;
j = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Confirm the code");
lcd.setCursor(0, 1);
lcd.print("and press A");
// lcd.clear();
// lcd.setCursor(0, 1);
// lcd.print("and press A");
while (keypressed != 'A')
{
keypressed = keypad.getKey();
if (keypressed != NO_KEY && keypressed != 'A')
{
lcd.setCursor(j, 2);
lcd.print("*");
check2[i] = keypressed;
i++;
j++;
}
}
keypressed = NO_KEY;
}
void OpenDoor()
{
// Lock opening function: open the door
lcd.clear();
lockServo.write(180);
}
void Alarm()
{
for (int i = 0; i < 5; i++)
{
PORTB |= 0b00110000; // digitalWrite(alarmPin, HIGH);
tone(buzzerPin, 900);
vTaskDelay(pdMS_TO_TICKS(250)); // _delay_ms(250);
PORTB &= 0b11001111; // digitalWrite(alarmPin, LOW);
noTone(buzzerPin);
vTaskDelay(pdMS_TO_TICKS(250)); // _delay_ms(250);
}
}
void Closed()
{
PORTB |= 0b00110000; // digitalWrite(alarmPin, HIGH);
tone(buzzerPin, 900);
vTaskDelay(pdMS_TO_TICKS(250)); // _delay_ms(250);
PORTB &= 0b11001111; // digitalWrite(alarmPin, LOW);
noTone(buzzerPin);
}
void wrong()
{
for (int i = 0; i < 3; i++)
{
PORTB |= 0b00110000;
tone(buzzerPin, 900);
vTaskDelay(pdMS_TO_TICKS(250));
PORTB &= 0b11001111;
noTone(buzzerPin);
vTaskDelay(pdMS_TO_TICKS(250));
}
}
//************************************************************************************************************************************************Loading
esp-01
esp-01