diff --git a/include/globals.h b/include/globals.h index 6f1b55ed..a3f3bbea 100644 --- a/include/globals.h +++ b/include/globals.h @@ -115,7 +115,6 @@ 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/timemanager.h b/include/timemanager.h index dcc5ca87..16f828fa 100644 --- a/include/timemanager.h +++ b/include/timemanager.h @@ -13,14 +13,18 @@ #include "dcf77.h" #endif +time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, + uint8_t mm, uint8_t ss); void clock_init(void); void clock_loop(void *pvParameters); void time_sync(void); -bool wait_for_pulse(void); +int 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); +int TimeIsValid(time_t t); +time_t compiledUTC(void); #endif // _timemanager_H \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 11ba7c5b..8393f2b9 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -7,7 +7,7 @@ // Local logging tag static const char TAG[] = "main"; -time_t userUTCTime; // Seconds since the UTC epoch +time_t userUTCTime; // Seconds since the UTC in seconds GPS time starting 1.1.2000 // do all housekeeping void doHousekeeping() { diff --git a/src/display.cpp b/src/display.cpp index f4f6abe8..9c949e75 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -134,8 +134,7 @@ void init_display(const char *Productname, const char *Version) { u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%d", cfg.rssilimit); I2C_MUTEX_UNLOCK(); // release i2c bus access - } - + } // mutex } // init_display void refreshtheDisplay() { @@ -155,8 +154,10 @@ void refreshtheDisplay() { } // if display is switched off we don't refresh it to relax cpu - if (!DisplayState) + if (!DisplayState) { + I2C_MUTEX_UNLOCK(); // release i2c bus access return; + } // update counter (lines 0-1) snprintf( @@ -226,12 +227,11 @@ void refreshtheDisplay() { u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status t = myTZ.toLocal(now()); - timePulse = (timeStatus() == timeSet) ? timePulseSymbol : timeNoPulseSymbol; - timeState = TimePulseTick ? timeSync : ' '; + timePulse = TimeIsSynced ? timePulseSymbol : timeNoPulseSymbol; + timeState = TimePulseTick ? timePulse : ' '; TimePulseTick = false; - u8x8.printf("%02d:%02d%c%02d%c %2d.%3s", hour(t), minute(t), - TimeIsSynced ? ':' : '.', second(t), timeState, day(t), - printmonth[month(t)]); + u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), + timeState, day(t), printmonth[month(t)]); #endif // update LMiC event display (line 7) @@ -250,8 +250,7 @@ void refreshtheDisplay() { #endif // HAS_LORA I2C_MUTEX_UNLOCK(); // release i2c bus access - } - + } // mutex } // refreshDisplay() #endif // HAS_DISPLAY \ No newline at end of file diff --git a/src/gpsread.cpp b/src/gpsread.cpp index d9b6d281..d8a397c2 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -73,19 +73,6 @@ void gps_read() { gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); } -// helper function to convert gps date/time into time_t -time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, - uint8_t mm, uint8_t ss) { - tmElements_t tm; - tm.Year = YYYY - 1970; // note year argument is offset from 1970 in time.h - tm.Month = MM; - tm.Day = DD; - tm.Hour = hh; - tm.Minute = mm; - tm.Second = ss; - return makeTime(tm); -} - // function to fetch current time from gps time_t get_gpstime(void) { // !! never call now() or delay in this function, this would break this @@ -93,8 +80,10 @@ time_t get_gpstime(void) { time_t t = 0; - if ((gps.time.age() < 900) && (gps.time.isValid()) && - (gps.date.year() >= compiled.Year())) { + if ((gps.time.age() < 950) && (gps.time.isValid())) { + + ESP_LOGD(TAG, "GPS time age: %dms, is valid: %s", gps.time.age(), + gps.time.isValid() ? "yes" : "no"); // use recent gps time t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), diff --git a/src/lmic_config.h b/src/lmic_config.h index eb9b53c7..50c0b4ad 100644 --- a/src/lmic_config.h +++ b/src/lmic_config.h @@ -22,7 +22,7 @@ //#define LMIC_USE_INTERRUPTS //time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3) -//#define LMIC_ENABLE_DeviceTimeReq 1 +#define LMIC_ENABLE_DeviceTimeReq 1 // 16 μs per tick // LMIC requires ticks to be 15.5μs - 100 μs long diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 789cf92b..f94c60d0 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -459,7 +459,8 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, } // Update userUTCTime, considering the difference between the GPS and UTC - // epoch, and the leap seconds + // time, and the leap seconds + // !!! DANGER !!! This code will expire in the year when next leap second happenes *pUserUTCTime = lmicTimeReference.tNetwork + 315964800; // Current time, in ticks ostime_t ticksNow = os_getTime(); @@ -474,7 +475,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, if (syncTime(*pUserUTCTime)) { // do we have a valid time? #ifdef HAS_RTC if (TimeIsSynced) - set_rtctime(now()); // epoch time + set_rtctime(now()); // UTC time #endif LastSyncTime = now(); // remember time of this sync event ESP_LOGI(TAG, "LORA has set the system time"); diff --git a/src/main.cpp b/src/main.cpp index 6f02cfd6..4211de6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,8 @@ 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, *clockCycle = NULL, *displaytimer = NULL; +hw_timer_t *sendCycle = NULL, *homeCycle = NULL, *clockCycle = NULL, + *displaytimer = NULL; TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; @@ -73,8 +74,6 @@ 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 std::set, Mallocator> macs; @@ -82,7 +81,7 @@ std::set, Mallocator> macs; // initialize payload encoder PayloadConvert payload(PAYLOAD_BUFFER_SIZE); -// set Time Zone, fetch user setting from paxcounter.conf +// set Time Zone for user setting from paxcounter.conf TimeChangeRule myDST = DAYLIGHT_TIME; TimeChangeRule mySTD = STANDARD_TIME; Timezone myTZ(myDST, mySTD); @@ -97,7 +96,7 @@ void setup() { char features[100] = ""; - // create some semaphores for syncing / mutexing tasks + // create some semaphores for syncing / mutexing tasks I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus if (I2Caccess) xSemaphoreGive(I2Caccess); // Flag the i2c bus available for use diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 7f39b361..a3bf3fcf 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -10,63 +10,52 @@ RtcDS3231 Rtc(Wire); // RTC hardware i2c interface // initialize RTC int rtc_init(void) { - // return = 0 -> error / return = 1 -> success - - // block i2c bus access - if (I2C_MUTEX_LOCK()) { + if (I2C_MUTEX_LOCK()) { // block i2c bus access Wire.begin(HAS_RTC); Rtc.Begin(); - if (!Rtc.IsDateTimeValid()) { - ESP_LOGW(TAG, - "RTC has no valid RTC date/time, setting to compilation date"); - Rtc.SetDateTime(compiled); - } + // configure RTC chip + Rtc.Enable32kHzPin(false); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); if (!Rtc.GetIsRunning()) { ESP_LOGI(TAG, "RTC not running, starting now"); Rtc.SetIsRunning(true); } - RtcDateTime now = Rtc.GetDateTime(); + RtcDateTime tt = Rtc.GetDateTime(); + time_t t = tt.Epoch32Time(); // sec2000 -> epoch - if (now < compiled) { - ESP_LOGI(TAG, "RTC date/time is older than compilation date, updating"); - Rtc.SetDateTime(compiled); + if (!Rtc.IsDateTimeValid() || !TimeIsValid(t)) { + ESP_LOGW(TAG, "RTC has no recent time, setting to compilation date"); + Rtc.SetDateTime( + RtcDateTime(compiledUTC() - SECS_YR_2000)); // epoch -> sec2000 } - // configure RTC chip - Rtc.Enable32kHzPin(false); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); - + I2C_MUTEX_UNLOCK(); // release i2c bus access + ESP_LOGI(TAG, "RTC initialized"); + return 1; // success } else { - ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); - goto error; + ESP_LOGE(TAG, "RTC initialization error, I2C bus busy"); + return 0; // failure } - I2C_MUTEX_UNLOCK(); // release i2c bus access - ESP_LOGI(TAG, "RTC initialized"); - return 1; - -error: - I2C_MUTEX_UNLOCK(); // release i2c bus access - return 0; - } // rtc_init() -int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 +int set_rtctime(time_t t) { // t is UTC in seconds epoch time if (I2C_MUTEX_LOCK()) { - Rtc.SetDateTime(RtcDateTime(t)); - I2C_MUTEX_UNLOCK(); // release i2c bus access + Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000 + I2C_MUTEX_UNLOCK(); ESP_LOGI(TAG, "RTC time synced"); return 1; // success - } - ESP_LOGE(TAG, "RTC set time failure"); - return 0; // failure + } else { + ESP_LOGE(TAG, "RTC set time failure"); + return 0; + } // failure } // set_rtctime() -int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970 +int set_rtctime(uint32_t t) { // t is UTC in seconds epoch time return set_rtctime(static_cast(t)); // set_rtctime() } @@ -74,25 +63,23 @@ int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970 time_t get_rtctime(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 - // block i2c bus access + time_t t = 0; if (I2C_MUTEX_LOCK()) { if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { RtcDateTime tt = Rtc.GetDateTime(); - t = tt.Epoch32Time(); + t = tt.Epoch32Time(); // sec2000 -> epoch } - I2C_MUTEX_UNLOCK(); // release i2c bus access + I2C_MUTEX_UNLOCK(); } return t; } // get_rtctime() float get_rtctemp(void) { - // block i2c bus access if (I2C_MUTEX_LOCK()) { RtcTemperature temp = Rtc.GetTemperature(); - I2C_MUTEX_UNLOCK(); // release i2c bus access + I2C_MUTEX_UNLOCK(); return temp.AsFloatDegC(); - } // while + } return 0; } // get_rtctemp() diff --git a/src/timemanager.cpp b/src/timemanager.cpp index 9adac15d..ee24c2c2 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -5,7 +5,7 @@ static const char TAG[] = "main"; void time_sync() { // synchonization of systime with external time source (GPS/LORA) - // function is frequently called from cyclic.cpp + // frequently called from cyclic.cpp #ifdef TIME_SYNC_INTERVAL @@ -25,12 +25,11 @@ void time_sync() { #ifdef HAS_RTC if (TimeIsSynced) { // recalibrate RTC, if we have one set_rtctime(now()); - } - else { // we switch to fallback time after a while + } else { // we switch to fallback time after a while if ((lastTimeSync >= (TIME_SYNC_TIMEOUT * 60000)) || - !LastSyncTime) { // sync stil due -> use RTC as fallback source + !LastSyncTime) { // sync is still due -> use RTC as fallback source syncTime(get_rtctime()); // sync with RTC time - TimeIsSynced = false; // + TimeIsSynced = false; } } #endif @@ -40,33 +39,33 @@ void time_sync() { // helper function to sync time on start of next second int syncTime(time_t t) { - if (t) { + if (TimeIsValid(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), + ESP_LOGD(TAG, "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"); + ESP_LOGD(TAG, "Time sync attempt failed"); TimeIsSynced = false; return 0; } // failure } -int syncTime(uint32_t t) { // t is epoch seconds starting 1.1.1970 +int syncTime(uint32_t t) { // t is UTC time in seconds epoch return syncTime(static_cast(t)); } // helper function to sync moment on timepulse -bool wait_for_pulse(void) { +int wait_for_pulse(void) { // sync on top of next second with 1pps timepulse if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) - return true; // success + return 1; // success ESP_LOGD(TAG, "Missing timepulse"); - return false; + return 0; // failure } // helper function to setup a pulse per second for time synchronisation @@ -95,7 +94,7 @@ int timepulse_init() { ESP_LOGI(TAG, "Timepulse: external (RTC)"); return 1; // success } else { - ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); + ESP_LOGE(TAG, "RTC initialization error, I2C bus busy"); return 0; // failure } return 1; // success @@ -132,6 +131,34 @@ void IRAM_ATTR CLOCKIRQ(void) { portYIELD_FROM_ISR(); } +// helper function to check plausibility of a time +int TimeIsValid(time_t t) { + // is it a time in the past? we use compile date to guess + ESP_LOGD(TAG, "t=%d, tt=%d, valid: %s", t, compiledUTC(), + (t >= compiledUTC()) ? "yes" : "no"); + return (t >= compiledUTC()); +} + +// helper function to convert compile time to UTC time +time_t compiledUTC(void) { + time_t t = RtcDateTime(__DATE__, __TIME__).Epoch32Time(); + return myTZ.toUTC(t); +} + +// helper function to convert gps date/time into time_t +time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, + uint8_t mm, uint8_t ss) { + tmElements_t tm; + tm.Year = + CalendarYrToTm(YYYY); // year offset from 1970 in time.h + tm.Month = MM; + tm.Day = DD; + tm.Hour = hh; + tm.Minute = mm; + tm.Second = ss; + return makeTime(tm); +} + #if defined HAS_IF482 || defined HAS_DCF77 #if defined HAS_DCF77 && defined HAS_IF482