diff --git a/include/globals.h b/include/globals.h index 5ca2286d..7b427e5a 100644 --- a/include/globals.h +++ b/include/globals.h @@ -83,6 +83,8 @@ typedef struct { uint8_t satellites; uint16_t hdop; int16_t altitude; + uint32_t time_age; + tmElements_t timedate; } gpsStatus_t; typedef struct { @@ -111,12 +113,13 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble, batt_voltage; // display values extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern timesource_t timeSource; -extern hw_timer_t *displayIRQ, *ppsIRQ; +extern hw_timer_t *displayIRQ, *ppsIRQ, *gpsIRQ; extern SemaphoreHandle_t I2Caccess; extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; extern time_t userUTCTime; +extern time_t volatile gps_pps_time; // application includes #include "led.h" diff --git a/include/gpsread.h b/include/gpsread.h index 6c20eef0..d1452b37 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -18,9 +18,10 @@ extern gpsStatus_t extern TaskHandle_t GpsTask; int gps_init(void); -void gps_read(void); +void IRAM_ATTR gps_storetime(gpsStatus_t &gps_store); +void gps_storelocation(gpsStatus_t &gps_store); void gps_loop(void *pvParameters); -time_t get_gpstime(void); +time_t get_gpstime(gpsStatus_t value); int gps_config(); #endif \ No newline at end of file diff --git a/include/irqhandler.h b/include/irqhandler.h index f7e31b92..67af99ba 100644 --- a/include/irqhandler.h +++ b/include/irqhandler.h @@ -8,7 +8,7 @@ #define TIMESYNC_IRQ 0x10 #define MASK_IRQ 0x20 #define UNMASK_IRQ 0x40 -#define RESERVED_IRQ 0x80 +#define GPS_IRQ 0x80 #include "globals.h" #include "cyclic.h" @@ -27,4 +27,8 @@ void IRAM_ATTR DisplayIRQ(); void IRAM_ATTR ButtonIRQ(); #endif +#if (HAS_GPS) +void IRAM_ATTR GpsIRQ(); +#endif + #endif \ No newline at end of file diff --git a/include/timekeeper.h b/include/timekeeper.h index 0d051a61..87f999c0 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -6,7 +6,7 @@ #include "TimeLib.h" #include "irqhandler.h" -#ifdef HAS_GPS +#if (HAS_GPS) #include "gpsread.h" #endif @@ -28,8 +28,6 @@ uint8_t timepulse_init(void); time_t timeIsValid(time_t const t); time_t timeProvider(void); time_t compiledUTC(void); -time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, - uint8_t ss); TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPins); time_t TimeSyncAns(uint8_t seqNo, uint64_t unixTime); diff --git a/platformio.ini b/platformio.ini index b07e0d91..9fc2e6a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,10 +31,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.7.5 +release_version = 1.7.541 ; 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 +debug_level = 0 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom @@ -45,8 +45,8 @@ platform_espressif32 = espressif32@1.7.0 board_build.partitions = min_spiffs.csv monitor_speed = 115200 lib_deps_lora = - ;MCCI LoRaWAN LMIC library@>=2.3.2 - https://github.com/mcci-catena/arduino-lmic.git#e5503ff + ;MCCI LoRaWAN LMIC library@2.3.2 + https://github.com/mcci-catena/arduino-lmic.git#dc18ee9 lib_deps_display = U8g2@>=2.25.7 lib_deps_rgbled = diff --git a/src/gpsread.cpp b/src/gpsread.cpp index c04eb432..62aa2da2 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -11,9 +11,10 @@ TaskHandle_t GpsTask; #ifdef GPS_SERIAL HardwareSerial GPS_Serial(1); // use UART #1 -static TickType_t gps_txDelay = tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL); +static uint16_t nmea_txDelay_ms = + tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS; #else -static TickType_t gps_txDelay = 0; +static uint16_t nmea_txDelay_ms = 0; #endif // initialize and configure GPS @@ -64,44 +65,48 @@ int gps_config() { return rslt; } -// read GPS data and cast to global struct -void gps_read() { - gps_status.latitude = (int32_t)(gps.location.lat() * 1e6); - gps_status.longitude = (int32_t)(gps.location.lng() * 1e6); - gps_status.satellites = (uint8_t)gps.satellites.value(); - gps_status.hdop = (uint16_t)gps.hdop.value(); - gps_status.altitude = (int16_t)gps.altitude.meters(); - // show NMEA data in debug mode, useful for debugging GPS - ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", - gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); +// store current GPS location data in struct +void gps_storelocation(gpsStatus_t &gps_store) { + gps_store.latitude = (int32_t)(gps.location.lat() * 1e6); + gps_store.longitude = (int32_t)(gps.location.lng() * 1e6); + gps_store.satellites = (uint8_t)gps.satellites.value(); + gps_store.hdop = (uint16_t)gps.hdop.value(); + gps_store.altitude = (int16_t)gps.altitude.meters(); } -// function to fetch current time from gps -time_t get_gpstime(void) { +// store current GPS timedate in struct +void IRAM_ATTR gps_storetime(gpsStatus_t &gps_store) { - // set time to wait for arrive next recent NMEA time record - static const uint32_t gpsDelay_ms = 1000 - gps_txDelay / portTICK_PERIOD_MS; + gps_store.time_age = gps.time.age(); - time_t t = 0; - uint32_t time_age = gps.time.age(); + if (gps.time.isValid() && gps.date.isValid() && (gps_store.time_age < 1000)) { + gps_store.timedate.Year = + CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h + gps_store.timedate.Month = gps.date.month(); + gps_store.timedate.Day = gps.date.day(); + gps_store.timedate.Hour = gps.time.hour(); + gps_store.timedate.Minute = gps.time.minute(); + gps_store.timedate.Second = gps.time.second(); + } else + gps_store.timedate = {0}; +} - if ((time_age < gpsDelay_ms) && gps.time.isValid() && gps.date.isValid() && - gps.time.isUpdated()) { +// function to fetch current time from struct; note: this is costly +time_t get_gpstime(gpsStatus_t value) { - t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), - gps.time.hour(), gps.time.minute(), gps.time.second()); + time_t t = timeIsValid(makeTime(value.timedate)); - if (time_age < (gpsDelay_ms / 2)) - t--; + // if (t) + // t = value.time_age > nmea_txDelay_ms ? t : t - 1; - ESP_LOGD(TAG, "GPS time age: %dms", time_age); + // show NMEA data in verbose mode, useful for debugging GPS + ESP_LOGV( + TAG, + "GPS time: %d | GPS NMEA data: passed %d / failed: %d / with fix: %d", t, + gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); + + return t; -#ifndef GPS_INT - // wait until top of second with millisecond precision - vTaskDelay(pdMS_TO_TICKS(1000 - time_age) - gps_txDelay); -#endif - } - return timeIsValid(t); } // get_gpstime() // GPS serial feed FreeRTos Task diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index d12dfa24..97c026e0 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -39,6 +39,12 @@ void irqHandler(void *pvParameters) { refreshtheDisplay(); #endif +// gps refresh buffer? +#if (HAS_GPS) + if (InterruptStatus & GPS_IRQ) + gps_storelocation(gps_status); +#endif + // are cyclic tasks due? if (InterruptStatus & CYCLIC_IRQ) doHousekeeping(); @@ -46,8 +52,8 @@ void irqHandler(void *pvParameters) { #if (TIME_SYNC_INTERVAL) // is time to be synced? if (InterruptStatus & TIMESYNC_IRQ) { + now(); // ensure sysTime is recent time_t t = timeProvider(); - ESP_LOGD(TAG, "Sync time = %d", t); if (timeIsValid(t)) setTime(t); } @@ -85,6 +91,17 @@ void IRAM_ATTR ButtonIRQ() { } #endif +#if (HAS_GPS) +void IRAM_ATTR GpsIRQ() { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + xTaskNotifyFromISR(irqHandlerTask, GPS_IRQ, eSetBits, + &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken) + portYIELD_FROM_ISR(); +} +#endif + int mask_user_IRQ() { // begin of time critical section: lock I2C bus to ensure accurate timing if (!I2C_MUTEX_LOCK()) diff --git a/src/main.cpp b/src/main.cpp index 8a5255d3..96198e4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,7 +33,7 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer clockloop 1 4 generates realtime telegrams for external clock timesync_req 1 3 processes realtime time sync requests -irqhandler 1 2 display, timesync, etc. tasks triggered by timer +irqhandler 1 2 display, timesync, gps, etc. triggered by timers gpsloop 1 2 reads data from GPS via serial or i2c bmeloop 1 1 reads data from BME sensor via i2c looptask 1 1 runs the LMIC LoRa stack (arduino loop) @@ -51,7 +51,7 @@ So don't do it if you do not own a digital oscilloscope. ------------------------------------------------------------------------------- 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS) 1 ppsIRQ -> pps clock irq -> 1sec -2 unused +2 gpsIRQ -> gps store data -> 300ms 3 unused @@ -61,6 +61,7 @@ So don't do it if you do not own a digital oscilloscope. fired by hardware DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1) CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1) +GpsIRQ -> esp32 timer 2 -> irqHandlerTask (Core 1) ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) fired by software (Ticker.h) @@ -84,11 +85,12 @@ 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 -hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL; +hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *gpsIRQ = NULL; TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; SemaphoreHandle_t I2Caccess; bool volatile TimePulseTick = false; +time_t volatile gps_pps_time = 0; time_t userUTCTime = 0; timesource_t timeSource = _unsynced; @@ -408,7 +410,15 @@ void setup() { timerAlarmEnable(displayIRQ); #endif -// cyclic function interrupts + // gps buffer read interrupt +#if (HAS_GPS) + gpsIRQ = timerBegin(2, 80, true); + timerAttachInterrupt(gpsIRQ, &GpsIRQ, true); + timerAlarmWrite(gpsIRQ, 300 * 1000, true); + timerAlarmEnable(gpsIRQ); +#endif + + // cyclic function interrupts sendcycler.attach(SENDCYCLE * 2, sendcycle); housekeeper.attach(HOMECYCLE, housekeeping); diff --git a/src/rcommand.cpp b/src/rcommand.cpp index fbc76ab7..20f8cc36 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -253,7 +253,6 @@ void get_status(uint8_t val[]) { void get_gps(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get gps status"); #if(HAS_GPS) - gps_read(); payload.reset(); payload.addGPS(gps_status); SendPayload(GPSPORT, prio_high); diff --git a/src/senddata.cpp b/src/senddata.cpp index 1f8a9e7a..ecbfb0ec 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -87,7 +87,6 @@ void sendCounter() { case GPS_DATA: // send GPS position only if we have a fix if (gps.location.isValid()) { - gps_read(); payload.reset(); payload.addGPS(gps_status); SendPayload(GPSPORT, prio_high); diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 717c0463..99c72a4f 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -18,6 +18,10 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS) #endif +#if (HAS_GPS) +static gpsStatus_t gps_pps_status; +#endif + Ticker timesyncer; void timeSync() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } @@ -27,13 +31,15 @@ time_t timeProvider(void) { time_t t = 0; #if (HAS_GPS) - t = get_gpstime(); // fetch recent time from last NEMA record + // fetch recent time from last NEMA record + t = get_gpstime(gps_pps_status); if (t) { #ifdef HAS_RTC set_rtctime(t, do_mutex); // calibrate RTC #endif timeSource = _gps; timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat + ESP_LOGD(TAG, "GPS time = %d", t); return t; } #endif @@ -44,6 +50,7 @@ time_t timeProvider(void) { if (t) { timeSource = _rtc; timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); // short retry + ESP_LOGD(TAG, "RTC time = %d", t); } #endif @@ -106,6 +113,7 @@ uint8_t timepulse_init() { } // timepulse_init void timepulse_start(void) { + #ifdef GPS_INT // start external clock gps pps line attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); #elif defined RTC_INT // start external clock rtc @@ -114,7 +122,11 @@ void timepulse_start(void) { timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true); timerAlarmEnable(ppsIRQ); #endif - now(); // refresh sysTime to pps + +// initialize gps time +#if (HAS_GPS) + gps_storetime(gps_pps_status); +#endif // start cyclic time sync timeSync(); // init systime by RTC or GPS or LORA @@ -126,13 +138,20 @@ void IRAM_ATTR CLOCKIRQ(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - SyncToPPS(); // calibrates UTC systime and advances it +1, see microTime.h + SyncToPPS(); // advance systime, see microTime.h + // store recent gps time, if we have gps +#if (HAS_GPS) + gps_storetime(gps_pps_status); +#endif + +// advance wall clock, if we have #if (defined HAS_IF482 || defined HAS_DCF77) xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, &xHigherPriorityTaskWoken); #endif +// flip time pulse ticker, if needed #ifdef HAS_DISPLAY #if (defined GPS_INT || defined RTC_INT) TimePulseTick = !TimePulseTick; // flip pulse ticker @@ -156,19 +175,6 @@ time_t compiledUTC(void) { return t; } -// helper function to convert gps date/time into time_t -time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, - uint8_t ss) { - tmElements_t tm; - tm.Year = CalendarYrToTm(YYYY); // year offset from 1970 in microTime.h - tm.Month = MM; - tm.Day = DD; - tm.Hour = hh; - tm.Minute = mm; - tm.Second = ss; - return makeTime(tm); -} - // helper function to calculate serial transmit time TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPins) { diff --git a/src/timesync.cpp b/src/timesync.cpp index 8af801f7..b6657085 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -216,8 +216,11 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { // adjust system time, calibrate RTC and RTC_INT pps void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) { - // advance time 1 sec wait time - time_t time_to_set = (time_t)(t_sec + 1); + time_t time_to_set = (time_t)t_sec; + + //#if (!defined GPS_INT && !defined RTC_INT) + // time_to_set++; + //#endif ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec", millis() / 1000.0, time_to_set, t_msec);