diff --git a/include/globals.h b/include/globals.h index 41002c32..a733a7fe 100644 --- a/include/globals.h +++ b/include/globals.h @@ -127,7 +127,6 @@ extern SemaphoreHandle_t I2Caccess; extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; -extern time_t userUTCTime; extern RTC_DATA_ATTR runmode_t RTC_runmode; // application includes diff --git a/include/lorawan.h b/include/lorawan.h index 90a7db56..d57a69fe 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -5,9 +5,6 @@ #include "rcommand.h" #include "timekeeper.h" #include -#if (TIME_SYNC_LORASERVER) -#include "timesync.h" -#endif // LMIC-Arduino LoRaWAN Stack #include @@ -54,9 +51,4 @@ const char *getSfName(rps_t rps); const char *getBwName(rps_t rps); const char *getCrName(rps_t rps); -#if (TIME_SYNC_LORAWAN) -void user_request_network_time_callback(void *pVoidUserUTCTime, - int flagSuccess); -#endif - #endif \ No newline at end of file diff --git a/include/timekeeper.h b/include/timekeeper.h index 20199ac1..8f45b0e7 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -5,6 +5,7 @@ #include "rtctime.h" #include "TimeLib.h" #include "irqhandler.h" +#include "timesync.h" #if (HAS_GPS) #include "gpsread.h" diff --git a/include/timesync.h b/include/timesync.h index 8e8a5f1d..40356847 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -1,30 +1,31 @@ #ifndef _TIMESYNC_H #define _TIMESYNC_H -#include #include "globals.h" #include "irqhandler.h" #include "timekeeper.h" -//#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds] #define TIME_SYNC_FRAME_LENGTH 0x07 // timeserver answer frame length [bytes] -#define TIME_SYNC_FIXUP 16 // empirical calibration to fixup processing time [milliseconds] +#define TIME_SYNC_FIXUP 16 // compensation for processing time [milliseconds] #define TIMEREQUEST_MAX_SEQNO 0xfe // threshold for wrap around seqno #define TIMEREQUEST_FINISH \ (TIMEREQUEST_MAX_SEQNO + 1) // marker for end of timesync handshake +#define GPS_UTC_DIFF 315964800 enum timesync_t { timesync_tx, timesync_rx, gwtime_sec, gwtime_msec, + gwtime_tzsec, no_of_timestamps }; void timesync_init(void); void send_timesync_req(void); int recv_timesync_ans(const uint8_t buf[], uint8_t buf_len); -void process_timesync_req(void *taskparameter); void store_timestamp(uint32_t timestamp, timesync_t timestamp_type); +void IRAM_ATTR process_timesync_req(void *taskparameter); +void IRAM_ATTR process_timesync_req(void *pVoidUserUTCTime, int flagSuccess); #endif diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 5c9ab138..853832aa 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -381,56 +381,6 @@ void lora_enqueuedata(MessageBuffer_t *message) { void lora_queuereset(void) { xQueueReset(LoraSendQueue); } -#if (TIME_SYNC_LORAWAN) -void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime, - int flagSuccess) { - // Explicit conversion from void* to uint32_t* to avoid compiler errors - time_t *pUserUTCTime = (time_t *)pVoidUserUTCTime; - - // A struct that will be populated by LMIC_getNetworkTimeReference. - // It contains the following fields: - // - tLocal: the value returned by os_GetTime() when the time - // request was sent to the gateway, and - // - tNetwork: the seconds between the GPS epoch and the time - // the gateway received the time request - lmic_time_reference_t lmicTimeReference; - - if (flagSuccess != 1) { - ESP_LOGW(TAG, "LoRaWAN network did not answer time request"); - return; - } - - // Populate lmic_time_reference - flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference); - if (flagSuccess != 1) { - ESP_LOGW(TAG, "LoRaWAN time request failed"); - return; - } - - // mask application irq to ensure accurate timing - mask_user_IRQ(); - - // Update userUTCTime, considering the difference between the GPS and UTC - // time, and the leap seconds until year 2019 - *pUserUTCTime = lmicTimeReference.tNetwork + 315964800; - // Current time, in ticks - ostime_t ticksNow = os_getTime(); - // Time when the request was sent, in ticks - ostime_t ticksRequestSent = lmicTimeReference.tLocal; - // Add the delay between the instant the time was transmitted and - // the current time - time_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000; - - // Update system time with time read from the network - setMyTime(*pUserUTCTime + requestDelaySec, 0, _lora); - -finish: - // end of time critical section: release app irq lock - unmask_user_IRQ(); - -} // user_request_network_time_callback -#endif // TIME_SYNC_LORAWAN - // LMIC lorawan stack task void lmictask(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); diff --git a/src/main.cpp b/src/main.cpp index 5e761b0e..60e57127 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -87,7 +87,6 @@ hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL; TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; SemaphoreHandle_t I2Caccess; bool volatile TimePulseTick = false; -time_t userUTCTime = 0; timesource_t timeSource = _unsynced; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 0558a156..0af5e956 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -37,12 +37,9 @@ void calibrateTime(void) { } #endif -// kick off asychronous Lora timeserver timesync if we have -#if (HAS_LORA) && (TIME_SYNC_LORASERVER) +// kick off asychronous lora timesync if we have +#if (HAS_LORA) && (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN) send_timesync_req(); -// kick off asychronous lora network sync if we have -#elif (HAS_LORA) && (TIME_SYNC_LORAWAN) - LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif // no time from GPS -> fallback to RTC time while trying lora sync @@ -233,7 +230,7 @@ void clock_init(void) { pinMode(HAS_DCF77, OUTPUT); #endif - userUTCTime = now(); + time_t userUTCTime = now(); xTaskCreatePinnedToCore(clock_loop, // task function "clockloop", // name of task diff --git a/src/timesync.cpp b/src/timesync.cpp index 325a04b6..6fa10648 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -1,31 +1,37 @@ /* -///--> IMPORTANT LICENSE NOTE for this file <--/// +///--> IMPORTANT LICENSE NOTE for timesync option 1 in this file <--/// PLEASE NOTE: There is a patent filed for the time sync algorithm used in the code of this file. The shown implementation example is covered by the repository's licencse, but you may not be eligible to deploy the applied algorithm in applications without granted license by the patent holder. +You may use timesync option 2 if you do not want or cannot accept this. + */ -#if (TIME_SYNC_LORASERVER) && (HAS_LORA) - #include "timesync.h" +#if (TIME_SYNC_LORASERVER) && (TIME_SYNC_LORAWAN) && (HAS_LORA) +#error Duplicate timesync method selected. You must select either LORASERVER or LORAWAN timesync. +#endif + // Local logging tag static const char TAG[] = __FILE__; -TaskHandle_t timeSyncReqTask = NULL; +// timesync option 1: use external timeserver (for LoRAWAN < 1.0.3) +#if (TIME_SYNC_LORASERVER) && (HAS_LORA) + +static TaskHandle_t timeSyncReqTask = NULL; +static bool timeSyncPending = false; static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO); static uint8_t sample_idx = 0; -static bool timeSyncPending = false; static uint32_t timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps] = {0}; // send time request message -void send_timesync_req() { - +void send_timesync_req(void) { // if a timesync handshake is pending then exit if (timeSyncPending) return; @@ -37,7 +43,7 @@ void send_timesync_req() { } // task for sending time sync requests -void process_timesync_req(void *taskparameter) { +void IRAM_ATTR process_timesync_req(void *taskparameter) { uint32_t rcv_seq_no = TIMEREQUEST_FINISH, time_offset_ms; @@ -175,22 +181,24 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) { else { // we received a probably valid time frame - // pointers to 4 bytes containing UTC seconds since unix epoch, msb + // pointers to 4 bytes msb order uint32_t timestamp_sec, *timestamp_ptr; - // extract 1 byte timezone from payload (one step being 15min * 60s = 900s) - // uint32_t timezone_sec = buf[0] * 900; // for future use + // extract 1 byte containing timezone offset + // one step being 15min * 60sec = 900sec + uint32_t timestamp_tzsec = buf[0] * 900; // timezone offset in secs buf++; - // extract 4 bytes timestamp from payload - // and convert it to uint32_t, octet order is big endian + // extract 4 bytes containing gateway time in UTC seconds since unix + // epoch and convert it to uint32_t, octet order is big endian timestamp_ptr = (uint32_t *)buf; - // swap byte order from msb to lsb, note: this is platform dependent + // swap byte order from msb to lsb, note: this is a platform dependent hack timestamp_sec = __builtin_bswap32(*timestamp_ptr); buf += 4; - // extract 1 byte fractional seconds in 2^-8 second steps - // (= 1/250th sec), we convert this to ms - uint16_t timestamp_msec = 4 * buf[0]; + + // extract 1 byte containing fractional seconds in 2^-8 second steps + // one step being 1/250th sec * 1000 = 4msec + uint16_t timestamp_msec = buf[0] * 4; // calculate absolute time received from gateway time_t t = timestamp_sec + timestamp_msec / 1000; @@ -202,6 +210,7 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) { // store time received from gateway store_timestamp(timestamp_sec, gwtime_sec); store_timestamp(timestamp_msec, gwtime_msec); + store_timestamp(timestamp_tzsec, gwtime_tzsec); // inform processing task xTaskNotify(timeSyncReqTask, seq_no, eSetBits); @@ -227,3 +236,56 @@ void timesync_init() { } #endif + +// timesync option 2: use LoRAWAN network time (requires LoRAWAN >= 1.0.3) + +#if (TIME_SYNC_LORAWAN) && (HAS_LORA) + +static time_t networkUTCTime; + +// send time request message +void send_timesync_req(void) { + LMIC_requestNetworkTime(process_timesync_req, &networkUTCTime); +} + +void IRAM_ATTR process_timesync_req(void *pVoidUserUTCTime, int flagSuccess) { + // Explicit conversion from void* to uint32_t* to avoid compiler errors + time_t *pUserUTCTime = (time_t *)pVoidUserUTCTime; + + // A struct that will be populated by LMIC_getNetworkTimeReference. + // It contains the following fields: + // - tLocal: the value returned by os_GetTime() when the time + // request was sent to the gateway, and + // - tNetwork: the seconds between the GPS epoch and the time + // the gateway received the time request + lmic_time_reference_t lmicTimeReference; + + if (flagSuccess != 1) { + ESP_LOGW(TAG, "LoRaWAN network did not answer time request"); + return; + } + + // Populate lmic_time_reference + flagSuccess = LMIC_getNetworkTimeReference(&lmicTimeReference); + if (flagSuccess != 1) { + ESP_LOGW(TAG, "LoRaWAN time request failed"); + return; + } + + // mask application irq to ensure accurate timing + mask_user_IRQ(); + + // Update networkUTCTime, considering the difference between GPS and UTC time + *pUserUTCTime = lmicTimeReference.tNetwork + GPS_UTC_DIFF; + // Add delay between the instant the time was transmitted and the current time + uint16_t requestDelaymSec = + osticks2ms(os_getTime() - lmicTimeReference.tLocal); + + // Update system time with time read from the network + setMyTime(*pUserUTCTime, requestDelaymSec, _lora); + + // end of time critical section: release app irq lock + unmask_user_IRQ(); + +} // user_request_network_time_callback +#endif // TIME_SYNC_LORAWAN \ No newline at end of file