improve RTC accuracy
This commit is contained in:
parent
43e6ec0cb6
commit
af01537a95
@ -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
|
@ -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
|
@ -10,8 +10,8 @@
|
|||||||
#include "dcf77.h"
|
#include "dcf77.h"
|
||||||
#include "esp_sntp.h"
|
#include "esp_sntp.h"
|
||||||
|
|
||||||
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
|
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
|
||||||
#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch
|
#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch
|
||||||
#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
|
#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
|
||||||
|
|
||||||
enum timesource_t { _gps, _rtc, _lora, _unsynced, _set };
|
enum timesource_t { _gps, _rtc, _lora, _unsynced, _set };
|
||||||
@ -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);
|
||||||
|
35
src/main.cpp
35
src/main.cpp
@ -25,26 +25,24 @@ licenses. Refer to LICENSE.txt file in repository for more details.
|
|||||||
|
|
||||||
// Tasks and timers:
|
// Tasks and timers:
|
||||||
|
|
||||||
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...");
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +139,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
|||||||
uint8_t timepulse_init() {
|
uint8_t timepulse_init() {
|
||||||
|
|
||||||
// set esp-idf API sntp sync mode
|
// set esp-idf API sntp sync mode
|
||||||
//sntp_init();
|
// sntp_init();
|
||||||
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
|
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
|
||||||
|
|
||||||
// use time pulse from GPS as time base with fixed 1Hz frequency
|
// use time pulse from GPS as time base with fixed 1Hz frequency
|
||||||
@ -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, ¤t_time, txDelay) == pdTRUE) {
|
if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_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);
|
||||||
|
Loading…
Reference in New Issue
Block a user