timemanager added (v1.7.32)

This commit is contained in:
Klaus K Wilting 2019-02-21 23:17:01 +01:00
parent 34eb681955
commit 3d26f737be
19 changed files with 277 additions and 310 deletions

View File

@ -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

View File

@ -5,7 +5,7 @@
#include "senddata.h" #include "senddata.h"
#include "rcommand.h" #include "rcommand.h"
#include "spislave.h" #include "spislave.h"
#include "rtctime.h" #include "timemanager.h"
#include <lmic.h> #include <lmic.h>
#ifdef HAS_BME #ifdef HAS_BME

View File

@ -3,7 +3,6 @@
#include <U8x8lib.h> #include <U8x8lib.h>
#include "cyclic.h" #include "cyclic.h"
#include "rtctime.h"
extern uint8_t volatile DisplayState; extern uint8_t volatile DisplayState;
extern HAS_DISPLAY u8x8; extern HAS_DISPLAY u8x8;

View File

@ -7,6 +7,7 @@
// Time functions // Time functions
#include <Time.h> #include <Time.h>
#include <Timezone.h> #include <Timezone.h>
#include <RtcDateTime.h>
// std::set for unified array functions // std::set for unified array functions
#include <set> #include <set>
@ -106,12 +107,15 @@ extern char display_line6[], display_line7[]; // screen buffers
extern uint8_t volatile channel; // wifi channel rotation counter extern uint8_t volatile channel; // wifi channel rotation counter
extern uint16_t volatile macs_total, macs_wifi, macs_ble, extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern bool volatile TimePulseTick; extern bool volatile TimePulseTick; // one-pulse-per-second flags set by GPS or RTC
extern hw_timer_t *sendCycle, *displaytimer; extern bool TimeIsSynced;
extern hw_timer_t *sendCycle, *displaytimer, *clockCycle;
extern SemaphoreHandle_t I2Caccess, TimePulse; extern SemaphoreHandle_t I2Caccess, TimePulse;
extern TaskHandle_t irqHandlerTask, ClockTask; extern TaskHandle_t irqHandlerTask, ClockTask;
extern TimerHandle_t WifiChanTimer; extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ; extern Timezone myTZ;
extern time_t LastSyncTime;
extern RtcDateTime compiled;
// application includes // application includes
#include "led.h" #include "led.h"

View File

@ -2,6 +2,7 @@
#define _GPSREAD_H #define _GPSREAD_H
#include <TinyGPS++.h> // library for parsing NMEA data #include <TinyGPS++.h> // library for parsing NMEA data
#include <RtcDateTime.h>
#ifdef GPS_I2C // Needed for reading from I2C Bus #ifdef GPS_I2C // Needed for reading from I2C Bus
#include <Wire.h> #include <Wire.h>

View File

@ -17,6 +17,5 @@
#include "led.h" #include "led.h"
#include "spislave.h" #include "spislave.h"
#include "lorawan.h" #include "lorawan.h"
#include "rtctime.h" #include "timemanager.h"
#include "clockcontroller.h"
#endif #endif

View File

@ -5,10 +5,6 @@
#include <Wire.h> // must be included here so that Arduino library object file references work #include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h> #include <RtcDS3231.h>
#ifdef HAS_GPS
#include "gpsread.h"
#endif
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
int rtc_init(void); int rtc_init(void);
@ -17,12 +13,5 @@ int set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(void);
float get_rtctemp(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 #endif // _RTCTIME_H

26
include/timemanager.h Normal file
View File

@ -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

View File

@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
[common] [common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c" ; 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! ; 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 ; 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 MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool upload_protocol = esptool
;upload_protocol = custom ;upload_protocol = custom

View File

@ -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

View File

@ -8,8 +8,6 @@
static const char TAG[] = "main"; static const char TAG[] = "main";
time_t userUTCTime; // Seconds since the UTC epoch time_t userUTCTime; // Seconds since the UTC epoch
unsigned long nextLoraTimeSync = millis();
unsigned long nextGPSTimeSync = millis();
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {
@ -23,35 +21,7 @@ void doHousekeeping() {
spi_housekeeping(); spi_housekeeping();
lora_housekeeping(); lora_housekeeping();
time_sync();
// 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
// task storage debugging // // task storage debugging //
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",

View File

@ -26,8 +26,6 @@ void DCF_Pulse(time_t t) {
TickType_t startTime = xTaskGetTickCount(); TickType_t startTime = xTaskGetTickCount();
ESP_LOGD(TAG, "DCF77 sec %d", sec);
// induce 10 pulses // induce 10 pulses
for (uint8_t pulse = 0; pulse <= 9; pulse++) { for (uint8_t pulse = 0; pulse <= 9; pulse++) {
@ -64,8 +62,6 @@ void IRAM_ATTR DCF77_Frame(time_t tt) {
uint8_t Parity; uint8_t Parity;
time_t t = myTZ.toLocal(tt); // convert to local time time_t t = myTZ.toLocal(tt); // convert to local time
ESP_LOGD(TAG, "DCF77 minute %d", minute(t));
// ENCODE HEAD // ENCODE HEAD
// secs 0..19 initialized with zeros // secs 0..19 initialized with zeros
for (int n = 0; n <= 19; n++) for (int n = 0; n <= 19; n++)

View File

@ -43,11 +43,11 @@ const char lora_datarate[] = {"121110090807FSNA"};
// time display symbols // time display symbols
#if defined HAS_GPS || defined HAS_RTC #if defined HAS_GPS || defined HAS_RTC
const char timeNosyncSymbol = '?'; const char timeNoPulseSymbol = '?';
#if defined HAS_IF482 #if defined HAS_IF482
const char timesyncSymbol = '+'; const char timePulseSymbol = '+';
#elif defined HAS_DCF77 #elif defined HAS_DCF77
const char timesyncSymbol = '*'; const char timePulseSymbol = '*';
#endif #endif
#endif #endif
@ -141,7 +141,7 @@ void init_display(const char *Productname, const char *Version) {
void refreshtheDisplay() { void refreshtheDisplay() {
uint8_t msgWaiting; uint8_t msgWaiting;
char timeSync, timeState; char timePulse, timeState;
char buff[16]; // 16 chars line buffer char buff[16]; // 16 chars line buffer
time_t t; time_t t;
@ -225,12 +225,13 @@ void refreshtheDisplay() {
// update LoRa status display (line 6) // update LoRa status display (line 6)
u8x8.printf("%-16s", display_line6); u8x8.printf("%-16s", display_line6);
#else // we want a systime display instead LoRa status #else // we want a systime display instead LoRa status
t = myTZ.toLocal(best_time()); t = myTZ.toLocal(now());
timeSync = (timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol; timePulse = (timeStatus() == timeSet) ? timePulseSymbol : timeNoPulseSymbol;
timeState = TimePulseTick ? timeSync : ' '; timeState = TimePulseTick ? timeSync : ' ';
TimePulseTick = false; TimePulseTick = false;
u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), u8x8.printf("%02d:%02d%c%02d%c %2d.%3s", hour(t), minute(t),
timeState, day(t), printmonth[month(t)]); TimeIsSynced ? ':' : '.', second(t), timeState, day(t),
printmonth[month(t)]);
#endif #endif
// update LMiC event display (line 7) // update LMiC event display (line 7)

View File

@ -91,12 +91,18 @@ time_t get_gpstime(void) {
// !! never call now() or delay in this function, this would break this // !! never call now() or delay in this function, this would break this
// function to be used as SyncProvider for Time.h // 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())) { if ((gps.time.age() < 900) && (gps.time.isValid()) &&
// get current gps time (gps.date.year() >= compiled.Year())) {
// use recent gps time
t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), t = tmConvert_t(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());
} }
return t; return t;
} // get_gpstime() } // get_gpstime()

View File

@ -471,13 +471,13 @@ void user_request_network_time_callback(void *pVoidUserUTCTime,
*pUserUTCTime += requestDelaySec; *pUserUTCTime += requestDelaySec;
// Update system time with time read from the network // Update system time with time read from the network
if (sync_TimePulse()) { // wait for start of next second if (syncTime(*pUserUTCTime)) { // do we have a valid time?
if (sync_SysTime(*pUserUTCTime)) { // do we have a valid time?
#ifdef HAS_RTC #ifdef HAS_RTC
if (TimeIsSynced)
set_rtctime(now()); // epoch time set_rtctime(now()); // epoch time
#endif #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 } else
ESP_LOGI(TAG, "Unable to sync system time with LORA"); ESP_LOGI(TAG, "Unable to sync system time with LORA");
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -65,14 +65,15 @@ uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display batt_voltage = 0; // globals for display
hw_timer_t *sendCycle = NULL, *homeCycle = NULL; hw_timer_t *sendCycle = NULL, *homeCycle = NULL, *clockCycle = NULL, *displaytimer = NULL;
#ifdef HAS_DISPLAY
hw_timer_t *displaytimer = NULL;
#endif
TaskHandle_t irqHandlerTask, ClockTask; TaskHandle_t irqHandlerTask, ClockTask;
SemaphoreHandle_t I2Caccess, TimePulse; SemaphoreHandle_t I2Caccess, TimePulse;
bool volatile TimePulseTick = false; 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, // container holding unique MAC address hashes with Memory Alloctor using PSRAM,
// if present // if present
@ -359,11 +360,12 @@ void setup() {
#endif #endif
// start pps timepulse // start pps timepulse
ESP_LOGI(TAG, "Starting timepulse..."); ESP_LOGI(TAG, "Starting Timepulse...");
if (timepulse_init()) // setup timepulse if (timepulse_init()) // setup timepulse
timepulse_start(); // start pulse timepulse_start(); // start pulse
else 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 // start wifi in monitor mode and start channel rotation timer
ESP_LOGI(TAG, "Starting Wifi..."); ESP_LOGI(TAG, "Starting Wifi...");
@ -416,31 +418,6 @@ void setup() {
#endif #endif
#endif // HAS_BUTTON #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 #if defined HAS_IF482 || defined HAS_DCF77
ESP_LOGI(TAG, "Starting Clock Controller..."); ESP_LOGI(TAG, "Starting Clock Controller...");
clock_init(); clock_init();

View File

@ -82,9 +82,9 @@
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
// settings for syncing time of node and external time sources // 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 10 // sync time each .. minutes from external time source (GPS/LORA) [default = 10], 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_TIMEOUT 30 // fallback to rtc for timesync after .. minutes no sync with external time source
//#define TIME_SYNC_INTERVAL_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means 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 // 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 DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time

View File

@ -3,120 +3,6 @@
// Local logging tag // Local logging tag
static const char TAG[] = "main"; 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<time_t>(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 #ifdef HAS_RTC // we have hardware RTC
RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
@ -132,8 +18,6 @@ int rtc_init(void) {
Wire.begin(HAS_RTC); Wire.begin(HAS_RTC);
Rtc.Begin(); Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
if (!Rtc.IsDateTimeValid()) { if (!Rtc.IsDateTimeValid()) {
ESP_LOGW(TAG, ESP_LOGW(TAG,
"RTC has no valid RTC date/time, setting to compilation date"); "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()) { if (I2C_MUTEX_LOCK()) {
Rtc.SetDateTime(RtcDateTime(t)); Rtc.SetDateTime(RtcDateTime(t));
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access
ESP_LOGI(TAG, "RTC calibrated"); ESP_LOGI(TAG, "RTC time synced");
return 1; // success return 1; // success
} }
ESP_LOGE(TAG, "RTC set time failure"); 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 time_t t = 0; // 0 effects calling SyncProvider() to not set time
// block i2c bus access // block i2c bus access
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
if (Rtc.IsDateTimeValid()) { if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) {
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
t = tt.Epoch32Time(); t = tt.Epoch32Time();
} }

204
src/timemanager.cpp Normal file
View File

@ -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<time_t>(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