From df8d218c43fa94b0f938d5629f38d2efb3b4a716 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 13 Jan 2022 23:30:18 +0100 Subject: [PATCH 01/22] removal of all time libs (inital commit) --- README.md | 10 +-- include/dcf77.h | 5 +- include/globals.h | 4 +- include/gpsread.h | 1 - include/mobaserial.h | 2 +- include/timekeeper.h | 10 ++- include/timesync.h | 2 - src/dcf77.cpp | 46 +++++++------ src/display.cpp | 18 +++-- src/gpsread.cpp | 42 +++++------- src/if482.cpp | 13 ++-- src/irqhandler.cpp | 1 - src/ledmatrixdisplay.cpp | 6 +- src/main.cpp | 1 + src/mobaserial.cpp | 24 +++---- src/rcommand.cpp | 4 +- src/reset.cpp | 11 ++-- src/sdcard.cpp | 2 +- src/timekeeper.cpp | 137 +++++++++++++++++++++++++++++---------- src/timesync.cpp | 5 +- 20 files changed, 208 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 55c482c3..0994689a 100644 --- a/README.md +++ b/README.md @@ -569,17 +569,17 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ bytes 1..4 = time/date in UTC epoch seconds (LSB) byte 5 = time source & status, see below - bits 0..3 last seen time source + bits 0..3 time source 0x00 = GPS 0x01 = RTC 0x02 = LORA - 0x03 = unsynched (never synched) + 0x03 = unsynched 0x04 = set (source unknown) bits 4..7 time status - 0x00 = timeNotSet (never synched) - 0x01 = timeNeedsSync (last sync failed) - 0x02 = timeSet (synched) + 0x00 = unknown + 0x01 = time needs sync + 0x02 = time synched 0x87 sync time/date diff --git a/include/dcf77.h b/include/dcf77.h index 964389ee..96b4583b 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -4,9 +4,6 @@ #include "globals.h" #include "timekeeper.h" -#define DCF77_FRAME_SIZE (60) -#define DCF77_PULSE_LENGTH (100) - #ifdef DCF77_ACTIVE_LOW enum dcf_pinstate { dcf_high, dcf_low }; #else @@ -16,7 +13,7 @@ enum dcf_pinstate { dcf_low, dcf_high }; enum DCF77_Pulses { dcf_Z, dcf_0, dcf_1 }; void DCF77_Pulse(time_t t, uint8_t const *DCFpulse); -uint8_t *IRAM_ATTR DCF77_Frame(time_t const t); +uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt); uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, uint8_t *DCFpulse); uint8_t IRAM_ATTR setParityBit(uint8_t const p); diff --git a/include/globals.h b/include/globals.h index f2547137..a3a60341 100644 --- a/include/globals.h +++ b/include/globals.h @@ -5,7 +5,6 @@ #include // Time functions -#include #include #include @@ -111,6 +110,7 @@ typedef struct { float pm25; } sdsStatus_t; -extern char clientId[20]; // unique clientID +extern char clientId[20]; // unique clientID +extern time_t _COMPILETIME; // epoch build time #endif \ No newline at end of file diff --git a/include/gpsread.h b/include/gpsread.h index a3cc7747..fe204bea 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -21,6 +21,5 @@ bool gps_hasfix(); void gps_storelocation(gpsStatus_t *gps_store); void gps_loop(void *pvParameters); time_t get_gpstime(uint16_t *msec); -time_t get_gpstime(void); #endif \ No newline at end of file diff --git a/include/mobaserial.h b/include/mobaserial.h index a9d56897..993db69e 100644 --- a/include/mobaserial.h +++ b/include/mobaserial.h @@ -10,7 +10,7 @@ #define MOBALINE_HEAD_PULSE_LENGTH (1500) void MOBALINE_Pulse(time_t t, uint8_t const *DCFpulse); -uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const t); +uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const tt); void IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, uint8_t *DCFpulse); diff --git a/include/timekeeper.h b/include/timekeeper.h index 2fc2c879..c95d41d0 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -9,13 +9,16 @@ #include "if482.h" #include "dcf77.h" +#define SECS_YR_2000 (946684800UL) // the time at the start of y2k +#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch +#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021 + enum timesource_t { _gps, _rtc, _lora, _unsynced, _set }; extern const char timeSetSymbols[]; extern Ticker timesyncer; extern timesource_t timeSource; extern TaskHandle_t ClockTask; -extern Timezone myTZ; extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern hw_timer_t *ppsIRQ; @@ -25,11 +28,12 @@ void clock_loop(void *pvParameters); void timepulse_start(void); void setTimeSyncIRQ(void); uint8_t timepulse_init(void); -time_t timeIsValid(time_t const t); +bool timeIsValid(time_t const t); void calibrateTime(void); void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource); -time_t compiledUTC(void); +time_t compileTime(const String compile_date); +time_t mkgmtime(const struct tm *ptm); TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPins); diff --git a/include/timesync.h b/include/timesync.h index a6db2a3d..cd517421 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -9,8 +9,6 @@ #define TIME_SYNC_FIXUP 25 // compensation for processing time [milliseconds] #define TIME_SYNC_MAX_SEQNO 0xfe // threshold for wrap around time_sync_seqNo #define TIME_SYNC_END_FLAG (TIME_SYNC_MAX_SEQNO + 1) // end of handshake marker -#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch -#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021 enum timesync_t { timesync_tx, diff --git a/src/dcf77.cpp b/src/dcf77.cpp index e579b216..8f27cf4e 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -20,9 +20,9 @@ static const char TAG[] = __FILE__; void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { TickType_t startTime = xTaskGetTickCount(); - uint8_t sec = myTZ.second(t); + uint8_t sec = t % 60; - ESP_LOGD(TAG, "[%s] DCF second: %d", myTZ.dateTime("H:i:s.v").c_str(), sec); + ESP_LOGD(TAG, "[%0.3f] DCF second: %d", _seconds(), sec); // induce a DCF Pulse for (uint8_t pulse = 0; pulse <= 2; pulse++) { @@ -46,51 +46,55 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { } // switch // pulse pause - vTaskDelayUntil(&startTime, pdMS_TO_TICKS(DCF77_PULSE_LENGTH)); + vTaskDelayUntil(&startTime, pdMS_TO_TICKS(100)); } // for } // DCF77_Pulse() -uint8_t *IRAM_ATTR DCF77_Frame(time_t const t) { +uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) { - // array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so - // we keep them statically to avoid same recalculation every minute + struct tm t = {0}; + localtime_r(&tt, &t); // convert to local time - static uint8_t DCFpulse[DCF77_FRAME_SIZE + 1] = { - dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, - dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, - dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_1}; + // array of dcf pulses for one minute + // secs 0..15 and 20 are never changing, thus we keep them statically to avoid + // same recalculation every minute + + static uint8_t DCFpulse[61] = {dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, + dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, + dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, + dcf_0, dcf_0, dcf_1}; uint8_t Parity; - // ENCODE DST CHANGE ANNOUNCEMENT (Sec 16) + // ENCODE DST CHANGE ANNOUNCEMENT (sec 16) DCFpulse[16] = dcf_0; // not yet implemented // ENCODE DAYLIGHTSAVING (secs 17..18) - DCFpulse[17] = myTZ.isDST(t) ? dcf_1 : dcf_0; - DCFpulse[18] = myTZ.isDST(t) ? dcf_0 : dcf_1; + // "01" = MEZ / "10" = MESZ + DCFpulse[17] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; + DCFpulse[18] = (t.tm_isdst > 0) ? dcf_0 : dcf_1; // ENCODE MINUTE (secs 21..28) - Parity = dec2bcd(myTZ.minute(t), 21, 27, DCFpulse); + Parity = dec2bcd(t.tm_min, 21, 27, DCFpulse); DCFpulse[28] = setParityBit(Parity); // ENCODE HOUR (secs 29..35) - Parity = dec2bcd(myTZ.hour(t), 29, 34, DCFpulse); + Parity = dec2bcd(t.tm_hour, 29, 34, DCFpulse); DCFpulse[35] = setParityBit(Parity); // ENCODE DATE (secs 36..58) - Parity = dec2bcd(myTZ.day(t), 36, 41, DCFpulse); - Parity += dec2bcd((myTZ.weekday(t) - 1) ? (myTZ.weekday(t) - 1) : 7, 42, 44, - DCFpulse); - Parity += dec2bcd(myTZ.month(t), 45, 49, DCFpulse); - Parity += dec2bcd(myTZ.year(t) - 2000, 50, 57, DCFpulse); + Parity = dec2bcd(t.tm_mday, 36, 41, DCFpulse); + Parity += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, DCFpulse); + Parity += dec2bcd(t.tm_mon + 1, 45, 49, DCFpulse); + Parity += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, DCFpulse); DCFpulse[58] = setParityBit(Parity); // ENCODE MARK (sec 59) DCFpulse[59] = dcf_Z; // !! missing code here for leap second !! // timestamp this frame with it's minute - DCFpulse[60] = myTZ.minute(t); + DCFpulse[60] = t.tm_min; return DCFpulse; diff --git a/src/display.cpp b/src/display.cpp index 34046274..fdf70904 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -220,7 +220,10 @@ void dp_drawPage(bool nextpage) { // nextpage = true -> flip 1 page static uint8_t DisplayPage = 0; - char timeState; + char timeState, strftime_buf[64]; + time_t now; + struct tm timeinfo = {0}; + #if (HAS_GPS) static bool wasnofix = true; #endif @@ -320,11 +323,15 @@ void dp_drawPage(bool nextpage) { dp_println(); // line 6: time + date - // 27.Feb 2019 20:27:00* + // Wed Jan 12 21:49:08 * + #if (TIME_SYNC_INTERVAL) timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource]; TimePulseTick = false; - dp_printf("%s", myTZ.dateTime("d.M Y H:i:s").c_str()); + time(&now); + localtime_r(&now, &timeinfo); + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + dp_printf("%.20s", strftime_buf); // display inverse timeState if clock controller is enabled #if (defined HAS_DCF77) || (defined HAS_IF482) @@ -442,7 +449,10 @@ void dp_drawPage(bool nextpage) { dp_setFont(MY_FONT_LARGE); dp_setTextCursor(0, 4); - dp_printf("%s", myTZ.dateTime("H:i:s").c_str()); + time(&now); + localtime_r(&now, &timeinfo); + strftime(strftime_buf, sizeof(strftime_buf), "%T", &timeinfo); + dp_printf("%.8s", strftime_buf); break; // ---------- page 5: pax graph ---------- diff --git a/src/gpsread.cpp b/src/gpsread.cpp index ab163a24..8d5cb228 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -89,7 +89,7 @@ bool gps_hasfix() { gps.altitude.age() < 4000); } -// function to poll current time from GPS data; note: this is costly +// function to poll UTC time from GPS NMEA data; note: this is costly time_t get_gpstime(uint16_t *msec) { // poll NMEA ZDA sentence @@ -104,27 +104,26 @@ time_t get_gpstime(uint16_t *msec) { // did we get a current date & time? if (gpstime.isValid()) { - time_t t = 0; - tmElements_t tm; uint32_t delay_ms = gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR; uint32_t zdatime = atof(gpstime.value()); - // convert time to maketime format and make time - tm.Second = zdatime % 100; // second - tm.Minute = (zdatime / 100) % 100; // minute - tm.Hour = zdatime / 10000; // hour - tm.Day = atoi(gpsday.value()); // day - tm.Month = atoi(gpsmonth.value()); // month - tm.Year = atoi(gpsyear.value()) - 1970; // year offset from 1970 - t = makeTime(tm); - - ESP_LOGD(TAG, "GPS date/time: %s", - UTC.dateTime(t, "d.M Y H:i:s.v T").c_str()); + // convert UTC time from gps NMEA ZDA sentence to tm format + struct tm gps_tm = {0}; + gps_tm.tm_sec = zdatime % 100; // second (UTC) + gps_tm.tm_min = (zdatime / 100) % 100; // minute (UTC) + gps_tm.tm_hour = zdatime / 10000; // hour (UTC) + gps_tm.tm_mday = atoi(gpsday.value()); // day, 01 to 31 + gps_tm.tm_mon = atoi(gpsmonth.value()) - 1; // month, 01 to 12 + gps_tm.tm_year = atoi(gpsyear.value()) - 1900; // year, YYYY + + // convert UTC tm to time_t epoch + gps_tm.tm_isdst = 0; // UTC has no DST + time_t t = mkgmtime(&gps_tm); // add protocol delay with millisecond precision - t += delay_ms / 1000 - 1; // whole seconds - *msec = delay_ms % 1000; // fractional seconds + t += (time_t)(delay_ms / 1000); + *msec = delay_ms % 1000; // fractional seconds return t; } @@ -135,11 +134,6 @@ time_t get_gpstime(uint16_t *msec) { } // get_gpstime() -time_t get_gpstime(void) { - uint16_t msec; - return get_gpstime(&msec); -} - // GPS serial feed FreeRTos Task void gps_loop(void *pvParameters) { @@ -165,14 +159,14 @@ void gps_loop(void *pvParameters) { // GPS time, we trigger a device time update to poll time from GPS if ((timeSource == _unsynced || timeSource == _set) && (gpstime.isUpdated() && gpstime.isValid() && gpstime.age() < 1000)) { - now(); calibrateTime(); } } // if - // show NMEA data in verbose mode, useful only for debugging GPS, very noisy - // ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u", + // show NMEA data in verbose mode, useful only for debugging GPS, very + // noisy ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: + // %u", // gps.passedChecksum(), gps.failedChecksum(), // gps.sentencesWithFix()); diff --git a/src/if482.cpp b/src/if482.cpp index afbc16cc..c80ae641 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -88,11 +88,12 @@ String IRAM_ATTR IF482_Frame(time_t t) { char mon, out[IF482_FRAME_SIZE + 1]; - switch (timeStatus()) { // indicates if time has been set and recently synced - case timeSet: // time is set and is synced + switch (sntp_get_sync_status()) { // indicates if time has been set and recently synced + case SNTP_SYNC_STATUS_COMPLETED: // time is set and is synced mon = 'A'; break; - case timeNeedsSync: // time had been set but sync attempt did not succeed + case SNTP_SYNC_STATUS_IN_PROGRESS: // time had been set but sync attempt did not succeed + case SNTP_SYNC_STATUS_RESET: mon = 'M'; break; default: // unknown time status (should never be reached) @@ -101,10 +102,10 @@ String IRAM_ATTR IF482_Frame(time_t t) { } // switch // generate IF482 telegram - snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME, "ymdwHis").c_str()); + // snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME, + // "ymdwHis").c_str()); - ESP_LOGD(TAG, "[%s] IF482 date/time: %s", myTZ.dateTime("H:i:s.v").c_str(), - out); + //ESP_LOGD(TAG, "[%s] IF482 date/time: %s", ctime(time(NULL), out); return out; } diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index f6a6e295..b971aeac 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -51,7 +51,6 @@ void irqHandler(void *pvParameters) { #if (TIME_SYNC_INTERVAL) // is time to be synced? if (irqSource & TIMESYNC_IRQ) { - now(); // ensure sysTime is recent calibrateTime(); } #endif diff --git a/src/ledmatrixdisplay.cpp b/src/ledmatrixdisplay.cpp index 2abd38aa..e6b1d070 100644 --- a/src/ledmatrixdisplay.cpp +++ b/src/ledmatrixdisplay.cpp @@ -12,7 +12,7 @@ static const char TAG[] = __FILE__; uint8_t MatrixDisplayIsOn = 0; static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0}; static unsigned long ulLastNumMacs = 0; -static time_t ulLastTime = now(); +static time_t ulLastTime = time(NULL); hw_timer_t *matrixDisplayIRQ = NULL; @@ -116,11 +116,11 @@ void refreshTheMatrixDisplay(bool nextPage) { case 1: - const time_t t = now(); + const time_t t = time(NULL); if (ulLastTime != t) { ulLastTime = t; matrix.clear(); - DrawNumber(myTZ.dateTime("H:i:s").c_str()); + //DrawNumber(myTZ.dateTime("H:i:s").c_str()); } break; diff --git a/src/main.cpp b/src/main.cpp index 82587c23..63ef2300 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,6 +126,7 @@ void setup() { snprintf(clientId, 20, "paxcounter_%08x", hashedmac); ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId, PROGVERSION, RTC_runmode, RTC_restarts); + ESP_LOGI(TAG, "code build date: %d", _COMPILETIME); // print chip information on startup if in verbose mode after coldstart #if (VERBOSE) diff --git a/src/mobaserial.cpp b/src/mobaserial.cpp index bffdea09..6639e5fe 100644 --- a/src/mobaserial.cpp +++ b/src/mobaserial.cpp @@ -18,10 +18,9 @@ static const char TAG[] = __FILE__; void MOBALINE_Pulse(time_t t, uint8_t const *DCFpulse) { TickType_t startTime = xTaskGetTickCount(); - uint8_t sec = myTZ.second(t); + uint8_t sec = t % 60; - ESP_LOGD(TAG, "[%s] MOBALINE sec: %d", myTZ.dateTime("H:i:s.v").c_str(), - sec); + ESP_LOGD(TAG, "[%0.3f] MOBALINE sec: %d", _seconds(), sec); // induce 3 pulses for (uint8_t pulse = 0; pulse <= 3; pulse++) { @@ -65,29 +64,30 @@ uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const tt) { // array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so // we keep them statically to avoid same recalculation every minute - static uint8_t DCFpulse[DCF77_FRAME_SIZE + 1]; + static uint8_t DCFpulse[61]; - time_t t = myTZ.tzTime(tt); // convert to local time + struct tm t = {0}; + localtime_r(&tt, &t); // convert to local time // ENCODE HEAD (bit 0)) DCFpulse[0] = dcf_Z; // not yet implemented // ENCODE DAYLIGHTSAVING (bit 1) - DCFpulse[1] = myTZ.isDST(t) ? dcf_1 : dcf_0; + DCFpulse[1] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; // ENCODE DATE (bits 2..20) - dec2bcd(false, year(t) - 2000, 2, 9, DCFpulse); - dec2bcd(false, month(t), 10, 14, DCFpulse); - dec2bcd(false, day(t), 15, 20, DCFpulse); + dec2bcd(false, t.tm_year + 1900 - 2000, 2, 9, DCFpulse); + dec2bcd(false, t.tm_mon + 1, 10, 14, DCFpulse); + dec2bcd(false, t.tm_mday, 15, 20, DCFpulse); // ENCODE HOUR (bits 21..26) - dec2bcd2(false, hour(t), 21, 26, DCFpulse); + dec2bcd2(false, t.tm_hour, 21, 26, DCFpulse); // ENCODE MINUTE (bits 27..33) - dec2bcd2(false, minute(t), 27, 33, DCFpulse); + dec2bcd2(false, t.tm_min, 27, 33, DCFpulse); // timestamp this frame with it's minute - DCFpulse[34] = minute(t); + DCFpulse[34] = t.tm_min; return DCFpulse; diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 2f747958..c69a5fd3 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -372,10 +372,10 @@ void get_batt(uint8_t val[]) { void get_time(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get time"); - time_t t = now(); + time_t t = time(NULL); payload.reset(); payload.addTime(t); - payload.addByte(timeStatus() << 4 | timeSource); + payload.addByte(sntp_get_sync_status() << 4 | timeSource); SendPayload(TIMEPORT); }; diff --git a/src/reset.cpp b/src/reset.cpp index 7827b80a..8cfc1d3d 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -49,8 +49,9 @@ void do_after_reset(void) { // set time zone to user value from paxcounter.conf #ifdef TIME_SYNC_TIMEZONE - myTZ.setPosix(TIME_SYNC_TIMEZONE); - ESP_LOGD(TAG, "Timezone set to %s", myTZ.getPosix().c_str()); + setenv("TZ", TIME_SYNC_TIMEZONE, 1); + tzset(); + ESP_LOGD(TAG, "Timezone set to %s", TIME_SYNC_TIMEZONE); #endif switch (rtc_get_reset_reason(0)) { @@ -75,8 +76,8 @@ void do_after_reset(void) { RTC_millis += sleep_time_ms; // increment system monotonic time ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms); - // restore time - setMyTime(RTC_time, sleep_time_ms, _set); + // do we have a valid time? -> set global variable + timeSource = timeIsValid(sleep_stop_time.tv_sec) ? _set : _unsynced; // set wakeup state, not if we have pending OTA update if (RTC_runmode == RUNMODE_SLEEP) @@ -196,7 +197,7 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) { // time stamp sleep start time and save system monotonic time. Deep sleep. gettimeofday(&RTC_sleep_start_time, NULL); RTC_millis += millis(); - RTC_time = now(); + time(&RTC_time); ESP_LOGI(TAG, "Going to sleep, good bye."); esp_deep_sleep_start(); } diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 197edcfd..748a1a3e 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -43,7 +43,7 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle, __attribute__((unused)) uint16_t noBleCWA) { static int counterWrites = 0; char tempBuffer[12 + 1]; - time_t t = now(); + time_t t = time(NULL); #if (HAS_SDS011) sdsStatus_t sds; #endif diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index b1512bb8..be5178a2 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -8,20 +8,16 @@ #endif #endif -#define _COMPILETIME compileTime() - // Local logging tag static const char TAG[] = __FILE__; // symbol to display current time source -// G = GPS / R = RTC / L = LORA / ? = unsynced / = sync unknown -const char timeSetSymbols[] = {'G', 'R', 'L', '?', ' '}; - -// set Time Zone -Timezone myTZ; +// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced +const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'}; bool volatile TimePulseTick = false; timesource_t timeSource = _unsynced; +time_t _COMPILETIME = compileTime(__DATE__); TaskHandle_t ClockTask = NULL; hw_timer_t *ppsIRQ = NULL; @@ -76,15 +72,17 @@ void calibrateTime(void) { } // calibrateTime() -// adjust system time, calibrate RTC and RTC_INT pps +// set system time (UTC), calibrate RTC and RTC_INT pps void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource) { + struct timeval tv = {0}; + // called with invalid timesource? if (mytimesource == _unsynced) return; - // increment t_sec only if t_msec > 1000 + // increment t_sec if t_msec > 1000 time_t time_to_set = (time_t)(t_sec + t_msec / 1000); // do we have a valid time? @@ -97,8 +95,18 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); } - ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", _seconds(), time_to_set, - t_msec % 1000); + tv.tv_sec = time_to_set; + tv.tv_usec = 0; + settimeofday(&tv, NULL); + + ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set); + + // if we have a software pps timer, shift it to top of second + if (ppsIRQ != NULL) { + + timerWrite(ppsIRQ, 0); // reset pps timer + CLOCKIRQ(); // fire clock pps, this advances time 1 sec + } // if we have got an external timesource, set RTC time and shift RTC_INT pulse // to top of second @@ -107,15 +115,9 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, set_rtctime(time_to_set); #endif - // if we have a software pps timer, shift it to top of second - if (ppsIRQ != NULL) { - timerWrite(ppsIRQ, 0); // reset pps timer - CLOCKIRQ(); // fire clock pps, this advances time 1 sec - } - - UTC.setTime(time_to_set); // set the time on top of second - timeSource = mytimesource; // set global variable + sntp_set_sync_status(SNTP_SYNC_STATUS_COMPLETED); + timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | timesource=%d", _seconds(), mytimesource); @@ -196,7 +198,7 @@ void IRAM_ATTR CLOCKIRQ(void) { // advance wall clock, if we have #if (defined HAS_IF482 || defined HAS_DCF77) - xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, + xTaskNotifyFromISR(ClockTask, uint32_t(time(NULL)), eSetBits, &xHigherPriorityTaskWoken); #endif @@ -213,9 +215,9 @@ void IRAM_ATTR CLOCKIRQ(void) { } // helper function to check plausibility of a given epoch time -time_t timeIsValid(time_t const t) { - // is it a time in the past? we use compile date to guess - return (t < myTZ.tzTime(_COMPILETIME) ? 0 : t); +bool timeIsValid(time_t const t) { + // is t a time in the past? we use compile time to guess + return (t > _COMPILETIME); } // helper function to calculate serial transmit time @@ -245,7 +247,7 @@ void clock_init(void) { pinMode(HAS_DCF77, OUTPUT); #endif - time_t userUTCTime = now(); + time_t userUTCTime = time(NULL); xTaskCreatePinnedToCore(clock_loop, // task function "clockloop", // name of task @@ -263,17 +265,16 @@ void clock_loop(void *taskparameter) { // ClockTask // caveat: don't use now() in this task, it will cause a race condition // due to concurrent access to i2c bus when reading/writing from/to rtc chip! -#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute - #ifdef HAS_TWO_LED static bool led1_state = false; #endif uint32_t printtime; - time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds + time_t t = *((time_t *)taskparameter); // UTC time seconds + time_t last_printtime = 0; #ifdef HAS_DCF77 - uint8_t *DCFpulse; // pointer on array with DCF pulse bits - DCFpulse = DCF77_Frame(nextmin(t)); // load first DCF frame before start + uint8_t *DCFpulse; // pointer on array with DCF pulse bits + DCFpulse = DCF77_Frame(t + 61); // load first DCF frame before start #elif defined HAS_IF482 static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); @@ -286,9 +287,15 @@ void clock_loop(void *taskparameter) { // ClockTask xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); t = time_t(printtime); + /* + // no confident or no recent time -> suppress clock output + if ((sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) || + !(timeIsValid(t)) || (t == last_printtime)) + continue; + */ + // no confident or no recent time -> suppress clock output - if ((timeStatus() == timeNotSet) || !(timeIsValid(t)) || - (t == last_printtime)) + if (!(timeIsValid(t)) || (t == last_printtime)) continue; #if defined HAS_IF482 @@ -304,11 +311,11 @@ void clock_loop(void *taskparameter) { // ClockTask #elif defined HAS_DCF77 - if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? - DCFpulse = DCF77_Frame(nextmin(t)); // generate frame for next minute + if ((t % 60) == 59) // is it time to load new frame? + DCFpulse = DCF77_Frame(t + 61); // generate frame for next minute - if (minute(nextmin(t)) == // do we still have a recent frame? - DCFpulse[DCF77_FRAME_SIZE]) // (timepulses could be missed!) + if ((((t + 61) / 60) % 60) == // do we still have a recent frame? + DCFpulse[60]) // (timepulses could be missed!) DCF77_Pulse(t + 1, DCFpulse); // then output next second's pulse // else we have no recent frame, thus suppressing clock output @@ -330,3 +337,63 @@ void clock_loop(void *taskparameter) { // ClockTask } // clock_loop() #endif // HAS_IF482 || defined HAS_DCF77 + +// Convert compile date to reference time "in the past" +time_t compileTime(const String compile_date) { + + char s_month[5]; + int year; + struct tm t = {0}; + static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + + // store compile time once it's calculated + static time_t secs = -1; + + if (secs == -1) { + + // determine month + sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday, &year); + t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; + t.tm_year = year - 1900; + + // convert to secs + secs = mkgmtime(&t); + } + + return secs; +} + +static bool IsLeapYear(short year) { + if (year % 4 != 0) + return false; + if (year % 100 != 0) + return true; + return (year % 400) == 0; +} + +// convert UTC tm time to time_t epoch time +time_t mkgmtime(const struct tm *ptm) { + + const int SecondsPerMinute = 60; + const int SecondsPerHour = 3600; + const int SecondsPerDay = 86400; + const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + time_t secs = 0; + // tm_year is years since 1900 + int year = ptm->tm_year + 1900; + for (int y = 1970; y < year; ++y) { + secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay; + } + // tm_mon is month from 0..11 + for (int m = 0; m < ptm->tm_mon; ++m) { + secs += DaysOfMonth[m] * SecondsPerDay; + if (m == 1 && IsLeapYear(year)) + secs += SecondsPerDay; + } + secs += (ptm->tm_mday - 1) * SecondsPerDay; + secs += ptm->tm_hour * SecondsPerHour; + secs += ptm->tm_min * SecondsPerMinute; + secs += ptm->tm_sec; + return secs; +} \ No newline at end of file diff --git a/src/timesync.cpp b/src/timesync.cpp index a09bdd04..aa969897 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -130,7 +130,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { // calculate average time offset over the summed up difference time_offset_ms /= TIME_SYNC_SAMPLES; - + // add milliseconds from latest gateway time, and apply a compensation // constant for processing times on node and gateway, strip full seconds time_offset_ms += timesync_timestamp[sample_idx - 1][gwtime_msec]; @@ -142,9 +142,6 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { time_offset_sec = timesync_timestamp[sample_idx - 1][gwtime_sec]; time_offset_sec += time_offset_ms / 1000; - ESP_LOGD(TAG, "LORA date/time: %s", - myTZ.dateTime(time_offset_sec, "d.M Y H:i:s.v T").c_str()); - setMyTime(time_offset_sec, time_offset_ms, _lora); // send timesync end char to show timesync was successful From 83faf69c9fae43e4a318f5605919aa4d5d90a630 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 16 Jan 2022 19:42:54 +0100 Subject: [PATCH 02/22] rework dcf77 --- include/dcf77.h | 10 ++--- src/dcf77.cpp | 101 ++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 51 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index 96b4583b..c870fe70 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -12,10 +12,10 @@ enum dcf_pinstate { dcf_low, dcf_high }; enum DCF77_Pulses { dcf_Z, dcf_0, dcf_1 }; -void DCF77_Pulse(time_t t, uint8_t const *DCFpulse); -uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt); -uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, - uint8_t *DCFpulse); -uint8_t IRAM_ATTR setParityBit(uint8_t const p); +void DCF77_Pulse(uint8_t const bit); +void DCF77_Frame(const struct tm t, uint8_t *frame); +uint8_t dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, + uint8_t *frame); +uint8_t setParityBit(uint8_t const p); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 8f27cf4e..ff6c54aa 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -17,12 +17,9 @@ https://github.com/udoklein/dcf77 static const char TAG[] = __FILE__; // triggered by second timepulse to ticker out DCF signal -void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { +void DCF77_Pulse(uint8_t const bit) { TickType_t startTime = xTaskGetTickCount(); - uint8_t sec = t % 60; - - ESP_LOGD(TAG, "[%0.3f] DCF second: %d", _seconds(), sec); // induce a DCF Pulse for (uint8_t pulse = 0; pulse <= 2; pulse++) { @@ -30,12 +27,12 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { switch (pulse) { case 0: // start of second -> start of timeframe for logic signal - if (DCFpulse[sec] != dcf_Z) + if (bit != dcf_Z) digitalWrite(HAS_DCF77, dcf_low); break; case 1: // 100ms after start of second -> end of timeframe for logic 0 - if (DCFpulse[sec] == dcf_0) + if (bit == dcf_0) digitalWrite(HAS_DCF77, dcf_high); break; @@ -51,64 +48,78 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { } // for } // DCF77_Pulse() -uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) { +void DCF77_Frame(const struct tm t, uint8_t *frame) { - struct tm t = {0}; - localtime_r(&tt, &t); // convert to local time - - // array of dcf pulses for one minute - // secs 0..15 and 20 are never changing, thus we keep them statically to avoid - // same recalculation every minute - - static uint8_t DCFpulse[61] = {dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, - dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, - dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, dcf_0, - dcf_0, dcf_0, dcf_1}; + // writes a 1 minute dcf pulse scheme for calendar time t to frame uint8_t Parity; - // ENCODE DST CHANGE ANNOUNCEMENT (sec 16) - DCFpulse[16] = dcf_0; // not yet implemented + // START OF NEW MINUTE + frame[0] = dcf_0; - // ENCODE DAYLIGHTSAVING (secs 17..18) + // PAYLOAD -> not used here + frame[1] = dcf_0; + frame[2] = dcf_0; + frame[3] = dcf_0; + frame[4] = dcf_0; + frame[5] = dcf_0; + frame[6] = dcf_0; + frame[7] = dcf_0; + frame[8] = dcf_0; + frame[9] = dcf_0; + frame[10] = dcf_0; + frame[11] = dcf_0; + frame[12] = dcf_0; + frame[13] = dcf_0; + frame[14] = dcf_0; + frame[15] = dcf_0; + + // DST CHANGE ANNOUNCEMENT + frame[16] = dcf_0; // not yet implemented + + // DAYLIGHTSAVING // "01" = MEZ / "10" = MESZ - DCFpulse[17] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; - DCFpulse[18] = (t.tm_isdst > 0) ? dcf_0 : dcf_1; + frame[17] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; + frame[18] = (t.tm_isdst > 0) ? dcf_0 : dcf_1; - // ENCODE MINUTE (secs 21..28) - Parity = dec2bcd(t.tm_min, 21, 27, DCFpulse); - DCFpulse[28] = setParityBit(Parity); + // LEAP SECOND + frame[19] = dcf_0; // not implemented - // ENCODE HOUR (secs 29..35) - Parity = dec2bcd(t.tm_hour, 29, 34, DCFpulse); - DCFpulse[35] = setParityBit(Parity); + // BEGIN OF TIME INFORMATION + frame[20] = dcf_1; - // ENCODE DATE (secs 36..58) - Parity = dec2bcd(t.tm_mday, 36, 41, DCFpulse); - Parity += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, DCFpulse); - Parity += dec2bcd(t.tm_mon + 1, 45, 49, DCFpulse); - Parity += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, DCFpulse); - DCFpulse[58] = setParityBit(Parity); + // MINUTE (bits 21..28) + Parity = dec2bcd(t.tm_min, 21, 27, frame); + frame[28] = setParityBit(Parity); - // ENCODE MARK (sec 59) - DCFpulse[59] = dcf_Z; // !! missing code here for leap second !! + // HOUR (bits 29..35) + Parity = dec2bcd(t.tm_hour, 29, 34, frame); + frame[35] = setParityBit(Parity); - // timestamp this frame with it's minute - DCFpulse[60] = t.tm_min; + // DATE (bits 36..58) + Parity = dec2bcd(t.tm_mday, 36, 41, frame); + Parity += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, frame); + Parity += dec2bcd(t.tm_mon + 1, 45, 49, frame); + Parity += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, frame); + frame[58] = setParityBit(Parity); - return DCFpulse; + // MARK (bit 59) + frame[59] = dcf_Z; // !! missing code here for leap second !! + + // internal timestamp for the frame + frame[60] = t.tm_min; } // DCF77_Frame() // helper function to convert decimal to bcd digit -uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, - uint8_t const endpos, uint8_t *DCFpulse) { +uint8_t dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, + uint8_t *array) { uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t parity = 0; for (uint8_t i = startpos; i <= endpos; i++) { - DCFpulse[i] = (data & 1) ? dcf_1 : dcf_0; + array[i] = (data & 1) ? dcf_1 : dcf_0; parity += (data & 1); data >>= 1; } @@ -117,8 +128,6 @@ uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, } // helper function to encode parity -uint8_t IRAM_ATTR setParityBit(uint8_t const p) { - return ((p & 1) ? dcf_1 : dcf_0); -} +uint8_t setParityBit(uint8_t const p) { return ((p & 1) ? dcf_1 : dcf_0); } #endif // HAS_DCF77 \ No newline at end of file From 60f191f436ac62f12e9e1dfcefa963a9c53d468a Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 16 Jan 2022 19:43:06 +0100 Subject: [PATCH 03/22] rework if482 --- include/if482.h | 2 +- src/if482.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/if482.h b/include/if482.h index c1826b8b..faf2a01f 100644 --- a/include/if482.h +++ b/include/if482.h @@ -7,6 +7,6 @@ #define IF482_FRAME_SIZE (17) #define IF482_SYNC_FIXUP (10) // calibration to fixup processing time [milliseconds] -String IRAM_ATTR IF482_Frame(time_t t); +String IF482_Frame(time_t t); #endif \ No newline at end of file diff --git a/src/if482.cpp b/src/if482.cpp index c80ae641..b4fb6250 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -84,7 +84,7 @@ not evaluated by model BU-190, use "F" instead for this model // Local logging tag static const char TAG[] = __FILE__; -String IRAM_ATTR IF482_Frame(time_t t) { +String IF482_Frame(time_t t) { char mon, out[IF482_FRAME_SIZE + 1]; From 1ea263daf604d8b7e0588e0999ba4ccf92e463bb Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 16 Jan 2022 19:43:19 +0100 Subject: [PATCH 04/22] delete mobaline --- include/mobaserial.h | 17 ------- src/mobaserial.cpp | 108 ------------------------------------------- 2 files changed, 125 deletions(-) delete mode 100644 include/mobaserial.h delete mode 100644 src/mobaserial.cpp diff --git a/include/mobaserial.h b/include/mobaserial.h deleted file mode 100644 index 993db69e..00000000 --- a/include/mobaserial.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _MOBALINE_H -#define _MOBALINE_H - -#include "globals.h" -#include "timekeeper.h" -#include "dcf77.h" - -#define MOBALINE_FRAME_SIZE (33) -#define MOBALINE_PULSE_LENGTH (100) -#define MOBALINE_HEAD_PULSE_LENGTH (1500) - -void MOBALINE_Pulse(time_t t, uint8_t const *DCFpulse); -uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const tt); -void IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, - uint8_t const endpos, uint8_t *DCFpulse); - -#endif \ No newline at end of file diff --git a/src/mobaserial.cpp b/src/mobaserial.cpp deleted file mode 100644 index 6639e5fe..00000000 --- a/src/mobaserial.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* -// Emulate a MOBATIME serial clock controller -// -// Protocol published and described here: -// -// -http://www.elektrorevue.cz/cz/download/time-distribution-within-industry-4-0-platform--controlling-slave-clocks-via-master-clock-hn50/ -*/ - -#ifdef HAS_MOBALINE - -#include "mobaline.h" - -// Local logging tag -static const char TAG[] = __FILE__; - -// triggered by pulse per second to ticker out mobaline frame -void MOBALINE_Pulse(time_t t, uint8_t const *DCFpulse) { - - TickType_t startTime = xTaskGetTickCount(); - uint8_t sec = t % 60; - - ESP_LOGD(TAG, "[%0.3f] MOBALINE sec: %d", _seconds(), sec); - - // induce 3 pulses - for (uint8_t pulse = 0; pulse <= 3; pulse++) { - - switch (pulse) { - - case 0: // start of bit -> start of timeframe for logic signal - if (DCFpulse[sec] != dcf_Z) { - digitalWrite(HAS_DCF77, dcf_high); - vTaskDelay(pdMS_TO_TICKS(MOBALINE_HEAD_PULSE_LENGTH)); - digitalWrite(HAS_DCF77, dcf_high); - vTaskDelay(pdMS_TO_TICKS(MOBALINE_HEAD_PULSE_LENGTH)); - return; // next bit - } else // start the signalling for the next bit - digitalWrite(HAS_DCF77, dcf_high); - break; - - case 1: // 100ms after start of bit -> end of timeframe for logic 0 - if (DCFpulse[sec] == dcf_1) - digitalWrite(HAS_DCF77, dcf_low); - break; - - case 2: // 200ms after start of bit -> end of timeframe for logic 1 - if (DCFpulse[sec] == dcf_0) - digitalWrite(HAS_DCF77, dcf_low); - break; - - case 3: // 300ms after start -> last pulse - break; - - } // switch - - // pulse pause - vTaskDelayUntil(&startTime, pdMS_TO_TICKS(MOBALINE_PULSE_LENGTH)); - - } // for -} // DCF77_Pulse() - -uint8_t *IRAM_ATTR MOBALINE_Frame(time_t const tt) { - - // array of dcf pulses for one minute, secs 0..16 and 20 are never touched, so - // we keep them statically to avoid same recalculation every minute - - static uint8_t DCFpulse[61]; - - struct tm t = {0}; - localtime_r(&tt, &t); // convert to local time - - // ENCODE HEAD (bit 0)) - DCFpulse[0] = dcf_Z; // not yet implemented - - // ENCODE DAYLIGHTSAVING (bit 1) - DCFpulse[1] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; - - // ENCODE DATE (bits 2..20) - dec2bcd(false, t.tm_year + 1900 - 2000, 2, 9, DCFpulse); - dec2bcd(false, t.tm_mon + 1, 10, 14, DCFpulse); - dec2bcd(false, t.tm_mday, 15, 20, DCFpulse); - - // ENCODE HOUR (bits 21..26) - dec2bcd2(false, t.tm_hour, 21, 26, DCFpulse); - - // ENCODE MINUTE (bits 27..33) - dec2bcd2(false, t.tm_min, 27, 33, DCFpulse); - - // timestamp this frame with it's minute - DCFpulse[34] = t.tm_min; - - return DCFpulse; - -} // MOBALINE_Frame() - -// helper function to convert decimal to bcd digit msb -void IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, - uint8_t const endpos, uint8_t *DCFpulse) { - - uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); - - for (uint8_t i = endpos; i >= startpos; i--) { - DCFpulse[i] = (data & 1) ? dcf_1 : dcf_0; - data >>= 1; - } -} - -#endif // HAS_MOBALINE \ No newline at end of file From a7f957dedd74ee0959ef55d4c8315e4391b41463 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 16 Jan 2022 19:43:29 +0100 Subject: [PATCH 05/22] rework timekeeper --- src/timekeeper.cpp | 107 +++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index be5178a2..cf58e189 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -18,16 +18,22 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'}; bool volatile TimePulseTick = false; timesource_t timeSource = _unsynced; time_t _COMPILETIME = compileTime(__DATE__); - TaskHandle_t ClockTask = NULL; hw_timer_t *ppsIRQ = NULL; +#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 + #ifdef HAS_IF482 +HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS) +static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - + tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); #if (HAS_SDS011) #error cannot use IF482 together with SDS011 (both use UART#2) #endif -HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS) -#endif +#endif // HAS_IF482 Ticker timesyncer; @@ -194,8 +200,6 @@ void IRAM_ATTR CLOCKIRQ(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - // syncToPPS(); // currently not used - // advance wall clock, if we have #if (defined HAS_IF482 || defined HAS_DCF77) xTaskNotifyFromISR(ClockTask, uint32_t(time(NULL)), eSetBits, @@ -232,12 +236,6 @@ TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, return round(txTime); } -#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 @@ -247,78 +245,81 @@ void clock_init(void) { pinMode(HAS_DCF77, OUTPUT); #endif - time_t userUTCTime = time(NULL); - - xTaskCreatePinnedToCore(clock_loop, // task function - "clockloop", // name of task - 2048, // stack size of task - (void *)&userUTCTime, // start time as task parameter - 4, // priority of the task - &ClockTask, // task handle - 1); // CPU core + 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 + 1); // CPU core _ASSERT(ClockTask != NULL); // has clock task started? } // clock_init void clock_loop(void *taskparameter) { // ClockTask - // caveat: don't use now() in this task, it will cause a race condition - // due to concurrent access to i2c bus when reading/writing from/to rtc chip! - + uint8_t ClockPulse[61] = {0}; + uint32_t current_time = 0, previous_time = 0; + time_t tt; + struct tm t = {0}; #ifdef HAS_TWO_LED static bool led1_state = false; #endif - uint32_t printtime; - time_t t = *((time_t *)taskparameter); // UTC time seconds - time_t last_printtime = 0; - -#ifdef HAS_DCF77 - uint8_t *DCFpulse; // pointer on array with DCF pulse bits - DCFpulse = DCF77_Frame(t + 61); // load first DCF frame before start -#elif defined HAS_IF482 - static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); -#endif // output the next second's pulse/telegram after pps arrived for (;;) { - // wait for timepulse and store UTC time in seconds got - xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); - t = time_t(printtime); + // wait for timepulse and store UTC time + xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, portMAX_DELAY); /* // no confident or no recent time -> suppress clock output if ((sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) || - !(timeIsValid(t)) || (t == last_printtime)) + !(timeIsValid(current_time)) || (current_time == previous_time)) continue; */ // no confident or no recent time -> suppress clock output - if (!(timeIsValid(t)) || (t == last_printtime)) + if (!(timeIsValid(current_time)) || (current_time == previous_time)) continue; + // initialize calendar time for next second of clock output + tt = (time_t)(current_time + 1); + localtime_r(&tt, &t); + mktime(&t); + #if defined HAS_IF482 // wait until moment to fire. Normally we won't get notified during this // timespan, except when next pps pulse arrives while waiting, because pps - // was adjusted by recent time sync - if (xTaskNotifyWait(0x00, ULONG_MAX, &printtime, txDelay) == pdTRUE) - t = time_t(printtime); // new adjusted UTC time seconds + // was adjusted by recent time sync, then advance next_time one second + if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, txDelay) == pdTRUE) { + tt = (time_t)(current_time + 1); + localtime_r(&tt, &t); + mktime(&t); + } // send IF482 telegram - IF482.print(IF482_Frame(t + 2)); // note: telegram is for *next* second + IF482.print(IF482_Frame(t)); // note: telegram is for *next* second + + ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(t)); #elif defined HAS_DCF77 - if ((t % 60) == 59) // is it time to load new frame? - DCFpulse = DCF77_Frame(t + 61); // generate frame for next minute + if (t.tm_min == // do we still have a recent frame? + ClockPulse[60]) { // (timepulses could be missed!) + DCF77_Pulse(ClockPulse[t.tm_sec]); // then output next second's pulse + ESP_LOGD(TAG, "[%0.3f] DCF77: %02d:%02d:%02d", _seconds(), t.tm_hour, + t.tm_min, t.tm_sec); + } - if ((((t + 61) / 60) % 60) == // do we still have a recent frame? - DCFpulse[60]) // (timepulses could be missed!) - DCF77_Pulse(t + 1, DCFpulse); // then output next second's pulse - - // else we have no recent frame, thus suppressing clock output + if (t.tm_sec == 59) { // is it time to load new frame? + t.tm_min++; + mktime(&t); // normalize calendar time + DCF77_Frame(t, ClockPulse); // generate frame for next minute + ESP_LOGD(TAG, "[%0.3f] DCF77: new frame for min %d", _seconds(), + t.tm_min); + } #endif @@ -331,14 +332,14 @@ void clock_loop(void *taskparameter) { // ClockTask led1_state = !led1_state; #endif - last_printtime = t; + previous_time = current_time; } // for } // clock_loop() #endif // HAS_IF482 || defined HAS_DCF77 -// Convert compile date to reference time "in the past" +// we use compile date to create a time_t reference "in the past" time_t compileTime(const String compile_date) { char s_month[5]; @@ -356,7 +357,7 @@ time_t compileTime(const String compile_date) { t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; t.tm_year = year - 1900; - // convert to secs + // convert to secs UTC secs = mkgmtime(&t); } From ee46833dfa555fe616db7d9e844cfa10bb723d43 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Tue, 18 Jan 2022 01:00:40 +0100 Subject: [PATCH 06/22] rework dcf77 (part 2) --- include/dcf77.h | 11 ++--- src/dcf77.cpp | 117 ++++++++++++++++++--------------------------- src/timekeeper.cpp | 30 +++++++----- 3 files changed, 69 insertions(+), 89 deletions(-) diff --git a/include/dcf77.h b/include/dcf77.h index c870fe70..df9400e9 100644 --- a/include/dcf77.h +++ b/include/dcf77.h @@ -4,18 +4,15 @@ #include "globals.h" #include "timekeeper.h" +#define set_dcfbit(b) (1ULL << (b)) + #ifdef DCF77_ACTIVE_LOW enum dcf_pinstate { dcf_high, dcf_low }; #else enum dcf_pinstate { dcf_low, dcf_high }; #endif -enum DCF77_Pulses { dcf_Z, dcf_0, dcf_1 }; - -void DCF77_Pulse(uint8_t const bit); -void DCF77_Frame(const struct tm t, uint8_t *frame); -uint8_t dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, - uint8_t *frame); -uint8_t setParityBit(uint8_t const p); +void DCF77_Pulse(uint8_t bit); +uint64_t DCF77_Frame(const struct tm t); #endif \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index ff6c54aa..f8ca280c 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -17,7 +17,7 @@ https://github.com/udoklein/dcf77 static const char TAG[] = __FILE__; // triggered by second timepulse to ticker out DCF signal -void DCF77_Pulse(uint8_t const bit) { +void DCF77_Pulse(uint8_t bit) { TickType_t startTime = xTaskGetTickCount(); @@ -27,12 +27,11 @@ void DCF77_Pulse(uint8_t const bit) { switch (pulse) { case 0: // start of second -> start of timeframe for logic signal - if (bit != dcf_Z) - digitalWrite(HAS_DCF77, dcf_low); + digitalWrite(HAS_DCF77, dcf_low); break; case 1: // 100ms after start of second -> end of timeframe for logic 0 - if (bit == dcf_0) + if (bit == 0) digitalWrite(HAS_DCF77, dcf_high); break; @@ -48,86 +47,64 @@ void DCF77_Pulse(uint8_t const bit) { } // for } // DCF77_Pulse() -void DCF77_Frame(const struct tm t, uint8_t *frame) { +// helper function to convert decimal to bcd digit +uint64_t dec2bcd(uint8_t const dec, uint8_t const startpos, + uint8_t const endpos, uint8_t *odd_parity) { - // writes a 1 minute dcf pulse scheme for calendar time t to frame + uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); + uint64_t bcd = 0; - uint8_t Parity; + *odd_parity = 0; + for (uint8_t i = startpos; i <= endpos; i++) { + bcd += (data & 1) ? set_dcfbit(i) : 0; + *odd_parity += (data & 1); + data >>= 1; + } + *odd_parity %= 2; - // START OF NEW MINUTE - frame[0] = dcf_0; + return bcd; +} - // PAYLOAD -> not used here - frame[1] = dcf_0; - frame[2] = dcf_0; - frame[3] = dcf_0; - frame[4] = dcf_0; - frame[5] = dcf_0; - frame[6] = dcf_0; - frame[7] = dcf_0; - frame[8] = dcf_0; - frame[9] = dcf_0; - frame[10] = dcf_0; - frame[11] = dcf_0; - frame[12] = dcf_0; - frame[13] = dcf_0; - frame[14] = dcf_0; - frame[15] = dcf_0; +// generates a 1 minute dcf pulse frame for calendar time t +uint64_t DCF77_Frame(const struct tm t) { - // DST CHANGE ANNOUNCEMENT - frame[16] = dcf_0; // not yet implemented + uint8_t parity = 0, parity_sum = 0; + uint64_t frame = 0; // start with all bits 0 - // DAYLIGHTSAVING + // DST CHANGE ANNOUNCEMENT (16) + // -- not implemented -- + + // DAYLIGHTSAVING (17, 18) // "01" = MEZ / "10" = MESZ - frame[17] = (t.tm_isdst > 0) ? dcf_1 : dcf_0; - frame[18] = (t.tm_isdst > 0) ? dcf_0 : dcf_1; + frame += t.tm_isdst > 0 ? set_dcfbit(17) : set_dcfbit(18); - // LEAP SECOND - frame[19] = dcf_0; // not implemented + // LEAP SECOND (19) + // -- not implemented -- - // BEGIN OF TIME INFORMATION - frame[20] = dcf_1; + // BEGIN OF TIME INFORMATION (20) + frame += set_dcfbit(20); - // MINUTE (bits 21..28) - Parity = dec2bcd(t.tm_min, 21, 27, frame); - frame[28] = setParityBit(Parity); + // MINUTE (21..28) + frame += dec2bcd(t.tm_min, 21, 27, &parity); + frame += parity ? set_dcfbit(28) : 0; - // HOUR (bits 29..35) - Parity = dec2bcd(t.tm_hour, 29, 34, frame); - frame[35] = setParityBit(Parity); + // HOUR (29..35) + frame += dec2bcd(t.tm_hour, 29, 34, &parity); + frame += parity ? set_dcfbit(35) : 0; - // DATE (bits 36..58) - Parity = dec2bcd(t.tm_mday, 36, 41, frame); - Parity += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, frame); - Parity += dec2bcd(t.tm_mon + 1, 45, 49, frame); - Parity += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, frame); - frame[58] = setParityBit(Parity); + // DATE (36..58) + frame += dec2bcd(t.tm_mday, 36, 41, &parity); + parity_sum += parity; + frame += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, &parity); + parity_sum += parity; + frame += dec2bcd(t.tm_mon + 1, 45, 49, &parity); + parity_sum += parity; + frame += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, &parity); + parity_sum += parity; + frame += parity_sum % 2 ? set_dcfbit(58) : 0; - // MARK (bit 59) - frame[59] = dcf_Z; // !! missing code here for leap second !! - - // internal timestamp for the frame - frame[60] = t.tm_min; + return frame; } // DCF77_Frame() -// helper function to convert decimal to bcd digit -uint8_t dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, - uint8_t *array) { - - uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); - uint8_t parity = 0; - - for (uint8_t i = startpos; i <= endpos; i++) { - array[i] = (data & 1) ? dcf_1 : dcf_0; - parity += (data & 1); - data >>= 1; - } - - return parity; -} - -// helper function to encode parity -uint8_t setParityBit(uint8_t const p) { return ((p & 1) ? dcf_1 : dcf_0); } - #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index cf58e189..2b08e4e0 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -258,8 +258,9 @@ void clock_init(void) { void clock_loop(void *taskparameter) { // ClockTask - uint8_t ClockPulse[61] = {0}; + uint64_t ClockPulse = 0; uint32_t current_time = 0, previous_time = 0; + uint8_t ClockMinute = 0; time_t tt; struct tm t = {0}; #ifdef HAS_TWO_LED @@ -283,7 +284,7 @@ void clock_loop(void *taskparameter) { // ClockTask if (!(timeIsValid(current_time)) || (current_time == previous_time)) continue; - // initialize calendar time for next second of clock output + // set calendar time for next second of clock output tt = (time_t)(current_time + 1); localtime_r(&tt, &t); mktime(&t); @@ -306,19 +307,24 @@ void clock_loop(void *taskparameter) { // ClockTask #elif defined HAS_DCF77 - if (t.tm_min == // do we still have a recent frame? - ClockPulse[60]) { // (timepulses could be missed!) - DCF77_Pulse(ClockPulse[t.tm_sec]); // then output next second's pulse - ESP_LOGD(TAG, "[%0.3f] DCF77: %02d:%02d:%02d", _seconds(), t.tm_hour, - t.tm_min, t.tm_sec); - } + // load new frame if second 59 is reached + if (t.tm_sec == 0) { + ClockMinute = t.tm_min; + t.tm_min++; // follow-up minute + mktime(&t); // normalize calendar time + ClockPulse = DCF77_Frame(t); // generate pulse frame + + /* to do here: leap second handling in second 59 */ - if (t.tm_sec == 59) { // is it time to load new frame? - t.tm_min++; - mktime(&t); // normalize calendar time - DCF77_Frame(t, ClockPulse); // generate frame for next minute ESP_LOGD(TAG, "[%0.3f] DCF77: new frame for min %d", _seconds(), t.tm_min); + } else { + + // generate impulse + if (t.tm_min == ClockMinute) { // ensure frame is recent + DCF77_Pulse(ClockPulse & 1); // output next second + ClockPulse >>= 1; + } } #endif From 7392fec66c63186f80e0083fd81f05c72e97e549 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Tue, 18 Jan 2022 01:01:05 +0100 Subject: [PATCH 07/22] set compile date back one day --- src/timekeeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 2b08e4e0..b5a36ccc 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -359,7 +359,7 @@ time_t compileTime(const String compile_date) { if (secs == -1) { // determine month - sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday, &year); + sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year); t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; t.tm_year = year - 1900; From cdad3c1891759d46a09c2396b1a4d78d0fbd16e7 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Tue, 18 Jan 2022 16:07:24 +0100 Subject: [PATCH 08/22] increase task stack size for clock_loop() --- src/timekeeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index b5a36ccc..d88568a4 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -247,7 +247,7 @@ void clock_init(void) { xTaskCreatePinnedToCore(clock_loop, // task function "clockloop", // name of task - 2048, // stack size of task + 3072, // stack size of task (void *)1, // task parameter 4, // priority of the task &ClockTask, // task handle From 7cc37ac8b990fc9c211644f607d60356c45316c4 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 19 Jan 2022 14:25:17 +0100 Subject: [PATCH 09/22] bugfix dcf77 pulse pause --- src/dcf77.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/dcf77.cpp b/src/dcf77.cpp index f8ca280c..fd59cb51 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -19,29 +19,31 @@ static const char TAG[] = __FILE__; // triggered by second timepulse to ticker out DCF signal void DCF77_Pulse(uint8_t bit) { - TickType_t startTime = xTaskGetTickCount(); + TickType_t startTime; // induce a DCF Pulse - for (uint8_t pulse = 0; pulse <= 2; pulse++) { + for (uint8_t pulseLength = 0; pulseLength <= 2; pulseLength++) { - switch (pulse) { + startTime = xTaskGetTickCount(); // reference time pulse start - case 0: // start of second -> start of timeframe for logic signal + switch (pulseLength) { + + case 0: // 0ms = start of pulse digitalWrite(HAS_DCF77, dcf_low); break; - case 1: // 100ms after start of second -> end of timeframe for logic 0 + case 1: // 100ms = logic 0 if (bit == 0) digitalWrite(HAS_DCF77, dcf_high); break; - case 2: // 200ms after start of second -> end of timeframe for logic 1 + case 2: // 200ms = logic 1 digitalWrite(HAS_DCF77, dcf_high); break; } // switch - // pulse pause + // delay to genrate pulseLength vTaskDelayUntil(&startTime, pdMS_TO_TICKS(100)); } // for From 76317efc00ba5cd7248fc5bfbc0944451272b823 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 19 Jan 2022 14:27:16 +0100 Subject: [PATCH 10/22] timekeeper.cpp: fix edge case ClockMinute = 0 --- src/timekeeper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index d88568a4..88e341df 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -22,6 +22,7 @@ TaskHandle_t ClockTask = NULL; hw_timer_t *ppsIRQ = NULL; #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 @@ -33,6 +34,7 @@ static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - #if (HAS_SDS011) #error cannot use IF482 together with SDS011 (both use UART#2) #endif + #endif // HAS_IF482 Ticker timesyncer; @@ -260,7 +262,7 @@ void clock_loop(void *taskparameter) { // ClockTask uint64_t ClockPulse = 0; uint32_t current_time = 0, previous_time = 0; - uint8_t ClockMinute = 0; + int8_t ClockMinute = -1; time_t tt; struct tm t = {0}; #ifdef HAS_TWO_LED From b2c89b3e1a171e47a31e6abc99c3e1c7fab7b993 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 19 Jan 2022 20:29:31 +0100 Subject: [PATCH 11/22] timekeeper.cpp: fix #if #endif issue --- src/timekeeper.cpp | 54 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 88e341df..ef614624 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -8,6 +8,10 @@ #endif #endif +#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[] = __FILE__; @@ -21,12 +25,6 @@ time_t _COMPILETIME = compileTime(__DATE__); TaskHandle_t ClockTask = NULL; hw_timer_t *ppsIRQ = NULL; -#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 - #ifdef HAS_IF482 HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS) static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - @@ -345,8 +343,6 @@ void clock_loop(void *taskparameter) { // ClockTask } // for } // clock_loop() -#endif // HAS_IF482 || defined HAS_DCF77 - // we use compile date to create a time_t reference "in the past" time_t compileTime(const String compile_date) { @@ -360,49 +356,15 @@ time_t compileTime(const String compile_date) { if (secs == -1) { - // determine month + // determine date + // we go one day back to bypass unknown timezone of local time sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year); t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; t.tm_year = year - 1900; - // convert to secs UTC - secs = mkgmtime(&t); + // convert to secs local time + secs = mktime(&t); } - return secs; -} - -static bool IsLeapYear(short year) { - if (year % 4 != 0) - return false; - if (year % 100 != 0) - return true; - return (year % 400) == 0; -} - -// convert UTC tm time to time_t epoch time -time_t mkgmtime(const struct tm *ptm) { - - const int SecondsPerMinute = 60; - const int SecondsPerHour = 3600; - const int SecondsPerDay = 86400; - const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - - time_t secs = 0; - // tm_year is years since 1900 - int year = ptm->tm_year + 1900; - for (int y = 1970; y < year; ++y) { - secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay; - } - // tm_mon is month from 0..11 - for (int m = 0; m < ptm->tm_mon; ++m) { - secs += DaysOfMonth[m] * SecondsPerDay; - if (m == 1 && IsLeapYear(year)) - secs += SecondsPerDay; - } - secs += (ptm->tm_mday - 1) * SecondsPerDay; - secs += ptm->tm_hour * SecondsPerHour; - secs += ptm->tm_min * SecondsPerMinute; - secs += ptm->tm_sec; return secs; } \ No newline at end of file From 3e045bfcf4eb63bd44465ee47425e550ad1965a2 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 11:43:41 +0100 Subject: [PATCH 12/22] bugfix curve plotter display --- src/display.cpp | 3 --- src/ledmatrixdisplay.cpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index fdf70904..07017bf8 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -174,9 +174,6 @@ void dp_init(bool verbose) { void dp_refresh(bool nextPage) { - // update counter values from libpax - libpax_counter_count(&count_from_libpax); - #ifndef HAS_BUTTON static uint32_t framecounter = 0; #endif diff --git a/src/ledmatrixdisplay.cpp b/src/ledmatrixdisplay.cpp index e6b1d070..25faf2fa 100644 --- a/src/ledmatrixdisplay.cpp +++ b/src/ledmatrixdisplay.cpp @@ -75,9 +75,6 @@ void refreshTheMatrixDisplay(bool nextPage) { case 0: - // update counter values from libpax - libpax_counter_count(&count_from_libpax); - if (cfg.countermode == 1) { // cumulative counter mode -> display total number of pax if (ulLastNumMacs != count_from_libpax.pax) { From 5ec8b68fff99b7d5319b9d927af8c20ef0670327 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 11:59:13 +0100 Subject: [PATCH 13/22] Revert "bugfix curve plotter display" This reverts commit 3e045bfcf4eb63bd44465ee47425e550ad1965a2. --- src/display.cpp | 3 +++ src/ledmatrixdisplay.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/display.cpp b/src/display.cpp index 07017bf8..fdf70904 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -174,6 +174,9 @@ void dp_init(bool verbose) { void dp_refresh(bool nextPage) { + // update counter values from libpax + libpax_counter_count(&count_from_libpax); + #ifndef HAS_BUTTON static uint32_t framecounter = 0; #endif diff --git a/src/ledmatrixdisplay.cpp b/src/ledmatrixdisplay.cpp index 25faf2fa..e6b1d070 100644 --- a/src/ledmatrixdisplay.cpp +++ b/src/ledmatrixdisplay.cpp @@ -75,6 +75,9 @@ void refreshTheMatrixDisplay(bool nextPage) { case 0: + // update counter values from libpax + libpax_counter_count(&count_from_libpax); + if (cfg.countermode == 1) { // cumulative counter mode -> display total number of pax if (ulLastNumMacs != count_from_libpax.pax) { From f8e8aec6e02a5b9a2aee2be40ce47632a08c2908 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 17:37:12 +0100 Subject: [PATCH 14/22] timekeeper.cpp: add mkgmtime() --- src/timekeeper.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index ef614624..6cb55d76 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -366,5 +366,38 @@ time_t compileTime(const String compile_date) { secs = mktime(&t); } + return secs; +} + +static bool IsLeapYear(short year) { + if (year % 4 != 0) + return false; + if (year % 100 != 0) + return true; + return (year % 400) == 0; +} + +// convert UTC tm time to time_t epoch time +time_t mkgmtime(const struct tm *ptm) { + const int SecondsPerMinute = 60; + const int SecondsPerHour = 3600; + const int SecondsPerDay = 86400; + const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + time_t secs = 0; + // tm_year is years since 1900 + int year = ptm->tm_year + 1900; + for (int y = 1970; y < year; ++y) { + secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay; + } + // tm_mon is month from 0..11 + for (int m = 0; m < ptm->tm_mon; ++m) { + secs += DaysOfMonth[m] * SecondsPerDay; + if (m == 1 && IsLeapYear(year)) + secs += SecondsPerDay; + } + secs += (ptm->tm_mday - 1) * SecondsPerDay; + secs += ptm->tm_hour * SecondsPerHour; + secs += ptm->tm_min * SecondsPerMinute; + secs += ptm->tm_sec; return secs; } \ No newline at end of file From 7d9d6d81c43034336fd6ab160b4db751c67b6861 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 17:37:34 +0100 Subject: [PATCH 15/22] sdcard.cpp: adapt to eztime removal --- src/sdcard.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 748a1a3e..925ab207 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -44,6 +44,10 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle, static int counterWrites = 0; char tempBuffer[12 + 1]; time_t t = time(NULL); + struct tm tt; + localtime_r(&t, &tt); + mktime(&tt); + #if (HAS_SDS011) sdsStatus_t sds; #endif @@ -52,9 +56,9 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle, return; ESP_LOGD(TAG, "writing to SD-card"); - sprintf(tempBuffer, "%02d.%02d.%4d,", day(t), month(t), year(t)); + strftime(tempBuffer, sizeof(tempBuffer), "%d.%m.%Y", &tt); fileSDCard.print(tempBuffer); - sprintf(tempBuffer, "%02d:%02d:%02d,", hour(t), minute(t), second(t)); + strftime(tempBuffer, sizeof(tempBuffer), "%H.%M.%S", &tt); fileSDCard.print(tempBuffer); sprintf(tempBuffer, "%d,%d", noWifi, noBle); fileSDCard.print(tempBuffer); From fb82d05caea7e242f704d678747122bede19b5d5 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 17:38:31 +0100 Subject: [PATCH 16/22] gpsread.cpp: add yield for thread safety --- src/gpsread.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 8d5cb228..428604e4 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -146,12 +146,14 @@ void gps_loop(void *pvParameters) { // feed GPS decoder with serial NMEA data from GPS device while (GPS_Serial.available()) { gps.encode(GPS_Serial.read()); + yield(); } #elif defined GPS_I2C Wire.requestFrom(GPS_ADDR, 32); // caution: this is a blocking call while (Wire.available()) { gps.encode(Wire.read()); delay(2); // 2ms delay according L76 datasheet + yield(); } #endif @@ -170,7 +172,7 @@ void gps_loop(void *pvParameters) { // gps.passedChecksum(), gps.failedChecksum(), // gps.sentencesWithFix()); - delay(2); // yield to CPU + yield(); // yield to CPU } // end of infinite loop From 64f82dcaab9a4a6e1196b2ad750cce2c17181c00 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Thu, 20 Jan 2022 17:39:01 +0100 Subject: [PATCH 17/22] dump to v3.1.0 --- platformio_orig.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platformio_orig.ini b/platformio_orig.ini index c0868236..100ede8a 100644 --- a/platformio_orig.ini +++ b/platformio_orig.ini @@ -48,7 +48,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 3.0.3 +release_version = 3.1.0 ; 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 debug_level = 3 @@ -82,7 +82,6 @@ lib_deps_basic = https://github.com/dbSuS/libpax.git @ ^1.0.0 https://github.com/SukkoPera/Arduino-Rokkit-Hash.git bblanchon/ArduinoJson @ ^6 - ropg/ezTime @ ^0.8.3 makuna/RTC @ ^2.3.5 spacehuhn/SimpleButton lewisxhe/AXP202X_Library @ ^1.1.3 From 3e4da9f1e95e914abc74a4a0bb8703226146debc Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sat, 22 Jan 2022 13:56:40 +0100 Subject: [PATCH 18/22] fix missing uint_8 casting --- src/main.cpp | 2 +- src/rcommand.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 63ef2300..fda59d8e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -449,7 +449,7 @@ void setup() { ESP_LOGI(TAG, "BME sensor initialized"); else { ESP_LOGE(TAG, "BME sensor could not be initialized"); - cfg.payloadmask &= ~MEMS_DATA; // switch off transmit of BME data + cfg.payloadmask &= (uint8_t)~MEMS_DATA; // switch off transmit of BME data } #endif diff --git a/src/rcommand.cpp b/src/rcommand.cpp index c69a5fd3..08c7618d 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -401,9 +401,9 @@ void set_enscount(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: set ENS_COUNT to %s", val[0] ? "on" : "off"); cfg.enscount = val[0] ? 1 : 0; if (val[0]) - cfg.payloadmask |= SENSOR1_DATA; + cfg.payloadmask |= (uint8_t)SENSOR1_DATA; else - cfg.payloadmask &= ~SENSOR1_DATA; + cfg.payloadmask &= (uint8_t)~SENSOR1_DATA; } void set_loadconfig(uint8_t val[]) { From 2ef25b369842df29c0788fadad313272d6899fe1 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sat, 22 Jan 2022 14:31:41 +0100 Subject: [PATCH 19/22] reset.cpp: remove unused RTC_time --- src/reset.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/reset.cpp b/src/reset.cpp index 8cfc1d3d..d21a042d 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -15,7 +15,6 @@ RTC_NOINIT_ATTR uint32_t RTC_restarts; // RTC_DATA_ATTR -> keep values after a wakeup from sleep RTC_DATA_ATTR struct timeval RTC_sleep_start_time; RTC_DATA_ATTR unsigned long long RTC_millis = 0; -RTC_DATA_ATTR time_t RTC_time = 0; timeval sleep_stop_time; @@ -197,7 +196,6 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) { // time stamp sleep start time and save system monotonic time. Deep sleep. gettimeofday(&RTC_sleep_start_time, NULL); RTC_millis += millis(); - time(&RTC_time); ESP_LOGI(TAG, "Going to sleep, good bye."); esp_deep_sleep_start(); } From b3c683d1acba1bad40b084a513144ad97f115243 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sat, 22 Jan 2022 14:56:16 +0100 Subject: [PATCH 20/22] timekeeper.cpp: fix millis --- src/timekeeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 6cb55d76..a1ebda4b 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -40,7 +40,7 @@ Ticker timesyncer; void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void calibrateTime(void) { - ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", millis() / 1000.0, + ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _seconds(), timeSource); time_t t = 0; uint16_t t_msec = 0; From 0b0542f09156a9b1d8336b42e731f3f62df05dde Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 23 Jan 2022 21:11:16 +0100 Subject: [PATCH 21/22] fix readme.md --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0994689a..1cf6607c 100644 --- a/README.md +++ b/README.md @@ -325,21 +325,21 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering. **Port #3:** Device configuration query result - byte 1: Lora SF (7..12) [default 9] + byte 1: Lora DR (0..15, see rcommand 0x05) [default 5] byte 2: Lora TXpower (2..15) [default 15] byte 3: Lora ADR (1=on, 0=off) [default 1] byte 4: Screensaver status (1=on, 0=off) [default 0] byte 5: Display status (1=on, 0=off) [default 0] byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] - bytes 7-8: RSSI limiter threshold value (negative) [default 0] - byte 9: Lora Payload send cycle in seconds/2 (0..255) [default 120] + bytes 7-8: RSSI limiter threshold value (negative, MSB) [default 0] + byte 9: Scan and send cycle in seconds/2 (0..255) [default 120] byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50] - byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [efault 10] + byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [default 10] byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] - byte 14: count randomizated MACs only (0=disabled, 1=enabled) [default 1] + byte 14: 0 (reserved) byte 15: RGB LED luminosity (0..100 %) [default 30] - byte 16: 0 (reserved) + byte 16: Payloadmask (Bitmask, 0..255, see rcommand 0x14) byte 17: 0 (reserved) bytes 18-28: Software version (ASCII format, terminating with zero) @@ -576,10 +576,10 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/ 0x03 = unsynched 0x04 = set (source unknown) - bits 4..7 time status - 0x00 = unknown - 0x01 = time needs sync - 0x02 = time synched + bits 4..7 esp32 sntp time status + 0x00 = SNTP_SYNC_STATUS_RESET + 0x01 = SNTP_SYNC_STATUS_COMPLETED + 0x02 = SNTP_SYNC_STATUS_IN_PROGRESS 0x87 sync time/date From 529059edf4466f79440c09a771537a8d9aadee2a Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 23 Jan 2022 21:12:28 +0100 Subject: [PATCH 22/22] integrate esp32 sntp --- include/if482.h | 1 + include/rcommand.h | 1 + include/timekeeper.h | 1 + src/if482.cpp | 18 +++++------------- src/timekeeper.cpp | 18 +++++++----------- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/include/if482.h b/include/if482.h index faf2a01f..9a3fe92f 100644 --- a/include/if482.h +++ b/include/if482.h @@ -3,6 +3,7 @@ #include "globals.h" #include "timekeeper.h" +#include "esp_sntp.h" #define IF482_FRAME_SIZE (17) #define IF482_SYNC_FIXUP (10) // calibration to fixup processing time [milliseconds] diff --git a/include/rcommand.h b/include/rcommand.h index 75689597..0cdb23c6 100644 --- a/include/rcommand.h +++ b/include/rcommand.h @@ -10,6 +10,7 @@ #include "sensor.h" #include "cyclic.h" #include "timekeeper.h" +#include "esp_sntp.h" #include "timesync.h" #include "power.h" #include "antenna.h" diff --git a/include/timekeeper.h b/include/timekeeper.h index c95d41d0..8eb184b4 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -8,6 +8,7 @@ #include "gpsread.h" #include "if482.h" #include "dcf77.h" +#include "esp_sntp.h" #define SECS_YR_2000 (946684800UL) // the time at the start of y2k #define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch diff --git a/src/if482.cpp b/src/if482.cpp index b4fb6250..37c12c38 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -88,24 +88,16 @@ String IF482_Frame(time_t t) { char mon, out[IF482_FRAME_SIZE + 1]; - switch (sntp_get_sync_status()) { // indicates if time has been set and recently synced - case SNTP_SYNC_STATUS_COMPLETED: // time is set and is synced - mon = 'A'; - break; - case SNTP_SYNC_STATUS_IN_PROGRESS: // time had been set but sync attempt did not succeed - case SNTP_SYNC_STATUS_RESET: - mon = 'M'; - break; - default: // unknown time status (should never be reached) - mon = '?'; - break; - } // switch + if (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) + mon = 'M'; // time had been set but sync not completed + else + mon = 'A'; // time has been set and was recently synced // generate IF482 telegram // snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME, // "ymdwHis").c_str()); - //ESP_LOGD(TAG, "[%s] IF482 date/time: %s", ctime(time(NULL), out); + // ESP_LOGD(TAG, "[%s] IF482 date/time: %s", ctime(time(NULL), out); return out; } diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index a1ebda4b..876bff85 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -103,7 +103,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, tv.tv_sec = time_to_set; tv.tv_usec = 0; - settimeofday(&tv, NULL); + sntp_sync_time(&tv); ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set); @@ -122,7 +122,6 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, #endif timeSource = mytimesource; // set global variable - sntp_set_sync_status(SNTP_SYNC_STATUS_COMPLETED); timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | timesource=%d", @@ -140,6 +139,10 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, // helper function to setup a pulse per second for time synchronisation uint8_t timepulse_init() { + // set esp-idf API sntp sync mode + //sntp_init(); + sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); + // use time pulse from GPS as time base with fixed 1Hz frequency #ifdef GPS_INT @@ -273,15 +276,8 @@ void clock_loop(void *taskparameter) { // ClockTask // wait for timepulse and store UTC time xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, portMAX_DELAY); - /* - // no confident or no recent time -> suppress clock output - if ((sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) || - !(timeIsValid(current_time)) || (current_time == previous_time)) - continue; - */ - - // no confident or no recent time -> suppress clock output - if (!(timeIsValid(current_time)) || (current_time == previous_time)) + if ((sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) || + !(timeIsValid(current_time)) || (current_time == previous_time)) continue; // set calendar time for next second of clock output