Arduino Project #1 – Alarm Clock Without Real-Time Clock (RTC)
One of the biggest reasons why I decided to get into Arduino was to be able to create useful products. One of my favorite (and first) builds was an alarm clock that incorporated an Arduino.
To be honest, when I started this journey, I was very tempted to google this project and copy other people’s work. Instead, I built it from scratch. I spent approximately 3 weeks meticulously planning, creating, and testing my project.
In the end, it turned out amazing. It’s user-friendly, and I only used the Arduino Official Starter Kit to create this. I’ve created this guide to show you how you can create a great Arduino alarm clock.
Components and Supplies
In case you don’t want to pay around $100 (at the time of writing) for the Arduino kit, here are the components I used. You can find them in component shops like Adafruit or Sparkfun.
Components | Amount |
---|---|
Arduino Uno Rev 3 | 1 |
Breadboard | 1 |
USB cable | 1 |
Computer | 1 |
Battery snap | 1 |
9V battery | 1 |
Piezo | 1 |
Pushbutton | 2 |
220 ohm resistor | 3 |
Standard LCD (16 x 2) | 1 |
Potentiometer | 1 |
Jumper wires | (at least) 20 |
How To Build Alarm Clock With Arduino
Here’s a model of the specific placements for the jumper wires and components:
Necessary Software
The only software you need is the Arduino IDE (integrated development environment). This is so that you can upload the alarm clock code to your Arduino board.
Alarm Clock Code
Here’s the code for the clock in regular time with the AM and PM notations:
// include the library code:
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// this is the user interface: user can change minutes and hours here to fit their time and/or set an alarm
byte userSetHour = 8; // user can change the hour value here to set their clock (use military time - 0-24)
byte userSetMinute = 2; // user can change the minute value here to set their clock
byte alarmHour = 12; // user can change the hour value here to set an alarm for their clock
byte alarmMinute = 15; // user can change the minute value here to set an alarm for their clock
byte pinSound = 8; // this is the pin attached to the piezo
int switchState = 0; // this determines whether the hour value sohuld be changing or not
int switchState1 = 0; // this determines whether the minute value should be changing or not
// the following variables are used for calculating and adjusting the minute values
bool turnOffMin = false; // this controls whether we should be using the user input for minutes - after their set minute is over, we stop using it
byte setMinute; // used to calculate the current time (minutes) based on user input
byte remainMin; // does the math to revert time (minutes) back to normal after user changes it
// the following variables are used for calculating and adjusting the hour values
bool turnOffHour = false; // this controls whether we should be using the user input for hours - after their set hour is over, we stop using it
byte setHour; // used to calculate the current time (hours) based on user input
byte remainHour; // does the math to revert time (hours) back to normal after user changes it
byte minuteMore; // adjusts the hour based on the minutes that went by
byte setMinute1; // used to calculate the current time (hours with minutes) based on user input
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the pushbuttons
pinMode(6, INPUT); // this changes the minute value
pinMode(7, INPUT); // this changes the hour value
}
void loop() {
// starts displaying the beginning words of the second line on the LCD
lcd.setCursor(0, 1);
lcd.print("It is");
// detects whether the button is pressed or not
switchState1 = digitalRead(6);
switchState = digitalRead(7);
// the following code handles the second algorithm
byte interval = 60; // the max number the second or minutes can't go above. Once they reach this, the minutes and second starts over from 0.
unsigned long mult = millis() / 60000; // multiplies the interval (below) to bring the second down between the 0-60 range
unsigned long currentSec = millis() / 1000; // keeps track of the seconds - is displayed as seconds on the LCD
if (currentSec >= interval) { // if the seconds value is over 60 seconds
currentSec = currentSec - (interval * mult); // does the math that brings the range for the second to 0-60
}
if (currentSec < 10) { // if the second is a single digit number
// add a zero in front of the single digit
lcd.setCursor(13, 1);
lcd.print(currentSec);
lcd.setCursor(12, 1);
lcd.print(0);
}
if (currentSec >= 10) { // if the second is a double digit number
// make its place regular
lcd.setCursor(12, 1);
lcd.print(currentSec);
}
// the following code handles the minute algorithm
if (turnOffMin == false) { // this utilizes the user's input for the minutes
setMinute = userSetMinute; // this changes the second
remainMin = 0;
}
if (turnOffMin == true) { // this stops using the user's input for the minutes
setMinute = 0;
remainMin = 60 - userSetMinute; // does the math to adjust minute's value due to user's input on minute
}
unsigned long multMin = (millis() / 60000 - remainMin) / 60; // multiplies the interval (below) to bring the minute down between the 0-60 range
unsigned long currentMin = millis() / 60000 + setMinute - remainMin; // keeps track of the minutes - this is displayed as minutes on the LCD
if ((currentMin >= interval) && (turnOffMin == true)) { // if the minutes value is over 60 and we're not using the user's input for minutes anymore
currentMin = currentMin - (interval * multMin); // does the math that brings the range for the minute to 0-60
}
if ((currentMin >= interval) && (turnOffMin == false)) { // if the minutes value is over 60 and we're using the user's input
currentMin = currentMin - 60;
turnOffMin = true; // now we stop using user's input and revert back to the old method
}
if (currentMin < 10) { // if the minute value is a single digit number
// readjust the numbers so that a zero is in the ten's place
lcd.setCursor(10, 1);
lcd.print(currentMin);
lcd.setCursor(9, 1);
lcd.print(0);
}
if (currentMin >= 10) { // if the minute value is a double digit number
// leave it how it is
lcd.setCursor(9, 1);
lcd.print(currentMin);
}
// the following code handles the hour algorithm
if (turnOffHour == false) { // use the following variables based on the user's changes
setMinute1 = userSetMinute;
setHour = userSetHour;
remainHour = 0;
minuteMore = 0;
}
if (turnOffHour == true) { // use the following variables without the user's changes
setMinute1 = 0;
setHour = 0;
remainHour = 24 - userSetHour - 1; // minus 1 because the minutes took away a number
minuteMore = 60 - userSetMinute; // helps readjust the hour value since the minute was changed by the user
}
byte intHour = 24; // doesn't allow the hour number go above 24
unsigned long currentHour = (millis() / 60000 + setMinute1 - minuteMore) / 60 + setHour - remainHour; // keeps track of the hour - shows the hour value on the LCD
unsigned long multHour = ((millis() / 60000 - remainMin) / 60 - remainHour) / 24; // multiplies the intHour to bring the hour down between the 0-24 range
if ((currentHour >= intHour) && (turnOffHour == true)) { // if the hour value is over 24 and we're not using the user's input anymore
currentHour = currentHour - (intHour * multHour); // does the math that brings the range for the hour to 0-24
}
if ((currentHour >= intHour) && (turnOffHour == false)) { // if the hour value is over 24 and we're using the user's input for hours
currentHour = currentHour - 24;
turnOffHour = true; // now we stop using the user's input
}
if (currentHour < 10) { // if it's a single digit number
lcd.setCursor(7, 1);
lcd.print(currentHour);
lcd.setCursor(6, 1);
lcd.print(0);
}
if (currentHour >= 10) { // if it's a double digit number
lcd.setCursor(6, 1);
lcd.print(currentHour);
}
// the following code turns the military time into regular AM/PM time
if ((currentHour > 23) && (currentHour < 1)) {
lcd.setCursor(6, 1);
lcd.print("12");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 0) && (currentHour < 2)) {
lcd.setCursor(6, 1);
lcd.print("01");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 1) && (currentHour < 3)) {
lcd.setCursor(6, 1);
lcd.print("02");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 2) && (currentHour < 4)) {
lcd.setCursor(6, 1);
lcd.print("03");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 3) && (currentHour < 5)) {
lcd.setCursor(6, 1);
lcd.print("04");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 4) && (currentHour < 6)) {
lcd.setCursor(6, 1);
lcd.print("05");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 5) && (currentHour < 7)) {
lcd.setCursor(6, 1);
lcd.print("06");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 6) && (currentHour < 8)) {
lcd.setCursor(6, 1);
lcd.print("07");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 7) && (currentHour < 9)) {
lcd.setCursor(6, 1);
lcd.print("08");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 8) && (currentHour < 10)) {
lcd.setCursor(6, 1);
lcd.print("09");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 9) && (currentHour < 11)) {
lcd.setCursor(6, 1);
lcd.print("10");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 10) && (currentHour < 12)) {
lcd.setCursor(6, 1);
lcd.print("11");
lcd.setCursor(14, 1);
lcd.print("AM");
}
if ((currentHour > 11) && (currentHour < 13)) {
lcd.setCursor(6, 1);
lcd.print("12");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 12) && (currentHour < 14)) {
lcd.setCursor(6, 1);
lcd.print("01");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 13) && (currentHour < 15)) {
lcd.setCursor(6, 1);
lcd.print("02");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 14) && (currentHour < 16)) {
lcd.setCursor(6, 1);
lcd.print("03");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 15) && (currentHour < 17)) {
lcd.setCursor(6, 1);
lcd.print("04");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 16) && (currentHour < 18)) {
lcd.setCursor(6, 1);
lcd.print("05");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 17) && (currentHour < 19)) {
lcd.setCursor(6, 1);
lcd.print("06");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 18) && (currentHour < 20)) {
lcd.setCursor(6, 1);
lcd.print("07");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 19) && (currentHour < 21)) {
lcd.setCursor(6, 1);
lcd.print("08");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 20) && (currentHour < 22)) {
lcd.setCursor(6, 1);
lcd.print("09");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 21) && (currentHour < 23)) {
lcd.setCursor(6, 1);
lcd.print("10");
lcd.setCursor(14, 1);
lcd.print("PM");
}
if ((currentHour > 22) && (currentHour < 24)) {
lcd.setCursor(6, 1);
lcd.print("11");
lcd.setCursor(14, 1);
lcd.print("PM");
}
// the following code polishes up the user interface
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(8, 1);
lcd.print(":");
lcd.setCursor(11, 1);
lcd.print(":");
// the following code handles the algorithm for the message on the top of the LCD
if ((currentHour >= 0) && (currentHour < 12)) { // if it is morning
if ((currentSec == 0) && (currentHour == 0)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good morning!");
}
if ((currentHour >= 12) && (currentHour < 18)) { // if it is the afternoon
if ((currentSec == 0) && (currentHour == 12)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good afternoon!");
}
if ((currentHour >= 18) && (currentHour < 21)) { // if it is the evening
if ((currentSec == 0) && (currentHour == 18)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good evening!");
}
if ((currentHour >= 21) && (currentHour < 24)) { // if it is night time
if ((currentSec == 0) && (currentHour == 21)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good night!");
}
// the following code handles the algorithm for sounding the alarm for 10 seconds to wake someone up (or to remind them of something)
if ((currentHour == alarmHour) && (currentMin == alarmMinute) && (currentSec <= 10)) { // only rings for 10 seconds
tone(pinSound, 650);
tone(pinSound, 1000);
} else {
noTone(pinSound);
}
// the following code handles the algorithm for detecting the button on the user's end to change either the hour or minute's value
if (switchState == HIGH) { // this changes the hour value if the button is pushed
if (userSetHour < 24) {
userSetHour = userSetHour + 1;
}
if (userSetHour >= 24) {
userSetHour = 0;
}
}
if (switchState1 == HIGH) {
if (userSetMinute < 60) {
userSetMinute = userSetMinute + 1;
}
if (userSetMinute >= 60) { // this changes the minute value if the button is pushed
userSetMinute = 0;
}
}
}
As you can see, I use a lot of millis() functions. If you want to learn more about the millis() function and some insider tips, consider reading my time function guide. It also contains other time-related functions you can use besides the millis() function.
Here’s a different version of my code, but the time is displayed as military time (meaning that it starts from 0 to 24 before repeating). Note: I didn’t program the buttons into it.
// include the library code:
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// this is the user interface: user can change minutes and hours here to fit their time and/or set an alarm
byte userSetHour = 12; // user can change the hour value here to set their clock (use military time - 0-24)
byte userSetMinute = 18; // user can change the minute value here to set their clock
byte alarmHour = 12; // user can change the hour value here to set an alarm for their clock
byte alarmMinute = 15; // user can change the minute value here to set an alarm for their clock
byte pinSound = 8; // this is the pin attached to the piezo
// the following variables are used for calculating and adjusting the minute values
bool turnOffMin = false; // this controls whether we should be using the user input for minutes - after their set minute is over, we stop using it
byte setMinute; // used to calculate the current time (minutes) based on user input
byte remainMin; // does the math to revert time (minutes) back to normal after user changes it
// the following variables are used for calculating and adjusting the hour values
bool turnOffHour = false; // this controls whether we should be using the user input for hours - after their set hour is over, we stop using it
byte setHour; // used to calculate the current time (hours) based on user input
byte remainHour; // does the math to revert time (hours) back to normal after user changes it
byte minuteMore; // adjusts the hour based on the minutes that went by
byte setMinute1; // used to calculate the current time (hours with minutes) based on user input
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop() {
// starts displaying the beginning words of the second line on the LCD
lcd.setCursor(0, 1);
lcd.print("It is");
// the following code handles the second algorithm
byte interval = 60; // the max number the second or minutes can't go above. Once they reach this, the minutes and second starts over from 0.
unsigned long mult = millis() / 60000; // multiplies the interval (below) to bring the second down between the 0-60 range
unsigned long currentSec = millis() / 1000; // keeps track of the seconds - is displayed as seconds on the LCD
if (currentSec >= interval) { // if the seconds value is over 60 seconds
currentSec = currentSec - (interval * mult); // does the math that brings the range for the second to 0-60
}
if (currentSec < 10) { // if the second is a single digit number
// add a zero in front of the single digit
lcd.setCursor(13, 1);
lcd.print(currentSec);
lcd.setCursor(12, 1);
lcd.print(0);
}
if (currentSec >= 10) { // if the second is a double digit number
// make its place regular
lcd.setCursor(12, 1);
lcd.print(currentSec);
}
// the following code handles the minute algorithm
if (turnOffMin == false) { // this utilizes the user's input for the minutes
setMinute = userSetMinute; // this changes the second
remainMin = 0;
}
if (turnOffMin == true) { // this stops using the user's input for the minutes
setMinute = 0;
remainMin = 60 - userSetMinute; // does the math to adjust minute's value due to user's input on minute
}
unsigned long multMin = (millis() / 60000 - remainMin) / 60; // multiplies the interval (below) to bring the minute down between the 0-60 range
unsigned long currentMin = millis() / 60000 + setMinute - remainMin; // keeps track of the minutes - this is displayed as minutes on the LCD
if ((currentMin >= interval) && (turnOffMin == true)) { // if the minutes value is over 60 and we're not using the user's input for minutes anymore
currentMin = currentMin - (interval * multMin); // does the math that brings the range for the minute to 0-60
}
if ((currentMin >= interval) && (turnOffMin == false)) { // if the minutes value is over 60 and we're using the user's input
currentMin = currentMin - 60;
turnOffMin = true; // now we stop using user's input and revert back to the old method
}
if (currentMin < 10) { // if the minute value is a single digit number
// readjust the numbers so that a zero is in the ten's place
lcd.setCursor(10, 1);
lcd.print(currentMin);
lcd.setCursor(9, 1);
lcd.print(0);
}
if (currentMin >= 10) { // if the minute value is a double digit number
// leave it how it is
lcd.setCursor(9, 1);
lcd.print(currentMin);
}
// the following code handles the hour algorithm
if (turnOffHour == false) { // use the following variables based on the user's changes
setMinute1 = userSetMinute;
setHour = userSetHour;
remainHour = 0;
minuteMore = 0;
}
if (turnOffHour == true) { // use the following variables without the user's changes
setMinute1 = 0;
setHour = 0;
remainHour = 24 - userSetHour - 1; // minus 1 because the minutes took away a number
minuteMore = 60 - userSetMinute; // helps readjust the hour value since the minute was changed by the user
}
byte intHour = 24; // doesn't allow the hour number go above 24
unsigned long currentHour = (millis() / 60000 + setMinute1 - minuteMore) / 60 + setHour - remainHour; // keeps track of the hour - shows the hour value on the LCD
unsigned long multHour = ((millis() / 60000 - remainMin) / 60 - remainHour) / 24; // multiplies the intHour to bring the hour down between the 0-24 range
if ((currentHour >= intHour) && (turnOffHour == true)) { // if the hour value is over 24 and we're not using the user's input anymore
currentHour = currentHour - (intHour * multHour); // does the math that brings the range for the hour to 0-24
}
if ((currentHour >= intHour) && (turnOffHour == false)) { // if the hour value is over 24 and we're using the user's input for hours
currentHour = currentHour - 24;
turnOffHour = true; // now we stop using the user's input
}
if (currentHour < 10) { // if it's a single digit number
lcd.setCursor(7, 1);
lcd.print(currentHour);
lcd.setCursor(6, 1);
lcd.print(0);
}
if (currentHour >= 10) { // if it's a double digit number
lcd.setCursor(6, 1);
lcd.print(currentHour);
}
// the following code polishes up the user interface
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(8, 1);
lcd.print(":");
lcd.setCursor(11, 1);
lcd.print(":");
// the following code handles the algorithm for the message on the top of the LCD
if ((currentHour >= 0) && (currentHour < 12)) { // if it is morning
if ((currentSec == 0) && (currentHour == 0)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good morning!");
}
if ((currentHour >= 12) && (currentHour < 18)) { // if it is the afternoon
if ((currentSec == 0) && (currentHour == 12)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good afternoon!");
}
if ((currentHour >= 18) && (currentHour < 21)) { // if it is the evening
if ((currentSec == 0) && (currentHour == 18)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good evening!");
}
if ((currentHour >= 21) && (currentHour < 24)) { // if it is night time
if ((currentSec == 0) && (currentHour == 21)) { // clears the message from before so that no stray letters so for one second (may appear to be glitchy)
lcd.clear();
}
lcd.setCursor(0, 0);
lcd.print("Good night!");
}
// the following code handles the algorithm for sounding the alarm for 10 seconds to wake someone up (or to remind them of something)
if ((currentHour == alarmHour) && (currentMin == alarmMinute) && (currentSec <= 10)) { // only rings for 10 seconds
tone(pinSound, 650);
tone(pinSound, 1000);
} else {
noTone(pinSound);
}
}
Alarm Clock Overview (What It Does)
The most notable feature on my alarm clock build is the LCD, which displays a friendly greeting (depending on the time of day), the hour, the minute, and the second. The first line contains the greeting, and the second contains the exact time.
The next cool feature is the battery snap with the 9V battery. This makes your clock more portable, and your clock won’t need to rely on your computer for power anymore.
There’s also a piezo, which is like a buzzer. It will emit a shrill (ambulance-like) noise for 10 seconds when it reaches the time you set it for to ring.
Lastly, to make this design more user-friendly, I’ve included 2 pushbuttons for the user to manipulate and change the hour and minute value. The button near the middle of the breadboard changes the minute value whereas the button near the outside of the breadboard changes the hour value. This is so that users don’t have to go back onto their computers to change the clock.
For a cool bonus, I’ve installed a potentiometer that will adjust the opacity of the words. You can turn it one way and the words on the LCD disappear completely!
If you’re interested in learning how I built this project from scratch, then you have to read my prototyping guide. It’s directed towards begineers and is very easy to understand.
Lastly, I hope this setup isn’t too time-consuming!