Description
It’s our first presentation for an Arduino based application. Here, we present a simple electronic project on an alarm clock which is based on an "Arduino Uno" development board. The alarm clock uses an alphanumeric liquid-crystal display (LCD), and 6 buttons. The LCD shows a real time clock, days of the week calendar, and the scheduled alarm. Five buttons are used for days, hours and minutes settings, and for setting hours and minutes for the scheduled alarm time, respectively. We also use an additional button for alarm deactivation. The alarm sounds for one minute, daily, at scheduled alarm time.
We use a simple 2x16 LCD display (2 lines / 16 characters per line). The current day of the week is shown on the first line of the display. Current time and scheduled alarm are showed on the second line of the display (see Photo 1). Hours, minutes and seconds are displayed for the current time, while only hours and minutes are displayed for the alarm. The clock uses a 24-hour cycle. The ":" character is used to separate hours, minutes and seconds. We also use the «AL» prefix before the scheduled alarm time. When the alarm is deactivated, a special phrase ("OFF") is displayed instead of the scheduled alarm time.
Photo 1. Clock's liquid crystal display
For the alarm, we use a simple buzzer which is activated and deactivated from an I/O (digital input-output) pin of the microcontroller. Besides the display, clock also uses an additional LED, which toggles every second, thereby indicating the accurate counting of time.
Alarm Clock Circuit
We use a standard alphanumeric LCD display module, based on an HD44780 – compatible controller. Data are transferred to the display via nibbles (groups of 4-bits). So, we use 4 data lines and 2 control lines for RS and E pins. The RW control line of the display module is permanently connected to logic 0 (ground).
Display data bus uses I/O pins 5, 4, 3 and 2, from the Arduino –Uno board. Those I/O pins are directly connected to D4, D5, D6 and D7 display terminals, respectively. RS and E control lines of the display module are connected to I/O pins 12 and 11, respectively. Display connection to the Arduino-Uno development board is illustrated in detail in Figure 1:
Figure 1. LCD connection to Arduino Uno
We also use six push-buttons for clock settings. All buttons are normally open (off) and they are activated on every button press. Each push-button is a simple switch which has two terminals. One terminal on every button is permanently connected to ground and the other terminal is connected to an I/O pin. Buttons use I/O pins 8, 9, 10, and A0, A1, A2, respectively. For the buzzer and the LED indicator, we use I/O pins A5 and 13, respectively. Connections for the buttons, the buzzer and the LED indicator, are shown in detail in Figure 2.
In series with the LED, we use a 220 Ohm resistor, to regulate its current. The buzzer operates on 5V and it is directly connected to an I/O pin, without any resistor.
When buttons are in their idle states (off), all I/O lines used for them, are in logic-1 through pull-up resistors. These pull-up resistors are part of the internal circuitry of the microcontroller, and they are activated from software. Each time we press a push-button, its corresponding I/O line is grounded through the switch and goes to logic 0.
Figure 2. Buttons, buzzer and the LED indicator connections
Alarm Clock Code
The real time clock
The real time clock is based on an interrupt routine (interrupt driven clock). We use a specific global variable (of type Long) as seconds-counter. The counter counts seconds, starting from Sunday midnight, and its value is increased by one on each beat of the clock. Each clock beat is derived from an interrupt routine, which runs every second. Time accuracy is derived from microcontroller’s quartz main-clock, which runs at 16MHz.
Photo 2. The LCD clock, built on a breadboard
The interrupt routine runs on every overflow of timer 1 module. Timer1 is part of the internal circuitry of the microcontroller. Timer1 is a 16 bit digital counter, and counts pulses from the output of a fixed prescaler. We use the prescaler to divide by 256, the 16MHz frequency of the main clock. The prescaler is also part of the internal circuitry of the microcontroller.
Figure 3. Timer1 count pulses from the output of a prescaler
The interrupt routine runs every 1 second on every overflow of timer1, and time keeping of one-second interval is ensured as follows:
Referring to Figure 3, we observe that the output of the prescaler corresponds to frequency F, which is given by:
F=Fclk/N (1)
Fclk = 16MHz, is the frequency of the microcontroller’s main clock, and N = 256, is the selected division ratio of the prescaler.
Frequency F corresponds to F cycles per second, and each cycle takes time T = 1/F, to be completed:
T=N/Fclk (2)
One second corresponds to M cycles, where M = 1 / T = F. When starting from a zero value, timer1 overflows every 216 = 65536 pulses, because it is a 16bit counter. In order to make timer1 to overflow every 1 second, the timer should overflow every M pulses, and this can be achieved if timer1 is preloaded with value K (starts counting from K instead of zero at every counting cycle):
Κ=216-Μ (3)
Substituting Fclk = 16 MHz and N = 256 in equations (2) and (3), we find that:
K=3036 (4)
This means that in order to achieve an interrupt routine that runs every 1 second, we should:
- Use the main clock of 16MHz.
- Divide main’s clock frequency by N = 256, by using a prescaler, and use the resulting frequency as the trigger source for timer 1.
- Initialize timer1 to the initial value of K = 3036 at every overflow (counting cycle of 1sec).
Steps 1 and 2 are implemented on initialization code, which is executed only once (setup):
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
// Set timer1_counter to the correct value for our interrupt interval
timer1_counter = 3036;
TCNT1 = timer1_counter; // preload timer
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
interrupts(); // enable all interrupts
In every interrupt routine (which runs every 1sec), the global counter variable is increased by 1, in order to count seconds, the LED toggles, and timer 1 is initialized (preloaded) to a certain value (which equals 3036). Therefore, the code of the interrupt routine is as follows:
ISR(TIMER1_OVF_vect) // interrupt service routine
{
TCNT1 = timer1_counter; // preload timer
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
seconds = seconds+1;
}
Endless loop code
Our clock is interrupt driven, and seconds are counted automatically. All the information we need about time is contained in a global variable which is used as a counter. The counter counts seconds, starting from Sunday midnight. Based on this observation we can easily describe the code which "runs" within the endless loop. Firstly, let’s describe the flowchart of the endless loop through pseudo-code:

- Start
- Check if any button is pressed and call a specific button code (function)
- Display current day in the first line of LCD
- Display current time at the beginning of the second line of LCD
- Display alarm time at the end of the second line of LCD
- Reset counter everytime it reaches 604800 seconds, since a whole week just elapsed
- Increase loop counter by 1 (to record loop cycles for buttons debouncing)
- Check whether the alarm is to ring and turn-on the buzzer
- Return to the top (start from beginning)
Now, let’s convert the pseudo code into real structured code, based on function calls:
void loop()
{
checkDaysButton();
checkHoursButton();
checkMinButton();
checkAlHoursButton();
checkAlMinButton();
checkAlButton();
printDay(getDay(seconds));
printHour(getHour(seconds));
printMin(getMin(seconds));
printSec(getSec(seconds));
printAlHour(AlHour);
printAlMin(AlMin);
if(seconds > 604799)seconds = 0;
cnt = cnt + 1;
checkAlarm();
}
Main loop functions, expalined
getDay function
Returns current day (0,1,2,3,4,5,6, for Sunday through Saturday, respectively). It accepts a unique parameter; current seconds from the global counter (seconds – counter).
The seconds counter starts (it is zero) on Sunday midnight (Su 00:00:00). One whole day (a 24-hour cycle) lasts 86,400 seconds. Therefore, the current day is calculated from the integer quotient of seconds / 86400:
int getDay(long sec)
{
int day;
day = sec / 86400;
return day;
}
PrintDay function
It displays the current day in the first line of the LCD.
void printDay(int days)
{
lcd.setCursor(0, 0);
switch (days)
{
case 0:
lcd.print(sun);
break;
case 1:
lcd.print(mon);
break;
case 2:
lcd.print(tue);
break;
case 3:
lcd.print(wed);
break;
case 4:
lcd.print(thu);
break;
case 5:
lcd.print(fri);
break;
case 6:
lcd.print(sat);
break;
}
}
sun, mon, ...., Sat are fixed 16-symbols phrases, declared as global variables of string-type:
String sun = "Su ";
String mon = " Mo ";
String tue = " Tu ";
String wed = " We ";
String thu = " Th ";
String fri = " Fr ";
String sat = " Sa ";
getΗour function
Returns hours (0,1,2,3 ... ..23), for the real time clock. It accepts as a parameter, the seconds counter.
Every day has 86,400 seconds, and each hour lasts 3600 seconds. Therefore, the current hour is calculated as follows:
- Calculate the integer remainder of seconds / 86400. The result is equal to how many seconds have elapsed since midnight of the current day.
- Calculate the integer quotient of the previous result / 3600. The result equals current hours.
long getHour(long sec)
{
long mod;
long hour;
mod = sec % 86400;
hour = mod / 3600;
return hour;
}
PrintΗour function
It displays current hours at the beginning of the second line of the LCD. Hours are displayed in two digits format (eg 08 or 21). Symbol ":", is also displayed after the second digit and is used as a text separator.
void printHour(int hr)
{
lcd.setCursor(0,1);
if(hr < 10){
lcd.print("0");
lcd.print(hr);
}else
lcd.print(hr);
lcd.print(":");
}
getMin function
Returns current minutes (0,1,2,3 ... ..59). It also accepts a unique parameter; the value of the seconds counter.
Every day has 86,400 seconds, every hour lasts 3600 seconds and every minute lasts 60 seconds. Therefore, minutes are calculated as follows:
- Calculate the integer remainder of seconds / 86400. The result is equal to the number of seconds that have been elapsed since the midnight of the current day.
- Calculate the remainder of the previous result / 3600. The result is equal to the number of seconds that have been elapsed since the most recent hour change.
- Calculate the integer quotient of the previous result / 60. The result equals current minutes ( minutes passed since the most recent hour change).
long getMin(long sec)
{
long mod;
long hour;
long minu;
mod = sec % 86400;
hour = mod % 3600;
minu = hour / 60;
return minu;
}
PrintMin function
It displays minutes of the current time, immediately after the current hour, on the second line of the LCD. Minutes are displayed in two digits (e.g. 09 or 58), followed by the separation symbol ":".
void printMin(int mi){
lcd.setCursor(3,1);
if(mi < 10){
lcd.print("0");
lcd.print(mi);
} else lcd.print(mi);
lcd.print(":");
}
getSec function
Returns current seconds (0,1,2,3 ... ..59). It also accepts a unique parameter; the value of the seconds counter.
Current seconds are equal to the remainder: seconds counter / 60.
long getSec(long sec){
return sec % 60;
}
PrintSec function
It displays current seconds, immediately after current minutes, in the second line of the LCD. Seconds are also displayed in two digits format (eg 01 or 59).
void printSec(int se)
{
lcd.setCursor(6,1);
if(se < 10){
lcd.print("0");
lcd.print(se);
}else
lcd.print(se);
}
PrintAlHour function
It displays the hours of the scheduled alarm at the end of the second line of the LCD. Alarm hours are displayed in two digits format (eg 08 or 21), after the "AL" prefix. We also print the ":" separator symbol, in order to separate alarm hours from alarm minutes. The scheduled alarm time is contained in a global variable (global variable AlHour, which is passed as a function parameter). A negative value in this global variable has a special meaning; it means that the alarm is OFF. When alarm is deactivated, the “AL OFF” message is displayed instead of the scheduled alarm.
void printAlHour(long hr){
lcd.setCursor(9,1);
lcd.print("AL");
if (hr>-1)
{
if(hr < 10){
lcd.print("0");
lcd.print(hr);
}else
lcd.print(hr);
lcd.print(":");
}
else lcd.print(" OFF ");
}
PrintAlMin function
It displays the minutes of the scheduled alarm, at the end of the second line of the LCD. The minutes of the scheduled alarm time are contained in a global variable which is passed in the function as a parameter. Alarm minutes are displayed only if the alarm is active (on) and this is true when the global variable AlHour (containing the hours of the scheduled alarm) is positive or zero.
void printAlMin(long mi){
lcd.setCursor(14,1);
if (AlHour>-1)
{
if(mi < 10){
lcd.print("0");
lcd.print(mi);
}else
lcd.print(mi);
}
}
checkΧButton functions-set
There are six checkXButton functions, with X being the name of each unique button. These are the
checkDaysButton();
checkHoursButton();
checkMinButton();
checkAlHoursButton();
checkAlMinButton();
checkAlButton();
Each one of the above functions checks if any specific button is pressed and calls another function to run some specific code lines (for the corresponding button).
The checkXButton functions are all identical, and each one of them implements button-debouncing (bounce suppression). All checkXButton functions run once in each cycle of the main loop.
Each checkXhButton function uses some global variables. These variables hold the current and the previous state of each button and the precise timing of every press-event, and are used for debouncing. Timing is based on a virtual timer; the global variable cnt (of type long). The value of the cnt variable is increased by 1, in every cycle of the main loop and is used as a time base counter for debouncing.
For more details about the technique we use for debouncing, please refer to: http://playground.arduino.cc/Learning/SoftwareDebounce, on the official website of Arduino.
Below is a typical example of a checkXButton function; the checkDaysButton function, which checks whether the user presses the button used for days setting. Once the button is pressed, checkDaysButton calls the dButtonPressed () function that runs some specific code lines for the specific button.
void checkDaysButton(){
int reading1 = digitalRead(dButton);
// If the switch changed, due to noise or pressing:
if (reading1 != lastButton1State) {
// reset the debouncing timer
lastDebounce1Time = cnt;
}
if ((cnt - lastDebounce1Time) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (reading1 != button1State) {
button1State = reading1;
// only toggle the LED if the new button state is HIGH
if (button1State == LOW) {
//Serial.println("button pressed");
dButtonPressed();
}
}
}
checkAlarm function
Checks the clock and compares it to the scheduled alarm. When there is a perfect match, the buzzer is activated. During comparison, seconds are ignored. Therefore, when the alarm is activated, it remains active for one minute.
void checkAlarm(){
if((getHour(seconds) == AlHour) && (getMin(seconds) == AlMin) ){
Serial.print("Alarm ON");
digitalWrite(buzzPin, HIGH);
}else{
Serial.print("Alarm OFF");
digitalWrite(buzzPin, LOW);
}
}
Buttons functions
Functions that implement button operations are not called directly from the main loop. They are called indirectly at each press of any key, through checkXButton functions.
dButtonPressed function
It changes clock day. Current day is changed to the next day of the week, at every press of the corresponding button. Changing the current day to the next one, is equivalent to an increase of 86,400 seconds of the seconds counter. There is a small detail about Saturday ( seconds> 518399); Moving from Saturday to Sunday it’s not just a simple increment of 86400 seconds of the seconds counter, because the upper limit of the seconds counter is 604799. Moving from Saturday to Sunday requires time of the day recovery and initialization of the seconds counter. During initialization, we turn off the interrupt routine, in order to avoid bouncing. Such a bounce could be happened from simultaneous accessing to the counter variable, from code and from interrupt.
void dButtonPressed(){
long buff;
buff = seconds;
if (buff>86400) buff=buff % 86400;
noInterrupts();
if (seconds>518399)
seconds = buff;
else seconds=seconds+86400;
//delay(300);
interrupts();
}
hButtonPressed function
It changes clock hours. Clock hours are increased by one, at every press of the corresponding button. Increasing clock hours by 1, is equivalent to an increase of the global seconds-counter by 3600. Some special care is needed for the last hour of the week (seconds> 518399), which is just before Sunday midnight. Moving from Saturday to Sunday requires initialization of the seconds counter. During initialization, we must also turn off the interrupt routine, in order to avoid possible bouncing.
void hButtonPressed(){
long buff;
buff = seconds;
if (buff>86400) buff=buff % 86400;
noInterrupts();
if (seconds>518399)
seconds = buff;
else seconds=seconds+3600;
interrupts();
}
mButtonPressed function
It changes clock minutes. Clock minutes are increased by one, at every press of the corresponding button. Increasing minutes is equivalent to an increase of the global seconds-counter by 60 seconds. Some special care is also needed, when clock points to a minute past midnight of Sunday, in order not to exceed the upper limit of the counter (which is 604799). In addition, lines
seconds = seconds / 60;
seconds = seconds * 60;
reset clock seconds, when user changes clock minutes. This is useful, when the user wishes to synchronize the clock with another reference clock, with maximum precision.
The interrupt routine is deactivated during accessing to the global counter. Again, this is to prevent a possible bounce from simultaneous accessing to the same variable from code and from the interrupt service routine.
void mButtonPressed(){
long buff;
buff = seconds;
if (buff>86400) buff=buff % 86400;
noInterrupts();
if (seconds>518399)
seconds = buff;
else seconds=seconds+60;
seconds = seconds / 60;
seconds = seconds * 60 ;
//delay(300);
interrupts();
}
AlHourPressed function
It changes the hours of the scheduled alarm. Hours of the scheduled alarm are increased by one, at every press of the corresponding button. This increase is equivalent to an increase by 1 of the global AlHour variable, which stores the hours of the scheduled alarm and takes values from 0-23.
void AlHourPressed()
{
AlHour++;
if (AlHour>23) AlHour=0;
}
AlMinPressed function
It changes the minutes of the scheduled alarm. The minutes of the scheduled alarm are increased by one, at every press of the corresponding button. This increase is equivalent to an increase by 1 of the global AlMin variable, which stores the minutes of the scheduled alarm and takes values from 0-59.
void AlMinPressed()
{
AlMin++;
if (AlMin>59) AlMin=0;
}
AlButtonPressed function
It deactivates the alarm at every press of the corresponding button. The alarm is disabled by setting a negative value to the universal AlHour variable. To reactivate the alarm, the user needs to press the button which sets the alarm hours.
void AlButtonPressed(){
AlHour=-1;
AlMin=0;
}
About this article
The Arduino based alarm clock was made in January of 2016, primarily for educational purposes. The initialization of timer1 to a fixed initial value in every run of the interrupt routine causes an infinitesimal time delay which, in long term, affects the accuracy of the clock. This delay causes a systematic error of about 2 seconds per hour which must be corrected through software. However, in the code presented in our article (and also in the attached file), we do not include code for correcting this systematic error. So, if you want to achieve absolute accuracy, you must add your own code lines on this project! An alternative solution to the problem would be to change the crystal of controller’s main clock and use one with frequency that is an integer multiple of 216 Hz, or make your own crystal oscillator from a standard clock crystal (32.768KHz) to “trigger” timer1 externally.
Clock Programming at the office (there is a large pizza on the desk ... not shown in photo!)
If you have any fresh ideas to suggest, you wish to add something to this project or you wish to make corrections, please post or send your comments. Everyone will appreciate further contribution. You are also welcome to publish your own Arduino projects on CircuitLib.
Attachments
Here, we present the code of the Arduino based clock. Assemble the hardware, open the code file with the Arduino programming environment, program the Arduino Uno board and .. voila!..you’ll have your own alarm clock! Have fun and we wish you happy learning hours with Arduino ..!
Arduino-based clock code