timemanager added (v1.7.32)
This commit is contained in:
parent
34eb681955
commit
3d26f737be
@ -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
|
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
@ -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
26
include/timemanager.h
Normal 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
|
@ -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
|
||||||
|
@ -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
|
|
@ -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",
|
||||||
|
@ -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++)
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
39
src/main.cpp
39
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,
|
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();
|
||||||
|
@ -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
|
||||||
|
120
src/rtctime.cpp
120
src/rtctime.cpp
@ -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
204
src/timemanager.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user