diff --git a/include/globals.h b/include/globals.h index e11fb67e..d2c6d7bc 100644 --- a/include/globals.h +++ b/include/globals.h @@ -102,7 +102,6 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern hw_timer_t *sendCycle, *displaytimer; extern SemaphoreHandle_t I2Caccess, TimePulse; -extern bool volatile BitsPending; extern std::set, Mallocator> macs; extern std::array::iterator it; diff --git a/include/rtctime.h b/include/rtctime.h index 603f6e64..daadff68 100644 --- a/include/rtctime.h +++ b/include/rtctime.h @@ -13,6 +13,7 @@ extern RtcDS3231 Rtc; // make RTC instance globally available extern TaskHandle_t ClockTask; extern hw_timer_t *clockCycle; +extern bool volatile TimePulseTick; int rtc_init(void); int set_rtctime(uint32_t t); @@ -23,6 +24,6 @@ float get_rtctemp(void); void IRAM_ATTR CLOCKIRQ(); int timepulse_init(uint32_t pps_freq); void timepulse_start(); -time_t sync_clock(time_t t); +void sync_clock(void); #endif // _RTCTIME_H \ No newline at end of file diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 7ff07cb7..8427edc0 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -17,6 +17,8 @@ https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/Funkuhr.zip/ // Local logging tag static const char TAG[] = "main"; +bool volatile BitsPending = false; + #define DCF77_FRAME_SIZE (60) #define DCF77_PULSE_DURATION (100) @@ -35,8 +37,6 @@ uint8_t DCFtimeframe[DCF77_FRAME_SIZE]; // initialize and configure DCF77 output int dcf77_init(void) { - BitsPending = false; - pinMode(HAS_DCF77, OUTPUT); set_DCF77_pin(dcf_low); timepulse_init(PPS); // setup timepulse @@ -49,9 +49,9 @@ int dcf77_init(void) { &ClockTask, // task handle 0); // CPU core - assert(ClockTask); // has clock task started? + assert(ClockTask); // has clock task started? DCF_Out(second(now())); // sync DCF time on next second - timepulse_start(); // start pulse + timepulse_start(); // start pulse return 1; // success } // ifdcf77_init @@ -70,7 +70,7 @@ void DCF_Out(uint8_t startOffset_sec) { if ((timeStatus() == timeSet) || (timeStatus() == timeNeedsSync)) { // prepare frame to send for next minute generateTimeframe(now() + DCF77_FRAME_SIZE + 1); - // start blinking symbol on display and kick off timer + // kick off output of telegram BitsPending = true; } else return; @@ -103,7 +103,7 @@ void DCF_Out(uint8_t startOffset_sec) { // recalibrate clock after a fixed timespan, do this in 59th second #ifdef TIME_SYNC_INTERVAL_DCF if ((millis() >= nextDCFsync)) { - sync_clock(now()); // waiting for second 59 + sync_clock(); // waiting for second 59 nextDCFsync = millis() + TIME_SYNC_INTERVAL_DCF * 60000; // set up next time sync period } diff --git a/src/display.cpp b/src/display.cpp index 75816b37..8ff2b83e 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -221,15 +221,12 @@ void refreshtheDisplay() { // update LoRa status display (line 6) u8x8.printf("%-16s", display_line6); #else // we want a time display instead LoRa status - // update time/date display (line 6) time_t t = myTZ.toLocal(now()); char timeState = - timeStatus() == timeSet ? timesyncSymbol : timeNosyncSymbol; - // make timestatus symbol blinking if pps line - if ((BitsPending) && (second(t) % 2)) - timeState = ' '; - u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t), - timeState, day(t), printmonth[month(t)]); + (timeStatus() == timeSet) ? timesyncSymbol : timeNosyncSymbol; + char timePulse = TimePulseTick ? '.' : ':'; + u8x8.printf("%02d:%02d%c%02d%c %2d.%3s", hour(t), minute(t), timePulse, + second(t), timeState, day(t), printmonth[month(t)]); #endif // update LMiC event display (line 7) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 4a46a482..604216a5 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -88,28 +88,23 @@ time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, // function to fetch current time from gps time_t get_gpstime(void) { - // never call now() in this function, this would break this function - // to use as SyncProvider due to recursive call to now() + // !! never call now() in this function, this would break this function + // to be used as SyncProvider due to recursive call to now() - time_t t; if ((gps.time.age() < 1500) && (gps.time.isValid())) { - t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), - gps.time.hour(), gps.time.minute(), gps.time.second()); + // get current gps time + time_t t = + tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(), + gps.time.hour(), gps.time.minute(), gps.time.second()); ESP_LOGD(TAG, "GPS time: %4d/%02d/%02d %02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t)); + // sync on top of next second by timepulse + sync_clock(); + return t + 1; } else { ESP_LOGW(TAG, "GPS has no confident time"); - return 0; + return 0; // sync failure, 0 effects calling SyncProvider() to not set time } - - // sync on top of next second bv timepulse - if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) - return t; - else { - ESP_LOGW(TAG, "No GPS timepulse, thus time can't be synced by GPS"); - return 0; - } // failure - } // get_gpstime() // GPS serial feed FreeRTos Task diff --git a/src/if482.cpp b/src/if482.cpp index cd2fa8ad..62fae740 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -161,16 +161,11 @@ void if482_loop(void *pvParameters) { TickType_t wakeTime; const TickType_t tTx = tx_time(HAS_IF482); // duration of telegram transmit - BitsPending = true; // start blink in display // phase 1: sync task on top of second const TickType_t t0 = xTaskGetTickCount(); // moment of start top of second - - sync_clock(now()); // delay until 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( diff --git a/src/main.cpp b/src/main.cpp index 64ec184f..b1ff0e34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,7 +65,6 @@ char display_line6[16], display_line7[16]; // display buffers uint8_t volatile channel = 0; // channel rotation counter uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, batt_voltage = 0; // globals for display -bool volatile BitsPending = false; // DCF77 or IF482 ticker indicator hw_timer_t *sendCycle = NULL, *homeCycle = NULL; #ifdef HAS_DISPLAY @@ -97,16 +96,14 @@ void setup() { char features[100] = ""; + // create some semaphores for syncing / mutexing tasks I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus if (I2Caccess) xSemaphoreGive(I2Caccess); // Flag the i2c bus available for use - TimePulse = xSemaphoreCreateMutex(); // for time pulse flip - if (TimePulse) - xSemaphoreTake(TimePulse, (TickType_t)10); - // Block TimePulse since we have no pulse yet + TimePulse = xSemaphoreCreateBinary(); // as signal that shows time pulse flip - // disable brownout detection + // disable brownout detection #ifdef DISABLE_BROWNOUT // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 (*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0; @@ -146,10 +143,21 @@ void setup() { ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(), ESP.getFlashChipSpeed()); - ESP_LOGI(TAG, "Wifi/BT software coexist version: %s", esp_coex_version_get()); + ESP_LOGI(TAG, "Wifi/BT software coexist version %s", esp_coex_version_get()); + +#ifdef HAS_LORA + ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, + LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); + + ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", + ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); +#endif #ifdef HAS_GPS - ESP_LOGI(TAG, "TinyGPS+ v%s", TinyGPSPlus::libraryVersion()); + ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); #endif #endif // verbose @@ -330,7 +338,7 @@ void setup() { #ifdef HAS_RTC strcat_P(features, " RTC"); assert(rtc_init()); - setSyncProvider(&get_rtctime); + setSyncProvider(&get_rtctime); // sync time now and then if (timeStatus() != timeSet) ESP_LOGI(TAG, "Unable to sync system time with RTC"); else @@ -408,7 +416,7 @@ void setup() { #endif // HAS_BUTTON #ifdef HAS_GPS - setSyncProvider(&get_gpstime); + setSyncProvider(&get_gpstime); // sync time now and then if (timeStatus() != timeSet) ESP_LOGI(TAG, "Unable to sync system time with GPS"); else { diff --git a/src/rtctime.cpp b/src/rtctime.cpp index 8beb3b4c..72bb64bb 100644 --- a/src/rtctime.cpp +++ b/src/rtctime.cpp @@ -7,104 +7,7 @@ static const char TAG[] = "main"; TaskHandle_t ClockTask; hw_timer_t *clockCycle = NULL; - -#ifdef HAS_RTC // we have hardware RTC - -RtcDS3231 Rtc(Wire); // RTC hardware i2c interface - -// initialize RTC -int rtc_init(void) { - - // return = 0 -> error / return = 1 -> success - - // block i2c bus access - if (I2C_MUTEX_LOCK()) { - - Wire.begin(HAS_RTC); - Rtc.Begin(); - - RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); - - if (!Rtc.IsDateTimeValid()) { - ESP_LOGW(TAG, - "RTC has no valid RTC date/time, setting to compilation date"); - Rtc.SetDateTime(compiled); - } - - if (!Rtc.GetIsRunning()) { - ESP_LOGI(TAG, "RTC not running, starting now"); - Rtc.SetIsRunning(true); - } - - RtcDateTime now = Rtc.GetDateTime(); - - if (now < compiled) { - ESP_LOGI(TAG, "RTC date/time is older than compilation date, updating"); - Rtc.SetDateTime(compiled); - } - - // configure RTC chip - Rtc.Enable32kHzPin(false); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); - - } else { - ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); - goto error; - } - - I2C_MUTEX_UNLOCK(); // release i2c bus access - ESP_LOGI(TAG, "RTC initialized"); - return 1; - -error: - I2C_MUTEX_UNLOCK(); // release i2c bus access - return 0; - -} // rtc_init() - -int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 - if (I2C_MUTEX_LOCK()) { - time_t tt = sync_clock(t); // wait for top of second - Rtc.SetDateTime(RtcDateTime(tt)); - I2C_MUTEX_UNLOCK(); // release i2c bus access - ESP_LOGI(TAG, "RTC calibrated"); - return 1; // success - } - return 0; // failure -} // set_rtctime() - -int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970 - return set_rtctime(static_cast(t)); - // set_rtctime() -} - -time_t get_rtctime(void) { - // never call now() in this function, this would cause a recursion! - time_t t = 0; - // block i2c bus access - if (I2C_MUTEX_LOCK()) { - if (Rtc.IsDateTimeValid()) { - RtcDateTime tt = Rtc.GetDateTime(); - t = tt.Epoch32Time(); - } else { - ESP_LOGW(TAG, "RTC has no confident time"); - } - I2C_MUTEX_UNLOCK(); // release i2c bus access - } - return t; -} // get_rtctime() - -float get_rtctemp(void) { - // block i2c bus access - if (I2C_MUTEX_LOCK()) { - RtcTemperature temp = Rtc.GetTemperature(); - I2C_MUTEX_UNLOCK(); // release i2c bus access - return temp.AsFloatDegC(); - } // while - return 0; -} // get_rtctemp() - -#endif // HAS_RTC +bool volatile TimePulseTick = false; // helper function to setup a pulse for time synchronisation int timepulse_init(uint32_t pulse_period_ms) { @@ -170,29 +73,139 @@ pulse_period_error: return 0; // failure } -void timepulse_start() { -#ifdef GPS_INT // start external clock +void timepulse_start(void) { +#ifdef GPS_INT // start external clock gps pps line attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); -#elif defined RTC_INT // start external clock +#elif defined RTC_INT // start external clock rtc attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); -#else // start internal clock +#else // start internal clock esp32 hardware timer timerAlarmEnable(clockCycle); #endif } // helper function to sync time_t of top of next second -time_t sync_clock(time_t t) { +void sync_clock(void) { +// do we have a second time pulse? Then wait for next pulse +#if defined(RTC_INT) || defined(GPS_INT) + // sync on top of next second by timepulse + if (xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)) == pdTRUE) { + ESP_LOGI(TAG, "clock synced by timepulse"); + return; + } else + ESP_LOGW(TAG, "Missing timepulse, thus clock can't be synced by second"); +#endif + // no external timepulse, thus we must use less precise internal system clock while (millis() % 1000) ; // wait for milli seconds to be zero before setting new time - return (now()); + ESP_LOGI(TAG, "clock synced by systime"); + return; } // interrupt service routine triggered by either rtc pps or esp32 hardware // timer void IRAM_ATTR CLOCKIRQ() { xTaskNotifyFromISR(ClockTask, xTaskGetTickCountFromISR(), eSetBits, NULL); -#ifdef GPS_INT - xSemaphoreGiveFromISR(TimePulse, NULL); +#if defined(GPS_INT) || defined(RTC_INT) + xSemaphoreGiveFromISR(TimePulse, pdFALSE); + TimePulseTick = !TimePulseTick; // flip ticker #endif portYIELD_FROM_ISR(); -} \ No newline at end of file +} + +#ifdef HAS_RTC // we have hardware RTC + +RtcDS3231 Rtc(Wire); // RTC hardware i2c interface + +// initialize RTC +int rtc_init(void) { + + // return = 0 -> error / return = 1 -> success + + // block i2c bus access + if (I2C_MUTEX_LOCK()) { + + Wire.begin(HAS_RTC); + Rtc.Begin(); + + RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); + + if (!Rtc.IsDateTimeValid()) { + ESP_LOGW(TAG, + "RTC has no valid RTC date/time, setting to compilation date"); + Rtc.SetDateTime(compiled); + } + + if (!Rtc.GetIsRunning()) { + ESP_LOGI(TAG, "RTC not running, starting now"); + Rtc.SetIsRunning(true); + } + + RtcDateTime now = Rtc.GetDateTime(); + + if (now < compiled) { + ESP_LOGI(TAG, "RTC date/time is older than compilation date, updating"); + Rtc.SetDateTime(compiled); + } + + // configure RTC chip + Rtc.Enable32kHzPin(false); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); + + } else { + ESP_LOGE(TAG, "I2c bus busy - RTC initialization error"); + goto error; + } + + I2C_MUTEX_UNLOCK(); // release i2c bus access + ESP_LOGI(TAG, "RTC initialized"); + return 1; + +error: + I2C_MUTEX_UNLOCK(); // release i2c bus access + return 0; + +} // rtc_init() + +int set_rtctime(time_t t) { // t is seconds epoch time starting 1.1.1970 + if (I2C_MUTEX_LOCK()) { + sync_clock(); // wait for top of second + Rtc.SetDateTime(RtcDateTime(t)); + I2C_MUTEX_UNLOCK(); // release i2c bus access + ESP_LOGI(TAG, "RTC calibrated"); + return 1; // success + } + return 0; // failure +} // set_rtctime() + +int set_rtctime(uint32_t t) { // t is epoch seconds starting 1.1.1970 + return set_rtctime(static_cast(t)); + // set_rtctime() +} + +time_t get_rtctime(void) { + // never call now() in this function, this would cause a recursion! + time_t t = 0; + // block i2c bus access + if (I2C_MUTEX_LOCK()) { + if (Rtc.IsDateTimeValid()) { + RtcDateTime tt = Rtc.GetDateTime(); + t = tt.Epoch32Time(); + } else { + ESP_LOGW(TAG, "RTC has no confident time"); + } + I2C_MUTEX_UNLOCK(); // release i2c bus access + } + return t; +} // get_rtctime() + +float get_rtctemp(void) { + // block i2c bus access + if (I2C_MUTEX_LOCK()) { + RtcTemperature temp = Rtc.GetTemperature(); + I2C_MUTEX_UNLOCK(); // release i2c bus access + return temp.AsFloatDegC(); + } // while + return 0; +} // get_rtctemp() + +#endif // HAS_RTC \ No newline at end of file