An I2C Bus Example Using the DS1307 Real-Time Clock

A more complete description of I2C can be read here on Wikipedia, but I will give you the four sentence summary here. I2C is a serial data bus protocol that allows multiple devices to connect to each other with fairly slow data transfer rates. These slow data transfer rates are fast enough for many devices and allow the bus to be very simple to implement. The real beauty of this protocol is that you can control up to 112 devices with just two wires from a microcontroller. Many microcontrollers have a libraries to support I2C; on Arduino the official Wire library handles the details for you.

In this example I use an Arduino board as the bus master and have just one slave device on the I2C bus, a DS1307. You can read the DS1307 data sheet here. As I mentioned before Arduino has a library called Wire that handles all the details of the I2C protocol. Wire uses analog pin 4 for the Serial Data (SDA) connection and analog pin 5 for the Serial Clock (SCL) connection. The I2C protocol defines the bus as an open drain bus, which means you need to use pull-up resistors on each of the two bus wires. Here is the circuit of how I wired the DS1307 to Arduino using a I2C bus.

Here is the code I wrote. There are a lot of other places on the web that have code for using the DS1307, but I couldn’t find a simple and efficient one that just did what I needed. This code has two main functions for accessing the DS1307. One sets the time and date, and the other gets the time and date. The DS1307 returns it’s numbers coded in binary-coded decimal (BCD). This code also converts the numbers returned from the DS1307 out of BCD for you. If all you want to do is display the time BCD is probably better, but most of the time I won’t be just displaying the time. Instead I’ll be using it to decide when to run events and for this it’s better to work with normal numbers.

//
// Maurice Ribble 
// 4-17-2008
// http://www.glacialwanderer.com/hobbyrobotics

// This code tests the DS1307 Real Time clock on the Arduino board.
// The ds1307 works in binary coded decimal or BCD.  You can look up
// bcd in google if you aren't familior with it.  There can output
// a square wave, but I don't expose that in this code.  See the
// ds1307 for it's full capabilities.

#include "Wire.h"
#define DS1307_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) );
}

// Stops the DS1307, but it has the side effect of setting seconds to 0
// Probably only want to use this for testing
/*void stopDs1307()
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);
  Wire.send(0x80);
  Wire.endTransmission();
}*/

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers
void setDateDs1307(byte second,        // 0-59
                   byte minute,        // 0-59
                   byte hour,          // 1-23
                   byte dayOfWeek,     // 1-7
                   byte dayOfMonth,    // 1-28/29/30/31
                   byte month,         // 1-12
                   byte year)          // 0-99
{
   Wire.beginTransmission(DS1307_I2C_ADDRESS);
   Wire.send(0);
   Wire.send(decToBcd(second));    // 0 to bit 7 starts the clock
   Wire.send(decToBcd(minute));
   Wire.send(decToBcd(hour));      // If you want 12 hour am/pm you need to set
                                   // bit 6 (also need to change readDateDs1307)
   Wire.send(decToBcd(dayOfWeek));
   Wire.send(decToBcd(dayOfMonth));
   Wire.send(decToBcd(month));
   Wire.send(decToBcd(year));
   Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
          byte *minute,
          byte *hour,
          byte *dayOfWeek,
          byte *dayOfMonth,
          byte *month,
          byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);
  Wire.endTransmission();
  
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.receive() & 0x7f);
  *minute     = bcdToDec(Wire.receive());
  *hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.receive());
  *dayOfMonth = bcdToDec(Wire.receive());
  *month      = bcdToDec(Wire.receive());
  *year       = bcdToDec(Wire.receive());
}


void setup()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin();
  Serial.begin(9600);
  
  // Change these values to what you want to set your clock to.
  // You probably only want to set your clock once and then remove
  // the setDateDs1307 call.
  second = 45;
  minute = 3;
  hour = 7;
  dayOfWeek = 5;
  dayOfMonth = 17;
  month = 4;
  year = 8;
  setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}

void loop()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  Serial.print(hour, DEC);
  Serial.print(":");
  Serial.print(minute, DEC);
  Serial.print(":");
  Serial.print(second, DEC);
  Serial.print("  ");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  Serial.print("  Day_of_week:");
  Serial.println(dayOfWeek, DEC);

  delay(1000);
}

UPDATE 5-20-2008
Updated the code to fix a few bugs Fabien pointed out. He also informed me of this: “Because you’re hooking up the clock to analog pins 4 & 5 on the Arduino and using the Wire lib, you don’t need to bother with pull-up resistors in your schematics. Turns out that the Arduino has built-in pull-up resistors for these pins. The Two Wire library enables the built-in pull-ups in the twi_init() function in twi.c (located here: \arduino-0011\hardware\libraries\Wire\utility).”

Thanks Fabien!

77 Comments

  1. Fabio Varesano said,

    January 24, 2011 @ 3:06 am

    Hey, nice article!

    you may be interested in helping me pushing the high level writeTo & readFrom into Wire to avoid code repetition and wasting program memory when using different I2C devices with Arduino.

    See http://code.google.com/p/arduino/issues/detail?id=466

    Thank you,

    Fabio Varesano

  2. Ashen Randika said,

    March 2, 2011 @ 10:57 am

    Hello!
    Thanks for the artcle 🙂 It saved my day

  3. hari said,

    March 5, 2011 @ 2:13 am

    *second = bcdToDec(Wire.receive() & 0x7f); why r u use this pointer name ,use another name like *sec

  4. Curt said,

    March 9, 2011 @ 10:41 am

    I’m total novice trying to get RTC 1307 module from Sparkfun working with Arduino Duemilanove and example code. LEDs indicate communication with the board.

    Nothing prints on the serial monitor unless…. I issue a Q1 or Q2 Command, the serial monitor shows stuff like : •2•ç—0•2—æ•æ—æ
    Am I correct in expecting the serial monitor to show the time (from the registers) when a Q2 command is sent ?

    Thanks,
    Curt

  5. Ray said,

    April 4, 2011 @ 12:43 pm

    Just wanted to thank you for the article/code, worked first time and I learned a lot.

    Cheers

    Ray

  6. Alvaro Justen said,

    April 23, 2011 @ 1:20 am

    Hello,
    I wrote an Arduino library to control ds1307 module. I think it is better (simple) than the code you post.

    More information at:
    – Discussion at Arduino Forum: http://arduino.cc/forum/index.php/topic,59256.0.html
    – Code at GitHub: https://github.com/turicas/DS1307/

    Please let me know if it works with your module. Thanks.

  7. angieb francis said,

    May 27, 2011 @ 10:45 am

    I’m total novice trying to get RTC 1307 module from Sparkfun working with Arduino Duemilanove and example code. LEDs indicate communication with the board.

  8. psypi said,

    August 12, 2011 @ 3:25 pm

    Thanks! I just copy-pasted the code and it worked right away! Leaving the RTC on backup battery for the night! Let’s see the time in the morning!
    Thanks again 🙂

  9. Blaise - French Riviera Wedding Photographer said,

    September 29, 2011 @ 3:22 pm

    Hi,

    Works perfectly, right out of the box. thanks!!! Next in my list with the DS1307 is finding how to store and retrieve data in the 56 remaining bytes (should be easy!)

    Blaise

  10. Messing with AVR Microcontrollers « Pointers Gone Wild said,

    October 4, 2011 @ 2:25 am

    […] UART and got a serial terminal (GtkTerm) communicating with them, and finally, I interfaced with a DS1307 real-time-clock chip through the I2C bus. I decided it would be cool to perhaps make an advanced alarm clock […]

  11. carter said,

    October 15, 2011 @ 11:02 pm

    hi everyone!!i’m try run this code on my arduino uno board..but it doesn’t seem to work for me.
    All I get back:

    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0
    0:0:0 0/0/0 Day_of_week:0

    it’s continue like that..is there any wrong?thanks for helping..

  12. husseinhazime said,

    October 23, 2011 @ 3:22 pm

    i’m lossing time after reconnecting the arduino ?????
    battry is connected and 100% delivering power to the ds1307.

  13. husseinhazime said,

    October 23, 2011 @ 3:23 pm

    i;m lossing data after disconnecting power the backup battery is installed and its working .

  14. mikeroo said,

    November 15, 2011 @ 5:23 pm

    I have the same trouble as husseinhazime.

    The time seems to be reset after disconnecting power, despite having a backup battery installed. (I removed the part of the sketch that sets the clock, so that’s not the problem). I thought that once I set the time on the real time clock module, it would be maintained.

  15. lsq1986 said,

    November 19, 2011 @ 3:11 pm

    kinda have small question or a small issue 0_0 , i wanted to know if its possible to set the time of the RTC to always display the time and not have to input the time with arduino.. yea maybe its only only possible with a small battery but it cant hurt to ask.

  16. Maurice Ribble said,

    November 19, 2011 @ 3:15 pm

    You either need a backup battery or need to not disconnect the power if you don’t want to have to reset the time.

  17. Reloj con el DS1307 | Electrónica práctica said,

    February 14, 2012 @ 11:01 am

    […] ideas me lleva a poner un poco más de mi parte, creo. El código original, se puede descargar de este enlace, si usted lo desea puede compararlo con el siguiente código tiene […]

  18. Roger said,

    February 15, 2012 @ 2:48 am

    RTC_working.cpp: In function ‘void setDateDs1307(byte, byte, byte, byte, byte, byte, byte)’:
    RTC_working:61: error: call of overloaded ‘write(int)’ is ambiguous
    E:\Software\Arduino\arduino-1.0-windows\arduino-1.0\libraries\Wire/Wire.h:55: note: candidates are: virtual size_t TwoWire::write(uint8_t)
    E:\Software\Arduino\arduino-1.0-windows\arduino-1.0\hardware\arduino\cores\arduino/Print.h:49: note: size_t Print::write(const char*)
    RTC_working.cpp: In function ‘void getDateDs1307(byte*, byte*, byte*, byte*, byte*, byte*, byte*)’:
    RTC_working:84: error: call of overloaded ‘write(int)’ is ambiguous
    E:\Software\Arduino\arduino-1.0-windows\arduino-1.0\libraries\Wire/Wire.h:55: note: candidates are: virtual size_t TwoWire::write(uint8_t)
    E:\Software\Arduino\arduino-1.0-windows\arduino-1.0\hardware\arduino\cores\arduino/Print.h:49: note: size_t Print::write(const char*)

    Hi !! i am getting the above problem i dont know how to debug it. i am using Arduino with atmega 328p-Pu

  19. Madison said,

    March 19, 2012 @ 12:54 am

    Roger,

    I’m having the same problem. Did you ever figure it out?

  20. Chris said,

    April 13, 2012 @ 4:42 am

    I fixed the code problems by changing all
    Wire.receive to Wire.read
    and
    Wire.send to Wire.write

    And where its saying the code is ambiguous you have
    Wire.Write(0);

    Change too
    int i = 0;
    Wire.write(i);

    This is because it doesn’t know how to handle 0, you have to give it a int type first, strangely casting it to Wire.write((int)0); doesn’t work, anyone know why?

    Not sure how right all that code is but it works for me. YMMV

  21. Maurice Ribble said,

    April 13, 2012 @ 5:26 am

    Thanks for the explaining the fixes. This is required if you want to use Arduino 1.0 or newer. I was fixing some other libs I use and I think they changed these names so they could inherit more stuff in the Wire class to inherit behavior from some of the Arduino base classes.

    I also ran into that 0 issue. You can also fix it by type casting the 0 to a byte. The reason for this is because of how c++ handles function overloading. It can’t tell if 0 is a valid string pointer and a valid byte so the compiler can’t figure out which overloaded function to choose and gives you the error.

  22. red_car said,

    September 25, 2012 @ 3:44 am

    Very useful.. even after 4.5 years… helped a lot. Nice simple examples are always the best.

    One thing I noticed.. there is no point in trying to to set the Day of Week register (0x03). It seems to be determined automatically by the DS1307 based on the date set… which makes sense I guess.

  23. limchonghan said,

    November 14, 2012 @ 4:24 am

    […] correction for DS1307 on Arduino.cc Forum DS1307 RTC Tutorial at Ladyada DS1307 RTC at bildr Blog An I2C Bus Example Using the DS1307 Real-Time Clock DS1307 […]

  24. Ajit Nayak said,

    February 27, 2013 @ 1:24 am

    can some tell me how did we assigned this address:#define DS1307_I2C_ADDRESS 0x68 . how we get this I2c address

    how they assigned as 68 .

  25. Respecto said,

    May 2, 2013 @ 8:18 pm

    re:how we get this l2c assress

    this may help
    http://playground.arduino.cc//Main/I2cScanner

  26. Nixon said,

    July 7, 2013 @ 8:25 pm

    is there a way we can stop the time from counting until it has been enable? i wanted to set time using a switch.

  27. Ismael said,

    August 24, 2013 @ 10:01 am

    Nice tutorial, everything’s working fine !

RSS feed for comments on this post