DS1307 Clock Library -- Set with UDP, New Libraries

I have downloaded the latest time Library. Unfortunately it used the "bytewise UDP" functions to retrieve the time. So I modified a few sketches to simplify things.

This sketch will use the "standard" udp library to read a time server, update the DS1307 RTC, and the display the clock -- it uses the Time Library.

This was tested on a Mega2560, the Ethernet shield and with all the "standard" version 22 libraries.

In a later post in this series I will show the updated libraries - hopefully whoever maintains the librairies will consider adding the code to access the Square Wave Oscillator on the chip..

The next sketch will be a simple routine to simply sync the software clock in the Time Library with the RTC clock -- without updating to NTP server.

You should be able to copy this code into a sketch and update your Clock.

DON"T FORGET! If you want use the SQW output on the clock you somehow need to supply a voltage (and current) to the pin. I just tied a 3.9K resistor to 5Volts, then to the pin. You could also use a digital pin on the Arduino and set the Pull Up resistor to high (supplying a voltage and current) then of course you set the pin to read... The real use of the oscillator is of course to tie it to an External Interrupt and use it to time reading from a analog pin, for example, and then use the clock to stamp the output when you send data to a PC or store the Data on the SD card (on the Ethernet shield).

Hope this all makes sense and is useful to people...

/*
 * Modified by D Robinson Feb 09, 2011
 *******************************
 * Time_NTP_UDP_DS1307.pde
 * Example showing time sync to NTP time source for V022 of Compiler and Time Libray
 *
 * This sketch uses the Ethenet library with the current UDP library. **** Note ****
 * 
 * Also, the time is retrieved from the NTP Packet, then used to update the DS1307 RTC
 * The DS1307 RTC is then used to synchronize the clock in the Time Library.
 * Now that the clock is synched, you can just use a simpler RTC/Time Lib synch to retrieve
 * the time e.g. now() in your sketches.
 * Considering the drift on the DS1307 RTC you should probably synch every day or two.
 * If you add the new libraries you can also access the square wave generator through set2(time, sqvalue)
 * See the setup function below for an example...
 * You only need to run this occasionally to sync to network time standards.
 ***************************************************************************
 */
 
#include <Time.h>
#include <SPI.h>         
#include <Ethernet.h>

#include <Udp.h>

#include <WProgram.h>;
#include <Wire.h>
#include <DS1307RTC.h>

/*
 *
 * You may need to modify the Udp library to allow enough space in the buffers for the NTP packets.
 * Open up UdpBytewse.h and set the following buffers to 64 bytes:
 *    #define UDP_TX_PACKET_MAX_SIZE 64
 *    #define UDP_RX_PACKET_MAX_SIZE 64
 */

byte mac[] = {  
  0x90, 0xA2, 0xDA, 0x00, 0x27, 0x7A };
byte ip[] = { 
  192,168,0,80 };

unsigned int localPort = 8888;      // local port to listen for UDP packets

 byte SNTP_server_IP[]    = { 192, 43, 244, 18}; // time.nist.gov
 
// byte SNTP_server_IP[] = { 132,246,11,227};    // time.chu.nrc.ca weird time 64ms RT time
//byte SNTP_server_IP[] = { 130,149,17,21};    // ntps1-0.cs.tu-berlin.de
//byte SNTP_server_IP[] = { 192,53,103,108};   // ptbtime1.ptb.de


time_t prevDisplay = 0; // when the digital clock was displayed
time_t yy;
uint8_t zz;

unsigned long xx;

long HoursOffset = -5;
long SecondsInMinute = 60;
long MinutesInHour =60;
long timeZoneOffset; // set this to the offset in seconds to your local time;
long correctionfactor = 1;
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 

void setup() 
{
  Serial.begin(9600);
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  Serial.println("waiting for sync -- tzo");
  timeZoneOffset = (HoursOffset * MinutesInHour * SecondsInMinute);
  // no delays in this group...

  yy= getNtpTime();
  zz= 0x91;
/*  RTC.set2 adds the Square Wave Generator setup
    
    0x90 sets to one blip a second
    0x91 sets the oscilator to a 4,096 KHz Square Wave
    0x92 sets the oscillator to a 8,192 KHz Sq. Wv.
    0x93 sets the oscillator to a 32768 KHz Sq. Wv. (XTAL Frequncy)
    
*/
 // RTC.set2(yy,zz);  // use this function with the modified libraries to set RTC
    RTC.set(yy);  //use this function with unmodified libraries to Set RTC

  /* Now that we have set RTC with the NTP signal -- we can sych the software timer
     using setSyncProvider.
     This will allow us to turn off the Arduino, and the load other programs
     which only need to sync to the RTC -- and don't have to update to an
     NTP server -- unless you want the most accurate time.
     If you believe the clock is drifiting -- run this program as appropriate
     or include the functionality on you program.
  */
  setSyncProvider(RTC.get); 
  while(timeStatus()== timeNotSet); // wait until the time is set by the sync provider

}

void loop()
{  
  if( now() != prevDisplay) //update the display only if the time has changed
  {
    prevDisplay = now();
    digitalClockDisplay();  
  }
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

/*-------- NTP code ----------*/

unsigned long getNtpTime()
{
  sendNTPpacket(SNTP_server_IP);
  delay(1000);// this delay is required...

  if ( Udp.available() ) {
    Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  

    // now convert NTP time into everyday time:
    const unsigned long seventy_years = 2208988800UL;     // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventy_years;
    epoch = epoch + timeZoneOffset + correctionfactor;  //do this here or we get apparent over/under flow
    return epoch;      
  }
  return 0; // return 0 if unable to get the time
}

unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  Serial.println("Sending packet to server...");
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123

}

Following on from the previous post...

Now if you only wish to use the RTC clock -- remember that you just set the clock Via an NTP server... So it should keep reasonable time for a while -- maybe a few days even.

This sketch simply syncs the software clock to the RTC -- which we set previously. When you load this program -- let it run for a while then see if you are getting much drift.

Any drift could be due to the arduino, or to the RTC. Maybe you can try setting an alarm to call the sync routine every hour or two -- then you could determine if it is the RTC or the internal Time Library routines. If calling the RTC Sync routine every hour keeps you "on time" -- then it is your arduino drifting -- so just add that routine to your program.

Maybe someone else can suggest a routine to add to our programs...

Here is the code

/*
 * TimeRTCSet.pde
 * example code illustrating Time library with Real Time Clock.
 *
 * Modified by D. Robinson Feb 10, 2011
 * RTC clock is set in response to RTC DS1307 
 * It does not sync to a PC or an NTP server
 * It only syncs the software clock in the Time Library to the DS1307 RTC
 *
 * So REMEMBER this routine depends on you having previously set the RTC in some manner
 */

#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

int myFactor;
void setup()  {
  Serial.begin(9600);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time"); 
  myFactor = 0;
  adjustTime(myFactor);     
}

void loop()
{
   //just read the clock after syching with the RTC DS1307
   digitalClockDisplay();  
   delay(1000);
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

have fun!

Here is the mod to the Time Library...

Al this does is support access to the Control Byte -- byte[7] on the DS1307 (That is the eighth byte if you start counting a ZERO like us normal people).

So I will include two sets of code -- the time.h and the time .cpp

All I did was add access to an additional byte in the tm array, add the enumerators and make sure it was accessible...

Any messing up of the code is due to me -- not the original author...

The DS1307 Libs will follow....

time.h here

/*
  time.h - low level time and date functions
  modified by D, Robinson Feb10, 2011 -- added access to square wave control byte
  see tmSQControl for example...
  and this below...
  uint8_t SQControl; // see DS1307 Sheet dwr feb10/2011  ** added tmSQControl above
*/

#ifndef _Time_h
#define _Time_h

#include <inttypes.h>

typedef unsigned long time_t;

typedef enum {timeNotSet, timeNeedsSync, timeSet
}  timeStatus_t ;

typedef enum {
    dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;

typedef enum {
    tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmSQControl,tmNbrFields
} tmByteFields;	   

typedef struct  { 
  uint8_t Second; 
  uint8_t Minute; 
  uint8_t Hour; 
  uint8_t Wday;   // day of week, sunday is day 1
  uint8_t Day;
  uint8_t Month; 
  uint8_t Year;   // offset from 1970;
  uint8_t SQControl; // see DS1307 Sheet dwr feb10/2011  ** added tmSQControl above
} 	tmElements_t, TimeElements, *tmElementsPtr_t;

//convenience macros to convert to and from tm years 
#define  tmYearToCalendar(Y) ((Y) + 1970)  // full four digit year 
#define  CalendarYrToTm(Y)   ((Y) - 1970)
#define  tmYearToY2k(Y)      ((Y) - 30)    // offset is from 2000
#define  y2kYearToTm(Y)      ((Y) + 30)   

typedef time_t(*getExternalTime)();
//typedef void  (*setExternalTime)(const time_t); // not used in this version


/*==============================================================================*/
/* Useful Constants */
#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)
#define DAYS_PER_WEEK (7UL)
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
#define SECS_YR_2000  (946684800UL) // the time at the start of y2k
 
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight 
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY ) // time at the end of the given day 
#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  (dayOfWeek(_time_) * SECS_PER_DAY) )   

/* Useful Macros for converting elapsed time to a time_t */
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)  
#define hoursToTime_t   ((H)) ( (H) * SECS_PER_HOUR)  
#define daysToTime_t    ((H)) ( (D) * SECS_PER_DAY) 
#define weeksToTime_t   ((W)) ( (W) * SECS_PER_WEEK)   

/*============================================================================*/
/*  time and date functions   */
int     hour();            // the hour now 
int     hour(time_t t);    // the hour for the given time
int     hourFormat12();    // the hour now in 12 hour format
int     hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM();            // returns true if time now is AM
uint8_t isAM(time_t t);    // returns true the given time is AM
uint8_t isPM();            // returns true if time now is PM
uint8_t isPM(time_t t);    // returns true the given time is PM
int     minute();          // the minute now 
int     minute(time_t t);  // the minute for the given time
int     second();          // the second now 
int     second(time_t t);  // the second for the given time
int     day();             // the day now 
int     day(time_t t);     // the day for the given time
int     weekday();         // the weekday now (Sunday is day 1) 
int     weekday(time_t t); // the weekday for the given time 
int     month();           // the month now  (Jan is month 1)
int     month(time_t t);   // the month for the given time
int     year();            // the full four digit year: (2009, 2010 etc) 
int     year(time_t t);    // the year for the given time

time_t now();              // return the current time as seconds since Jan 1 1970 
void    setTime(time_t t);
void    setTime(int hr,int min,int sec,int day, int month, int yr);
void    adjustTime(long adjustment);

/* date strings */ 
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
char* monthStr(uint8_t month);
char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day);
	
/* time sync functions	*/
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void    setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void    setSyncInterval(time_t interval); // set the number of seconds between re-sync

/* low level functions to convert to and from system time                     */
void breakTime(time_t time, tmElements_t &tm);  // break time_t into elements
time_t makeTime(tmElements_t &tm);  // convert time elements into time_t


#endif /* _Time_h */

I will post the cpp following....

Here is the cpp code to match the previous time .h

/*
  time.c - low level time and date functions
  Copyright (c) Michael Margolis 2009

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  
  10 February 2011 -- Time.h Modified by D. Robinson to add access to Square Wave Byte... on DS1307 RTC
     Probably only the Header was modified -- but noted here for clarity
     DS1307 RTC lib modifies as well

  6  Jan 2010 - initial release 
  12 Feb 2010 - fixed leap year calculation error
  1  Nov 2010 - fixed setTime bug (thanks to Korman for this)
*/

#include <WProgram.h> 

#include "Time.h"

static tmElements_t tm;          // a cache of time elements
static time_t       cacheTime;   // the time the cache was updated
static time_t       syncInterval = 300;  // time sync will be attempted after this many seconds

void refreshCache( time_t t){
  if( t != cacheTime)
  {
    breakTime(t, tm); 
    cacheTime = t; 
  }
}

int hour() { // the hour now 
  return hour(now()); 
}

int hour(time_t t) { // the hour for the given time
  refreshCache(t);
  return tm.Hour;  
}

int hourFormat12() { // the hour now in 12 hour format
  return hourFormat12(now()); 
}

int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
  refreshCache(t);
  if( tm.Hour == 0 )
    return 12; // 12 midnight
  else if( tm.Hour  > 12)
    return tm.Hour - 12 ;
  else
    return tm.Hour ;
}

uint8_t isAM() { // returns true if time now is AM
  return !isPM(now()); 
}

uint8_t isAM(time_t t) { // returns true if given time is AM
  return !isPM(t);  
}

uint8_t isPM() { // returns true if PM
  return isPM(now()); 
}

uint8_t isPM(time_t t) { // returns true if PM
  return (hour(t) >= 12); 
}

int minute() {
  return minute(now()); 
}

int minute(time_t t) { // the minute for the given time
  refreshCache(t);
  return tm.Minute;  
}

int second() {
  return second(now()); 
}

int second(time_t t) {  // the second for the given time
  refreshCache(t);
  return tm.Second;
}

int day(){
  return(day(now())); 
}

int day(time_t t) { // the day for the given time (0-6)
  refreshCache(t);
  return tm.Day;
}

int weekday() {   // Sunday is day 1
  return  weekday(now()); 
}

int weekday(time_t t) {
  refreshCache(t);
  return tm.Wday;
}
   
int month(){
  return month(now()); 
}

int month(time_t t) {  // the month for the given time
  refreshCache(t);
  return tm.Month;
}

int year() {  // as in Processing, the full four digit year: (2009, 2010 etc) 
  return year(now()); 
}

int year(time_t t) { // the year for the given time
  refreshCache(t);
  return tmYearToCalendar(tm.Year);
}

/*============================================================================*/	
/* functions to convert to and from system time */
/* These are for interfacing with time serivces and are not normally needed in a sketch */

// leap year calulator expects year argument as years offset from 1970
#define LEAP_YEAR(Y)     ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )

static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
 
void breakTime(time_t time, tmElements_t &tm){
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!

  uint8_t year;
  uint8_t month, monthLength;
  unsigned long days;
  
  tm.Second = time % 60;
  time /= 60; // now it is minutes
  tm.Minute = time % 60;
  time /= 60; // now it is hours
  tm.Hour = time % 24;
  time /= 24; // now it is days
  tm.Wday = ((time + 4) % 7) + 1;  // Sunday is day 1 
  
  year = 0;  
  days = 0;
  while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
    year++;
  }
  tm.Year = year; // year is offset from 1970 
  
  days -= LEAP_YEAR(year) ? 366 : 365;
  time  -= days; // now it is days in this year, starting at 0
  
  days=0;
  month=0;
  monthLength=0;
  for (month=0; month<12; month++) {
    if (month==1) { // february
      if (LEAP_YEAR(year)) {
        monthLength=29;
      } else {
        monthLength=28;
      }
    } else {
      monthLength = monthDays[month];
    }
    
    if (time >= monthLength) {
      time -= monthLength;
    } else {
        break;
    }
  }
  tm.Month = month + 1;  // jan is month 1  
  tm.Day = time + 1;     // day of month
}

time_t makeTime(tmElements_t &tm){   
// assemble time elements into time_t 
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
  
  int i;
  time_t seconds;

  // seconds from 1970 till 1 jan 00:00:00 of the given year
  seconds= tm.Year*(SECS_PER_DAY * 365);
  for (i = 0; i < tm.Year; i++) {
    if (LEAP_YEAR(i)) {
      seconds +=  SECS_PER_DAY;   // add extra days for leap years
    }
  }
  
  // add days for this year, months start from 1
  for (i = 1; i < tm.Month; i++) {
    if ( (i == 2) && LEAP_YEAR(tm.Year)) { 
      seconds += SECS_PER_DAY * 29;
    } else {
      seconds += SECS_PER_DAY * monthDays[i-1];  //monthDay array starts from 0
    }
  }
  seconds+= (tm.Day-1) * SECS_PER_DAY;
  seconds+= tm.Hour * SECS_PER_HOUR;
  seconds+= tm.Minute * SECS_PER_MIN;
  seconds+= tm.Second;
  return seconds; 
}
/*=====================================================*/	
/* Low level system time functions  */

static time_t sysTime = 0;
static time_t prevMillis = 0;
static time_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;

getExternalTime getTimePtr;  // pointer to external sync function
//setExternalTime setTimePtr; // not used in this version

#ifdef TIME_DRIFT_INFO   // define this to get drift data
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync  
#endif


time_t now(){
  while( millis() - prevMillis >= 1000){      
    sysTime++;
    prevMillis += 1000;	
#ifdef TIME_DRIFT_INFO
    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
#endif	
  }
  if(nextSyncTime <= sysTime){
	if(getTimePtr != 0){
	  time_t t = getTimePtr();
      if( t != 0)
        setTime(t);
      else
        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;        
    }
  }  
  return sysTime;
}

void setTime(time_t t){ 
#ifdef TIME_DRIFT_INFO
 if(sysUnsyncedTime == 0) 
   sysUnsyncedTime = t;   // store the time of the first call to set a valid Time   
#endif

  sysTime = t;  
  nextSyncTime = t + syncInterval;
  Status = timeSet; 
  prevMillis = millis();  // restart counting from now (thanks to Korman for this fix)
} 

void  setTime(int hr,int min,int sec,int dy, int mnth, int yr){
 // year can be given as full four digit year or two digts (2010 or 10 for 2010);  
 //it is converted to years since 1970
  if( yr > 99)
      yr = yr - 1970;
  else
      yr += 30;  
  tm.Year = yr;
  tm.Month = mnth;
  tm.Day = dy;
  tm.Hour = hr;
  tm.Minute = min;
  tm.Second = sec;
  setTime(makeTime(tm));
}

void adjustTime(long adjustment){
  sysTime += adjustment;
}

timeStatus_t timeStatus(){ // indicates if time has been set and recently synchronized
  return Status;
}

void setSyncProvider( getExternalTime getTimeFunction){
  getTimePtr = getTimeFunction;  
  nextSyncTime = sysTime;
  now(); // this will sync the clock
}

void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
  syncInterval = interval;
}

There should not be any changes -- but who remembers these things???

OK Finally -- this is what it's all about..

PS: Before you ask. I made a "set2" function so that anyone using this code would not break other programs. They can continue to use the "set" function.

I tested other programs that use the time library and could not find anything that was broken by the changes -- mainly because the original author directly access each array location and does not iterate through the locations with a for..next loop.

First the new Keywords file, then the .h file then the cpp file...

Keywords.h

#######################################
# Syntax Coloring Map For DS1307RTC
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

#######################################
# Methods and Functions (KEYWORD2)
#######################################
get	KEYWORD2
set KEYWORD2
read KEYWORD2
write KEYWORD2
set2 KEYWOORD2
#######################################
# Instances (KEYWORD2)
#######################################
RTC
#######################################
# Constants (LITERAL1)
#######################################

Ok Now the header file...

/*
 * DS1307RTC.h - library for DS1307 RTC
 * This library is intended to be uses with Arduino Time.h library functions
 * set2 added Feb 10, 2011 by D. Robinson.
*  This is for Square Wave Oscillator control
 */

#ifndef DS1307RTC_h
#define DS1307RTC_h

#include <Time.h>

// library interface description
class DS1307RTC
{
  // user-accessible "public" interface
  public:
    DS1307RTC();
    static time_t get();
	static void set(time_t t);
      static void set2(time_t t, uint8_t sq);	//added for sq wave dwr feb 10, 2011
      static void read(tmElements_t &tm);
	static void write(tmElements_t &tm);

  private:
	static uint8_t dec2bcd(uint8_t num);
    static uint8_t bcd2dec(uint8_t num);
};

extern DS1307RTC RTC;

#endif

Here is the CPP code

/*
 * DS1307RTC.h - library for DS1307 RTC
  
  Copyright (c) Michael Margolis 2009
  This library is intended to be uses with Arduino Time.h library functions

  The library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  
  11 Feb 2011 Modified by D. Robinson // added set2 function to controll square wave 
     setting the SQ byte as 90, 91,92 or 93 HEX gives 1Hz, 4096HZ, 8192HZ or 32768Hz at thew SQ pin
     Assuming you added the required Pull up resistor (3.9K for example) to 5V
  30 Dec 2009 - Initial release
 */

#include <Wire.h>
#include "DS1307RTC.h"

#define DS1307_CTRL_ID 0x68 

DS1307RTC::DS1307RTC()
{
  Wire.begin();
}
  
// PUBLIC FUNCTIONS
time_t DS1307RTC::get()   // Aquire data from buffer and convert to time_t
{
  tmElements_t tm;
  read(tm);
  return(makeTime(tm));
}

void  DS1307RTC::set(time_t t)
{
  tmElements_t tm;
  breakTime(t, tm);
  tm.Second |= 0x80;  // stop the clock
  write(tm); 
  tm.Second &= 0x7f;  // start the clock
  write(tm); 
}

// Aquire data from the RTC chip in BCD format
void DS1307RTC::read( tmElements_t &tm)
{
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.send(0x00);
  Wire.endTransmission();

  // request the 7 data fields   (secs, min, hr, dow, date, mth, yr)
  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
  
  tm.Second = bcd2dec(Wire.receive() & 0x7f);   
  tm.Minute = bcd2dec(Wire.receive() );
  tm.Hour =   bcd2dec(Wire.receive() & 0x3f);  // mask assumes 24hr clock
  tm.Wday = bcd2dec(Wire.receive() );
  tm.Day = bcd2dec(Wire.receive() );
  tm.Month = bcd2dec(Wire.receive() );
  tm.Year = y2kYearToTm((bcd2dec(Wire.receive())));
  tm.SQControl = (Wire.receive() );
}

void DS1307RTC::write(tmElements_t &tm)
{
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.send(0x00); // reset register pointer
  
  Wire.send(dec2bcd(tm.Second)) ;   
  Wire.send(dec2bcd(tm.Minute));
  Wire.send(dec2bcd(tm.Hour));      // sets 24 hour format
  Wire.send(dec2bcd(tm.Wday));   
  Wire.send(dec2bcd(tm.Day));
  Wire.send(dec2bcd(tm.Month));
  Wire.send(dec2bcd(tmYearToY2k(tm.Year)));
  Wire.send(tm.SQControl);   

  Wire.endTransmission();  
}

void  DS1307RTC::set2(time_t t, uint8_t sq)
{
  tmElements_t tm;
  breakTime(t, tm);
  tm.SQControl = sq; 
  tm.Second |= 0x80;  // stop the clock
  write(tm); 
  tm.Second &= 0x7f;  // start the clock
  write(tm); 
}


// PRIVATE FUNCTIONS

// Convert Decimal to Binary Coded Decimal (BCD)
uint8_t DS1307RTC::dec2bcd(uint8_t num)
{
  return ((num/10 * 16) + (num % 10));
}

// Convert Binary Coded Decimal (BCD) to Decimal
uint8_t DS1307RTC::bcd2dec(uint8_t num)
{
  return ((num/16 * 10) + (num % 16));
}

DS1307RTC RTC = DS1307RTC(); // create an instance for the user

Now you should be able to replace your time lib and generate square waves to your hearts content.

Anyone looking for an elegant solution is disappointed I''m sure. But the code does work and the square waves do appear. I checked -- with an oscilloscope and a frequency counter:-)

Hopefully this is the final modification to the library DS1307RTC supplied with the Time.h library found on this site.

I have added the NVSRAM access functions created by Matt Joyce. There were only minor modifications to make them compatible with the DS1307RTC lib and the Time.h lib.

These routines are based on the work developed by Matt Joyce -- so if they work give him credit -- if not blame me.

Included in this library is the Keywords, the .h file and the .cpp file

First the Keywords...

#######################################
# Syntax Coloring Map For DS1307RTC
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

#######################################
# Methods and Functions (KEYWORD2)
#######################################
get	KEYWORD2
set KEYWORD2
read KEYWORD2
write KEYWORD2
set2 KEYWORD2
set_sram_data KEYWORD2
get_sram_data KEYWORD2
get_sram_byte KEYWORD2
set_sram_byte KEYWORD2

#######################################
# Instances (KEYWORD2)
#######################################
RTC
#######################################
# Constants (LITERAL1)
#######################################

The .h file

/*
 * DS1307RTC.h - library for DS1307 RTC
 * This library is intended to be used with Arduino Time.h library functions
 * 11 Feb 2011 Modified by D. Robinson // added the NVSRAM functions for 56 bytes starting at Location 8 
 * Feb 10, 2011 by D. Robinson. //set2 added 
 * set2 is for Square Wave Oscillator control
 * Matt Joyce originally wrote the NVSRAM functions I modified the slightly
 * for compatibility with the Time.h library 
*/

#ifndef DS1307RTC_h
#define DS1307RTC_h

#include <Time.h>
#define DS1307_DATASTART 0x08  //added dwr feb 11, 2011

// library interface description
class DS1307RTC
{
  // user-accessible "public" interface
  public:
    DS1307RTC();
    static time_t get();
	static void set(time_t t);
      static void set2(time_t t, uint8_t sq);	//added for sq wave dwr feb 10, 2011
      static void read(tmElements_t &tm);
	static void write(tmElements_t &tm);
      void get_sram_data(uint8_t *);
      void set_sram_data(uint8_t *);
      uint8_t get_sram_byte(int);
      void set_sram_byte(uint8_t, int);

  // library-accessible "private" interface
  private:
	static uint8_t dec2bcd(uint8_t num);
    static uint8_t bcd2dec(uint8_t num);
};

extern DS1307RTC RTC;

#endif

finally the .cpp file

/*
 * DS1307RTC.cpp - library for DS1307 RTC
  
  Copyright (c) Michael Margolis 2009
  This library is intended to be uses with Arduino Time.h library functions

  The library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  11 Feb 2011 Modified by D. Robinson // added the NVSRAM functions for 56 bytes starting at Location 8 
  10 Feb 2011 Modified by D. Robinson // added set2 function to controll square wave 
     setting the SQ byte as 90, 91,92 or 93 HEX gives 1Hz, 4096HZ, 8192HZ or 32768Hz at thew SQ pin
     Assuming you added the required Pull up resistor (3.9K for example) to 5V
  30 Dec 2009 - Initial release
 */

#include <Wire.h>
#include "DS1307RTC.h"

#define DS1307_CTRL_ID 0x68 

DS1307RTC::DS1307RTC()
{
  Wire.begin();
}
  
// PUBLIC FUNCTIONS
time_t DS1307RTC::get()   // Aquire data from buffer and convert to time_t
{
  tmElements_t tm;
  read(tm);
  return(makeTime(tm));
}

void  DS1307RTC::set(time_t t)
{
  tmElements_t tm;
  breakTime(t, tm);
  tm.Second |= 0x80;  // stop the clock
  write(tm); 
  tm.Second &= 0x7f;  // start the clock
  write(tm); 
}

// Aquire data from the RTC chip in BCD format
void DS1307RTC::read( tmElements_t &tm)
{
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.send(0x00);
  Wire.endTransmission();

  // request the 7 data fields   (secs, min, hr, dow, date, mth, yr)
  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
  
  tm.Second = bcd2dec(Wire.receive() & 0x7f);   
  tm.Minute = bcd2dec(Wire.receive() );
  tm.Hour =   bcd2dec(Wire.receive() & 0x3f);  // mask assumes 24hr clock
  tm.Wday = bcd2dec(Wire.receive() );
  tm.Day = bcd2dec(Wire.receive() );
  tm.Month = bcd2dec(Wire.receive() );
  tm.Year = y2kYearToTm((bcd2dec(Wire.receive())));
  tm.SQControl = (Wire.receive() );
}

void DS1307RTC::write(tmElements_t &tm)
{
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.send(0x00); // reset register pointer
  
  Wire.send(dec2bcd(tm.Second)) ;   
  Wire.send(dec2bcd(tm.Minute));
  Wire.send(dec2bcd(tm.Hour));      // sets 24 hour format
  Wire.send(dec2bcd(tm.Wday));   
  Wire.send(dec2bcd(tm.Day));
  Wire.send(dec2bcd(tm.Month));
  Wire.send(dec2bcd(tmYearToY2k(tm.Year)));
  Wire.send(tm.SQControl);   

  Wire.endTransmission();  
}

void  DS1307RTC::set2(time_t t, uint8_t sq)
{
  tmElements_t tm;
  breakTime(t, tm);
  tm.SQControl = sq; 
  tm.Second |= 0x80;  // stop the clock
  write(tm); 
  tm.Second &= 0x7f;  // start the clock
  write(tm); 
}

void DS1307RTC::get_sram_data(uint8_t *sram_data)
{
  // set the register to the sram area and read 56 bytes
  Wire.beginTransmission(DS1307_CTRL_ID);
    Wire.send(DS1307_DATASTART);
  Wire.endTransmission();
  
  for(int i=0;i<56;i++)
  {
        Wire.requestFrom(DS1307_CTRL_ID, 56);
    sram_data[i]=Wire.receive();
  }
}

void DS1307RTC::set_sram_data(uint8_t *sram_data)
{
  // set the register to the sram area and save 56 bytes
  Wire.beginTransmission(DS1307_CTRL_ID);
    Wire.send(DS1307_DATASTART);
  
  for(int i=0;i<56;i++)
  {
    Wire.send(sram_data[i]);
  }
  Wire.endTransmission();
}

uint8_t DS1307RTC::get_sram_byte(int p)
{
    // set the register to a specific the sram location and read a single byte
    Wire.beginTransmission(DS1307_CTRL_ID);
    Wire.send(DS1307_DATASTART+p);
    Wire.endTransmission();  
    Wire.requestFrom(DS1307_CTRL_ID, 1);
    return Wire.receive();
}

void DS1307RTC::set_sram_byte(uint8_t b, int p)
{
    // set the register to a specific the sram location and save a single byte
    Wire.beginTransmission(DS1307_CTRL_ID);
    Wire.send(DS1307_DATASTART+p);
    Wire.send(b);
    Wire.endTransmission();  
}



// PRIVATE FUNCTIONS

// Convert Decimal to Binary Coded Decimal (BCD)
uint8_t DS1307RTC::dec2bcd(uint8_t num)
{
  return ((num/10 * 16) + (num % 10));
}

// Convert Binary Coded Decimal (BCD) to Decimal
uint8_t DS1307RTC::bcd2dec(uint8_t num)
{
  return ((num/16 * 10) + (num % 16));
}

DS1307RTC RTC = DS1307RTC(); // create an instance for the user

If anyone has problems just post here I will try to sort it out.

OK just one more post showing how to sync to the DS1307 RTC and simultaneously demonstrating the NVSRAM access (Non Volatile Static Random Access Memory for those who are jargon challenged and/or sick of acronyms)...

Again this code is an adaptation of the demonstration of the example code in the library and the code by Matt Joyce.

So install the time.h library and then make these updates. I put the time library and the DS1307RTC library just below the library directory -- that way it is not required to do anything but use the library name in declarations...

This sketch assumes that you used the NTP routine or one of the other demonstration programs in the Time.h library to previously set the DS1307 RTC...

/*
 * TimeRTCSet.pde
 * example code illustrating Time library with Real Time Clock.
 * Modified by D. Robinson Feb 11, 2011
 * Added the NVSRAM demonstration, it requires the latest libraries.
 * Modified by D. Robinson Feb 10, 2011
 * RTC clock is set in response to RTC DS1307 
 * It does not sync to a PC or an NTP server
 * It only syncs the software clock in the Time Library to the DS1307 RTC
 *
 * So REMEMBER this routine depends on you having previously set the RTC in some manner
 */

#include <Time.h>  
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t

int myFactor;
boolean loopit = true;

void setup()  {
  Serial.begin(9600);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time"); 
  myFactor = 0;
  adjustTime(myFactor); 

//lets's test the SRAM
   for(int i=0; i<56; i++)
  {
    RTC.set_sram_byte(65+i,i);
  }
  loopit = true;
}

void loop()
{
   //just read the clock after syching with the RTC DS1307
   digitalClockDisplay();  
   delay(1000);

// the boolean "if" is so that we just print the NVSRAM once
  if (loopit == true) {
//  Print the NVSRAM Contents 
    Serial.println("DS1307 NVSRAM Contents of Bytes 0 to 55  ");  
    for (int i=0;i<56;i++){
      Serial.print(RTC.get_sram_byte(i));
      if (i < 55) { Serial.print(", ");}//skip the last comma -- not needed
    }// for i=0 to 55
  Serial.println();
  loopit = false;
  } //if loopit
} //end main loop


void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

FYI,

In some other thread I suggested to use the NVRAM to store the timestamp when the RTC was last synced with NTP. This helps to estimate the deviation of the clock, e.g. if one knows the RTC deviates 10 second per week one can calculate a better approximation of the exact time.

Rob

Excellent suggestion on the time stamp. Do you think maybe it is worth adding to the library? Maybe as an automatic function whenever an NTP (or other) sync is received?

Any opinions?

Adding it to the library makes only sense if the purpose is clear. Holding the latest sync time gives insight when to resync again, especially after reboots this info is very functional.

A more elaborated version one could log far more in the EEPROM. Besides the sync time the sync source and the offset could be logged. 4 bytes for time, 1 for source and 1 for offset == 6 bytes total. IIRC there are 52 bytes eeprom, meaning 8 sync times can be logged. Example:

  • 10 jan 2011 12:00 NTP -4
  • 11 jan 2011 12:00 NTP -5
  • 12 jan 2011 12:00 NTP -4
  • 13 jan 2011 12:00 NTP -4
  • 14 jan 2011 12:00 NTP -4
  • 15 jan 2011 12:00 NTP -5
  • 16 jan 2011 12:00 NTP -4
  • 17 jan 2011 12:00 NTP -4
    so on average 34/8 = 4.25 sec per 24h shift
    [frankly I think this is a bit overdone]

Another idea is to use the EEPROM to hold reboot resistant alarm times. The 52 bytes could hold 10 alarm timestamps, that are kept over reboots. When passed they are overwritten with 0.

I like the idea and have not thought of a better use. So I will try to implement your solution as soon as I can. I am working with the BMA180 and the BMP085 and those are eating my time. Indeed the clock is so that I can time their data...

Thanks for the thought.

If any one else has ideas for improvement or can get to the "library maintainer" with some of these ideas please do so. I am happy to do more on this project if it can be used generally.