DS1307 woes, I2C freezes and locks arduino

I've come across a problem with the DS1307 real time clock module and I thought I'd share my work around solution.

The DS1307 operates on the i2c bus.  I've been using an arduino to query the device.  For some reason my DS1307 chip frequently locks up the i2c bus within the hour.  I've tried adding capacitors to the voltage supply, suspecting noise in the circuit, but to no success.   I tried to make i2c calls less frequently, but really that is not acceptable operation.

What makes this problem worse is that the standard TWI library for the arduino does have a safe time out feature.  Instead, when the i2c stops in the middle of a read or write, the arduino simply stops and waits indefinitely.  This is probably your primary symptom - an unexplained halt to your arduino!  Therefore whatever else your arduino is supposed to be doing is actually at the mercy of an i2c device working reliably.

The solution therefore is to recognise when the i2c bus has frozen, and then reset the DS1307 chip.  Here is how.

Detecting the i2c freeze:

I found an altered TWI library modified with a time out, available here.  It is easy to use this library instead.  Download it and place it into your sketchbook/libraries folder like any other library.

I have alternated between two librarys for the DS1307.  The specific library (here) and a generic library called RTClib from Adafruit (here).  RTClib seems to be more reliable to me.  I experience the DS1307 crash with both.

In either case, to use the modified TWI library.  Change

#include <Wire.h>
#include <WSWire.h>

Now when the i2c bus crashes your arduino will exit the originating call and on your next program iteration any call that checks the status of the device will return a fail ( RTC.isrunning(), RTC.read(), or RTC.chipPresent() ).  

Resetting the DS1307 chip:

There is no reset pin on the DS1307.  Instead, wire the DS1307 Vcc pin to an arduino digital pin instead of the 5v supply.  The DS1307 (datasheet) compares the voltage of its Vcc pin to the BAT pin, and if Vcc is lower it resets the i2c subsystem and denies access until Vcc returns to 5v.  Even when Vcc drops to 0v, the battery maintains the time and date within the DS1307, so we don't have to worry about that.

Therefore, when we detect an i2c failure on the arduino, we simply toggle the digital pin attached to the DS1307 Vcc pin.

CAUTION: when using this method, in any other code, such as the setTime and readTime examples, you'll need to add a pinMode and digitalWrite to supply power to the DS1307.

Here is some example code:

#include <WSWire.h>
#include "RTClib.h"

RTC_DS1307 RTC;  // declaire instance of the chip for RTClib

void setup() {
  Serial.begin( 9600 );    // Serial for debuging.
  Serial.println("Power Up");
  pinMode( 8, OUTPUT );    // DS1307 Vcc connected to digital pin 8
  digitalwrite( 8, HIGH ); // Enable DS1307 chip.
  Wire.begin();            // Start i2c/twi interface
  RTC.begin();             // initialise RTC interface

void loop() {
  // Check if the DS1307 chip is down.
  if( ! RTC.isrunning() ) {
      Serial.println("DS1307 down, attempting chip reset"); 
      digitalWrite( 8, LOW );  // Power down
      delay(20);               // time for any capacitance to discharge
      digitalWrite( 8, HIGH ); // Power back up
  DateTime now = RTC.now();    // Make a read to the DS1307 chip.
                               // With WSWire, this will not lock your 
                               // program if the DS1307 stops working.