Last active
May 12, 2022 17:45
-
-
Save raveren/4729733eb70b6218ad0912ebb4968c4b to your computer and use it in GitHub Desktop.
arduino alarm clock consisting of RTC_DS3231 clock module, button and old school phone dial. https://photos.app.goo.gl/sZ6CsrMxHYXJodcP6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
rodo laika | |
pradejus rinkti - nustatomas zadintuvas | |
paspaudus sverti - rodo zadintuvo laika (arba 8888 jei dar neivestas) | |
palaikius sverti 5 sek - nustatomas laikas | |
skamba zadintuvas | |
skamba minute - tada snuzina 5 min | |
paspaudus sverti - uzsnuzina 5 min | |
palaikius sverti 3 sek - isjungia zadintuva | |
maksimaliai snuzina 1 val, tada issijungia | |
*/ | |
// install RTClib, TM1637 and ezButton from arduino library manager for these to work: | |
#include "RTClib.h" | |
#include <TM1637Display.h> | |
#include <ezButton.h> | |
/*** | |
* _____ _____ _ _ _____ | |
* | __ \|_ _|| \ | | / ____| | |
* | |__) | | | | \| || (___ | |
* | ___/ | | | . ` | \___ \ | |
* | | _| |_ | |\ | ____) | | |
* |_| |_____||_| \_||_____/ | |
* | |
* | |
*/ | |
// pin 6, geltonas laidas. mygtukas tel diske sukliksi tiek kartų pagal tai koks skaičius | |
// renkamas nuo 1 iki 10; | |
ezButton btn_phone_number(6); | |
// pin 7, žalias laidas. mygtukas tel diske įsijungia tada, kai diskas pradedamas sukti, | |
// išjungimas baigus sukti diską. btn_phone_dial_is_spinning.isPressed() returns true while | |
// the dial is spinning | |
ezButton btn_phone_dial_is_spinning(7); | |
// pin 2, žadintuvo snooze mygtukas | |
ezButton btn_snooze(2); | |
#define skambucio_rite_a 10 // skambučio ritės 1 pin | |
#define skambucio_rite_b 11 // skambučio ritės 2 pin | |
// Define the connections pins for TM1637 4 digit 7 segment display | |
TM1637Display display = TM1637Display(8, 9); | |
/*** | |
* _ _ _ | |
* /\ (_) | | | | | |
* / \ _ __ _ __ __ __ __ _ _ __ _ __ _ | |__ | | ___ ___ | |
* / /\ \ | '_ \ | '_ \ \ \ / // _` || '__|| | / _` || '_ \ | | / _ \/ __| | |
* / ____ \ | |_) || |_) | \ V /| (_| || | | || (_| || |_) || || __/\__ \ | |
* /_/ \_\| .__/ | .__/ \_/ \__,_||_| |_| \__,_||_.__/ |_| \___||___/ | |
* | | | | | |
* |_| |_| | |
*/ | |
// Create rtc and display object | |
RTC_DS3231 real_time_clock; | |
const int DISPLAY_DOUBLE_COLON = 0b01000000; | |
enum global_states { | |
SHOWING_TIME, | |
SETTING_ALARM, | |
SHOWING_ALARM_TIME, | |
BLARING_ALARM, | |
SETTING_TIME, | |
SNOOZING | |
}; | |
enum global_states global_state = SHOWING_TIME; | |
/** | |
* SETTINGS | |
*/ | |
const int LED_BLINK_TIME_MS = 200; | |
const int ALARM_SNOOZE_TIME_MINS = 5; | |
const int MAX_ALARM_SNOOZE_TIME = 100; // hours * 100 + minutes | |
// time for alarm - stored as 100 x hrs + mins, i.e. 9:48 is 948 and 15:36 is 1536 | |
int alarm_time = -1; | |
int snoozed_alarm_time = -1; | |
bool is_alarm_snoozed = false; | |
unsigned int currently_setting_time = 0; | |
unsigned int which_digit_is_being_set = 0; | |
// for toggling (blinking) led | |
bool last_led_state_on = true; | |
// store milliseconds since led last toggled | |
unsigned long led_blink_timer = 0; | |
unsigned long ringing_timer = 0; | |
// this stores when the snooze button was PRESSED DOWN () | |
unsigned long snooze_button_pressed = 0; | |
unsigned long alarm_time_displayed_since = 0; | |
/*** | |
* _ ____ | |
* | | / /\ \ | |
* ___ ___ | |_ _ _ _ __ | | | | | |
* / __| / _ \| __|| | | || '_ \ | | | | | |
* \__ \| __/| |_ | |_| || |_) || | | | | |
* |___/ \___| \__| \__,_|| .__/ | | | | | |
* | | \_\/_/ | |
* |_| | |
*/ | |
void setup() { | |
Serial.begin(9600); | |
// Check if RTC is connected correctly | |
if (!real_time_clock.begin()) { | |
Serial.println("Couldn't find real time clock module"); | |
while (1); // halt program | |
} | |
// Check if the RTC lost power (battery removed) and if so, set the time to whenever this was | |
// compiled & uploaded... | |
if (real_time_clock.lostPower()) { | |
real_time_clock.adjust(DateTime(F(__DATE__), F(__TIME__))); | |
} | |
display.setBrightness(5); | |
display.clear(); | |
btn_phone_number.setDebounceTime(5); | |
btn_phone_number.setCountMode(COUNT_FALLING); | |
btn_phone_dial_is_spinning.setDebounceTime(10); | |
btn_phone_dial_is_spinning.setCountMode(COUNT_FALLING); | |
btn_snooze.setDebounceTime(10); | |
btn_snooze.setCountMode(COUNT_FALLING); | |
pinMode(skambucio_rite_a, OUTPUT); // signalas į skambučio ritę | |
pinMode(skambucio_rite_b, OUTPUT); // signalas į skambučio ritę | |
} | |
/*** | |
_ | |
| | | |
| | ___ ___ _ __ | |
| | / _ \ / _ \ | '_ \ | |
| || (_) || (_) || |_) | | |
|_| \___/ \___/ | .__/ | |
| | | |
|_| | |
*/ | |
void loop() { | |
/*** | |
INITIALIZE | |
*/ | |
btn_phone_dial_is_spinning.loop(); // MUST call the loop() function first | |
btn_phone_number.loop(); // MUST call the loop() function first | |
btn_snooze.loop(); // MUST call the loop() function first | |
// we use the main button for multiple functionalities - always be registering when it's pressed | |
if (btn_snooze.isPressed()) { | |
snooze_button_pressed = millis(); // save when the button was pressed | |
} | |
if (btn_snooze.isReleased()) { | |
snooze_button_pressed = 0; | |
} | |
switch (global_state) { | |
/*** | |
_ __ _ _ _ _ | |
| | / _| | || | | | | | | |
__| | ___ | |_ __ _ _ _ | || |_ ___ | |_ __ _ | |_ ___ | |
/ _` | / _ \| _|/ _` || | | || || __| / __|| __|/ _` || __|/ _ \ | |
| (_| || __/| | | (_| || |_| || || |_ \__ \| |_| (_| || |_| __/ | |
\__,_| \___||_| \__,_| \__,_||_| \__| |___/ \__|\__,_| \__|\___| | |
*/ | |
case SHOWING_TIME: | |
led_blink_timer = 0; // we are not blinking the display anymore | |
ringing_timer = 0; // alarm is not ringing | |
if (btn_phone_dial_is_spinning.isPressed()) { // means we started dialing | |
global_state = SETTING_ALARM; | |
alarm_time = 0; // reset alarm time | |
Serial.println("SETTING_ALARM"); | |
return; | |
} | |
// isPressed() is a one-time event, it's reported once and then becomes off | |
// regardless of button state - pressed or not | |
if (btn_snooze.isPressed()) { | |
switch_to_showing_alarm_time(); | |
Serial.println("SHOWING_ALARM_TIME"); | |
return; | |
} | |
if (is_longpress(5000)) { | |
global_state = SETTING_TIME; | |
which_digit_is_being_set = 0; | |
Serial.println("SETTING_TIME"); | |
return; | |
} | |
if (is_alarm_time_now()) { | |
if (!is_alarm_snoozed) { | |
global_state = BLARING_ALARM; | |
ringing_timer = millis(); | |
Serial.println("BLARING_ALARM"); | |
return; | |
} | |
} else { | |
// we just need this variable set for the one minute when alarm is on | |
is_alarm_snoozed = false; | |
} | |
show_current_time(); | |
return; | |
break; | |
/*** | |
_ _ _ _ | |
| | | | (_) | | | |
___ ___ | |_ | |_ _ _ __ __ _ __ _ | | __ _ _ __ _ __ ___ | |
/ __| / _ \| __|| __|| || '_ \ / _` | / _` || | / _` || '__|| '_ ` _ \ | |
\__ \| __/| |_ | |_ | || | | || (_| | | (_| || || (_| || | | | | | | | | |
|___/ \___| \__| \__||_||_| |_| \__, | \__,_||_| \__,_||_| |_| |_| |_| | |
__/ | | |
|___/ | |
*/ | |
case SETTING_ALARM: | |
if (btn_phone_dial_is_spinning.isReleased()) { | |
// todo almost identical to setting time, refactor before changing more | |
int reported_count = btn_phone_number.getCount(); | |
btn_phone_number.resetCount(); | |
if (which_digit_is_being_set == 0) { | |
reported_count++; // somewhy the first number you dial in always gets registered as less by one | |
} | |
if (reported_count >= 10) { | |
reported_count = 0; | |
} | |
if (which_digit_is_being_set == 0) { | |
if (reported_count > 2) { | |
reported_count = 2; | |
} | |
reported_count = reported_count * 1000; | |
} | |
if (which_digit_is_being_set == 1) { | |
if (alarm_time > 1000 && reported_count > 3) { | |
reported_count = 3; | |
} | |
reported_count = reported_count * 100; | |
} | |
if (which_digit_is_being_set == 2) { | |
if (reported_count > 5) { | |
reported_count = 5; | |
} | |
reported_count = reported_count * 10; | |
} | |
if (which_digit_is_being_set == 3) { | |
} | |
alarm_time += reported_count; | |
display.showNumberDecEx(alarm_time, DISPLAY_DOUBLE_COLON, true); | |
// Serial.print(alarm_time); | |
// Serial.print(" - alarmo reiksme; pridetas skaitmuo: "); | |
// Serial.print(reported_count); | |
// Serial.print(" i pozicija # "); | |
// Serial.println(which_digit_is_being_set); | |
// Serial.println("isReleased........................................................................."); | |
which_digit_is_being_set++; | |
if (which_digit_is_being_set >= 4) { | |
which_digit_is_being_set = 0; | |
snoozed_alarm_time = -1; | |
is_alarm_snoozed = false; | |
switch_to_showing_alarm_time(); | |
} | |
} | |
break; | |
/*** | |
_ _ _ | |
| | (_) | | | |
___ | |__ ___ __ __ _ _ __ __ _ __ _ | | __ _ _ __ _ __ ___ | |
/ __|| '_ \ / _ \\ \ /\ / /| || '_ \ / _` | / _` || | / _` || '__|| '_ ` _ \ | |
\__ \| | | || (_) |\ V V / | || | | || (_| | | (_| || || (_| || | | | | | | | | |
|___/|_| |_| \___/ \_/\_/ |_||_| |_| \__, | \__,_||_| \__,_||_| |_| |_| |_| | |
__/ | | |
|___/ | |
*/ | |
case SHOWING_ALARM_TIME: | |
if (alarm_time == -1) { | |
blink_number(8888); | |
} else { | |
blink_number(alarm_time); | |
} | |
// if we start dialing right now - ignore it completely | |
btn_phone_number.resetCount(); | |
// show the alarm on screen for 1.5s | |
if (millis() - alarm_time_displayed_since >= 1500) { | |
global_state = SHOWING_TIME; | |
Serial.println("SHOWING_TIME"); | |
return; | |
} | |
break; | |
/*** | |
_ _ _ | |
| | | | (_) | |
| |__ | | __ _ _ __ _ _ __ __ _ | |
| '_ \ | | / _` || '__|| || '_ \ / _` | | |
| |_) || || (_| || | | || | | || (_| | | |
|_.__/ |_| \__,_||_| |_||_| |_| \__, | | |
__/ | | |
|___/ | |
*/ | |
case BLARING_ALARM: | |
if (!is_alarm_time_now()) { // means a minute has passed and it's no longer alarm time | |
snooze_alarm(); | |
Serial.println("auto_snooze_alarm"); | |
global_state = SHOWING_TIME; | |
return; | |
} | |
if (is_longpress(3000)) { | |
// longpress - off, short press - snooze | |
turn_alarm_off(); | |
Serial.println("turn_alarm_off"); | |
global_state = SHOWING_TIME; | |
return; | |
} | |
if (btn_snooze.isReleased()) { | |
snooze_alarm(); | |
Serial.println("snooze_alarm"); | |
global_state = SHOWING_TIME; | |
return; | |
} | |
sound_the_alarm(); | |
break; | |
/*** | |
_ _ _ _ _ | |
| | | | (_) | | (_) | |
___ ___ | |_ | |_ _ _ __ __ _ | |_ _ _ __ ___ ___ | |
/ __| / _ \| __|| __|| || '_ \ / _` | | __|| || '_ ` _ \ / _ \ | |
\__ \| __/| |_ | |_ | || | | || (_| | | |_ | || | | | | || __/ | |
|___/ \___| \__| \__||_||_| |_| \__, | \__||_||_| |_| |_| \___| | |
__/ | | |
|___/ | |
*/ | |
case SETTING_TIME: | |
if (which_digit_is_being_set == 0) { // until the dial is dialed blink current time | |
DateTime now = real_time_clock.now(); | |
blink_number(now.hour() * 100 + now.minute()); | |
if (btn_snooze.isPressed()) { | |
global_state = SHOWING_TIME; | |
return; | |
} | |
} | |
if (btn_phone_dial_is_spinning.isReleased()) { | |
int reported_count = btn_phone_number.getCount(); | |
btn_phone_number.resetCount(); | |
if (which_digit_is_being_set == 0) { | |
currently_setting_time = 0; // start | |
reported_count++; // somewhy the first number you dial in always gets registered as less by one | |
} | |
if (reported_count == 10) { | |
reported_count = 0; | |
} | |
if (which_digit_is_being_set == 0) { | |
if (reported_count > 2) { | |
reported_count = 2; | |
} | |
reported_count = reported_count * 1000; | |
} | |
if (which_digit_is_being_set == 1) { | |
if (currently_setting_time > 1000 && reported_count > 3) { | |
reported_count = 3; | |
} | |
reported_count = reported_count * 100; | |
} | |
if (which_digit_is_being_set == 2) { | |
if (reported_count > 5) { | |
reported_count = 5; | |
} | |
reported_count = reported_count * 10; | |
} | |
if (which_digit_is_being_set == 3) { | |
} | |
currently_setting_time += reported_count; | |
display.showNumberDecEx(currently_setting_time, DISPLAY_DOUBLE_COLON, true); | |
which_digit_is_being_set++; | |
if (which_digit_is_being_set >= 4) { | |
real_time_clock.adjust( | |
DateTime( | |
2000, 12, 31, // date - we don't care about it | |
currently_setting_time / 100, // hour | |
currently_setting_time % 100, // minute | |
0 // second | |
) | |
); | |
currently_setting_time = 0; | |
which_digit_is_being_set = 0; | |
global_state = SHOWING_TIME; | |
} | |
} | |
break; | |
} | |
} | |
/*** | |
_ _ | |
| | | | | |
| |__ ___ | | _ __ ___ _ __ ___ | |
| '_ \ / _ \| || '_ \ / _ \| '__|/ __| | |
| | | || __/| || |_) || __/| | \__ \ | |
|_| |_| \___||_|| .__/ \___||_| |___/ | |
| | | |
|_| | |
*/ | |
bool is_longpress(int ms) { | |
// snooze_button_pressed stores time since button was depressed, and is set to 0 when released | |
return snooze_button_pressed && millis() - snooze_button_pressed >= ms; | |
} | |
void switch_to_showing_alarm_time() { | |
global_state = SHOWING_ALARM_TIME; | |
alarm_time_displayed_since = millis(); | |
} | |
void show_current_time() { | |
// Get current date and time | |
DateTime now = real_time_clock.now(); | |
// Create time format to display: | |
int displayTime = (now.hour() * 100) + now.minute(); | |
display.showNumberDecEx( | |
displayTime, | |
now.second() % 2 == 0 ? DISPLAY_DOUBLE_COLON | |
: 0, // display center double colon only every other second; i.e. blink | |
true | |
); | |
} | |
void blink_number(int number) { | |
if (led_blink_timer == 0) { | |
led_blink_timer = millis(); | |
} | |
if (millis() - led_blink_timer >= LED_BLINK_TIME_MS) { | |
last_led_state_on = !last_led_state_on; | |
led_blink_timer = millis(); | |
} | |
if (last_led_state_on) { | |
display.showNumberDecEx(number, DISPLAY_DOUBLE_COLON, true); | |
} else { | |
display.clear(); | |
} | |
} | |
void snooze_alarm() { | |
is_alarm_snoozed = true; | |
// now set the snooze time | |
unsigned int mins = real_time_clock.now().minute(); | |
unsigned int hrs = real_time_clock.now().hour(); | |
mins += ALARM_SNOOZE_TIME_MINS; | |
if (mins >= 60) { | |
hrs += mins / 60; | |
mins = mins % 60; | |
} | |
snoozed_alarm_time = (hrs * 100) + mins; | |
// limit maximum snooze time | |
if (snoozed_alarm_time - alarm_time > MAX_ALARM_SNOOZE_TIME) { | |
Serial.println("MAX_ALARM_SNOOZE_TIME"); | |
turn_alarm_off(); | |
} else { | |
if (hrs > 23) { // handle 24hrs | |
snoozed_alarm_time = mins; | |
} | |
} | |
Serial.print("snoozed_alarm_time "); | |
Serial.println(snoozed_alarm_time); | |
} | |
void turn_alarm_off() { | |
// simply snooze the alarm but don't set the next ring time | |
is_alarm_snoozed = true; | |
snoozed_alarm_time = -1; | |
Serial.println("turned alarm off for today"); | |
} | |
bool is_alarm_time_now() { | |
DateTime now = real_time_clock.now(); | |
return (now.hour() == (alarm_time / 100)) && (now.minute() == (alarm_time % 100)) | |
|| (now.hour() == (snoozed_alarm_time / 100)) | |
&& (now.minute() == (snoozed_alarm_time % 100)); | |
} | |
void sound_the_alarm() { | |
unsigned int since_start = millis() - ringing_timer; | |
DateTime now = real_time_clock.now(); | |
blink_number(now.hour() * 100 + now.minute()); | |
if (since_start < 10) { | |
digitalWrite(skambucio_rite_a, HIGH); | |
digitalWrite(skambucio_rite_b, LOW); | |
} else if (since_start < 20) { | |
digitalWrite(skambucio_rite_a, LOW); | |
digitalWrite(skambucio_rite_b, LOW); | |
} else if (since_start < 30) { | |
digitalWrite(skambucio_rite_a, LOW); | |
digitalWrite(skambucio_rite_b, HIGH); | |
} else if (since_start < 38) { | |
digitalWrite(skambucio_rite_a, LOW); | |
digitalWrite(skambucio_rite_b, LOW); | |
} else { | |
ringing_timer = millis(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment