#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo motor;
const int btnUp = 2;
const int btnGo = 3;
const int btnDown = 4;
const int servoPin = 9;
const int buzzerPin = 8; // Buzzer connected here
int minutes = 0;
const int maxMinutes = 90;
int timeLeft = 0; // Minutes remaining in countdown
bool running = false;
unsigned long previousMillis = 0;
const unsigned long interval = 1000; // 1 second update interval
int elapsedSeconds = 0; // Seconds elapsed since countdown start
// Previous button states for detecting press event
bool prevBtnUpState = LOW;
bool prevBtnDownState = LOW;
bool prevBtnGoState = LOW;
void setup() {
pinMode(btnUp, INPUT);
pinMode(btnGo, INPUT);
pinMode(btnDown, INPUT);
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
lcd.init();
lcd.backlight();
motor.attach(servoPin);
motor.write(90); // Start at 90°
updateDisplay();
}
void loop() {
// Read current button states
bool currentBtnUpState = digitalRead(btnUp);
bool currentBtnDownState = digitalRead(btnDown);
bool currentBtnGoState = digitalRead(btnGo);
if (!running) {
// Detect rising edge (not pressed -> pressed) for Up button
if (currentBtnUpState == HIGH && prevBtnUpState == LOW) {
if (minutes < maxMinutes) {
minutes++;
updateDisplay();
}
}
// Detect rising edge for Down button
if (currentBtnDownState == HIGH && prevBtnDownState == LOW) {
if (minutes > 0) {
minutes--;
updateDisplay();
}
}
// Detect rising edge for Go button
if (currentBtnGoState == HIGH && prevBtnGoState == LOW) {
if (minutes > 0) {
timeLeft = minutes;
elapsedSeconds = 0;
running = true;
previousMillis = millis();
motor.write(90); // Reset servo position
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Starting...");
delay(1000);
drawProgressBar(timeLeft);
}
}
} else {
// During countdown, detect down button press to reduce timeLeft by 1 minute
if (currentBtnDownState == HIGH && prevBtnDownState == LOW) {
if (timeLeft > 0) {
timeLeft--;
if (timeLeft < 0) timeLeft = 0;
drawProgressBar(timeLeft);
}
}
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
elapsedSeconds++;
int totalSeconds = minutes * 60;
int secondsLeft = totalSeconds - elapsedSeconds;
if (secondsLeft < 0) secondsLeft = 0;
if (secondsLeft == 0) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Done!");
lcd.setCursor(0, 1);
lcd.print(" ");
motor.write(0);
running = false;
buzzDone(); // Sound buzzer for 2 seconds
delay(3000);
updateDisplay();
} else {
// Calculate current angle for servo (90° to 0°)
int angle = map(secondsLeft, 0, totalSeconds, 0, 90);
if (angle > 90) angle = 90;
if (angle < 0) angle = 0;
motor.write(angle);
// Update timeLeft in minutes left (rounded up)
timeLeft = (secondsLeft + 59) / 60;
drawProgressBar(timeLeft);
// Display countdown MM:SS
displayCountdown(secondsLeft);
}
}
}
// Save current button states for next loop iteration
prevBtnUpState = currentBtnUpState;
prevBtnDownState = currentBtnDownState;
prevBtnGoState = currentBtnGoState;
}
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Time:");
lcd.setCursor(0, 1);
lcd.print(minutes);
lcd.print(" min ");
drawProgressBar(minutes);
}
void drawProgressBar(int timeVal) {
int barLength = 16;
int filled = map(timeVal, 0, maxMinutes, 0, barLength);
if (filled > barLength) filled = barLength;
if (filled < 0) filled = 0;
lcd.setCursor(0, 1);
for (int i = 0; i < barLength; i++) {
if (i < filled) {
lcd.write(byte(255)); // Full block
} else {
lcd.print("");
}
}
}
void displayCountdown(int secondsLeft) {
int dispMinutes = secondsLeft / 60;
int dispSeconds = secondsLeft % 60;
lcd.setCursor(0, 0);
lcd.print("Time Left: ");
if (dispMinutes < 10) lcd.print('0');
lcd.print(dispMinutes);
lcd.print(':');
if (dispSeconds < 10) lcd.print('0');
lcd.print(dispSeconds);
lcd.print(" ");
}
void buzzDone() {
tone(buzzerPin, 1500); // Play 1000 Hz tone
delay(2500); // for 2 seconds
noTone(buzzerPin); // Stop tone
}