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
2 Answers 2
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.
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()
.
-
1
now
is the time from the RTC at the moment you assign thenow
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– Majenko2020年11月04日 12:24:07 +00:00Commented Nov 4, 2020 at 12:24 -
1It'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 onsetSyncInterval()
. when you ask forhour()
, if more time has passed since it last got the real time that you specified insetSyncInterval()
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– Majenko2020年11月04日 12:35:20 +00:00Commented Nov 4, 2020 at 12:35 -
1I updated my post with help provided. Almost have it understood.RickH– RickH2020年11月04日 14:36:02 +00:00Commented Nov 4, 2020 at 14:36
-
1Because you're constantly changing the sync provider and interval. You set those once and once only in setup, and never again.Majenko– Majenko2020年11月05日 12:42:13 +00:00Commented Nov 5, 2020 at 12:42
-
1That 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. JustSerial.print(hour());
Majenko– Majenko2020年11月05日 13:11:27 +00:00Commented Nov 5, 2020 at 13:11