ESP32-PaxCounter/src/timekeeper.cpp

244 lines
6.9 KiB
C++
Raw Normal View History

2019-02-24 01:44:55 +01:00
#include "timekeeper.h"
2019-02-21 23:17:01 +01:00
// Local logging tag
static const char TAG[] = "main";
2019-02-24 15:08:41 +01:00
// symbol to display current time source
const char timeSetSymbols[] = {'G', 'R', 'L', '?'};
2019-02-24 23:13:15 +01:00
getExternalTime TimeSourcePtr; // pointer to time source function
2019-02-21 23:17:01 +01:00
void time_sync() {
2019-02-24 23:13:15 +01:00
// check synchonization of systime, called by cyclic.cpp
2019-02-21 23:17:01 +01:00
#ifdef TIME_SYNC_INTERVAL
2019-02-24 23:13:15 +01:00
if (timeStatus() == timeSet) // timeStatus() is flipped in Time.h
2019-02-24 01:44:55 +01:00
return;
2019-02-21 23:17:01 +01:00
#ifdef HAS_GPS
2019-02-24 23:13:15 +01:00
if (syncTime(get_gpstime, _gps))
2019-02-24 01:44:55 +01:00
return; // attempt sync with GPS time
2019-02-21 23:17:01 +01:00
#endif
2019-02-23 23:39:45 +01:00
2019-02-24 13:47:18 +01:00
// no GPS -> fallback to RTC time while trying lora sync
2019-02-24 01:44:55 +01:00
#ifdef HAS_RTC
2019-02-24 23:13:15 +01:00
if (!syncTime(get_rtctime, _rtc)) // sync with RTC time
2019-02-24 01:44:55 +01:00
ESP_LOGW(TAG, "no confident RTC time");
2019-02-22 23:17:28 +01:00
#endif
2019-02-21 23:17:01 +01:00
2019-02-24 01:44:55 +01:00
// try lora sync if we have
#if defined HAS_LORA && defined TIME_SYNC_LORA
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
2019-02-21 23:17:01 +01:00
#endif
#endif // TIME_SYNC_INTERVAL
} // time_sync()
2019-02-24 23:13:15 +01:00
// 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
2019-02-22 22:28:35 +01:00
if (TimeIsValid(t)) {
2019-02-24 23:13:15 +01:00
if (caller == _gps) // gps time concerns past second
t++;
setTime(t); // flips timeStatus() in Time.h
2019-02-24 15:08:41 +01:00
timeSource = caller;
ESP_LOGD(TAG, "Time source %c set time to %02d:%02d:%02d",
timeSetSymbols[timeSource], hour(t), minute(t), second(t));
2019-02-24 23:13:15 +01:00
2019-02-24 01:44:55 +01:00
#ifdef HAS_RTC
2019-02-24 23:13:15 +01:00
if (caller != _rtc)
set_rtctime(t);
2019-02-24 01:44:55 +01:00
#endif
2019-02-24 23:13:15 +01:00
return 1; // success
2019-02-21 23:17:01 +01:00
}
2019-02-24 23:13:15 +01:00
error:
ESP_LOGD(TAG, "Time source %c sync attempt failed", timeSetSymbols[caller]);
timeSource = _unsynced;
return 0; // failure
2019-02-24 01:44:55 +01:00
} // syncTime()
2019-02-21 23:17:01 +01:00
2019-02-24 23:13:15 +01:00
2019-02-24 15:08:41 +01:00
// callback function called by Time.h in interval set in main.cpp
time_t syncProvider_CB(void) {
timeSource = _unsynced;
return 0;
}
2019-02-21 23:17:01 +01:00
// helper function to setup a pulse per second for time synchronisation
2019-02-24 01:44:55 +01:00
uint8_t timepulse_init() {
2019-02-21 23:17:01 +01:00
// use time pulse from GPS as time base with fixed 1Hz frequency
#ifdef GPS_INT
2019-02-23 20:28:11 +01:00
// setup external interupt pin for GPS INT output
2019-02-21 23:17:01 +01:00
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
2019-02-23 20:28:11 +01:00
// setup external interupt pin for active low RTC INT output
2019-02-21 23:17:01 +01:00
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 {
2019-02-22 22:28:35 +01:00
ESP_LOGE(TAG, "RTC initialization error, I2C bus busy");
2019-02-21 23:17:01 +01:00
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();
}
2019-02-22 22:28:35 +01:00
// helper function to check plausibility of a time
2019-02-24 01:44:55 +01:00
uint8_t TimeIsValid(time_t const t) {
2019-02-22 22:28:35 +01:00
// 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
2019-02-23 23:39:45 +01:00
time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm,
uint8_t ss) {
2019-02-22 22:28:35 +01:00
tmElements_t tm;
2019-02-22 23:17:28 +01:00
tm.Year = CalendarYrToTm(YYYY); // year offset from 1970 in time.h
2019-02-22 22:28:35 +01:00
tm.Month = MM;
tm.Day = DD;
tm.Hour = hh;
tm.Minute = mm;
tm.Second = ss;
return makeTime(tm);
}
2019-02-21 23:17:01 +01:00
#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
2019-02-24 01:44:55 +01:00
// preload first DCF frame before start
2019-02-21 23:17:01 +01:00
#ifdef HAS_DCF77
2019-02-24 01:44:55 +01:00
uint8_t *DCFpulse; // pointer on array with DCF pulse bits
2019-02-23 20:28:11 +01:00
DCFpulse = DCF77_Frame(t1(now()));
2019-02-21 23:17:01 +01:00
#endif
// output time telegram for second following sec beginning with timepulse
for (;;) {
xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime,
portMAX_DELAY); // wait for timepulse
2019-02-24 23:13:15 +01:00
// no confident time -> suppress clock output
if (timeStatus() == timeNotSet)
2019-02-21 23:17:01 +01:00
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?
2019-02-23 21:51:24 +01:00
DCFpulse = DCF77_Frame(t1(t)); // generate next frame
2019-02-21 23:17:01 +01:00
2019-02-23 18:31:47 +01:00
if (DCFpulse[DCF77_FRAME_SIZE] ==
2019-02-23 20:28:11 +01:00
minute(t1(t))) // have recent frame? (pulses could be missed!)
DCF77_Pulse(t2(t), DCFpulse); // then output next second of this frame
2019-02-21 23:17:01 +01:00
#endif
} // for
} // clock_loop()
#endif // HAS_IF482 || defined HAS_DCF77