Arduino – 6 – DS3231 Real Time Clock Module

How to use DS3231 RTC breakout board to display time and date ?

rtc-3

The datasheet for the DS3231 explains that this part is an “Extremely Accurate I²C-Integrated RTC/TCXO/Crystal”. And, hey, it does exactly what it says on the tin! This Real Time Clock (RTC) is the most precise you can get in a small, low power package.

Most RTC’s use an external 32kHz timing crystal that is used to keep time with low current draw. And that’s all well and good, but those crystals have slight drift, particularly when the temperature changes (the temperature changes the oscillation frequency very very very slightly but it does add up!) This RTC 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.

This is the finest RTC you can get, and now we have it in a compact, breadboard-friendly breakout. With a coin cell plugged into the back, you can get years of precision timekeeping, even when main power is lost. Great for data logging and clocks, or anything where you need to really know the time.

rtc-6

Figure-1: DS3231 RTC Module.

Power Pins:

  • Vcc – this is the power pin. Since the RTC can be powered from 2.3V to 5.5V power, you do not need a regulator or level shifter for 3.3V or 5V logic/power. To power the board, give it 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 microcontrollers I2C clock line. This pin has a 10K pull-up resistor to Vcc.
  • SDA – I2C data pin, connect to your microcontrollers I2C data line. This pin has a 10K pull-up resistor to Vcc.

Other Pins:

  • 32K – 32-KHz oscillator output. Open drain, you need to attach a pull-up to read this signal from a microcontroller pin.
  • SQW – optional square wave or interrupt output. Open drain, you need to attach a pull-up to read this signal from a microcontroller pin.

Along with keeping track of the time and date, this modules also have a small EEPROM, an alarm function and the ability to generate a square-wave of various frequencies.

What you need for this tutorial ?

Connecting your module to an Arduino

This modules use the I2C bus, which makes connection very easy. First you will need to identify which pins on your Arduino or compatible boards are used for the I2C bus – these will be knows as SDA (or data) and SCL (or clock). On Arduino Uno or compatible boards, these pins are A4 and A5 for data and clock:

 arduino-1

 Figure-2: SDA & SCL pin on Arduino Uno Board

If you’re using an Arduino Mega the pins are D20 and D21 for data and clock:

arduino-2

Figure-3: SDA & SCL pin on Arduino Mega Board

Connecting this module is easy as header pins are installed on the board at the factory. You can simply run jumper wires again from SCL and SDA to the Arduino and again from the module’s Vcc and GND pins to your board’s 5V or 3.3.V and GND. However these are duplicated on the other side for soldering your own wires.
This module have the required pull-up resistors, so you don’t need to add your own. Like all devices connected to the I2C bus, try and keep the length of the SDA and SCL wires to a minimum.

Reading and writing the time from your RTC Module

Once you have wired up your RTC module. enter and upload the following sketch.

/*==========================================================================
//  Author      : Handson Technology
//  Project     : Arduino
//  Description : DS3231 Real Time Clock
//  LiquidCrystal 
//  Source-Code : RTC-Tutorial-1.ino
//==========================================================================
*/


#include "Wire.h"
#define DS3231_I2C_ADDRESS 0x68
// Convert normal decimal numbers to binary coded decimal

byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  // set the initial time here:
  // DS3231 seconds, minutes, hours, day, date, month, year
  // setDS3231time(30,42,21,4,26,11,14);
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}

void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  
// request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

void displayTime()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  // send it to the serial monitor
  Serial.print(hour, DEC);

  // convert the byte variable to a decimal number when displayed
  Serial.print(":");

  if (minute<10)
  {
    Serial.print("0");
  }
  Serial.print(minute, DEC);
  Serial.print(":");
  if (second<10)
  {
    Serial.print("0");
  }
  Serial.print(second, DEC);
  Serial.print(" ");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.print(" Day of week: ");
  switch(dayOfWeek){
  case 1:
    Serial.println("Sunday");
    break;
  case 2:
    Serial.println("Monday");
    break;
  case 3:
    Serial.println("Tuesday");
    break;
  case 4:
    Serial.println("Wednesday");
    break;
  case 5:
    Serial.println("Thursday");
    break;
  case 6:
    Serial.println("Friday");
    break;
  case 7:
    Serial.println("Saturday");
    break;
  }
}

void loop()
{
  displayTime(); // display the real-time clock data on the Serial Monitor,
  delay(1000); // every second
}

Download This Sketch

There may be a lot of code, however it breaks down well into manageable parts.
It first includes the Wire library, which is used for I2C bus communication, followed by defining the bus address for the RTC as 0x68. These are followed by two functions that convert decimal numbers to BCD (binary-coded decimal) and vice versa. These are necessary as the RTC ICs work in BCD not decimal.
The function setDS3231time() is used to set the clock. Using it is very easy, simple insert the values from year down to second, and the RTC will start from that time. For example if you want to set the following date and time – Wednesday November 26, 2014 and 9:42 pm and 30 seconds – you would use:

setDS3231time(30,42,21,4,26,11,14);

Note that the time is set using 24-hour time, and the fourth parameter is the “day of week”. This falls between 1 and 7 which is Sunday to Saturday respectively. These parameters are byte values if you are substituting your own variables.
Once you have run the function once it’s wise to prefix it with // and upload your code again, so it will not reset the time once the power has been cycled or micrcontroller reset.
Reading the time form your RTC Is just as simple, in fact the process can be followed neatly inside the function displayTime(). You will need to define seven byte variables to store the data from the RTC, and these are then inserted in the function readDS3231time().

For example if your variables are:

byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

… you would refresh them with the current data from the RTC by using:

readDS3232time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

Then you can use the variables as you see fit, from sending the time and date to the serial monitor as the example sketch does – to converting the data into a suitable form for all sorts of output devices.

Just to check everything is working, enter the appropriate time and date into the demonstration sketch, upload it, comment out the setDS3231time() function and upload it again. Then open the serial monitor, and you should be provided with a running display of the current time and date, for example:

rtc-7

From this point you now have the software tools to set data to and retrieve it from your real-time clock module, and we hope you have an understanding of how to use these inexpensive and highly accurate timing modules.