diff --git a/include/clockcontroller.h b/include/clockcontroller.h new file mode 100644 index 00000000..c9748ce8 --- /dev/null +++ b/include/clockcontroller.h @@ -0,0 +1,15 @@ +#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/dcf77.h b/include/dcf77.h index cd06ea5a..f2693246 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -2,16 +2,12 @@ #define _DCF77_H #include "globals.h" -#include "rtctime.h" enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; enum dcf_pinstate { dcf_low, dcf_high }; -int dcf77_init(void); -void dcf77_loop(void *pvParameters); -void sendDCF77(void); -void DCF_Out(uint8_t startsec); -void generateTimeframe(time_t t); +void DCF_Pulse(time_t startTime); +void 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[]); diff --git a/include/globals.h b/include/globals.h index fdca3b5e..753fb281 100644 --- a/include/globals.h +++ b/include/globals.h @@ -40,11 +40,17 @@ #define SCREEN_MODE (0x80) // I2C bus access control -#define I2C_MUTEX_LOCK() xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == pdTRUE -#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) +#define I2C_MUTEX_LOCK() \ + xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == \ + pdTRUE +#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) -// time pulse frequency 1Hz -#define PPS (1000) +// Clock controller settings +#define PPS (1000) // on board time pulse frequency in ms +#define IF482_FRAME_SIZE (17) +#define IF482_PULSE_LENGTH (1000) +#define DCF77_FRAME_SIZE (60) +#define DCF77_PULSE_LENGTH (100) // Struct holding devices's runtime configuration typedef struct { @@ -66,7 +72,8 @@ typedef struct { uint8_t runmode; // 0=normal, 1=update uint8_t payloadmask; // bitswitches for payload data char version[10]; // Firmware version - uint8_t bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor + uint8_t + bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor } configData_t; // Struct holding payload for data send queue @@ -151,12 +158,4 @@ extern Timezone myTZ; #include "bme680mems.h" #endif -#ifdef HAS_IF482 -#include "if482.h" -#endif - -#ifdef HAS_DCF77 -#include "dcf77.h" -#endif - #endif \ No newline at end of file diff --git a/include/if482.h b/include/if482.h index b38013d8..19f1f256 100644 --- a/include/if482.h +++ b/include/if482.h @@ -2,11 +2,12 @@ #define _IF482_H #include "globals.h" -#include "rtctime.h" -int if482_init(void); -void if482_loop(void *pvParameters); -TickType_t tx_time(unsigned long baud, uint32_t config, int8_t rxPin, +extern HardwareSerial IF482; + +void IF482_Pulse(time_t t); +String 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/main.h b/include/main.h index 4f4452b7..a3666baf 100644 --- a/include/main.h +++ b/include/main.h @@ -17,5 +17,5 @@ #include "led.h" #include "spislave.h" #include "lorawan.h" - +#include "clockcontroller.h" #endif \ No newline at end of file diff --git a/include/rtctime.h b/include/rtctime.h index 50f95539..8b617f45 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -24,6 +24,7 @@ float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); int timepulse_init(void); void timepulse_start(); -void sync_clock(void); +int sync_TimePulse(void); +int sync_SysTime(time_t); #endif // _RTCTIME_H \ No newline at end of file diff --git a/src/clockcontroller.cpp b/src/clockcontroller.cpp new file mode 100644 index 00000000..91236213 --- /dev/null +++ b/src/clockcontroller.cpp @@ -0,0 +1,72 @@ +#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 + +#if (PPS < IF482_PULSE_LENGTH) || (PPS < DCF77_PULSE_LENGTH) +#error On board timepulse too fast for clockcontroller +#endif + +// Local logging tag +static const char TAG[] = "main"; + +void clock_init(void) { // ClockTask + + timepulse_init(); // setup timepulse + +// setup output interface +#ifdef HAS_IF482 + // initialize and configure IF482 Generator + IF482.begin(HAS_IF482); +#elif defined HAS_DCF77 + // initialize and configure DCF77 output + pinMode(HAS_DCF77, OUTPUT); + set_DCF77_pin(dcf_low); +#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? + timepulse_start(); // start pulse +} // clock_init + +void clock_loop(void *pvParameters) { // ClockTask + + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check + + TickType_t wakeTime, txDelay; + uint32_t pulseCycle; + void (*pTimeTx)(time_t); // pointer to time telegram output function + +#ifdef HAS_IF482 + txDelay = pdMS_TO_TICKS(1000) - tx_Ticks(HAS_IF482); + pulseCycle = PPS / IF482_PULSE_LENGTH; + pTimeTx = IF482_Pulse; +#elif defined HAS_DCF77 + txDelay = pdMS_TO_TICKS(DCF77_PULSE_LENGTH); + pulseCycle = PPS / DCF77_PULSE_LENGTH; + pTimeTx = DCF_Pulse; +#endif + + // output time telegram triggered by timepulse + for (;;) { + if (timeStatus() == timeSet) // do we have valid time? + xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, + portMAX_DELAY); // wait for timepulse + for (uint8_t i = 1; i <= pulseCycle; i++) { + pTimeTx(now()); + vTaskDelayUntil(&wakeTime, txDelay); + } + } // for +} // clock_loop() + +#endif // HAS_DCF77 || HAS_IF482 \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index e20d2948..da42af4e 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -41,7 +41,8 @@ void doHousekeeping() { if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) { nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC * 60000; // set up next time sync period - if (!set_rtctime(now())) // epoch time + sync_TimePulse(); // wait for next start of second + if (!set_rtctime(now())) // epoch time ESP_LOGE(TAG, "RTC set time failure"); else ESP_LOGI(TAG, "RTC time updated"); diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 4ab18fca..f63965bf 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -8,76 +8,41 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ #ifdef HAS_DCF77 -#ifdef IF_482 -#error You must define at most one of IF482 or DCF77! -#endif - #include "dcf77.h" // Local logging tag static const char TAG[] = "main"; -bool volatile BitsPending = false; - -#define DCF77_FRAME_SIZE (60) -#define DCF77_PULSE_DURATION (100) - // array of dcf pulses for three minutes -uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; +uint8_t DCFpulse[DCF77_FRAME_SIZE]; -// initialize and configure DCF77 output -int dcf77_init(void) { +// called by timepulse interrupt to ticker out DCF signal +void DCF_Pulse(time_t startTime) { - pinMode(HAS_DCF77, OUTPUT); - set_DCF77_pin(dcf_low); - timepulse_init(); // setup timepulse - - xTaskCreatePinnedToCore(dcf77_loop, // task function - "dcf77loop", // name of task - 2048, // stack size of task - (void *)1, // parameter of the task - 3, // priority of the task - &ClockTask, // task handle - 0); // CPU core - - assert(ClockTask); // has clock task started? - DCF_Out(second(now())); // sync DCF time on next second - timepulse_start(); // start pulse - - return 1; // success -} // ifdcf77_init - -// called every 100msec by hardware timer to pulse out DCF signal -void DCF_Out(uint8_t startOffset_sec) { - - static uint8_t bit = startOffset_sec; + static uint8_t current_second = second(startTime); static uint8_t pulse = 0; -#ifdef TIME_SYNC_INTERVAL_DCF - static uint32_t nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * 60000; -#endif + static bool SecondsPending = false; - if (!BitsPending) { - // do we have confident time/date? - if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) { - // prepare frame to send for next minute - generateTimeframe(now() + DCF77_FRAME_SIZE + 1); - // kick off output of telegram - BitsPending = true; - } else - return; + if (!SecondsPending) { + // prepare dcf timeframe to send for next minute + DCF77_Frame(now() + DCF77_FRAME_SIZE + 1); + ESP_LOGD(TAG, "DCF77 minute %d", minute(now() + DCF77_FRAME_SIZE + 1)); + // begin output of dcf timeframe + SecondsPending = true; } // ticker out current DCF frame - if (BitsPending) { + if (SecondsPending) { switch (pulse++) { case 0: // start of second -> start of timeframe for logic signal - if (DCFtimeframe[bit] != dcf_off) + if (DCFpulse[current_second] != dcf_off) set_DCF77_pin(dcf_low); + ESP_LOGD(TAG, "DCF77 bit %d", current_second); break; case 1: // 100ms after start of second -> end of timeframe for logic 0 - if (DCFtimeframe[bit] == dcf_zero) + if (DCFpulse[current_second] == dcf_zero) set_DCF77_pin(dcf_high); break; @@ -87,53 +52,17 @@ void DCF_Out(uint8_t startOffset_sec) { case 9: // 900ms after start -> last pulse before next second starts pulse = 0; - if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) + if (current_second++ >= + (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) { - bit = 0; - BitsPending = false; -// recalibrate clock after a fixed timespan, do this in 59th second -#ifdef TIME_SYNC_INTERVAL_DCF - if ((millis() >= nextDCFsync)) { - sync_clock(); // waiting for second 59 - nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * - 60000; // set up next time sync period - } -#endif + current_second = 0; + SecondsPending = false; }; break; }; // switch }; // if -} // DCF_Out() - -void dcf77_loop(void *pvParameters) { - - configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - - TickType_t wakeTime; - - // task remains in blocked state until it is notified by isr - for (;;) { - xTaskNotifyWait( - 0x00, // don't clear any bits on entry - ULONG_MAX, // clear all bits on exit - &wakeTime, // receives moment of call from isr - portMAX_DELAY); // wait forever (missing error handling here...) - - // select clock scale -#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling - DCF_Out(0); -#elif (PPS > DCF77_PULSE_DURATION) // we need upclocking - for (uint8_t i = 1; i <= PPS / DCF77_PULSE_DURATION; i++) { - DCF_Out(0); - vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION)); - } -#else // we need downclocking, not yet implemented -#error Timepulse too fast for DCF77 emulator -#endif - - } // for -} // dcf77_loop() +} // DCF_Pulse() // helper function to convert decimal to bcd digit uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, @@ -151,40 +80,38 @@ uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, return parity; } -void generateTimeframe(time_t tt) { +void DCF77_Frame(time_t tt) { - uint8_t ParityCount; + uint8_t Parity; time_t t = myTZ.toLocal(tt); // convert to local time // ENCODE HEAD // bits 0..19 initialized with zeros for (int n = 0; n <= 19; n++) - DCFtimeframe[n] = dcf_zero; + DCFpulse[n] = dcf_zero; // bits 17..18: adjust for DayLightSaving - DCFtimeframe[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; + DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; // bit 20: must be 1 to indicate time active - DCFtimeframe[20] = dcf_one; + DCFpulse[20] = dcf_one; // ENCODE MINUTE (bits 21..28) - ParityCount = dec2bcd(minute(t), 21, 27, DCFtimeframe); - DCFtimeframe[28] = (ParityCount & 1) ? dcf_one : dcf_zero; + Parity = dec2bcd(minute(t), 21, 27, DCFpulse); + DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero; // ENCODE HOUR (bits 29..35) - ParityCount = dec2bcd(hour(t), 29, 34, DCFtimeframe); - DCFtimeframe[35] = (ParityCount & 1) ? dcf_one : dcf_zero; + Parity = dec2bcd(hour(t), 29, 34, DCFpulse); + DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero; // ENCODE DATE (bits 36..58) - ParityCount = dec2bcd(day(t), 36, 41, DCFtimeframe); - ParityCount += - dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFtimeframe); - ParityCount += dec2bcd(month(t), 45, 49, DCFtimeframe); - ParityCount += - dec2bcd(year(t) - 2000, 50, 57, - DCFtimeframe); // yes, we have a millenium 3000 bug here ;-) - DCFtimeframe[58] = (ParityCount & 1) ? dcf_one : dcf_zero; + 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 ;-) + DCFpulse[58] = (Parity & 1) ? dcf_one : dcf_zero; // ENCODE TAIL (bit 59) - DCFtimeframe[59] = dcf_off; + DCFpulse[59] = dcf_off; // !! missing code here for leap second !! /* @@ -192,7 +119,7 @@ void generateTimeframe(time_t tt) { char out[DCF77_FRAME_SIZE + 1]; uint8_t i; for (i = 0; i < DCF77_FRAME_SIZE; i++) { - out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii + out[i] = DCFpulse[i] + '0'; // convert int digit to printable ascii } out[DCF77_FRAME_SIZE] = '\0'; // string termination char ESP_LOGD(TAG, "DCF Timeframe = %s", out); @@ -219,4 +146,6 @@ void set_DCF77_pin(dcf_pinstate state) { } // switch } // DCF77_pulse +// helper function calculates next minute + #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index 8ff2b83e..07afdebb 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -139,7 +139,7 @@ void refreshtheDisplay() { u8x8.setPowerSave(!cfg.screenon); } - // if display is switched off we don't refresh it and save time + // if display is switched off we don't refresh it to relax cpu if (!DisplayState) return; @@ -220,7 +220,7 @@ void refreshtheDisplay() { #if (!defined HAS_DCF77) && (!defined HAS_IF482) // update LoRa status display (line 6) u8x8.printf("%-16s", display_line6); -#else // we want a time display instead LoRa status +#else // we want a systime display instead LoRa status time_t t = myTZ.toLocal(now()); char timeState = (timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol; diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 604216a5..622920b2 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -88,8 +88,8 @@ time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, // function to fetch current time from gps time_t get_gpstime(void) { - // !! never call now() in this function, this would break this function - // to be used as SyncProvider due to recursive call to now() + // !! never call now() or delay in this function, this would break this + // function to be used as SyncProvider for Time.h if ((gps.time.age() < 1500) && (gps.time.isValid())) { // get current gps time @@ -98,9 +98,7 @@ time_t get_gpstime(void) { gps.time.hour(), gps.time.minute(), gps.time.second()); ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t)); - // sync on top of next second by timepulse - sync_clock(); - return t + 1; + return t; } else { ESP_LOGW(TAG, "GPS has no confident time"); return 0; // sync failure, 0 effects calling SyncProvider() to not set time diff --git a/src/if482.cpp b/src/if482.cpp index a7fe5015..57ee66c5 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -79,46 +79,21 @@ not evaluated by model BU-190 #ifdef HAS_IF482 -#ifdef HAS_DCF77 -#error You must define at most one of IF482 or DCF77! -#endif - #include "if482.h" // Local logging tag static const char TAG[] = "main"; -#define IF482_FRAME_SIZE (17) -#define IF482_PULSE_DURATION (1000) - HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) -// initialize and configure IF482 Generator -int if482_init(void) { +// called by timepulse interrupt to ticker out DCF signal +void IF482_Pulse(time_t startTime) { + IF482.print(IF482_Frame(startTime + 1)); // if482 telegram for next second +} - // open serial interface - IF482.begin(HAS_IF482); - // setup timepulse - timepulse_init(); +String IF482_Frame(time_t startTime) { - // start if482 serial output feed task - xTaskCreatePinnedToCore(if482_loop, // task function - "if482loop", // name of task - 2048, // stack size of task - (void *)1, // parameter of the task - 4, // priority of the task - &ClockTask, // task handle - 0); // CPU core - - assert(ClockTask); // has clock task started? - // timepulse_start(); // start pulse - - return 1; // success -} // if482_init - -String IF482_Out(time_t tt) { - - time_t t = myTZ.toLocal(tt); + time_t t = myTZ.toLocal(startTime); char mon, buf[14], out[IF482_FRAME_SIZE]; switch (timeStatus()) { // indicates if time has been set and recently synced @@ -134,7 +109,7 @@ String IF482_Out(time_t tt) { } // switch // do we have confident time/date? - if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) + if (timeStatus() == timeSet) 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)); else @@ -146,62 +121,9 @@ String IF482_Out(time_t tt) { return out; } -void if482_loop(void *pvParameters) { - - configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - - TickType_t wakeTime; - const TickType_t tTx = tx_time(HAS_IF482); // duration of telegram transmit - - // phase 1: sync task on top of second - - const TickType_t t0 = xTaskGetTickCount(); // moment of start top of second - sync_clock(); // delay until top of second - timepulse_start(); // start timepulse - - xTaskNotifyWait( - 0x00, // don't clear any bits on entry - ULONG_MAX, // clear all bits on exit - &wakeTime, // receives moment of call from isr - portMAX_DELAY); // wait forever (missing error handling here...) - - const TickType_t tOffset = wakeTime - t0; - const TickType_t tShot = - (tOffset < tTx) ? (1000 - tOffset - tTx) : (tOffset - tTx); - - ESP_LOGI(TAG, "IF482 signal synced with clock, tShot=%dms", tShot); - - // phase 2: sync task on time pulse interrupt - for (;;) { - xTaskNotifyWait( - 0x00, // don't clear any bits on entry - ULONG_MAX, // clear all bits on exit - &wakeTime, // receives moment of call from isr - portMAX_DELAY); // wait forever (missing error handling here...) - -// select clock scale -#if (PPS == IF482_PULSE_DURATION) // we don't need clock rescaling - // wait until it's time to start transmit telegram for next second - vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of tShot - IF482.print(IF482_Out(now() + 1)); - -#elif (PPS > IF482_PULSE_DURATION) // we need upclocking - for (uint8_t i = 1; i <= PPS / IF482_PULSE_DURATION; i++) { - vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of shot - IF482.print(IF482_Out(now() + 1)); - } - -#else // we need downclocking, not yet implemented -#error Timepulse too fast for IF482 generator -#endif - - } // forever - -} // if482_loop() - // calculate serial tx time from IF482 serial settings -TickType_t tx_time(unsigned long baud, uint32_t config, int8_t rxPin, - int8_t txPins) { +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 stopbits = ((config & 0x20) >> 5) + 1; diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index fbbf06fa..3f680702 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -16,7 +16,7 @@ void irqHandler(void *pvParameters) { 0x00, // Don't clear any bits on entry ULONG_MAX, // Clear all bits on exit &InterruptStatus, // Receives the notification value - portMAX_DELAY); // wait forever (missing error handling here...) + portMAX_DELAY); // wait forever // button pressed? #ifdef HAS_BUTTON diff --git a/src/lorawan.cpp b/src/lorawan.cpp index edc0d630..b0ea74e0 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -6,7 +6,7 @@ static const char TAG[] = "lora"; #ifdef HAS_LORA -#if CLOCK_ERROR_PROCENTAGE > 7 +#if CLOCK_ERROR_PROCENTAGE > 7 #warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects #endif @@ -408,7 +408,7 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { BaseType_t ret; switch (prio) { case prio_high: - ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); break; case prio_low: case prio_normal: @@ -471,7 +471,8 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, setTime(*pUserUTCTime); ESP_LOGI(TAG, "LoRaWAN network has set the system time"); #ifdef HAS_RTC - if (!set_rtctime(*pUserUTCTime)) // epoch time + sync_TimePulse(); // wait for next start of second + if (!set_rtctime(*pUserUTCTime+1)) // epoch time ESP_LOGE(TAG, "RTC set time failure"); #endif } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index e0754d49..ff4625b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -338,6 +338,7 @@ void setup() { #ifdef HAS_RTC strcat_P(features, " RTC"); assert(rtc_init()); + sync_TimePulse(); // wait for next start of second setSyncProvider(get_rtctime); // sync time now and then if (timeStatus() != timeSet) ESP_LOGI(TAG, "Unable to sync system time with RTC"); @@ -416,6 +417,7 @@ void setup() { #endif // HAS_BUTTON #ifdef HAS_GPS + sync_TimePulse(); // wait for next start of second setSyncProvider(get_gpstime); // sync time now and then if (timeStatus() != timeSet) ESP_LOGI(TAG, "Unable to sync system time with GPS"); @@ -429,12 +431,9 @@ void setup() { setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); #endif -#ifdef HAS_IF482 - ESP_LOGI(TAG, "Starting IF482 Generator..."); - assert(if482_init()); -#elif defined HAS_DCF77 - ESP_LOGI(TAG, "Starting DCF77 Generator..."); - assert(dcf77_init()); +#if defined HAS_IF482 || defined HAS_DCF77 + ESP_LOGI(TAG, "Starting Clock Controller..."); + clock_init(); #endif } // setup() diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 555a0491..1b215647 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -82,11 +82,10 @@ #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 source GPS [default = 5], comment out means off +#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_WRITE_INTERVAL_RTC 60 // write time each .. minutes from GPS/LORA to 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_DCF 60 // sync DCF signal time each .. minutes from internal time [default = 60], comment out means 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 d5e10163..f1ced44f 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -16,7 +16,7 @@ int timepulse_init() { // 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: GPS timepulse"); + ESP_LOGI(TAG, "Time base: external (GPS)"); return 1; // success // use pulse from on board RTC chip as time base with fixed frequency @@ -30,7 +30,7 @@ int timepulse_init() { Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); I2C_MUTEX_UNLOCK(); - ESP_LOGI(TAG, "Time base: external RTC timepulse"); + ESP_LOGI(TAG, "Time base: external (RTC)"); return 1; // success } else { ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); @@ -41,9 +41,9 @@ int timepulse_init() { #else // use ESP32 hardware timer as time base with adjustable frequency clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec - timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); + //timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); timerAlarmWrite(clockCycle, 10000, true); // 1000ms - ESP_LOGI(TAG, "Time base: ESP32 hardware timer"); + ESP_LOGI(TAG, "Time base: internal (ESP32 hardware timer)"); return 1; // success #endif @@ -55,18 +55,30 @@ void timepulse_start(void) { #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 } -// helper function to sync time_t of top of a second -void sync_clock(void) { +// helper function to sync systime on start of next second +int sync_SysTime(time_t t) { + if (sync_TimePulse()) { + setTime(t + 1); + ESP_LOGD(TAG, "Systime synced on timepulse"); + return 1; // success + } else + return 0; // failure +} + +// 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(PPS)) == pdTRUE) - ESP_LOGI(TAG, "clock synced by timepulse"); + if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(PPS)) == pdTRUE) { + return 1; + } // success else - ESP_LOGW(TAG, "Missing timepulse, clock not synced"); - return; + ESP_LOGW(TAG, "Missing timepulse, time not synced"); + return 0; // failure } // interrupt service routine triggered by either rtc pps or esp32 hardware @@ -136,7 +148,6 @@ error: int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 if (I2C_MUTEX_LOCK()) { - sync_clock(); // wait for top of second Rtc.SetDateTime(RtcDateTime(t)); I2C_MUTEX_UNLOCK(); // release i2c bus access ESP_LOGI(TAG, "RTC calibrated"); @@ -151,7 +162,8 @@ int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970 } time_t get_rtctime(void) { - // never call now() in this function, this would cause a recursion! + // !! 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; // block i2c bus access if (I2C_MUTEX_LOCK()) {