timesync tasking restructured

This commit is contained in:
Verkehrsrot 2019-04-07 16:13:04 +02:00
parent 0b40d0df70
commit 4f8d92ea7e
4 changed files with 113 additions and 103 deletions

View File

@ -10,8 +10,7 @@
#define TIME_SYNC_FRAME_LENGTH 0x05 // timeserver answer frame length [bytes] #define TIME_SYNC_FRAME_LENGTH 0x05 // timeserver answer frame length [bytes]
#define TIME_SYNC_FIXUP 6 // calibration to fixup processing time [milliseconds] #define TIME_SYNC_FIXUP 6 // calibration to fixup processing time [milliseconds]
extern TaskHandle_t timeSyncReqTask; void timesync_init(void);
void send_timesync_req(void); void send_timesync_req(void);
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len); int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len);
void process_timesync_req(void *taskparameter); void process_timesync_req(void *taskparameter);

View File

@ -224,9 +224,8 @@ void onEvent(ev_t ev) {
ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf,
cfg.txpower); cfg.txpower);
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// kick off timesync task if pending // kickoff first time sync
if (timeSyncReqTask) send_timesync_req();
xTaskNotifyGive(timeSyncReqTask);
#endif #endif
break; break;
@ -316,8 +315,9 @@ void onEvent(ev_t ev) {
if (!(LMIC.opmode & OP_JOINING)) if (!(LMIC.opmode & OP_JOINING))
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX time // if last packet sent was a timesync request, store TX time
if ((LMIC.pendTxPort == TIMEPORT) && timeSyncReqTask) // if ((LMIC.pendTxPort == TIMEPORT) && timeSyncPending)
strcpy_P(buff, PSTR("SYNCING TIME")); if (LMIC.pendTxPort == TIMEPORT)
strcpy_P(buff, PSTR("TX TIMESYNC"));
else else
#endif #endif
strcpy_P(buff, PSTR("TX START")); strcpy_P(buff, PSTR("TX START"));

View File

@ -31,7 +31,7 @@ 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 IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
timesync_req 1 4 temporary task for processing time sync requests timesync_req 1 4 processes realtime time sync requests
clockloop 1 3 generates realtime telegrams for external clock clockloop 1 3 generates realtime telegrams for external clock
irqhandler 1 2 display, timesync, etc. tasks triggered by timer irqhandler 1 2 display, timesync, etc. tasks triggered by timer
gpsloop 1 2 reads data from GPS via serial or i2c gpsloop 1 2 reads data from GPS via serial or i2c
@ -411,16 +411,23 @@ void setup() {
#endif // HAS_BUTTON #endif // HAS_BUTTON
#if (TIME_SYNC_INTERVAL) #if (TIME_SYNC_INTERVAL)
#if (!defined(TIME_SYNC_LORAWAN) && !defined(TIME_SYNC_LORASERVER) && \ #if (!(TIME_SYNC_LORAWAN) && !(TIME_SYNC_LORASERVER) && !defined HAS_GPS && \
!defined HAS_GPS && !defined HAS_RTC) !defined HAS_RTC)
#warning you did not specify a time source, time will not be synched #warning you did not specify a time source, time will not be synched
#endif #endif
// start pps timepulse // start pps timepulse
ESP_LOGI(TAG, "Starting Timekeeper..."); ESP_LOGI(TAG, "Starting Timekeeper...");
assert(timepulse_init()); // setup timepulse assert(timepulse_init()); // setup timepulse
timepulse_start(); timepulse_start();
timeSync(); // init systime timeSync(); // init systime
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
#if (TIME_SYNC_LORASERVER)
// create time sync task
timesync_init();
#endif
#endif #endif
#if defined HAS_IF482 || defined HAS_DCF77 #if defined HAS_IF482 || defined HAS_DCF77
@ -442,6 +449,4 @@ void loop() {
delay(2); // yield to CPU delay(2); // yield to CPU
#endif #endif
} }
vTaskDelete(NULL); // shoud never be reached
} }

View File

@ -13,145 +13,141 @@ algorithm in applications without granted license by the patent holder.
#include "timesync.h" #include "timesync.h"
using namespace std::chrono;
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
TaskHandle_t timeSyncReqTask = NULL; using namespace std::chrono;
static uint8_t time_sync_seqNo = TIMEANSWERPORT_MIN;
typedef std::chrono::system_clock myClock; 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;
myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; TaskHandle_t timeSyncReqTask = NULL;
myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
static uint8_t time_sync_seqNo = TIMEANSWERPORT_MIN;
static bool timeSyncPending = false;
static myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
static myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
// send time request message // send time request message
void send_timesync_req() { void send_timesync_req() {
// if a timesync handshake is pending then exit // if a timesync handshake is pending or we are not joined then exit
if (timeSyncReqTask) { if (timeSyncPending || !LMIC.devaddr)
ESP_LOGD(TAG, "Timeserver sync request already pending");
return; return;
} else { // else unblock timesync task
else {
timeSyncPending = true;
ESP_LOGI(TAG, "[%0.3f] Timeserver sync request started", millis() / 1000.0); ESP_LOGI(TAG, "[%0.3f] Timeserver sync request started", millis() / 1000.0);
xTaskNotifyGive(timeSyncReqTask);
// clear timestamp array
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++)
time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint();
// kick off temporary task for timeserver handshake processing
if (!timeSyncReqTask)
xTaskCreatePinnedToCore(process_timesync_req, // task function
"timesync_req", // name of task
4096, // stack size of task
(void *)1, // task parameter
4, // priority of the task
&timeSyncReqTask, // task handle
1); // CPU core
} }
} }
// task for sending time sync requests // task for sending time sync requests
void process_timesync_req(void *taskparameter) { void process_timesync_req(void *taskparameter) {
uint8_t k = 0; uint8_t k;
uint16_t time_to_set_fraction_msec; uint16_t time_to_set_fraction_msec;
uint32_t seq_no = 0, time_to_set; uint32_t seq_no, time_to_set;
auto time_offset_ms = myClock_msecTick::zero(); auto time_offset_ms = myClock_msecTick::zero();
// wait until we are joined while (1) {
if (!LMIC.devaddr)
// clear timestamp array before next sync run
time_offset_ms = myClock_msecTick::zero();
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++)
time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint();
// wait for kickoff
ulTaskNotifyTake(pdFALSE, portMAX_DELAY); ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
// enqueue timestamp samples in lora sendqueue // collect timestamp samples
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) {
// send sync request to server // send sync request to server
payload.reset(); payload.reset();
payload.addByte(time_sync_seqNo); payload.addByte(time_sync_seqNo);
SendPayload(TIMEPORT, prio_high); SendPayload(TIMEPORT, prio_high);
// process answer, wait for notification from recv_timesync_ans() // process answer, wait for notification from recv_timesync_ans()
if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no,
pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) || pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) ==
(seq_no != time_sync_seqNo)) pdFALSE) ||
goto error; // no valid sequence received before timeout (seq_no != time_sync_seqNo))
goto error; // no valid sequence received before timeout
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;
// cumulate timepoint diffs // cumulate timepoint diffs
time_offset_ms += time_point_cast<milliseconds>(time_sync_rx[k]) - time_offset_ms += time_point_cast<milliseconds>(time_sync_rx[k]) -
time_point_cast<milliseconds>(time_sync_tx[k]); time_point_cast<milliseconds>(time_sync_tx[k]);
// wrap around seqNo keeping it in time port range // wrap around seqNo keeping it in time port range
time_sync_seqNo = (time_sync_seqNo < TIMEANSWERPORT_MAX) time_sync_seqNo = (time_sync_seqNo < TIMEANSWERPORT_MAX)
? time_sync_seqNo + 1 ? time_sync_seqNo + 1
: TIMEANSWERPORT_MIN; : TIMEANSWERPORT_MIN;
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 { // before sending last time sample... } else { // before sending last time sample...
// ...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 alive open a receive window for last time_sync_answer
//LMIC_sendAlive(); // LMIC_sendAlive();
}
} }
} } // end for() collect timestamp samples
} // for
// begin of time critical section: lock I2C bus to ensure accurate timing // begin of time critical section: lock I2C bus to ensure accurate timing
if (!mask_user_IRQ()) if (!mask_user_IRQ())
goto error; // failure goto error; // failure
// average time offset from collected diffs // average time offset from collected diffs
time_offset_ms /= TIME_SYNC_SAMPLES; time_offset_ms /= TIME_SYNC_SAMPLES;
// calculate time offset with millisecond precision using LMIC's time base, // calculate time offset with millisecond precision using LMIC's time base,
// since we use LMIC's ostime_t txEnd as tx timestamp. // since we use LMIC's ostime_t txEnd as tx timestamp.
// Finally apply calibration const for processing time. // Finally apply calibration const for processing time.
time_offset_ms += time_offset_ms +=
milliseconds(osticks2ms(os_getTime())) + milliseconds(TIME_SYNC_FIXUP); milliseconds(osticks2ms(os_getTime())) + milliseconds(TIME_SYNC_FIXUP);
// calculate absolute time in UTC epoch: convert to whole seconds, round to // calculate absolute time in UTC epoch: convert to whole seconds, round to
// ceil, and calculate fraction milliseconds // ceil, and calculate fraction milliseconds
time_to_set = (uint32_t)(time_offset_ms.count() / 1000) + 1; time_to_set = (uint32_t)(time_offset_ms.count() / 1000) + 1;
// calculate fraction milliseconds // calculate fraction milliseconds
time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000); time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000);
setMyTime(time_to_set, time_to_set_fraction_msec); setMyTime(time_to_set, time_to_set_fraction_msec);
// end of time critical section: release I2C bus // end of time critical section: release I2C bus
unmask_user_IRQ(); unmask_user_IRQ();
goto finish; // end task goto finish;
error: error:
ESP_LOGW(TAG, "[%0.3f] Timeserver error: handshake timed out", ESP_LOGW(TAG, "[%0.3f] Timeserver error: handshake timed out",
millis() / 1000.0); millis() / 1000.0);
finish: finish:
vTaskDelete(NULL); // end task timeSyncPending = false;
} // infinite while(1)
} }
// called from lorawan.cpp after time_sync_req was sent // called from lorawan.cpp after time_sync_req was sent
void store_time_sync_req(uint32_t timestamp) { void store_time_sync_req(uint32_t timestamp) {
if (timeSyncReqTask) { if (timeSyncPending) {
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
time_sync_tx[k] += milliseconds(timestamp); time_sync_tx[k] += milliseconds(timestamp);
ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d", ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d",
millis() / 1000.0, k, timestamp / 1000, millis() / 1000.0, k, timestamp / 1000, timestamp % 1000);
timestamp % 1000);
} }
} }
@ -159,7 +155,7 @@ void store_time_sync_req(uint32_t timestamp) {
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
// if no timesync handshake is pending then exit // if no timesync handshake is pending then exit
if (!timeSyncReqTask) if (!timeSyncPending)
return 0; // failure return 0; // failure
// if no time is available or spurious buffer then exit // if no time is available or spurious buffer then exit
@ -197,8 +193,7 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
millis() / 1000.0, k, timestamp_sec, timestamp_msec); millis() / 1000.0, k, timestamp_sec, timestamp_msec);
// inform processing task // inform processing task
if (timeSyncReqTask) xTaskNotify(timeSyncReqTask, seq_no, eSetBits);
xTaskNotify(timeSyncReqTask, seq_no, eSetBits);
return 1; // success return 1; // success
} else { } else {
@ -245,4 +240,15 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) {
millis() / 1000.0); millis() / 1000.0);
} }
void timesync_init() {
// create task for timeserver handshake processing, called from main.cpp
xTaskCreatePinnedToCore(process_timesync_req, // task function
"timesync_req", // name of task
2048, // stack size of task
(void *)1, // task parameter
4, // priority of the task
&timeSyncReqTask, // task handle
1); // CPU core
}
#endif #endif