#include <Wire.h>
#include <RTClib.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
#include <queue.h>
// Initialize DS1307RTC
RTC_DS1307 rtc;
// Initialize LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Define keypad matrix
bool buttonState = false;
const int button = 8; //connected at pin 8
const int buzzer = 9; //connected at pin 9
const byte ROWS = 4; //rows
const byte COLS = 4; //columns
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {22, 24, 26, 28}; //pins
byte colPins[COLS] = {30, 32, 34, 36}; //pins
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );
const int maxInput = 16;
static int cursorPos = 0;
static char inputBuffer[5];
// Alarm time variables
struct Alarm {
int hour;
int minute;
bool enabled;
};
Alarm alarms[3];
int currentAlarm = 0;
//The use of Semaphores
SemaphoreHandle_t interruptSemaphore1;
//The use of Queues
QueueHandle_t queue1;
//The use of Task Handles
TaskHandle_t TaskHandle_1;
TaskHandle_t TaskHandle_3;
TaskHandle_t TaskHandle_2;
void Task1(void *pvParameters);
void Task2(void *pvParameters);
void Task3(void *pvPArameters);
void setup() {
Serial.begin(9600);
pinMode(button, INPUT);
pinMode(buzzer, OUTPUT);
// the use of interrupts
interruptSemaphore1 = xSemaphoreCreateBinary();
// the use of queues
queue1 = xQueueCreate(1, sizeof(Alarm));
// this will set the RTC
xTaskCreate(Task1, "Real_Time_Clock", 128, NULL, 3, &TaskHandle_1);
// this will set the alarm
xTaskCreate(Task2, "Set_Alarm", 128, NULL, 2, &TaskHandle_2);
// this will trigger the alarm
xTaskCreate(Task3, "Buzzer_On", 128, NULL, 3, &TaskHandle_3);
// where the real time clock initiates
rtc.begin();
// to initialize the lcd
lcd.init();
lcd.backlight();
}
void loop() {
//This part of the code must be EMPTY.
}
//This Tasks is display the real time clock
void Task1(void *pvParameters) {
for(;;) {
// vTaskSuspend(TaskHandle_3);
DateTime now = rtc.now();
char key = keypad.getKey();
static unsigned long lastDisplayTime = 0;
if (millis() - lastDisplayTime >= 1000) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Current Time:");
lcd.setCursor(0, 1);
lcd.print(now.hour(), DEC);
lcd.print(":");
lcd.print(now.minute(), DEC);
lcd.print(":");
lcd.print(now.second(), DEC);
lastDisplayTime = millis();
}
for (int i = 0; i < 3; i++) {
if (alarms[i].enabled && now.hour() == alarms[i].hour && now.minute() == alarms[i].minute) {
xSemaphoreGive(interruptSemaphore1);
}
}
if (key == '*') {
vTaskSuspend(NULL); // to suspend the calling task
vTaskDelay(1000/portTICK_PERIOD_MS); // giving a delay of 1 second
}
}
}
//This Tasks is where the keypad functionalities work
void Task2(void *pvParameters) {
for (;;) {
DateTime now = rtc.now();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Alarm " + String(currentAlarm + 1) + ":");
lcd.setCursor(0, 1);
lcd.print("HH:MM");
char key = NO_KEY;
while (key != '#') {
key = keypad.getKey();
if (key != NO_KEY) {
if (((inputBuffer[0] - '0') * 10 + (inputBuffer[1] - '0') <= 23) && (((inputBuffer[3] - '0') * 10) <= 50)) {
if (key >= '0' && key <= '9') {
inputBuffer[cursorPos] = key;
lcd.setCursor(cursorPos, 1);
lcd.print(key);
cursorPos++;
if (cursorPos == 2) {
cursorPos += 1;
}
if (cursorPos == 5){
vTaskResume(TaskHandle_1);
break;
}
}
else if (key == '*') { // to set the alarm
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Alarm " + String(currentAlarm + 1) + ":");
lcd.setCursor(0, 1);
lcd.print("HH:MM");
cursorPos = 0;
}
else if (key == 'D'){ // to clear the number if the number was inputted wrong
deleteLastCharacter();
lcd.setCursor(cursorPos, 2);
}
}
else { // if the user inputs an invalid time
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Invalid Time");
vTaskDelay(500/portTICK_PERIOD_MS);
vTaskSuspend(NULL);
vTaskResume(TaskHandle_1);
}
}
}
if (cursorPos == 5) {
alarms[currentAlarm].hour = (inputBuffer[0] - '0') * 10 + (inputBuffer[1] - '0');
alarms[currentAlarm].minute = (inputBuffer[3] - '0') * 10 + (inputBuffer[4] - '0');
alarms[currentAlarm].enabled = true;
lcd.setCursor(0, 1);
lcd.print("Alarm set ");
vTaskDelay(1000/portTICK_PERIOD_MS);
lcd.clear();
currentAlarm++;
if (currentAlarm >= 3) {
currentAlarm = 0;
}
}
lcd.setCursor(0, 0);
lcd.print("Press '*' to set");
lcd.setCursor(0, 1);
lcd.print("alarm time");
vTaskDelay(3000/portTICK_PERIOD_MS);
cursorPos = 0;
}
}
//This Task will set the alarm/trigger the alarm when
//the user has set their given alotted time.
void Task3(void *pvParameters) {
for(;;){
if(xSemaphoreTake(interruptSemaphore1,portMAX_DELAY) == pdTRUE){
buttonState = digitalRead(button);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ALARM!!");
//vTaskDelay(2000/portTICK_PERIOD_MS);
tone(buzzer, 500, 200);
vTaskDelay(1000/portTICK_PERIOD_MS);
tone(buzzer, 1000, 200);
vTaskDelay(1000/portTICK_PERIOD_MS);
tone(buzzer, 2000, 200);
vTaskDelay(1000/portTICK_PERIOD_MS);
tone(buzzer, 3000, 200);
vTaskDelay(1000/portTICK_PERIOD_MS);
tone(buzzer, 4000, 200);
vTaskDelay(1000/portTICK_PERIOD_MS);
if (buttonState == HIGH) {
noTone(buzzer);
lcd.clear();
lcd.setCursor(0, 0);
vTaskSuspend(NULL); // suspends any task that is currently doing
vTaskResume(TaskHandle_1); // returns back to operate the real time clock
xSemaphoreGive(interruptSemaphore1); //proceeds back in setting the alarm
taskYIELD(); // does the context switching
}
}
}
}
//---------------------------------------------
// Deleting the given set alarm time
void deleteLastCharacter(){
if (cursorPos > 0){
cursorPos--;
if(cursorPos==2)
cursorPos=1;
}
}