refactored clock controller code

This commit is contained in:
Klaus K Wilting 2019-02-16 15:02:07 +01:00
parent 91889a2af8
commit c8978a0745
17 changed files with 199 additions and 254 deletions

15
include/clockcontroller.h Normal file
View File

@ -0,0 +1,15 @@
#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

@ -2,16 +2,12 @@
#define _DCF77_H #define _DCF77_H
#include "globals.h" #include "globals.h"
#include "rtctime.h"
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 };
int dcf77_init(void); void DCF_Pulse(time_t startTime);
void dcf77_loop(void *pvParameters); void DCF77_Frame(time_t t);
void sendDCF77(void);
void DCF_Out(uint8_t startsec);
void generateTimeframe(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

@ -40,11 +40,17 @@
#define SCREEN_MODE (0x80) #define SCREEN_MODE (0x80)
// I2C bus access control // I2C bus access control
#define I2C_MUTEX_LOCK() xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == pdTRUE #define I2C_MUTEX_LOCK() \
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess) xSemaphoreTake(I2Caccess, (3 * DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == \
pdTRUE
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess)
// time pulse frequency 1Hz // Clock controller settings
#define PPS (1000) #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 {
@ -66,7 +72,8 @@ typedef struct {
uint8_t runmode; // 0=normal, 1=update uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data uint8_t payloadmask; // bitswitches for payload data
char version[10]; // Firmware version char version[10]; // Firmware version
uint8_t bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor uint8_t
bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor
} configData_t; } configData_t;
// Struct holding payload for data send queue // Struct holding payload for data send queue
@ -151,12 +158,4 @@ extern Timezone myTZ;
#include "bme680mems.h" #include "bme680mems.h"
#endif #endif
#ifdef HAS_IF482
#include "if482.h"
#endif
#ifdef HAS_DCF77
#include "dcf77.h"
#endif
#endif #endif

View File

@ -2,11 +2,12 @@
#define _IF482_H #define _IF482_H
#include "globals.h" #include "globals.h"
#include "rtctime.h"
int if482_init(void); extern HardwareSerial IF482;
void if482_loop(void *pvParameters);
TickType_t tx_time(unsigned long baud, uint32_t config, int8_t rxPin, void IF482_Pulse(time_t t);
String IF482_Frame(time_t tt);
TickType_t tx_Ticks(unsigned long baud, uint32_t config, int8_t rxPin,
int8_t txPins); int8_t txPins);
#endif #endif

View File

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

View File

@ -24,6 +24,7 @@ float get_rtctemp(void);
void IRAM_ATTR CLOCKIRQ(); void IRAM_ATTR CLOCKIRQ();
int timepulse_init(void); int timepulse_init(void);
void timepulse_start(); void timepulse_start();
void sync_clock(void); int sync_TimePulse(void);
int sync_SysTime(time_t);
#endif // _RTCTIME_H #endif // _RTCTIME_H

72
src/clockcontroller.cpp Normal file
View File

@ -0,0 +1,72 @@
#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
#if (PPS < IF482_PULSE_LENGTH) || (PPS < DCF77_PULSE_LENGTH)
#error On board timepulse too fast for clockcontroller
#endif
// Local logging tag
static const char TAG[] = "main";
void clock_init(void) { // ClockTask
timepulse_init(); // setup timepulse
// setup output interface
#ifdef HAS_IF482
// initialize and configure IF482 Generator
IF482.begin(HAS_IF482);
#elif defined HAS_DCF77
// initialize and configure DCF77 output
pinMode(HAS_DCF77, OUTPUT);
set_DCF77_pin(dcf_low);
#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?
timepulse_start(); // start pulse
} // clock_init
void clock_loop(void *pvParameters) { // ClockTask
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
TickType_t wakeTime, txDelay;
uint32_t pulseCycle;
void (*pTimeTx)(time_t); // pointer to time telegram output function
#ifdef HAS_IF482
txDelay = pdMS_TO_TICKS(1000) - tx_Ticks(HAS_IF482);
pulseCycle = PPS / IF482_PULSE_LENGTH;
pTimeTx = IF482_Pulse;
#elif defined HAS_DCF77
txDelay = pdMS_TO_TICKS(DCF77_PULSE_LENGTH);
pulseCycle = PPS / DCF77_PULSE_LENGTH;
pTimeTx = DCF_Pulse;
#endif
// output time telegram triggered by timepulse
for (;;) {
if (timeStatus() == timeSet) // do we have valid time?
xTaskNotifyWait(0x00, ULONG_MAX, &wakeTime,
portMAX_DELAY); // wait for timepulse
for (uint8_t i = 1; i <= pulseCycle; i++) {
pTimeTx(now());
vTaskDelayUntil(&wakeTime, txDelay);
}
} // for
} // clock_loop()
#endif // HAS_DCF77 || HAS_IF482

View File

@ -41,7 +41,8 @@ void doHousekeeping() {
if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) { if ((millis() >= nextRTCTimeSync) && (timeStatus() == timeSet)) {
nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC * nextRTCTimeSync = millis() + TIME_WRITE_INTERVAL_RTC *
60000; // set up next time sync period 60000; // set up next time sync period
if (!set_rtctime(now())) // epoch time sync_TimePulse(); // wait for next start of second
if (!set_rtctime(now())) // epoch time
ESP_LOGE(TAG, "RTC set time failure"); ESP_LOGE(TAG, "RTC set time failure");
else else
ESP_LOGI(TAG, "RTC time updated"); ESP_LOGI(TAG, "RTC time updated");

View File

@ -8,76 +8,41 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/
#ifdef HAS_DCF77 #ifdef HAS_DCF77
#ifdef IF_482
#error You must define at most one of IF482 or DCF77!
#endif
#include "dcf77.h" #include "dcf77.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
bool volatile BitsPending = false;
#define DCF77_FRAME_SIZE (60)
#define DCF77_PULSE_DURATION (100)
// array of dcf pulses for three minutes // array of dcf pulses for three minutes
uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; uint8_t DCFpulse[DCF77_FRAME_SIZE];
// initialize and configure DCF77 output // called by timepulse interrupt to ticker out DCF signal
int dcf77_init(void) { void DCF_Pulse(time_t startTime) {
pinMode(HAS_DCF77, OUTPUT); static uint8_t current_second = second(startTime);
set_DCF77_pin(dcf_low);
timepulse_init(); // setup timepulse
xTaskCreatePinnedToCore(dcf77_loop, // task function
"dcf77loop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
3, // priority of the task
&ClockTask, // task handle
0); // CPU core
assert(ClockTask); // has clock task started?
DCF_Out(second(now())); // sync DCF time on next second
timepulse_start(); // start pulse
return 1; // success
} // ifdcf77_init
// called every 100msec by hardware timer to pulse out DCF signal
void DCF_Out(uint8_t startOffset_sec) {
static uint8_t bit = startOffset_sec;
static uint8_t pulse = 0; static uint8_t pulse = 0;
#ifdef TIME_SYNC_INTERVAL_DCF static bool SecondsPending = false;
static uint32_t nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * 60000;
#endif
if (!BitsPending) { if (!SecondsPending) {
// do we have confident time/date? // prepare dcf timeframe to send for next minute
if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) { DCF77_Frame(now() + DCF77_FRAME_SIZE + 1);
// prepare frame to send for next minute ESP_LOGD(TAG, "DCF77 minute %d", minute(now() + DCF77_FRAME_SIZE + 1));
generateTimeframe(now() + DCF77_FRAME_SIZE + 1); // begin output of dcf timeframe
// kick off output of telegram SecondsPending = true;
BitsPending = true;
} else
return;
} }
// ticker out current DCF frame // ticker out current DCF frame
if (BitsPending) { if (SecondsPending) {
switch (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 (DCFtimeframe[bit] != dcf_off) if (DCFpulse[current_second] != dcf_off)
set_DCF77_pin(dcf_low); set_DCF77_pin(dcf_low);
ESP_LOGD(TAG, "DCF77 bit %d", current_second);
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 (DCFtimeframe[bit] == dcf_zero) if (DCFpulse[current_second] == dcf_zero)
set_DCF77_pin(dcf_high); set_DCF77_pin(dcf_high);
break; break;
@ -87,53 +52,17 @@ void DCF_Out(uint8_t startOffset_sec) {
case 9: // 900ms after start -> last pulse before next second starts case 9: // 900ms after start -> last pulse before next second starts
pulse = 0; pulse = 0;
if (bit++ == (DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second) if (current_second++ >=
(DCF77_FRAME_SIZE - 1)) // end of DCF77 frame (59th second)
{ {
bit = 0; current_second = 0;
BitsPending = false; SecondsPending = false;
// recalibrate clock after a fixed timespan, do this in 59th second
#ifdef TIME_SYNC_INTERVAL_DCF
if ((millis() >= nextDCFsync)) {
sync_clock(); // waiting for second 59
nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF *
60000; // set up next time sync period
}
#endif
}; };
break; break;
}; // switch }; // switch
}; // if }; // if
} // DCF_Out() } // DCF_Pulse()
void dcf77_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
TickType_t wakeTime;
// task remains in blocked state until it is notified by isr
for (;;) {
xTaskNotifyWait(
0x00, // don't clear any bits on entry
ULONG_MAX, // clear all bits on exit
&wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...)
// select clock scale
#if (PPS == DCF77_PULSE_DURATION) // we don't need clock rescaling
DCF_Out(0);
#elif (PPS > DCF77_PULSE_DURATION) // we need upclocking
for (uint8_t i = 1; i <= PPS / DCF77_PULSE_DURATION; i++) {
DCF_Out(0);
vTaskDelayUntil(&wakeTime, pdMS_TO_TICKS(DCF77_PULSE_DURATION));
}
#else // we need downclocking, not yet implemented
#error Timepulse too fast for DCF77 emulator
#endif
} // for
} // dcf77_loop()
// 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 dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
@ -151,40 +80,38 @@ uint8_t dec2bcd(uint8_t dec, uint8_t startpos, uint8_t endpos,
return parity; return parity;
} }
void generateTimeframe(time_t tt) { void DCF77_Frame(time_t tt) {
uint8_t ParityCount; uint8_t Parity;
time_t t = myTZ.toLocal(tt); // convert to local time time_t t = myTZ.toLocal(tt); // convert to local time
// ENCODE HEAD // ENCODE HEAD
// bits 0..19 initialized with zeros // bits 0..19 initialized with zeros
for (int n = 0; n <= 19; n++) for (int n = 0; n <= 19; n++)
DCFtimeframe[n] = dcf_zero; DCFpulse[n] = dcf_zero;
// bits 17..18: adjust for DayLightSaving // bits 17..18: adjust for DayLightSaving
DCFtimeframe[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one; DCFpulse[18 - (myTZ.locIsDST(t) ? 1 : 0)] = dcf_one;
// bit 20: must be 1 to indicate time active // bit 20: must be 1 to indicate time active
DCFtimeframe[20] = dcf_one; DCFpulse[20] = dcf_one;
// ENCODE MINUTE (bits 21..28) // ENCODE MINUTE (bits 21..28)
ParityCount = dec2bcd(minute(t), 21, 27, DCFtimeframe); Parity = dec2bcd(minute(t), 21, 27, DCFpulse);
DCFtimeframe[28] = (ParityCount & 1) ? dcf_one : dcf_zero; DCFpulse[28] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE HOUR (bits 29..35) // ENCODE HOUR (bits 29..35)
ParityCount = dec2bcd(hour(t), 29, 34, DCFtimeframe); Parity = dec2bcd(hour(t), 29, 34, DCFpulse);
DCFtimeframe[35] = (ParityCount & 1) ? dcf_one : dcf_zero; DCFpulse[35] = (Parity & 1) ? dcf_one : dcf_zero;
// ENCODE DATE (bits 36..58) // ENCODE DATE (bits 36..58)
ParityCount = dec2bcd(day(t), 36, 41, DCFtimeframe); Parity = dec2bcd(day(t), 36, 41, DCFpulse);
ParityCount += Parity += dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFpulse);
dec2bcd((weekday(t) - 1) ? (weekday(t) - 1) : 7, 42, 44, DCFtimeframe); Parity += dec2bcd(month(t), 45, 49, DCFpulse);
ParityCount += dec2bcd(month(t), 45, 49, DCFtimeframe); Parity += dec2bcd(year(t) - 2000, 50, 57,
ParityCount += DCFpulse); // yes, we have a millenium 3000 bug here ;-)
dec2bcd(year(t) - 2000, 50, 57, DCFpulse[58] = (Parity & 1) ? dcf_one : dcf_zero;
DCFtimeframe); // yes, we have a millenium 3000 bug here ;-)
DCFtimeframe[58] = (ParityCount & 1) ? dcf_one : dcf_zero;
// ENCODE TAIL (bit 59) // ENCODE TAIL (bit 59)
DCFtimeframe[59] = dcf_off; DCFpulse[59] = dcf_off;
// !! missing code here for leap second !! // !! missing code here for leap second !!
/* /*
@ -192,7 +119,7 @@ void generateTimeframe(time_t tt) {
char out[DCF77_FRAME_SIZE + 1]; char out[DCF77_FRAME_SIZE + 1];
uint8_t i; uint8_t i;
for (i = 0; i < DCF77_FRAME_SIZE; i++) { for (i = 0; i < DCF77_FRAME_SIZE; i++) {
out[i] = DCFtimeframe[i] + '0'; // convert int digit to printable ascii out[i] = DCFpulse[i] + '0'; // convert int digit to printable ascii
} }
out[DCF77_FRAME_SIZE] = '\0'; // string termination char out[DCF77_FRAME_SIZE] = '\0'; // string termination char
ESP_LOGD(TAG, "DCF Timeframe = %s", out); ESP_LOGD(TAG, "DCF Timeframe = %s", out);
@ -219,4 +146,6 @@ 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

@ -139,7 +139,7 @@ void refreshtheDisplay() {
u8x8.setPowerSave(!cfg.screenon); u8x8.setPowerSave(!cfg.screenon);
} }
// if display is switched off we don't refresh it and save time // if display is switched off we don't refresh it to relax cpu
if (!DisplayState) if (!DisplayState)
return; return;
@ -220,7 +220,7 @@ void refreshtheDisplay() {
#if (!defined HAS_DCF77) && (!defined HAS_IF482) #if (!defined HAS_DCF77) && (!defined HAS_IF482)
// 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 time display instead LoRa status #else // we want a systime display instead LoRa status
time_t t = myTZ.toLocal(now()); time_t t = myTZ.toLocal(now());
char timeState = char timeState =
(timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol; (timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol;

View File

@ -88,8 +88,8 @@ time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh,
// function to fetch current time from gps // function to fetch current time from gps
time_t get_gpstime(void) { time_t get_gpstime(void) {
// !! never call now() in this function, this would break this function // !! never call now() or delay in this function, this would break this
// to be used as SyncProvider due to recursive call to now() // function to be used as SyncProvider for Time.h
if ((gps.time.age() < 1500) && (gps.time.isValid())) { if ((gps.time.age() < 1500) && (gps.time.isValid())) {
// get current gps time // get current gps time
@ -98,9 +98,7 @@ time_t get_gpstime(void) {
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), ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t),
day(t), hour(t), minute(t), second(t)); day(t), hour(t), minute(t), second(t));
// sync on top of next second by timepulse return t;
sync_clock();
return t + 1;
} else { } else {
ESP_LOGW(TAG, "GPS has no confident time"); ESP_LOGW(TAG, "GPS has no confident time");
return 0; // sync failure, 0 effects calling SyncProvider() to not set time return 0; // sync failure, 0 effects calling SyncProvider() to not set time

View File

@ -79,46 +79,21 @@ not evaluated by model BU-190
#ifdef HAS_IF482 #ifdef HAS_IF482
#ifdef HAS_DCF77
#error You must define at most one of IF482 or DCF77!
#endif
#include "if482.h" #include "if482.h"
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
#define IF482_FRAME_SIZE (17)
#define IF482_PULSE_DURATION (1000)
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)
// initialize and configure IF482 Generator // called by timepulse interrupt to ticker out DCF signal
int if482_init(void) { void IF482_Pulse(time_t startTime) {
IF482.print(IF482_Frame(startTime + 1)); // if482 telegram for next second
}
// open serial interface String IF482_Frame(time_t startTime) {
IF482.begin(HAS_IF482);
// setup timepulse
timepulse_init();
// start if482 serial output feed task time_t t = myTZ.toLocal(startTime);
xTaskCreatePinnedToCore(if482_loop, // task function
"if482loop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
4, // priority of the task
&ClockTask, // task handle
0); // CPU core
assert(ClockTask); // has clock task started?
// timepulse_start(); // start pulse
return 1; // success
} // if482_init
String IF482_Out(time_t tt) {
time_t t = myTZ.toLocal(tt);
char mon, buf[14], out[IF482_FRAME_SIZE]; char mon, buf[14], out[IF482_FRAME_SIZE];
switch (timeStatus()) { // indicates if time has been set and recently synced switch (timeStatus()) { // indicates if time has been set and recently synced
@ -134,7 +109,7 @@ String IF482_Out(time_t tt) {
} // switch } // switch
// do we have confident time/date? // do we have confident time/date?
if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) 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 else
@ -146,62 +121,9 @@ String IF482_Out(time_t tt) {
return out; return out;
} }
void if482_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
TickType_t wakeTime;
const TickType_t tTx = tx_time(HAS_IF482); // duration of telegram transmit
// phase 1: sync task on top of second
const TickType_t t0 = xTaskGetTickCount(); // moment of start top of second
sync_clock(); // delay until top of second
timepulse_start(); // start timepulse
xTaskNotifyWait(
0x00, // don't clear any bits on entry
ULONG_MAX, // clear all bits on exit
&wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...)
const TickType_t tOffset = wakeTime - t0;
const TickType_t tShot =
(tOffset < tTx) ? (1000 - tOffset - tTx) : (tOffset - tTx);
ESP_LOGI(TAG, "IF482 signal synced with clock, tShot=%dms", tShot);
// phase 2: sync task on time pulse interrupt
for (;;) {
xTaskNotifyWait(
0x00, // don't clear any bits on entry
ULONG_MAX, // clear all bits on exit
&wakeTime, // receives moment of call from isr
portMAX_DELAY); // wait forever (missing error handling here...)
// select clock scale
#if (PPS == IF482_PULSE_DURATION) // we don't need clock rescaling
// wait until it's time to start transmit telegram for next second
vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of tShot
IF482.print(IF482_Out(now() + 1));
#elif (PPS > IF482_PULSE_DURATION) // we need upclocking
for (uint8_t i = 1; i <= PPS / IF482_PULSE_DURATION; i++) {
vTaskDelayUntil(&wakeTime, tShot); // sets waketime to moment of shot
IF482.print(IF482_Out(now() + 1));
}
#else // we need downclocking, not yet implemented
#error Timepulse too fast for IF482 generator
#endif
} // forever
} // if482_loop()
// calculate serial tx time from IF482 serial settings // calculate serial tx time from IF482 serial settings
TickType_t tx_time(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) {
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;

View File

@ -16,7 +16,7 @@ void irqHandler(void *pvParameters) {
0x00, // Don't clear any bits on entry 0x00, // Don't clear any bits on entry
ULONG_MAX, // Clear all bits on exit ULONG_MAX, // Clear all bits on exit
&InterruptStatus, // Receives the notification value &InterruptStatus, // Receives the notification value
portMAX_DELAY); // wait forever (missing error handling here...) portMAX_DELAY); // wait forever
// button pressed? // button pressed?
#ifdef HAS_BUTTON #ifdef HAS_BUTTON

View File

@ -6,7 +6,7 @@ static const char TAG[] = "lora";
#ifdef HAS_LORA #ifdef HAS_LORA
#if CLOCK_ERROR_PROCENTAGE > 7 #if CLOCK_ERROR_PROCENTAGE > 7
#warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects #warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects
#endif #endif
@ -408,7 +408,7 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
BaseType_t ret; BaseType_t ret;
switch (prio) { switch (prio) {
case prio_high: case prio_high:
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
break; break;
case prio_low: case prio_low:
case prio_normal: case prio_normal:
@ -471,7 +471,8 @@ void user_request_network_time_callback(void *pVoidUserUTCTime,
setTime(*pUserUTCTime); setTime(*pUserUTCTime);
ESP_LOGI(TAG, "LoRaWAN network has set the system time"); ESP_LOGI(TAG, "LoRaWAN network has set the system time");
#ifdef HAS_RTC #ifdef HAS_RTC
if (!set_rtctime(*pUserUTCTime)) // epoch time sync_TimePulse(); // wait for next start of second
if (!set_rtctime(*pUserUTCTime+1)) // epoch time
ESP_LOGE(TAG, "RTC set time failure"); ESP_LOGE(TAG, "RTC set time failure");
#endif #endif
} }

View File

@ -338,6 +338,7 @@ 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
setSyncProvider(get_rtctime); // sync time now and then setSyncProvider(get_rtctime); // sync time now and then
if (timeStatus() != timeSet) if (timeStatus() != timeSet)
ESP_LOGI(TAG, "Unable to sync system time with RTC"); ESP_LOGI(TAG, "Unable to sync system time with RTC");
@ -416,6 +417,7 @@ void setup() {
#endif // HAS_BUTTON #endif // HAS_BUTTON
#ifdef HAS_GPS #ifdef HAS_GPS
sync_TimePulse(); // wait for next start of second
setSyncProvider(get_gpstime); // sync time now and then setSyncProvider(get_gpstime); // sync time now and then
if (timeStatus() != timeSet) if (timeStatus() != timeSet)
ESP_LOGI(TAG, "Unable to sync system time with GPS"); ESP_LOGI(TAG, "Unable to sync system time with GPS");
@ -429,12 +431,9 @@ void setup() {
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60); setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
#endif #endif
#ifdef HAS_IF482 #if defined HAS_IF482 || defined HAS_DCF77
ESP_LOGI(TAG, "Starting IF482 Generator..."); ESP_LOGI(TAG, "Starting Clock Controller...");
assert(if482_init()); clock_init();
#elif defined HAS_DCF77
ESP_LOGI(TAG, "Starting DCF77 Generator...");
assert(dcf77_init());
#endif #endif
} // setup() } // setup()

View File

@ -82,11 +82,10 @@
#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 source 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_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
#define TIME_SYNC_INTERVAL_DCF 60 // sync DCF signal time each .. minutes from internal time [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
#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

@ -16,7 +16,7 @@ int timepulse_init() {
// setup external interupt for active low RTC INT pin // setup external interupt for active low RTC INT pin
pinMode(GPS_INT, INPUT_PULLDOWN); pinMode(GPS_INT, INPUT_PULLDOWN);
// setup external rtc 1Hz clock as pulse per second clock // setup external rtc 1Hz clock as pulse per second clock
ESP_LOGI(TAG, "Time base: GPS timepulse"); ESP_LOGI(TAG, "Time base: external (GPS)");
return 1; // success return 1; // success
// use pulse from on board RTC chip as time base with fixed frequency // use pulse from on board RTC chip as time base with fixed frequency
@ -30,7 +30,7 @@ int timepulse_init() {
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
ESP_LOGI(TAG, "Time base: external RTC timepulse"); ESP_LOGI(TAG, "Time base: external (RTC)");
return 1; // success return 1; // success
} else { } else {
ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); ESP_LOGE(TAG, "I2c bus busy - RTC initialization error");
@ -41,9 +41,9 @@ 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); //timerAttachInterrupt(clockCycle, &CLOCKIRQ, true);
timerAlarmWrite(clockCycle, 10000, true); // 1000ms timerAlarmWrite(clockCycle, 10000, true); // 1000ms
ESP_LOGI(TAG, "Time base: ESP32 hardware timer"); ESP_LOGI(TAG, "Time base: internal (ESP32 hardware timer)");
return 1; // success return 1; // success
#endif #endif
@ -55,18 +55,30 @@ void timepulse_start(void) {
#elif defined RTC_INT // start external clock rtc #elif defined RTC_INT // start external clock rtc
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
#else // start internal clock esp32 hardware timer #else // start internal clock esp32 hardware timer
timerAttachInterrupt(clockCycle, &CLOCKIRQ, true);
timerAlarmEnable(clockCycle); timerAlarmEnable(clockCycle);
#endif #endif
} }
// helper function to sync time_t of top of a second // helper function to sync systime on start of next second
void sync_clock(void) { int sync_SysTime(time_t t) {
if (sync_TimePulse()) {
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 // sync on top of next second by timepulse
if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(PPS)) == pdTRUE) if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(PPS)) == pdTRUE) {
ESP_LOGI(TAG, "clock synced by timepulse"); return 1;
} // success
else else
ESP_LOGW(TAG, "Missing timepulse, clock not synced"); ESP_LOGW(TAG, "Missing timepulse, time not synced");
return; return 0; // failure
} }
// interrupt service routine triggered by either rtc pps or esp32 hardware // interrupt service routine triggered by either rtc pps or esp32 hardware
@ -136,7 +148,6 @@ error:
int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
sync_clock(); // wait for top of second
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 calibrated");
@ -151,7 +162,8 @@ int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970
} }
time_t get_rtctime(void) { time_t get_rtctime(void) {
// never call now() in this function, this would cause a recursion! // !! never call now() or delay in this function, this would break this
// function to be used as SyncProvider for Time.h
time_t t = 0; time_t t = 0;
// block i2c bus access // block i2c bus access
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {