DS1307 Real Time Clock Module with Arduino.
Real time clocks are required in applications where we need to keep track of the current time for example in data logging. In this tutorial I will look at the DS1307 RTC which is one of the common Real Time Clocks, the other one being DS3231 RTC.
Most microcontrollers, including the Arduino, have a built-in timer that can keep track of longer time periods like minutes or days. However, this timer only keeps track of time since the microcontroller was last powered which means that whenever the power is turned off, the timer is set back to zero!
Real time Clocks on the other hand are simply watches that keep track of date and time even when the power supply to the microcontroller is turned off because they have power backups.
DS1307 RTC Hardware overview
The DS1307 RTC module (Tiny RTC) comes with I/O pins on either side. These pins configuration is similar and you can use whichever side you prefer depending on the application.
The pinout of the DS1307 RTC is as follows;
- DS -Device Select and is used when a temperature sensor is attached to the RTC to output the temperature readings.
- SCL – Serial clock input for the I2C interface
- SDA – Serial data input/output for the I2C serial interface.
- VCC – 3.3V to 5V power supply for the module.
- GND – Ground pin.
- BAT – Backup 3V power supply for keeping track of time when the main power supply is turned off.
- SQW – For outputting square-wave frequencies of 1Hz, 4kHz, 8kHz or 32kHz.
The major components of this RTC module include;
- DS1307 RTC chip: This is the main IC for the device and keeps track of time in both 12-hour and 24-hour format, date, days of the week, months and years with accuracy of one second. It even does automatic adjustment of number of days in a month and leap years.
- 32kHz crystal oscillator for keeping time.
- 24C32 EEPROM chip with 32bytes storage space. This IC also uses I2C interface and shares the I2C bus with the DS1307 RTC IC.
- There is also a provision for attaching a DS18B20 Temperature sensor.
This DS1307 RTC module has a CR2032 3V Lithium coin cell battery attached to a built-in power-sense circuit that detects power off at the main power supply and automatically switches to the backup supply in order not to disrupt the time keeping process.
Since the Crystal Oscillator is external. its oscillation frequency is affected by external temperature which in the long run leads to loss of some minutes in the time keeping process. This is a major downside of using the DS1307 RTC as you may need to reset the time after some period to do correction for the lost minutes.
Connecting the DS1307 RTC to Arduino
As the DS1307 RTC uses I2C communication, connecting it to Arduino is done as follows;
GND and VCC are connected to the corresponding Ground and 5V pins of the Arduino.
SCL and SDA are connected to the I2C pins of the Arduino in use. Note that I2C communication is supported at specific pins depending on the type of Arduino but in this example am using Arduino UNO therefore the I2C pins are A5 and A4 for SCL and SDA respectively.
Installing the necessary libraries in the Arduino IDE.
There are a number of libraries that have been developed to be able to use the DS1307 RTC with Arduino. I’ll use the RTClib library from Adafruit which can be installed from the Arduino IDE Library Manager.
Go to Tools>Manage Libraries… and install the RTClib library as illustrated below.
Code for Setting Time for the DS1307 RTC.
To set the time of the RTC, you can use the ds1307 example from RTClib library.
Go to File>Examples> RTClib> ds1307
Code for the ds1307 example.
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
void setup () {
Serial.begin(57600);
#ifndef ESP8266
while (!Serial); // wait for serial port to connect. Needed for native USB
#endif
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
abort();
}
if (! rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
}
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();
Serial.print(" since midnight 1/1/1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");
// calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
DateTime future (now + TimeSpan(7,12,30,6));
Serial.print(" now + 7d + 12h + 30m + 6s: ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();
Serial.println();
delay(3000);
}
The Wire.h library is needed for I2C communication and the RTClib.h library contains the classes and methods that enable us to read data from the RTC module.
Then an object rtc is created from the RTC_DS1307 class which is part of the RTClib library. Also a 2D character array for storing the days of the week is defined.
In the setup section, we initialize the RTC module using the begin() method and the isrunning() method is for preparing the DS1307 chip to reset the time.
The adjust() function is for setting the date and time. This is an overloaded function.
In order to set the date and time of the RTC to correspond to the time at which the code sketch was compiled, we use the statement below.
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
The date and date set using the above method will correspond to that on your computer.
To be able to set a specific date and time you can use;
rtc.adjust(DateTime(YYYY, M, D, H, M, s));
For example to set January 28 2020 at 12:56 this would be: rtc.adjust(DateTime(2020, 1, 28, 12, 56, 0));
After setting the desired time these functions should be commented out of the code sketch to avoid repetitive resetting of the time and date whenever you want to upload a new program
The loop section contains a number of methods from the DateTime class including;
- now() method returns current date and time.
- year() , month() and day() functions return the current year, month and day respectively.
- dayOfTheWeek() function returns current day of the week.
- hour(),minute() and second() functions return current hour, minutes and seconds respectively
- unixtime() function returns unix time in seconds.
- TimeSpan() function is used to add or subtract time to or from the current time. For example now() + TimeSpan(seconds) returns the future time with seconds added into current time.
When this code is uploaded to the Arduino, you will be able to observe the date and time that has been set and other applications for future time.
The ds1307 example can be used for setting the date and time of the RTC and also for checking whether the module is working properly or faulty.
Displaying the Time and Date on an I2C LCD.
I’ll now demonstrate how to display the time and date from the DS1307 RTC on an I2C LCD. The setup is done as shown below with both the RTC and LCD connected to analog pins A5 and A4 of the Arduino for SCL and SDA respectively.
Code for displaying DS1307 RTC Time and Date on I2C LCD.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
void setup ()
{
Serial.begin(9600);
lcd.begin (16,2); // initialize the lcd
lcd.backlight();//To Power ON the back light
if (! rtc.begin())
{
lcd.print("Couldn't find RTC");
while (1);
}
if (! rtc.isrunning())
{
lcd.print("RTC is NOT running!");
}
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));//auto update from computer time
//rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));// to set the time manually
}
void loop ()
{
DateTime now = rtc.now();
lcd.setCursor(0, 1);
lcd.print("TIME");
lcd.print(" ");
lcd.print(now.hour());
lcd.print(':');
lcd.print(now.minute());
lcd.print(':');
lcd.print(now.second());
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("DATE");
lcd.print(" ");
//lcd.print(daysOfTheWeek[now.dayOfTheWeek()]);
//lcd.print(" ");
lcd.print(now.day());
lcd.print('/');
lcd.print(now.month());
lcd.print('/');
lcd.print(now.year());
lcd.print(" ");
}
The code is quite similar to the one used before apart from the added LiquidCrystal_I2C.h library for controlling the LCD.
In case you need reference on how to use the I2C LCD display with Arduino you can make reference to a previous tutorial;
Digital Clock using DS1307 RTC and MAX7219 LED matrix with Arduino.
After learning the basic structure of DS1307 RTC module and how to interface it with Arduino, I’ll now demonstrate how to use this real time clock module for making a digital clock using Arduino and MAX7219 LED Matrix display. Before proceeding you must have prior knowledge on how to interface the MAX7219 LED matrix with Arduino. You can use the link below as reference.
Schematic for Arduino digital clock with MAX7219 Led matrix and DS1307 RTC.
Code for the Digital Clock using MAX7219 LED matrix, DS1307 RTC and Arduino.
// Use the DS1307 clock module
#define USE_DS1307
// Header file includes
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <Wire.h>
#include <MD_DS1307.h>
#include "Font_Data.h"
// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8
#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10
// HARDWARE SPI
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// SOFTWARE SPI
//MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
volatile boolean buttonA = false;
volatile boolean buttonB = false;
volatile boolean buttonC = false;
int StateOfbuttonA = 0;
int StateOfbuttonB = 0;
int StateOfbuttonC = 0;
int NewStateOfbuttonA = 0;
int NewStateOfbuttonB = 0;
int NewStateOfbuttonC = 0;
int temp;
int Humi;
int Mode = 0;
int contrast = 0;
int SPEED_TIME = 75;
#define PAUSE_TIME 0
#define MAX_MESG 20
// Global variables
char szTime[9]; // hh:mm
char szsecond[4]; // ss
char szMesg[MAX_MESG+1] = "";
uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 }; // Deg C
char *mon2str(uint8_t mon, char *psz, uint8_t len)
// Get a label from PROGMEM into a char array
{
const __FlashStringHelper* const Jan = F("Jan");
const __FlashStringHelper* const Feb = F("Feb");
const __FlashStringHelper* const Mar = F("Mar");
const __FlashStringHelper* const Apr = F("Apr");
const __FlashStringHelper* const May = F("May");
const __FlashStringHelper* const Jun = F("Jun");
const __FlashStringHelper* const Jul = F("Jul");
const __FlashStringHelper* const Aug = F("Aug");
const __FlashStringHelper* const Sep = F("Sep");
const __FlashStringHelper* const Oct = F("Oct");
const __FlashStringHelper* const Nov = F("Nov");
const __FlashStringHelper* const Dec = F("Dec");
const __FlashStringHelper* str[] =
{
Jan, Feb, Mar, Apr,
May, Jun, Jul, Aug,
Sep, Oct, Nov, Dec
};
strncpy_P(psz, (const char PROGMEM *)str[mon-1], len);
psz[len] = '';
return(psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len)
{
const __FlashStringHelper* const Sunday = F("Sunday");
const __FlashStringHelper* const Monday = F("Monday");
const __FlashStringHelper* const Tuesday = F("Tuesday");
const __FlashStringHelper* const Wednesday = F("Wednesday");
const __FlashStringHelper* const Thursday = F("Thursday");
const __FlashStringHelper* const Friday = F("Friday");
const __FlashStringHelper* const Saturday = F("Saturday");
const __FlashStringHelper* str[] =
{
Sunday, Monday, Tuesday,
Wednesday, Thursday,Friday,
Saturday ,Sunday
};
strncpy_P(psz, (const char PROGMEM *)str[code-1], len);
psz[len] = '';
return(psz);
}
void getTime(char *psz, bool f = true)
// Code for reading clock time
{
RTC.readTime();
sprintf(psz, "%02d%c%02d", RTC.h, (f ? ':' : ' '), RTC.m);
}
void getTim(char *psz, bool f = true)
// Code for reading clock time
{
RTC.readTime();
sprintf(psz, "%02d%c%02d", RTC.h, ':', RTC.m);
}
void getDate(char *psz)
// Code for reading clock date
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%d %s %04d", RTC.dd, mon2str(RTC.mm, szBuf, sizeof(szBuf)-1), RTC.yyyy);
}
void getsecond(char *psz)
// Code for reading clock date
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%02d", RTC.s);
}
void gethh(char *psz, bool f = true)
// Code for reading clock time
{
RTC.readTime();
sprintf(psz, "%c%02d%c%02d", (f ? ':' : ' '), RTC.h, (f ? ':' : ' '), RTC.m);
}
void getmin(char *psz, bool f = true)
// Code for reading clock time
{
RTC.readTime();
sprintf(psz, "%02d%c%02d%c", RTC.h, (f ? ':' : ' '), RTC.m, (f ? ':' : ' '));
}
void getsec(char *psz)
// Code for reading clock date
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%02d", RTC.s);
}
void getdyy(char *psz)
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%02d", RTC.dd);
}
void getmon(char *psz)
// Code for reading clock date
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%s", mon2str(RTC.mm, szBuf, sizeof(szBuf)-1));
}
void getyyyy(char *psz)
// Code for reading clock date
{
char szBuf[10];
RTC.readTime();
sprintf(psz, "%04d", RTC.yyyy);
}
void setup(void)
{
//dht.setup(2); // data pin 2
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
P.begin(3);
P.setInvert(false);
P.setZone(2, 0, 3);
P.setZone(1, 1, 3);
P.setZone(0, 4, 1);
P.setFont(1, numeric7Se);
P.setFont(0, numeric7Seg);
P.displayZoneText(1, szTime, PA_LEFT, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(0, szsecond, PA_LEFT, SPEED_TIME, 0, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(2, szMesg, PA_CENTER, SPEED_TIME, 0, PA_PRINT, PA_SCROLL_LEFT);
P.addChar('
When this code is uploaded to the Arduino board, the current time is displayed. The buttons can be used to adjust the brightness of the led display and to change the time and date. https://youtu.be/uSOpftD8VwU
, degC);
RTC.control(DS1307_CLOCK_HALT, DS1307_OFF);
RTC.control(DS1307_12H, DS1307_OFF);
getTime(szTime);
}
void loop(void)
{
P.setIntensity(contrast);
NewStateOfbuttonA = digitalRead(3);
NewStateOfbuttonB = digitalRead(4);
NewStateOfbuttonC = digitalRead(5);
buttonAisPressed();
buttonBisPressed();
buttonCisPressed();
if (buttonA) {
if (Mode == 0 ) {
buttonA = false;
contrast++;
if (contrast >= 51 ) {
contrast = 50;
}
}
else if (Mode == 1 ) {
buttonA = false;
Mode = 0;
}
else if (Mode == 2 ) {
buttonA = false;
RTC.h++;
if (RTC.h >= 24 ) {
RTC.h = 0;
}
RTC.writeTime();
}
else if (Mode == 3 ) {
buttonA = false;
RTC.m++;
if (RTC.m >= 60 ) {
RTC.m = 0;
}
RTC.writeTime();
}
else if (Mode == 4 ) {
buttonA = false;
RTC.s = 0;
RTC.writeTime();
}
else if (Mode == 5 ) {
buttonA = false;
RTC.dow++;
if (RTC.dow >= 8 ) {
RTC.dow = 1;
}
RTC.writeTime();
P.displayReset(2);
}
else if (Mode == 6 ) {
buttonA = false;
RTC.dd++;
if (RTC.dd >= 32 ) {
RTC.dd = 1;
}
RTC.writeTime();
}
else if (Mode == 7 ) {
buttonA = false;
RTC.mm++;
if (RTC.mm >= 13 ) {
RTC.mm = 1;
}
RTC.writeTime();
}
else if (Mode == 8 ) {
buttonA = false;
RTC.yyyy++;
if (RTC.yyyy >= 2035 ) {
RTC.yyyy = 2015;
}
RTC.writeTime();
}
}
else if (buttonB) {
buttonB = false;
Mode++;
P.displayReset(2);
if (Mode >= 9 ) {
Mode = 0;
}
}
if (buttonC) {
if (Mode == 0 ) {
buttonC = false;
contrast--;
if (contrast <= 0 ) {
contrast = 0;
}
}
else if (Mode == 1 ) {
buttonC = false;
Mode = 0;
}
else if (Mode == 2 ) {
buttonC = false;
RTC.h--;
if (RTC.h <= 0 ) {
RTC.h = 23;
}
RTC.writeTime();
}
else if (Mode == 3 ) {
buttonC = false;
RTC.m--;
if (RTC.m <= 0 ) {
RTC.m = 59;
}
RTC.writeTime();
}
else if (Mode == 4 ) {
buttonC = false;
RTC.s = 0;
RTC.writeTime();
}
else if (Mode == 5 ) {
buttonC = false;
RTC.dow--;
if (RTC.dow <= 0 ) {
RTC.dow = 7;
}
RTC.writeTime();
P.displayReset(2);
}
else if (Mode == 6 ) {
buttonC = false;
RTC.dd--;
if (RTC.dd <= 0 ) {
RTC.dd = 31;
}
RTC.writeTime();
}
else if (Mode == 7 ) {
buttonC = false;
RTC.mm--;
if (RTC.mm <= 0 ) {
RTC.mm = 12;
}
RTC.writeTime();
}
else if (Mode == 8 ) {
buttonC = false;
RTC.yyyy--;
if (RTC.yyyy <= 2010 ) {
RTC.yyyy = 2025;
}
RTC.writeTime();
}
}
if (Mode == 0)
{
static uint32_t lastTime = 0; // millis() memory
static bool flasher = false; // seconds passing flasher
P.displayAnimate();
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
P.getZoneStatus(1);
P.getZoneStatus(0);
if (millis() - lastTime >= 1000)
{
lastTime = millis();
getsecond(szsecond);
getTime(szTime, flasher);
flasher = !flasher;
P.displayReset(1);
P.displayReset(0);
}
}
if (Mode == 1)
{
static uint8_t display = 0; // current display mode
P.displayAnimate();
P.getZoneStatus(2);
if (P.getZoneStatus(2))
{
switch (display)
{
case 0: // Time
P.setTextEffect(2, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
getTim(szMesg);
break;
case 1: // Day
P.setTextEffect(2, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(RTC.dow, szMesg, MAX_MESG);
break;
case 2: // Calendar
P.setTextEffect(2, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
getDate(szMesg);
break;
}
P.displayReset(2);
}
}
if (Mode == 2)
{
static uint32_t lastTime = 0; // millis() memory
static bool flasher = false; // seconds passing flasher
P.displayAnimate();
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
P.getZoneStatus(1);
P.getZoneStatus(0);
if (millis() - lastTime >= 200)
{
lastTime = millis();
getsecond(szsecond);
gethh(szTime, flasher);
flasher = !flasher;
P.displayReset(1);
P.displayReset(0);
}
}
if (Mode == 3)
{
static uint32_t lastTime = 0; // millis() memory
static bool flasher = false; // seconds passing flasher
P.displayAnimate();
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
P.getZoneStatus(1);
P.getZoneStatus(0);
if (millis() - lastTime >= 200)
{
lastTime = millis();
getsecond(szsecond);
getmin(szTime, flasher);
flasher = !flasher;
P.displayReset(1);
P.displayReset(0);
}
}
if (Mode == 4)
{
static uint32_t lastTime = 0; // millis() memory
static bool flasher = false; // seconds passing flasher
P.displayAnimate();
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
P.getZoneStatus(1);
P.getZoneStatus(0);
if (millis() - lastTime >= 200)
{
lastTime = millis();
getTim(szTime);
getsecond(szsecond);
P.displayReset(1);
P.displayReset(0);
}
}
if (Mode == 5)
{
static uint8_t display = 0; // current display mode
P.displayAnimate();
P.getZoneStatus(2);
P.setTextEffect(2, PA_PRINT, PA_SCROLL_LEFT);
dow2str(RTC.dow, szMesg, MAX_MESG);
P.displayReset(2);
}
if (Mode == 6)
{
P.displayAnimate();
P.getZoneStatus(2);
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
getdyy(szMesg);
P.displayReset(2);
}
if (Mode == 7)
{
P.displayAnimate();
P.getZoneStatus(2);
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
getmon(szMesg);
P.displayReset(2);
}
if (Mode == 8)
{
P.displayAnimate();
P.getZoneStatus(2);
P.setTextEffect(2, PA_PRINT, PA_NO_EFFECT);
getyyyy(szMesg);
P.displayReset(2);
}
}
void buttonAisPressed()
{
if (NewStateOfbuttonA != StateOfbuttonA)
{
if (NewStateOfbuttonA == 0)
{
buttonA=true;
}
delay(50);
}
StateOfbuttonA = NewStateOfbuttonA;
}
void buttonBisPressed()
{
if (NewStateOfbuttonB != StateOfbuttonB)
{
if (NewStateOfbuttonB == 0) {
buttonB=true;
}
delay(50);
}
StateOfbuttonB = NewStateOfbuttonB;
}
void buttonCisPressed()
{
if (NewStateOfbuttonC != StateOfbuttonC)
{
if (NewStateOfbuttonC == 0) {
buttonC=true;
}
delay(50);
}
StateOfbuttonC = NewStateOfbuttonC;
}