
A real-time clock (RTC) chip is basically just like a watch โ it runs on a battery and keeps time for you even when there is a power outage. Using an RTC in your project, you can keep track of long timelines, even if you reprogram your microcontroller or disconnect it from USB or its power plug.
Most microcontrollers, including the Arduino, have a built-in timekeeper function called millis() and there are also timers built into the chip that can keep track of longer time periods like minutes or days. So why would you want to have a separate RTC chip? Well, the biggest reason is that millis() only keeps track of time since the Arduino was last powered. That means that when the power is turned on, the millisecond timer is set back to 0. The Arduino doesn’t know that itโs โTuesdayโ or โMarch 8th,โ all it can tell is โItโs been 14,000 milliseconds since I was last turned on.โ
OK, so what if you wanted to set the time on the Arduino? Youโd have to program in the date and time, and you could have it count from that point on. But if it lost power, youโd have to reset the time like a cheap alarm clock: every time they lose power they blink 12:00.
While this sort of basic timekeeping is OK for some projects, other projects such as data loggers, clocks, etc., need to have consistent timekeeping that doesnโt reset when the Arduinoโs battery dies or itโs reprogrammed. Thus, we include a separate RTC! The RTC is a specialized chip that just keeps track of time. It can count leap years, and it knows how many days are in each month. Just note that it doesnโt take care of Daylight Savings Time โ because that changes from place to place, youโll have to code that for your specific region or preference.
Choosing an RTC
Three real-time clocks are commonly used by makers: the PCF8523, DS1307, and DS32231. These are great battery-backed RTCs that are good for data logging, clock building, time stamping, timers, and alarms. Each communicates via the two-wire I2C protocol. As long as it has a coin cell to run it, your RTC will merrily tick along for years.
ยป The PCF8523 is not high-precision โ it may lose or gain up to 2 seconds a day โ but itโs the least expensive of the three. And it works with 3.3V or 5V power and logic.
ยป The DS1307 is the most common; itโs also not high-precision but it is low-cost and easy to solder. It works best with 5V-based microcontrollers like Arduino; it requires 5V power, though it can work with 3.3V logic.
ยป The DS3231 datasheet explains that this temperature-compensated chip is an โExtremely Accurate IยฒC-Integrated RTC/TCXO/Crystal.โ And, hey, it does exactly what it says on the tin! This RTC is the most precise you can get in a small, low-power package, and it works with 3.3V or 5V boards.
Using the DS3231
Most RTCs use an external 32kHz timing crystal to keep time with low current draw. Thatโs all well and good, but those crystals have a slight drift, particularly when the temperature changes โ the temperature affects the oscillation frequency very, very slightly but it does add up. The DS3231 is in a beefy package because the crystal is inside the chip! And right next to the integrated crystal is a temperature sensor. That sensor compensates for the frequency changes by adding or removing clock ticks so that the timekeeping stays on schedule.
Adafruit offers the DS3231 in a compact, breadboard-friendly breakout board. With a CR1220 coin cell plugged into the back, you can get years of precision timekeeping, even when main power is lost. You can solder the included header to plug it into a breadboard, or just solder wires directly to the pads.
DS3231 Pinout
Power Pins
โข Vin โ The power pin. Since the RTC can be powered from 2.3V to 5.5V power, you donโt need a regulator or level shifter to use 3.3V or 5V logic/power. Just give this board the same power as the logic level of your microcontroller โ e.g., for a 5V micro like Arduino, use 5V.
โข GND โ Common ground for power and logic.
I2C Logic Pins
โข SCL โ I2C clock pin, connect to your microcontrollerโs I2C clock line. This pin has a 10K pullup resistor to Vin.
โข SDA โ I2C data pin, connect to your microcontrollerโs I2C data line. Also has a 10K pullup resistor to Vin.
Other Pins
โข BAT โ This is the same connection as the positive pad of the battery. Use this if you want to power something else from the coin cell, or provide battery backup from a separate battery. VBat can be between 2.3V and 5.5V and the DS3231 will switch over when main Vin power is lost.
โข 32K โ The 32kHz oscillator output. Open drain, so you need to attach a pullup to read this signal directly from a microcontroller pin.
โข SQW โ Optional square wave or interrupt output. Again, open drain, attach a pullup to read this signal from a microcontroller pin.
โข RST โ This one is a little different than most RST pins; rather than being just an input, itโs designed to be used to reset an external device or indicate when main power is lost. Open drain, but has an internal 50K pullup. The pullup keeps this pin voltage high as long as Vin is present. When Vin drops and the chip switches to battery backup, this pin goes low.
Arduino Usage
You can easily wire this breakout to any microcontroller; here we’ll use an Arduino (Figure TK). For other microcontrollers, just make sure it has I2C, then port the code โ itโs pretty simple stuff!
Connect Vin to the power supply, 3Vโ5V is fine. Use the same voltage as your microcontroller logic; for most Arduinos, thatโs 5V.
Connect GND to common power/data ground.
Connect the SCL pin to the I2C clock SCL pin on your Arduino. On an Uno and other Atmega328-based Arduinos, this is also known as A5; on a Mega itโs digital 21; and on a Leonardo or Micro, digital 3.
Connect the SDA pin to the I2C data SDA pin on your Arduino. On an Uno and other 328s this is also known as A4; on a Mega itโs digital 20; and on a Leonardo/Micro, digital 2.
IMPORTANT: The DS3231 has a default I2C address of 0x68 and cannot be changed.
Download RTClib
To begin using your DS3231, youโll need an Arduino library for getting and setting time from an RTC. Weโre using a fork of JeeLab’s excellent RTClib โ our version is slightly different, so please only use ours with this DS3231 breakout to make sure itโs compatible!
Download Adafruitโs RTClib from our Github repository at github.com/adafruit/RTClib.
Rename the uncompressed folder RTClib and check that theย RTClib folder contains RTClib.cpp and RTClib.h.
Place theย RTClib library folder in your [arduinosketchfolder]/libraries/ folder. (You may need to create the libraries subfolder if itโs your first library.)
Restart the IDE.
We also have a great tutorial on Arduino library installation on Adafruit.com
Your First RTC Test
The first thing weโll demonstrate is a test sketch that will read the time from the RTC once per second. Weโll also show what happens if you remove the battery and replace it, since that causes the RTC to halt. So to start, remove the battery from the holder (Figure TK) while the Arduino is not powered or plugged into USB. Wait 3 seconds and then replace the battery. This resets the RTC chip.
Load Demo Sketch
In the Arduino IDE, open up FileโExamplesโRTClibโds3231 and upload it to your Arduino, wired up to the RTC.
Now check the Serial Monitor console at 9600 baud. After a few seconds, youโll see the report that the Arduino noticed this is the first time the DS3231 has been powered up, and will set the time based on the Arduino sketch.
Unplug your Arduino plus RTC for a few seconds (or minutes, or hours, or weeks) and plug back in.
Next time you run it you won’t get the same “RTC lost power” message, instead it will come immediately and let you know the correct time!
From now on, you wonโt have to set the time again. The battery will last 5 or more years.
Reading the Time
Now that the RTC is merrily ticking away, weโll want to query it for the time. Letโs look at the sketch again to see how this is done.
void loop () { DateTime now = rtc.now(); Serial.print(now.year(), DEC); Serial.print('/'); Serial.print(now.month(), DEC); Serial.print('/'); Serial.print(now.day(), DEC); Serial.print(" ("); Serial.print(daysOfTheWeek[now.dayOfTheWeek()]); Serial.print(") "); Serial.print(now.hour(), DEC); Serial.print(':'); Serial.print(now.minute(), DEC); Serial.print(':'); Serial.print(now.second(), DEC); Serial.println();
Thereโs pretty much only one way to get the time using the RTClib, which is to call now(), a function that returns a DateTime object that describes the year, month, day, hour, minute, and second when you called now().
There are some RTC libraries that instead have you call something like RTC.year() and RTC.hour() to get the current year and hour. However, there’s one problem where if you happen to ask for the minute right at 3:14:59 just before the next minute rolls over, and then the second right after the minute rolls over (so at 3:15:00) youโll see the time as 3:14:00 which is a minute off. If you did it the other way around you could get 3:15:59 โ so one minute off in the other direction.
Because this is not an especially unlikely occurrence โ particularly if youโre querying the time pretty often โ we take a โsnapshotโ of the time from the RTC all at once and then we can pull it apart into day() or second() as seen above. Itโs a tiny bit more effort but we think itโs worth it to avoid mistakes!
We can also get a โtimestampโ out of the DateTime object by calling unixtime() which counts the number of seconds (not counting leap seconds) since midnight, January 1st 1970:
Serial.print(" since midnight 1/1/1970 = "); Serial.print(now.unixtime()); Serial.print("s = "); Serial.print(now.unixtime() / 86400L); Serial.println("d");
Since there are 60 * 60 * 24 = 86,400 seconds in a day, we can easily count days since then as well. This can be useful when you want to keep track of how much time has passed since the last query, making some math a lot easier. For instanced, to check if itโs been 5 minutes later, just see if unixtime() has increased by 300 and you donโt have to worry about hour changes.
Going Further
CircuitPython โ Itโs easy to use the DS3231 RTC with CircuitPython too!ย Thereโs a handy module on Github you can load on a board and get started setting and reading the time with Python code. Simply import the DS3231 module, create an instance of the class, and interact with its datetime property to set and get the time (Figure TK)! Learn more here.
Raspberry Pi โ The ultra-low-cost Pi lacks an RTC; it fetches the time from the internetโs NTP (Network Time Protocol) servers instead. For stand-alone projects, itโs easy to connect an RTC. Make sure youโre running an updated kernel with RTC drivers, enable I2C, and add your RTC to /boot/config.txt, e.g. dtoverlay=i2c-rtc,ds3231. Then remove the fake-hwclock package and edit the real hardware clock script /lib/udev/hwclock-set to use your RTC instead. Learn more here.
ADVERTISEMENT