v1.7.31 (clockcontroller deeply reworked)

This commit is contained in:
Klaus K Wilting 2019-02-17 19:21:08 +01:00
parent c8978a0745
commit 05a3f3b2a8
18 changed files with 271 additions and 240 deletions

View File

@ -11,5 +11,6 @@
void clock_init(void); void clock_init(void);
void clock_loop(void *pvParameters); void clock_loop(void *pvParameters);
time_t telegram_time(void);
#endif // _CLOCKCONTROLLER_H #endif // _CLOCKCONTROLLER_H

View File

@ -5,15 +5,14 @@
#include "senddata.h" #include "senddata.h"
#include "rcommand.h" #include "rcommand.h"
#include "spislave.h" #include "spislave.h"
#include "rtctime.h"
#include <lmic.h> #include <lmic.h>
#ifdef HAS_BME #ifdef HAS_BME
#include "bme680mems.h" #include "bme680mems.h"
#endif #endif
#ifdef HAS_RTC
#include "rtctime.h"
#endif
void doHousekeeping(void); void doHousekeeping(void);
uint64_t uptime(void); uint64_t uptime(void);

View File

@ -3,11 +3,16 @@
#include "globals.h" #include "globals.h"
#define DCF77_FRAME_SIZE (60)
#define DCF77_PULSE_LENGTH (100)
extern uint8_t DCFpulse[];
enum dcf_pulses { dcf_off, dcf_zero, dcf_one }; enum dcf_pulses { dcf_off, dcf_zero, dcf_one };
enum dcf_pinstate { dcf_low, dcf_high }; enum dcf_pinstate { dcf_low, dcf_high };
void DCF_Pulse(time_t startTime); void DCF_Pulse(time_t t);
void DCF77_Frame(time_t t); void IRAM_ATTR DCF77_Frame(time_t t);
void set_DCF77_pin(dcf_pinstate state); void set_DCF77_pin(dcf_pinstate state);
uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]); uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t pArray[]);

View File

@ -45,13 +45,6 @@
pdTRUE pdTRUE
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) #define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess)
// 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 // Struct holding devices's runtime configuration
typedef struct { typedef struct {
uint8_t lorasf; // 7-12, lora spreadfactor uint8_t lorasf; // 7-12, lora spreadfactor
@ -104,20 +97,19 @@ typedef struct {
enum sendprio_t { prio_low, prio_normal, prio_high }; enum sendprio_t { prio_low, prio_normal, prio_high };
// global variables extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons;
extern configData_t cfg; // current device configuration extern configData_t cfg; // current device configuration
extern char display_line6[], display_line7[]; // screen buffers 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 hw_timer_t *sendCycle, *displaytimer; extern hw_timer_t *sendCycle, *displaytimer;
extern SemaphoreHandle_t I2Caccess, TimePulse; extern SemaphoreHandle_t I2Caccess, TimePulse;
extern TaskHandle_t irqHandlerTask, ClockTask;
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons;
extern TaskHandle_t irqHandlerTask;
extern TimerHandle_t WifiChanTimer; extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ; extern Timezone myTZ;

View File

@ -3,10 +3,13 @@
#include "globals.h" #include "globals.h"
#define IF482_FRAME_SIZE (17)
#define IF482_PULSE_LENGTH (1000)
extern HardwareSerial IF482; extern HardwareSerial IF482;
void IF482_Pulse(time_t t); void IF482_Pulse(time_t t);
String IF482_Frame(time_t tt); String IRAM_ATTR IF482_Frame(time_t tt);
TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin, TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin,
int8_t txPins); int8_t txPins);

View File

@ -10,16 +10,14 @@
#include <SPI.h> #include <SPI.h>
#include <arduino_lmic_hal_boards.h> #include <arduino_lmic_hal_boards.h>
#include "loraconf.h" #include "loraconf.h"
#include "rtctime.h"
// Needed for 24AA02E64, does not hurt anything if included and not used // Needed for 24AA02E64, does not hurt anything if included and not used
#ifdef MCP_24AA02E64_I2C_ADDRESS #ifdef MCP_24AA02E64_I2C_ADDRESS
#include <Wire.h> #include <Wire.h>
#endif #endif
// Needed for RTC time sync if RTC present on board
#ifdef HAS_RTC
#include "rtctime.h"
#endif
extern QueueHandle_t LoraSendQueue; extern QueueHandle_t LoraSendQueue;

View File

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

View File

@ -11,20 +11,17 @@
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
extern TaskHandle_t ClockTask;
extern hw_timer_t *clockCycle;
extern bool volatile TimePulseTick;
int rtc_init(void); int rtc_init(void);
int set_rtctime(uint32_t t); int set_rtctime(uint32_t t);
int set_rtctime(time_t t); 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 IRAM_ATTR CLOCKIRQ(void);
int timepulse_init(void); int timepulse_init(void);
void timepulse_start(); void timepulse_start(void);
int sync_TimePulse(void); int sync_TimePulse(void);
int sync_SysTime(time_t); int sync_SysTime(time_t);
int sync_SysTime(uint32_t t);
#endif // _RTCTIME_H #endif // _RTCTIME_H

View File

@ -30,7 +30,7 @@ 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.24 release_version = 1.7.31
; 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 = 0
@ -67,7 +67,7 @@ lib_deps_all =
build_flags_basic = build_flags_basic =
-include "src/hal/${PIOENV}.h" -include "src/hal/${PIOENV}.h"
-include "src/paxcounter.conf" -include "src/paxcounter.conf"
-w ; -w
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h' '-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
'-DCORE_DEBUG_LEVEL=${common.debug_level}' '-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DLOG_LOCAL_LEVEL=${common.debug_level}' '-DLOG_LOCAL_LEVEL=${common.debug_level}'

View File

@ -6,25 +6,16 @@
#error You must define at most one of IF482 or DCF77! #error You must define at most one of IF482 or DCF77!
#endif #endif
#if (PPS < IF482_PULSE_LENGTH) || (PPS < DCF77_PULSE_LENGTH)
#error On board timepulse too fast for clockcontroller
#endif
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
void clock_init(void) { // ClockTask void clock_init(void) {
timepulse_init(); // setup timepulse // setup clock output interface
// setup output interface
#ifdef HAS_IF482 #ifdef HAS_IF482
// initialize and configure IF482 Generator
IF482.begin(HAS_IF482); IF482.begin(HAS_IF482);
#elif defined HAS_DCF77 #elif defined HAS_DCF77
// initialize and configure DCF77 output
pinMode(HAS_DCF77, OUTPUT); pinMode(HAS_DCF77, OUTPUT);
set_DCF77_pin(dcf_low);
#endif #endif
xTaskCreatePinnedToCore(clock_loop, // task function xTaskCreatePinnedToCore(clock_loop, // task function
@ -36,37 +27,68 @@ void clock_init(void) { // ClockTask
0); // CPU core 0); // CPU core
assert(ClockTask); // has clock task started? assert(ClockTask); // has clock task started?
timepulse_start(); // start pulse
} // clock_init } // clock_init
void clock_loop(void *pvParameters) { // ClockTask void clock_loop(void *pvParameters) { // ClockTask
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
TickType_t wakeTime, txDelay; TickType_t wakeTime;
uint32_t pulseCycle; time_t t;
void (*pTimeTx)(time_t); // pointer to time telegram output function
#ifdef HAS_IF482 #define t1(t) (t + DCF77_FRAME_SIZE + 1) // future time for next frame
txDelay = pdMS_TO_TICKS(1000) - tx_Ticks(HAS_IF482);
pulseCycle = PPS / IF482_PULSE_LENGTH; // preload first DCF frame before start
pTimeTx = IF482_Pulse; #ifdef HAS_DCF77
#elif defined HAS_DCF77 DCF77_Frame(t1(telegram_time()));
txDelay = pdMS_TO_TICKS(DCF77_PULSE_LENGTH);
pulseCycle = PPS / DCF77_PULSE_LENGTH;
pTimeTx = DCF_Pulse;
#endif #endif
// output time telegram triggered by timepulse // output time telegram for second following sec beginning with timepulse
for (;;) { for (;;) {
if (timeStatus() == timeSet) // do we have valid time?
xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime, xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime,
portMAX_DELAY); // wait for timepulse portMAX_DELAY); // wait for timepulse
for (uint8_t i = 1; i <= pulseCycle; i++) {
pTimeTx(now()); if (timeStatus() == timeNotSet) // do we have valid time?
vTaskDelayUntil(&wakeTime, txDelay); continue;
}
t = telegram_time(); // time to send to clock
#if defined HAS_IF482
IF482_Pulse(t + 1); // next second
#elif defined HAS_DCF77
if (ts == 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 } // for
} // clock_loop() } // clock_loop()
#endif // HAS_DCF77 || HAS_IF482 // helper function to fetch current second from most precise time source
time_t telegram_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
#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();
}
#endif // HAS_IF482 || defined HAS_DCF77

View File

@ -9,7 +9,7 @@ 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 nextLoraTimeSync = millis();
unsigned long nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC * 60000; unsigned long nextGPSTimeSync = millis();
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {
@ -24,8 +24,26 @@ void doHousekeeping() {
spi_housekeeping(); spi_housekeeping();
lora_housekeeping(); lora_housekeeping();
// do cyclic time sync with LORA network // do cyclic sync of systime with GPS timepulse, if present
#ifdef TIME_SYNC_INTERVAL_LORA #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) { if (millis() >= nextLoraTimeSync) {
nextLoraTimeSync = millis() + TIME_SYNC_INTERVAL_LORA * nextLoraTimeSync = millis() + TIME_SYNC_INTERVAL_LORA *
60000; // set up next time sync period 60000; // set up next time sync period
@ -35,20 +53,6 @@ void doHousekeeping() {
} }
#endif #endif
// do cyclic write back system time to RTC if we have an external time source
#if (defined TIME_SYNC_INTERVAL_LORA || defined TIME_SYNC_INTERVAL_GPS) && \
defined HAS_RTC
if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) {
nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC *
60000; // set up next time sync period
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");
}
#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",
uxTaskGetStackHighWaterMark(irqHandlerTask), uxTaskGetStackHighWaterMark(irqHandlerTask),

View File

@ -1,5 +1,5 @@
/* /*
// Emulate a DCF77 radio receiver // Emulate a DCF77 radio receiver to control an external clock
// //
// a nice & free logic test program for DCF77 can be found here: // a nice & free logic test program for DCF77 can be found here:
https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/
@ -13,36 +13,32 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
// array of dcf pulses for three minutes // array of dcf pulses for one minute
uint8_t DCFpulse[DCF77_FRAME_SIZE]; uint8_t DCFpulse[DCF77_FRAME_SIZE + 1];
// called by timepulse interrupt to ticker out DCF signal // triggered by 1 second timepulse to ticker out DCF signal
void DCF_Pulse(time_t startTime) { void DCF_Pulse(time_t t) {
static uint8_t current_second = second(startTime); uint8_t sec = second(t);
static uint8_t pulse = 0;
static bool SecondsPending = false;
if (!SecondsPending) { TickType_t startTime = xTaskGetTickCount();
// 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 ESP_LOGD(TAG, "DCF77 sec %d", sec);
if (SecondsPending) {
switch (pulse++) { // induce 10 pulses
for (uint8_t pulse = 0; pulse <= 9; pulse++) {
switch (pulse) {
case 0: // start of second -> start of timeframe for logic signal case 0: // start of second -> start of timeframe for logic signal
if (DCFpulse[current_second] != dcf_off) if (DCFpulse[sec] != dcf_off)
set_DCF77_pin(dcf_low); set_DCF77_pin(dcf_low);
ESP_LOGD(TAG, "DCF77 bit %d", current_second); else // 59th second reached, nothing more to do
return;
break; break;
case 1: // 100ms after start of second -> end of timeframe for logic 0 case 1: // 100ms after start of second -> end of timeframe for logic 0
if (DCFpulse[current_second] == dcf_zero) if (DCFpulse[sec] == dcf_zero)
set_DCF77_pin(dcf_high); set_DCF77_pin(dcf_high);
break; break;
@ -50,22 +46,69 @@ void DCF_Pulse(time_t startTime) {
set_DCF77_pin(dcf_high); set_DCF77_pin(dcf_high);
break; break;
case 9: // 900ms after start -> last pulse before next second starts case 9: // 900ms after start -> last pulse
pulse = 0; return;
if (current_second++ >=
(DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second)
{
current_second = 0;
SecondsPending = false;
};
break;
}; // switch } // switch
}; // if
vTaskDelayUntil(&startTime, pdMS_TO_TICKS(DCF77_PULSE_LENGTH));
} // for
} // DCF_Pulse() } // DCF_Pulse()
void IRAM_ATTR DCF77_Frame(time_t tt) {
uint8_t Parity;
time_t t = myTZ.toLocal(tt); // convert to local time
ESP_LOGD(TAG, "DCF77 minute %d", minute(t));
// ENCODE HEAD
// secs 0..19 initialized with zeros
for (int n = 0; n <= 19; n++)
DCFpulse[n] = dcf_zero;
// secs 17..18: adjust for DayLightSaving
DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one;
// sec 20: must be 1 to indicate time active
DCFpulse[20] = dcf_one;
// ENCODE MINUTE (secs 21..28)
Parity = dec2bcd(minute(t), 21, 27, DCFpulse);
DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE HOUR (secs 29..35)
Parity = dec2bcd(hour(t), 29, 34, DCFpulse);
DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE DATE (secs 36..58)
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 (sec 59)
DCFpulse[59] = dcf_off;
// !! missing code here for leap second !!
// timestamp the frame with minute pointer
DCFpulse[60] = minute(t);
/*
// for debug: print the DCF77 frame buffer
char out[DCF77_FRAME_SIZE + 1];
uint8_t i;
for (i = 0; i < DCF77_FRAME_SIZE; i++) {
out[i] = DCFpulse[i] + '0'; // convert int digit to printable ascii
}
out[DCF77_FRAME_SIZE] = '\0'; // string termination char
ESP_LOGD(TAG, "DCF minute %d = %s", DCFpulse[DCF77_FRAME_SIZE], out);
*/
}
// helper function to convert decimal to bcd digit // helper function to convert decimal to bcd digit
uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos, uint8_t IRAM_ATTR dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
uint8_t pArray[]) { uint8_t pArray[]) {
uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10);
@ -80,52 +123,6 @@ uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
return parity; return parity;
} }
void DCF77_Frame(time_t tt) {
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++)
DCFpulse[n] = dcf_zero;
// bits 17..18: adjust for DayLightSaving
DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one;
// bit 20: must be 1 to indicate time active
DCFpulse[20] = dcf_one;
// ENCODE MINUTE (bits 21..28)
Parity = dec2bcd(minute(t), 21, 27, DCFpulse);
DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE HOUR (bits 29..35)
Parity = dec2bcd(hour(t), 29, 34, DCFpulse);
DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE DATE (bits 36..58)
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)
DCFpulse[59] = dcf_off;
// !! missing code here for leap second !!
/*
// for debug: print the DCF77 frame buffer
char out[DCF77_FRAME_SIZE + 1];
uint8_t i;
for (i = 0; i < DCF77_FRAME_SIZE; i++) {
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);
*/
}
// helper function to switch GPIO line with DCF77 signal // helper function to switch GPIO line with DCF77 signal
void set_DCF77_pin(dcf_pinstate state) { void set_DCF77_pin(dcf_pinstate state) {
switch (state) { switch (state) {
@ -146,6 +143,4 @@ void set_DCF77_pin(dcf_pinstate state) {
} // switch } // switch
} // DCF77_pulse } // DCF77_pulse
// helper function calculates next minute
#endif // HAS_DCF77 #endif // HAS_DCF77

View File

@ -96,8 +96,6 @@ time_t get_gpstime(void) {
time_t t = time_t t =
tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), 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: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t),
day(t), hour(t), minute(t), second(t));
return t; return t;
} else { } else {
ESP_LOGW(TAG, "GPS has no confident time"); ESP_LOGW(TAG, "GPS has no confident time");

View File

@ -86,12 +86,16 @@ static const char TAG[] = "main";
HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS)
// called by timepulse interrupt to ticker out DCF signal // triggered by timepulse to ticker out DCF signal
void IF482_Pulse(time_t startTime) { void IF482_Pulse(time_t t) {
IF482.print(IF482_Frame(startTime + 1)); // if482 telegram for next second
TickType_t startTime = xTaskGetTickCount();
static const TickType_t txDelay = pdMS_TO_TICKS(IF482_PULSE_LENGTH) - tx_Ticks(HAS_IF482);
vTaskDelayUntil(&startTime, txDelay);
IF482.print(IF482_Frame(t+1)); // note: if482 telegram for *next* second
} }
String IF482_Frame(time_t startTime) { String IRAM_ATTR IF482_Frame(time_t startTime) {
time_t t = myTZ.toLocal(startTime); time_t t = myTZ.toLocal(startTime);
char mon, buf[14], out[IF482_FRAME_SIZE]; char mon, buf[14], out[IF482_FRAME_SIZE];
@ -103,19 +107,14 @@ String IF482_Frame(time_t startTime) {
case timeNeedsSync: // time had been set but sync attempt did not succeed case timeNeedsSync: // time had been set but sync attempt did not succeed
mon = 'M'; mon = 'M';
break; break;
default: // time not set, no valid time default: // unknown time status (should never be reached)
mon = '?'; mon = '?';
break; break;
} // switch } // switch
// do we have confident time/date? // generate IF482 telegram
if (timeStatus() == timeSet)
snprintf(buf, sizeof(buf), "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000, 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)); month(t), day(t), weekday(t), hour(t), minute(t), second(t));
else
snprintf(buf, sizeof(buf), "000000F000000"); // no confident time/date
// output IF482 telegram
snprintf(out, sizeof(out), "O%cL%s\r", mon, buf); snprintf(out, sizeof(out), "O%cL%s\r", mon, buf);
ESP_LOGD(TAG, "IF482 = %s", out); ESP_LOGD(TAG, "IF482 = %s", out);
return out; return out;
@ -127,8 +126,11 @@ TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin,
uint32_t datenbits = ((config & 0x0c) >> 2) + 5; uint32_t datenbits = ((config & 0x0c) >> 2) + 5;
uint32_t stopbits = ((config & 0x20) >> 5) + 1; uint32_t stopbits = ((config & 0x20) >> 5) + 1;
return pdMS_TO_TICKS( uint32_t tx_delay =
round(((1 + datenbits + stopbits) * IF482_FRAME_SIZE * 1000.0 / baud))); (2 + datenbits + stopbits) * IF482_FRAME_SIZE * 1000.0 / baud;
// +2 ms margin for the startbit and the clock's processing time
return pdMS_TO_TICKS(round(tx_delay));
} }
#endif // HAS_IF482 #endif // HAS_IF482

View File

@ -399,7 +399,7 @@ esp_err_t lora_stack_init() {
} }
return ESP_OK; // continue main program return ESP_OK; // continue main program
#endif #endif // HAS_LORA
} }
void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
@ -439,6 +439,7 @@ void lora_housekeeping(void) {
void user_request_network_time_callback(void *pVoidUserUTCTime, void user_request_network_time_callback(void *pVoidUserUTCTime,
int flagSuccess) { int flagSuccess) {
#ifdef HAS_LORA
// Explicit conversion from void* to uint32_t* to avoid compiler errors // Explicit conversion from void* to uint32_t* to avoid compiler errors
uint32_t *pUserUTCTime = (uint32_t *)pVoidUserUTCTime; uint32_t *pUserUTCTime = (uint32_t *)pVoidUserUTCTime;
lmic_time_reference_t lmicTimeReference; lmic_time_reference_t lmicTimeReference;
@ -468,11 +469,14 @@ 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
setTime(*pUserUTCTime); if (sync_TimePulse()) { // wait for start of next second
ESP_LOGI(TAG, "LoRaWAN network has set the system time"); if (sync_SysTime(*pUserUTCTime)) { // do we have a valid time?
#ifdef HAS_RTC #ifdef HAS_RTC
sync_TimePulse(); // wait for next start of second set_rtctime(now()); // epoch time
if (!set_rtctime(*pUserUTCTime+1)) // epoch time
ESP_LOGE(TAG, "RTC set time failure");
#endif #endif
ESP_LOGI(TAG, "LORA has set the system time");
} }
} else
ESP_LOGI(TAG, "Unable to sync system time with LORA");
#endif // HAS_LORA
} // user_request_network_time_callback

View File

@ -27,9 +27,8 @@ Uused tasks and timers:
Task Core Prio Purpose Task Core Prio Purpose
==================================================================================== ====================================================================================
clockloop 0 4 generates realtime telegrams for external clock
ledloop 0 3 blinks LEDs ledloop 0 3 blinks LEDs
if482loop 0 3 generates serial feed of IF482 time telegrams
dcf77loop 0 3 generates DCF77 timeframe pulses
spiloop 0 2 reads/writes data on spi interface spiloop 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
@ -71,8 +70,9 @@ hw_timer_t *sendCycle = NULL, *homeCycle = NULL;
hw_timer_t *displaytimer = NULL; hw_timer_t *displaytimer = NULL;
#endif #endif
TaskHandle_t irqHandlerTask; TaskHandle_t irqHandlerTask, ClockTask;
SemaphoreHandle_t I2Caccess, TimePulse; SemaphoreHandle_t I2Caccess, TimePulse;
bool volatile TimePulseTick = false;
// 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
@ -338,20 +338,13 @@ void setup() {
#ifdef HAS_RTC #ifdef HAS_RTC
strcat_P(features, " RTC"); strcat_P(features, " RTC");
assert(rtc_init()); assert(rtc_init());
sync_TimePulse(); // wait for next start of second #endif
setSyncProvider(get_rtctime); // sync time now and then
if (timeStatus() != timeSet)
ESP_LOGI(TAG, "Unable to sync system time with RTC");
else
ESP_LOGI(TAG, "RTC has set the system time");
setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60);
#endif // HAS_RTC
#if defined HAS_DCF77 #if defined HAS_DCF77
strcat_P(features, " DCF77"); strcat_P(features, " DCF77");
#endif #endif
#if defined HAS_IF482 && defined RTC_INT #if defined HAS_IF482
strcat_P(features, " IF482"); strcat_P(features, " IF482");
#endif #endif
@ -365,6 +358,13 @@ void setup() {
#endif #endif
#endif #endif
// start pps timepulse
ESP_LOGI(TAG, "Starting timepulse...");
if (timepulse_init()) // setup timepulse
timepulse_start(); // start pulse
else
ESP_LOGE(TAG, "No timepulse, systime will not be synced!");
// 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...");
wifi_sniffer_init(); wifi_sniffer_init();
@ -417,19 +417,29 @@ void setup() {
#endif // HAS_BUTTON #endif // HAS_BUTTON
#ifdef HAS_GPS #ifdef HAS_GPS
sync_TimePulse(); // wait for next start of second // sync systime on next timepulse
setSyncProvider(get_gpstime); // sync time now and then ESP_LOGI(TAG, "GPS is setting system time");
if (timeStatus() != timeSet) if (sync_SysTime(get_gpstime())) {
ESP_LOGI(TAG, "Unable to sync system time with GPS"); //setSyncProvider(get_gpstime); // reset sync cycle on top of second
else { //setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
ESP_LOGI(TAG, "GPS has set the system time"); // calibrate RTC
#ifdef HAS_RTC #ifdef HAS_RTC
if (!set_rtctime(now())) // epoch time set_rtctime(now()); // epoch time
ESP_LOGE(TAG, "RTC set time failure");
#endif
}
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
#endif #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...");
@ -439,7 +449,6 @@ void setup() {
} // setup() } // setup()
void loop() { void loop() {
while (1) { while (1) {
#ifdef HAS_LORA #ifdef HAS_LORA
os_runloop_once(); // execute lmic scheduled jobs and events os_runloop_once(); // execute lmic scheduled jobs and events

View File

@ -84,7 +84,6 @@
// 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_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_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_LORA 60 // sync time each .. minutes from LORA network [default = 60], comment out means 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

View File

@ -3,9 +3,7 @@
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
TaskHandle_t ClockTask;
hw_timer_t *clockCycle = NULL; hw_timer_t *clockCycle = NULL;
bool volatile TimePulseTick = false;
// helper function to setup a pulse per second for time synchronisation // helper function to setup a pulse per second for time synchronisation
int timepulse_init() { int timepulse_init() {
@ -41,7 +39,6 @@ int timepulse_init() {
#else #else
// use ESP32 hardware timer as time base with adjustable frequency // use ESP32 hardware timer as time base with adjustable frequency
clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec clockCycle = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
//timerAttachInterrupt(clockCycle, &CLOCKIRQ, true);
timerAlarmWrite(clockCycle, 10000, true); // 1000ms timerAlarmWrite(clockCycle, 10000, true); // 1000ms
ESP_LOGI(TAG, "Time base: internal (ESP32 hardware timer)"); ESP_LOGI(TAG, "Time base: internal (ESP32 hardware timer)");
return 1; // success return 1; // success
@ -60,30 +57,9 @@ void timepulse_start(void) {
#endif #endif
} }
// helper function to sync systime on start of next second // interrupt service routine triggered by either pps or esp32 hardware timer
int sync_SysTime(time_t t) { void IRAM_ATTR CLOCKIRQ(void) {
if (sync_TimePulse()) { if (ClockTask != NULL)
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) {
return 1;
} // success
else
ESP_LOGW(TAG, "Missing timepulse, time not synced");
return 0; // failure
}
// interrupt service routine triggered by either rtc pps or esp32 hardware
// timer
void IRAM_ATTR CLOCKIRQ() {
xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL);
#if defined GPS_INT || defined RTC_INT #if defined GPS_INT || defined RTC_INT
xSemaphoreGiveFromISR(TimePulse, NULL); xSemaphoreGiveFromISR(TimePulse, NULL);
@ -92,6 +68,31 @@ void IRAM_ATTR CLOCKIRQ() {
portYIELD_FROM_ISR(); 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
}
#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
@ -153,6 +154,7 @@ int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970
ESP_LOGI(TAG, "RTC calibrated"); ESP_LOGI(TAG, "RTC calibrated");
return 1; // success return 1; // success
} }
ESP_LOGE(TAG, "RTC set time failure");
return 0; // failure return 0; // failure
} // set_rtctime() } // set_rtctime()