We are all aware that most MCUs used in our projects are time-agnostic; that is, they are unaware of the time around them. It’s fine for most of our projects, but every now and then, when you come across an idea where keeping time is critical, the DS1307 precision RTC module comes in handy. It is suitable for a wide range of projects, including clocks, timers, and alarms, as well as data logging and time stamping.
Hardware Overview
This module is built around the highly capable DS1307 RTC chip and the AT24C32 EEPROM, both of which have been around for a while and have good library support.
DS1307 RTC Chip
At the heart of the module is a low-cost, extremely accurate RTC chip from Maxim — the DS1307. It handles all timekeeping functions and communicates with the microcontroller over I2C.
The DS1307 can keep track of seconds, minutes, hours, days, dates, months, and years. It can work in either a 12-hour or 24-hour format and has an AM/PM indicator. For months with fewer than 31 days, it automatically adjusts the date at the end of the month, including leap year corrections (valid up to 2100).
Another interesting feature of the DS1307 is the SQW pin, which can be programmed to output one of four square-wave frequencies: 1Hz, 4kHz, 8kHz, or 32kHz.
The DS1307 requires an external 32kHz crystal for timekeeping, the frequency of which is easily affected by external temperature. Even though this change in frequency is negligible, it does add up.
It might seem like a problem, but it’s not. It actually causes the clock to be off by about two minutes per month.
Battery Backup
The DS1307 IC incorporates a battery input for maintaining accurate timekeeping even when the device’s main power is interrupted.
The built-in power-sense circuit continuously monitors the status of VCC to detect power failures and automatically switches to the backup supply. This means that even in the event of a power outage, the IC can still keep time.
For backup power, the bottom of the board houses a battery holder for a 20mm 3V lithium coin cell. Any CR2032 battery will fit nicely here.
Assuming you use a fully charged 47mAh coin cell battery and keep the chip current draw to a minimum of 300nA, the battery should be able to power the RTC for at least 17 years without the need for an external power supply.
47mAh/300nA = 156666.67 hours = 6527.78 days = 17.87 years
Onboard 24C32 EEPROM
The DS1307 RTC module also includes a 32-byte (4K x 8-bits) AT24C32 EEPROM (non-volatile) chip with 1,000,000 write cycles. This chip doesn’t really have anything to do with the RTC, but it can be useful for things like data logging or storing any other data that you want to be non-volatile.
The 24C32 EEPROM communicates via I2C and shares the same I2C bus as the DS1307.
The onboard 24C32 EEPROM has a fixed I2C address of 0x50.
The AT24C32 EEPROM is rated for 1,000,000 write cycles, so it won’t wear out during normal data logging applications as long as you’re not writing data every second.
Module’s Hidden Feature – DS18B20
The DS1307 RTC module has a provision for installing a DS18B20 temperature sensor that often goes unnoticed.
The DS18B20 can be installed using the three mounting holes in the upper right corner, close to the battery holder (marked U1).
After installing the DS18B20, you will be able to obtain temperature readings from the DS pin. These readings can be used to compensate for temperature-based time drift.
Solder the DS18B20 according to the polarity shown on the silk screen. You may also require a 4.7K resistor between VCC and DS.
I2C Interface
The module has a simple I2C interface that takes up two addresses. The DS1307 RTC chip’s fixed I2C address is 0x68, and the 24C32 EEPROM’s fixed I2C address is 0x50.
The SDA and SCL signals, as well as power and ground, are also broken out to the other side of the module to allow these signals to be looped out to another module.
To enable communication, both the SDA and SCL lines have 4.7K pull-up resistors.
Technical Specifications
Here are the specifications:
Operating Voltage | 4.5 to 5.5V (5V typical) |
Current Consumption | < 1.5mA (typ.) |
Accuracy (0-40°C) | Depends on crystal (± 20ppm typ.) |
Battery | CR2032 (3V Coin) |
For more information on the DS1307 RTC and the 24C32 EEPROM chip, please refer to the datasheets listed below.
DS1307 RTC Module Pinout
The DS1307 RTC module has 7 pins in total. The pinout is as follows:
SQW pin outputs one of four square-wave frequencies: 1Hz, 4kHz, 8kHz, or 32kHz.
DS pin should provide temperature readings if a DS18B20 temperature sensor is installed using the three mounting holes in the upper right corner, close to the battery holder (marked U1).
SCL is a serial clock pin for the I2C interface.
SDA is a serial data pin for the I2C interface.
VCC provides power to the module. You can connect it to a 5 volt power supply.
GND is the ground pin.
BAT is a backup supply input for any standard 3V lithium cell or other energy source to keep accurate time when the device’s main power is interrupted.
Wiring a DS1307 RTC Module to an Arduino
Let’s connect the RTC to the Arduino.
Connections are straightforward. Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.
Now we are left with the pins that are used for I2C communication. Note that each Arduino board has different I2C pins that must be connected correctly. Â On Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also referred to as A5 (SCL) and A4 (SDA).
Check out the table below for quick reference.
SCL | SDA | |
Arduino Uno | A5 | A4 |
Arduino Nano | A5 | A4 |
Arduino Mega | 21 | 20 |
Leonardo/Micro | 3 | 2 |
The diagram below shows how to connect everything.
Installing uRTCLib Library
It takes a lot of effort to communicate with an RTC module. Fortunately, the uRTCLib library was created to hide all of the complexities, allowing us to issue simple commands to read the RTC data.
It is a simple yet powerful library that also supports programming the SQW output, which is not supported by many RTC libraries.
To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.
Filter your search by entering ‘urtclib’. Look for uRTCLib by Naguissa. Click on that entry and then choose Install.
At the end of the tutorial, we’ve also included code for reading and writing the onboard 24C32 EEPROM. If you’re interested, you’ll need to install the uEEPROMLib library. Look for ‘ueepromlib‘ and install it as well.
Arduino Code – Reading Date and Time
This is a simple sketch for setting/reading the date and time from the DS1307 RTC module.
#include "Arduino.h"
#include "uRTCLib.h"
// uRTCLib rtc;
uRTCLib rtc(0x68);
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
void setup() {
Serial.begin(9600);
delay(3000); // wait for console opening
URTCLIB_WIRE.begin();
// Comment out below line once you set the date & time.
// Following line sets the RTC with an explicit date & time
// for example to set January 13 2022 at 12:56 you would call:
rtc.set(0, 56, 12, 5, 13, 1, 22);
// rtc.set(second, minute, hour, dayOfWeek, dayOfMonth, month, year)
// set day of week (1=Sunday, 7=Saturday)
}
void loop() {
rtc.refresh();
Serial.print("Current Date & Time: ");
Serial.print(rtc.year());
Serial.print('/');
Serial.print(rtc.month());
Serial.print('/');
Serial.print(rtc.day());
Serial.print(" (");
Serial.print(daysOfTheWeek[rtc.dayOfWeek()-1]);
Serial.print(") ");
Serial.print(rtc.hour());
Serial.print(':');
Serial.print(rtc.minute());
Serial.print(':');
Serial.println(rtc.second());
delay(1000);
}
Here’s what the output looks like:
Code Explanation:
The sketch begins by including the Arduino.h and uRTCLib.h libraries for communicating with the module. We then create an uRTCLib library object and define the daysOfTheWeek
2D character array to store the days information.
To interact with the RTC module, we use the following functions in the setup and loop sections of the code:
begin() function ensures that the RTC module is properly initialized.
set(ss, mm, hh, day, dd, mm, yy) function sets the RTC to an explicit date and time. For example, to set January 13, 2022 at 12:56, you would call: rtc.set(0, 56, 12, 5, 13, 1, 22);
refresh() function refreshes data from the HW RTC.
year() function returns the current year.
month() function returns the current month.
day() function returns the current day.
dayOfWeek() function returns the current day of the week (1 to 7). This function is typically used as an index for a 2D character array that stores information about the days.
hour() function returns the current hour.
minute() function returns the current minute.
second() function returns the current seconds.
Arduino Code – Read/Write the 24C32 EEPROM
As an added bonus, the DS1307 RTC module includes 32 bytes of Electrically Erasable ROM. Its contents will not be erased even if the device’s main power is interrupted.
This is a simple sketch that attempts to write an integer, float, character, and string to the 24C32 EEPROM and then reads them back. This sketch can be extended to save settings, passwords, or just about anything.
#include "Arduino.h"
#include "Wire.h"
#include "uEEPROMLib.h"
// uEEPROMLib eeprom;
uEEPROMLib eeprom(0x50);
void setup() {
Serial.begin(9600);
delay(2500);
Wire.begin();
int inttmp = 32123;
float floattmp = 3.1416;
char chartmp = 'A';
char c_string[] = "lastminuteengineers.com"; //23
int string_length = strlen(c_string);
Serial.println("Writing into memory...");
// Write single char at address
if (!eeprom.eeprom_write(8, chartmp)) {
Serial.println("Failed to store char.");
} else {
Serial.println("char was stored correctly.");
}
// Write a long string of chars FROM position 33 which isn't aligned to the 32 byte pages of the EEPROM
if (!eeprom.eeprom_write(33, (byte *) c_string, strlen(c_string))) {
Serial.println("Failed to store string.");
} else {
Serial.println("string was stored correctly.");
}
// Write an int
if (!eeprom.eeprom_write(0, inttmp)) {
Serial.println("Failed to store int.");
} else {
Serial.println("int was stored correctly.");
}
// write a float
if (!eeprom.eeprom_write(4, floattmp)) {
Serial.println("Failed to store float.");
} else {
Serial.println("float was stored correctly.");
}
Serial.println("");
Serial.println("Reading memory...");
Serial.print("int: ");
eeprom.eeprom_read(0, &inttmp);
Serial.println(inttmp);
Serial.print("float: ");
eeprom.eeprom_read(4, &floattmp);
Serial.println((float) floattmp);
Serial.print("char: ");
eeprom.eeprom_read(8, &chartmp);
Serial.println(chartmp);
Serial.print("string: ");
eeprom.eeprom_read(33, (byte *) c_string, string_length);
Serial.println(c_string);
Serial.println();
}
void loop() {
}
Here’s what the output looks like:
When reading/writing the EEPROM, keep in mind that different data types take up different amounts of memory. For example, a character is one byte, an integer is two bytes, and a float is four bytes.
In other words, if you’re storing a character, it can be written to each memory location (0, 1, 2, 3, 4, 5….).
However, if you are storing an integer, you must reserve two memory locations for each value, so you will want to store data in every other memory location, such as (0,2,4,6….).