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.

ComponentsAmount
Arduino Uno Rev 31
Breadboard1
USB cable1
Computer1
Battery snap1
9V battery1
Piezo1
Pushbutton2
220 ohm resistor3
Standard LCD (16 x 2)1
Potentiometer1
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!

Similar Posts