The Freetronics Real-Time Clock Module keeps accurate time for years using a tiny coin-cell, and is very simple to connect to your Arduino project.The module uses your Arduino or Eleven's analogue pins A4 and A5 for "I2C" serial communications with your sketch.

If you are using a Freetronics LeoStick or Arduino Leonardo-compatible board, please use D2 (SDA) and D3 (SCL). No other pins on the Arduino are used by the module other than power and GND, so they are all available for connection to other devices in your projects.

Module Pinout

VCC: Connect the 5V or 3.3V from your Arduino or circuit here to power the module
GND: Connect this to your Arduino or board’s GND pin
SDA: This is the I2C bus data pin, connect to your Arduino’s A4 pin
SCL: This is the I2C bus clock pin, connect to your Arduino’s A5 pin
32K: The module has a 32 kHz square wave output from this pin
BAT: You can connect the ‘+’ of an external backup battery to this point
SQI: From this pin comes the controllable square-wave. 
RST: At this stage it’s unused, it can be used as a part of a reset switch.


Don’t forget to install the backup battery. The ‘+’ side faces outwards. There are two 4k7 ohm resistors fitted to the board as I2C bus pull-up resistors. If your circuit requires these, bridge the pads with a little solder to connect the resistors to the I2C bus. 

Getting Started

There are two ways to work with the module. You can either use an Arduino library created by Rhys Weatherley, or use raw I2C commands. If you are new to the world of Arduino, using the library will be simpler. However if you enjoy working with I2C, wish to explore the DS3232 to the fullest, or you’re using a different development platform - the I2C commands will be necessary.

There may be a small piece of plastic fitted between the battery and the holder, please remove this when you're ready to use your RTC module.

Now let’s get started - we will first demonstrate using the library - which is simpler and allows you a fast start, and then examine the useful direct I2C commands - which give you complete control over the real-time clock module. 

Note: When discussing time values, they are always in 24-hour time. Years are always two digits, so your sketch can deal with year 2000 to 2099.

Using The DS3232 Arduino Library

Install Library

Please note we’re using Arduino IDE v1.0.1 or higher. Next, download the DS3232 library from 

https://github.com/rweather/arduino-projects/tree/master/libraries/RTC

Extract the RTC and I2C folders from the downloaded .zip file. They need to be installed in a location where the Arduino IDE can read them, so check the location of your "sketchbook" directory (you can look in the "preferences" window in the IDE if you don't know where it is) and then create a new directory inside that called "libraries" if it doesn't already exist.

Now copy the extracted RTC and I2C directories into your "libraries" directory. Then close all Arduino IDE windows if any are open, then restart the IDE so it will see the new libraries.

Testing The Module

First, let’s test the module and set the time and date.  Connect your module to the board, and the board to the computer. Next, open the the “TestRTC” sketch included with the DS3232 library. It can be found in the IDE by selecting the menu File ->Examples ->RTC ->TestRTC. Now upload the sketch.

Finally, open the serial monitor. The following instructions should be displayed.

You can repeat this display by entering “HELP” into the serial monitor. This sketch allows you to easily set or read the data in the module. For example, to set the time to 14:57:00 just type 

TIME 14:57:00

and press Enter. To retrieve the time, just type TIME and press Enter. As indicated you can also set the other parameters.

Once you have finished, and as long as the battery has been inserted into the module - the data will be remembered after the module unless it’s disconnected from the 5V power, the battery dies or is removed. 

Using The DS3232 Library In Your Sketches

Firstly, always put the following four lines of code somewhere before "void setup()". They call the required libraries and set the pins for the special I2C library used in our examples.

#include <SoftI2C.h> // initialise required libraries
#include <DS3232RTC.h>
SoftI2C i2c(A4, A5); // assign pins to SDA and SCL
DS3232RTC rtc(i2c);

Note that in the third line we assigned the pins for the I2C bus - however you can use other I/O pins but only when using the SoftI2C library. 

Next, we’ll examine the functions for setting and retrieving time data from the module

Setting The Time

First setup an instance of time with 

RTCTime time;

Next, assign values to the hours, minutes and seconds (using byte variables) with 

time.hour = hour;
time.minute = minute;
time.second = second;

Then set the time to the DS3232 with

rtc.writeTime(&time); 

Setting The Date

Setting the date requires a similar process. First setup an instance of date with 

RTCDate date;

then assign values to the various parameters (byte variables) with

date.day = dd;
date.month = mm;
date.year = yyyy;

finally set the date to the DS3232 with

rtc.writeDate(&date);

When setting the time and date, if invalid values are stored (such as February 31st) an error will not be returned. If building devices that require user input (such as an alarm clock) you should test for errors as part of your user interface before writing the data to the DS3232.

Retrieving The Time

First, retrieve the time data from the RTC with

rtc.readTime(&time);

then you can work with the time parameters by using the byte variables

time.hour
time.minute
time.second

So to display the time in the serial monitor, you now use the following lines

Serial.print("Time: ");
Serial.print(time.hour, DEC);
Serial.print(':');
Serial.print(time.minute, DEC);
Serial.print(':');
Serial.print(time.second, DEC);

However, when displaying time (and alarm data) there won’t be a leading zero for values less than ten. So you will need to test for this with an if.. statement to draw a zero before the value. For example:

if ( time.hour < 10 ) { Serial.print( “0” ); }

Retrieving The Date

Just as we did with the time, first retrieve the date data from the RTC with

rtc.readDate(&date);

then you can work with the date parameters by using the byte variables

date.day
date.month
date.year

There is also one more parameter, the numerical day of the week. The values span from 1 for Monday to 7 for Sunday. This is automatically calculated by the DS3232 so we don’t have to set it. So to read it, we work with the following and either display it or assign it to a byte variable. 

RTC::dayOfWeek(&date)

For example, to display it on the serial monitor we use

Serial.println(RTC::dayOfWeek(&date), DEC);

Retrieving The Temperature

The DS3232 also has a thermometer with a range between zero and 70 degrees Celsius which we can read on demand. To do this is simple, and requires two stages. As the value returned from the DS3232 is in quarters of a degree, we need to divide it by four to convert to degrees Celsius. For example:

int t1= rtc.readTemperature(); // retrieve the value from the DS3232
float temp = t1 / 4; // temperature in Celsius stored in temp

If you need to convert it to Fahrenheit, use:

int t1= rtc.readTemperature(); // retrieve the value from the DS3232
float tempf = (( t1 * 9 ) / 20 ) + 32; // Convert to Fahrenheit.

There may a situation when the DS3232 cannot determine the temperature. You can test for this with the following code:

if (temp == RTC::NO_TEMPERATURE) { // can’t read temperature }

Using The Alarms

The DS3232 has two alarm memories, numbered zero and one. The alarm data is also kept accurate with the time, so the settings will be remembered during a power loss unless the backup battery is dead or missing. Your Arduino sketch can then check if an alarm should be activated in two ways:

  1. By reading the stored alarm times (below) and comparing against the current time
  2. By using the special “fired alarm” function (below) which polls the DS3232 and returns which alarm times have passed

Setting The Alarm Times

First setup an instance of the alarm with 

RTCAlarm alarm;

Then assign the values in hours and minutes to the following:

alarm.hour
alarm.minute

Finally, set the data to the required alarm using one of the following

rtc.writeAlarm(0,&alarm);  // for alarm 0
rtc.writeAlarm(1,&alarm);  // for alarm 1

Reading The Alarm Times

Just as we did with the time, first setup an instance of the alarm with 

RTCAlarm alarm;

Then to retrieve the alarm data, using one of the following

rtc.readAlarm(0,&alarm);  // for alarm 0
rtc.readAlarm(1,&alarm);  // for alarm 1

Then you can assign the hour and minute values from these byte variables:

alarm.hour
alarm.minute

You can then compare the time data against the alarm data to determine if an alarm time has elapsed.

Which Alarm Went Off?

You can check which alarm time occurred last by using the following function which returns an integer

int w=rtc.firedAlarm();

The value of w will determine which alarm activated:

0 - alarm 0
1 - alarm 1
2 - both activated 
-1 - neither alarms have activated since the last check

After each use of the firefdAlarm() function, the value returned is reset to -1.

Controlling The 32.768 kHz Output

There is a 32.768 kHz square-wave output available from the “32k” pad on the module. To turn it on and off, use the following functions:

rtc.enable32kHzOutput();
rtc.disable32kHzOutput();

Using The Non-Volatile RAM (NVRAM)

Inside the DS3232 are 236 bytes of memory, of which we can use 223 for your own data. This is an ideal place to store information that is generated by the sketch or user input that needs to be remembered. 

To store a byte of data, use the following

rtc.writeByte(location, value);

where location is the position in memory (from 0 to 222) and value is the byte of data to store.

To retrieve a byte of data, assign the following to a byte variable. For example:

byte d=rtc.readByte(location);

where location is the memory position of the byte to retrieve. 

The following sketch is a simple example that writes the number 255 to each NVRAM position, then retrieves and displays each one on the serial monitor:

#include <SoftI2C.h>
#include <DS3232RTC.h>

// using Arduino pins A4 and A5 for SDA and SCL respectively
SoftI2C i2c(A4, A5);
DS3232RTC rtc(i2c);

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

int a,b;

void loop() 
{
  Serial.println("Writing...");
  for (a = 0; a<226; a++)
  {
    rtc.writeByte(a, 255); // writes 255 into MRVRAM position 'a'
  }
  Serial.println("Reading ...");  
  for (a = 0; a<226; a++)
  {
    b=rtc.readByte(a); // retrieve data from position 'a' into 'b'
    Serial.println(b);
  }
  delay(1000);
}

Controlling The RTC Module Via Direct I2C Commands

In this section we examine how to directly control the DS3232 real-time clock IC using I2C commands in the Arduino environment. Again, we’re using Arduino IDE v1.0.1 for these examples. At this point you will find a copy of the DS3232 datasheet convenient, please download a copy from http://www.maxim-ic.com/datasheet/index.mvp/id/4984. You will need the address map on page 11.

Finally, the I2C bus address for the module is 0x68. In our examples we use:

#define DS3232_I2C_ADDRESS 0x68

for ease of reference. 

Setting The Time And Date

The time and date are stored in the first seven registers starting from 00h. They are 

00h - seconds
01h - minutes
02h - hours
03h - day of the week. The convention is to use 1 for Sunday, 2 for Monday, etc. 
04h - date (1~31)
05h - month (1~12)
06h - year (00~99). 

You can use part of register 02h to store AM/PM, however for simplicity we stick with 24-hour time. You can then convert to 12-hour time and AM/PM in your sketch.

When sending numerical data to the DS3232, it is sent as one byte in BCD (binary-coded decimal). To simplify this, the following function will be necessary in your sketches:

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

Then when writing (for example) the hours, you would use

Wire.write(decToBcd(hour));

To set the time and date date, you can either write to each register individually, or do it in one function. The following example function does just that:

void setDS3232time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
// sets time and date data to DS3232
{
  Wire.beginTransmission(DS3232_I2C_ADDRESS);  
  Wire.write(0); // moveds register pointer to 00h 
  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
  Wire.write(decToBcd(month));      // set month
  Wire.write(decToBcd(year));       // set year
  Wire.endTransmission();
}

Retrieving the data from the DS3232 is just as simple. However the data will be returned in BCD form, so we need another function to convert the data back to decimal:

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

So to read the data, move the register pointer to 00h, and then request seven bytes of data which is converted from BCD and stored into byte variables. This is accomplished with the following function:

void readDS3232time(byte *second, 
byte *minute, 
byte *hour, 
byte *dayOfWeek, 
byte *dayOfMonth, 
byte *month, 
byte *year)
{
  Wire.beginTransmission(DS3232_I2C_ADDRESS);
  Wire.write(0); // set DS3232 register pointer to 00h
  Wire.endTransmission();  
  Wire.requestFrom(DS3232_I2C_ADDRESS, 7); 
  *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());
}

We have put these example functions together in a demonstration sketch that you can download and examine. With it you can set and retrieve the time and date data, and modify it for your own purposes.

I2Cds3232.ino

Below is the example output from the sketch.

Setting The Alarms

Now to set the alarm data. You can either check the alarm data against the time in the sketch to determine if an alarm needs to be activated, or a signal can be sent from the SQI pin. 

The alarm registers are as follows:

Alarm One
07h - seconds
08h - minutes
09h - hours
0Ah - date

Alarm Two
0Bh - minutes (no seconds for alarm two)
0Ch - hours
0Dh - date 

Setting the alarm data is the same process as the time, just move the register pointer to the desired address and write the data. For example, the following function can be used to set alarm one:

void setDS3232alarm1(byte second, byte minute, byte hour, byte dayOfMonth)
// sets alarm 1 data
{
  Wire.beginTransmission(DS3232_I2C_ADDRESS);  
  Wire.write(0x07); 
  Wire.write(decToBcd(second));     // set seconds
  Wire.write(decToBcd(minute));     // set minutes
  Wire.write(decToBcd(hour));       // set hours
  Wire.write(decToBcd(dayOfMonth));  // set date of month
  Wire.endTransmission();
}

Again, retrieving the alarm data is the same process as time. Just move the register pointer to the start of alarm one or two, then request the bytes of data. For example, the following retrieves the alarm one data:

void readDS3232alarm1(byte *second, 
byte *minute, 
byte *hour, 
byte *dayOfMonth)
{
  Wire.beginTransmission(DS3232_I2C_ADDRESS);
  Wire.write(0x07); // set DS3232 register pointer to 07h
  Wire.endTransmission();  
  Wire.requestFrom(DS3232_I2C_ADDRESS, 4); 
  *second     = bcdToDec(Wire.read());
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f);
  *dayOfMonth = bcdToDec(Wire.read()) ;
}

You can repeat the functions for alarm two - just remember that there is only minutes, hours and date data to work with. We have put these example functions together in a demonstration sketch that you can download and examine. With it you can set and retrieve data for both alarms, and modify it for your own purposes. 

I2Cds3232alarms.ino

Below is the example output from the sketch.



When an alarm event occurs, you can generate a signal from the SQI pin that could be used (for example) to trigger an interrupt. To do this, you first need to set bit 2 of the control register (0x0E) to 1 (this also disables the square-wave generator descibed later). Then you need to enable each alarm to have control over the interrupt by setting bits 1 and 0 to allow alarms 2 and 1 respectively to control the interrupt signal. Finally, you can determine the frequency of alarm interrupt by altering bit 7 of each alarm register. See the Table 2 entitled “Alarm Mask bits” on page twelve of the data sheet. Doing so may seem complex, so remember you can always check for alarm events in the sketch instead of hardware.

Note that you can also check the status of alarm events by reading bits 1 and 0 of register 0x0F, which represent alarms 2 and 1. A 1 denotes the alarm event has occurred, 0 for not.

Note - while working with the control register 0x0E, which contains data relevant to several functions. Therefore when changing one function, you may not want to change others. To solve this problem, before changing it - read the control register, then use the bitwise and ‘&’ to safely combine the two bytes that contain the existing contents and your new changes.

Using The Square-Wave Generator

You can source a square wave with one of four frequencies from the SQI pin. The square wave generator is set via the Control Register. 
To select 1Hz. use

Wire.beginTransmission(DS3232_I2C_ADDRESS);  
Wire.write(0x0E);  // move register pointer to control register
Wire.write(0);     // set to 1Hz  
Wire.endTransmission();

Instead of 0 for 1Hz you can also use 0x08 for 1.024 kHz, 0x10 for 4.096 kHz and 0x18 for 8.192 kHz. You can use the following example sketch to run through the different frequencies. 

DS3232sqwave.ino

Finally there is also a seperate 32 kHz output from the 32K pin on the module. To activate it use:

Wire.beginTransmission(DS3232_I2C_ADDRESS);  
Wire.write(0x0F);  // move register pointer to control register
Wire.write(0x08);     // set to 32 kHz
Wire.endTransmission();

To deactivate, send 0 instead of 0x08. 

Reading The Thermometer

The temperature data is stored over two registers, starting at 0x11. To retrieve the temperature (in Celsius), use the following:

Wire.requestFrom(DS3232_I2C_ADDRESS, 2); 
tempMSB = Wire.read();
tempLSB = Wire.read() >> 6;

You can then convert the bytes to a temperature value in Celsius with:

temperature = tempMSB + (0.25*tempLSB);

For a demonstration, download the following sketch:

ds3232temp.ino

Using The Non-Volatile RAM (NVRAM)

Inside the DS3232 are 236 bytes of memory. This is an ideal place to store information that is generated by the sketch or user input that needs to be remembered. The locations are one byte in size and start from register address 0x14 (decimal 20) and finish at 0xFF (decimal 255).

Writing data is a simple matter of moving the register pointer to the required register and writing the byte of data; and reading only requires moving the register pointer to the required register and requesting one (or more) bytes of data. For a demonstration that saves and retrieves a number in every available NVRAM register, download the following sketch:

i2cds3232nvram.ino

And there you have it. We hope you find this tutorial useful. For discussion about your real-time clock module and other products, please visit the "modules" section of the Freetronics forum.