2019-02-24 01:44:55 +01:00
|
|
|
#include "timekeeper.h"
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2019-03-31 15:56:56 +02:00
|
|
|
#if !(HAS_LORA)
|
|
|
|
#if (TIME_SYNC_LORASERVER)
|
|
|
|
#error TIME_SYNC_LORASERVER defined, but device has no LORA configured
|
2019-03-09 22:08:57 +01:00
|
|
|
#elif (TIME_SYNC_LORAWAN)
|
|
|
|
#error TIME_SYNC_LORAWAN defined, but device has no LORA configured
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2022-01-19 20:29:31 +01:00
|
|
|
#if (defined HAS_DCF77 && defined HAS_IF482)
|
|
|
|
#error You must define at most one of IF482 or DCF77!
|
|
|
|
#endif
|
|
|
|
|
2019-02-21 23:17:01 +01:00
|
|
|
// Local logging tag
|
2019-02-27 00:52:27 +01:00
|
|
|
static const char TAG[] = __FILE__;
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2019-02-24 15:08:41 +01:00
|
|
|
// symbol to display current time source
|
2022-01-13 23:30:18 +01:00
|
|
|
// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced
|
|
|
|
const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'};
|
2021-03-31 19:02:01 +02:00
|
|
|
|
|
|
|
bool volatile TimePulseTick = false;
|
|
|
|
timesource_t timeSource = _unsynced;
|
|
|
|
TaskHandle_t ClockTask = NULL;
|
|
|
|
hw_timer_t *ppsIRQ = NULL;
|
|
|
|
|
2019-03-23 15:12:11 +01:00
|
|
|
#ifdef HAS_IF482
|
2022-01-16 19:43:29 +01:00
|
|
|
HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS)
|
|
|
|
static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) -
|
|
|
|
tx_Ticks(IF482_FRAME_SIZE, HAS_IF482);
|
2020-02-03 15:28:45 +01:00
|
|
|
#if (HAS_SDS011)
|
|
|
|
#error cannot use IF482 together with SDS011 (both use UART#2)
|
|
|
|
#endif
|
2022-01-19 14:27:16 +01:00
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
#endif // HAS_IF482
|
2019-03-23 15:12:11 +01:00
|
|
|
|
2019-03-03 00:30:57 +01:00
|
|
|
Ticker timesyncer;
|
2019-02-24 23:13:15 +01:00
|
|
|
|
2020-10-04 14:29:57 +02:00
|
|
|
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
|
2019-03-02 13:32:02 +01:00
|
|
|
|
2019-08-03 12:27:24 +02:00
|
|
|
void calibrateTime(void) {
|
2022-01-26 16:21:44 +01:00
|
|
|
|
2019-02-25 00:26:46 +01:00
|
|
|
time_t t = 0;
|
2019-08-03 14:01:25 +02:00
|
|
|
uint16_t t_msec = 0;
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2020-03-06 19:24:58 +01:00
|
|
|
// kick off asychronous lora timesync if we have
|
2020-09-29 07:59:53 +02:00
|
|
|
#if (HAS_LORA) && ((TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN))
|
2020-03-11 23:49:06 +01:00
|
|
|
timesync_request();
|
2019-02-21 23:17:01 +01:00
|
|
|
#endif
|
2019-02-23 23:39:45 +01:00
|
|
|
|
2020-09-27 06:35:27 +02:00
|
|
|
// if no LORA timesource is available, or if we lost time, then fallback to
|
|
|
|
// local time source RTS or GPS
|
|
|
|
if (((!TIME_SYNC_LORASERVER) && (!TIME_SYNC_LORAWAN)) ||
|
|
|
|
(timeSource == _unsynced)) {
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2020-03-06 19:24:58 +01:00
|
|
|
// has RTC -> fallback to RTC time
|
2019-08-03 14:01:25 +02:00
|
|
|
#ifdef HAS_RTC
|
2022-01-26 16:21:44 +01:00
|
|
|
t = get_rtctime(&t_msec);
|
2020-09-29 17:28:27 +02:00
|
|
|
// set time from RTC - method will check if time is valid
|
2020-09-29 07:59:53 +02:00
|
|
|
setMyTime((uint32_t)t, t_msec, _rtc);
|
2019-08-03 14:01:25 +02:00
|
|
|
#endif
|
|
|
|
|
2020-03-06 19:24:58 +01:00
|
|
|
// no RTC -> fallback to GPS time
|
|
|
|
#if (HAS_GPS)
|
2020-03-11 23:49:06 +01:00
|
|
|
t = get_gpstime(&t_msec);
|
2020-09-29 07:59:53 +02:00
|
|
|
// set time from GPS - method will check if time is valid
|
|
|
|
setMyTime((uint32_t)t, t_msec, _gps);
|
2020-03-06 19:24:58 +01:00
|
|
|
#endif
|
2020-03-11 23:49:06 +01:00
|
|
|
|
2020-03-07 19:41:27 +01:00
|
|
|
} // fallback
|
2019-02-24 23:13:15 +01:00
|
|
|
|
2020-03-07 19:41:27 +01:00
|
|
|
else
|
|
|
|
|
|
|
|
// no fallback time source available -> we can't set time
|
|
|
|
return;
|
2019-03-02 13:32:02 +01:00
|
|
|
|
2019-08-03 12:27:24 +02:00
|
|
|
} // calibrateTime()
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2022-01-13 23:30:18 +01:00
|
|
|
// set system time (UTC), calibrate RTC and RTC_INT pps
|
2019-08-13 22:40:33 +02:00
|
|
|
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
|
|
|
timesource_t mytimesource) {
|
|
|
|
|
2022-01-13 23:30:18 +01:00
|
|
|
struct timeval tv = {0};
|
|
|
|
|
2019-08-18 22:13:42 +02:00
|
|
|
// called with invalid timesource?
|
|
|
|
if (mytimesource == _unsynced)
|
|
|
|
return;
|
|
|
|
|
2022-01-13 23:30:18 +01:00
|
|
|
// increment t_sec if t_msec > 1000
|
2019-08-13 22:40:33 +02:00
|
|
|
time_t time_to_set = (time_t)(t_sec + t_msec / 1000);
|
|
|
|
|
|
|
|
// do we have a valid time?
|
|
|
|
if (timeIsValid(time_to_set)) {
|
|
|
|
|
|
|
|
// if we have msec fraction, then wait until top of second with
|
|
|
|
// millisecond precision
|
|
|
|
if (t_msec % 1000) {
|
|
|
|
time_to_set++;
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
|
|
|
|
}
|
|
|
|
|
2022-01-26 16:21:44 +01:00
|
|
|
// from here on we are on top of next second
|
|
|
|
|
2022-01-13 23:30:18 +01:00
|
|
|
tv.tv_sec = time_to_set;
|
|
|
|
tv.tv_usec = 0;
|
2022-01-23 21:12:28 +01:00
|
|
|
sntp_sync_time(&tv);
|
2019-08-13 22:40:33 +02:00
|
|
|
|
2022-01-13 23:30:18 +01:00
|
|
|
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set);
|
2019-08-13 22:40:33 +02:00
|
|
|
|
2022-01-26 16:21:44 +01:00
|
|
|
// if we have a precise time timesource, set RTC time and shift RTC_INT
|
|
|
|
// pulse to top of second
|
2022-01-13 23:30:18 +01:00
|
|
|
#ifdef HAS_RTC
|
|
|
|
if ((mytimesource == _gps) || (mytimesource == _lora))
|
|
|
|
set_rtctime(time_to_set);
|
|
|
|
#endif
|
2019-08-13 22:40:33 +02:00
|
|
|
|
2022-01-26 16:21:44 +01:00
|
|
|
// if we have a software pps timer, shift it to top of second
|
|
|
|
if (ppsIRQ != NULL) {
|
|
|
|
timerWrite(ppsIRQ, 0); // reset pps timer
|
|
|
|
CLOCKIRQ(); // fire clock pps to advance wall clock by 1 sec
|
|
|
|
}
|
|
|
|
|
2019-08-13 22:40:33 +02:00
|
|
|
timeSource = mytimesource; // set global variable
|
2022-01-13 23:30:18 +01:00
|
|
|
|
2020-10-04 14:29:57 +02:00
|
|
|
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
2021-04-08 18:19:39 +02:00
|
|
|
ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | timesource=%d",
|
|
|
|
_seconds(), mytimesource);
|
2019-08-13 22:40:33 +02:00
|
|
|
} else {
|
2020-10-04 14:29:57 +02:00
|
|
|
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
|
2021-10-02 13:19:41 +02:00
|
|
|
ESP_LOGV(TAG,
|
2021-04-07 20:20:31 +02:00
|
|
|
"[%0.3f] Failed to synchronise time from source %c | unix sec "
|
|
|
|
"obtained from source: %d | unix sec at program compilation: %d",
|
|
|
|
_seconds(), timeSetSymbols[mytimesource], time_to_set,
|
2022-01-26 16:21:44 +01:00
|
|
|
compileTime());
|
2019-08-13 22:40:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-01-23 21:12:28 +01:00
|
|
|
// set esp-idf API sntp sync mode
|
2022-01-26 16:21:44 +01:00
|
|
|
// sntp_init();
|
2022-01-23 21:12:28 +01:00
|
|
|
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
|
|
|
|
|
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-27 22:40:58 +01:00
|
|
|
// setup external interupt pin for rising edge GPS INT
|
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-27 22:40:58 +01:00
|
|
|
// setup external interupt pin for falling edge RTC INT
|
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
|
2019-03-03 17:35:08 +01:00
|
|
|
ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
|
2019-03-03 12:57:00 +01:00
|
|
|
timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms
|
2019-02-21 23:17:01 +01:00
|
|
|
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
|
2019-03-03 12:57:00 +01:00
|
|
|
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
|
|
|
|
timerAlarmEnable(ppsIRQ);
|
2019-02-21 23:17:01 +01:00
|
|
|
#endif
|
2019-04-14 22:54:27 +02:00
|
|
|
|
2022-01-08 19:10:34 +01:00
|
|
|
// get time if we don't have one
|
|
|
|
if (timeSource != _set)
|
|
|
|
setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
|
2019-04-13 13:59:30 +02:00
|
|
|
// start cyclic time sync
|
2020-10-04 14:29:57 +02:00
|
|
|
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
2019-02-21 23:17:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// interrupt service routine triggered by either pps or esp32 hardware timer
|
|
|
|
void IRAM_ATTR CLOCKIRQ(void) {
|
2019-02-27 22:40:58 +01:00
|
|
|
|
2019-03-24 00:15:04 +01:00
|
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
2019-03-02 13:32:02 +01:00
|
|
|
|
2019-04-14 22:54:27 +02:00
|
|
|
// advance wall clock, if we have
|
2019-04-10 21:38:17 +02:00
|
|
|
#if (defined HAS_IF482 || defined HAS_DCF77)
|
2022-01-13 23:30:18 +01:00
|
|
|
xTaskNotifyFromISR(ClockTask, uint32_t(time(NULL)), eSetBits,
|
2019-04-10 21:38:17 +02:00
|
|
|
&xHigherPriorityTaskWoken);
|
|
|
|
#endif
|
2019-02-27 22:40:58 +01:00
|
|
|
|
2019-04-14 22:54:27 +02:00
|
|
|
// flip time pulse ticker, if needed
|
2019-03-24 01:05:13 +01:00
|
|
|
#ifdef HAS_DISPLAY
|
2019-03-23 15:12:11 +01:00
|
|
|
#if (defined GPS_INT || defined RTC_INT)
|
|
|
|
TimePulseTick = !TimePulseTick; // flip pulse ticker
|
2019-03-24 01:05:13 +01:00
|
|
|
#endif
|
2019-02-21 23:17:01 +01:00
|
|
|
#endif
|
2019-02-27 22:40:58 +01:00
|
|
|
|
2019-03-02 20:58:06 +01:00
|
|
|
// yield only if we should
|
|
|
|
if (xHigherPriorityTaskWoken)
|
|
|
|
portYIELD_FROM_ISR();
|
2019-02-21 23:17:01 +01:00
|
|
|
}
|
|
|
|
|
2021-05-09 00:02:41 +02:00
|
|
|
// helper function to check plausibility of a given epoch time
|
2022-01-13 23:30:18 +01:00
|
|
|
bool timeIsValid(time_t const t) {
|
|
|
|
// is t a time in the past? we use compile time to guess
|
2022-01-26 16:21:44 +01:00
|
|
|
// compile time is some local time, but we do not know it's time zone
|
|
|
|
// thus, we go 1 full day back to be sure to catch a time in the past
|
|
|
|
return (t > (compileTime() - 86400));
|
2019-02-22 22:28:35 +01:00
|
|
|
}
|
|
|
|
|
2019-02-25 20:22:03 +01:00
|
|
|
// 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;
|
2019-03-23 15:12:11 +01:00
|
|
|
uint32_t txTime = (databits + stopbits + 1) * framesize * 1000.0 / baud;
|
|
|
|
// +1 for the startbit
|
2019-02-25 20:22:03 +01:00
|
|
|
|
|
|
|
return round(txTime);
|
|
|
|
}
|
|
|
|
|
2019-02-21 23:17:01 +01:00
|
|
|
void clock_init(void) {
|
|
|
|
|
|
|
|
// setup clock output interface
|
|
|
|
#ifdef HAS_IF482
|
|
|
|
IF482.begin(HAS_IF482);
|
|
|
|
#elif defined HAS_DCF77
|
|
|
|
pinMode(HAS_DCF77, OUTPUT);
|
|
|
|
#endif
|
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
xTaskCreatePinnedToCore(clock_loop, // task function
|
|
|
|
"clockloop", // name of task
|
2022-01-18 16:07:24 +01:00
|
|
|
3072, // stack size of task
|
2022-01-16 19:43:29 +01:00
|
|
|
(void *)1, // task parameter
|
2022-01-26 16:32:54 +01:00
|
|
|
6, // priority of the task
|
2022-01-16 19:43:29 +01:00
|
|
|
&ClockTask, // task handle
|
|
|
|
1); // CPU core
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2020-10-30 12:24:16 +01:00
|
|
|
_ASSERT(ClockTask != NULL); // has clock task started?
|
2019-02-21 23:17:01 +01:00
|
|
|
} // clock_init
|
|
|
|
|
2019-03-02 20:01:27 +01:00
|
|
|
void clock_loop(void *taskparameter) { // ClockTask
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2022-01-18 01:00:40 +01:00
|
|
|
uint64_t ClockPulse = 0;
|
2022-01-16 19:43:29 +01:00
|
|
|
uint32_t current_time = 0, previous_time = 0;
|
2022-01-19 14:27:16 +01:00
|
|
|
int8_t ClockMinute = -1;
|
2022-01-16 19:43:29 +01:00
|
|
|
time_t tt;
|
|
|
|
struct tm t = {0};
|
2019-08-03 18:06:06 +02:00
|
|
|
#ifdef HAS_TWO_LED
|
2019-03-24 01:05:13 +01:00
|
|
|
static bool led1_state = false;
|
2019-08-03 18:06:06 +02:00
|
|
|
#endif
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2019-04-13 13:59:30 +02:00
|
|
|
// output the next second's pulse/telegram after pps arrived
|
2019-02-21 23:17:01 +01:00
|
|
|
for (;;) {
|
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
// wait for timepulse and store UTC time
|
|
|
|
xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, portMAX_DELAY);
|
2019-04-13 13:59:30 +02:00
|
|
|
|
2022-01-23 21:12:28 +01:00
|
|
|
if ((sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) ||
|
|
|
|
!(timeIsValid(current_time)) || (current_time == previous_time))
|
2019-03-16 21:01:43 +01:00
|
|
|
continue;
|
|
|
|
|
2022-01-18 01:00:40 +01:00
|
|
|
// set calendar time for next second of clock output
|
2022-01-16 19:43:29 +01:00
|
|
|
tt = (time_t)(current_time + 1);
|
|
|
|
localtime_r(&tt, &t);
|
2022-01-26 16:21:44 +01:00
|
|
|
tt = mktime(&t);
|
2022-01-16 19:43:29 +01:00
|
|
|
|
2019-02-21 23:17:01 +01:00
|
|
|
#if defined HAS_IF482
|
|
|
|
|
2019-04-08 21:22:24 +02:00
|
|
|
// wait until moment to fire. Normally we won't get notified during this
|
|
|
|
// timespan, except when next pps pulse arrives while waiting, because pps
|
2022-01-16 19:43:29 +01:00
|
|
|
// was adjusted by recent time sync, then advance next_time one second
|
|
|
|
if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, txDelay) == pdTRUE) {
|
|
|
|
tt = (time_t)(current_time + 1);
|
|
|
|
localtime_r(&tt, &t);
|
2022-01-26 16:21:44 +01:00
|
|
|
tt = mktime(&t);
|
2022-01-16 19:43:29 +01:00
|
|
|
}
|
2019-04-08 21:22:24 +02:00
|
|
|
|
|
|
|
// send IF482 telegram
|
2022-01-26 16:21:44 +01:00
|
|
|
IF482.print(IF482_Frame(tt)); // note: telegram is for *next* second
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2022-01-26 16:21:44 +01:00
|
|
|
ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(tt).c_str());
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
#elif defined HAS_DCF77
|
2019-02-21 23:17:01 +01:00
|
|
|
|
2022-01-18 01:00:40 +01:00
|
|
|
// load new frame if second 59 is reached
|
|
|
|
if (t.tm_sec == 0) {
|
|
|
|
ClockMinute = t.tm_min;
|
|
|
|
t.tm_min++; // follow-up minute
|
|
|
|
mktime(&t); // normalize calendar time
|
|
|
|
ClockPulse = DCF77_Frame(t); // generate pulse frame
|
|
|
|
|
|
|
|
/* to do here: leap second handling in second 59 */
|
2019-04-10 21:38:17 +02:00
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
ESP_LOGD(TAG, "[%0.3f] DCF77: new frame for min %d", _seconds(),
|
|
|
|
t.tm_min);
|
2022-01-18 01:00:40 +01:00
|
|
|
} else {
|
|
|
|
|
|
|
|
// generate impulse
|
|
|
|
if (t.tm_min == ClockMinute) { // ensure frame is recent
|
|
|
|
DCF77_Pulse(ClockPulse & 1); // output next second
|
|
|
|
ClockPulse >>= 1;
|
|
|
|
}
|
2022-01-16 19:43:29 +01:00
|
|
|
}
|
2019-02-21 23:17:01 +01:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2019-04-10 21:38:17 +02:00
|
|
|
// pps blink on secondary LED if we have one
|
|
|
|
#ifdef HAS_TWO_LED
|
|
|
|
if (led1_state)
|
|
|
|
switch_LED1(LED_OFF);
|
|
|
|
else
|
|
|
|
switch_LED1(LED_ON);
|
|
|
|
led1_state = !led1_state;
|
|
|
|
#endif
|
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
previous_time = current_time;
|
2019-04-10 21:38:17 +02:00
|
|
|
|
2019-02-21 23:17:01 +01:00
|
|
|
} // for
|
|
|
|
} // clock_loop()
|
|
|
|
|
2022-01-16 19:43:29 +01:00
|
|
|
// we use compile date to create a time_t reference "in the past"
|
2022-01-26 16:21:44 +01:00
|
|
|
time_t compileTime(void) {
|
2022-01-13 23:30:18 +01:00
|
|
|
|
|
|
|
char s_month[5];
|
|
|
|
int year;
|
|
|
|
struct tm t = {0};
|
|
|
|
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
|
|
|
|
|
|
|
// store compile time once it's calculated
|
|
|
|
static time_t secs = -1;
|
|
|
|
|
|
|
|
if (secs == -1) {
|
|
|
|
|
2022-01-19 20:29:31 +01:00
|
|
|
// determine date
|
2022-01-26 16:21:44 +01:00
|
|
|
sscanf(__DATE__, "%s %d %d", s_month, &t.tm_mday, &year);
|
2022-01-13 23:30:18 +01:00
|
|
|
t.tm_mon = (strstr(month_names, s_month) - month_names) / 3;
|
|
|
|
t.tm_year = year - 1900;
|
2022-01-26 16:21:44 +01:00
|
|
|
// determine time
|
|
|
|
sscanf(__TIME__, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
|
2022-01-13 23:30:18 +01:00
|
|
|
|
2022-01-19 20:29:31 +01:00
|
|
|
// convert to secs local time
|
|
|
|
secs = mktime(&t);
|
2022-01-13 23:30:18 +01:00
|
|
|
}
|
|
|
|
|
2022-01-20 17:37:12 +01:00
|
|
|
return secs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsLeapYear(short year) {
|
|
|
|
if (year % 4 != 0)
|
|
|
|
return false;
|
|
|
|
if (year % 100 != 0)
|
|
|
|
return true;
|
|
|
|
return (year % 400) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert UTC tm time to time_t epoch time
|
|
|
|
time_t mkgmtime(const struct tm *ptm) {
|
|
|
|
const int SecondsPerMinute = 60;
|
|
|
|
const int SecondsPerHour = 3600;
|
|
|
|
const int SecondsPerDay = 86400;
|
|
|
|
const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
time_t secs = 0;
|
|
|
|
// tm_year is years since 1900
|
|
|
|
int year = ptm->tm_year + 1900;
|
|
|
|
for (int y = 1970; y < year; ++y) {
|
|
|
|
secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
|
|
|
|
}
|
|
|
|
// tm_mon is month from 0..11
|
|
|
|
for (int m = 0; m < ptm->tm_mon; ++m) {
|
|
|
|
secs += DaysOfMonth[m] * SecondsPerDay;
|
|
|
|
if (m == 1 && IsLeapYear(year))
|
|
|
|
secs += SecondsPerDay;
|
|
|
|
}
|
|
|
|
secs += (ptm->tm_mday - 1) * SecondsPerDay;
|
|
|
|
secs += ptm->tm_hour * SecondsPerHour;
|
|
|
|
secs += ptm->tm_min * SecondsPerMinute;
|
|
|
|
secs += ptm->tm_sec;
|
2022-01-13 23:30:18 +01:00
|
|
|
return secs;
|
|
|
|
}
|