You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Using an Arduino Uno R4 WiFi, if the RTC date is set to before 2000-01-01, it will incorrectly report the date when it is read back from the RTC. For an easy repro, see the sketch I have provided in the Repro Sketch section.
Contents
This has gotten pretty long, so I'll leave this here as an easy way to jump between sections...
During my search for answers, I have spent a long time trying to work out where it's all going wrong. I will do my best to impart that knowledge onto others here.
My Findings
The RTC in the RA4M1 has a 100 year calendar, which in itself is not a problem, but it seems to be bodged into working with the C standard for tm_year (defined as the number of years since 1900 in the UNIX specification).
In the Arduino RTC library, the years of RTCTime get 1900 subtracted from them, so 1970 becomes 70 and 2017 becomes 117 etc:
#defineTM_YEAR_OFFSET (1900)
...
boolRTCTime::setYear(int _y) {
if (_y >= TM_YEAR_OFFSET) {
_y -= TM_YEAR_OFFSET;
}
year = _y;
stime.tm_year = _y;
//stime.tm_yday = day + yday(year, Month2tm(month));returntrue;
}
I am unable to debug what happens within the fsp, but according to the comments validation of the year should fail for 70 since the expected range is 100-199. This is detailed in r_rtc.c of Renesas/fsp. It looks like the validation is disabled, i.e. RTC_CFG_PARAM_CHECKING_ENABLE is not defined, since the result of RTC.setTime for these invalid dates returns True.
Since RTC_CFG_PARAM_CHECKING_ENABLE isn't defined, a year such as 70 (from 1970) can be passed to R_RTC_CalendarTimeSet without causing an error. When this happens, the year (i.e. 70) will have 100 subtracted from it, and then be cast to a uint8_t:
/* Subtract 100 to match with HW register */
R_RTC->RYRCNT = rtc_dec_to_bcd((uint8_t) (p_time->tm_year - RTC_C_TIME_OFFSET));
This results in (int) 70 becoming (int) -30. When cast to a uint8_t this becomes 226. This is then converted using the following function (rtc_dec_to_bcd) into a binary-coded decimal:
/*******************************************************************************************************************//** * Convert decimal to BCD * * @param[in] to_convert Decimal Value to be converted **********************************************************************************************************************/staticuint8_trtc_dec_to_bcd (uint8_t to_convert)
{
return (uint8_t) ((((to_convert / (uint8_t) 10) << 4) & (uint8_t) RTC_MASK_LSB) | (to_convert % (uint8_t) 10));
}
Breaking this down in to the three parts:
226 is divided by 10 (22) and then bit-shifted by 4 (352).
RTC_MASK_LSB is defined elsewhere as 0xF0.
The modulo of 226 and 10 is 6.
Bringing this together, 352 & 0xF0 is 96. 96 | 6 is 102. 102 (0b01100110) is the binary-coded representation of 66, which will then be turned into 66 + 100 + 1900 = 2066 when the time is taken back from the RTC.
Issue Summary
It seems like the Arduino RTC library isn't validating whether the value has been constrained properly, which leads to issues when a value pre-2000 is passed in to R_RTC_CalendarTimeGet. It also seems like the fsp is being built without this validation in place by defining RTC_CFG_PARAM_CHECKING_ENABLE.
If the RTC cannot store the year correctly, it should be converted to a compatible value before being written to the RTC to avoid inconsistent behavior between getting and setting the RTC time. Even if the RTC cannot store a specified value, it should at least be constrained so that the conversion works the same in both directions.
I am aware that some of the code mentioned here is not maintained by Arduino, but I feel it important to highlight the restrictions in both hardware and software that must be taken into account if a fix is to be developed.
The comments within the RTC library are also a bit misleading about the valid range of years for RTCTime. One of the comments in the RTC library suggests that setting the date to a year 1989 is possible (See line 91 of RTC.h):
/* setters */boolsetDayOfMonth(int day); /* day from 1 to 31 */boolsetMonthOfYear(Month m); /* month from 1 (January) to 12 (December) */boolsetYear(int year); /* the year 1989 or 2022 */
It should either be better documented that the valid range of years is only 2000-2099, and perhaps even constrain the year and have an error state returned when an invalid date is used to explain why the year has been constrained. Each of the setters already returns a bool, make use of it!
This also needs commenting or accounting for in RTCTime::setUnixTime, as 0 is a valid for a UTC, but cannot be stored by the RTC itself!
Repro Sketch
I have created a test sketch to demonstrate the issue:
// Include the RTC library
#include"RTC.h"// The number of seconds between the Epoch and 01/01/2000 12:00 am.
#defineSECONDS_FROM_1970_TO_2000946684800// Function to print an RTCTime to the serial monitor.// A newline will be printed after the tme.voidprint_time(RTCTime time) {
Serial.print(time.getYear());
Serial.print("-");
Serial.print(Month2int(time.getMonth()));
Serial.print("-");
Serial.print(time.getDayOfMonth());
Serial.print("");
Serial.print(time.getHour());
Serial.print(":");
Serial.print(time.getMinutes());
Serial.print(":");
Serial.println(time.getSeconds());
}
// Function to set the time to a specified value.// Also prints debug infomation while setting the time.voidset_rtc_time(RTCTime time) {
// Print the time the RTC will be set to.
Serial.print("Setting time to: ");
print_time(time);
// Set the time on the RTC.bool result = RTC.setTime(time);
// Check whether the RTC was set successfully.
Serial.print("RTC Set Result: ");
Serial.println(result ? "OK!" : "Failed.");
// To debug, fetch the time from the RTC and print it.
Serial.println("RTC now set to: ");
RTCTime currentTime;
RTC.getTime(currentTime);
print_time(currentTime);
}
voidsetup() {
// Start the serial monitor at 115200 baud.
Serial.begin(115200);
// Initialize the RTC.
RTC.begin();
// Create an RTCTime set to the epoch (UNIX time 0).
RTCTime epochTime = RTCTime(0);
// Create an RTCTime set to the 01/01/2000 12 am.
RTCTime milleniumTime = RTCTime(SECONDS_FROM_1970_TO_2000);
// Try and set the time to the epoch. Print debug info...set_rtc_time(epochTime);
// Now try and set the time to the millenium.set_rtc_time(milleniumTime);
}
voidloop() {
}
The output to the serial monitor will be as follows:
Setting time to: 1970-1-1 0:0:0
RTC Set Result: OK!
RTC now set to:
2066-1-1 0:0:0
Setting time to: 2000-1-1 0:0:0
RTC Set Result: OK!
RTC now set to:
2000-1-1 0:0:0
The text was updated successfully, but these errors were encountered:
NathanielJS1541
changed the title
RTC cannot be set to dates before the year 2000
RTC time set incorrectly for years before 2000
May 20, 2024
NathanielJS1541
changed the title
RTC time set incorrectly for years before 2000
RTC date set incorrectly for years before 2000
May 20, 2024
The Bug 🐛
Using an Arduino Uno R4 WiFi, if the RTC date is set to before 2000-01-01, it will incorrectly report the date when it is read back from the RTC. For an easy repro, see the sketch I have provided in the Repro Sketch section.
Contents
This has gotten pretty long, so I'll leave this here as an easy way to jump between sections...
Debugging Attempt...
During my search for answers, I have spent a long time trying to work out where it's all going wrong. I will do my best to impart that knowledge onto others here.
My Findings
The RTC in the RA4M1 has a 100 year calendar, which in itself is not a problem, but it seems to be bodged into working with the C standard for
tm_year
(defined as the number of years since 1900 in the UNIX specification).In the Arduino RTC library, the years of RTCTime get 1900 subtracted from them, so 1970 becomes 70 and 2017 becomes 117 etc:
I am unable to debug what happens within the fsp, but according to the comments validation of the year should fail for 70 since the expected range is 100-199. This is detailed in r_rtc.c of Renesas/fsp. It looks like the validation is disabled, i.e.
RTC_CFG_PARAM_CHECKING_ENABLE
is not defined, since the result ofRTC.setTime
for these invalid dates returnsTrue
.Since
RTC_CFG_PARAM_CHECKING_ENABLE
isn't defined, a year such as 70 (from 1970) can be passed to R_RTC_CalendarTimeSet without causing an error. When this happens, the year (i.e. 70) will have 100 subtracted from it, and then be cast to auint8_t
:This results in (int) 70 becoming (int) -30. When cast to a
uint8_t
this becomes 226. This is then converted using the following function (rtc_dec_to_bcd
) into a binary-coded decimal:Breaking this down in to the three parts:
RTC_MASK_LSB
is defined elsewhere as0xF0
.Bringing this together,
352 & 0xF0
is 96.96 | 6
is 102. 102 (0b01100110) is the binary-coded representation of 66, which will then be turned into 66 + 100 + 1900 = 2066 when the time is taken back from the RTC.Issue Summary
It seems like the Arduino RTC library isn't validating whether the value has been constrained properly, which leads to issues when a value pre-2000 is passed in to
R_RTC_CalendarTimeGet
. It also seems like the fsp is being built without this validation in place by definingRTC_CFG_PARAM_CHECKING_ENABLE
.If the RTC cannot store the year correctly, it should be converted to a compatible value before being written to the RTC to avoid inconsistent behavior between getting and setting the RTC time. Even if the RTC cannot store a specified value, it should at least be constrained so that the conversion works the same in both directions.
I am aware that some of the code mentioned here is not maintained by Arduino, but I feel it important to highlight the restrictions in both hardware and software that must be taken into account if a fix is to be developed.
The comments within the RTC library are also a bit misleading about the valid range of years for
RTCTime
. One of the comments in the RTC library suggests that setting the date to a year 1989 is possible (See line 91 of RTC.h):It should either be better documented that the valid range of years is only 2000-2099, and perhaps even constrain the year and have an error state returned when an invalid date is used to explain why the year has been constrained. Each of the setters already returns a bool, make use of it!
This also needs commenting or accounting for in
RTCTime::setUnixTime
, as 0 is a valid for a UTC, but cannot be stored by the RTC itself!Repro Sketch
I have created a test sketch to demonstrate the issue:
The output to the serial monitor will be as follows:
The text was updated successfully, but these errors were encountered: