In this article we will learn how to make a digital clock using Arduino nano with below features:
- Display temperature & humidity
- Display date and day
- Can set alarm
- Can set time, day and date
Prerequisites:
- Arduino nano with cable
- DS1302 RTC
- MAX7219 4in1 Display
- DHT11 Temperature and Humidity sensor
- Active buzzer
- Momentary switches
- Two 220 ohm and one 10K ohm resistor
- Female to female jumper wires, normal wires
- 5v charger
- MDF board, paint, drill machine, screws, glue gun etc
Implementation
We will design our circuit based on below diagram. Momentary switches use 220ohm resistors and DHT sensor uses 10K ohm resistor.

Two momentary switches are "back" and "select". On press back clock can display day, temperature, humidity etc. Select button works as menu which supports momentary press and long press. Momentary press is used to display menu items and long press is used to get into menu item also to save and exit.
If you want to change time, click on select button(button-2 in video) keep clicking until you see option "Time". Once time is displayed press and hold same button, you will be able to get into time setting mode. Click back(Button-1 in video) to change between hours and minutes, press select button to change time and again log press to save and exit.
Full video of making this clock:
Below is the code will be uploading to Arduino:
#include <MD_Parola.h> #include <MD_MAX72xx.h> #include <SPI.h> #include "Font_Data.h" #include <virtuabotixRTC.h> #include "DHT.h" #include <EEPROM.h> //Display #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define DISPLAY_CLK_PIN 13 #define DISPLAY_DATA_PIN 11 #define DISPLAY_CS_PIN 10 //RTC #define RTC_CLK_PIN 2 #define RTC_DATA_PIN 3 #define RTC_RST_PIN 4 //DHT #define DHT_PIN 6 #define DHTTYPE DHT11 // Hardware SPI connection MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_CS_PIN, MAX_DEVICES); //RTC virtuabotixRTC myRTC(RTC_CLK_PIN,RTC_DATA_PIN,RTC_RST_PIN); //DHT DHT dht(DHT_PIN, DHTTYPE); //OTHER PINS #define BTN_BACK_PIN 8 #define BTN_SELECT_PIN 7 #define ALARM_PIN 5 char Buffer[7]; int SELECT = 0; int SUBSELECT = 0; int BACK = 0; int OPTION = 0; int RING = 0; int ALARMHOUR = 6; int ALARMMINUTE = 30; int ALARMON = 0; int ALARMOFFONBUTTONPRESS = 0; int ROMALARMHOURADD = 0; int ROMALARMMINUTEADD = 2; int ROMALARMONADD = 4; void setup(void) { P.begin(); dht.begin(); Serial.begin(9600); P.setFont(newFont); P.setIntensity(1); pinMode(ALARM_PIN, OUTPUT); pinMode(BTN_BACK_PIN, INPUT); pinMode(BTN_SELECT_PIN, INPUT); ALARMHOUR=EEPROM.read(ROMALARMHOURADD); ALARMMINUTE=EEPROM.read(ROMALARMMINUTEADD); ALARMON=EEPROM.read(ROMALARMONADD); if(ALARMHOUR > 23) ALARMHOUR = 6; if(ALARMMINUTE > 59) ALARMHOUR = 30; if(ALARMON > 1) ALARMON = 0; } void loop(void) { int alarmMinDiff = (myRTC.hours*60 + myRTC.minutes) - (ALARMHOUR*60 + ALARMMINUTE); //Ring if time is alarm time, alarm is on, user did not press any button and user is not inside menu if(alarmMinDiff == 0 && ALARMON == 1 && ALARMOFFONBUTTONPRESS == 0 && SELECT == 0) { ringAlarm(); } else { digitalWrite(ALARM_PIN, LOW); } if(alarmMinDiff != 0) { ALARMOFFONBUTTONPRESS = 0; } int longPress = 0; if(digitalRead(BTN_BACK_PIN)==HIGH) { if(tryStopAlarm(alarmMinDiff) == 0) clickedBack(); } while(digitalRead(BTN_SELECT_PIN)==HIGH) { longPress += 100; delay(100); if(longPress > 1500 || digitalRead(BTN_SELECT_PIN)==LOW) break; continue; } if(longPress > 0) { if(tryStopAlarm(alarmMinDiff) == 0) { if(SELECT > 0 && longPress > 1500) { clickedLongSelect(); } else if(longPress > 0) { clickedSelect(); } } } P.displayAnimate(); while(digitalRead(BTN_BACK_PIN)==HIGH) delay(100); while(digitalRead(BTN_SELECT_PIN)==HIGH) delay(100); if(BACK == 0 && SELECT == 0) { displayTime(); } delay(100); } void ringAlarm() { if(RING == 0) { digitalWrite(ALARM_PIN, HIGH); RING = 1; } else { digitalWrite(ALARM_PIN, LOW); RING = 0; } } int tryStopAlarm(int alarmMinDiff) { if(alarmMinDiff == 0 && ALARMON == 1 && ALARMOFFONBUTTONPRESS == 0) { ALARMOFFONBUTTONPRESS = 1; digitalWrite(ALARM_PIN, LOW); return 1; } return 0; } //---------------------------------Button click methods--------------------------------------// void clickedBack() { BACK++; if(SELECT == 0) { switch(BACK) { case 1: { displayDay(); break; } case 2: { displayDate(); break; } case 3: { displayTemparature(); break; } case 4: { displayHumidity(); break; } default: { BACK = 0; break; } } } else if(SELECT > 0) { switch(SUBSELECT) { case 1: { updateTime(); break; } case 2: { updateDate(); break; } case 3: { updateDay(); break; } case 4: { updateAlarm(); break; } case 5: { updateOnOffAlarm(); break; } default: { reset(); } } } } void clickedSelect() { SELECT++; if(SUBSELECT == 0) { switch(SELECT) { case 1: { P.displayText("Time", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); break; } case 2: { P.displayText("Date", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); //P.displayText("Set Date", PA_LEFT, 60, 0, PA_SCROLL_LEFT, PA_NO_EFFECT); break; } case 3: { P.displayText("Day", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); break; } case 4: { P.displayText("Alarm", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); break; } case 5: { P.displayClear(); P.displayText("ON/OFF - Alarm", PA_LEFT, 60, 0, PA_SCROLL_LEFT, PA_NO_EFFECT); break; } default: { SELECT = 0; } } } else { switch(SUBSELECT) { case 1: { if(OPTION == 0) OPTION++; else OPTION=0; displayOptionTime(); break; } case 2: { if(OPTION < 2) OPTION++; else OPTION=0; displayOptionDate(); break; } case 3: { displayOptionDay(); break; } case 4: { if(OPTION == 0) OPTION++; else OPTION=0; displayOptionAlarm(); break; } case 5: { displayOptionOnOffAlarm(); break; } default: { SELECT = 0; } } } } void clickedLongSelect() { if(SELECT > 0 && SUBSELECT > 0) { reset(); displayTime(); } if(SELECT > 0) { SUBSELECT = SELECT; switch(SELECT) { case 1: { displayOptionTime(); break; } case 2: { displayOptionDate(); break; } case 3: { displayOptionDay(); break; } case 4: { displayOptionAlarm(); break; } case 5: { displayOptionOnOffAlarm(); break; } } } } //---------------------------------Update Date Time--------------------------------------// void updateTime() { if(OPTION == 0) { int hours = myRTC.hours < 23 ? myRTC.hours + 1 : 0; myRTC.setDS1302Time(00, myRTC.minutes, hours, myRTC.dayofweek, myRTC.dayofmonth, myRTC.month, myRTC.year); } else { int minutes = myRTC.minutes < 59 ? myRTC.minutes + 1 : 0; myRTC.setDS1302Time(00, minutes, myRTC.hours, myRTC.dayofweek, myRTC.dayofmonth, myRTC.month, myRTC.year); } myRTC.updateTime(); displayOptionTime(); } void updateDate() { if(OPTION == 0) { int dayofmonth = myRTC.dayofmonth < 31 ? myRTC.dayofmonth + 1 : 1; myRTC.setDS1302Time(00, myRTC.minutes, myRTC.hours, myRTC.dayofweek, dayofmonth, myRTC.month, myRTC.year); } else if(OPTION == 1) { int month = myRTC.month < 12 ? myRTC.month + 1 : 1; myRTC.setDS1302Time(00, myRTC.minutes, myRTC.hours, myRTC.dayofweek, myRTC.dayofmonth, month, myRTC.year); } else if (OPTION == 2) { int year = myRTC.year < 2050 ? myRTC.year + 1 : 2020; myRTC.setDS1302Time(00, myRTC.minutes, myRTC.hours, myRTC.dayofweek, myRTC.dayofmonth, myRTC.month, year); } myRTC.updateTime(); displayOptionDate(); } void updateDay() { int dayofweek = myRTC.dayofweek < 7 ? myRTC.dayofweek + 1 : 1; myRTC.setDS1302Time(00, myRTC.minutes, myRTC.hours, dayofweek, myRTC.dayofmonth, myRTC.month, myRTC.year); myRTC.updateTime(); displayOptionDay(); } void updateAlarm() { if(OPTION == 0) { ALARMHOUR = ALARMHOUR < 23 ? ALARMHOUR + 1 : 0; EEPROM.write(ROMALARMHOURADD, ALARMHOUR); } else { ALARMMINUTE = ALARMMINUTE < 59 ? ALARMMINUTE + 1 : 0; EEPROM.write(ROMALARMMINUTEADD, ALARMMINUTE); } displayOptionAlarm(); } void updateOnOffAlarm() { if(ALARMON == 0) ALARMON++; else ALARMON = 0; EEPROM.write(ROMALARMONADD, ALARMON); displayOptionOnOffAlarm(); } //---------------------------------Display methods--------------------------------------// void displayTime() { myRTC.updateTime(); int hours = myRTC.hours > 12 ? myRTC.hours - 12 : myRTC.hours == 0 ? 12 : myRTC.hours; sprintf(Buffer,"%02d : %02d",hours,myRTC.minutes); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayTemparature() { int tempC = dht.readTemperature(); sprintf(Buffer,"%02d ºC", tempC); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayHumidity() { int humi = dht.readHumidity(); sprintf(Buffer,"%02d %%", humi); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayDay() { myRTC.updateTime(); char* dayPtr = getDay(myRTC.dayofweek); P.displayText(dayPtr, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayDate() { myRTC.updateTime(); char* monthPtr = getMonth(myRTC.month); sprintf(Buffer,"%s %02d", monthPtr, myRTC.dayofmonth); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayOptionTime() { if(OPTION == 0) sprintf(Buffer,">%02d:%02d",myRTC.hours,myRTC.minutes); else sprintf(Buffer,"%02d:>%02d",myRTC.hours,myRTC.minutes); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayOptionDate() { if(OPTION == 0) sprintf(Buffer,"D>%02d",myRTC.dayofmonth); else if(OPTION == 1) sprintf(Buffer,"M>%02d",myRTC.month); else sprintf(Buffer,"Y>%02d",myRTC.year); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayOptionDay() { char* dayName = getDay(myRTC.dayofweek); sprintf(Buffer,"D>%0s",dayName); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayOptionAlarm() { if(OPTION == 0) sprintf(Buffer,">%02d:%02d",ALARMHOUR,ALARMMINUTE); else sprintf(Buffer,"%02d:>%02d",ALARMHOUR,ALARMMINUTE); P.displayText(Buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } void displayOptionOnOffAlarm() { if(ALARMON == 0) P.displayText("> OFF", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); else P.displayText("> ON", PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); } //-------------------------------Day/month int to string methods----------------------------------// char* getDay(int dayofweek) { switch(dayofweek) { case 1: return "MON"; case 2: return "TUE"; case 3: return "WED"; case 4: return "THU"; case 5: return "FRI"; case 6: return "SAT"; case 7: return "SUN"; } } char* getMonth(int monthNo) { switch(monthNo) { case 1: return "JAN"; case 2: return "FEB"; case 3: return "MAR"; case 4: return "APR"; case 5: return "MAY"; case 6: return "JUN"; case 7: return "JUL"; case 8: return "AUG"; case 9: return "SEP"; case 10: return "OCT"; case 11: return "NOV"; case 12: return "DEC"; } } void reset() { SELECT = 0; BACK = 0; SUBSELECT = 0; OPTION = 0; }
Font_Data.h file also to be added to Arduino code:
// Data file for user example user defined fonts #ifndef FONTDATA_H #define FONTDATA_H MD_MAX72XX::fontType_t newFont[] PROGMEM = { 0, // 0 0, // 1 0, // 2 0, // 3 0, // 4 0, // 5 0, // 6 0, // 7 0, // 8 0, // 9 0, // 10 0, // 11 0, // 12 0, // 13 0, // 14 0, // 15 0, // 16 0, // 17 0, // 18 0, // 19 0, // 20 0, // 21 0, // 22 0, // 23 0, // 24 0, // 25 0, // 26 0, // 27 0, // 28 0, // 29 0, // 30 0, // 31 1, 0, // 32 0, // 33 0, // 34 0, // 35 0, // 36 5, 152, 88, 32, 208, 200, // 37 0, // 38 0, // 39 0, // 40 0, // 41 0, // 42 0, // 43 0, // 44 0, // 45 0, // 46 5, 64, 32, 16, 8, 4, // 47 5, 124, 162, 146, 138, 124, // 48 5, 136, 132, 254, 128, 128, // 49 5, 132, 194, 162, 146, 140, // 50 5, 68, 146, 146, 146, 108, // 51 5, 48, 40, 36, 254, 32, // 52 5, 78, 138, 138, 138, 114, // 53 5, 124, 146, 146, 146, 100, // 54 5, 2, 226, 18, 10, 6, // 55 5, 108, 146, 146, 146, 108, // 56 5, 76, 146, 146, 146, 124, // 57 1, 40, // 58 0, // 59 0, // 60 0, // 61 3, 68, 40, 16, // 62 0, // 63 0, // 64 5, 252, 18, 18, 18, 252, // 65 5, 254, 146, 146, 146, 108, // 66 5, 124, 130, 130, 130, 68, // 67 5, 254, 130, 130, 130, 124, // 68 5, 254, 146, 146, 146, 130, // 69 5, 254, 18, 18, 18, 2, // 70 5, 124, 130, 130, 162, 100, // 71 5, 254, 16, 16, 16, 254, // 72 3, 130, 254, 130, // 73 5, 64, 130, 130, 130, 126, // 74 5, 254, 16, 16, 40, 198, // 75 5, 254, 128, 128, 128, 128, // 76 5, 254, 4, 8, 4, 254, // 77 5, 254, 8, 16, 32, 254, // 78 5, 124, 130, 130, 130, 124, // 79 5, 254, 18, 18, 18, 12, // 80 5, 124, 130, 162, 194, 252, // 81 5, 254, 18, 18, 18, 236, // 82 5, 76, 146, 146, 146, 100, // 83 5, 2, 2, 254, 2, 2, // 84 5, 126, 128, 128, 128, 126, // 85 5, 62, 64, 128, 64, 62, // 86 5, 254, 64, 32, 64, 254, // 87 5, 198, 40, 16, 40, 198, // 88 5, 6, 8, 240, 8, 6, // 89 5, 194, 162, 146, 138, 134, // 90 8, 0, 0, 0, 0, 0, 0, 0, 0, // 91 0, // 92 0, // 93 0, // 94 0, // 95 0, // 96 5, 64, 168, 168, 168, 248, // 97 5, 254, 144, 136, 136, 112, // 98 5, 112, 136, 136, 136, 64, // 99 5, 112, 136, 136, 144, 254, // 100 5, 112, 168, 168, 168, 48, // 101 4, 16, 252, 18, 2, // 102 5, 16, 168, 168, 168, 120, // 103 5, 254, 16, 8, 8, 240, // 104 3, 136, 250, 128, // 105 3, 128, 136, 122, // 106 4, 254, 32, 80, 136, // 107 3, 130, 254, 128, // 108 5, 248, 8, 240, 8, 240, // 109 5, 248, 8, 8, 8, 240, // 110 5, 112, 136, 136, 136, 112, // 111 5, 248, 40, 40, 40, 16, // 112 5, 16, 40, 40, 40, 248, // 113 5, 248, 16, 8, 8, 16, // 114 5, 144, 168, 168, 168, 72, // 115 5, 4, 254, 132, 128, 64, // 116 5, 120, 128, 128, 128, 248, // 117 5, 24, 96, 128, 96, 24, // 118 5, 120, 128, 96, 128, 120, // 119 5, 136, 72, 112, 144, 136, // 120 5, 136, 144, 96, 32, 24, // 121 5, 136, 200, 168, 152, 136, // 122 0, // 123 0, // 124 0, // 125 0, // 126 0, // 127 0, // 128 0, // 129 0, // 130 0, // 131 0, // 132 0, // 133 0, // 134 0, // 135 0, // 136 0, // 137 0, // 138 0, // 139 0, // 140 0, // 141 0, // 142 0, // 143 0, // 144 0, // 145 0, // 146 0, // 147 0, // 148 0, // 149 0, // 150 0, // 151 0, // 152 0, // 153 0, // 154 0, // 155 0, // 156 0, // 157 0, // 158 0, // 159 0, // 160 0, // 161 0, // 162 0, // 163 0, // 164 0, // 165 0, // 166 0, // 167 0, // 168 0, // 169 0, // 170 0, // 171 0, // 172 0, // 173 0, // 174 0, // 175 0, // 176 0, // 177 0, // 178 0, // 179 0, // 180 0, // 181 0, // 182 0, // 183 0, // 184 0, // 185 4, 12, 18, 18, 12, // 186 0, // 187 0, // 188 0, // 189 0, // 190 0, // 191 0, // 192 0, // 193 0, // 194 0, // 195 0, // 196 0, // 197 0, // 198 0, // 199 0, // 200 0, // 201 0, // 202 0, // 203 0, // 204 0, // 205 0, // 206 0, // 207 0, // 208 0, // 209 0, // 210 0, // 211 0, // 212 0, // 213 0, // 214 0, // 215 0, // 216 0, // 217 0, // 218 0, // 219 0, // 220 0, // 221 0, // 222 0, // 223 0, // 224 0, // 225 0, // 226 0, // 227 0, // 228 0, // 229 0, // 230 0, // 231 0, // 232 0, // 233 0, // 234 0, // 235 0, // 236 0, // 237 0, // 238 0, // 239 0, // 240 0, // 241 0, // 242 0, // 243 0, // 244 0, // 245 0, // 246 0, // 247 0, // 248 0, // 249 0, // 250 0, // 251 0, // 252 0, // 253 0, // 254 0, // 255 }; #endif
Comments:
Hi great clock, just breadboarded it and might make it an actual for my workshop but with a 16x64 matrix. Not much of a coder and have been trying to make it change back to time after a minute on the other options and scroll through time, date, temp and humidity as an option. Also would like 24 hour time. Any ideas on how to change my code to suit?
Thanks Weka, Could not get you question 1 but for changing it to 24hours clock you change where I'm displaying time:
Just remove calculation of hours and display as it is:
You may have to make this change at couple of places.
With breadboard setup will become more cleaner and less messy. Good luck with clock, let me also know once you complete it.
Thanks. Only had to change the code there. Looks like Ill have to get a nano to shrink the form factor as my pro minis' havent got enough power to handle the matrix, temp sensor, RTC and buzzer at once
Thanks thank you, i managed to do everything here very well. But I wanted to change the resolution to 16x64 and if possible change it to message and clock message clock thank you
Thanks Hugo,
You can buy 16*64 device and connect(I think you just need to change MAX_DEVICES variable value to 16) but check if that requires additional power supply, Arduino might not be able supply required current.
Error compiling for board Arduino Nano any solution
Hi Saadi,
All the files are available to download in zip, may I know what is the error you are facing?
Hi Sir,
I have been trying this clock project and it works well except the clock's buzzer is always make a sound even I turn it off(I mean that I turn the Alarm to "OFF" position.but every time when I connect the power cord to the clock(Arduino board),the buzzer always make sound(not beeping sound but constant non-stop sound).
Could you help me with that error.Please.
Hi Phone,
Most probably you would have connected buzzer to some wrong pin, I have buzzer with 3 pins. Alarm pin in my code is D5. Rest pins you have to connect to GND and VCC.
If your buzzer has only 2 pins then you connect Negative to GND and Positive to pin 5.
Thank you for your answer Sir.I made it now.
Can I ask another one question Sir,
How can I make the two dots ":" between Hour and minutes blinking.I mean ,for example, the clock display like this 12:25 twelve hour and twenty five minutes.So ,how can I make the two dots between 12 and 25 blinking.Could you help me with that Sir,Please.
Can I use DS1307 clock module instead of DS1302 ? (If I want to use just clock part of the code and not the temperature and humidity sensor)
Hi Jagannath,
Not sure if library virtuabotixRTC would work with DS1307. You might have to change the code wherever this library methods are used.
Hi mate, i just have one problem. When i unplug the clock and plug it again, it will rewrite my set up day, date and time back to 24 : 00, MON, JAN 1. Can you please help me?