improve RTC accuracy

This commit is contained in:
cyberman54 2022-01-26 16:21:44 +01:00
parent 43e6ec0cb6
commit af01537a95
6 changed files with 59 additions and 53 deletions

View File

@ -111,6 +111,5 @@ typedef struct {
} sdsStatus_t; } sdsStatus_t;
extern char clientId[20]; // unique clientID extern char clientId[20]; // unique clientID
extern time_t _COMPILETIME; // epoch build time
#endif #endif

View File

@ -12,7 +12,7 @@ extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
uint8_t rtc_init(void); uint8_t rtc_init(void);
uint8_t set_rtctime(time_t t); uint8_t set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(uint16_t *msec);
float get_rtctemp(void); float get_rtctemp(void);
#endif // _RTCTIME_H #endif // _RTCTIME_H

View File

@ -33,7 +33,7 @@ bool timeIsValid(time_t const t);
void calibrateTime(void); void calibrateTime(void);
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
timesource_t mytimesource); timesource_t mytimesource);
time_t compileTime(const String compile_date); time_t compileTime(void);
time_t mkgmtime(const struct tm *ptm); time_t mkgmtime(const struct tm *ptm);
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
int8_t rxPin, int8_t txPins); int8_t rxPin, int8_t txPins);

View File

@ -27,24 +27,22 @@ licenses. Refer to LICENSE.txt file in repository for more details.
Task Core Prio Purpose Task Core Prio Purpose
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
ledloop 0 3 blinks LEDs ledloop* 0 3 blinks LEDs
spiloop 0 2 reads/writes data on spi interface spiloop# 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer lmictask* 1 2 MCCI LMiC LORAWAN stack
clockloop# 1 4 generates realtime telegrams for external clock
mqttloop# 1 2 reads/writes data on ETH interface
timesync_proc# 1 3 processes realtime time sync requests
irqhandler# 1 2 cyclic tasks (i.e. displayrefresh) triggered by
timers gpsloop* 1 1 reads data from GPS via serial or i2c
lorasendtask# 1 1 feeds data from lora sendqueue to lmcic
rmcd_process# 1 1 Remote command interpreter loop
lmictask 1 2 MCCI LMiC LORAWAN stack * spinning task
clockloop 1 4 generates realtime telegrams for external clock # blocked/waiting task
mqttloop 1 2 reads/writes data on ETH interface
timesync_proc 1 3 processes realtime time sync requests
irqhandler 1 2 cyclic tasks (i.e. displayrefresh) triggered by timers
gpsloop 1 1 reads data from GPS via serial or i2c
lorasendtask 1 1 feeds data from lora sendqueue to lmcic
rmcd_process 1 1 Remote command interpreter loop
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks. Low priority numbers denote low priority tasks.
-------------------------------------------------------------------------------
NOTE: Changing any timings will have impact on time accuracy of whole code.
So don't do it if you do not own a digital oscilloscope.
// ESP32 hardware timers // ESP32 hardware timers
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -126,7 +124,7 @@ void setup() {
snprintf(clientId, 20, "paxcounter_%08x", hashedmac); snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId, ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
PROGVERSION, RTC_runmode, RTC_restarts); PROGVERSION, RTC_runmode, RTC_restarts);
ESP_LOGI(TAG, "code build date: %d", _COMPILETIME); ESP_LOGI(TAG, "code build date: %d", compileTime());
// print chip information on startup if in verbose mode after coldstart // print chip information on startup if in verbose mode after coldstart
#if (VERBOSE) #if (VERBOSE)
@ -494,8 +492,7 @@ void setup() {
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ); cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
// only if we have a timesource we do timesync // only if we have a timesource we do timesync
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || \ #if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || (HAS_RTC))
defined HAS_RTC)
#if (defined HAS_IF482 || defined HAS_DCF77) #if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting Clock Controller..."); ESP_LOGI(TAG, "Starting Clock Controller...");

View File

@ -25,14 +25,14 @@ uint8_t rtc_init(void) {
} }
#if (TIME_SYNC_COMPILEDATE) #if (TIME_SYNC_COMPILEDATE)
// initialize a blank RTC without battery backup with compiled time // initialize a blank RTC without battery backup with build time
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
time_t t = tt.Epoch32Time(); // sec2000 -> epoch time_t t = tt.Epoch32Time(); // sec2000 -> epoch
if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) { if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) {
ESP_LOGW(TAG, "RTC has no recent time, setting to compiled time"); ESP_LOGW(TAG, "RTC has no recent time, setting to compiletime");
Rtc.SetDateTime( Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) -
RtcDateTime(_COMPILETIME - SECS_YR_2000)); // epoch -> sec2000 SECS_YR_2000)); // epoch -> sec2000
} }
#endif #endif
@ -62,15 +62,23 @@ uint8_t set_rtctime(time_t t) { // t is sec epoch time
} }
} // set_rtctime() } // set_rtctime()
time_t get_rtctime(void) { time_t get_rtctime(uint16_t *msec) {
time_t t = 0; time_t t = 0;
*msec = 0;
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) {
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
t = tt.Epoch32Time(); // sec2000 -> epoch t = tt.Epoch32Time(); // sec2000 -> epoch
} }
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return timeIsValid(t); #ifdef RTC_INT
// adjust time to top of next second by waiting TimePulseTick to flip
bool lastTick = TimePulseTick;
while (TimePulseTick == lastTick) {
};
t++;
#endif
return t;
} else { } else {
ESP_LOGE(TAG, "RTC get time failure"); ESP_LOGE(TAG, "RTC get time failure");
return 0; // failure return 0; // failure

View File

@ -21,7 +21,6 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'};
bool volatile TimePulseTick = false; bool volatile TimePulseTick = false;
timesource_t timeSource = _unsynced; timesource_t timeSource = _unsynced;
time_t _COMPILETIME = compileTime(__DATE__);
TaskHandle_t ClockTask = NULL; TaskHandle_t ClockTask = NULL;
hw_timer_t *ppsIRQ = NULL; hw_timer_t *ppsIRQ = NULL;
@ -40,8 +39,7 @@ Ticker timesyncer;
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
void calibrateTime(void) { void calibrateTime(void) {
ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _seconds(),
timeSource);
time_t t = 0; time_t t = 0;
uint16_t t_msec = 0; uint16_t t_msec = 0;
@ -57,7 +55,7 @@ void calibrateTime(void) {
// has RTC -> fallback to RTC time // has RTC -> fallback to RTC time
#ifdef HAS_RTC #ifdef HAS_RTC
t = get_rtctime(); t = get_rtctime(&t_msec);
// set time from RTC - method will check if time is valid // set time from RTC - method will check if time is valid
setMyTime((uint32_t)t, t_msec, _rtc); setMyTime((uint32_t)t, t_msec, _rtc);
#endif #endif
@ -101,26 +99,27 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
} }
// from here on we are on top of next second
tv.tv_sec = time_to_set; tv.tv_sec = time_to_set;
tv.tv_usec = 0; tv.tv_usec = 0;
sntp_sync_time(&tv); sntp_sync_time(&tv);
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set); ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set);
// if we have a software pps timer, shift it to top of second // if we have a precise time timesource, set RTC time and shift RTC_INT
if (ppsIRQ != NULL) { // pulse to top of second
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
}
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
// to top of second
#ifdef HAS_RTC #ifdef HAS_RTC
if ((mytimesource == _gps) || (mytimesource == _lora)) if ((mytimesource == _gps) || (mytimesource == _lora))
set_rtctime(time_to_set); set_rtctime(time_to_set);
#endif #endif
// if we have a software pps timer, shift it to top of second
if (ppsIRQ != NULL) {
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps to advance wall clock by 1 sec
}
timeSource = mytimesource; // set global variable timeSource = mytimesource; // set global variable
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
@ -132,7 +131,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
"[%0.3f] Failed to synchronise time from source %c | unix sec " "[%0.3f] Failed to synchronise time from source %c | unix sec "
"obtained from source: %d | unix sec at program compilation: %d", "obtained from source: %d | unix sec at program compilation: %d",
_seconds(), timeSetSymbols[mytimesource], time_to_set, _seconds(), timeSetSymbols[mytimesource], time_to_set,
_COMPILETIME); compileTime());
} }
} }
@ -224,7 +223,9 @@ void IRAM_ATTR CLOCKIRQ(void) {
// helper function to check plausibility of a given epoch time // helper function to check plausibility of a given epoch time
bool timeIsValid(time_t const t) { bool timeIsValid(time_t const t) {
// is t a time in the past? we use compile time to guess // is t a time in the past? we use compile time to guess
return (t > _COMPILETIME); // compile time is some local time, but we do not know it's time zone
// thus, we go 1 full day back to be sure to catch a time in the past
return (t > (compileTime() - 86400));
} }
// helper function to calculate serial transmit time // helper function to calculate serial transmit time
@ -283,7 +284,7 @@ void clock_loop(void *taskparameter) { // ClockTask
// set calendar time for next second of clock output // set calendar time for next second of clock output
tt = (time_t)(current_time + 1); tt = (time_t)(current_time + 1);
localtime_r(&tt, &t); localtime_r(&tt, &t);
mktime(&t); tt = mktime(&t);
#if defined HAS_IF482 #if defined HAS_IF482
@ -293,13 +294,13 @@ void clock_loop(void *taskparameter) { // ClockTask
if (xTaskNotifyWait(0x00, ULONG_MAX, &current_time, txDelay) == pdTRUE) { if (xTaskNotifyWait(0x00, ULONG_MAX, &current_time, txDelay) == pdTRUE) {
tt = (time_t)(current_time + 1); tt = (time_t)(current_time + 1);
localtime_r(&tt, &t); localtime_r(&tt, &t);
mktime(&t); tt = mktime(&t);
} }
// send IF482 telegram // send IF482 telegram
IF482.print(IF482_Frame(t)); // note: telegram is for *next* second IF482.print(IF482_Frame(tt)); // note: telegram is for *next* second
ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(t)); ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(tt).c_str());
#elif defined HAS_DCF77 #elif defined HAS_DCF77
@ -340,7 +341,7 @@ void clock_loop(void *taskparameter) { // ClockTask
} // clock_loop() } // clock_loop()
// we use compile date to create a time_t reference "in the past" // we use compile date to create a time_t reference "in the past"
time_t compileTime(const String compile_date) { time_t compileTime(void) {
char s_month[5]; char s_month[5];
int year; int year;
@ -353,10 +354,11 @@ time_t compileTime(const String compile_date) {
if (secs == -1) { if (secs == -1) {
// determine date // determine date
// we go one day back to bypass unknown timezone of local time sscanf(__DATE__, "%s %d %d", s_month, &t.tm_mday, &year);
sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year);
t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; t.tm_mon = (strstr(month_names, s_month) - month_names) / 3;
t.tm_year = year - 1900; t.tm_year = year - 1900;
// determine time
sscanf(__TIME__, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
// convert to secs local time // convert to secs local time
secs = mktime(&t); secs = mktime(&t);