#include <LiquidCrystal_I2C.h>
// LCD setup (change address if needed)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Button pins
const int setBtn = 3;
const int hourBtn = 4;
const int minBtn = 5;
const int alarmToggleBtn = 6;
const int buzzerPin = 7;
// Modes
enum Mode { CLOCK, SET_TIME, SET_ALARM };
Mode currentMode = CLOCK;
// Time variables
int setHour = 12;
int setMinute = 0;
bool timeAM = true;
int alarmHour = 6;
int alarmMinute = 0;
bool alarmAM = true;
bool alarmOn = false;
bool alarmRinging = false;
// Snooze variables
bool snoozed = false;
unsigned long snoozeStart = 0;
const unsigned long snoozeDuration = 60000; // 1 min snooze
// Debounce & timing
unsigned long lastTimeUpdate = 0;
const unsigned long timeInterval = 60000; // 1 minute in ms
unsigned long lastButtonPress = 0;
const unsigned long debounceDelay = 300;
// Alarm off button press count
int togglePressCount = 0;
unsigned long firstTogglePressTime = 0;
const int maxTogglePresses = 5;
const unsigned long togglePressWindow = 10000; // 10 seconds
// For LCD update optimization
int prevSetHour = -1, prevSetMinute = -1;
bool prevTimeAM = false;
int prevAlarmHour = -1, prevAlarmMinute = -1;
bool prevAlarmAM = false;
Mode prevMode = CLOCK;
bool prevAlarmOn = false;
bool prevAlarmRinging = false;
void setup() {
pinMode(setBtn, INPUT_PULLUP);
pinMode(hourBtn, INPUT_PULLUP);
pinMode(minBtn, INPUT_PULLUP);
pinMode(alarmToggleBtn, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
lcd.init();
lcd.backlight();
Serial.begin(9600);
Serial.println("Alarm clock started");
}
void loop() {
handleButtons();
// Increment time every minute if not in set time mode
if (currentMode == CLOCK && (millis() - lastTimeUpdate >= timeInterval)) {
incrementTime();
lastTimeUpdate = millis();
}
checkAlarm();
updateDisplay();
delay(50);
}
void incrementTime() {
setMinute++;
if (setMinute >= 60) {
setMinute = 0;
setHour++;
if (setHour > 12) setHour = 1;
if (setHour == 12) timeAM = !timeAM;
}
Serial.print("Time: ");
Serial.print(setHour);
Serial.print(":");
if (setMinute < 10) Serial.print("0");
Serial.print(setMinute);
Serial.print(" ");
Serial.println(timeAM ? "AM" : "PM");
}
void updateDisplay() {
// Update first line only if time or mode changed
if (setHour != prevSetHour || setMinute != prevSetMinute || timeAM != prevTimeAM || currentMode != prevMode) {
lcd.setCursor(0, 0);
lcd.print(" "); // clear line
lcd.setCursor(0, 0);
if (setHour < 10) lcd.print(' ');
lcd.print(setHour);
lcd.print(':');
if (setMinute < 10) lcd.print('0');
lcd.print(setMinute);
lcd.print(' ');
lcd.print(timeAM ? "AM" : "PM");
prevSetHour = setHour;
prevSetMinute = setMinute;
prevTimeAM = timeAM;
}
// Update second line only if alarm or mode changed
if (alarmHour != prevAlarmHour || alarmMinute != prevAlarmMinute || alarmAM != prevAlarmAM ||
alarmOn != prevAlarmOn || alarmRinging != prevAlarmRinging || currentMode != prevMode) {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if (currentMode == SET_ALARM) {
if (alarmHour < 10) lcd.print(' ');
lcd.print("Alarm ");
lcd.print(alarmHour);
lcd.print(':');
if (alarmMinute < 10) lcd.print('0');
lcd.print(alarmMinute);
lcd.print(' ');
lcd.print(alarmAM ? "AM" : "PM");
} else if (alarmRinging) {
lcd.print("ALARM RINGING!");
} else if (alarmOn) {
lcd.print("Alarm ON ");
} else {
lcd.print("Alarm OFF");
}
prevAlarmHour = alarmHour;
prevAlarmMinute = alarmMinute;
prevAlarmAM = alarmAM;
prevAlarmOn = alarmOn;
prevAlarmRinging = alarmRinging;
}
prevMode = currentMode;
}
void handleButtons() {
if (millis() - lastButtonPress < debounceDelay) return;
if (digitalRead(setBtn) == LOW) {
lastButtonPress = millis();
if (currentMode == CLOCK) currentMode = SET_TIME;
else if (currentMode == SET_TIME) currentMode = SET_ALARM;
else currentMode = CLOCK;
Serial.print("Mode changed to: ");
Serial.println(currentMode);
}
if (digitalRead(hourBtn) == LOW) {
lastButtonPress = millis();
if (currentMode == SET_TIME) {
setHour = (setHour % 12) + 1;
if (setHour == 12) timeAM = !timeAM;
Serial.print("Set Hour: ");
Serial.println(setHour);
} else if (currentMode == SET_ALARM) {
alarmHour = (alarmHour % 12) + 1;
if (alarmHour == 12) alarmAM = !alarmAM;
Serial.print("Alarm Hour: ");
Serial.println(alarmHour);
}
}
if (digitalRead(minBtn) == LOW) {
lastButtonPress = millis();
if (currentMode == SET_TIME) {
setMinute = (setMinute + 1) % 60;
Serial.print("Set Min: ");
Serial.println(setMinute);
} else if (currentMode == SET_ALARM) {
alarmMinute = (alarmMinute + 1) % 60;
Serial.print("Alarm Min: ");
Serial.println(alarmMinute);
}
}
if (digitalRead(alarmToggleBtn) == LOW) {
lastButtonPress = millis();
if (alarmRinging) {
// Stop alarm ringing or snooze it
if (!snoozed) {
snoozed = true;
snoozeStart = millis();
Serial.println("Alarm snoozed for 1 min");
alarmRinging = false;
noTone(buzzerPin);
} else {
// Already snoozed, turn off alarm
alarmRinging = false;
alarmOn = false;
snoozed = false;
Serial.println("Alarm turned OFF");
noTone(buzzerPin);
}
togglePressCount = 0; // reset press count when snooze or off
} else {
// Toggle alarm on/off normally
alarmOn = !alarmOn;
Serial.print("Alarm toggled to: ");
Serial.println(alarmOn);
togglePressCount = 0; // reset count on normal toggle
}
// Count presses to force alarm off (5 times in 10 sec)
unsigned long now = millis();
if (togglePressCount == 0) firstTogglePressTime = now;
togglePressCount++;
if (togglePressCount >= maxTogglePresses && (now - firstTogglePressTime) <= togglePressWindow) {
alarmOn = false;
alarmRinging = false;
snoozed = false;
noTone(buzzerPin);
Serial.println("Alarm forcibly turned OFF by rapid toggling");
togglePressCount = 0;
} else if ((now - firstTogglePressTime) > togglePressWindow) {
togglePressCount = 1;
firstTogglePressTime = now;
}
}
}
void checkAlarm() {
if (!alarmOn) {
alarmRinging = false;
noTone(buzzerPin);
snoozed = false;
return;
}
unsigned long now = millis();
if (snoozed) {
if (now - snoozeStart >= snoozeDuration) {
alarmRinging = true;
snoozed = false;
Serial.println("Snooze over, alarm ringing again!");
tone(buzzerPin, 1000);
} else {
noTone(buzzerPin);
alarmRinging = false;
return;
}
}
if (!alarmRinging) {
// Check if current time matches alarm time
if (setHour == alarmHour && setMinute == alarmMinute && timeAM == alarmAM) {
alarmRinging = true;
tone(buzzerPin, 1000);
Serial.println("Alarm ringing!");
}
}
else {
// Keep buzzer on if alarm ringing
tone(buzzerPin, 1000);
}
}
Menu
Set time
Set min
Alarm