Arduino

Arduino Cloc: A Maxim DS1307/MAX7221 Based Clock with Auto Daylight Savings Adjustment

Serially Interfaced, 8 digit 7 Segment Display based on a MAX7221 IC

I have been busying myself recently with some experiments into programming the Arduino prototyping platform. 

I have already created some hardware, a Maxim DS1307 Real Time Clock shield and a Serially Interfaced, 8 digit 7 Segment Display based on a MAX7221 IC.  So far I have created test routines to test my hardware but I wanted to really get my teeth into creating some more practical code. 

And here it is, A more or less functioning LED clock. 

I have included a download of the sketch (Arduino speak for program) that I wrote so that Arduino owners can examine and play with my code, There is a cheesy video blog at the end of the article as well if you want to see the clock in action…

Time and date are input into the clock via a rotary encoder which is attached to the digital pins 2,3 and 4 of the Arduino. 

As I am fairly recent to Arduino programming I had a look around for some guidance on using the Arduino alongside the DS1307 Real Time Clock and it is clearly a popular choice with builders. 

As a starting point I used code from this this tutorial from John Boxall, and with what I had already learned in building my 24 Hour Countdown Sketch I managed to put together a functioning clock with relative ease.  Although I did not use them Adafruit also have a good tutorial on using the DS1307 as well as a library that simplifies somewhat setting and fetching the time from the DS1307 clock. 

As well as finding excellent tutorials on interfacing an Arduino with a Maxim DS1307 real time clock I also uncovered quite a few projects from people using this hardware, it would appear a popular choice.  What I did not see in any of the examples that I found was a clock that catered for Daylight Savings Time. (British Summer Time here in the UK).  Having had a lot of the code for my clock spoon fed to me already, I thought it would be interesting to build on it a bit so my Arduino sketch has that extra trick up it’s sleeve.

British Summer Time is slightly complicated to program for as the clocks do not go forwards and backwards on the same date each year.  The clocks go forwards  at 1am on the last Sunday of the month of March each year and back on the Last Sunday of October at the same time.  Adjusting the time on the correct day was going to require a more complex formula than I initially predicted.  You can see in the code below how I calculate it, basically its through a set of AND and OR statements.  To calculate the correct date you need to take the day of the month and subtract the day of the week (Sunday == 1, Saturday ==7).  The result of this will be greater than 24 in March if we have passed from Winter Time into Summer Time.  The formula is similar for the October changeover.

imageI also added a photo-resistor to analogue pin 0 of my Arduino.  This is soldered to my 7 segment 8 digit LED display module and is used to calculate the ambient light.  photo-resistors (Sometimes called Light Dependent Resistors, LDR, or Photo-cells) are not very accurate measuring devices, but are fine for what I am doing.  My sketch adjusts the display brightness up or down in bright and dim light respectively. 

To add a photo-resistor you also need a resistor of around 10k value,  You can see how I connected them in the diagram to the left. 

The other thing that you will find in my sketch is a small collection of animations that I programmed.  There are various fades, wipes and other animations.  Some are used in the startup sequence the clock goes through.  Some I used during the clock setting routine.  Some did not make the final cut at all but I decided to leave them in the sketch for use later or if anyone else thinks that they are cool. 

Please feel free to use any of my code if it helps you out.  Just be aware that it is probably not the best optimised code out there as it represents pretty much all of my knowledge at this moment in time!



// *****************************************************************
// *                      Arduino Cloc Ver 1                       *
// * A Maxim DS1307 based clock which displays on a Maxim MAX7221/ *
// *  MAX9219 based Serially Interfaced, 8-Digit, 7-Segment, LED   *
// * Display for Arduino.  The clock is set via a cheap mechanical *
// *  rotary encoder with push switch.  Display brightness is set  *
// *  based on the value returned by a Photo-Resistor.  No alarm   *
// *       function in this version, but lots of eye candy!        *
// *  -----------------------------------------------------------  *
// *         Written by Blair Thompson www.justblair.co.uk         *
// *          Thanks to John Boxal for his tutorial at             *
// *              https://tronixstuff.wordpress.com/                *
// *   and to Sunbox for posting his rotary encoder library here   *
// *  https://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1205879808   *
// *    I modified his library only to add keyword higlighting     *
// *****************************************************************

// ************************** Includes *****************************
#include <LedControl.h>             // Adds DS7221/7219 function
#include "Wire.h"                   // We use this library to talk to the DS1307 
#include "RotaryEncoder.h"          // This is a library for cheap mechanical rotary encoders
                                    // it debounces the switch contacts of the encoder

// ******************* Declare Some Constants **********************

//Lets start with the pinouts to the Max72xx led driver
const byte DIN = 12;            // Pin 1  on the Max72xx
const byte CLK = 11;            // Pin 13 on the Max72xx
const byte LOADCS = 10;         // Pin 12 on the Max72xx

// select the pin for the LED, we will flash the lights to show that we are in set mode
const byte led_pin = 13;        // Its the built in LED on the Arduino

// Now the pins of the Rotary Encoder... It's one of those with the push buttons
const byte DATA_A = 3;
const byte DATA_B = 4;
const byte PUSH   = 2;

const byte pot_pin = 0;         // Photo-resistor 

const int DS1307_I2C_ADDRESS = 0x68;     // This is the address of the DS1307 on the I2C bus

// *********** define the RotaryEncoder instance *********************
RotaryEncoder rotary(DATA_A, DATA_B, PUSH);  // declare pins(data A, data B, Push Button)

// ************* define the LedControl instance **********************

//  if I want more I can setup lc2, lc3 etc
LedControl lc=LedControl(DIN,CLK,LOADCS,1);    // DIN, CLK, Load/CS, 1 = only one chip MAX chip attached.

// ***************** We need a few variables as well*****************

// Need some variables to store the time 
byte second, minute, hour, day_of_week, day_of_month, month, year, sqwe, bst_flag;

byte serial_debug     = 1; // if "1" it switches on the debug messages to the serial port
byte serial_show_time = 1; // If "1" it switches on time updates to the serial port

byte chip_id = 0;          // This is not strictly reqd, but if using more than one display this will be needed
byte led_brightness = 10;  // range is 0-15.  0=lowest, 15 = full power
byte row = 0;              // Set the starting position

int val = 0;               // Variable to store the data from the serial port

byte test_delay = 50;      // Sets the speed of the animations

int photo_value = 0;       // Variable to store the data from the photo-resistor


// ****************************************************************
// *          Lets do the setup for one time only                 *
// ****************************************************************

void setup()             // once only, lets make the pins outputs
{ 
  pinMode(led_pin, OUTPUT);
  pinMode(DIN, OUTPUT); 
  pinMode(CLK, OUTPUT); 
  pinMode(LOADCS, OUTPUT); 

  // take pins out of power save mode.  No updates otherwise. 
  for(int index=0;index<lc.getDeviceCount();index++) { 
    lc.shutdown(index,false); 
  } 
  lc.setIntensity(0,led_brightness  ); //set the brightness
  Wire.begin();
  rotary.minimum(0);
  rotary.maximum(60);
  rotary.position(30);

  Serial.begin(9600);
  LEDTest();
  // Before we get started, we will call the current time into the Arduino
  get_date_DS1307(&second, &minute, &hour, &day_of_week, &day_of_month, &month, &year, &sqwe, &bst_flag);
}

// ****************************************************************
// *    The Main Loop... This just keeps going round and round    *
// ****************************************************************

void loop() {          // here comes the good stuff, the main loop!


  // First lets find out if the clocks need to go forward?
  // Check to see if it's the last Sunday in March with an IF/AND statement 
  if (bst_flag == 0 && month == 3 && day_of_week == 1 && day_of_month >24 && hour >0){ 
    bst_start ();
  }
  // Second, perhaps they need to go back?
  // Something similar to before
  if (bst_flag == 1 && month == 10 && day_of_week == 1 && day_of_month >23 && hour >0){
    bst_end ();
  }

  // We will measure the photoresistor and adjust the display brightness
  // read the photo_value of the potentiometer (photo_valueue between 0 and 1023)
  // and then use the map command to inverse/scale the result and set brightness. 
  photo_value = analogRead(pot_pin);                          // Read
  led_brightness = map(photo_value, 0, 1023, 15, 0);         // Map
  lc.setIntensity( 0, led_brightness);                       // Set the brightness

    // Now a quick check to see if the Rotary Encoders Button has been pressed
  if (rotary.pressed()){
    Set();                    // Go to the "Set" routine and set the time and date
  }

  // We can also run the test routine by typing "t" from the serial monitor
  if( Serial.available() ) {
    val = Serial.read();      // read the serial port  
    if(val =='t'){            // If "t" is pressed in the Serial Monitor, 
      LEDTest();              // run the test.
    }
    if(val =='s'){            // If "s" is pressed in the Serial Monitor,
      scrolling_marque();     // Scroll "Arduino Cloc" on the LED
    }
  }

  // On the 5th second of each minute, we will display the date
  if (second == 5){

    led_print(day_of_month, 6);                     // Print the hour
    lc.setChar(chip_id, 5, '-', false);             // Print the divider
    led_print(month, 3);                            // Now the minutes
    lc.setChar(chip_id, 2, '-', false);             // Another divider
    led_print(year, 0);                             // And the year
    delay (2000);                                   // And pause for 2 seconds

    if (serial_debug == 1){                         // Print to the Serial Port
      Serial.print (day_of_month);                  // if debug is on.
      Serial.print ('-');
      Serial.print (month);
      Serial.print ('-');
      Serial.println (year);
    }
  }

  // **************************************************************
  // *      Display the time, this just keeps looping unless      *
  // *      there is an event such as a serial message recd       *
  // *      or the rotary encoder is pressed                      *
  // **************************************************************

  get_date_DS1307(&second, &minute, &hour, &day_of_week, &day_of_month, &month, &year, &sqwe, &bst_flag);
  // Captures the current time from the Maxim clock IC

  led_print(hour, 6);                               // Print the hour
  lc.setChar(chip_id, 5, '-', false);                // Print the divider
  led_print(minute, 3);                             // Now the minutes
  lc.setChar(chip_id, 2, '-', false);                // Another divider
  led_print(second, 0);                             // And the seconds just to make it look exciting!

  // This bit sends the time to the serial port
  if (serial_show_time == 1){
    Serial.print(hour, DEC);// convert the byte variable to a decimal number when being 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(day_of_month, DEC);
    Serial.print("/");
    Serial.print(month, DEC);
    Serial.print("/");
    Serial.print(year, DEC);
    Serial.print("  Day of week:");
    switch(day_of_week){
    case 1: 
      Serial.print("Sunday");
      break;
    case 2: 
      Serial.print("Monday");
      break;
    case 3: 
      Serial.print("Tuesday");
      break;
    case 4: 
      Serial.print("Wednesday");
      break;
    case 5: 
      Serial.print("Thursday");
      break;
    case 6: 
      Serial.print("Friday");
      break;
    case 7: 
      Serial.print("Saturday");
      break;
    }
  }
  if (serial_debug == 1){
    Serial.print(" - Summertime? ");
    Serial.println(bst_flag, DEC);
    delay(100);
  }
}

// *****************************************************************
// *   This function can be called to set the time via Encoder     *
// *  It is a little long winded, but as well as setting the time  *
// *  it checks to see if it's Summer-Time as well and records it  *
// *****************************************************************

void Set(){
  digitalWrite(led_pin, HIGH);             // Sets a LED to on to indicate we are in set mode
  byte set_second, set_minute, set_hour, set_day_of_week, set_day_of_month, set_month, set_year, set_bst_flag;
  // set variables that we can store the newly inputted dates.  I could have reused the variables
  // from the time routine, but I decided that having new ones might be more flexible if I want to 
  // reuse this routine in other projects

  set_second = second;                    // moves the current time over into my "set" variables.  
  set_minute = minute;                    // I do this because I dont want to set the time from 
  set_hour = hour;                        // scratch each time... for instance in a 
  set_day_of_week = day_of_week;
  set_day_of_month = day_of_month;
  set_month = month;
  set_year = year;

  wipe_led_down(false);       
  // A brief transition which also doubles up as debounce to the rotary encoders push switch
  byte set_progress = 0;                  // This just controls the progress of the set routine

  rotary.minimum(0);                      // This sets the max and min values the rotary
  rotary.maximum(23);                     // encoder can return when we set the hours
  rotary.position(set_hour);              // Set the rotary encoder starting position to match prev time

  while (set_progress ==0){
    lc.setChar(0, 7, '5',false);          // If you squint your eyes, the first three digits 
    lc.setChar(0, 6, 'e',false);          // almost read "SET"
    lc.setRow(0, 5, 70);                  // That makes a "T"
    led_print (rotary.position(), 3);     // The rotary encoder position will be displayed on the LED
    lc.setChar(chip_id, 2, '-', false);    // Print the divider
    led_print(minute, 0);                 // Pop the minutes in positions 0 and 1
    if (rotary.pressed()){                // We have selected the value we want, store the value
      set_hour = rotary.position();       
      set_progress++;                     // ....and progress to the next level
      if (serial_debug == 1){             // Send a message to make sure!
        Serial.print ("Hour Set to ");
        Serial.println (set_hour, DEC);
      }
    }
  }

  wipe_led_rl(false);
  // Another transition which also doubles up as debounce to the rotary encoders push switch

  rotary.minimum(0);                      // This sets the max and min values the rotary
  rotary.maximum(59);                     // encoder can return when we set the minutes
  rotary.position(set_minute);            // Set the rotary encoder starting position to match prev time

    while (set_progress == 1){
    lc.setChar(0, 7, '5',false);          // If you squint your eyes, the first three digits 
    lc.setChar(0, 6, 'e',false);          // almost read "SET"
    lc.setRow(0, 5, 70);                  // That makes a "T"              
    led_print (set_hour, 3);
    lc.setChar(chip_id, 2, '-', false);    // Print the divider
    led_print(rotary.position(), 0);
    if (rotary.pressed()){
      set_minute = rotary.position();
      set_progress++;
      if (serial_debug == 1){             // Send a message to make sure!
        Serial.print ("Min Set to ");
        Serial.println (set_minute, DEC);
      }
    }
  }
  wipe_led_rl(false);
  // Another transition which also doubles up as debounce to the rotary encoders push switch

  rotary.minimum(0);                      // This sets the max and min values the rotary
  rotary.maximum(31);                     // encoder can return when we set the minutes
  rotary.position(set_day_of_month);        // Set the rotary encoder starting position to match prev date

  while (set_progress ==2){
    led_print(rotary.position(), 6);
    lc.setChar(chip_id, 5, '-', false);    // Print the divider
    led_print(month, 3);
    lc.setChar(chip_id, 2, '-', false);    // Another divider
    led_print(year, 0);
    if (rotary.pressed()){
      set_day_of_month = rotary.position();
      set_progress++;
      if (serial_debug == 1){             // Send a message to make sure!
        Serial.print ("Day Set to ");
        Serial.println (set_day_of_month, DEC);
      }
    }
  }

  wipe_led_rl(false);
  rotary.minimum(0);
  rotary.maximum(12);
  rotary.position(set_month);
  while (set_progress ==3){
    led_print(set_day_of_month, 6);
    lc.setChar(chip_id, 5, '-', false);                // Print the divider
    led_print(rotary.position(), 3);
    lc.setChar(chip_id, 2, '-', false);                // Another divider
    led_print(year, 0);
    if (rotary.pressed()){
      set_month = rotary.position();
      set_progress++;
    }
  }
  wipe_led_rl(false);
  rotary.minimum(0);
  rotary.maximum(12);
  rotary.position(set_month);
  while (set_progress ==3){
    led_print(set_day_of_month, 6);
    lc.setChar(chip_id, 5, '-', false);                // Print the divider
    led_print(rotary.position(), 3);
    lc.setChar(chip_id, 2, '-', false);                // Another divider
    led_print(year, 0);
    if (rotary.pressed()){
      set_month = rotary.position();
      set_progress++;
    }
  }    
  wipe_led_rl(false);
  rotary.minimum(0);
  rotary.maximum(99);
  rotary.position(set_year);
  while (set_progress ==4){
    led_print(set_day_of_month, 6);
    lc.setChar(chip_id, 5, '-', false);                // Print the divider
    led_print(set_month, 3);
    lc.setChar(chip_id, 2, '-', false);                // Another divider
    led_print(rotary.position(), 0);
    if (rotary.pressed()){
      set_year = rotary.position();
      set_progress++;
    }
  }
  wipe_led_rl(false);
  rotary.minimum(1);
  rotary.maximum(7);
  rotary.position(set_day_of_week);
  while (set_progress == 5){
    lc.setChar(chip_id, 7, 'd', false);
    lc.setChar(chip_id, 6, 'a', false);                // Another divider
    lc.setRow(0, 5, 59); 

    led_print(rotary.position(), 0);
    if (rotary.pressed()){
      set_day_of_week = rotary.position();
      set_progress++;
    }
  } 

  if (set_month > 10 || set_month < 3 
  || (set_month == 10 && (set_day_of_month - set_day_of_week) > 22) 
  || (set_month == 3 && (set_day_of_month - set_day_of_week)<24)){
    Serial.println ("Winter");
    set_bst_flag = 0;    
  }
  else{
    Serial.println ("Summer"); 
    set_bst_flag =1;
  }
  wipe_led_lr(true);
  second = 0;                      // not going to bother with a user input for seconds
  setDateDs1307(second, set_minute, set_hour, set_day_of_week, set_day_of_month, set_month, set_year, set_bst_flag);
  val=0;
  digitalWrite(led_pin, LOW);
}

// *****************************************************************
// *                  Special Effects Functions                    *
// *   Just some eye candy really to handle transisitons on the    *
// *   MAX7221/MAX7219 based display.  Not all are used in the     *
// *   sketch, but I left them in so that you can play with them   *
// *****************************************************************

// ************ Cause we have to show off sometimes ****************
void LEDTest (){
  word_arduino ();
  wipe_led_lr(false);
  word_cloc();
  wipe_led_lr(false);
  wipe_led_one_by_one(true);
  wipe_led_rl(false);
  wipe_led_lr(true);
  wipe_led_up(false);
  wipe_led_down(true);
  val=0;
}

// ****************** Display the word "Arduino" *******************
void word_arduino (){
  lc.clearDisplay(chip_id);  
  lc.setRow(chip_id, 7, 119);  // "A"
  delay(test_delay);
  lc.setRow(chip_id, 6, 5);    // "r"
  delay(test_delay);
  lc.setRow(chip_id, 5, 61);   // "d"
  delay(test_delay);
  lc.setRow(chip_id, 4, 28);   // "u"
  delay(test_delay);
  lc.setRow(chip_id, 3, 4);    // "i"
  delay(test_delay);
  lc.setRow(chip_id, 2, 21);   // "n"
  delay(test_delay);
  lc.setRow(chip_id, 1, 29);   // "0"
  delay(test_delay);
  lc.setRow(chip_id, 0, 0);    // " "
  delay(1000);
}

// ******************* Display the word "cloc" *********************
void word_cloc(){
  lc.clearDisplay(chip_id);
  lc.setRow(chip_id, 5, 78);  // "C"
  delay(test_delay);
  lc.setRow(chip_id, 4,  6);  // "l"
  delay(test_delay);
  lc.setRow(chip_id, 3, 29);  // "o"
  delay(test_delay);
  lc.setRow(chip_id, 2, 13);  // "c"
  delay(1000);
}
// ****************** Scrolling Marque *****************************
void scrolling_marque(){
  byte title_text[] = {
    0,0,0,0,0,0,0,119, 5, 61, 28, 4, 21, 29, 0, 78, 6, 29, 13, 0,0,0,0,0,0,0,0    };
  byte digit_count = 8;
  for (byte count = 0; count < 20; count++){
    int this_digit = 0;
    lc.setRow (chip_id, 7, title_text[count]);
    lc.setRow (chip_id, 6, title_text[count+1]);
    lc.setRow (chip_id, 5, title_text[count+2]);  
    lc.setRow (chip_id, 4, title_text[count+3]);  
    lc.setRow (chip_id, 3, title_text[count+4]);  
    lc.setRow (chip_id, 2, title_text[count+5]);  
    lc.setRow (chip_id, 1, title_text[count+6]);  
    lc.setRow (chip_id, 0, title_text[count+7]);
    delay(test_delay*4);  
  }
}

// *************** Wipe the screen to empty or full ****************

// Right to Left
void wipe_led_rl (boolean on){
  for (int r=0; r<=7; r++){
    lc.setRow(chip_id, r, on);
    delay(test_delay);
  }
}

// ******************** Left to Right *****************************

void wipe_led_lr (boolean on){
  for (int r=7; r>=0; r--){
    int x=0;
    if(on==true){
      x=255;
    }
    lc.setRow(chip_id, r, x);
    delay(test_delay);
  }
}

// ************************ Top to Bottom **************************

void wipe_led_down (boolean on){
  int x=0;
  if(on==true){
    x=255;
  }
  lc.setColumn(chip_id, 1, x);
  delay(test_delay);
  lc.setColumn(chip_id, 2, x);
  lc.setColumn(chip_id, 6, x);
  delay(test_delay);
  lc.setColumn(chip_id, 7, x);
  delay(test_delay);
  lc.setColumn(chip_id, 5, x);
  lc.setColumn(chip_id, 3, x);
  delay(test_delay);
  lc.setColumn(chip_id, 4, x);
  lc.setColumn(chip_id, 0, x);
  delay(test_delay);
}

// ******************* Bottom to Top *******************************

void wipe_led_up (boolean on){
  int x=0;
  if(on==true){
    x=255;
  }
  lc.setColumn(chip_id, 4, x);
  lc.setColumn(chip_id, 0, x);
  delay(test_delay);
  lc.setColumn(chip_id, 3, x);
  lc.setColumn(chip_id, 5, x);
  delay(test_delay);
  lc.setColumn(chip_id, 7, x);
  delay(test_delay);
  lc.setColumn(chip_id, 6, x);
  lc.setColumn(chip_id, 2, x);
  delay(test_delay);
  lc.setColumn(chip_id, 1, x);
  delay(test_delay);
}

// ***************** Super Duper, Led by Led ***********************

void wipe_led_one_by_one(boolean on){

  for (int r=7; r>=0; r--){
    lc.setLed(chip_id, r, 0, on); 
    delay(test_delay);
  }
  for (int r=0; r<=7; r++){
    lc.setLed(chip_id, r, 1, on);
    delay(test_delay);
  }
  for (int r=7; r>=0; r--){
    lc.setLed(chip_id, r, 2, on);
    delay(test_delay);
  }
  for (int r=0; r<=7; r++){
    lc.setLed(chip_id, r, 3, on);
    delay(test_delay);
  }
  for (int r=7; r>=0; r--){
    lc.setLed(chip_id, r, 4, on);
    delay(test_delay);
  }
  for (int r=0; r<=7; r++){
    lc.setLed(chip_id, r, 5, on);
    delay(test_delay);
  }
  for (int r=7; r>=0; r--){
    lc.setLed(chip_id, r, 6, on);
    delay(test_delay);
  }
  for (int r=0; r<=7; r++){
    lc.setLed(chip_id, r, 7, on);
    delay(test_delay);
  }
}

// ******************  Fade Bright or Dull  ***********************
void fade_led (boolean on){
  if(on==true){
    int brightlevel = 0;
    while (brightlevel<led_brightness){
      lc.setIntensity(0,brightlevel  ); //set the brightness 
      delay(test_delay*2);
      Serial.println(brightlevel);
      brightlevel++;
    }
  }
  else{
    int brightlevel = led_brightness;
    while (brightlevel>=0){
      lc.setIntensity(0,brightlevel ); //set the brightness 
      delay(test_delay*2);
      Serial.println(brightlevel);
      brightlevel--;
    }
  }
  if (serial_debug == 1){
    Serial.print("fade");
  }
}

// *****************************************************************
// *                  Print the numbers function                   *
// *  This function takes 2 digit numbers, turns them into a pair  *
// *       of 1 digit numbers and sends them to the MAX7221        *
// *****************************************************************

void led_print(int time_int, int pos){              // Ask for the number and the position to print
  byte ones, tens;                                  // A couple of variables to fill with digits
  ones=time_int%10;                                 // %10 divides by ten and extracts the remainder
  tens=time_int/10%10;                              // Handy for splitting the digits in two for printing
  lc.setDigit(chip_id, pos, (byte) ones, false);    // These two lines send the digits to the Max7221 
  lc.setDigit(chip_id, pos+1, (byte) tens, false);  // one by one
}

// *****************************************************************
// *             These functions talk to the DS1307                *
// *  Based on code from  tronixstuff.com/tutorials in turn based  *
// *          based on code by Maurice Ribble 17-4-2008 -          *
// *         https://www.glacialwanderer.com/hobbyrobotics          *
// *****************************************************************

// Convert normal decimal numbers to binary coded decimal
// The DS1307 stores most of the units it uses in this format
byte dec_to_bcd(byte val){
  return ( (val/10*16) + (val%10) );
}

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

// Gets the date and time from the ds1307
void get_date_DS1307(
byte *second,
byte *minute,
byte *hour,
byte *day_of_week,
byte *day_of_month,
byte *month,
byte *year,
byte *sqwe,
byte *bst_flag)
{
  // Reset the register pointer, this tells the DS1307 where we want to 
  // start reading from (ie, where the time is stored on the chip)
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 9);          // Ask for 9 bytes from the DS1307

  // A few of these need masks because certain bits are control bits
  *second      = bcd_to_dec(Wire.receive() & 0x7f);
  *minute      = bcd_to_dec(Wire.receive());
  *hour        = bcd_to_dec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  *day_of_week = bcd_to_dec(Wire.receive());
  *day_of_month= bcd_to_dec(Wire.receive());
  *month       = bcd_to_dec(Wire.receive());
  *year        = bcd_to_dec(Wire.receive());
  *sqwe        = Wire.receive();                   
  // I was lazy here, I'm reading the Square Wave control so I dont have to address the Ram 
  // in a seperate read cycle
  *bst_flag    = Wire.receive();                  
  // This is a flag to tell the arduino if we are in Summer-time or Standard-time
  if (serial_debug == 1){                      // Send a message to make sure!
    Serial.println ("Time Read");
  }
  // Nothing left to do, all requested bytes are read and understood
}

//Sends the date and time to the DS1307
void setDateDs1307(
byte second,          // 0-59
byte minute,          // 0-59
byte hour,            // 1-23
byte day_of_week,     // 1-7
byte day_of_month,    // 1-28/29/30/31
byte month,           // 1-12
byte year,            // 0-99
byte bst_flag)        // 0-1
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);                   // Reset the register pointer like before
  Wire.send(dec_to_bcd(second));  // 0 to bit 7 starts the clock (ie if there are seconds stored 
  Wire.send(dec_to_bcd(minute));  // in the DS1307 then we are telling the time
  Wire.send(dec_to_bcd(hour));     
  Wire.send(dec_to_bcd(day_of_week));
  Wire.send(dec_to_bcd(day_of_month));
  Wire.send(dec_to_bcd(month));
  Wire.send(dec_to_bcd(year));
  Wire.send(0x10);                 
  // sends 0x10 (hex) 00010000 (binary) to control register - turns on 1hz square wave
  Wire.send(bst_flag);             // 0 == standard-time  1 == summer-time 
  Wire.endTransmission();          // Time sent to DS1307, this functions work is done
  if (serial_debug == 1){          // Send a message to make sure!
    Serial.println ("Time Sent");
  }
}

// *****************************************************************
// *           British Summer Time Correction Functions            *
// *****************************************************************

void bst_start (){                             // Lets put the clock forward
  change_bst_flag(1);                          // Change the BST flag to Summer-time
  Wire.beginTransmission(DS1307_I2C_ADDRESS);  // Begins talking to the DS1307 
  Wire.send(0x2);                              // Points the DS1307 to the internal address for hour  
  Wire.send(dec_to_bcd(hour+1));               // Puts the clocks forward
  Wire.endTransmission();                      // That's it, an hour of sleep lost in a function
}

void bst_end (){                               // Lets put the clock back
  change_bst_flag(0);                          // Change the BST flag to Standard-time
  Wire.beginTransmission(DS1307_I2C_ADDRESS);  // Begins talking to the DS1307 
  Wire.send(0x2);                              // Points the DS1307 to the internal address for hour  
  Wire.send(dec_to_bcd(hour-1));               // Puts the clocks back
  Wire.endTransmission();                      // That's it, an hour longer in bed tonight
}

void change_bst_flag (byte state){             // A mini function for changing the BST flag
  Wire.beginTransmission(DS1307_I2C_ADDRESS);  // Begins talking to the DS1307 
  Wire.send(0x8);                              // Points the DS1307 to the first internal address for RAM
  Wire.send(state);                            // sets flag to say if we are in or out of summertime
  Wire.endTransmission();                      // Thats it done, the flag is set
  if (serial_debug == 1){                      // Send a message to make sure!
    Serial.println ("Flag Set");
  }
}

So there you have it, Ver 1 laid bare. There is a little more mork still to do. The Summer Time correction functions are still pretty crude. At the moment for instance you really want your clock to be on on the key changeover dates (or within a day of them anyway depending on what day of the month Sunday lies. I would also like to add more function to the clock, an Alarm would seem to be an obvious addition

{youtube}dlzOMjgnZ28|600|450|1{/youtube}