timesync fixes
This commit is contained in:
parent
0b50a2e6e1
commit
cad13b72a1
@ -6,12 +6,12 @@
|
|||||||
#include "timesync.h"
|
#include "timesync.h"
|
||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
|
|
||||||
#define TIME_SYNC_SAMPLES 2 // number of time requests for averaging
|
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
|
||||||
#define TIME_SYNC_CYCLE 20 // delay between two time samples [seconds]
|
#define TIME_SYNC_CYCLE 20 // delay between two time samples [seconds]
|
||||||
#define TIME_SYNC_TIMEOUT 120 // timeout waiting for timeserver answer [seconds]
|
#define TIME_SYNC_TIMEOUT 120 // timeout waiting for timeserver answer [seconds]
|
||||||
#define TIME_SYNC_TRIGGER 100 // deviation triggering a time sync [milliseconds]
|
#define TIME_SYNC_TRIGGER 100 // deviation triggering a time sync [milliseconds]
|
||||||
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes]
|
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes]
|
||||||
#define TIME_SYNC_FIXUP 0 // calibration to fixup processing time [milliseconds]
|
#define TIME_SYNC_FIXUP 30 // calibration to fixup processing time [milliseconds]
|
||||||
|
|
||||||
void send_timesync_req(void);
|
void send_timesync_req(void);
|
||||||
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);
|
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);
|
||||||
|
@ -56,13 +56,10 @@ void irqHandler(void *pvParameters) {
|
|||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
void IRAM_ATTR DisplayIRQ() {
|
void IRAM_ATTR DisplayIRQ() {
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
|
||||||
xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits,
|
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits,
|
||||||
&xHigherPriorityTaskWoken);
|
&xHigherPriorityTaskWoken);
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
|
||||||
if (xHigherPriorityTaskWoken)
|
if (xHigherPriorityTaskWoken)
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
}
|
}
|
||||||
@ -70,13 +67,10 @@ void IRAM_ATTR DisplayIRQ() {
|
|||||||
|
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
void IRAM_ATTR ButtonIRQ() {
|
void IRAM_ATTR ButtonIRQ() {
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
|
||||||
xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits,
|
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits,
|
||||||
&xHigherPriorityTaskWoken);
|
&xHigherPriorityTaskWoken);
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
|
||||||
|
|
||||||
if (xHigherPriorityTaskWoken)
|
if (xHigherPriorityTaskWoken)
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
|
@ -180,7 +180,7 @@ void setup() {
|
|||||||
strcat_P(features, " PSRAM");
|
strcat_P(features, " PSRAM");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// set external power mode to off
|
// set external power mode
|
||||||
#ifdef EXT_POWER_SW
|
#ifdef EXT_POWER_SW
|
||||||
pinMode(EXT_POWER_SW, OUTPUT);
|
pinMode(EXT_POWER_SW, OUTPUT);
|
||||||
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
|
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#ifdef USE_OTA
|
#if (USE_OTA)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Parts of this code:
|
Parts of this code:
|
||||||
|
@ -24,7 +24,7 @@ uint8_t rtc_init(void) {
|
|||||||
Rtc.SetIsRunning(true);
|
Rtc.SetIsRunning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef 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 compiled time
|
||||||
RtcDateTime tt = Rtc.GetDateTime();
|
RtcDateTime tt = Rtc.GetDateTime();
|
||||||
time_t t = tt.Epoch32Time(); // sec2000 -> epoch
|
time_t t = tt.Epoch32Time(); // sec2000 -> epoch
|
||||||
|
@ -122,10 +122,9 @@ void timepulse_start(void) {
|
|||||||
void IRAM_ATTR CLOCKIRQ(void) {
|
void IRAM_ATTR CLOCKIRQ(void) {
|
||||||
|
|
||||||
portENTER_CRITICAL_ISR(&mux);
|
portENTER_CRITICAL_ISR(&mux);
|
||||||
BaseType_t xHigherPriorityTaskWoken;
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
|
|
||||||
SyncToPPS(); // calibrates UTC systime, see microTime.h
|
SyncToPPS(); // calibrates UTC systime and advances it +1, see microTime.h
|
||||||
|
|
||||||
if (ClockTask != NULL)
|
if (ClockTask != NULL)
|
||||||
xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
|
xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
|
||||||
@ -215,7 +214,8 @@ void clock_loop(void *taskparameter) { // ClockTask
|
|||||||
#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute
|
#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute
|
||||||
|
|
||||||
uint32_t printtime;
|
uint32_t printtime;
|
||||||
time_t t = *((time_t *)taskparameter); // UTC time seconds
|
time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds
|
||||||
|
TickType_t startTime;
|
||||||
|
|
||||||
#ifdef HAS_DCF77
|
#ifdef HAS_DCF77
|
||||||
uint8_t *DCFpulse; // pointer on array with DCF pulse bits
|
uint8_t *DCFpulse; // pointer on array with DCF pulse bits
|
||||||
@ -228,18 +228,24 @@ void clock_loop(void *taskparameter) { // ClockTask
|
|||||||
|
|
||||||
// output the next second's pulse after timepulse arrived
|
// output the next second's pulse after timepulse arrived
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
// ensure the notification state is not already pending
|
||||||
xTaskNotifyWait(0x00, ULONG_MAX, &printtime,
|
xTaskNotifyWait(0x00, ULONG_MAX, &printtime,
|
||||||
portMAX_DELAY); // wait for timepulse
|
portMAX_DELAY); // wait for timepulse
|
||||||
|
|
||||||
|
startTime = xTaskGetTickCount();
|
||||||
|
|
||||||
t = time_t(printtime); // UTC time seconds
|
t = time_t(printtime); // UTC time seconds
|
||||||
|
|
||||||
// no confident time -> suppress clock output
|
// no confident or no recent time -> suppress clock output
|
||||||
if ((timeStatus() == timeNotSet) || !(timeIsValid(t)))
|
if ((timeStatus() == timeNotSet) || !(timeIsValid(t)) ||
|
||||||
|
(t == last_printtime))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
last_printtime = t;
|
||||||
|
|
||||||
#if defined HAS_IF482
|
#if defined HAS_IF482
|
||||||
|
|
||||||
vTaskDelay(txDelay); // wait until moment to fire
|
vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire
|
||||||
IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second
|
IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second
|
||||||
|
|
||||||
#elif defined HAS_DCF77
|
#elif defined HAS_DCF77
|
||||||
|
102
src/timesync.cpp
102
src/timesync.cpp
@ -27,7 +27,6 @@ typedef std::chrono::system_clock myClock;
|
|||||||
typedef myClock::time_point myClock_timepoint;
|
typedef myClock::time_point myClock_timepoint;
|
||||||
typedef std::chrono::duration<long long int, std::ratio<1, 1000>>
|
typedef std::chrono::duration<long long int, std::ratio<1, 1000>>
|
||||||
myClock_msecTick;
|
myClock_msecTick;
|
||||||
typedef std::chrono::duration<double> myClock_secTick;
|
|
||||||
|
|
||||||
myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
|
myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
|
||||||
myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
|
myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
|
||||||
@ -63,11 +62,11 @@ void send_timesync_req() {
|
|||||||
// task for sending time sync requests
|
// task for sending time sync requests
|
||||||
void process_timesync_req(void *taskparameter) {
|
void process_timesync_req(void *taskparameter) {
|
||||||
|
|
||||||
uint32_t seq_no = 0, time_to_set_us, time_to_set_ms;
|
|
||||||
uint16_t time_to_set_fraction_msec;
|
|
||||||
uint8_t k = 0, i = 0;
|
uint8_t k = 0, i = 0;
|
||||||
|
uint16_t time_to_set_fraction_msec;
|
||||||
|
uint32_t seq_no = 0;
|
||||||
time_t time_to_set;
|
time_t time_to_set;
|
||||||
auto time_offset = myClock_msecTick::zero();
|
auto time_offset_ms = myClock_msecTick::zero();
|
||||||
|
|
||||||
// wait until we are joined
|
// wait until we are joined
|
||||||
while (!LMIC.devaddr) {
|
while (!LMIC.devaddr) {
|
||||||
@ -98,81 +97,67 @@ void process_timesync_req(void *taskparameter) {
|
|||||||
else { // calculate time diff from collected timestamps
|
else { // calculate time diff from collected timestamps
|
||||||
k = seq_no % TIME_SYNC_SAMPLES;
|
k = seq_no % TIME_SYNC_SAMPLES;
|
||||||
|
|
||||||
auto t_tx = time_point_cast<milliseconds>(
|
// cumulate timepoint diffs
|
||||||
time_sync_tx[k]); // timepoint when node TX_completed
|
time_offset_ms += time_point_cast<milliseconds>(time_sync_rx[k]) -
|
||||||
auto t_rx = time_point_cast<milliseconds>(
|
time_point_cast<milliseconds>(time_sync_tx[k]);
|
||||||
time_sync_rx[k]); // timepoint when message was seen on gateway
|
|
||||||
|
|
||||||
time_offset += t_rx - t_tx; // cumulate timepoint diffs
|
|
||||||
|
|
||||||
if (i < TIME_SYNC_SAMPLES - 1) {
|
if (i < TIME_SYNC_SAMPLES - 1) {
|
||||||
// wait until next cycle
|
// wait until next cycle
|
||||||
vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000));
|
vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000));
|
||||||
} else {
|
} else {
|
||||||
// send flush to open a receive window for last time_sync_answer
|
// send flush to open a receive window for last time_sync_answer
|
||||||
// payload.reset();
|
payload.reset();
|
||||||
// payload.addByte(0x99);
|
payload.addByte(0x99);
|
||||||
// SendPayload(RCMDPORT, prio_high);
|
SendPayload(RCMDPORT, prio_high);
|
||||||
|
// Send a alive open a receive window for last time_sync_answer
|
||||||
// Send a payload-less message to open a receive window for last
|
// void LMIC_sendAlive();
|
||||||
// time_sync_answer
|
|
||||||
void LMIC_sendAlive();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // for
|
} // for
|
||||||
|
|
||||||
// calculate time offset from collected diffs
|
// average time offset from collected diffs
|
||||||
time_offset /= TIME_SYNC_SAMPLES;
|
time_offset_ms /= TIME_SYNC_SAMPLES;
|
||||||
ESP_LOGD(TAG, "[%0.3f] avg time diff: %0.3f sec", millis() / 1000.0,
|
|
||||||
myClock_secTick(time_offset).count());
|
|
||||||
|
|
||||||
// calculate absolute time offset with millisecond precision using time base
|
// calculate time offset with millisecond precision using time base
|
||||||
// of LMIC os, since we use LMIC's ostime_t txEnd as tx timestamp
|
// of LMIC os, since we use LMIC's ostime_t txEnd as tx timestamp
|
||||||
time_offset += milliseconds(osticks2ms(os_getTime()));
|
time_offset_ms += milliseconds(osticks2ms(os_getTime()));
|
||||||
|
|
||||||
// apply calibration factor for processing time
|
// apply calibration factor for processing time
|
||||||
time_offset += milliseconds(TIME_SYNC_FIXUP);
|
time_offset_ms += milliseconds(TIME_SYNC_FIXUP);
|
||||||
// convert to whole seconds
|
|
||||||
time_to_set = static_cast<time_t>(myClock_secTick(time_offset).count());
|
|
||||||
// time_to_set =
|
|
||||||
// static_cast<time_t>(duration_cast<seconds>(time_offset).count());
|
|
||||||
|
|
||||||
|
// calculate absolute time in UTC epoch
|
||||||
|
// convert to whole seconds, floor
|
||||||
|
time_to_set = (time_t)(time_offset_ms.count() / 1000) + 1;
|
||||||
// calculate fraction milliseconds
|
// calculate fraction milliseconds
|
||||||
time_to_set_fraction_msec = static_cast<uint16_t>(time_offset.count() % 1000);
|
time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec",
|
ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec",
|
||||||
millis() / 1000.0, time_to_set, time_to_set_fraction_msec);
|
millis() / 1000.0, time_to_set, time_to_set_fraction_msec);
|
||||||
|
|
||||||
// adjust system time
|
// adjust system time
|
||||||
if (timeIsValid(time_to_set)) {
|
if (timeIsValid(time_to_set)) {
|
||||||
if (abs(time_offset.count()) >=
|
|
||||||
TIME_SYNC_TRIGGER) { // milliseconds threshold
|
|
||||||
|
|
||||||
// wait until top of second
|
// wait until top of second
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000 - time_to_set_fraction_msec));
|
vTaskDelay(pdMS_TO_TICKS(1000 - time_to_set_fraction_msec));
|
||||||
|
time_to_set++; // advance time 1 sec wait time
|
||||||
time_to_set++; // advance time the one waited second
|
|
||||||
|
|
||||||
#if (!defined GPS_INT && !defined RTC_INT)
|
#if (!defined GPS_INT && !defined RTC_INT)
|
||||||
// sync esp32 hardware timer based pps to top of second
|
// sync esp32 hardware timer based pps to top of second
|
||||||
timerRestart(ppsIRQ); // reset pps timer
|
timerRestart(ppsIRQ); // reset pps timer
|
||||||
CLOCKIRQ(); // fire clock pps interrupt
|
CLOCKIRQ(); // fire clock pps interrupt
|
||||||
|
|
||||||
|
#elif defined HAS_RTC
|
||||||
|
// calibrate RTC and RTC_INT pulse on top of second
|
||||||
|
set_rtctime(time_to_set);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
setTime(time_to_set); // set the time on top of second
|
setTime(time_to_set); // set the time on top of second
|
||||||
|
|
||||||
#ifdef HAS_RTC
|
timeSource = _lora;
|
||||||
set_rtctime(time_to_set); // calibrate RTC if we have one
|
timesyncer.attach(TIME_SYNC_INTERVAL * 60,
|
||||||
#endif
|
timeSync); // set to regular repeat
|
||||||
|
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was adjusted",
|
||||||
|
millis() / 1000.0);
|
||||||
|
|
||||||
timeSource = _lora;
|
|
||||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60,
|
|
||||||
timeSync); // set to regular repeat
|
|
||||||
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time adjusted by %.3f sec",
|
|
||||||
millis() / 1000.0, myClock_secTick(time_offset).count());
|
|
||||||
} else
|
|
||||||
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time is up to date",
|
|
||||||
millis() / 1000.0);
|
|
||||||
} else
|
} else
|
||||||
ESP_LOGW(TAG, "[%0.3f] Timesync failed, outdated time calculated",
|
ESP_LOGW(TAG, "[%0.3f] Timesync failed, outdated time calculated",
|
||||||
millis() / 1000.0);
|
millis() / 1000.0);
|
||||||
@ -196,21 +181,6 @@ void store_time_sync_req(uint32_t t_txEnd_ms) {
|
|||||||
t_txEnd_ms % 1000);
|
t_txEnd_ms % 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// called from lorawan.cpp after time_sync_req was sent
|
|
||||||
void store_time_sync_req_pwm(void) {
|
|
||||||
|
|
||||||
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
|
|
||||||
|
|
||||||
time_sync_tx[k] += milliseconds(t_txEnd_ms);
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d",
|
|
||||||
millis() / 1000.0, time_sync_seqNo, t_txEnd_ms / 1000,
|
|
||||||
t_txEnd_ms % 1000);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// process timeserver timestamp answer, called from lorawan.cpp
|
// process timeserver timestamp answer, called from lorawan.cpp
|
||||||
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
|
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user