// Detailed Alarm Clock with Keypad Control (fully commented)
#include <Wire.h> // Include I²C library for RTC & LCD bus
#include <LiquidCrystal_I2C.h> // Include I²C LCD library for 16×2 display
#include <RTClib.h> // Include RTClib for DS3231 real-time clock
#include <Keypad.h> // Include Keypad library to read matrix keypad
// Create RTC and LCD objects
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 cols, 2 rows
// Pin connected to the buzzer (pin 10 chosen to avoid keypad pins)
const int buzzerPin = 10;
// Variables to hold the alarm time; not const so they can be updated via keypad
typedef uint8_t byte;
int alarmHour = 13; // Default alarm hour (24‑hour format)
int alarmMinute = 15; // Default alarm minute
bool alarmTriggered = false; // Flag to ensure one‑shot alarm
// Keypad configuration: a 4×4 matrix
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'}, // Row 1
{'4','5','6','B'}, // Row 2
{'7','8','9','C'}, // Row 3
{'*','0','#','D'} // Row 4
};
// Map keypad rows and columns to Arduino pins (R1→9, R2→8, R3→7, R4→6)
byte rowPins[ROWS] = {9, 8, 7, 6};
// Map keypad columns to pins (C1→5, C2→4, C3→3, C4→2)
byte colPins[COLS] = {5, 4, 3, 2};
// Initialize the Keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Variables for capturing user input and display timing
String inputTime = ""; // Holds HHMM digits when setting alarm
bool settingAlarm = false; // Tracks whether we are in alarm‑setting mode
unsigned long lastUpdate = 0; // Timestamp for periodic display updates
void setup() {
Wire.begin(); // Start I2C bus (used by RTC & LCD)
lcd.begin(16, 2); // Initialize the LCD dimensions
lcd.backlight(); // Turn on LCD backlight
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW); // Ensure buzzer is off initially
// Initialize RTC; halt if DS3231 not found
if (!rtc.begin()) {
lcd.print("RTC not found!");
while (1);
}
// Show welcome message then prompt for alarm
lcd.print("Welcome 7oka");
delay(4200); // Give user time to read
lcd.clear();
lcd.print("Press A: Alarm");
delay(4200); // Give user time to read
lcd.clear();
}
void loop() {
// 1) Handle keypad input first, so we can switch modes immediately
char key = keypad.getKey();
if (key) {
// If 'A' pressed, enter alarm‑setting mode
if (key == 'A') {
settingAlarm = true;
inputTime = ""; // Clear any previous input
lcd.clear();
lcd.print("Set Alarm HHMM:");
}
// If '#' pressed, confirm the 4‑digit input
else if (key == '#') {
if (inputTime.length() == 4) {
// Parse the two‑digit hour and minute
alarmHour = inputTime.substring(0, 2).toInt();
alarmMinute = inputTime.substring(2, 4).toInt();
alarmTriggered = false; // Reset trigger for new alarm
// Show confirmation
lcd.clear();
lcd.print("Alarm Set:");
lcd.setCursor(0, 1);
// Print HH:MM with leading zeros
if (alarmHour < 10) lcd.print('0');
lcd.print(alarmHour);
lcd.print(":");
if (alarmMinute < 10) lcd.print('0');
lcd.print(alarmMinute);
delay(2000);
}
settingAlarm = false; // Exit setting mode
lcd.clear();
}
// If 'C' pressed in setting mode, delete last character (backspace)
else if (settingAlarm && key == 'C' && inputTime.length() > 0) {
inputTime.remove(inputTime.length() - 1); // Remove last char
// Update display: clear second line and reprint inputTime
lcd.setCursor(0, 1);
lcd.print(" "); // Clear 4-char field
lcd.setCursor(0, 1);
lcd.print(inputTime);
}
// If in setting mode and digit pressed, append to input
else if (settingAlarm && isDigit(key) && inputTime.length() < 4) {
inputTime += key;
// Echo the digit on the second line
lcd.setCursor(inputTime.length() - 1, 1);
lcd.print(key);
}
}
// 2) Update time/date display every 1s if not in setting mode
if (!settingAlarm && millis() - lastUpdate > 1000) {
lastUpdate = millis(); // Reset timer
DateTime now = rtc.now(); // Read current time
// First line: show HH:MM:SS
lcd.setCursor(0, 0);
lcd.print("Time: ");
if (now.hour() < 10) lcd.print('0');
lcd.print(now.hour());
lcd.print(':');
if (now.minute() < 10) lcd.print('0');
lcd.print(now.minute());
lcd.print(':');
if (now.second() < 10) lcd.print('0');
lcd.print(now.second());
// Second line: show DD/MM/YYYY
lcd.setCursor(0, 1);
lcd.print("Date: ");
if (now.day() < 10) lcd.print('0');
lcd.print(now.day());
lcd.print('/');
if (now.month() < 10) lcd.print('0');
lcd.print(now.month());
lcd.print('/');
lcd.print(now.year());
}
// 3) Alarm trigger: check every loop for exact match at second 0
DateTime now2 = rtc.now();
if (!alarmTriggered &&
now2.hour() == alarmHour &&
now2.minute() == alarmMinute &&
now2.second() == 0) {
alarmTriggered = true; // Prevent re-trigger
digitalWrite(buzzerPin, HIGH);
delay(10000); // Buzz for 10s
digitalWrite(buzzerPin, LOW);
}
}