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.

Here’s an older computer motherboard with a Dallas Semiconductor real-time clock (RTC) called the DS1387. There’s a lithium battery in there, which is why it’s so big. ; CREDIT: Wikimedia Commons

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.

If you’ll deploy your RTC somewhere cooler than cool, check out Chris Fastie’s comparison of how four variants of the DS3221 chip (and their batteries) perform at very low temperatures.

 

 

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!

Oops, I removed the power wire from Arduino 5V pin to the breadboard Vin rail before taking this pic, don’t forget it!

 

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.