Real-Time Clock

In this project, you'll learn how to connect and control the DS3231 real-time clock module with the Arduino.

The DS3231 IC is an extremely accurate real-time clock (RTC) module that keeps the date, time and alarm setting, and also has a built-in temperature sensor. In addition to the DS3231 RTC chip, the module comes with an AT24C32 EEPROM chip for storing data permanently, and backup battery to maintain the time even when the module is not connected to a power source.

Both the DS3231 and the AT24C32 use the I2C protocol. The I2C protocol uses two signals, SDA and SCL, for communication. This protocol has been implemented in the wire.h library so that you do not need to know the details of how the I2C protocol works.

The datasheet for the DS3231 can be found here, and the datasheet for the AT24C32 can be found here.

Parts needed:
  • Arduino
  • Real-time clock module DS3231
  • Wires
  • Breadboard
DS3231 real-time clock     
1 Making the connections

The DS3231 requires four connections: SDA, SCL, VCC and GND
  • Connect SDA to analog pin A4
  • Connect SCL to analog pin A5
  • Connect the VCC to 5V.
  • Connect the GND to GND.
2 Copy the program on the right into your Arduino IDE


You need to set the real-time clock the first time you use this module. To set the clock:
  • uncomment the call to the setDate() function in the setup() routine, and
  • change the seven numbers in the parameter for the setDate() function to the correct time and date.
    The seven numbers in order are:
       1) seconds (0 to 59)
       2) minute (0 to 59)
       3) hour (24 hour clock from 0 to 23)
       4) weekday (1=Sunday to 7=Saturday)
       5) day (1 to 31)
       6) month (1 to 12)
       7) year (two digit)


The module has a battery backup so you do not need to set it again even if you disconnect power from it.
// Copyright May 2014 Enoch Hwang
// the I2C Wire.h library requires these two connections:
// SDA - A4
// SCL - A5
#include "Wire.h"
/*
 * DS3231 I2C address is:
 *   1101000 R/W'
 *   = 0x68
 *
 * The Wire.h library will append the R/W' bit.
 * So we only specify the first 7 bits  
 */
#define DS3231_I2C_ID 0x68    //I2C address for the DS3231 RTC chip

void setup() {
  Serial.begin(9600);
  Wire.begin();

  //the parameters are: Second, Minute, Hour, WeekDay, Day, Month, Year
  //the following sets: second=45, minute=59, hour=3, weekday=Wednesday, day=29, month=April, year=2017
  setDate(45, 59, 3, 4, 29, 4, 17);  // 24 hour format; Sun=1 
}

void loop() {
  readDate();
  readTemperature();
  delay(1000);
}

void setDate(byte Second, byte Minute, byte Hour, byte WeekDay, byte Day, byte Month, byte Year) {
  Wire.beginTransmission(DS3231_I2C_ID);
  Wire.write(0x0);  // register address for the time and date
  Wire.write(dec2bcd(Second));
  Wire.write(dec2bcd(Minute));
  // Hour must be in 24h format
  bool h12 = 1;  // set 12 hour mode
  if(h12){  // 12 hour mode
    if(Hour >= 12){
      Hour = dec2bcd(Hour-12) | 0b01100000;  // bit5(PM)=1; bit6(12 hour)=1
    }else{
      Hour = dec2bcd(Hour) & 0b11011111 | 0b01000000;  // bit5(PM)=0; bit6(12 hour)=1
    }
  }else{  // 24 hour mode
    Hour = dec2bcd(Hour) & 0b10111111;
  }
  Wire.write(Hour);
  Wire.write(dec2bcd(WeekDay));
  Wire.write(dec2bcd(Day));
  Wire.write(dec2bcd(Month));
  Wire.write(dec2bcd(Year));
  Wire.endTransmission();
}

void readDate() {
  Wire.beginTransmission(DS3231_I2C_ID);
  Wire.write(0x0);  // register address for the time and date
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ID, 7);  // get 7 bytes

  byte Second = bcd2dec(Wire.read() & 0b1111111);
  byte Minute = bcd2dec(Wire.read() & 0b1111111);
  byte Hour = Wire.read();
  bool h12 = Hour & 0b01000000;
  bool PM;
  if (h12) {  // 12 hour clock
    PM = Hour & 0b00100000;
    Hour = bcd2dec(Hour & 0b00011111);
  } else {  // 24 hour clock
    Hour = bcd2dec(Hour & 0b00111111);
  }
  byte WeekDay = bcd2dec(Wire.read());  // 1=Sunday
  byte Day = bcd2dec(Wire.read());
  byte Month = bcd2dec(Wire.read());
  byte Year = bcd2dec(Wire.read());
  
  const char* days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

  Serial.print(days[WeekDay-1]);
  Serial.print("  ");
  Serial.print(Month);
  Serial.print("/");
  Serial.print(Day);
  Serial.print("/20");
  Serial.print(Year);
  Serial.print("  ");
  Serial.print(Hour);
  Serial.print(":");
  Serial.print(Minute);
  Serial.print(":");
  if (Second < 10) Serial.print("0");
  Serial.print(Second);
  if (PM) Serial.print("P");
  else Serial.print("A");
}

void readTemperature() {
  Wire.beginTransmission(DS3231_I2C_ID);
  Wire.write(0x11);  // register address for the temperature
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ID, 2);  // get 2 bytes
  int MSB = Wire.read();  // 2's complement int portion
  int LSB = Wire.read();  // fraction portion
  float temperature = MSB & 0x7F;  // do 2's moth on MSB
  temperature = temperature + (LSB >> 6) * 0.25;  // only care about bits 7 and 8
  
  Serial.print("  ");
  Serial.print(temperature);
  Serial.println("C");
  
}

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

// Convert normal decimal number to binary coded decimal
byte dec2bcd(byte val) {
  return ((val/10*16) + (val%10));
}
3 Upload and run the program to first set the clock.
4 Comment out the call to the setDate() function in the setup() routine so that the clock will not be set to the same time each time the program runs.
5 Upload and run the program.
6 Open up the serial monitor and you should see the time and temperature displayed on the monitor.
7 Here's the code to write and read from the AT24C32 EEPROM IC.
// Copyright May 2014 Enoch Hwang
// the I2C Wire.h library requires these two connections:
// SDA - A4
// SCL - A5
#include "Wire.h"
/*
 * AT24C32 I2C address (on the DS3231 board) is:
 *   1010111 R/W'
 *   = 0x57
 * AT24C32 I2C address (on the DS1307 board) is:
 *   1010000 R/W'
 *   = 0x50
 * The last three bits of the address for the AT24C32 is A2,A1,A0
 * which is hardware configured.
 * On the DS3231 board, it is 111,
 * and on the DS1307 board it is 000.
 *
 * The Wire.h library will append the R/W' bit.
 * So we only specify the first 7 bits  
 */
#define AT24C32_I2C_ID 0x57   //I2C address for the AT24C32 EEPROM chip on the DS3231 RTC board
//#define AT24C32_I2C_ID 0x50   //I2C address for the AT24C32 EEPROM chip on the DS1307 RTC board

void setup() {
  Serial.begin(9600);
  Wire.begin();

  writeEEPROM(0,98);  		// Write 98 to EEPROM location 0
  byte c = readEEPROM(0);	// Read from location 0
  Serial.print("Byte read from EEPROM location 0 is ");
  Serial.println(c);
}

void loop() {
}

void writeEEPROM(int address, byte data) {
  Wire.beginTransmission(AT24C32_I2C_ID);
  // send memory address to write
  // the memory address consists of 12 bits (2^12 = 4096 = 32K bytes)
  Wire.write(address >> 8);    // Address MSB = upper 4 bits
  Wire.write(address & 0xff);  // Address LSB = last 8 bits
  // write data
  Wire.write(data);
    
  Wire.endTransmission();
  delay(5);  // need this delay after a write before a read
}

byte readEEPROM(int address) {
  // send address to read
  Wire.beginTransmission(AT24C32_I2C_ID);
  Wire.write(address >> 8);    // Address MSB = upper 4 bits
  Wire.write(address & 0xff);  // Address LSB = last 8 bits
  Wire.endTransmission();
  // request 1 byte to read
  Wire.requestFrom(AT24C32_I2C_ID, 1);
  return Wire.read();
}