commit
e3001f2df8
@ -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"
|
||||
|
@ -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
|
@ -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
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
18
src/main.cpp
18
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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user