In this age of WiFi and the Internet of Things, clocks should set themselves and be super accurate. Perhaps you’ve built an Arduino alarm clock in the past - you’ll be pleasantly surprised to see how easy it is to do this stuff with the newer Arduino processors and libraries.
The same thing can be said for time displays. With I2C interfaces, 7 segment and LCD displays can be used almost effortlessly to display time and date. We will look at some examples.
BackgroundIn this tutorial, I’m using the Arduino MKR WiFi 1010 processor to demonstrate how easy it is to implement super accurate clocks with equally easy to use displays. The MKR WiFi 1010 has built-in WiFi and a built-in RTC (real time clock). The WiFiNina library used with the MKR 1010 has the capability of obtaining NTP (network time protocol) time. And the RTCZero library can use NTP time to set the built-in RTC. The result is a clock that is accurate to within a few milliseconds using nothing but WiFi and the processor itself!
The most common way to express the time on computer networks is called Unix time or Epoch time. It is expressed as the number of seconds since 0:00:00 on January 1, 1970. Since 1985, a network of servers has provided the Internet with a time service called Network Time Protocol or NTP. It is linked to atomic clocks and the super accurate GPS clock system. Computers do not always respond instantly and communication between them is limited by the speed of light, so keeping this network of time servers perfectly synchronized is complicated. But the net result is that anyone with an Internet connection can obtain the Epoch time accurate to within a few milliseconds of Coordinated Universal Time (UTC) – the super accurate world time standard.
Epoch time has to be converted to month, date, year, hour, minute, and second. Fortunately, the RTCZero library knows how to make this conversion and does it all for us. Epoch time, like other time standards, is based on the GMT time zone of Greenwich, England, so it has to be adjusted to local time zone. We have to adjust for the time zone ourselves.
RTCZero converts Epoch time to a 24 hour clock. But in the US, we typically use a 12 hour clock. Most other places use the 24 hour clock. In the U.S, we use month/day, while most other places use day/month. So the sketches included in this tutorial all have some User Settings where, in addition to your WiFi credentials and Time Zone, you can also specify 12 or 24 hour and the date ordered as DMY or MDY.
Four ProjectsSo far, we talked mostly about getting super accurate self-setting clocks, and adjusting them to our local time zone. But now let’s talk hardware and implementation. This tutorial is four projects in one. It is intended as a guide for your own easy clock building. Whether you need a sprinkler timer or whatever, you can incorporate these super easy clocks! Our four projects are:
1) Use the MKR WiFi 1010 to build a super accurate, self-setting clock. Then display the time and date on the Serial Monitor corrected to your time zone. We'll explain how the time is obtained and how it's adjusted for time zone.
2) Use the clock above to display the time on a 4 digit seven segment LED display in either 24 hour or 12 hour mode. A few years ago, it was a real pain to wire-up 4 seven segment displays to display the time. Programing them to display the time was a lot of work as well. Now we can buy a 4 digit display with an I2C interface (2 wires). The one I bought uses the HT16K33 interface chip, and of course there is a library for that, so just give it the hour and minutes and they display.
3) Perhaps you’d rather display the time with the month and date on a LCD display. We use the clock in 1) with a 2 x 16 LCD display to display time including seconds, with month and date on the second line. And again, we use I2C and there is a library to make interfacing it to an Arduino super easy.
4) Although there are already hundreds of alarm clock projects posted here, we can’t have a time tutorial without an alarm clock, so here we add an alarm clock to the clock in item 2) above. For that we need to add some buttons, an LED and a buzzer.
Each project has its own sketch, but they are all similar, all taking advantage of WiFi and NTP to set the time and keep it accurate to within a few milliseconds.
PLEASE NOTE: The MKR WiFi 1010 has a 3.3 volt processor with 3.3 volt logic levels. Everything shown in our 4 projects runs fine at 3.3 volts, including the 5 volt LCD display. Don’t try to mix 5 volt logic with the 3.3 volt digital pins on this processor!
Project 1 – The Super Accurate Self-setting ClockThis project doesn’t use any hardware other than the MKR WiFi 1010. We display the time on the serial monitor. So we will discuss here how the software works. This software is used in all four projects, but will only be discussed in detail here.
Before uploading the Project 1 sketch to the MKR WiFi 1010, you need to set up the User Settings. Supply a WiFi network and password, and then specify your time zone as a positive or negative deviation from GMT. I have not explicitly dealt with daylight savings, so you will need to include it in the deviation if applicable. You can also specify a 12 or 24 hour clock, and whether you like the date order as DMY or MDY.
As we have already said, once a WiFi connection is established, WiFiNina gets the NTP time from the nearest NTP server. An instance of RTCZero takes the NTP time and sets the built-in RTC. The RTC stores the GMT time zone time and we don’t change the RTC for time zone changes. Instead we modify our own time and date parameters to account for time zone.
There are libraries that deal with time zones and I could use one of those, but I decided to go it alone, and write a single subroutine/function that deals with all the issues at once.
Time zone changes actually complicate things a lot. We normally think changing time zones only involves hours. But under some conditions, it may change the day as well. Changing the day may cause the month to change, and if it changes backwards, we need to know the last day of the previous month. And on New Year’s Eve, it might even effect the year. And Leap years adds yet another issue.
Fixing the hours is pretty easy. We add or subtract our time zone. If adding exceeds 24 hours, we subtract 24. If subtracting goes negative, we add 24 hours. Fixing the day of the month is pretty easy too. If I subtract 24 hours, I need to increment the day. If I add 24 hours, I need to decrement the day. But incrementing the day on the last day of month causes the month to increment. And to deal with that, I have to know that our day is the last day, so I need a table with the days in each month. Similarly, decrementing the day on the first day of the month causes the month to decrement and the day to be the last day of the previous month.
That still leaves leap year to deal with, so I’ve changed February to 29 days on leap years. I also had to check if it’s Dec 31st or Jan 1st, because time zone changes on those may cause the year to change.
All of that is built into a single subroutine called fixTimeZone() that takes GMT from the RTC and changes it to your local time zone. It runs once per minute when seconds are at 0. (It is hard to thoroughly test this routine as there are so many different situations to test - I think it all works, but if you find I missed something, let me know.)
How often do we need to get Epoch time from NTP? Real time clocks are pretty accurate, so if you just need to be accurate to the second, once every 24 hours would probably be sufficient. I am getting Epoch time once every hour! At 59 minutes and 00 seconds every hour, I go get NTP again.
For this first project, there is no display device. We simply send the date and time to the serial monitor once per second. Nothing complicated here, but we do need to add leading 0s to get the time to look like it normally does i.e. 03:06:00 rather than 3:6:0.
Project 2 – A 4 Digit 7 Segment LED ClockThe Adafruit display I chose uses the HT16K33 chip for its I2C interface. There is a library for the HT16K33 which makes displaying the time very easy. In setup(), we setDigits to 4 and turn the display on. In loop(), to display time, we simply say displayTime(hours, minutes). The colon is blinked by displayColon(1) where 1 (true ) is on and 0(false) is off. Everything else in this sketch is derived from the sketch in project 1. The display works great at 3.3 volts, and with the I2C interface, there are only 4 wires: 3.3 v, Ground, Data, and Clock. So adding this display to show the time was extremely easy.
From a hardware standpoint, this project is just as easy as the previous one. The 16 x 2 LCD display is controlled by the Arduino again with only a 2 wire I2C interface! I was pleasantly surprised when this supposedly 5 volt LCD display worked perfectly from the MKR WiFi 1010’s 3.3 volt logic levels whether powered from 3.3 volts or 5 volts. I preferred using 5 volts for Vcc as the display is a little brighter. You do need to adjust the contrast potentiometer (on the back) to make the display visible.
Software for this display was very easy as well. A library called LCD_I2C did all the work. In setup(), we have to start it and turn on the backlight. In loop(), the display is updated with setCursor and print commands. The print command wants to output character strings, so I used dtostrf to convert numbers to text strings. And as discussed in Project 1, I needed to add leading 0s to get 03:06:00 rather than 3:6:0.
I wanted to see the month written out as its abbreviation rather than as a number, so I added a routine to perform that translation. There is also a little logic to allow 12 or 24 clocks and DMY or MDY date configuration.
This project takes our 4 digit 7 Segment LED Clock and adds an alarm clock to it. Finally, for this last project, we have a little hardware to talk about. We have added four buttons, a red LED, a 180 ohm resistor, a 3 volt beeper, and an NPN transistor.
The four buttons are for Set Alarm, Increment Alarm Hours, Increment Alarm Minutes, Arm/Dis-Arm Alarm - similar to a lot of alarm clocks, so fairly intuitive. The LED (through the 180 ohm resistor) shows when the alarm is armed. The beeper of course sounds the alarm. The MKR WiFi 1010’s output pins supply a maximum of 7 ma., so we need the NPN transistor to get the 25ma required by the beeper. We would usually use a resistor to limit the base current of the transistor, but with a maximum drive current of 7 ma., I decided to leave it out for simplicity.
There is a lot of additional software to implement the alarm clock, but I am not going to go into a lot of detail here. This tutorial is mainly about self-setting accurate clocks, and there are hundreds of tutorials on alarm clocks. I basically took the sketch for the 7 segment display clock and added four subroutines to handle the alarm - one to set the alarm, one to check for a match and sound the alarm, and two for checking the buttons. The first button check is for closure of the Set Alarm or Arm/Dis-Arm buttons. If one is pushed, it delays ½ second to de-bounce and sets a flag. If no switch closure is detected, it returns after 10 ms. The other one simply combines a bunch of switch checks to form a specified time delay while continuously monitoring the switches. You can hold down the hour and minute buttons and the numbers will increase automatically.
One additional point on software is that I restricted this version to a 24 hour clock only. That way I don’t need another LED for AM/PM and I don’t need to deal with AM/PM in the alarm software. It would be an easy addition if you need it. For additional details on the alarm software, please see the attached sketch.
ConclusionSo we have a super accurate self-setting clock implemented with the MKR WiFi 1010. And in our four projects, I have shown some fairly easy ways to make use of it. Hope this will help you with your own clock and timer applications.
Comments