2012/06/01

Freeduino/Arduino: Set/Sync Date and Time on DS1307 Real Time Clock Shield

When setting date and time on DS1307 real time clock sensor shield using RTClib.h, there will be some seconds delay between the time on the shield and the standard time, so I need a sketch to adjust the clock on  the shield. And here comes the sketch.

The sketch can be used on the Freeduino/Arduino side to manually set or automatically (using program) sync the date and time of DS1307 RTC (real time clock) sensor shield or breakout.

The sketch will check the range of values keyed in including second, minute, hour, date of the month, month, year, and automatically generate day of the week.

Update: The sketch is updated on May 3, 2013 to be more modular, readable and extendable.

Pins Used

Analog pins 4 and 5 are used for I2C protocol implemented by Maxim DS1307.


Serial Monitor Output

Before Pressing <Enter> Key:

After Pressing <Enter> Key:

Updated Sketch


//
// 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.

// Revised by Befun Hung to set/sync date and time for DS1307 Real Time Clock on Freeduino/Arduino shield.
// June-01-2012
// Revised by Befun Hung to create terminalSync function to be more modular
// May-03-2013
// http://cheaphousetek.blogspot.com/
// Usage: After uploading to Freeduino/Arduino board, open the serial monitor.
// Input Format: YYYY-MM-DD hh:mm:ss <Enter>

#include <Wire.h>
#define DS1307_I2C_ADDRESS 0x68

String datetimeIn;
int TimeSet = 0;
int timeArray[19], checkStatus = 1;
int centuryCode = 6; // for year 2000-2099 (Wikipedia: determination of the day of the week)
int monTable[12] = {0,3,3,6,1,4,6,2,5,0,3,5};
int leapmonTable[12] = {6,2,3,6,1,4,6,2,5,0,3,5};

// 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 printFormatError() {
  Serial.println("Format Error\n");
}
/*
void printValueError() {
  Serial.println("Value Error\n");
}
*/

void setup() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin();
  Serial.begin(9600);
  delay(100);
  Serial.print("Waiting for setting the date and time now.\n");
  Serial.print("Input Format: YYYY-MM-DD HH:MM:SS\n");
  // 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() {
  terminalSync();
}

void terminalSync() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  
  if (Serial.available() == 19 ) {
    for (int i=0;i<19;i++) {
      timeArray[i] = Serial.read();  // Serial.read() read int type ASCII code value 
    }
    // for digits subtract 48 ('0' ASCII code value)
    for (int j=0;j<19;j++) {
      if (timeArray[j] > 47 && timeArray[j] < 58) {
        timeArray[j] -= 48;
      }
      if (timeArray[j] <= 9) {
         Serial.print(timeArray[j]);
      } 
      else {
        Serial.print(char(timeArray[j]));
      }
    }
    Serial.println("");
    // check for digits range
    checkStatus = 1;
    if (timeArray[0] != 2) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[1] != 0) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[2] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[3] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[5] > 1) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[6] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[5]*10+timeArray[6] > 12) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[8] > 3) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[9] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 1 || 
         timeArray[5]*10+timeArray[6] == 3 ||
         timeArray[5]*10+timeArray[6] == 5 ||
         timeArray[5]*10+timeArray[6] == 7 ||
         timeArray[5]*10+timeArray[6] == 8 ||
         timeArray[5]*10+timeArray[6] == 10 ||
         timeArray[5]*10+timeArray[6] == 12) && timeArray[8]*10+timeArray[9] > 31) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 4 || 
         timeArray[5]*10+timeArray[6] == 6 ||
         timeArray[5]*10+timeArray[6] == 9 ||
         timeArray[5]*10+timeArray[6] == 11) && timeArray[8]*10+timeArray[9] > 30) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 2 && year % 4 == 0) && timeArray[8]*10+timeArray[9] > 29) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 2 && year % 4 != 0) && timeArray[8]*10+timeArray[9] > 28) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[11] > 2) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[12] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[11]*10+timeArray[12] > 23) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[14] > 5) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[15] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[14]*10+timeArray[15] > 59) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[17] > 5) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[18] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[17]*10+timeArray[18] > 59) {
      // printValueError();
      checkStatus = 0;
    }
    // Serial.println(checkStatus);
    if (checkStatus) {
      second = timeArray[17]*10+timeArray[18];
      minute = timeArray[14]*10+timeArray[15];
      hour = timeArray[11]*10+timeArray[12];
      // dayOfWeek = 5;
      dayOfMonth = timeArray[8]*10+timeArray[9];
      month = timeArray[5]*10+timeArray[6];
      year = timeArray[2]*10+timeArray[3];
      if ((year % 4) == 0) {
        dayOfWeek = (centuryCode + year + ((year - (year % 4)) / 4) + leapmonTable[month-1] + dayOfMonth) % 7;
      }
      else {
        dayOfWeek = (centuryCode + year + ((year - (year % 4)) / 4) + monTable[month-1] + dayOfMonth) % 7;
      }
      if (dayOfWeek == 0) {
        dayOfWeek += 7;
      }
      setDateDs1307(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);
  }
}


Original Sketch

//
// 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.

// Revised by Befun Hung to set/sync date and time for DS1307 Real Time Clock on Freeduino/Arduino shield.
// 1 June 2012
// File Name: SetSyncDateTime.pde
// http://cheaphousetek.blogspot.com/
// Usage: After uploading to Freeduino/Arduino board, open the serial monitor.
// Input Format: YYYY-MM-DD hh:mm:ss <Enter>
// The sketch works on Arduino IDE 0022

#include <Wire.h>
#define DS1307_I2C_ADDRESS 0x68

String datetimeIn;
int TimeSet = 0;
int timeArray[19], checkStatus = 1;
int centuryCode = 6; // for year 2000-2099 (Wikipedia: determination of the day of the week)
int monTable[12] = {0,3,3,6,1,4,6,2,5,0,3,5};
int leapmonTable[12] = {6,2,3,6,1,4,6,2,5,0,3,5};

// 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 printFormatError() {
  Serial.println("Format Error\n");
}
/*
void printValueError() {
  Serial.println("Value Error\n");
}
*/

void setup() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin();
  Serial.begin(9600);
  delay(100);
  Serial.print("Waiting for setting the date and time now.\n");
  Serial.print("Input Format: YYYY-MM-DD HH:MM:SS\n");
  // 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;
  
  if (Serial.available() == 19 ) {
    for (int i=0;i<19;i++) {
      timeArray[i] = Serial.read();  // Serial.read() read int type ASCII code value 
    }
    // for digits subtract 48 ('0' ASCII code value)
    for (int j=0;j<19;j++) {
      if (timeArray[j] > 47 && timeArray[j] < 58) {
        timeArray[j] -= 48;
      }
      if (timeArray[j] <= 9) {
         Serial.print(timeArray[j]);
      } 
      else {
        Serial.print(char(timeArray[j]));
      }
    }
    Serial.println("");
    // check for digits range
    checkStatus = 1;
    if (timeArray[0] != 2) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[1] != 0) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[2] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[3] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[5] > 1) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[6] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[5]*10+timeArray[6] > 12) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[8] > 3) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[9] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 1 || 
         timeArray[5]*10+timeArray[6] == 3 ||
         timeArray[5]*10+timeArray[6] == 5 ||
         timeArray[5]*10+timeArray[6] == 7 ||
         timeArray[5]*10+timeArray[6] == 8 ||
         timeArray[5]*10+timeArray[6] == 10 ||
         timeArray[5]*10+timeArray[6] == 12) && timeArray[8]*10+timeArray[9] > 31) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 4 || 
         timeArray[5]*10+timeArray[6] == 6 ||
         timeArray[5]*10+timeArray[6] == 9 ||
         timeArray[5]*10+timeArray[6] == 11) && timeArray[8]*10+timeArray[9] > 30) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 2 && year % 4 == 0) && timeArray[8]*10+timeArray[9] > 29) {
      // printValueError();
      checkStatus = 0;
    }
    if ((timeArray[5]*10+timeArray[6] == 2 && year % 4 != 0) && timeArray[8]*10+timeArray[9] > 28) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[11] > 2) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[12] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[11]*10+timeArray[12] > 23) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[14] > 5) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[15] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[14]*10+timeArray[15] > 59) {
      // printValueError();
      checkStatus = 0;
    }
    if (timeArray[17] > 5) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[18] > 9) {
      printFormatError();
      checkStatus = 0;
    }
    if (timeArray[17]*10+timeArray[18] > 59) {
      // printValueError();
      checkStatus = 0;
    }
    // Serial.println(checkStatus);
    if (checkStatus) {
      second = timeArray[17]*10+timeArray[18];
      minute = timeArray[14]*10+timeArray[15];
      hour = timeArray[11]*10+timeArray[12];
      // dayOfWeek = 5;
      dayOfMonth = timeArray[8]*10+timeArray[9];
      month = timeArray[5]*10+timeArray[6];
      year = timeArray[2]*10+timeArray[3];
      if ((year % 4) == 0) {
        dayOfWeek = (centuryCode + year + ((year - (year % 4)) / 4) + leapmonTable[month-1] + dayOfMonth) % 7;
      }
      else {
        dayOfWeek = (centuryCode + year + ((year - (year % 4)) / 4) + monTable[month-1] + dayOfMonth) % 7;
      }
      if (dayOfWeek == 0) {
        dayOfWeek += 7;
      }
      setDateDs1307(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);
  }
}

No comments:

Post a Comment