1

I am using RTClib.h and TimeLib.h with an Uno.

Have been struggling for days in understanding the various RTC libraries available and how to use them with the Arduino Time Library.

Question 1.
If I do not want to create a new now() object each time through the loop, how do I sync the Time Library to the RTC every x seconds? Any time I try to use the RTClib function now.hour now.minute etc it requires me to use DateTime now = RTC.now() creating the now() object.

Is there an equivalent function in RTCLib as setSyncProvider(RTC.get) in DS1307RTC library?

Is there an equivalent to setSyncInterval(5000)

Question 2.
Which RTCLib should I call out? There are several used in examples:

<RTClib.h>

"RTClib" using quotes

<RTCLib.h> with capital L

and sometimes also calling out one of these:

RTC_DS1307 RTC

or RTC_DS1307 rtc

or RTC_DS3231 rtc

Question 3
a. Are tmElements_t tm part of the DS1307RTC library?
b. Are these tm.Hour tm.Minute from the RTC?
c. Is there an equivalent in the RTClib.h ?

And finally I have posted some code below just to show my usage:

#include "RTClib.h" 
#include <Wire.h>   
#include <TimeLib.h>

RTC_DS1307 RTC;

void setup() {
  
Serial.begin(9600);
Wire.begin();  //sets up the I2C
RTC.begin();   //initializes the I2C to the RTC


if (! RTC.begin()) {
    Serial.println("Couldn't find RTC");
}
    
//  Set the RTC Time to 5:10:30 Nov 3 2020
      RTC.adjust(DateTime(2020,11,3,5,10,30));

//Set Arduino Time Library different than RTC time to see which is which
setTime(9, 27, 05, 14, 07, 2015);
      
}


 void loop() {

/*
// How do I sync Time Library to RTC after x seconds?
  setSyncProvider(RTC.get); // This is in the DS1307RTC.h Library
  setSyncInterval(5000);   //  Are there such functions in RTClib ?
*/

DateTime now = RTC.now();   // Pulls RTC time into now().  Does not sync with Time Library
                            // Do not want to do this every loop.

//Print RTC Time
Serial.println();
Serial.print("RTC now.hour        ");
Serial.println(now.hour());
Serial.print("RTC now.minute      ");
Serial.println(now.minute());
Serial.print("RTC now.second:     ");
Serial.println(now.second());
Serial.println();
Serial.println();


//Print Arduino TimeLib Time
Serial.print("Time Lib Hour:       "); 
Serial.print(hour());  
Serial.println();
Serial.print("Time Lib Min:        "); 
Serial.print(minute());  
Serial.println();
Serial.print("Time Lib Sec:        "); 
Serial.print(second());  
Serial.println();
Serial.println();

delay(1000);
  
 }

Updated 5 Nov 2020

I think I almost understand. Please let me know if this code is doing what I think it is doing, specifically:

In Setup:
Set the RTC to 5:10:30.
Set Time Library to 9:27:05 with setTime().
Set Time Library to RTC time with DateTime now (over writing 9:27:05)?
Set Time Library variables to RTC using tm.Hour = now.Hour, etc.

In Loop:
Every 5 seconds update hour(), minute() second() with RTC time.
Every loop update tm.hour with hour(), etc.

I know it is still not right since the serial prints show tm.Hour,tm.Minute alternating each loop as shown at the bottom.

#include "RTClib.h"
#include <Wire.h>
#include <TimeLib.h>

RTC_DS1307 RTC;

time_t time_provider()
{
    return RTC.now().unixtime();
}

tmElements_t tm;  //part of Time Library

void setup() {
    //setSyncProvider(time_provider);
    Serial.begin(9600);
    Wire.begin();  //sets up the I2C
    RTC.begin();   //initializes the I2C to the RTC

    if (! RTC.begin()) {
        Serial.println("Couldn't find RTC");
    }

    //  Set the RTC Time to 5:10:30 Nov 3 2020
    RTC.adjust(DateTime(2020,11,3,5,10,30));

    //Set Arduino Time Library different than RTC time 9:27:05
    setTime(9, 27, 05, 14, 07, 2015);

    //Setting Time Library to RTC time
    DateTime now = RTC.now();
    tm.Hour = now.hour();
    tm.Minute = now.minute();
    tm.Second = now.second();
}


void loop() {
    setSyncProvider(RTC.now);
    setSyncInterval(5000);

    //Time Library time updates to RTC every 5 seconds
    tm.Hour = hour();
    tm.Minute = minute();
    tm.Second = second();

    Serial.print("tm.Hour:    ");
    Serial.print(tm.Hour);
    Serial.println();
    Serial.print("tm.Minute:  ");
    Serial.print(tm.Minute);
    Serial.println();
    Serial.print("tm.Seconds: ");
    Serial.print(tm.Second);
    Serial.println();
    Serial.println();
    delay(1000);
}

Serial Prints:

tm.Hour:    5
tm.Minute:  34
tm.Seconds: 56

tm.Hour:    18
tm.Minute:  0
tm.Seconds: 0

tm.Hour:    5
tm.Minute:  34
tm.Seconds: 56

tm.Hour:    18
tm.Minute:  0
tm.Seconds: 0

tm.Hour:    5
tm.Minute:  34
tm.Seconds: 56

tm.Hour:    18
tm.Minute:  0
tm.Seconds: 0
4

Let me first give some background about these libraries.

The Time library uses millis() for timekeeping. Since this can be subject to significant drift, it provides a means to periodically sync to an external time provider. Hence the functions setSyncProvider() and setSyncInterval().

RTClib is meant to interface an RTC. It does not provide timekeeping on its own: you get the current time by querying an RTC through it's now() method.

These two libraries can nicely complement each other, as RTClib can be used as a time provider for the Time library:

// Provide the RTC time to the Time library.
time_t time_provider() {
    return RTC.now().unixtime();
}

void setup() {
    // ...
    setSyncProvider(time_provider);
}

If I do not want to create a new now() object [...]

It is actually called a DateTime object.

Is there an equivalent [to tmElements_t] in the RTClib

Yes, the DateTime class. Note that, unlike the tmElements_t structure, the data fields of DateTime are not public, and you have to use accessors to get them: year(), month(), day()...

Alternative timekeeping method

Since you are using an Arduino Uno, there is a third method of timekeeping you may want to consider. Instead of querying the RTC on every loop iteration (RTClib) or interpolating the RTC readings with millis() (Time library), you may route a 1 Hz output of the RTC to an interrupt pin, and count the seconds in the ISR. The avr-libc timing code is designed to provide timekeeping in this fashion. It all boils down to:

// Initialize the system time from the RTC.
set_system_time(RTC.now().secondstime());

// Keep the time in sync using the 1 Hz output of the RTC.
pinMode(pin1Hz, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin1Hz), system_tick, FALLING);

For more details, see the example sketch interrupts1Hz.ino provided with RTClib.


Edit 1: Answering a comment

which pin does [the Arduino interrupt pin] connect to on the RTC?

It depends on the RTC you are using. I will assume it's a DS1307, as implied by the code in your question. Take a look at the datasheet of the RTC. On page 6, there is a table titled “Pin description”. From this table:

pin: 7
name: SQW/OUT
function: Square Wave/Output Driver. When enabled, the SQWE bit set to 1, the SQW/OUT pin outputs one of four square-wave frequencies (1Hz, 4kHz, 8kHz, 32kHz). The SQW/OUT pin is open drain and requires an external pullup resistor. SQW/OUT operates with either VCC or VBAT applied. The pullup voltage can be up to 5.5V regardless of the voltage on VCC. If not used, this pin can be left floating.

If you are using an RTC module, you will have to figure out where this pin is exposed on the module's connector.

In any case, you will have to enable the 1 Hz square wave output with

RTC.writeSqwPinMode(DS1307_SquareWave1HZ);

Edit 2: Commenting on the update to the question:

// Set the RTC Time to 5:10:30 Nov 3 2020
RTC.adjust(DateTime(2020,11,3,5,10,30));

Correct.

// Set Arduino Time Library different than RTC time 9:27:05
setTime(9, 27, 05, 14, 07, 2015);

Correct. Date is 2015-07-14.

// Setting Time Library to RTC time
DateTime now = RTC.now();
tm.Hour = now.hour();
tm.Minute = now.minute();
tm.Second = now.second();

No. This is only partially initializing the tm variable. It has no effect on the idea the Time library has of the current time. Note that the date fields of tm have not been initialized at this point, and could well be invalid (like month 23, day 125).

setSyncProvider(RTC.now);

This is incorrect, and should have generated a compiler warning. setSyncProvider() expects a function that returns the current time as Unix time (a simple integer, of type time_t). You are providing a function that returns the current time in broken down form (year, month...), with type DateTime. The Time library will not understand that and may yield garbage like, say, 18:00:00.

The time returned by RTC.now() can be converted to Unix time with the unixtime() method. That's why I gave you the time_provider() function.

Also, you are not supposed to call setSyncProvider() and setSyncInterval() on every loop iteration. Do it once and for all in setup().

// Time Library time updates to RTC every 5 seconds
tm.Hour = hour();
tm.Minute = minute();
tm.Second = second();

Again, this is only updating the variable tm. It has no effect on what the Time library believes is the current time.

2

Is there an equivalent function in RTCLib as setSyncProvider(RTC.get) in DS1307RTC library?

Is there an equivalent to setSyncInterval(5000)

Those are TimeLib functions. There's no "equivalent to" since you are using TimeLib.

The first one just expects a pointer to a function that returns a time_t. What is in that function is up to you.

Which RTCLib should I call out? There are several used in examples:

The capitalisation should match the name of the header file. Windows doesn't care, and OS X usually doesn't care, but can be configured to. Linux does care and needs it to match. If you want your code to be portable then you must keep the capitalisation the same as the file.

and sometimes also calling out one of these:

You need to create an object that matches your physical RTC.

a. Are tmElements_t tm part of the DS1307RTC library?

No. They are part of TimeLib.

b. Are these tm.Hour tm.Minute from the RTC?

They are whatever gets assigned to them by a function that is called. They are just variables. They can contain anything.

c. Is there an equivalent in the RTClib.h ?

No. But then you don't need there to be if you're using TimeLib.

So: you need to

  • Create a function that gets the time from the RTC through whatever library you want to use (RTCLib for example) and calculates the number of seconds since 00:00:00 01/01/1970, which it then returns as a time_t.
  • Pass that to TimeLib's setSyncProvider()
  • Set a sync frequency with setSyncInterval().
11
  • 1
    now is the time from the RTC at the moment you assign the now object. .hour() is just the hour contained inside that object. hour() is the time from the TimeLib library, yes. setSyncProvider() doesn't send anything to the RTC, it gets the time from the RTC on a regular basis and updates the TimeLib with the current time. – Majenko Nov 4 '20 at 12:24
  • 1
    It's a little more complex than that. TimeLib uses millis() to keep track of the time. It regularly updates its internal idea of what the time is depending on setSyncInterval(). when you ask for hour(), if more time has passed since it last got the real time that you specified in setSyncInterval() it will go and get the time from the RTC. Otherwise it will just tell you what it thinks the time is according to its own internal calculations. – Majenko Nov 4 '20 at 12:35
  • 1
    I updated my post with help provided. Almost have it understood. – RickH Nov 4 '20 at 14:36
  • 1
    Because you're constantly changing the sync provider and interval. You set those once and once only in setup, and never again. – Majenko Nov 5 '20 at 12:42
  • 1
    That looks right, yes. Though you don't need the tmElements_t struct at all since all you're dealing with is simple numbers. tmElements_t is just a convenient way of bundling them all together if you want to pass it to another function. Just Serial.print(hour()); – Majenko Nov 5 '20 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.