/*
LED Hourglass Timer
Based on:
https://github.com/fbrnc/Arduino_Hourglass/tree/master
Uses a modified version of Eberhard Fahle's LedControl.
*/
//#include "Arduino.h"
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include "LedControl.h"
#include <mechButton.h>
#include <timeObj.h>
#include "buttons.h"
#define ROTATION_OFFSET 180
// user constants
const int START_TIME = 630; // default countdown time
const int MAX_VAL = 3600; // 1 hour max
const int MIN_VAL = 0;
// pin constants
const int DATA_PIN = 5;
const int LOAD_PIN = 6;
const int CLK_PIN = 7;
const int BUZZ_PIN = A3;
// instantiate buttons
CounterButton btnUp(3);
CounterButton btnDown(4);
mechButton btnStartPause(2);
// global variables
bool isRunning = false;
bool alarmActive = false;
int runCounter = START_TIME; // default seconds to countdown from
int setCounter = START_TIME;
unsigned long lastTickTime = 0;
unsigned long alarmStartTime = 0;
// create objects
Adafruit_MPU6050 mpu;
LedControl lc = LedControl(DATA_PIN, CLK_PIN, LOAD_PIN, 2);
void decrementCounter() {
if (runCounter > MIN_VAL) {
runCounter--;
setCounter = runCounter;
printCounter();
}
}
void incrementCounter() {
if (runCounter < MAX_VAL) {
runCounter++;
setCounter = runCounter;
printCounter();
}
}
void onTimerFinished() {
isRunning = false;
alarmActive = true;
alarmStartTime = millis(); // Set time anchor for the buzzer routine
Serial.println("***********************");
Serial.println(" ALARM: TIME'S UP! ");
Serial.println("***********************");
}
void printCounter() {
Serial.print("Time Remaining: ");
Serial.print(runCounter);
Serial.println("s");
}
void toggleStartPause() {
if (!btnStartPause.trueFalse()) {
// If the alarm is currently ringing, clicking this button instantly silences it
if (alarmActive) {
noTone(BUZZ_PIN);
alarmActive = false;
runCounter = setCounter;
Serial.println(">>> ALARM SILENCED BY USER <<<");
printCounter();
return;
}
isRunning = !isRunning;
if (isRunning) {
Serial.println(">>> TIMER STARTED <<<");
lastTickTime = millis();
tone(BUZZ_PIN, 1500, 100); // Friendly start tone
} else {
Serial.println(">>> TIMER PAUSED <<<");
tone(BUZZ_PIN, 1200, 100); // Friendly pause tone
}
}
}
void setup() {
Serial.begin(115200);
// try to start MPU
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
for (;;) {}
}
Serial.println("MPU6050 Found!\n");
// configure MPU
mpu.setAccelerometerRange(MPU6050_RANGE_2_G);
mpu.setGyroRange(MPU6050_RANGE_2000_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
// configure matrices
for (byte i = 0; i < 2; i++) {
lc.shutdown(i, false);
lc.setIntensity(i, 1);
}
//lc.setRotation((ROTATION_OFFSET + gravity) % 360);
lc.setRotation(ROTATION_OFFSET);
// set button callbacks
btnUp.setActionCallback(incrementCounter);
btnDown.setActionCallback(decrementCounter);
btnStartPause.setCallback(toggleStartPause);
// set pinmode, seed random
pinMode(BUZZ_PIN, OUTPUT);
randomSeed(analogRead(A0));
// show ready
Serial.println("--- Countdown Timer Ready ---");
printCounter();
Serial.println("Status: PAUSED");
}
void loop() {
idle(); // Background debouncer for all 3 buttons
int minRows = 0;
int minCols = 0;
int row = 0;
int col = 0;
// 1. If paused and alarm isn't ringing, allow user to adjust the timer setting
if (!isRunning && !alarmActive) {
btnUp.update();
btnDown.update();
}
// 2. If running, process the countdown clock
else if (isRunning) {
unsigned long currentMillis = millis();
if (currentMillis - lastTickTime >= 1000) {
lastTickTime = currentMillis;
if (runCounter > MIN_VAL) {
runCounter--;
printCounter();
// Play a short click tone on the buzzer every second for a "ticking" effect
if (runCounter > 0) {
tone(BUZZ_PIN, 2000, 20);
}
if (runCounter == MIN_VAL) {
onTimerFinished();
}
}
}
}
// 3. Non-blocking buzzer alarm control
if (alarmActive) {
unsigned long elapsedAlarm = millis() - alarmStartTime;
// Ring the alarm for a total of 3 seconds
if (elapsedAlarm < 3000) {
// Create a pulsing beep pattern (on for 250ms, off for 250ms)
if ((elapsedAlarm / 250) % 2 == 0) {
tone(BUZZ_PIN, 1000); // Ring at a clear 1000 Hz tone
} else {
noTone(BUZZ_PIN); // Pause sound
}
} else {
// 3 seconds are up, silence the alarm
noTone(BUZZ_PIN);
alarmActive = false;
runCounter = setCounter;
Serial.println("Alarm stopped automatically.");
printCounter();
}
}
int minutes = runCounter / 60;
int seconds = runCounter % 60;
while (minutes > 0) {
lc.setXY(0, row, col, true);
row++;
minutes--;
if (row > 7) {
row = 0;
col++;
}
};
row = 0;
col = 0;
while (seconds > 0) {
lc.setXY(1, row, col, true);
row++;
seconds--;
if (row > 7) {
row = 0;
col++;
}
};
/*
minRows = (minutes % 8);
//Serial.println(minRows);
minCols = (minutes / 8);
//Serial.println(minCols);
for (int i = 0; i < minRows; i++) {
for (int j = 0; j <= minCols; j++) {
lc.setXY(0, i, j, true);
}
}
minRows = (seconds % 8);
//Serial.println(minRows);
minCols = (seconds / 8);
//Serial.println(minCols);
for (int i = 0; i <= minRows; i++) {
for (int j = 0; j <= minCols; j++) {
lc.setXY(1, i, j, true);
}
}
*/
}
Start
Inc
Dec