From 3d26f737be0c4dd03fd8a8222c7348f81b7c0a78 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 21 Feb 2019 23:17:01 +0100 Subject: [PATCH] timemanager added (v1.7.32) --- include/clockcontroller.h | 15 --- include/cyclic.h | 2 +- include/display.h | 1 - include/globals.h | 8 +- include/gpsread.h | 1 + include/main.h | 3 +- include/rtctime.h | 11 -- include/timemanager.h | 26 +++++ platformio.ini | 4 +- src/clockcontroller.cpp | 74 -------------- src/cyclic.cpp | 32 +----- src/dcf77.cpp | 4 - src/display.cpp | 17 ++-- src/gpsread.cpp | 12 ++- src/lorawan.cpp | 8 +- src/main.cpp | 39 ++------ src/paxcounter.conf | 6 +- src/rtctime.cpp | 120 +--------------------- src/timemanager.cpp | 204 ++++++++++++++++++++++++++++++++++++++ 19 files changed, 277 insertions(+), 310 deletions(-) delete mode 100644 include/clockcontroller.h create mode 100644 include/timemanager.h delete mode 100644 src/clockcontroller.cpp create mode 100644 src/timemanager.cpp diff --git a/include/clockcontroller.h b/include/clockcontroller.h deleted file mode 100644 index c9748ce8..00000000 --- a/include/clockcontroller.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _CLOCKCONTROLLER_H -#define _CLOCKCONTROLLER_H - -#include "globals.h" - -#ifdef HAS_IF482 -#include "if482.h" -#elif defined HAS_DCF77 -#include "dcf77.h" -#endif - -void clock_init(void); -void clock_loop(void *pvParameters); - -#endif // _CLOCKCONTROLLER_H \ No newline at end of file diff --git a/include/cyclic.h b/include/cyclic.h index 3592bc86..7e80637f 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -5,7 +5,7 @@ #include "senddata.h" #include "rcommand.h" #include "spislave.h" -#include "rtctime.h" +#include "timemanager.h" #include #ifdef HAS_BME diff --git a/include/display.h b/include/display.h index 91f1be0a..99379b58 100644 --- a/include/display.h +++ b/include/display.h @@ -3,7 +3,6 @@ #include #include "cyclic.h" -#include "rtctime.h" extern uint8_t volatile DisplayState; extern HAS_DISPLAY u8x8; diff --git a/include/globals.h b/include/globals.h index a5aca5f8..6f1b55ed 100644 --- a/include/globals.h +++ b/include/globals.h @@ -7,6 +7,7 @@ // Time functions #include #include +#include // std::set for unified array functions #include @@ -106,12 +107,15 @@ extern char display_line6[], display_line7[]; // screen buffers extern uint8_t volatile channel; // wifi channel rotation counter extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values -extern bool volatile TimePulseTick; -extern hw_timer_t *sendCycle, *displaytimer; +extern bool volatile TimePulseTick; // one-pulse-per-second flags set by GPS or RTC +extern bool TimeIsSynced; +extern hw_timer_t *sendCycle, *displaytimer, *clockCycle; extern SemaphoreHandle_t I2Caccess, TimePulse; extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; +extern time_t LastSyncTime; +extern RtcDateTime compiled; // application includes #include "led.h" diff --git a/include/gpsread.h b/include/gpsread.h index 9fccefb4..e11c2644 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -2,6 +2,7 @@ #define _GPSREAD_H #include // library for parsing NMEA data +#include #ifdef GPS_I2C // Needed for reading from I2C Bus #include diff --git a/include/main.h b/include/main.h index 0c6adbdd..44f2279d 100644 --- a/include/main.h +++ b/include/main.h @@ -17,6 +17,5 @@ #include "led.h" #include "spislave.h" #include "lorawan.h" -#include "rtctime.h" -#include "clockcontroller.h" +#include "timemanager.h" #endif \ No newline at end of file diff --git a/include/rtctime.h b/include/rtctime.h index b0ac1221..18eebf94 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -5,10 +5,6 @@ #include // must be included here so that Arduino library object file references work #include -#ifdef HAS_GPS -#include "gpsread.h" -#endif - extern RtcDS3231 Rtc; // make RTC instance globally available int rtc_init(void); @@ -17,12 +13,5 @@ int set_rtctime(time_t t); void sync_rtctime(void); time_t get_rtctime(void); float get_rtctemp(void); -void IRAM_ATTR CLOCKIRQ(void); -int timepulse_init(void); -void timepulse_start(void); -int sync_TimePulse(void); -int sync_SysTime(time_t); -int sync_SysTime(uint32_t t); -time_t best_time(void); #endif // _RTCTIME_H \ No newline at end of file diff --git a/include/timemanager.h b/include/timemanager.h new file mode 100644 index 00000000..dcc5ca87 --- /dev/null +++ b/include/timemanager.h @@ -0,0 +1,26 @@ +#ifndef _timemanager_H +#define _timemanager_H + +#include "globals.h" +#include "rtctime.h" + +#ifdef HAS_GPS +#include "gpsread.h" +#endif +#ifdef HAS_IF482 +#include "if482.h" +#elif defined HAS_DCF77 +#include "dcf77.h" +#endif + +void clock_init(void); +void clock_loop(void *pvParameters); +void time_sync(void); +bool wait_for_pulse(void); +int syncTime(time_t); +int syncTime(uint32_t t); +void IRAM_ATTR CLOCKIRQ(void); +int timepulse_init(void); +void timepulse_start(void); + +#endif // _timemanager_H \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 5532f163..ee5fc357 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.7.31 +release_version = 1.7.32 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose -debug_level = 0 +debug_level = 3 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom diff --git a/src/clockcontroller.cpp b/src/clockcontroller.cpp deleted file mode 100644 index 7c01b377..00000000 --- a/src/clockcontroller.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "clockcontroller.h" - -#if defined HAS_IF482 || defined HAS_DCF77 - -#if defined HAS_DCF77 && defined HAS_IF482 -#error You must define at most one of IF482 or DCF77! -#endif - -// Local logging tag -static const char TAG[] = "main"; - -void clock_init(void) { - -// setup clock output interface -#ifdef HAS_IF482 - IF482.begin(HAS_IF482); -#elif defined HAS_DCF77 - pinMode(HAS_DCF77, OUTPUT); -#endif - - xTaskCreatePinnedToCore(clock_loop, // task function - "clockloop", // name of task - 2048, // stack size of task - (void *)1, // task parameter - 4, // priority of the task - &ClockTask, // task handle - 0); // CPU core - - assert(ClockTask); // has clock task started? -} // clock_init - -void clock_loop(void *pvParameters) { // ClockTask - - configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - - TickType_t wakeTime; - time_t t; - -#define t1(t) (t + DCF77_FRAME_SIZE + 1) // future time for next frame - -// preload first DCF frame before start -#ifdef HAS_DCF77 - DCF77_Frame(t1(best_time())); -#endif - - // output time telegram for second following sec beginning with timepulse - for (;;) { - xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, - portMAX_DELAY); // wait for timepulse - - if (timeStatus() == timeNotSet) // do we have valid time? - continue; - - t = best_time(); // time to send to clock - -#if defined HAS_IF482 - - IF482_Pulse(t + 1); // next second - -#elif defined HAS_DCF77 - - if (second(t) == DCF77_FRAME_SIZE - 1) // moment to reload frame? - DCF77_Frame(t1(t)); // generate next frame - - if (DCFpulse[DCF77_FRAME_SIZE] == - minute(t1(t))) // do he have a recent frame? - DCF_Pulse(t + 1); // then output next second of current frame - -#endif - - } // for -} // clock_loop() - -#endif // HAS_IF482 || defined HAS_DCF77 \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 5adcd698..11ba7c5b 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -8,8 +8,6 @@ static const char TAG[] = "main"; time_t userUTCTime; // Seconds since the UTC epoch -unsigned long nextLoraTimeSync = millis(); -unsigned long nextGPSTimeSync = millis(); // do all housekeeping void doHousekeeping() { @@ -23,35 +21,7 @@ void doHousekeeping() { spi_housekeeping(); lora_housekeeping(); - -// do cyclic sync of systime with GPS timepulse, if present -#if defined HAS_GPS && defined TIME_SYNC_INTERVAL_GPS - if (millis() >= nextGPSTimeSync) { - nextGPSTimeSync = millis() + TIME_SYNC_INTERVAL_GPS * - 60000; // set up next time sync period - - // sync systime on next timepulse - if (sync_SysTime(get_gpstime())) { - //setSyncProvider(get_gpstime); -#ifdef HAS_RTC - set_rtctime(now()); // epoch time -#endif - ESP_LOGI(TAG, "GPS has set the system time"); - } else - ESP_LOGI(TAG, "Unable to sync system time with GPS"); - } // if -#endif - -// do cyclic time sync with LORA network, if present -#if defined HAS_LORA && defined TIME_SYNC_INTERVAL_LORA - if (millis() >= nextLoraTimeSync) { - nextLoraTimeSync = millis() + TIME_SYNC_INTERVAL_LORA * - 60000; // set up next time sync period - // Schedule a network time sync request at the next possible time - LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); - ESP_LOGI(TAG, "LORAWAN time request scheduled"); - } -#endif + time_sync(); // task storage debugging // ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 20b6344c..824a0b81 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -26,8 +26,6 @@ void DCF_Pulse(time_t t) { TickType_t startTime = xTaskGetTickCount(); - ESP_LOGD(TAG, "DCF77 sec %d", sec); - // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { @@ -64,8 +62,6 @@ void IRAM_ATTR DCF77_Frame(time_t tt) { uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time - ESP_LOGD(TAG, "DCF77 minute %d", minute(t)); - // ENCODE HEAD // secs 0..19 initialized with zeros for (int n = 0; n <= 19; n++) diff --git a/src/display.cpp b/src/display.cpp index aae90c4a..f4f6abe8 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -43,11 +43,11 @@ const char lora_datarate[] = {"121110090807FSNA"}; // time display symbols #if defined HAS_GPS || defined HAS_RTC -const char timeNosyncSymbol = '?'; +const char timeNoPulseSymbol = '?'; #if defined HAS_IF482 -const char timesyncSymbol = '+'; +const char timePulseSymbol = '+'; #elif defined HAS_DCF77 -const char timesyncSymbol = '*'; +const char timePulseSymbol = '*'; #endif #endif @@ -141,7 +141,7 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { uint8_t msgWaiting; - char timeSync, timeState; + char timePulse, timeState; char buff[16]; // 16 chars line buffer time_t t; @@ -225,12 +225,13 @@ void refreshtheDisplay() { // update LoRa status display (line 6) u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status - t = myTZ.toLocal(best_time()); - timeSync = (timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol; + t = myTZ.toLocal(now()); + timePulse = (timeStatus() == timeSet) ? timePulseSymbol : timeNoPulseSymbol; timeState = TimePulseTick ? timeSync : ' '; TimePulseTick = false; - u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), - timeState, day(t), printmonth[month(t)]); + u8x8.printf("%02d:%02d%c%02d%c %2d.%3s", hour(t), minute(t), + TimeIsSynced ? ':' : '.', second(t), timeState, day(t), + printmonth[month(t)]); #endif // update LMiC event display (line 7) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index d19f585b..d9b6d281 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -91,12 +91,18 @@ time_t get_gpstime(void) { // !! never call now() or delay in this function, this would break this // function to be used as SyncProvider for Time.h - time_t t = 0; // 0 effects calling SyncProvider() to not set time + time_t t = 0; - if ((gps.time.age() < 1000) && (gps.time.isValid())) { - // get current gps time + if ((gps.time.age() < 900) && (gps.time.isValid()) && + (gps.date.year() >= compiled.Year())) { + + // use recent gps time t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); + + ESP_LOGD(TAG, "GPS time: %02d.%02d.%04d %02d:%02d:%02d", gps.date.day(), + gps.date.month(), gps.date.year(), gps.time.hour(), + gps.time.minute(), gps.time.second()); } return t; } // get_gpstime() diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 4991b943..789cf92b 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -471,13 +471,13 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (sync_TimePulse()) { // wait for start of next second - if (sync_SysTime(*pUserUTCTime)) { // do we have a valid time? + if (syncTime(*pUserUTCTime)) { // do we have a valid time? #ifdef HAS_RTC + if (TimeIsSynced) set_rtctime(now()); // epoch time #endif - ESP_LOGI(TAG, "LORA has set the system time"); - } + LastSyncTime = now(); // remember time of this sync event + ESP_LOGI(TAG, "LORA has set the system time"); } else ESP_LOGI(TAG, "Unable to sync system time with LORA"); #endif // HAS_LORA diff --git a/src/main.cpp b/src/main.cpp index da35ab79..6f02cfd6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,14 +65,15 @@ uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -hw_timer_t *sendCycle = NULL, *homeCycle = NULL; -#ifdef HAS_DISPLAY -hw_timer_t *displaytimer = NULL; -#endif +hw_timer_t *sendCycle = NULL, *homeCycle = NULL, *clockCycle = NULL, *displaytimer = NULL; TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; bool volatile TimePulseTick = false; +bool TimeIsSynced = false; +time_t LastSyncTime = 0; + +RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present @@ -359,11 +360,12 @@ void setup() { #endif // start pps timepulse - ESP_LOGI(TAG, "Starting timepulse..."); + ESP_LOGI(TAG, "Starting Timepulse..."); if (timepulse_init()) // setup timepulse timepulse_start(); // start pulse else - ESP_LOGE(TAG, "No timepulse, systime will not be synced!"); + ESP_LOGE(TAG, "No timepulse, time will not be synced!"); + time_sync(); // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); @@ -416,31 +418,6 @@ void setup() { #endif #endif // HAS_BUTTON -#ifdef HAS_GPS - // sync systime on next timepulse - ESP_LOGI(TAG, "GPS is setting system time"); - if (sync_SysTime(get_gpstime())) { - //setSyncProvider(get_gpstime); // reset sync cycle on top of second - //setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); - // calibrate RTC -#ifdef HAS_RTC - set_rtctime(now()); // epoch time -#endif - } else - ESP_LOGI(TAG, "Unable to sync system time with GPS"); -#endif // HAS_GPS - - // initialize systime from timesource -#ifdef HAS_RTC - // sync systime on next timepulse - ESP_LOGI(TAG, "RTC is setting system time"); - if (sync_SysTime(get_rtctime())) { - //setSyncProvider(get_rtctime); // reset sync cycle on top of second - //setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60); - } else - ESP_LOGI(TAG, "Unable to sync system time with RTC"); -#endif // HAS_RTC - #if defined HAS_IF482 || defined HAS_DCF77 ESP_LOGI(TAG, "Starting Clock Controller..."); clock_init(); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index c86b870b..ea741ae5 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -82,9 +82,9 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node and external time sources -#define TIME_SYNC_INTERVAL_GPS 5 // sync time each .. minutes from GPS [default = 5], comment out means off -#define TIME_SYNC_INTERVAL_RTC 60 // sync time each .. minutes from RTC [default = 60], comment out means off -//#define TIME_SYNC_INTERVAL_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means off +#define TIME_SYNC_INTERVAL 10 // sync time each .. minutes from external time source (GPS/LORA) [default = 10], comment out means off +#define TIME_SYNC_TIMEOUT 30 // fallback to rtc for timesync after .. minutes no sync with external time source +//#define TIME_SYNC_LORA 1 // use LORA network for timesync, comment out means off [default = off] // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time diff --git a/src/rtctime.cpp b/src/rtctime.cpp index d565440a..7f39b361 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -3,120 +3,6 @@ // Local logging tag static const char TAG[] = "main"; -hw_timer_t *clockCycle = NULL; - -// helper function to setup a pulse per second for time synchronisation -int timepulse_init() { - -// use time pulse from GPS as time base with fixed 1Hz frequency -#ifdef GPS_INT - - // setup external interupt for active low RTC INT pin - pinMode(GPS_INT, INPUT_PULLDOWN); - // setup external rtc 1Hz clock as pulse per second clock - ESP_LOGI(TAG, "Time base: external (GPS)"); - return 1; // success - -// use pulse from on board RTC chip as time base with fixed frequency -#elif defined RTC_INT - - // setup external interupt for active low RTC INT pin - pinMode(RTC_INT, INPUT_PULLUP); - - // setup external rtc 1Hz clock as pulse per second clock - if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); - I2C_MUTEX_UNLOCK(); - ESP_LOGI(TAG, "Time base: external (RTC)"); - return 1; // success - } else { - ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); - return 0; // failure - } - return 1; // success - -#else - // use ESP32 hardware timer as time base with adjustable frequency - clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec - timerAlarmWrite(clockCycle, 10000, true); // 1000ms - ESP_LOGI(TAG, "Time base: internal (ESP32 hardware timer)"); - return 1; // success - -#endif -} // timepulse_init - -void timepulse_start(void) { -#ifdef GPS_INT // start external clock gps pps line - attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); -#elif defined RTC_INT // start external clock rtc - attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); -#else // start internal clock esp32 hardware timer - timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); - timerAlarmEnable(clockCycle); -#endif -} - -// interrupt service routine triggered by either pps or esp32 hardware timer -void IRAM_ATTR CLOCKIRQ(void) { - if (ClockTask != NULL) - xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); -#if defined GPS_INT || defined RTC_INT - xSemaphoreGiveFromISR(TimePulse, NULL); - TimePulseTick = !TimePulseTick; // flip ticker -#endif - portYIELD_FROM_ISR(); -} - -// helper function to sync systime on start of next second -int sync_SysTime(time_t t) { - if (sync_TimePulse() && (t)) { // wait for start of next second by timepulse - setTime(t + 1); - ESP_LOGD(TAG, "Systime synced on second"); - return 1; // success - } else - return 0; // failure -} - -int sync_SysTime(uint32_t t) { // t is epoch seconds starting 1.1.1970 - return sync_SysTime(static_cast(t)); -} - -// helper function to sync moment on timepulse -int sync_TimePulse(void) { - // sync on top of next second by timepulse - if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1100)) == pdTRUE) { - return 1; - } // success - else - ESP_LOGW(TAG, "Missing timepulse, time not synced"); - return 0; // failure -} - -// helper function to fetch current second from most precise time source -time_t best_time(void) { - - time_t t; - -#ifdef HAS_GPS // gps is our primary time source if present - t = get_gpstime(); - if (t) // did we get a valid time? - return t; -#endif - - /* - // Reading RTC time from chip take too long on i2c bus, causes jitter - #ifdef HAS_RTC // rtc is our secondary time source if present - t = get_rtctime(); - if (t) - return t; - #endif - */ - - // else we use systime as fallback source - return now(); -} - #ifdef HAS_RTC // we have hardware RTC RtcDS3231 Rtc(Wire); // RTC hardware i2c interface @@ -132,8 +18,6 @@ int rtc_init(void) { Wire.begin(HAS_RTC); Rtc.Begin(); - RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); - if (!Rtc.IsDateTimeValid()) { ESP_LOGW(TAG, "RTC has no valid RTC date/time, setting to compilation date"); @@ -175,7 +59,7 @@ int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 if (I2C_MUTEX_LOCK()) { Rtc.SetDateTime(RtcDateTime(t)); I2C_MUTEX_UNLOCK(); // release i2c bus access - ESP_LOGI(TAG, "RTC calibrated"); + ESP_LOGI(TAG, "RTC time synced"); return 1; // success } ESP_LOGE(TAG, "RTC set time failure"); @@ -193,7 +77,7 @@ time_t get_rtctime(void) { time_t t = 0; // 0 effects calling SyncProvider() to not set time // block i2c bus access if (I2C_MUTEX_LOCK()) { - if (Rtc.IsDateTimeValid()) { + if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { RtcDateTime tt = Rtc.GetDateTime(); t = tt.Epoch32Time(); } diff --git a/src/timemanager.cpp b/src/timemanager.cpp new file mode 100644 index 00000000..9adac15d --- /dev/null +++ b/src/timemanager.cpp @@ -0,0 +1,204 @@ +#include "timemanager.h" + +// Local logging tag +static const char TAG[] = "main"; + +void time_sync() { + // synchonization of systime with external time source (GPS/LORA) + // function is frequently called from cyclic.cpp + +#ifdef TIME_SYNC_INTERVAL + + time_t lastTimeSync = now() - LastSyncTime; // check if a sync is due + + if ((lastTimeSync >= (TIME_SYNC_INTERVAL * 60000)) || !LastSyncTime) + // is it time to sync with external source? +#ifdef HAS_GPS + syncTime(get_gpstime()); // attempt sync with GPS time +#elif defined HAS_LORA && defined TIME_SYNC_LORA + LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); +#else + { + } // no time source -> no sync +#endif + +#ifdef HAS_RTC + if (TimeIsSynced) { // recalibrate RTC, if we have one + set_rtctime(now()); + } + else { // we switch to fallback time after a while + if ((lastTimeSync >= (TIME_SYNC_TIMEOUT * 60000)) || + !LastSyncTime) { // sync stil due -> use RTC as fallback source + syncTime(get_rtctime()); // sync with RTC time + TimeIsSynced = false; // + } + } +#endif + +#endif // TIME_SYNC_INTERVAL +} // time_sync() + +// helper function to sync time on start of next second +int syncTime(time_t t) { + if (t) { + TimeIsSynced = wait_for_pulse(); // wait for next 1pps timepulse + setTime(t); + adjustTime(1); // forward time to next second + LastSyncTime = now(); // store time of this sync + ESP_LOGD(TAG, "System time was set to %02d:%02d:%02d", hour(t), minute(t), + second(t)); + return 1; // success + } else { + ESP_LOGD(TAG, "System time sync attempt failed"); + TimeIsSynced = false; + return 0; + } + // failure +} + +int syncTime(uint32_t t) { // t is epoch seconds starting 1.1.1970 + return syncTime(static_cast(t)); +} + +// helper function to sync moment on timepulse +bool wait_for_pulse(void) { + // sync on top of next second with 1pps timepulse + if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) + return true; // success + ESP_LOGD(TAG, "Missing timepulse"); + return false; +} + +// helper function to setup a pulse per second for time synchronisation +int timepulse_init() { + +// use time pulse from GPS as time base with fixed 1Hz frequency +#ifdef GPS_INT + + // setup external interupt for active low RTC INT pin + pinMode(GPS_INT, INPUT_PULLDOWN); + // setup external rtc 1Hz clock as pulse per second clock + ESP_LOGI(TAG, "Timepulse: external (GPS)"); + return 1; // success + +// use pulse from on board RTC chip as time base with fixed frequency +#elif defined RTC_INT + + // setup external interupt for active low RTC INT pin + pinMode(RTC_INT, INPUT_PULLUP); + + // setup external rtc 1Hz clock as pulse per second clock + if (I2C_MUTEX_LOCK()) { + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); + I2C_MUTEX_UNLOCK(); + ESP_LOGI(TAG, "Timepulse: external (RTC)"); + return 1; // success + } else { + ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); + return 0; // failure + } + return 1; // success + +#else + // use ESP32 hardware timer as time base with adjustable frequency + clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec + timerAlarmWrite(clockCycle, 10000, true); // 1000ms + ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); + return 1; // success + +#endif +} // timepulse_init + +void timepulse_start(void) { +#ifdef GPS_INT // start external clock gps pps line + attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); +#elif defined RTC_INT // start external clock rtc + attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); +#else // start internal clock esp32 hardware timer + timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); + timerAlarmEnable(clockCycle); +#endif +} + +// interrupt service routine triggered by either pps or esp32 hardware timer +void IRAM_ATTR CLOCKIRQ(void) { + if (ClockTask != NULL) + xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); +#if defined GPS_INT || defined RTC_INT + xSemaphoreGiveFromISR(TimePulse, NULL); + TimePulseTick = !TimePulseTick; // flip ticker +#endif + portYIELD_FROM_ISR(); +} + +#if defined HAS_IF482 || defined HAS_DCF77 + +#if defined HAS_DCF77 && defined HAS_IF482 +#error You must define at most one of IF482 or DCF77! +#endif + +void clock_init(void) { + +// setup clock output interface +#ifdef HAS_IF482 + IF482.begin(HAS_IF482); +#elif defined HAS_DCF77 + pinMode(HAS_DCF77, OUTPUT); +#endif + + xTaskCreatePinnedToCore(clock_loop, // task function + "clockloop", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 4, // priority of the task + &ClockTask, // task handle + 0); // CPU core + + assert(ClockTask); // has clock task started? +} // clock_init + +void clock_loop(void *pvParameters) { // ClockTask + + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check + + TickType_t wakeTime; + time_t t; + +#define t1(t) (t + DCF77_FRAME_SIZE + 1) // future time for next DCF77 frame +#define t2(t) (t + 1) // future time for sync with 1pps trigger + +// preload first DCF frame before start +#ifdef HAS_DCF77 + DCF77_Frame(t1(now())); +#endif + + // output time telegram for second following sec beginning with timepulse + for (;;) { + xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, + portMAX_DELAY); // wait for timepulse + + if (timeStatus() == timeNotSet) // do we have valid time? + continue; + + t = now(); // payload to send to clock + +#if defined HAS_IF482 + + IF482_Pulse(t2(t)); // next second + +#elif defined HAS_DCF77 + + if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? + DCF77_Frame(t1(t)); // generate next frame + + if (DCFpulse[DCF77_FRAME_SIZE] == + minute(t1(t))) // have recent frame? (pulses could be missed!) + DCF_Pulse(t2(t)); // then output next second of this frame + +#endif + + } // for +} // clock_loop() + +#endif // HAS_IF482 || defined HAS_DCF77 \ No newline at end of file