From 34eb681955ae834b7ef2c8d3ec0d903d66384570 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 20 Feb 2019 23:04:37 +0100 Subject: [PATCH 01/27] get_gpstime(): max time age reduced to 1000 --- src/gpsread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 592488bf..d19f585b 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -93,7 +93,7 @@ time_t get_gpstime(void) { time_t t = 0; // 0 effects calling SyncProvider() to not set time - if ((gps.time.age() < 1500) && (gps.time.isValid())) { + if ((gps.time.age() < 1000) && (gps.time.isValid())) { // get current gps time t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); From 3d26f737be0c4dd03fd8a8222c7348f81b7c0a78 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 21 Feb 2019 23:17:01 +0100 Subject: [PATCH 02/27] 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 From 0c1c95d868a28785402a5e7db234fbcc2b6f8836 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 22 Feb 2019 22:28:35 +0100 Subject: [PATCH 03/27] timemanager fixes --- include/globals.h | 1 - include/timemanager.h | 6 +++- src/cyclic.cpp | 2 +- src/display.cpp | 19 ++++++------ src/gpsread.cpp | 19 +++--------- src/lmic_config.h | 2 +- src/lorawan.cpp | 5 ++-- src/main.cpp | 9 +++--- src/rtctime.cpp | 69 ++++++++++++++++++------------------------- src/timemanager.cpp | 53 +++++++++++++++++++++++++-------- 10 files changed, 95 insertions(+), 90 deletions(-) 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 From 8c93f44c847a6dbb96c593b2ea95c9718c5be76f Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 22 Feb 2019 23:17:28 +0100 Subject: [PATCH 04/27] timemanager fixes #2 --- include/globals.h | 2 +- src/cyclic.cpp | 2 -- src/display.cpp | 27 ++++++++++++++------------- src/main.cpp | 2 +- src/timemanager.cpp | 17 ++++++++--------- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/include/globals.h b/include/globals.h index a3f3bbea..ff3f2028 100644 --- a/include/globals.h +++ b/include/globals.h @@ -114,7 +114,7 @@ extern SemaphoreHandle_t I2Caccess, TimePulse; extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; -extern time_t LastSyncTime; +extern time_t LastSyncTime, userUTCTime; // application includes #include "led.h" diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 8393f2b9..021ea8d6 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -7,8 +7,6 @@ // Local logging tag static const char TAG[] = "main"; -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 9c949e75..ef5fdecc 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -41,16 +41,6 @@ const char lora_datarate[] = {"1211100908078CNA1211109C8C7C"}; const char lora_datarate[] = {"121110090807FSNA"}; #endif -// time display symbols -#if defined HAS_GPS || defined HAS_RTC -const char timeNoPulseSymbol = '?'; -#if defined HAS_IF482 -const char timePulseSymbol = '+'; -#elif defined HAS_DCF77 -const char timePulseSymbol = '*'; -#endif -#endif - // helper arry for converting month values to text const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; @@ -139,8 +129,18 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { + // time display symbols + const char timeNotSetSymbol = '?'; +#if defined HAS_IF482 + const char timeIsSetSymbol = '+'; +#elif defined HAS_DCF77 + const char timeIsSetSymbol = '*'; +#else + const char timeIsSetSymbol = '#'; +#endif + uint8_t msgWaiting; - char timePulse, timeState; + char timeIsSet, timeState; char buff[16]; // 16 chars line buffer time_t t; @@ -227,8 +227,9 @@ void refreshtheDisplay() { u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status t = myTZ.toLocal(now()); - timePulse = TimeIsSynced ? timePulseSymbol : timeNoPulseSymbol; - timeState = TimePulseTick ? timePulse : ' '; + timeIsSet = + (timeStatus() == timeNotSet) ? timeNotSetSymbol : timeIsSetSymbol; + timeState = TimePulseTick ? ' ' : timeIsSet; TimePulseTick = false; u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), timeState, day(t), printmonth[month(t)]); diff --git a/src/main.cpp b/src/main.cpp index 4211de6e..af783420 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,7 +72,7 @@ TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; bool volatile TimePulseTick = false; bool TimeIsSynced = false; -time_t LastSyncTime = 0; +time_t LastSyncTime = 0, userUTCTime = 0; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present diff --git a/src/timemanager.cpp b/src/timemanager.cpp index ee24c2c2..8d559c5b 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -11,16 +11,16 @@ void time_sync() { 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? + 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 +#if defined HAS_LORA && defined TIME_SYNC_LORA + if (!TimeIsSynced) // no GPS sync -> try lora sync + LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); +#endif + } #ifdef HAS_RTC if (TimeIsSynced) { // recalibrate RTC, if we have one @@ -149,8 +149,7 @@ time_t compiledUTC(void) { 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.Year = CalendarYrToTm(YYYY); // year offset from 1970 in time.h tm.Month = MM; tm.Day = DD; tm.Hour = hh; From 011bf0463eff7acb2e249a969cd44a0e77251e78 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 18:12:43 +0100 Subject: [PATCH 05/27] DCF77 code performance improvement --- include/dcf77.h | 14 +++++--- src/dcf77.cpp | 86 +++++++++++++++++---------------------------- src/timemanager.cpp | 9 ++--- 3 files changed, 46 insertions(+), 63 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index 81c8ea97..e8b7266f 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -6,14 +6,18 @@ #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_LENGTH (100) -extern uint8_t DCFpulse[]; +#ifdef DCF77_ACTIVE_LOW +enum dcf_pinstate { dcf_high, dcf_low }; +#else +enum dcf_pinstate { dcf_low, dcf_high }; +#endif enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; -enum dcf_pinstate { dcf_low, dcf_high }; void DCF_Pulse(time_t t); -void IRAM_ATTR DCF77_Frame(time_t t); -void set_DCF77_pin(dcf_pinstate state); -uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); +uint8_t IRAM_ATTR DCF77_Frame(time_t t); +uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, + uint8_t pArray[]); +uint8_t IRAM_ATTR setParityBit(uint8_t p); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 824a0b81..b8ca988e 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -16,15 +16,20 @@ https://github.com/udoklein/dcf77 // Local logging tag static const char TAG[] = "main"; -// array of dcf pulses for one minute -uint8_t DCFpulse[DCF77_FRAME_SIZE + 1]; +// array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so +// we initialize them statically to avoid dumb recalculation every minute +static uint8_t DCFpulse[DCF77_FRAME_SIZE] = { + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, + dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_one}; // triggered by 1 second timepulse to ticker out DCF signal void DCF_Pulse(time_t t) { + TickType_t startTime = xTaskGetTickCount(); uint8_t sec = second(t); - TickType_t startTime = xTaskGetTickCount(); + ESP_LOGD(TAG, "DCF77 sec %d", sec); // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { @@ -33,18 +38,18 @@ void DCF_Pulse(time_t t) { case 0: // start of second -> start of timeframe for logic signal if (DCFpulse[sec] != dcf_off) - set_DCF77_pin(dcf_low); + digitalWrite(HAS_DCF77, dcf_low); else // 59th second reached, nothing more to do return; break; case 1: // 100ms after start of second -> end of timeframe for logic 0 if (DCFpulse[sec] == dcf_zero) - set_DCF77_pin(dcf_high); + digitalWrite(HAS_DCF77, dcf_high); break; case 2: // 200ms after start of second -> end of timeframe for logic 1 - set_DCF77_pin(dcf_high); + digitalWrite(HAS_DCF77, dcf_high); break; case 9: // 900ms after start -> last pulse @@ -52,32 +57,31 @@ void DCF_Pulse(time_t t) { } // switch + // impulse period pause vTaskDelayUntil(&startTime, pdMS_TO_TICKS(DCF77_PULSE_LENGTH)); } // for } // DCF_Pulse() -void IRAM_ATTR DCF77_Frame(time_t tt) { +uint8_t IRAM_ATTR DCF77_Frame(time_t tt) { uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time - // ENCODE HEAD - // secs 0..19 initialized with zeros - for (int n = 0; n <= 19; n++) - DCFpulse[n] = dcf_zero; - // secs 17..18: adjust for DayLightSaving - DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; - // sec 20: must be 1 to indicate time active - DCFpulse[20] = dcf_one; + // ENCODE DST CHANGE ANNOUNCEMENT (Sec 16) + DCFpulse[16] = dcf_zero; // not yet implemented + + // ENCODE DAYLIGHTSAVING (secs 17..18) + DCFpulse[17] = myTZ.locIsDST(t) ? dcf_one : dcf_zero; + DCFpulse[18] = myTZ.locIsDST(t) ? dcf_zero : dcf_one; // ENCODE MINUTE (secs 21..28) Parity = dec2bcd(minute(t), 21, 27, DCFpulse); - DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[28] = setParityBit(Parity); // ENCODE HOUR (secs 29..35) Parity = dec2bcd(hour(t), 29, 34, DCFpulse); - DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[35] = setParityBit(Parity); // ENCODE DATE (secs 36..58) Parity = dec2bcd(day(t), 36, 41, DCFpulse); @@ -85,30 +89,24 @@ void IRAM_ATTR DCF77_Frame(time_t tt) { Parity += dec2bcd(month(t), 45, 49, DCFpulse); Parity += dec2bcd(year(t) - 2000, 50, 57, DCFpulse); // yes, we have a millenium 3000 bug here ;-) - DCFpulse[58] = (Parity & 1) ? dcf_one : dcf_zero; + DCFpulse[58] = setParityBit(Parity); - // ENCODE TAIL (sec 59) - DCFpulse[59] = dcf_off; - // !! missing code here for leap second !! + // ENCODE MARK (sec 59) + DCFpulse[59] = dcf_off; // !! missing code here for leap second !! - // timestamp the frame with minute pointer - DCFpulse[60] = minute(t); + // return the minute for which this frame is generated + return minute(t); - /* - // for debug: print the DCF77 frame buffer - char out[DCF77_FRAME_SIZE + 1]; - uint8_t i; - for (i = 0; i < DCF77_FRAME_SIZE; i++) { - out[i] = DCFpulse[i] + '0'; // convert int digit to printable ascii - } - out[DCF77_FRAME_SIZE] = '\0'; // string termination char - ESP_LOGD(TAG, "DCF minute %d = %s", DCFpulse[DCF77_FRAME_SIZE], out); - */ +} // DCF77_Frame() + +// helper function to encode parity +uint8_t IRAM_ATTR setParityBit(uint8_t p) { + return ((p & 1) ? dcf_one : dcf_zero); } // helper function to convert decimal to bcd digit uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]) { + uint8_t pArray[]) { uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t parity = 0; @@ -122,24 +120,4 @@ uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, return parity; } -// helper function to switch GPIO line with DCF77 signal -void set_DCF77_pin(dcf_pinstate state) { - switch (state) { - case dcf_low: -#ifdef DCF77_ACTIVE_LOW - digitalWrite(HAS_DCF77, HIGH); -#else - digitalWrite(HAS_DCF77, LOW); -#endif - break; - case dcf_high: -#ifdef DCF77_ACTIVE_LOW - digitalWrite(HAS_DCF77, LOW); -#else - digitalWrite(HAS_DCF77, HIGH); -#endif - break; - } // switch -} // DCF77_pulse - #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/timemanager.cpp b/src/timemanager.cpp index 8d559c5b..f78f4127 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -62,7 +62,7 @@ int syncTime(uint32_t t) { // t is UTC time in seconds epoch // helper function to sync moment on timepulse int wait_for_pulse(void) { // sync on top of next second with 1pps timepulse - if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) + if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1010)) == pdTRUE) return 1; // success ESP_LOGD(TAG, "Missing timepulse"); return 0; // failure @@ -190,13 +190,14 @@ void clock_loop(void *pvParameters) { // ClockTask TickType_t wakeTime; time_t t; + uint8_t current_frame; #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())); + current_frame = DCF77_Frame(t1(now())); #endif // output time telegram for second following sec beginning with timepulse @@ -216,9 +217,9 @@ void clock_loop(void *pvParameters) { // ClockTask #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 + current_frame = DCF77_Frame(t1(t)); // generate next frame - if (DCFpulse[DCF77_FRAME_SIZE] == + if (current_frame == minute(t1(t))) // have recent frame? (pulses could be missed!) DCF_Pulse(t2(t)); // then output next second of this frame From 590e4d91f81ce3758ced23884f20dc18d1c11861 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 18:31:47 +0100 Subject: [PATCH 06/27] DCF77 fixes --- include/dcf77.h | 3 ++- src/dcf77.cpp | 8 ++++---- src/timemanager.cpp | 7 +++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index e8b7266f..5e48aa19 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -13,9 +13,10 @@ enum dcf_pinstate { dcf_low, dcf_high }; #endif enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; +extern uint8_t DCFpulse[]; void DCF_Pulse(time_t t); -uint8_t IRAM_ATTR DCF77_Frame(time_t t); +void IRAM_ATTR DCF77_Frame(time_t t); uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); uint8_t IRAM_ATTR setParityBit(uint8_t p); diff --git a/src/dcf77.cpp b/src/dcf77.cpp index b8ca988e..adc4ecca 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -18,7 +18,7 @@ static const char TAG[] = "main"; // array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so // we initialize them statically to avoid dumb recalculation every minute -static uint8_t DCFpulse[DCF77_FRAME_SIZE] = { +uint8_t DCFpulse[DCF77_FRAME_SIZE + 1] = { dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_one}; @@ -63,7 +63,7 @@ void DCF_Pulse(time_t t) { } // for } // DCF_Pulse() -uint8_t IRAM_ATTR DCF77_Frame(time_t tt) { +void IRAM_ATTR DCF77_Frame(time_t tt) { uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time @@ -94,8 +94,8 @@ uint8_t IRAM_ATTR DCF77_Frame(time_t tt) { // ENCODE MARK (sec 59) DCFpulse[59] = dcf_off; // !! missing code here for leap second !! - // return the minute for which this frame is generated - return minute(t); + // timestamp this frame with it's minute + DCFpulse[60] = minute(t); } // DCF77_Frame() diff --git a/src/timemanager.cpp b/src/timemanager.cpp index f78f4127..07459a00 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -190,14 +190,13 @@ void clock_loop(void *pvParameters) { // ClockTask TickType_t wakeTime; time_t t; - uint8_t current_frame; #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 - current_frame = DCF77_Frame(t1(now())); + DCF77_Frame(t1(now())); #endif // output time telegram for second following sec beginning with timepulse @@ -217,9 +216,9 @@ void clock_loop(void *pvParameters) { // ClockTask #elif defined HAS_DCF77 if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? - current_frame = DCF77_Frame(t1(t)); // generate next frame + DCF77_Frame(t1(t)); // generate next frame - if (current_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 From f08a64908604c9b7c94c9a1b01869886fd8d5049 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 20:28:11 +0100 Subject: [PATCH 07/27] DCF77 code sanitization --- include/dcf77.h | 13 +++++---- src/dcf77.cpp | 64 +++++++++++++++++++++------------------------ src/timemanager.cpp | 13 ++++----- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index 5e48aa19..4159d040 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -12,13 +12,12 @@ enum dcf_pinstate { dcf_high, dcf_low }; enum dcf_pinstate { dcf_low, dcf_high }; #endif -enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; -extern uint8_t DCFpulse[]; +enum DCF77_Pulses { dcf_Z, dcf_0, dcf_1 }; -void DCF_Pulse(time_t t); -void IRAM_ATTR DCF77_Frame(time_t t); -uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]); -uint8_t IRAM_ATTR setParityBit(uint8_t p); +void DCF77_Pulse(time_t t, uint8_t const *DCFpulse); +uint8_t *IRAM_ATTR DCF77_Frame(time_t const t); +uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, + uint8_t *DCFpulse); +uint8_t IRAM_ATTR setParityBit(uint8_t const p); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index adc4ecca..be5a1b0b 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -16,35 +16,24 @@ https://github.com/udoklein/dcf77 // Local logging tag static const char TAG[] = "main"; -// array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so -// we initialize them statically to avoid dumb recalculation every minute -uint8_t DCFpulse[DCF77_FRAME_SIZE + 1] = { - dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, - dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, - dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_zero, dcf_one}; - -// triggered by 1 second timepulse to ticker out DCF signal -void DCF_Pulse(time_t t) { +// triggered by second timepulse to ticker out DCF signal +void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { TickType_t startTime = xTaskGetTickCount(); uint8_t sec = second(t); - ESP_LOGD(TAG, "DCF77 sec %d", sec); - // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { switch (pulse) { case 0: // start of second -> start of timeframe for logic signal - if (DCFpulse[sec] != dcf_off) + if (DCFpulse[sec] != dcf_Z) digitalWrite(HAS_DCF77, dcf_low); - else // 59th second reached, nothing more to do - return; break; case 1: // 100ms after start of second -> end of timeframe for logic 0 - if (DCFpulse[sec] == dcf_zero) + if (DCFpulse[sec] == dcf_0) digitalWrite(HAS_DCF77, dcf_high); break; @@ -53,27 +42,35 @@ void DCF_Pulse(time_t t) { break; case 9: // 900ms after start -> last pulse - return; + break; } // switch - // impulse period pause + // pulse pause vTaskDelayUntil(&startTime, pdMS_TO_TICKS(DCF77_PULSE_LENGTH)); } // for -} // DCF_Pulse() +} // DCF77_Pulse() -void IRAM_ATTR DCF77_Frame(time_t tt) { +uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) { + + // array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so + // we keep them statically to avoid same recalculation every minute + + static uint8_t DCFpulse[DCF77_FRAME_SIZE + 1] = { + dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, + dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, + dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_1}; uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time // ENCODE DST CHANGE ANNOUNCEMENT (Sec 16) - DCFpulse[16] = dcf_zero; // not yet implemented + DCFpulse[16] = dcf_0; // not yet implemented // ENCODE DAYLIGHTSAVING (secs 17..18) - DCFpulse[17] = myTZ.locIsDST(t) ? dcf_one : dcf_zero; - DCFpulse[18] = myTZ.locIsDST(t) ? dcf_zero : dcf_one; + DCFpulse[17] = myTZ.locIsDST(t) ? dcf_1 : dcf_0; + DCFpulse[18] = myTZ.locIsDST(t) ? dcf_0 : dcf_1; // ENCODE MINUTE (secs 21..28) Parity = dec2bcd(minute(t), 21, 27, DCFpulse); @@ -87,32 +84,28 @@ void IRAM_ATTR DCF77_Frame(time_t tt) { Parity = dec2bcd(day(t), 36, 41, DCFpulse); Parity += dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFpulse); Parity += dec2bcd(month(t), 45, 49, DCFpulse); - Parity += dec2bcd(year(t) - 2000, 50, 57, - DCFpulse); // yes, we have a millenium 3000 bug here ;-) + Parity += dec2bcd(year(t) - 2000, 50, 57, DCFpulse); DCFpulse[58] = setParityBit(Parity); // ENCODE MARK (sec 59) - DCFpulse[59] = dcf_off; // !! missing code here for leap second !! + DCFpulse[59] = dcf_Z; // !! missing code here for leap second !! // timestamp this frame with it's minute DCFpulse[60] = minute(t); + return DCFpulse; + } // DCF77_Frame() -// helper function to encode parity -uint8_t IRAM_ATTR setParityBit(uint8_t p) { - return ((p & 1) ? dcf_one : dcf_zero); -} - // helper function to convert decimal to bcd digit -uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, - uint8_t pArray[]) { +uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, + uint8_t *DCFpulse) { uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t parity = 0; - for (uint8_t n = startpos; n <= endpos; n++) { - pArray[n] = (data & 1) ? dcf_one : dcf_zero; + for (uint8_t i = startpos; i <= endpos; i++) { + DCFpulse[i] = (data & 1) ? dcf_1 : dcf_0; parity += (data & 1); data >>= 1; } @@ -120,4 +113,7 @@ uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, return parity; } +// helper function to encode parity +uint8_t IRAM_ATTR setParityBit(uint8_t const p) { return ((p & 1) ? dcf_1 : dcf_0); } + #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/timemanager.cpp b/src/timemanager.cpp index 07459a00..f8029b60 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -74,7 +74,7 @@ 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 + // setup external interupt pin for GPS INT output pinMode(GPS_INT, INPUT_PULLDOWN); // setup external rtc 1Hz clock as pulse per second clock ESP_LOGI(TAG, "Timepulse: external (GPS)"); @@ -83,7 +83,7 @@ int timepulse_init() { // 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 + // setup external interupt pin for active low RTC INT output pinMode(RTC_INT, INPUT_PULLUP); // setup external rtc 1Hz clock as pulse per second clock @@ -196,7 +196,8 @@ void clock_loop(void *pvParameters) { // ClockTask // preload first DCF frame before start #ifdef HAS_DCF77 - DCF77_Frame(t1(now())); + uint8_t *DCFpulse; + DCFpulse = DCF77_Frame(t1(now())); #endif // output time telegram for second following sec beginning with timepulse @@ -216,11 +217,11 @@ void clock_loop(void *pvParameters) { // ClockTask #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 + DCFpulse = 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 + minute(t1(t))) // have recent frame? (pulses could be missed!) + DCF77_Pulse(t2(t), DCFpulse); // then output next second of this frame #endif From d0de4ae3bc4f5cd9fb6fd812ffc71e9d1b3d5c42 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 20:38:05 +0100 Subject: [PATCH 08/27] removed debug output from battery.cpp --- src/battery.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battery.cpp b/src/battery.cpp index c7a43070..940e3500 100644 --- a/src/battery.cpp +++ b/src/battery.cpp @@ -48,7 +48,6 @@ uint16_t read_voltage() { #ifdef BATT_FACTOR voltage *= BATT_FACTOR; #endif - ESP_LOGD(TAG, "Raw: %d / Voltage: %dmV", adc_reading, voltage); return voltage; #else return 0; From 60467ca1958d0ea37630f56fb637942e821c49c3 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 21:51:24 +0100 Subject: [PATCH 09/27] timesource display --- include/display.h | 4 +++- include/rtctime.h | 1 + include/timemanager.h | 12 +++++++----- src/display.cpp | 16 +++------------- src/gpsread.cpp | 2 +- src/lorawan.cpp | 5 ++--- src/paxcounter.conf | 2 +- src/timemanager.cpp | 29 ++++++++++++++++++----------- 8 files changed, 36 insertions(+), 35 deletions(-) diff --git a/include/display.h b/include/display.h index 99379b58..8f3227d0 100644 --- a/include/display.h +++ b/include/display.h @@ -4,7 +4,9 @@ #include #include "cyclic.h" -extern uint8_t volatile DisplayState; +extern uint8_t DisplayState; +extern char timeSource; + extern HAS_DISPLAY u8x8; void init_display(const char *Productname, const char *Version); diff --git a/include/rtctime.h b/include/rtctime.h index 18eebf94..a4788c22 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -2,6 +2,7 @@ #define _RTCTIME_H #include "globals.h" +#include "timemanager.h" #include // must be included here so that Arduino library object file references work #include diff --git a/include/timemanager.h b/include/timemanager.h index 16f828fa..648caca2 100644 --- a/include/timemanager.h +++ b/include/timemanager.h @@ -13,18 +13,20 @@ #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); +enum timesources { pps, rtc, lora, unsynced }; + void clock_init(void); void clock_loop(void *pvParameters); void time_sync(void); int wait_for_pulse(void); -int syncTime(time_t); -int syncTime(uint32_t t); +int syncTime(time_t const t, uint8_t const timesource); +int syncTime(uint32_t const t, uint8_t const timesource); void IRAM_ATTR CLOCKIRQ(void); int timepulse_init(void); void timepulse_start(void); -int TimeIsValid(time_t t); +int TimeIsValid(time_t const t); time_t compiledUTC(void); +time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, + uint8_t mm, uint8_t ss); #endif // _timemanager_H \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index ef5fdecc..41a58010 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -45,7 +45,8 @@ const char lora_datarate[] = {"121110090807FSNA"}; const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -uint8_t volatile DisplayState = 0; +uint8_t DisplayState = 0; +char timeSource = '?'; // helper function, prints a hex key on display void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { @@ -129,16 +130,6 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { - // time display symbols - const char timeNotSetSymbol = '?'; -#if defined HAS_IF482 - const char timeIsSetSymbol = '+'; -#elif defined HAS_DCF77 - const char timeIsSetSymbol = '*'; -#else - const char timeIsSetSymbol = '#'; -#endif - uint8_t msgWaiting; char timeIsSet, timeState; char buff[16]; // 16 chars line buffer @@ -227,8 +218,7 @@ void refreshtheDisplay() { u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status t = myTZ.toLocal(now()); - timeIsSet = - (timeStatus() == timeNotSet) ? timeNotSetSymbol : timeIsSetSymbol; + timeIsSet = (timeStatus() == timeNotSet) ? '#' : timeSource; timeState = TimePulseTick ? ' ' : timeIsSet; TimePulseTick = false; u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), diff --git a/src/gpsread.cpp b/src/gpsread.cpp index d8a397c2..a1ddadc3 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -86,7 +86,7 @@ time_t get_gpstime(void) { gps.time.isValid() ? "yes" : "no"); // use recent gps time - t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), + t = tmConvert(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(), diff --git a/src/lorawan.cpp b/src/lorawan.cpp index f94c60d0..69cd6f34 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -460,7 +460,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, // Update userUTCTime, considering the difference between the GPS and UTC // time, and the leap seconds - // !!! DANGER !!! This code will expire in the year when next leap second happenes + // !!! DANGER !!! This code will expire in next year with leap second *pUserUTCTime = lmicTimeReference.tNetwork + 315964800; // Current time, in ticks ostime_t ticksNow = os_getTime(); @@ -472,12 +472,11 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (syncTime(*pUserUTCTime)) { // do we have a valid time? + if (syncTime(*pUserUTCTime, lora)) { // do we have a valid time? #ifdef HAS_RTC if (TimeIsSynced) set_rtctime(now()); // UTC time #endif - 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"); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index ea741ae5..e1f48b01 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -84,7 +84,7 @@ // settings for syncing time of node and external time sources #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] +#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/timemanager.cpp b/src/timemanager.cpp index f8029b60..d2b6b99b 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -14,11 +14,12 @@ void time_sync() { 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 + if (syncTime(get_gpstime(), pps)) // attempt sync with GPS time #endif #if defined HAS_LORA && defined TIME_SYNC_LORA - if (!TimeIsSynced) // no GPS sync -> try lora sync - LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); + if (!TimeIsSynced) // no GPS sync -> try lora sync + LMIC_requestNetworkTime(user_request_network_time_callback, + &userUTCTime); #endif } @@ -28,8 +29,8 @@ void time_sync() { } else { // we switch to fallback time after a while if ((lastTimeSync >= (TIME_SYNC_TIMEOUT * 60000)) || !LastSyncTime) { // sync is still due -> use RTC as fallback source - syncTime(get_rtctime()); // sync with RTC time - TimeIsSynced = false; + if (syncTime(get_rtctime(), rtc)) // sync with RTC time + TimeIsSynced = false; } } #endif @@ -38,25 +39,31 @@ void time_sync() { } // time_sync() // helper function to sync time on start of next second -int syncTime(time_t t) { +int syncTime(time_t const t, uint8_t const timesource) { + + // symbol to display current time source + const char timeSetSymbols[] = {'G', 'R', 'L', '~' }; + 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 + timeSource = timeSetSymbols[timesource]; ESP_LOGD(TAG, "Time was set to %02d:%02d:%02d", hour(t), minute(t), second(t)); return 1; // success } else { ESP_LOGD(TAG, "Time sync attempt failed"); + timeSource = timeSetSymbols[unsynced]; TimeIsSynced = false; return 0; } // failure } -int syncTime(uint32_t t) { // t is UTC time in seconds epoch - return syncTime(static_cast(t)); +int syncTime(uint32_t const t, uint8_t const timesource) { // t is UTC time in seconds epoch + return syncTime(static_cast(t), timesource); } // helper function to sync moment on timepulse @@ -132,7 +139,7 @@ void IRAM_ATTR CLOCKIRQ(void) { } // helper function to check plausibility of a time -int TimeIsValid(time_t t) { +int TimeIsValid(time_t const 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"); @@ -146,7 +153,7 @@ time_t compiledUTC(void) { } // 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, +time_t tmConvert(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 @@ -217,7 +224,7 @@ void clock_loop(void *pvParameters) { // ClockTask #elif defined HAS_DCF77 if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? - DCFpulse = DCF77_Frame(t1(t)); // generate next frame + DCFpulse = DCF77_Frame(t1(t)); // generate next frame if (DCFpulse[DCF77_FRAME_SIZE] == minute(t1(t))) // have recent frame? (pulses could be missed!) From feda8dd938e14e5fd98a59a5e8927f1d73422aa8 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 23 Feb 2019 23:39:45 +0100 Subject: [PATCH 10/27] timemanager fixes --- include/globals.h | 2 +- include/timemanager.h | 1 - src/if482.cpp | 26 ++++++++++++++----------- src/lorawan.cpp | 6 +++--- src/main.cpp | 2 +- src/timemanager.cpp | 45 ++++++++++++++++++++++--------------------- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/include/globals.h b/include/globals.h index ff3f2028..17c485c2 100644 --- a/include/globals.h +++ b/include/globals.h @@ -114,7 +114,7 @@ extern SemaphoreHandle_t I2Caccess, TimePulse; extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; -extern time_t LastSyncTime, userUTCTime; +extern time_t lastSyncTime, userUTCTime; // application includes #include "led.h" diff --git a/include/timemanager.h b/include/timemanager.h index 648caca2..16e7276a 100644 --- a/include/timemanager.h +++ b/include/timemanager.h @@ -20,7 +20,6 @@ void clock_loop(void *pvParameters); void time_sync(void); int wait_for_pulse(void); int syncTime(time_t const t, uint8_t const timesource); -int syncTime(uint32_t const t, uint8_t const timesource); void IRAM_ATTR CLOCKIRQ(void); int timepulse_init(void); void timepulse_start(void); diff --git a/src/if482.cpp b/src/if482.cpp index 54b9ae5d..9f21f7ac 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -89,16 +89,19 @@ HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) // triggered by timepulse to ticker out DCF signal void IF482_Pulse(time_t t) { + static const TickType_t txDelay = + pdMS_TO_TICKS(IF482_PULSE_LENGTH) - tx_Ticks(HAS_IF482); + TickType_t startTime = xTaskGetTickCount(); - static const TickType_t txDelay = pdMS_TO_TICKS(IF482_PULSE_LENGTH) - tx_Ticks(HAS_IF482); - vTaskDelayUntil(&startTime, txDelay); - IF482.print(IF482_Frame(t+1)); // note: if482 telegram for *next* second + + vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire + IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second } String IRAM_ATTR IF482_Frame(time_t startTime) { time_t t = myTZ.toLocal(startTime); - char mon, buf[14], out[IF482_FRAME_SIZE]; + char mon, out[IF482_FRAME_SIZE]; switch (timeStatus()) { // indicates if time has been set and recently synced case timeSet: // time is set and is synced @@ -113,9 +116,10 @@ String IRAM_ATTR IF482_Frame(time_t startTime) { } // switch // generate IF482 telegram - snprintf(buf, sizeof(buf), "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000, - month(t), day(t), weekday(t), hour(t), minute(t), second(t)); - snprintf(out, sizeof(out), "O%cL%s\r", mon, buf); + snprintf(out, sizeof(out), "O%cL%02u%02u%02u%1u%02u%02u%02u\r", mon, + year(t) - 2000, month(t), day(t), weekday(t), hour(t), minute(t), + second(t)); + ESP_LOGD(TAG, "IF482 = %s", out); return out; } @@ -124,13 +128,13 @@ String IRAM_ATTR IF482_Frame(time_t startTime) { TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPins) { - uint32_t datenbits = ((config & 0x0c) >> 2) + 5; + uint32_t databits = ((config & 0x0c) >> 2) + 5; uint32_t stopbits = ((config & 0x20) >> 5) + 1; - uint32_t tx_delay = - (2 + datenbits + stopbits) * IF482_FRAME_SIZE * 1000.0 / baud; + uint32_t txTime = + (databits + stopbits + 2) * IF482_FRAME_SIZE * 1000.0 / baud; // +2 ms margin for the startbit and the clock's processing time - return pdMS_TO_TICKS(round(tx_delay)); + return pdMS_TO_TICKS(round(txTime)); } #endif // HAS_IF482 \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 69cd6f34..40f2d1e8 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -443,7 +443,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, int flagSuccess) { #ifdef HAS_LORA // Explicit conversion from void* to uint32_t* to avoid compiler errors - uint32_t *pUserUTCTime = (uint32_t *)pVoidUserUTCTime; + time_t *pUserUTCTime = (time_t *)pVoidUserUTCTime; lmic_time_reference_t lmicTimeReference; if (flagSuccess != 1) { @@ -468,11 +468,11 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, ostime_t ticksRequestSent = lmicTimeReference.tLocal; // Add the delay between the instant the time was transmitted and // the current time - uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000; + time_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000; *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (syncTime(*pUserUTCTime, lora)) { // do we have a valid time? + if (syncTime(*pUserUTCTime, lora)) { // have we got a valid time? #ifdef HAS_RTC if (TimeIsSynced) set_rtctime(now()); // UTC time diff --git a/src/main.cpp b/src/main.cpp index af783420..e4056af0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,7 +72,7 @@ TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; bool volatile TimePulseTick = false; bool TimeIsSynced = false; -time_t LastSyncTime = 0, userUTCTime = 0; +time_t lastSyncTime = 0, userUTCTime = 0; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present diff --git a/src/timemanager.cpp b/src/timemanager.cpp index d2b6b99b..47f1d7c2 100644 --- a/src/timemanager.cpp +++ b/src/timemanager.cpp @@ -9,17 +9,21 @@ void time_sync() { #ifdef TIME_SYNC_INTERVAL - time_t lastTimeSync = now() - LastSyncTime; // check if a sync is due + static time_t ageOfTime = 0; + + ageOfTime = now() - lastSyncTime; // check if a sync is due + + // is it time to sync with external source or did we never sync yet? + if ((ageOfTime >= (TIME_SYNC_INTERVAL * 60000)) || !lastSyncTime) { - if ((lastTimeSync >= (TIME_SYNC_INTERVAL * 60000)) || !LastSyncTime) { - // is it time to sync with external source? #ifdef HAS_GPS - if (syncTime(get_gpstime(), pps)) // attempt sync with GPS time + syncTime(get_gpstime(), pps); // attempt sync with GPS time #endif + #if defined HAS_LORA && defined TIME_SYNC_LORA - if (!TimeIsSynced) // no GPS sync -> try lora sync - LMIC_requestNetworkTime(user_request_network_time_callback, - &userUTCTime); + if (!TimeIsSynced) // no GPS sync -> try lora sync + LMIC_requestNetworkTime(user_request_network_time_callback, + &userUTCTime); #endif } @@ -27,10 +31,11 @@ void time_sync() { 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 is still due -> use RTC as fallback source - if (syncTime(get_rtctime(), rtc)) // sync with RTC time - TimeIsSynced = false; + if ((ageOfTime >= (TIME_SYNC_TIMEOUT * 60000)) || + !lastSyncTime) { // sync is still due -> use RTC as fallback source + if (!syncTime(get_rtctime(), rtc)) // sync with RTC time + ESP_LOGW(TAG, "no valid time"); + TimeIsSynced = false; } } #endif @@ -42,30 +47,26 @@ void time_sync() { int syncTime(time_t const t, uint8_t const timesource) { // symbol to display current time source - const char timeSetSymbols[] = {'G', 'R', 'L', '~' }; + const char timeSetSymbols[] = {'G', 'R', 'L', '~'}; 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 + lastSyncTime = now(); // store time of this sync timeSource = timeSetSymbols[timesource]; - ESP_LOGD(TAG, "Time was set to %02d:%02d:%02d", hour(t), minute(t), - second(t)); + ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSource, + hour(t), minute(t), second(t)); return 1; // success } else { - ESP_LOGD(TAG, "Time sync attempt failed"); timeSource = timeSetSymbols[unsynced]; TimeIsSynced = false; + ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSource); return 0; } // failure } -int syncTime(uint32_t const t, uint8_t const timesource) { // t is UTC time in seconds epoch - return syncTime(static_cast(t), timesource); -} - // helper function to sync moment on timepulse int wait_for_pulse(void) { // sync on top of next second with 1pps timepulse @@ -153,8 +154,8 @@ time_t compiledUTC(void) { } // helper function to convert gps date/time into time_t -time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, - uint8_t mm, uint8_t ss) { +time_t tmConvert(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; From 98797c0fe1142de55d25dab167df4788b15001dd Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 01:44:55 +0100 Subject: [PATCH 11/27] timemanager reworked --- include/cyclic.h | 2 +- include/globals.h | 3 +- include/lorawan.h | 2 +- include/main.h | 2 +- include/rtctime.h | 2 +- include/{timemanager.h => timekeeper.h} | 16 +++--- src/display.cpp | 6 +- src/lorawan.cpp | 4 -- src/main.cpp | 18 +++--- src/paxcounter.conf | 7 +-- src/rtctime.cpp | 16 +++--- src/{timemanager.cpp => timekeeper.cpp} | 75 +++++++++++-------------- 12 files changed, 68 insertions(+), 85 deletions(-) rename include/{timemanager.h => timekeeper.h} (69%) rename src/{timemanager.cpp => timekeeper.cpp} (78%) diff --git a/include/cyclic.h b/include/cyclic.h index 7e80637f..89ccb240 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -5,7 +5,7 @@ #include "senddata.h" #include "rcommand.h" #include "spislave.h" -#include "timemanager.h" +#include "timekeeper.h" #include #ifdef HAS_BME diff --git a/include/globals.h b/include/globals.h index 17c485c2..349e61bd 100644 --- a/include/globals.h +++ b/include/globals.h @@ -108,13 +108,12 @@ 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; // 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, userUTCTime; +extern time_t userUTCTime; // application includes #include "led.h" diff --git a/include/lorawan.h b/include/lorawan.h index 30620dc5..76a57397 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -3,6 +3,7 @@ #include "globals.h" #include "rcommand.h" +#include "timekeeper.h" // LMIC-Arduino LoRaWAN Stack #include @@ -10,7 +11,6 @@ #include #include #include "loraconf.h" -#include "rtctime.h" // Needed for 24AA02E64, does not hurt anything if included and not used #ifdef MCP_24AA02E64_I2C_ADDRESS diff --git a/include/main.h b/include/main.h index 44f2279d..b60ab7b9 100644 --- a/include/main.h +++ b/include/main.h @@ -17,5 +17,5 @@ #include "led.h" #include "spislave.h" #include "lorawan.h" -#include "timemanager.h" +#include "timekeeper.h" #endif \ No newline at end of file diff --git a/include/rtctime.h b/include/rtctime.h index a4788c22..e9c58b27 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -2,7 +2,7 @@ #define _RTCTIME_H #include "globals.h" -#include "timemanager.h" +#include "timekeeper.h" #include // must be included here so that Arduino library object file references work #include diff --git a/include/timemanager.h b/include/timekeeper.h similarity index 69% rename from include/timemanager.h rename to include/timekeeper.h index 16e7276a..d627e10d 100644 --- a/include/timemanager.h +++ b/include/timekeeper.h @@ -1,5 +1,5 @@ -#ifndef _timemanager_H -#define _timemanager_H +#ifndef _timekeeper_H +#define _timekeeper_H #include "globals.h" #include "rtctime.h" @@ -15,17 +15,17 @@ enum timesources { pps, rtc, lora, unsynced }; +void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); void clock_loop(void *pvParameters); void time_sync(void); -int wait_for_pulse(void); -int syncTime(time_t const t, uint8_t const timesource); -void IRAM_ATTR CLOCKIRQ(void); -int timepulse_init(void); void timepulse_start(void); -int TimeIsValid(time_t const t); +uint8_t wait_for_pulse(void); +uint8_t syncTime(time_t const t, uint8_t const caller); +uint8_t timepulse_init(void); +uint8_t TimeIsValid(time_t const t); time_t compiledUTC(void); time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss); -#endif // _timemanager_H \ No newline at end of file +#endif // _timekeeper_H \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index 41a58010..54e6f1ad 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -131,8 +131,7 @@ void init_display(const char *Productname, const char *Version) { void refreshtheDisplay() { uint8_t msgWaiting; - char timeIsSet, timeState; - char buff[16]; // 16 chars line buffer + char timeState, buff[16]; time_t t; // block i2c bus access @@ -218,8 +217,7 @@ void refreshtheDisplay() { u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status t = myTZ.toLocal(now()); - timeIsSet = (timeStatus() == timeNotSet) ? '#' : timeSource; - timeState = TimePulseTick ? ' ' : timeIsSet; + timeState = TimePulseTick ? ' ' : timeSource; TimePulseTick = false; u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), timeState, day(t), printmonth[month(t)]); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 40f2d1e8..7c4ead8e 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -473,10 +473,6 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, // Update system time with time read from the network if (syncTime(*pUserUTCTime, lora)) { // have we got a valid time? -#ifdef HAS_RTC - if (TimeIsSynced) - set_rtctime(now()); // UTC time -#endif ESP_LOGI(TAG, "LORA has set the system time"); } else ESP_LOGI(TAG, "Unable to sync system time with LORA"); diff --git a/src/main.cpp b/src/main.cpp index e4056af0..3bbac73f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,8 +71,7 @@ hw_timer_t *sendCycle = NULL, *homeCycle = NULL, *clockCycle = NULL, TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; bool volatile TimePulseTick = false; -bool TimeIsSynced = false; -time_t lastSyncTime = 0, userUTCTime = 0; +time_t userUTCTime = 0; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present @@ -96,7 +95,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 @@ -358,13 +357,12 @@ void setup() { #endif #endif - // start pps timepulse - ESP_LOGI(TAG, "Starting Timepulse..."); - if (timepulse_init()) // setup timepulse - timepulse_start(); // start pulse - else - ESP_LOGE(TAG, "No timepulse, time will not be synced!"); - time_sync(); + // start pps timepulse and timekeepr + ESP_LOGI(TAG, "Starting Timekeeper..."); + assert(timepulse_init()); // setup timepulse + timepulse_start(); + time_sync(); // sync time + setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index e1f48b01..0579e8b5 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -81,10 +81,9 @@ #define OTA_MIN_BATT 3600 // minimum battery level for OTA [millivolt] #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 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] +// settings for syncing time of node with external time source +#define TIME_SYNC_INTERVAL 10 // sync time each .. minutes from time source (GPS/LORA) [default = 10], comment out means off +#define TIME_SYNC_LORA 1 // use LORA network as time source, 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 a3bf3fcf..f626e390 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -24,14 +24,16 @@ int rtc_init(void) { Rtc.SetIsRunning(true); } - RtcDateTime tt = Rtc.GetDateTime(); - time_t t = tt.Epoch32Time(); // sec2000 -> epoch + /* + RtcDateTime tt = Rtc.GetDateTime(); + time_t t = tt.Epoch32Time(); // sec2000 -> epoch - 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 - } + 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 + } + */ I2C_MUTEX_UNLOCK(); // release i2c bus access ESP_LOGI(TAG, "RTC initialized"); diff --git a/src/timemanager.cpp b/src/timekeeper.cpp similarity index 78% rename from src/timemanager.cpp rename to src/timekeeper.cpp index 47f1d7c2..6a1c46ec 100644 --- a/src/timemanager.cpp +++ b/src/timekeeper.cpp @@ -1,4 +1,4 @@ -#include "timemanager.h" +#include "timekeeper.h" // Local logging tag static const char TAG[] = "main"; @@ -9,66 +9,57 @@ void time_sync() { #ifdef TIME_SYNC_INTERVAL - static time_t ageOfTime = 0; - - ageOfTime = now() - lastSyncTime; // check if a sync is due - - // is it time to sync with external source or did we never sync yet? - if ((ageOfTime >= (TIME_SYNC_INTERVAL * 60000)) || !lastSyncTime) { + if (timeStatus() == timeSet) + return; #ifdef HAS_GPS - syncTime(get_gpstime(), pps); // attempt sync with GPS time + if (syncTime(get_gpstime(), pps)) + return; // attempt sync with GPS time #endif -#if defined HAS_LORA && defined TIME_SYNC_LORA - if (!TimeIsSynced) // no GPS sync -> try lora sync - LMIC_requestNetworkTime(user_request_network_time_callback, - &userUTCTime); -#endif - } - +// no GPS -> fallback to RTC time #ifdef HAS_RTC - if (TimeIsSynced) { // recalibrate RTC, if we have one - set_rtctime(now()); - } else { // we switch to fallback time after a while - if ((ageOfTime >= (TIME_SYNC_TIMEOUT * 60000)) || - !lastSyncTime) { // sync is still due -> use RTC as fallback source - if (!syncTime(get_rtctime(), rtc)) // sync with RTC time - ESP_LOGW(TAG, "no valid time"); - TimeIsSynced = false; - } - } + if (!syncTime(get_rtctime(), rtc)) // sync with RTC time + ESP_LOGW(TAG, "no confident RTC time"); +#endif + +// try lora sync if we have +#if defined HAS_LORA && defined TIME_SYNC_LORA + LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif #endif // TIME_SYNC_INTERVAL } // time_sync() // helper function to sync time on start of next second -int syncTime(time_t const t, uint8_t const timesource) { +uint8_t syncTime(time_t const t, uint8_t const caller) { // symbol to display current time source - const char timeSetSymbols[] = {'G', 'R', 'L', '~'}; + const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; if (TimeIsValid(t)) { - TimeIsSynced = wait_for_pulse(); // wait for next 1pps timepulse + uint8_t const TimeIsPulseSynced = + wait_for_pulse(); // wait for next 1pps timepulse setTime(t); - adjustTime(1); // forward time to next second - lastSyncTime = now(); // store time of this sync - timeSource = timeSetSymbols[timesource]; + adjustTime(1); // forward time to next second + timeSource = timeSetSymbols[caller]; ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSource, hour(t), minute(t), second(t)); +#ifdef HAS_RTC + if ((TimeIsPulseSynced) && (caller != rtc)) + set_rtctime(now()); +#endif return 1; // success + } else { + ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); timeSource = timeSetSymbols[unsynced]; - TimeIsSynced = false; - ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSource); - return 0; + return 0; // failure } - // failure -} +} // syncTime() // helper function to sync moment on timepulse -int wait_for_pulse(void) { +uint8_t wait_for_pulse(void) { // sync on top of next second with 1pps timepulse if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1010)) == pdTRUE) return 1; // success @@ -77,7 +68,7 @@ int wait_for_pulse(void) { } // helper function to setup a pulse per second for time synchronisation -int timepulse_init() { +uint8_t timepulse_init() { // use time pulse from GPS as time base with fixed 1Hz frequency #ifdef GPS_INT @@ -140,7 +131,7 @@ void IRAM_ATTR CLOCKIRQ(void) { } // helper function to check plausibility of a time -int TimeIsValid(time_t const t) { +uint8_t TimeIsValid(time_t const 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"); @@ -202,9 +193,9 @@ void clock_loop(void *pvParameters) { // ClockTask #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 + // preload first DCF frame before start #ifdef HAS_DCF77 - uint8_t *DCFpulse; + uint8_t *DCFpulse; // pointer on array with DCF pulse bits DCFpulse = DCF77_Frame(t1(now())); #endif @@ -213,7 +204,7 @@ void clock_loop(void *pvParameters) { // ClockTask xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, portMAX_DELAY); // wait for timepulse - if (timeStatus() == timeNotSet) // do we have valid time? + if (timeStatus() != timeSet) // no confident time -> no output to clock continue; t = now(); // payload to send to clock From 7dd82eb5eeea4610bc044fa41ed740305c3bccff Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 13:35:22 +0100 Subject: [PATCH 12/27] Cayenne LPP2 protocol enhancements --- include/payload.h | 1 + src/paxcounter.conf | 54 ++++++++++++++++++++++++++------------------- src/payload.cpp | 37 +++++++++++++++++++++++++++++-- src/senddata.cpp | 23 ++++++++++++++----- 4 files changed, 84 insertions(+), 31 deletions(-) diff --git a/include/payload.h b/include/payload.h index 686a5b62..d8fe468b 100644 --- a/include/payload.h +++ b/include/payload.h @@ -49,6 +49,7 @@ public: void addBME(bmeStatus_t value); void addButton(uint8_t value); void addSensor(uint8_t[]); + void addTime(time_t value); #if PAYLOAD_ENCODER == 1 // format plain diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 0579e8b5..ac0a22b7 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -11,7 +11,7 @@ // Payload send cycle and encoding #define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec. -#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=CayenneLPP dynamic, 4=CayenneLPP packed +#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed // Set this to include BLE counting and vendor filter functions #define VENDORFILTER 1 // comment out if you want to count things, not people @@ -49,28 +49,12 @@ #define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy #define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue [1 = no queue] -// Ports on which the device sends and listenes on LoRaWAN and SPI -#define COUNTERPORT 1 // Port on which device sends counts -#define RCMDPORT 2 // Port on which device listenes for remote commands -#define STATUSPORT 2 // Port on which device sends remote command results -#define CONFIGPORT 3 // Port on which device sends config query results -#define GPSPORT 4 // Port on which device sends gps data -#define BUTTONPORT 5 // Port on which device sends button pressed signal -#define LPP1PORT 1 // Port for Cayenne LPP 1.0 dynamic sensor encoding -#define LPP2PORT 2 // Port for Cayenne LPP 2.0 packed sensor encoding -#define BEACONPORT 6 // Port on which device sends beacon alarms -#define BMEPORT 7 // Port on which device sends BME680 sensor data -#define BATTPORT 8 // Port on which device sends battery voltage data -#define SENSOR1PORT 10 // Port on which device sends User sensor #1 data -#define SENSOR2PORT 11 // Port on which device sends User sensor #2 data -#define SENSOR3PORT 12 // Port on which device sends User sensor #3 data - -// Some hardware settings +// Hardware settings #define RGBLUMINOSITY 30 // RGB LED luminosity [default = 30%] #define DISPLAYREFRESH_MS 40 // OLED refresh cycle in ms [default = 40] -> 1000/40 = 25 frames per second #define HOMECYCLE 30 // house keeping cycle in seconds [default = 30 secs] -// Settings for BME680 environmental sensor (if present) +// Settings for BME680 environmental sensor #define BME_TEMP_OFFSET 5.0f // Offset sensor on chip temp <-> ambient temp [default = 5°C] #define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // update every 360 minutes = 4 times a day @@ -82,12 +66,36 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node with external time source -#define TIME_SYNC_INTERVAL 10 // sync time each .. minutes from time source (GPS/LORA) [default = 10], comment out means off -#define TIME_SYNC_LORA 1 // use LORA network as time source, comment out means off [default = off] +#define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA), comment out means [default = 5] +#define TIME_SYNC_LORA 1 // use LORA network as time source, 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 #define STANDARD_TIME {"CET ", Last, Sun, Oct, 3, 60} // Central European Standard Time -// LMIC settings -// moved to src/lmic_config.h \ No newline at end of file +// Ports on which the device sends and listenes on LoRaWAN and SPI +#define COUNTERPORT 1 // counts +#define RCMDPORT 2 // remote commands +#define STATUSPORT 2 // remote command results +#define CONFIGPORT 3 // config query results +#define GPSPORT 4 // gps +#define BUTTONPORT 5 // button pressed signal +#define BEACONPORT 6 // beacon alarms +#define BMEPORT 7 // BME680 sensor +#define BATTPORT 8 // battery voltage +#define TIMEPORT 9 // time +#define SENSOR1PORT 10 // user sensor #1 +#define SENSOR2PORT 11 // user sensor #2 +#define SENSOR3PORT 12 // user sensor #3 + +// Cayenne LPP Ports, see https://community.mydevices.com/t/cayenne-lpp-2-0/7510 +#define CAYENNE_LPP1 1 // dynamic sensor payload (LPP 1.0) +#define CAYENNE_LPP2 2 // packed sensor payload (LPP 2.0) +#define CAYENNE_GPS 3 // full scale GPS payload +#define CAYENNE_ACTUATOR 10 // actuator commands +#define CAYENNE_DEVICECONFIG 11 // device period configuration +#define CAYENNE_SENSORREAD 13 // sensor period configuration +#define CAYENNE_SENSORENABLE 14 // sensor enable configuration + +// LMIC settings +// -> in src/lmic_config.h \ No newline at end of file diff --git a/src/payload.cpp b/src/payload.cpp index eebe88d7..b94c07f3 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -126,6 +126,14 @@ void PayloadConvert::addButton(uint8_t value) { #endif } +void PayloadConvert::addTime(time_t value) { + uint32_t time = (uint32_t)value; + buffer[cursor++] = (byte)((time & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((time & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((time & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((time & 0x000000FF)); +} + /* ---------------- packed format with LoRa serialization Encoder ---------- */ // derived from @@ -133,7 +141,9 @@ void PayloadConvert::addButton(uint8_t value) { #elif PAYLOAD_ENCODER == 2 -void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { writeUint16(value); } +void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { + writeUint16(value); +} void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { writeUint8(rssi); @@ -208,6 +218,11 @@ void PayloadConvert::addButton(uint8_t value) { #endif } +void PayloadConvert::addTime(time_t value) { + uint32_t time = (uint32_t)value; + writeUint32(time); +} + void PayloadConvert::intToBytes(uint8_t pos, int32_t i, uint8_t byteSize) { for (uint8_t x = 0; x < byteSize; x++) { buffer[x + pos] = (byte)(i >> (x * 8)); @@ -282,7 +297,7 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, #elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4) void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { - switch(snifftype) { + switch (snifftype) { case MAC_SNIFF_WIFI: #if (PAYLOAD_ENCODER == 3) buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL; @@ -436,6 +451,24 @@ void PayloadConvert::addButton(uint8_t value) { #endif // HAS_BUTTON } +void PayloadConvert::addTime(time_t value) { +#if (PAYLOAD_ENCODER == 4) + uint32_t t = (uint32_t)value; + uint32_t tx_period = (uint32_t)SEND_SECS * 2; + buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod + // UTCTime in seconds + buffer[cursor++] = (byte)((t & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((t & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((t & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((t & 0x000000FF)); + // TXPeriod in seconds + buffer[cursor++] = (byte)((tx_period & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((tx_period & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((tx_period & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((tx_period & 0x000000FF)); +#endif +} + #else #error No valid payload converter defined! #endif \ No newline at end of file diff --git a/src/senddata.cpp b/src/senddata.cpp index c13d907f..386ab6ab 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -8,15 +8,26 @@ void SendPayload(uint8_t port, sendprio_t prio) { SendBuffer.MessageSize = payload.getSize(); switch (PAYLOAD_ENCODER) { - case 1: - case 2: + case 1: // plain -> no mapping + case 2: // packed -> no mapping SendBuffer.MessagePort = port; break; - case 3: - SendBuffer.MessagePort = LPP1PORT; + case 3: // Cayenne LPP dynamic -> all payload goes out on same port + SendBuffer.MessagePort = CAYENNE_LPP1; break; - case 4: - SendBuffer.MessagePort = LPP2PORT; + case 4: // Cayenne LPP packed -> we need to map some paxcounter ports + SendBuffer.MessagePort = CAYENNE_LPP2; + switch (SendBuffer.MessagePort) { + case COUNTERPORT: + SendBuffer.MessagePort = CAYENNE_LPP2; + break; + case RCMDPORT: + SendBuffer.MessagePort = CAYENNE_ACTUATOR; + break; + case TIMEPORT: + SendBuffer.MessagePort = CAYENNE_DEVICECONFIG; + break; + } break; default: SendBuffer.MessagePort = port; From c02b8bc7514865f9fec231753dcdb683df520023 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 13:35:40 +0100 Subject: [PATCH 13/27] rcommand get time --- src/rcommand.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 038db7db..397c8e1f 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -68,7 +68,7 @@ void set_sendcycle(uint8_t val[]) { void set_wifichancycle(uint8_t val[]) { cfg.wifichancycle = val[0]; // update Wifi channel rotation timer period - xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100 ); + xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100); ESP_LOGI(TAG, "Remote command: set Wifi channel switch interval to %.1f seconds", @@ -244,9 +244,8 @@ void get_status(uint8_t val[]) { uint16_t voltage = 0; #endif payload.reset(); - payload.addStatus(voltage, uptime() / 1000, temperatureRead(), - getFreeRAM(), rtc_get_reset_reason(0), - rtc_get_reset_reason(1)); + payload.addStatus(voltage, uptime() / 1000, temperatureRead(), getFreeRAM(), + rtc_get_reset_reason(0), rtc_get_reset_reason(1)); SendPayload(STATUSPORT, prio_high); }; @@ -273,6 +272,17 @@ void get_bme(uint8_t val[]) { #endif }; +void get_time(uint8_t val[]) { + ESP_LOGI(TAG, "Remote command: get time"); +#ifdef HAS_BME + payload.reset(); + payload.addtime(now()); + SendPayload(TIMEPORT, prio_high); +#else + ESP_LOGW(TAG, "BME680 sensor not supported"); +#endif +}; + // assign previously defined functions to set of numeric remote commands // format: opcode, function, #bytes params, // flag (true = do make settings persistent / false = don't) @@ -289,7 +299,7 @@ cmd_t table[] = { {0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false}, {0x13, set_sensor, 2, true}, {0x80, get_config, 0, false}, {0x81, get_status, 0, false}, {0x84, get_gps, 0, false}, - {0x85, get_bme, 0, false}, + {0x85, get_bme, 0, false}, {0x86, get_time, 0, false}, }; const uint8_t cmdtablesize = From 171207af22a3c74ef18b1af8456eb0c5dc56bf03 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 13:47:18 +0100 Subject: [PATCH 14/27] timekeeper code sanitizations --- include/rtctime.h | 5 ++--- include/timekeeper.h | 2 +- src/lorawan.cpp | 2 +- src/main.cpp | 4 +++- src/rtctime.cpp | 10 +++------- src/timekeeper.cpp | 14 +++++++------- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/rtctime.h b/include/rtctime.h index e9c58b27..3429d44e 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -8,9 +8,8 @@ extern RtcDS3231 Rtc; // make RTC instance globally available -int rtc_init(void); -int set_rtctime(uint32_t t); -int set_rtctime(time_t t); +uint8_t rtc_init(void); +uint8_t set_rtctime(time_t t); void sync_rtctime(void); time_t get_rtctime(void); float get_rtctemp(void); diff --git a/include/timekeeper.h b/include/timekeeper.h index d627e10d..176aec83 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -13,7 +13,7 @@ #include "dcf77.h" #endif -enum timesources { pps, rtc, lora, unsynced }; +enum timesources { _gps, _rtc, _lora, _unsynced }; void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 7c4ead8e..7c0cee66 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -472,7 +472,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (syncTime(*pUserUTCTime, lora)) { // have we got a valid time? + if (syncTime(*pUserUTCTime, _lora)) { // have we got a valid time? ESP_LOGI(TAG, "LORA has set the system time"); } else ESP_LOGI(TAG, "Unable to sync system time with LORA"); diff --git a/src/main.cpp b/src/main.cpp index 3bbac73f..daadf5b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -361,8 +361,10 @@ void setup() { ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse timepulse_start(); - time_sync(); // sync time + time_sync(); // sync time +#ifdef TIME_SYNC_INTERVAL setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() +#endif // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); diff --git a/src/rtctime.cpp b/src/rtctime.cpp index f626e390..5b7952e3 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -8,7 +8,7 @@ static const char TAG[] = "main"; RtcDS3231 Rtc(Wire); // RTC hardware i2c interface // initialize RTC -int rtc_init(void) { +uint8_t rtc_init(void) { if (I2C_MUTEX_LOCK()) { // block i2c bus access @@ -24,6 +24,7 @@ int rtc_init(void) { Rtc.SetIsRunning(true); } + // If you want to initialize a fresh RTC to compiled time, use this code /* RtcDateTime tt = Rtc.GetDateTime(); time_t t = tt.Epoch32Time(); // sec2000 -> epoch @@ -45,7 +46,7 @@ int rtc_init(void) { } // rtc_init() -int set_rtctime(time_t t) { // t is UTC in seconds epoch time +uint8_t set_rtctime(time_t t) { // t is UTC in seconds epoch time if (I2C_MUTEX_LOCK()) { Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000 I2C_MUTEX_UNLOCK(); @@ -57,11 +58,6 @@ int set_rtctime(time_t t) { // t is UTC in seconds epoch time } // failure } // set_rtctime() -int set_rtctime(uint32_t t) { // t is UTC in seconds epoch time - return set_rtctime(static_cast(t)); - // set_rtctime() -} - 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 diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 6a1c46ec..5bebbc57 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -13,13 +13,13 @@ void time_sync() { return; #ifdef HAS_GPS - if (syncTime(get_gpstime(), pps)) + if (syncTime(get_gpstime(), _gps)) return; // attempt sync with GPS time #endif -// no GPS -> fallback to RTC time +// no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - if (!syncTime(get_rtctime(), rtc)) // sync with RTC time + if (!syncTime(get_rtctime(), _rtc)) // sync with RTC time ESP_LOGW(TAG, "no confident RTC time"); #endif @@ -40,20 +40,20 @@ uint8_t syncTime(time_t const t, uint8_t const caller) { if (TimeIsValid(t)) { uint8_t const TimeIsPulseSynced = wait_for_pulse(); // wait for next 1pps timepulse - setTime(t); - adjustTime(1); // forward time to next second + setTime(t); // sync time and reset timeStatus() to timeSet + adjustTime(1); // forward time to next second timeSource = timeSetSymbols[caller]; ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSource, hour(t), minute(t), second(t)); #ifdef HAS_RTC - if ((TimeIsPulseSynced) && (caller != rtc)) + if ((TimeIsPulseSynced) && (caller != _rtc)) set_rtctime(now()); #endif return 1; // success } else { ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); - timeSource = timeSetSymbols[unsynced]; + timeSource = timeSetSymbols[_unsynced]; return 0; // failure } } // syncTime() From 4e61ca1efa3b97c43d09a8c498ec275af3bd0879 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 13:47:39 +0100 Subject: [PATCH 15/27] readme.md updated for new rcommand get_time --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e44aafc1..34ca4f5f 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,10 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe +**Port #9:** Time/Date + + byte 1-2: board's local time/date in UNIX epoch (number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds) + # Remote control The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them. @@ -358,6 +362,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts. Device answers with BME680 sensor data set on Port 7. +0x86 get time/date + + Device answers with it's local time/date (UTC Unix epoch) on Port 9. + # License From 658402976bab1cb2a9f412e79c868c30e786641b Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 13:48:42 +0100 Subject: [PATCH 16/27] readme.md corrections --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 34ca4f5f..255fe26c 100644 --- a/README.md +++ b/README.md @@ -235,11 +235,11 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. **Port #8:** Battery voltage data (only if device has feature BATT) - byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe + bytes 1-2: Battery or USB Voltage [mV], 0 if no battery probe **Port #9:** Time/Date - byte 1-2: board's local time/date in UNIX epoch (number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds) + bytes 1-4: board's local time/date in UNIX epoch (number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds) # Remote control From a676658977575e4649f4ebd1c1ea84853ce08c5e Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 15:04:47 +0100 Subject: [PATCH 17/27] rcommand.cpp bugfix get_time() --- src/rcommand.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 397c8e1f..a0c89cc4 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -274,13 +274,9 @@ void get_bme(uint8_t val[]) { void get_time(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get time"); -#ifdef HAS_BME payload.reset(); - payload.addtime(now()); + payload.addTime(now()); SendPayload(TIMEPORT, prio_high); -#else - ESP_LOGW(TAG, "BME680 sensor not supported"); -#endif }; // assign previously defined functions to set of numeric remote commands From c20ef02ce839a0c2cc20f2b157fec374a40421cc Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 15:08:41 +0100 Subject: [PATCH 18/27] timekeeper fixes --- include/display.h | 2 +- include/globals.h | 1 + include/timekeeper.h | 5 +++-- src/display.cpp | 4 ++-- src/main.cpp | 5 +++-- src/paxcounter.conf | 4 ++-- src/timekeeper.cpp | 27 ++++++++++++++++----------- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/include/display.h b/include/display.h index 8f3227d0..d4a873c2 100644 --- a/include/display.h +++ b/include/display.h @@ -5,7 +5,7 @@ #include "cyclic.h" extern uint8_t DisplayState; -extern char timeSource; +extern timesource_t timeSource; extern HAS_DISPLAY u8x8; diff --git a/include/globals.h b/include/globals.h index 349e61bd..5805af63 100644 --- a/include/globals.h +++ b/include/globals.h @@ -97,6 +97,7 @@ typedef struct { } bmeStatus_t; enum sendprio_t { prio_low, prio_normal, prio_high }; +enum timesource_t { _gps, _rtc, _lora, _unsynced }; extern std::set, Mallocator> macs; extern std::array::iterator it; diff --git a/include/timekeeper.h b/include/timekeeper.h index 176aec83..90441736 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -13,7 +13,7 @@ #include "dcf77.h" #endif -enum timesources { _gps, _rtc, _lora, _unsynced }; +extern const char timeSetSymbols[]; void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); @@ -21,9 +21,10 @@ void clock_loop(void *pvParameters); void time_sync(void); void timepulse_start(void); uint8_t wait_for_pulse(void); -uint8_t syncTime(time_t const t, uint8_t const caller); +uint8_t syncTime(time_t const t, timesource_t const caller); uint8_t timepulse_init(void); uint8_t TimeIsValid(time_t const t); +time_t syncProvider_CB(void); time_t compiledUTC(void); time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss); diff --git a/src/display.cpp b/src/display.cpp index 54e6f1ad..f0c556d6 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -46,7 +46,7 @@ const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; uint8_t DisplayState = 0; -char timeSource = '?'; +timesource_t timeSource = _unsynced; // helper function, prints a hex key on display void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { @@ -217,7 +217,7 @@ void refreshtheDisplay() { u8x8.printf("%-16s", display_line6); #else // we want a systime display instead LoRa status t = myTZ.toLocal(now()); - timeState = TimePulseTick ? ' ' : timeSource; + timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource]; TimePulseTick = false; u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), timeState, day(t), printmonth[month(t)]); diff --git a/src/main.cpp b/src/main.cpp index daadf5b4..27c6fa3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -361,10 +361,11 @@ void setup() { ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse timepulse_start(); - time_sync(); // sync time #ifdef TIME_SYNC_INTERVAL - setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() + setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() via Time.h + setSyncProvider(syncProvider_CB); // is called by Time.h #endif + time_sync(); // sync time // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index ac0a22b7..766688eb 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -66,8 +66,8 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node with external time source -#define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA), comment out means [default = 5] -#define TIME_SYNC_LORA 1 // use LORA network as time source, comment out means off [default = off] +#define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source (GPS/LORA) [default = 60], comment out means off +//#define TIME_SYNC_LORA 1 // use LORA network as time source, 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/timekeeper.cpp b/src/timekeeper.cpp index 5bebbc57..a34ff6a3 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -3,6 +3,9 @@ // Local logging tag static const char TAG[] = "main"; +// symbol to display current time source +const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; + void time_sync() { // synchonization of systime with external time source (GPS/LORA) // frequently called from cyclic.cpp @@ -31,20 +34,16 @@ void time_sync() { #endif // TIME_SYNC_INTERVAL } // time_sync() -// helper function to sync time on start of next second -uint8_t syncTime(time_t const t, uint8_t const caller) { - - // symbol to display current time source - const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; - +// sync time on start of next second +uint8_t syncTime(time_t const t, timesource_t const caller) { if (TimeIsValid(t)) { uint8_t const TimeIsPulseSynced = wait_for_pulse(); // wait for next 1pps timepulse setTime(t); // sync time and reset timeStatus() to timeSet adjustTime(1); // forward time to next second - timeSource = timeSetSymbols[caller]; - ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSource, - hour(t), minute(t), second(t)); + timeSource = caller; + ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", + timeSetSymbols[timeSource], hour(t), minute(t), second(t)); #ifdef HAS_RTC if ((TimeIsPulseSynced) && (caller != _rtc)) set_rtctime(now()); @@ -53,11 +52,17 @@ uint8_t syncTime(time_t const t, uint8_t const caller) { } else { ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); - timeSource = timeSetSymbols[_unsynced]; + timeSource = _unsynced; return 0; // failure } } // syncTime() +// callback function called by Time.h in interval set in main.cpp +time_t syncProvider_CB(void) { + timeSource = _unsynced; + return 0; +} + // helper function to sync moment on timepulse uint8_t wait_for_pulse(void) { // sync on top of next second with 1pps timepulse @@ -204,7 +209,7 @@ void clock_loop(void *pvParameters) { // ClockTask xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, portMAX_DELAY); // wait for timepulse - if (timeStatus() != timeSet) // no confident time -> no output to clock + if (timeStatus() == timeNotSet) // no confident time -> no output to clock continue; t = now(); // payload to send to clock From 6ab4a0bd9fd5f05186af69679228819945b92061 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 24 Feb 2019 23:13:15 +0100 Subject: [PATCH 19/27] timekeeper fixes --- include/lorawan.h | 7 ++--- include/timekeeper.h | 7 ++--- src/gpsread.cpp | 11 +++----- src/lorawan.cpp | 20 +++++++++---- src/rtctime.cpp | 2 -- src/timekeeper.cpp | 67 +++++++++++++++++++++++++------------------- 6 files changed, 63 insertions(+), 51 deletions(-) diff --git a/include/lorawan.h b/include/lorawan.h index 76a57397..d02e8931 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -17,10 +17,9 @@ #include #endif - - extern QueueHandle_t LoraSendQueue; +esp_err_t lora_stack_init(); void onEvent(ev_t ev); void gen_lora_deveui(uint8_t *pdeveui); void RevBytes(unsigned char *b, size_t c); @@ -36,7 +35,7 @@ void lora_queuereset(void); void lora_housekeeping(void); void user_request_network_time_callback(void *pVoidUserUTCTime, int flagSuccess); - -esp_err_t lora_stack_init(); +time_t set_loratime(time_t t); +time_t get_loratime(void); #endif \ No newline at end of file diff --git a/include/timekeeper.h b/include/timekeeper.h index 90441736..2b36bbf2 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -20,13 +20,12 @@ void clock_init(void); void clock_loop(void *pvParameters); void time_sync(void); void timepulse_start(void); -uint8_t wait_for_pulse(void); -uint8_t syncTime(time_t const t, timesource_t const caller); +uint8_t syncTime(getExternalTime getTimeFunction, timesource_t const caller); uint8_t timepulse_init(void); uint8_t TimeIsValid(time_t const t); time_t syncProvider_CB(void); time_t compiledUTC(void); -time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, - uint8_t mm, uint8_t ss); +time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, + uint8_t ss); #endif // _timekeeper_H \ No newline at end of file diff --git a/src/gpsread.cpp b/src/gpsread.cpp index a1ddadc3..514499f5 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -75,9 +75,6 @@ void gps_read() { // function to fetch current time from gps 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; if ((gps.time.age() < 950) && (gps.time.isValid())) { @@ -87,11 +84,11 @@ time_t get_gpstime(void) { // use recent gps time t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), - gps.time.hour(), gps.time.minute(), gps.time.second()); + 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()); + // 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 7c0cee66..b4067cd9 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -472,9 +472,19 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (syncTime(*pUserUTCTime, _lora)) { // have we got a valid time? - ESP_LOGI(TAG, "LORA has set the system time"); - } else - ESP_LOGI(TAG, "Unable to sync system time with LORA"); + set_loratime(*pUserUTCTime); // store time in time sync provider function + if (syncTime(get_loratime, _lora)) + ESP_LOGI(TAG, "Received recent time from LoRa"); + else + ESP_LOGI(TAG, "Invalid time received from LoRa"); #endif // HAS_LORA -} // user_request_network_time_callback \ No newline at end of file +} // user_request_network_time_callback + +time_t set_loratime(time_t t) { + static time_t loratime = 0; // stores time for retrieval + if (t > loratime) + loratime = t; // store time if it is recent + return loratime; +} + +time_t get_loratime(void) { return set_loratime(0); } diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 5b7952e3..ff542318 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -59,8 +59,6 @@ uint8_t set_rtctime(time_t t) { // t is UTC in seconds epoch time } // set_rtctime() 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; if (I2C_MUTEX_LOCK()) { if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index a34ff6a3..316835ec 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -6,23 +6,24 @@ static const char TAG[] = "main"; // symbol to display current time source const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; +getExternalTime TimeSourcePtr; // pointer to time source function + void time_sync() { - // synchonization of systime with external time source (GPS/LORA) - // frequently called from cyclic.cpp + // check synchonization of systime, called by cyclic.cpp #ifdef TIME_SYNC_INTERVAL - if (timeStatus() == timeSet) + if (timeStatus() == timeSet) // timeStatus() is flipped in Time.h return; #ifdef HAS_GPS - if (syncTime(get_gpstime(), _gps)) + if (syncTime(get_gpstime, _gps)) return; // attempt sync with GPS time #endif // no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - if (!syncTime(get_rtctime(), _rtc)) // sync with RTC time + if (!syncTime(get_rtctime, _rtc)) // sync with RTC time ESP_LOGW(TAG, "no confident RTC time"); #endif @@ -34,43 +35,50 @@ void time_sync() { #endif // TIME_SYNC_INTERVAL } // time_sync() -// sync time on start of next second -uint8_t syncTime(time_t const t, timesource_t const caller) { +// sync time on start of next second from GPS or RTC +uint8_t syncTime(getExternalTime getTimeFunction, timesource_t const caller) { + + TimeSourcePtr = getTimeFunction; + time_t t; + + if (!TimeSourcePtr) + goto error; + + if ((caller == _gps || caller == _rtc)) // ticking timesource? + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // then wait on pps + + t = TimeSourcePtr(); // get time from given timesource + if (TimeIsValid(t)) { - uint8_t const TimeIsPulseSynced = - wait_for_pulse(); // wait for next 1pps timepulse - setTime(t); // sync time and reset timeStatus() to timeSet - adjustTime(1); // forward time to next second + if (caller == _gps) // gps time concerns past second + t++; + setTime(t); // flips timeStatus() in Time.h timeSource = caller; ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSetSymbols[timeSource], hour(t), minute(t), second(t)); -#ifdef HAS_RTC - if ((TimeIsPulseSynced) && (caller != _rtc)) - set_rtctime(now()); -#endif - return 1; // success - } else { - ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); - timeSource = _unsynced; - return 0; // failure +#ifdef HAS_RTC + if (caller != _rtc) + set_rtctime(t); +#endif + + return 1; // success } + +error: + ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); + timeSource = _unsynced; + return 0; // failure + } // syncTime() + // callback function called by Time.h in interval set in main.cpp time_t syncProvider_CB(void) { timeSource = _unsynced; return 0; } -// helper function to sync moment on timepulse -uint8_t wait_for_pulse(void) { - // sync on top of next second with 1pps timepulse - if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1010)) == pdTRUE) - return 1; // success - ESP_LOGD(TAG, "Missing timepulse"); - return 0; // failure -} // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { @@ -209,7 +217,8 @@ void clock_loop(void *pvParameters) { // ClockTask xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, portMAX_DELAY); // wait for timepulse - if (timeStatus() == timeNotSet) // no confident time -> no output to clock + // no confident time -> suppress clock output + if (timeStatus() == timeNotSet) continue; t = now(); // payload to send to clock From 5fb4c7bec2cb98f39eed705addcdd5f11fac349d Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 25 Feb 2019 00:26:46 +0100 Subject: [PATCH 20/27] timekeeper use syncprovider by Time.h --- include/cyclic.h | 3 --- include/lorawan.h | 3 --- include/timekeeper.h | 4 ++-- src/cyclic.cpp | 1 - src/display.cpp | 5 ++--- src/lorawan.cpp | 16 ++++------------ src/main.cpp | 3 +-- src/timekeeper.cpp | 36 ++++++++++++++---------------------- 8 files changed, 23 insertions(+), 48 deletions(-) diff --git a/include/cyclic.h b/include/cyclic.h index 89ccb240..829f9610 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -5,15 +5,12 @@ #include "senddata.h" #include "rcommand.h" #include "spislave.h" -#include "timekeeper.h" #include #ifdef HAS_BME #include "bme680mems.h" #endif - - void doHousekeeping(void); uint64_t uptime(void); void reset_counters(void); diff --git a/include/lorawan.h b/include/lorawan.h index d02e8931..ddc2fc41 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -35,7 +35,4 @@ void lora_queuereset(void); void lora_housekeeping(void); void user_request_network_time_callback(void *pVoidUserUTCTime, int flagSuccess); -time_t set_loratime(time_t t); -time_t get_loratime(void); - #endif \ No newline at end of file diff --git a/include/timekeeper.h b/include/timekeeper.h index 2b36bbf2..198d8987 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -18,9 +18,9 @@ extern const char timeSetSymbols[]; void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); void clock_loop(void *pvParameters); -void time_sync(void); +time_t time_sync(void); void timepulse_start(void); -uint8_t syncTime(getExternalTime getTimeFunction, timesource_t const caller); +time_t syncTime(getExternalTime getTimeFunction, timesource_t const caller); uint8_t timepulse_init(void); uint8_t TimeIsValid(time_t const t); time_t syncProvider_CB(void); diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 021ea8d6..3d170eb1 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -19,7 +19,6 @@ void doHousekeeping() { spi_housekeeping(); lora_housekeeping(); - time_sync(); // task storage debugging // ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", diff --git a/src/display.cpp b/src/display.cpp index f0c556d6..d5e2b382 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -132,7 +132,7 @@ void refreshtheDisplay() { uint8_t msgWaiting; char timeState, buff[16]; - time_t t; + time_t t = myTZ.toLocal(now()); // note: call now() here *before* locking mutex! // block i2c bus access if (I2C_MUTEX_LOCK()) { @@ -216,8 +216,7 @@ 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(now()); - timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource]; + timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource]; TimePulseTick = false; u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), timeState, day(t), printmonth[month(t)]); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index b4067cd9..02503aba 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -472,19 +472,11 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - set_loratime(*pUserUTCTime); // store time in time sync provider function - if (syncTime(get_loratime, _lora)) + if (TimeIsValid(*pUserUTCTime)) { + setTime(*pUserUTCTime); ESP_LOGI(TAG, "Received recent time from LoRa"); + } else ESP_LOGI(TAG, "Invalid time received from LoRa"); #endif // HAS_LORA -} // user_request_network_time_callback - -time_t set_loratime(time_t t) { - static time_t loratime = 0; // stores time for retrieval - if (t > loratime) - loratime = t; // store time if it is recent - return loratime; -} - -time_t get_loratime(void) { return set_loratime(0); } +} // user_request_network_time_callback \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 27c6fa3c..a2a807ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -363,9 +363,8 @@ void setup() { timepulse_start(); #ifdef TIME_SYNC_INTERVAL setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() via Time.h - setSyncProvider(syncProvider_CB); // is called by Time.h + setSyncProvider(time_sync); // is called by Time.h #endif - time_sync(); // sync time // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 316835ec..712fd09b 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -8,22 +8,23 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; getExternalTime TimeSourcePtr; // pointer to time source function -void time_sync() { +time_t time_sync() { // check synchonization of systime, called by cyclic.cpp + time_t t = 0; + #ifdef TIME_SYNC_INTERVAL - if (timeStatus() == timeSet) // timeStatus() is flipped in Time.h - return; - #ifdef HAS_GPS - if (syncTime(get_gpstime, _gps)) - return; // attempt sync with GPS time + t = syncTime(get_gpstime, _gps); + if (t) + return t; // attempt sync with GPS time #endif // no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - if (!syncTime(get_rtctime, _rtc)) // sync with RTC time + t = syncTime(get_rtctime, _rtc); // sync with RTC time + if (!t) ESP_LOGW(TAG, "no confident RTC time"); #endif @@ -33,26 +34,25 @@ void time_sync() { #endif #endif // TIME_SYNC_INTERVAL + + return t; } // time_sync() // sync time on start of next second from GPS or RTC -uint8_t syncTime(getExternalTime getTimeFunction, timesource_t const caller) { +time_t syncTime(getExternalTime getTimeFunction, timesource_t const caller) { - TimeSourcePtr = getTimeFunction; time_t t; - + TimeSourcePtr = getTimeFunction; if (!TimeSourcePtr) goto error; - if ((caller == _gps || caller == _rtc)) // ticking timesource? - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // then wait on pps + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps t = TimeSourcePtr(); // get time from given timesource if (TimeIsValid(t)) { if (caller == _gps) // gps time concerns past second t++; - setTime(t); // flips timeStatus() in Time.h timeSource = caller; ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", timeSetSymbols[timeSource], hour(t), minute(t), second(t)); @@ -62,7 +62,7 @@ uint8_t syncTime(getExternalTime getTimeFunction, timesource_t const caller) { set_rtctime(t); #endif - return 1; // success + return t; // success } error: @@ -72,14 +72,6 @@ error: } // syncTime() - -// callback function called by Time.h in interval set in main.cpp -time_t syncProvider_CB(void) { - timeSource = _unsynced; - return 0; -} - - // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { From 5c191a269f0a2ce98d939860123841e923ee840e Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 25 Feb 2019 20:22:03 +0100 Subject: [PATCH 21/27] timekeeper once again reworked and shortened --- include/globals.h | 15 ++++---- include/gpsread.h | 1 + include/if482.h | 5 ++- include/timekeeper.h | 5 +-- src/gpsread.cpp | 21 ++++++++---- src/if482.cpp | 15 +------- src/lorawan.cpp | 1 + src/rtctime.cpp | 2 +- src/timekeeper.cpp | 81 ++++++++++++++++++++------------------------ 9 files changed, 68 insertions(+), 78 deletions(-) diff --git a/include/globals.h b/include/globals.h index 5805af63..24d34865 100644 --- a/include/globals.h +++ b/include/globals.h @@ -41,13 +41,13 @@ #define SCREEN_MODE (0x80) // I2C bus access control -#define I2C_MUTEX_LOCK() \ - xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == \ - pdTRUE +#define I2C_MUTEX_LOCK() +xSemaphoreTake(I2Caccess, + (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == pdTRUE #define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) -// Struct holding devices's runtime configuration -typedef struct { + // Struct holding devices's runtime configuration + typedef struct { uint8_t lorasf; // 7-12, lora spreadfactor uint8_t txpower; // 2-15, lora tx power uint8_t adrmode; // 0=disabled, 1=enabled @@ -107,8 +107,9 @@ extern configData_t cfg; // current device configuration 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; // one-pulse-per-second flags set by GPS or RTC + batt_voltage; // display values +extern bool volatile TimePulseTick; // one-pulse-per-second flags set by GPS or + // RTC extern hw_timer_t *sendCycle, *displaytimer, *clockCycle; extern SemaphoreHandle_t I2Caccess, TimePulse; extern TaskHandle_t irqHandlerTask, ClockTask; diff --git a/include/gpsread.h b/include/gpsread.h index e11c2644..e96e2531 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -3,6 +3,7 @@ #include // library for parsing NMEA data #include +#include "timekeeper.h" #ifdef GPS_I2C // Needed for reading from I2C Bus #include diff --git a/include/if482.h b/include/if482.h index db09eb35..d31f5d59 100644 --- a/include/if482.h +++ b/include/if482.h @@ -2,15 +2,14 @@ #define _IF482_H #include "globals.h" +#include "timekeeper.h" #define IF482_FRAME_SIZE (17) #define IF482_PULSE_LENGTH (1000) -extern HardwareSerial IF482; +extern HardwareSerial IF482; void IF482_Pulse(time_t t); String IRAM_ATTR IF482_Frame(time_t tt); -TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin, - int8_t txPins); #endif \ No newline at end of file diff --git a/include/timekeeper.h b/include/timekeeper.h index 198d8987..b4afb1b7 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -20,12 +20,13 @@ void clock_init(void); void clock_loop(void *pvParameters); time_t time_sync(void); void timepulse_start(void); -time_t syncTime(getExternalTime getTimeFunction, timesource_t const caller); uint8_t timepulse_init(void); -uint8_t TimeIsValid(time_t const t); +time_t TimeIsValid(time_t const t); time_t syncProvider_CB(void); time_t compiledUTC(void); time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss); +TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, + int8_t rxPin, int8_t txPins); #endif // _timekeeper_H \ No newline at end of file diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 514499f5..2ef5d84d 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -75,22 +75,29 @@ void gps_read() { // function to fetch current time from gps time_t get_gpstime(void) { + +#define NMEA_FRAME_SIZE 80 // NEMA has a maxium of 80 bytes per record +#define NMEA_BUFFER 50 // 50ms safety time regardless + time_t t = 0; - if ((gps.time.age() < 950) && (gps.time.isValid())) { +// set timeout for reading recent time from GPS +#ifdef GPS_SERIAL // serial GPS + static const TickType_t txDelay = + pdMS_TO_TICKS(1000 - NMEA_BUFFER - tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL)); +#else // I2C GPS + static const TickType_t txDelay = 1000 - NMEA_BUFFER; +#endif + + if ((gps.time.age() < txDelay) && (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(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; + return TimeIsValid(t); } // get_gpstime() // GPS serial feed FreeRTos Task diff --git a/src/if482.cpp b/src/if482.cpp index 9f21f7ac..a0d93182 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -90,7 +90,7 @@ HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) void IF482_Pulse(time_t t) { static const TickType_t txDelay = - pdMS_TO_TICKS(IF482_PULSE_LENGTH) - tx_Ticks(HAS_IF482); + pdMS_TO_TICKS(IF482_PULSE_LENGTH - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482)); TickType_t startTime = xTaskGetTickCount(); @@ -124,17 +124,4 @@ String IRAM_ATTR IF482_Frame(time_t startTime) { return out; } -// calculate serial tx time from IF482 serial settings -TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin, - int8_t txPins) { - - uint32_t databits = ((config & 0x0c) >> 2) + 5; - uint32_t stopbits = ((config & 0x20) >> 5) + 1; - uint32_t txTime = - (databits + stopbits + 2) * IF482_FRAME_SIZE * 1000.0 / baud; - // +2 ms margin for the startbit and the clock's processing time - - return pdMS_TO_TICKS(round(txTime)); -} - #endif // HAS_IF482 \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 02503aba..36ffa3ad 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -474,6 +474,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, // Update system time with time read from the network if (TimeIsValid(*pUserUTCTime)) { setTime(*pUserUTCTime); + timeSource = _lora; ESP_LOGI(TAG, "Received recent time from LoRa"); } else diff --git a/src/rtctime.cpp b/src/rtctime.cpp index ff542318..730d4d98 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -67,7 +67,7 @@ time_t get_rtctime(void) { } I2C_MUTEX_UNLOCK(); } - return t; + return TimeIsValid(t); } // get_rtctime() float get_rtctemp(void) { diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 712fd09b..5a1bcae1 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -13,18 +13,26 @@ time_t time_sync() { time_t t = 0; -#ifdef TIME_SYNC_INTERVAL - #ifdef HAS_GPS - t = syncTime(get_gpstime, _gps); - if (t) - return t; // attempt sync with GPS time + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps + ESP_LOGD(TAG, "micros = %d", micros()); + t = get_gpstime(); + if (t) { + t++; // gps time concerns past second, so we add one second +#ifdef HAS_RTC + set_rtctime(t); // calibrate RTC +#endif + timeSource = _gps; + goto exit; + } #endif // no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - t = syncTime(get_rtctime, _rtc); // sync with RTC time - if (!t) + t = get_rtctime(); + if (t) + timeSource = _rtc; + else ESP_LOGW(TAG, "no confident RTC time"); #endif @@ -33,45 +41,18 @@ time_t time_sync() { LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif -#endif // TIME_SYNC_INTERVAL +exit: + ESP_LOGD(TAG, "micros = %d", micros()); + if (t) + ESP_LOGD(TAG, "Time was set by %c to %02d:%02d:%02d", + timeSetSymbols[timeSource], hour(t), minute(t), second(t)); + else + timeSource = _unsynced; return t; + } // time_sync() -// sync time on start of next second from GPS or RTC -time_t syncTime(getExternalTime getTimeFunction, timesource_t const caller) { - - time_t t; - TimeSourcePtr = getTimeFunction; - if (!TimeSourcePtr) - goto error; - - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps - - t = TimeSourcePtr(); // get time from given timesource - - if (TimeIsValid(t)) { - if (caller == _gps) // gps time concerns past second - t++; - timeSource = caller; - ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d", - timeSetSymbols[timeSource], hour(t), minute(t), second(t)); - -#ifdef HAS_RTC - if (caller != _rtc) - set_rtctime(t); -#endif - - return t; // success - } - -error: - ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]); - timeSource = _unsynced; - return 0; // failure - -} // syncTime() - // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { @@ -136,11 +117,11 @@ void IRAM_ATTR CLOCKIRQ(void) { } // helper function to check plausibility of a time -uint8_t TimeIsValid(time_t const t) { +time_t TimeIsValid(time_t const 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()); + return (t >= compiledUTC() ? t : 0); } // helper function to convert compile time to UTC time @@ -162,6 +143,18 @@ time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, return makeTime(tm); } +// helper function to calculate serial transmit time +TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, + int8_t rxPin, int8_t txPins) { + + uint32_t databits = ((config & 0x0c) >> 2) + 5; + uint32_t stopbits = ((config & 0x20) >> 5) + 1; + uint32_t txTime = (databits + stopbits + 2) * framesize * 1000.0 / baud; + // +1 ms margin for the startbit +1 ms for pending processing time + + return round(txTime); +} + #if defined HAS_IF482 || defined HAS_DCF77 #if defined HAS_DCF77 && defined HAS_IF482 From 55e0c73c77dab17ba62f5f73d629da655805ce8c Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 27 Feb 2019 00:49:32 +0100 Subject: [PATCH 22/27] Log tags changed --- src/battery.cpp | 2 +- src/bme680mems.cpp | 2 +- src/button.cpp | 2 +- src/macsniff.cpp | 2 +- src/ota.cpp | 2 +- src/payload.cpp | 4 +++- src/rcommand.cpp | 2 +- src/senddata.cpp | 10 +++++++++- src/sensor.cpp | 2 +- 9 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/battery.cpp b/src/battery.cpp index 940e3500..ab15fe4e 100644 --- a/src/battery.cpp +++ b/src/battery.cpp @@ -1,7 +1,7 @@ #include "globals.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; #ifdef HAS_BATTERY_PROBE esp_adc_cal_characteristics_t *adc_characs = diff --git a/src/bme680mems.cpp b/src/bme680mems.cpp index 924bc656..d0679507 100644 --- a/src/bme680mems.cpp +++ b/src/bme680mems.cpp @@ -3,7 +3,7 @@ #include "bme680mems.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; bmeStatus_t bme_status; TaskHandle_t BmeTask; diff --git a/src/button.cpp b/src/button.cpp index d7d5c2a4..93201580 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -4,7 +4,7 @@ #include "button.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; void readButton() { ESP_LOGI(TAG, "Button pressed"); diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 5cc02c64..c98761cb 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -7,7 +7,7 @@ #endif // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; uint16_t salt; diff --git a/src/ota.cpp b/src/ota.cpp index 4210b704..a2623702 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -31,7 +31,7 @@ int volatile contentLength = 0; bool volatile isValidContentType = false; // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // helper function to extract header value from header inline String getHeaderValue(String header, String headerName) { diff --git a/src/payload.cpp b/src/payload.cpp index b94c07f3..4e0d0d16 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -290,7 +290,9 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, } /* ---------------- Cayenne LPP 2.0 format ---------- */ -// see specs http://community.mydevices.com/t/cayenne-lpp-2-0/7510 +// see specs +// http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0) +// https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md (LPP 1.0) // PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels -> FPort 1 // PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels -> FPort 2 diff --git a/src/rcommand.cpp b/src/rcommand.cpp index a0c89cc4..2873b9bf 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -3,7 +3,7 @@ #include "rcommand.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // helper function void do_reset() { diff --git a/src/senddata.cpp b/src/senddata.cpp index 386ab6ab..6886117a 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -34,9 +34,13 @@ void SendPayload(uint8_t port, sendprio_t prio) { } memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); - // enqueue message in device's send queues +// enqueue message in device's send queues +#ifdef HAS_LORA lora_enqueuedata(&SendBuffer, prio); +#endif +#ifdef HAS_SPI spi_enqueuedata(&SendBuffer, prio); +#endif } // SendPayload @@ -130,6 +134,10 @@ void sendCounter() { } // sendCounter() void flushQueues() { +#ifdef HAS_LORA lora_queuereset(); +#endif +#ifdef HAS_SPI spi_queuereset(); +#endif } \ No newline at end of file diff --git a/src/sensor.cpp b/src/sensor.cpp index 0892f4dd..6b672f0e 100644 --- a/src/sensor.cpp +++ b/src/sensor.cpp @@ -2,7 +2,7 @@ #include "globals.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; #define SENSORBUFFER \ 10 // max. size of user sensor data buffer in bytes [default=20] From 7e4a360bb71e7a51c6f2898635e0c3b10ba7e691 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 27 Feb 2019 00:52:27 +0100 Subject: [PATCH 23/27] timekeeper fixes (still experimental) --- include/display.h | 1 - include/globals.h | 14 +++++----- include/gpsread.h | 4 +++ include/irqhandler.h | 1 + include/payload.h | 5 ++-- include/timekeeper.h | 2 +- src/cyclic.cpp | 6 ++++- src/dcf77.cpp | 2 +- src/display.cpp | 1 - src/gpsread.cpp | 27 +++++++++++--------- src/if482.cpp | 10 ++++---- src/irqhandler.cpp | 15 ++++++----- src/lorawan.cpp | 39 ++++++++++++---------------- src/main.cpp | 14 +++++----- src/rtctime.cpp | 2 +- src/timekeeper.cpp | 61 ++++++++++++++++++++++++-------------------- 16 files changed, 106 insertions(+), 98 deletions(-) diff --git a/include/display.h b/include/display.h index d4a873c2..38d503a9 100644 --- a/include/display.h +++ b/include/display.h @@ -5,7 +5,6 @@ #include "cyclic.h" extern uint8_t DisplayState; -extern timesource_t timeSource; extern HAS_DISPLAY u8x8; diff --git a/include/globals.h b/include/globals.h index 24d34865..72036687 100644 --- a/include/globals.h +++ b/include/globals.h @@ -41,13 +41,13 @@ #define SCREEN_MODE (0x80) // I2C bus access control -#define I2C_MUTEX_LOCK() -xSemaphoreTake(I2Caccess, - (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == pdTRUE +#define I2C_MUTEX_LOCK() \ + xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == \ + pdTRUE #define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) - // Struct holding devices's runtime configuration - typedef struct { +// Struct holding devices's runtime configuration +typedef struct { uint8_t lorasf; // 7-12, lora spreadfactor uint8_t txpower; // 2-15, lora tx power uint8_t adrmode; // 0=disabled, 1=enabled @@ -108,8 +108,8 @@ 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; // one-pulse-per-second flags set by GPS or - // RTC +extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC +extern timesource_t timeSource; extern hw_timer_t *sendCycle, *displaytimer, *clockCycle; extern SemaphoreHandle_t I2Caccess, TimePulse; extern TaskHandle_t irqHandlerTask, ClockTask; diff --git a/include/gpsread.h b/include/gpsread.h index e96e2531..62eb1a15 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -9,10 +9,14 @@ #include #endif +#define NMEA_FRAME_SIZE 80 // NEMA has a maxium of 80 bytes per record +#define NMEA_BUFFERTIME 50 // 50ms safety time regardless + extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern gpsStatus_t gps_status; // Make struct for storing gps data globally available extern TaskHandle_t GpsTask; +extern TickType_t const gpsDelay_ticks; // time to NMEA arrival int gps_init(void); void gps_read(void); diff --git a/include/irqhandler.h b/include/irqhandler.h index cc8d15ac..64cb12fe 100644 --- a/include/irqhandler.h +++ b/include/irqhandler.h @@ -9,6 +9,7 @@ #include "globals.h" #include "cyclic.h" #include "senddata.h" +#include "timekeeper.h" void irqHandler(void *pvParameters); void IRAM_ATTR homeCycleIRQ(); diff --git a/include/payload.h b/include/payload.h index d8fe468b..159dcfc4 100644 --- a/include/payload.h +++ b/include/payload.h @@ -1,7 +1,8 @@ #ifndef _PAYLOAD_H_ #define _PAYLOAD_H_ -// MyDevices CayenneLPP channels for dynamic sensor payload format +// MyDevices CayenneLPP 1.0 channels for Synamic sensor payload format +// all payload goes out on LoRa FPort 1 #if (PAYLOAD_ENCODER == 3) #define LPP_GPS_CHANNEL 20 @@ -19,7 +20,7 @@ #endif -// MyDevices CayenneLPP types +// MyDevices CayenneLPP 2.0 types for Packed Sensor Payload, not using channels, but different FPorts #define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m #define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed MSB #define LPP_DIGITAL_INPUT 0 // 1 byte diff --git a/include/timekeeper.h b/include/timekeeper.h index b4afb1b7..ae58df3d 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -18,7 +18,7 @@ extern const char timeSetSymbols[]; void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); void clock_loop(void *pvParameters); -time_t time_sync(void); +void timeSync(void); void timepulse_start(void); uint8_t timepulse_init(void); time_t TimeIsValid(time_t const t); diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 3d170eb1..c9341f03 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -5,7 +5,7 @@ #include "cyclic.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // do all housekeeping void doHousekeeping() { @@ -17,8 +17,12 @@ void doHousekeeping() { if (cfg.runmode == 1) do_reset(); +#ifdef HAS_SPI spi_housekeeping(); +#endif +#ifdef HAS_LORA lora_housekeeping(); +#endif // task storage debugging // ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", diff --git a/src/dcf77.cpp b/src/dcf77.cpp index be5a1b0b..1c27235e 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -14,7 +14,7 @@ https://github.com/udoklein/dcf77 #include "dcf77.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // triggered by second timepulse to ticker out DCF signal void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { diff --git a/src/display.cpp b/src/display.cpp index d5e2b382..047c233e 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -46,7 +46,6 @@ const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; uint8_t DisplayState = 0; -timesource_t timeSource = _unsynced; // helper function, prints a hex key on display void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) { diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 2ef5d84d..9c34049d 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -3,7 +3,7 @@ #include "globals.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; TinyGPSPlus gps; gpsStatus_t gps_status; @@ -11,6 +11,10 @@ TaskHandle_t GpsTask; #ifdef GPS_SERIAL HardwareSerial GPS_Serial(1); // use UART #1 +TickType_t const gpsDelay_ticks = pdMS_TO_TICKS(1000 - NMEA_BUFFERTIME) - + tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL); +#else +TickType_t const gpsDelay_ticks = pdMS_TO_TICKS(1000 - NMEA_BUFFERTIME); #endif // initialize and configure GPS @@ -23,6 +27,13 @@ int gps_init(void) { return 0; } +// set timeout for reading recent time from GPS +#ifdef GPS_SERIAL // serial GPS + +#else // I2C GPS + +#endif + #if defined GPS_SERIAL GPS_Serial.begin(GPS_SERIAL); ESP_LOGI(TAG, "Using serial GPS"); @@ -76,20 +87,12 @@ void gps_read() { // function to fetch current time from gps time_t get_gpstime(void) { -#define NMEA_FRAME_SIZE 80 // NEMA has a maxium of 80 bytes per record -#define NMEA_BUFFER 50 // 50ms safety time regardless + // set time to wait for arrive next recent NMEA time record + static const uint32_t gpsDelay_ms = gpsDelay_ticks / portTICK_PERIOD_MS; time_t t = 0; -// set timeout for reading recent time from GPS -#ifdef GPS_SERIAL // serial GPS - static const TickType_t txDelay = - pdMS_TO_TICKS(1000 - NMEA_BUFFER - tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL)); -#else // I2C GPS - static const TickType_t txDelay = 1000 - NMEA_BUFFER; -#endif - - if ((gps.time.age() < txDelay) && (gps.time.isValid())) { + if ((gps.time.age() < gpsDelay_ms) && (gps.time.isValid())) { ESP_LOGD(TAG, "GPS time age: %dms, is valid: %s", gps.time.age(), gps.time.isValid() ? "yes" : "no"); diff --git a/src/if482.cpp b/src/if482.cpp index a0d93182..a532fdaa 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -82,19 +82,19 @@ not evaluated by model BU-190 #include "if482.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) -// triggered by timepulse to ticker out DCF signal +// triggered by timepulse to send IF482 signal void IF482_Pulse(time_t t) { static const TickType_t txDelay = pdMS_TO_TICKS(IF482_PULSE_LENGTH - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482)); - TickType_t startTime = xTaskGetTickCount(); - - vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire + //TickType_t startTime = xTaskGetTickCount(); + //vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire + vTaskDelay(txDelay); // wait until moment to fire IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second } diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 3f680702..1236225c 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -1,7 +1,7 @@ #include "irqhandler.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // irq handler task, handles all our application level interrupts void irqHandler(void *pvParameters) { @@ -12,11 +12,10 @@ void irqHandler(void *pvParameters) { // task remains in blocked state until it is notified by an irq for (;;) { - xTaskNotifyWait( - 0x00, // Don't clear any bits on entry - ULONG_MAX, // Clear all bits on exit - &InterruptStatus, // Receives the notification value - portMAX_DELAY); // wait forever + xTaskNotifyWait(0x00, // Don't clear any bits on entry + ULONG_MAX, // Clear all bits on exit + &InterruptStatus, // Receives the notification value + portMAX_DELAY); // wait forever // button pressed? #ifdef HAS_BUTTON @@ -31,8 +30,10 @@ void irqHandler(void *pvParameters) { #endif // are cyclic tasks due? - if (InterruptStatus & CYCLIC_IRQ) + if (InterruptStatus & CYCLIC_IRQ) { doHousekeeping(); + timeSync(); + } // is time to send the payload? if (InterruptStatus & SENDCOUNTER_IRQ) diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 36ffa3ad..df73da4f 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -358,12 +358,7 @@ void lora_send(osjob_t *job) { lora_send); } -#endif // HAS_LORA - esp_err_t lora_stack_init() { -#ifndef HAS_LORA - return ESP_OK; // continue main program -#else assert(SEND_QUEUE_SIZE); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { @@ -401,12 +396,10 @@ esp_err_t lora_stack_init() { } return ESP_OK; // continue main program -#endif // HAS_LORA } void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in LORA send queue -#ifdef HAS_LORA BaseType_t ret; switch (prio) { case prio_high: @@ -423,27 +416,26 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { } else { ESP_LOGW(TAG, "LORA sendqueue is full"); } -#endif } -void lora_queuereset(void) { -#ifdef HAS_LORA - xQueueReset(LoraSendQueue); -#endif -} +void lora_queuereset(void) { xQueueReset(LoraSendQueue); } void lora_housekeeping(void) { -#ifdef HAS_LORA -// ESP_LOGD(TAG, "loraloop %d bytes left", -// uxTaskGetStackHighWaterMark(LoraTask)); -#endif + // ESP_LOGD(TAG, "loraloop %d bytes left", + // uxTaskGetStackHighWaterMark(LoraTask)); } void user_request_network_time_callback(void *pVoidUserUTCTime, int flagSuccess) { -#ifdef HAS_LORA // 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) { @@ -473,11 +465,12 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, // Update system time with time read from the network if (TimeIsValid(*pUserUTCTime)) { - setTime(*pUserUTCTime); + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps + setTime(*pUserUTCTime + 1); timeSource = _lora; ESP_LOGI(TAG, "Received recent time from LoRa"); - } - else + } else ESP_LOGI(TAG, "Invalid time received from LoRa"); -#endif // HAS_LORA -} // user_request_network_time_callback \ No newline at end of file +} // user_request_network_time_callback + +#endif // HAS_LORA \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a2a807ab..eb04d063 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,7 +52,7 @@ ESP32 hardware irq timers RTC hardware timer (if present) ================================ - triggers IF482 clock signal + triggers pps 1 sec impulse */ @@ -72,6 +72,7 @@ TaskHandle_t irqHandlerTask, ClockTask; SemaphoreHandle_t I2Caccess, TimePulse; bool volatile TimePulseTick = false; time_t userUTCTime = 0; +timesource_t timeSource = _unsynced; // container holding unique MAC address hashes with Memory Alloctor using PSRAM, // if present @@ -86,7 +87,7 @@ TimeChangeRule mySTD = STANDARD_TIME; Timezone myTZ(myDST, mySTD); // local Tag for logging -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; void setup() { @@ -283,14 +284,14 @@ void setup() { // initialize LoRa #ifdef HAS_LORA strcat_P(features, " LORA"); -#endif assert(lora_stack_init() == ESP_OK); +#endif // initialize SPI #ifdef HAS_SPI strcat_P(features, " SPI"); -#endif assert(spi_init() == ESP_OK); +#endif #ifdef VENDORFILTER strcat_P(features, " OUIFLT"); @@ -361,10 +362,7 @@ void setup() { ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse timepulse_start(); -#ifdef TIME_SYNC_INTERVAL - setSyncInterval(TIME_SYNC_INTERVAL * 60); // controls timeStatus() via Time.h - setSyncProvider(time_sync); // is called by Time.h -#endif + timeSync(); // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 730d4d98..bea2f277 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -1,7 +1,7 @@ #include "rtctime.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; #ifdef HAS_RTC // we have hardware RTC diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 5a1bcae1..c1a54de1 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -1,57 +1,64 @@ #include "timekeeper.h" // Local logging tag -static const char TAG[] = "main"; +static const char TAG[] = __FILE__; // symbol to display current time source const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; getExternalTime TimeSourcePtr; // pointer to time source function -time_t time_sync() { - // check synchonization of systime, called by cyclic.cpp + +// syncs systime from external time source and sets/reads RTC, called by +// cyclic.cpp +void timeSync(void) { time_t t = 0; #ifdef HAS_GPS - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps - ESP_LOGD(TAG, "micros = %d", micros()); - t = get_gpstime(); - if (t) { - t++; // gps time concerns past second, so we add one second + // do we have a valid GPS time? + if (get_gpstime()) { // then let's sync GPS time on top of second + + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps + vTaskDelay(gpsDelay_ticks); + t = get_gpstime(); // fetch time from recent NEMA record + if (t) { + t++; // gps time concerns past second, so we add one + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps + setTime(t); #ifdef HAS_RTC - set_rtctime(t); // calibrate RTC + set_rtctime(t); // calibrate RTC #endif - timeSource = _gps; - goto exit; - } + timeSource = _gps; + goto exit; + } + } #endif // no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - t = get_rtctime(); - if (t) - timeSource = _rtc; - else - ESP_LOGW(TAG, "no confident RTC time"); + t = get_rtctime(); + if (t) { + setTime(t); + timeSource = _rtc; + } else + ESP_LOGW(TAG, "no confident RTC time"); #endif // try lora sync if we have #if defined HAS_LORA && defined TIME_SYNC_LORA - LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); + LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif exit: - ESP_LOGD(TAG, "micros = %d", micros()); - if (t) - ESP_LOGD(TAG, "Time was set by %c to %02d:%02d:%02d", - timeSetSymbols[timeSource], hour(t), minute(t), second(t)); - else - timeSource = _unsynced; - return t; + if (t) + ESP_LOGD(TAG, "Time was set by %c to %02d:%02d:%02d", + timeSetSymbols[timeSource], hour(t), minute(t), second(t)); + else + timeSource = _unsynced; -} // time_sync() +} // timeSync() // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { @@ -119,8 +126,6 @@ void IRAM_ATTR CLOCKIRQ(void) { // helper function to check plausibility of a time time_t TimeIsValid(time_t const 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() ? t : 0); } From b2a563c84ee893f6c40ceafbbe6853954e2efa3b Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 27 Feb 2019 00:52:45 +0100 Subject: [PATCH 24/27] spislave.cpp: compiler directives fixed --- src/spislave.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/spislave.cpp b/src/spislave.cpp index 5f6a1b60..ed6ab7b0 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -22,6 +22,8 @@ licenses. Refer to LICENSE.txt file in repository for more details. */ +#ifdef HAS_SPI + #include "spislave.h" #include @@ -102,9 +104,6 @@ void spi_slave_task(void *param) { } esp_err_t spi_init() { -#ifndef HAS_SPI - return ESP_OK; -#else assert(SEND_QUEUE_SIZE); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (SPISendQueue == 0) { @@ -146,13 +145,10 @@ esp_err_t spi_init() { } return ret; - -#endif } void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in SPI send queue -#ifdef HAS_SPI BaseType_t ret; switch (prio) { case prio_high: @@ -170,17 +166,12 @@ void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { } else { ESP_LOGW(TAG, "SPI sendqueue is full"); } -#endif } -void spi_queuereset(void) { -#ifdef HAS_SPI - xQueueReset(SPISendQueue); -#endif -} +void spi_queuereset(void) { xQueueReset(SPISendQueue); } void spi_housekeeping(void) { -#ifdef HAS_SPI ESP_LOGD(TAG, "spiloop %d bytes left", uxTaskGetStackHighWaterMark(spiTask)); -#endif } + +#endif // HAS_SPI \ No newline at end of file From c489123e0622eed96e3042f102b3184b4a5022a8 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 27 Feb 2019 22:40:58 +0100 Subject: [PATCH 25/27] timekeeper fixes (experimental) --- src/dcf77.cpp | 2 ++ src/gpsread.cpp | 12 ++++---- src/main.cpp | 4 +-- src/timekeeper.cpp | 76 +++++++++++++++++++++------------------------- 4 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 1c27235e..7a1e5ab3 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -22,6 +22,8 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { TickType_t startTime = xTaskGetTickCount(); uint8_t sec = second(t); + ESP_LOGD (TAG, "DCF second %d", sec); + // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 9c34049d..c0c67292 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -92,14 +92,14 @@ time_t get_gpstime(void) { time_t t = 0; - if ((gps.time.age() < gpsDelay_ms) && (gps.time.isValid())) { + if ((gps.time.age() < gpsDelay_ms) && (gps.time.isValid()) && (gps.date.isValid())) { - ESP_LOGD(TAG, "GPS time age: %dms, is valid: %s", gps.time.age(), - gps.time.isValid() ? "yes" : "no"); + ESP_LOGD(TAG, "GPS time age: %dms, second: %d, is valid: %s", gps.time.age(), gps.time.second(), + gps.time.isValid() ? "yes" : "no"); - t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), - gps.time.hour(), gps.time.minute(), gps.time.second()); - } + t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), + gps.time.hour(), gps.time.minute(), gps.time.second()); + } return TimeIsValid(t); } // get_gpstime() diff --git a/src/main.cpp b/src/main.cpp index eb04d063..ea1ea917 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,11 +27,11 @@ Uused tasks and timers: Task Core Prio Purpose ==================================================================================== -clockloop 0 4 generates realtime telegrams for external clock ledloop 0 3 blinks LEDs spiloop 0 2 reads/writes data on spi interface IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer +clockloop 1 4 generates realtime telegrams for external clock looptask 1 1 arduino core -> runs the LMIC LoRa stack irqhandler 1 1 executes tasks triggered by hw irq, see table below gpsloop 1 2 reads data from GPS via serial or i2c @@ -362,7 +362,6 @@ void setup() { ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse timepulse_start(); - timeSync(); // start wifi in monitor mode and start channel rotation timer ESP_LOGI(TAG, "Starting Wifi..."); @@ -417,6 +416,7 @@ void setup() { #if defined HAS_IF482 || defined HAS_DCF77 ESP_LOGI(TAG, "Starting Clock Controller..."); + timeSync(); clock_init(); #endif diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index c1a54de1..33efe357 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -8,7 +8,6 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; getExternalTime TimeSourcePtr; // pointer to time source function - // syncs systime from external time source and sets/reads RTC, called by // cyclic.cpp void timeSync(void) { @@ -16,47 +15,36 @@ void timeSync(void) { time_t t = 0; #ifdef HAS_GPS - // do we have a valid GPS time? - if (get_gpstime()) { // then let's sync GPS time on top of second - - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps - vTaskDelay(gpsDelay_ticks); - t = get_gpstime(); // fetch time from recent NEMA record - if (t) { - t++; // gps time concerns past second, so we add one - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps - setTime(t); + xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1100)); // wait for pps + t = get_gpstime(); // fetch recent time from last NEMA record + if (t) { + / t++; // last NMEA record concerns past second, so we add one + ESP_LOGD(TAG, "millis: %d, second: %d", millis(), second(t)); + setTime(t); #ifdef HAS_RTC - set_rtctime(t); // calibrate RTC + set_rtctime(t); // calibrate RTC #endif - timeSource = _gps; - goto exit; - } - } + timeSource = _gps; + return; + } #endif // no GPS -> fallback to RTC time while trying lora sync #ifdef HAS_RTC - t = get_rtctime(); - if (t) { - setTime(t); - timeSource = _rtc; - } else - ESP_LOGW(TAG, "no confident RTC time"); + t = get_rtctime(); + if (t) { + setTime(t); + timeSource = _rtc; + } #endif // try lora sync if we have #if defined HAS_LORA && defined TIME_SYNC_LORA - LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); + LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif -exit: - - if (t) - ESP_LOGD(TAG, "Time was set by %c to %02d:%02d:%02d", - timeSetSymbols[timeSource], hour(t), minute(t), second(t)); - else - timeSource = _unsynced; + if (!t) + timeSource = _unsynced; } // timeSync() @@ -66,7 +54,7 @@ uint8_t timepulse_init() { // use time pulse from GPS as time base with fixed 1Hz frequency #ifdef GPS_INT - // setup external interupt pin for GPS INT output + // setup external interupt pin for rising edge GPS INT pinMode(GPS_INT, INPUT_PULLDOWN); // setup external rtc 1Hz clock as pulse per second clock ESP_LOGI(TAG, "Timepulse: external (GPS)"); @@ -75,7 +63,7 @@ uint8_t timepulse_init() { // use pulse from on board RTC chip as time base with fixed frequency #elif defined RTC_INT - // setup external interupt pin for active low RTC INT output + // setup external interupt pin for falling edge RTC INT pinMode(RTC_INT, INPUT_PULLUP); // setup external rtc 1Hz clock as pulse per second clock @@ -114,12 +102,15 @@ void timepulse_start(void) { // interrupt service routine triggered by either pps or esp32 hardware timer void IRAM_ATTR CLOCKIRQ(void) { + if (ClockTask != NULL) - xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); + xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, NULL); + #if defined GPS_INT || defined RTC_INT - xSemaphoreGiveFromISR(TimePulse, NULL); + xSemaphoreGiveFromISR(TimePulse, &xHigherPriorityTaskWoken); TimePulseTick = !TimePulseTick; // flip ticker #endif + portYIELD_FROM_ISR(); } @@ -181,7 +172,7 @@ void clock_init(void) { (void *)1, // task parameter 4, // priority of the task &ClockTask, // task handle - 0); // CPU core + 1); // CPU core assert(ClockTask); // has clock task started? } // clock_init @@ -191,10 +182,11 @@ void clock_loop(void *pvParameters) { // ClockTask configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check TickType_t wakeTime; + uint32_t ppstime; 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 +#define t1(t) (t + DCF77_FRAME_SIZE + 1) // future minute for next DCF77 frame +#define t2(t) (t + 1) // future second after sync with 1pps trigger // preload first DCF frame before start #ifdef HAS_DCF77 @@ -204,14 +196,14 @@ void clock_loop(void *pvParameters) { // ClockTask // output time telegram for second following sec beginning with timepulse for (;;) { - xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, + xTaskNotifyWait(0x00, ULONG_MAX, &ppstime, portMAX_DELAY); // wait for timepulse // no confident time -> suppress clock output if (timeStatus() == timeNotSet) continue; - t = now(); // payload to send to clock + t = time_t(ppstime); #if defined HAS_IF482 @@ -222,8 +214,10 @@ void clock_loop(void *pvParameters) { // ClockTask if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? DCFpulse = DCF77_Frame(t1(t)); // generate next frame - if (DCFpulse[DCF77_FRAME_SIZE] == - minute(t1(t))) // have recent frame? (pulses could be missed!) + if (DCFpulse[DCF77_FRAME_SIZE] != + minute(t1(t))) // have recent frame? (timepulses could be missed!) + continue; + else DCF77_Pulse(t2(t), DCFpulse); // then output next second of this frame #endif From 19ffae05e65ab62a7cf859db861eba1180ec3652 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Thu, 28 Feb 2019 17:25:50 +0100 Subject: [PATCH 26/27] bugfix timekeeper --- platformio.ini | 10 +++++----- src/hal/ttgobeam.h | 24 ++++++++++++++---------- src/hal/ttgofox.h | 9 +++++---- src/paxcounter.conf | 2 +- src/timekeeper.cpp | 4 ++-- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/platformio.ini b/platformio.ini index ee5fc357..8e36506e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = generic +;env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -16,7 +16,7 @@ env_default = generic ;env_default = ttgov21old ;env_default = ttgov21new ;env_default = ttgobeam -;env_default = ttgofox +env_default = ttgofox ;env_default = lopy ;env_default = lopy4 ;env_default = fipy @@ -30,16 +30,16 @@ 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.32 +release_version = 1.7.323 ; 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 = 3 +debug_level = 4 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom extra_scripts = pre:build.py keyfile = ota.conf -platform_espressif32 = espressif32@1.6.0 +platform_espressif32 = espressif32@1.7.0 ;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage board_build.partitions = min_spiffs.csv monitor_speed = 115200 diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index 3052468e..d4d1730e 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -21,24 +21,28 @@ #define BATT_FACTOR 2 // voltage divider 100k/100k on board // GPS settings -//#define HAS_GPS 1 // use on board GPS -//#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M -//#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34 +#define HAS_GPS 1 // use on board GPS +#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M +#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34 + +// Settings for on board DS3231 RTC chip +//#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL +//#define RTC_INT GPIO_NUM_13 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus -//#define HAS_BME SDA, SCL -//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! +#define HAS_BME SDA, SCL +#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! // display (if connected) -//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C -//#define MY_OLED_SDA SDA -//#define MY_OLED_SCL SCL -//#define MY_OLED_RST U8X8_PIN_NONE +#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C +#define MY_OLED_SDA SDA +#define MY_OLED_SCL SCL +#define MY_OLED_RST U8X8_PIN_NONE //#define DISPLAY_FLIP 1 // use if display is rotated // Settings for DCF77 interface -//#define HAS_DCF77 GPIO_NUM_13 +#define HAS_DCF77 GPIO_NUM_13 // Settings for IF482 interface //#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters diff --git a/src/hal/ttgofox.h b/src/hal/ttgofox.h index 7b23aedc..32ddb874 100644 --- a/src/hal/ttgofox.h +++ b/src/hal/ttgofox.h @@ -28,12 +28,13 @@ //#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters // Settings for DCF77 interface -//#define HAS_DCF77 GPIO_NUM_14 -//#define DCF77_ACTIVE_LOW 1 +#define HAS_DCF77 GPIO_NUM_14 +#define DCF77_ACTIVE_LOW 1 // Settings for external GPS chip -//#define HAS_GPS 1 // use on board GPS -//#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration +#define HAS_GPS 1 // use on board GPS +#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration +#define GPS_INT GPIO_NUM_13 // Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK (5) diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 766688eb..98087310 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -66,7 +66,7 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node with external time source -#define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source (GPS/LORA) [default = 60], comment out means off +#define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], comment out means off //#define TIME_SYNC_LORA 1 // use LORA network as time source, comment out means off [default = off] // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 33efe357..232b25b1 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -18,7 +18,7 @@ void timeSync(void) { xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1100)); // wait for pps t = get_gpstime(); // fetch recent time from last NEMA record if (t) { - / t++; // last NMEA record concerns past second, so we add one + t++; // last NMEA record concerns past second, so we add one ESP_LOGD(TAG, "millis: %d, second: %d", millis(), second(t)); setTime(t); #ifdef HAS_RTC @@ -107,7 +107,7 @@ void IRAM_ATTR CLOCKIRQ(void) { xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, NULL); #if defined GPS_INT || defined RTC_INT - xSemaphoreGiveFromISR(TimePulse, &xHigherPriorityTaskWoken); + xSemaphoreGiveFromISR(TimePulse, NULL); TimePulseTick = !TimePulseTick; // flip ticker #endif From d2530ba91acfd08879093f502557c2d466ec5305 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 2 Mar 2019 13:32:02 +0100 Subject: [PATCH 27/27] timekeeper changes (experimental) --- include/timekeeper.h | 6 +++--- lib/microTime | 1 + src/gpsread.cpp | 2 +- src/irqhandler.cpp | 1 - src/lorawan.cpp | 2 +- src/main.cpp | 5 ++++- src/rtctime.cpp | 4 ++-- src/timekeeper.cpp | 41 ++++++++++++++++++++++------------------- 8 files changed, 34 insertions(+), 28 deletions(-) create mode 160000 lib/microTime diff --git a/include/timekeeper.h b/include/timekeeper.h index ae58df3d..410d7dec 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -3,6 +3,7 @@ #include "globals.h" #include "rtctime.h" +#include "TimeLib.h" #ifdef HAS_GPS #include "gpsread.h" @@ -18,11 +19,10 @@ extern const char timeSetSymbols[]; void IRAM_ATTR CLOCKIRQ(void); void clock_init(void); void clock_loop(void *pvParameters); -void timeSync(void); void timepulse_start(void); uint8_t timepulse_init(void); -time_t TimeIsValid(time_t const t); -time_t syncProvider_CB(void); +time_t timeIsValid(time_t const t); +time_t timeProvider(void); time_t compiledUTC(void); time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss); diff --git a/lib/microTime b/lib/microTime new file mode 160000 index 00000000..6d5b82c5 --- /dev/null +++ b/lib/microTime @@ -0,0 +1 @@ +Subproject commit 6d5b82c554590f49864cc44ce295ec91dcf1114e diff --git a/src/gpsread.cpp b/src/gpsread.cpp index c0c67292..afa53888 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -100,7 +100,7 @@ time_t get_gpstime(void) { t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); } - return TimeIsValid(t); + return timeIsValid(t); } // get_gpstime() // GPS serial feed FreeRTos Task diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 1236225c..a2af0cf7 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -32,7 +32,6 @@ void irqHandler(void *pvParameters) { // are cyclic tasks due? if (InterruptStatus & CYCLIC_IRQ) { doHousekeeping(); - timeSync(); } // is time to send the payload? diff --git a/src/lorawan.cpp b/src/lorawan.cpp index df73da4f..e9efe4ca 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -464,7 +464,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, *pUserUTCTime += requestDelaySec; // Update system time with time read from the network - if (TimeIsValid(*pUserUTCTime)) { + if (timeIsValid(*pUserUTCTime)) { xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps setTime(*pUserUTCTime + 1); timeSource = _lora; diff --git a/src/main.cpp b/src/main.cpp index ea1ea917..2fa91e8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -414,9 +414,12 @@ void setup() { #endif #endif // HAS_BUTTON + // set time source + setSyncInterval(TIME_SYNC_INTERVAL * 60); + setSyncProvider(&timeProvider); + #if defined HAS_IF482 || defined HAS_DCF77 ESP_LOGI(TAG, "Starting Clock Controller..."); - timeSync(); clock_init(); #endif diff --git a/src/rtctime.cpp b/src/rtctime.cpp index bea2f277..7fc1edbd 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -29,7 +29,7 @@ uint8_t rtc_init(void) { RtcDateTime tt = Rtc.GetDateTime(); time_t t = tt.Epoch32Time(); // sec2000 -> epoch - if (!Rtc.IsDateTimeValid() || !TimeIsValid(t)) { + 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 @@ -67,7 +67,7 @@ time_t get_rtctime(void) { } I2C_MUTEX_UNLOCK(); } - return TimeIsValid(t); + return timeIsValid(t); } // get_rtctime() float get_rtctemp(void) { diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 232b25b1..5b75b637 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -8,24 +8,22 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; getExternalTime TimeSourcePtr; // pointer to time source function -// syncs systime from external time source and sets/reads RTC, called by -// cyclic.cpp -void timeSync(void) { +time_t timeProvider(void) { + + ESP_LOGD(TAG, "time synched"); time_t t = 0; #ifdef HAS_GPS - xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1100)); // wait for pps + // xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1100)); // wait for pps t = get_gpstime(); // fetch recent time from last NEMA record if (t) { - t++; // last NMEA record concerns past second, so we add one - ESP_LOGD(TAG, "millis: %d, second: %d", millis(), second(t)); - setTime(t); + // t++; // last NMEA record concerns past second, so we add one #ifdef HAS_RTC set_rtctime(t); // calibrate RTC #endif timeSource = _gps; - return; + return t; } #endif @@ -33,12 +31,11 @@ void timeSync(void) { #ifdef HAS_RTC t = get_rtctime(); if (t) { - setTime(t); timeSource = _rtc; } #endif -// try lora sync if we have +// kick off asychron lora sync if we have #if defined HAS_LORA && defined TIME_SYNC_LORA LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif @@ -46,7 +43,9 @@ void timeSync(void) { if (!t) timeSource = _unsynced; -} // timeSync() + return t; + +} // timeProvider() // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { @@ -103,6 +102,8 @@ void timepulse_start(void) { // interrupt service routine triggered by either pps or esp32 hardware timer void IRAM_ATTR CLOCKIRQ(void) { + SyncToPPS(); // calibrate systime from Time.h + if (ClockTask != NULL) xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, NULL); @@ -115,15 +116,15 @@ void IRAM_ATTR CLOCKIRQ(void) { } // helper function to check plausibility of a time -time_t TimeIsValid(time_t const t) { +time_t timeIsValid(time_t const t) { // is it a time in the past? we use compile date to guess return (t >= compiledUTC() ? t : 0); } // helper function to convert compile time to UTC time time_t compiledUTC(void) { - time_t t = RtcDateTime(__DATE__, __TIME__).Epoch32Time(); - return myTZ.toUTC(t); + static time_t t = myTZ.toUTC(RtcDateTime(__DATE__, __TIME__).Epoch32Time()); + return t; } // helper function to convert gps date/time into time_t @@ -182,7 +183,7 @@ void clock_loop(void *pvParameters) { // ClockTask configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check TickType_t wakeTime; - uint32_t ppstime; + uint32_t printtime; time_t t; #define t1(t) (t + DCF77_FRAME_SIZE + 1) // future minute for next DCF77 frame @@ -196,18 +197,19 @@ void clock_loop(void *pvParameters) { // ClockTask // output time telegram for second following sec beginning with timepulse for (;;) { - xTaskNotifyWait(0x00, ULONG_MAX, &ppstime, + xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); // wait for timepulse // no confident time -> suppress clock output if (timeStatus() == timeNotSet) continue; - t = time_t(ppstime); + t = time_t(printtime); #if defined HAS_IF482 - IF482_Pulse(t2(t)); // next second + // IF482_Pulse(t2(t)); // next second + IF482_Pulse(t); // next second #elif defined HAS_DCF77 @@ -218,7 +220,8 @@ void clock_loop(void *pvParameters) { // ClockTask minute(t1(t))) // have recent frame? (timepulses could be missed!) continue; else - DCF77_Pulse(t2(t), DCFpulse); // then output next second of this frame + // DCF77_Pulse(t2(t), DCFpulse); // then output next second of this frame + DCF77_Pulse(t, DCFpulse); // then output next second of this frame #endif