getting time from GPS reworked
This commit is contained in:
parent
817f7793c4
commit
a71a7e08a4
@ -5,17 +5,10 @@
|
||||
#include <RtcDateTime.h>
|
||||
#include "timekeeper.h"
|
||||
|
||||
#ifdef GPS_I2C // Needed for reading from I2C Bus
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
#ifndef GPS_BAUDRATE
|
||||
#define GPS_BAUDRATE 115200
|
||||
#define GPS_BAUDRATE 115200UL
|
||||
#endif
|
||||
|
||||
#define NMEA_FRAME_SIZE 82 // NEMA has a maxium of 82 bytes per record
|
||||
#define NMEA_COMPENSATION_FACTOR 480 // empiric for Ublox Neo 6M
|
||||
|
||||
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
||||
extern TaskHandle_t GpsTask;
|
||||
|
||||
|
@ -21,13 +21,13 @@ extern Ticker timesyncer;
|
||||
extern timesource_t timeSource;
|
||||
extern TaskHandle_t ClockTask;
|
||||
extern DRAM_ATTR bool TimePulseTick; // 1sec pps flag set by GPS or RTC
|
||||
extern DRAM_ATTR unsigned long lastPPS;
|
||||
extern hw_timer_t *ppsIRQ;
|
||||
extern portMUX_TYPE mux;
|
||||
|
||||
void IRAM_ATTR CLOCKIRQ(void);
|
||||
void IRAM_ATTR GPSIRQ(void);
|
||||
void clock_init(void);
|
||||
void clock_loop(void *pvParameters);
|
||||
void timepulse_start(void);
|
||||
void setTimeSyncIRQ(void);
|
||||
uint8_t timepulse_init(void);
|
||||
bool timeIsValid(time_t const t);
|
||||
@ -38,5 +38,4 @@ time_t compileTime(void);
|
||||
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);
|
||||
|
||||
#endif // _timekeeper_H
|
@ -317,9 +317,6 @@ void dp_drawPage(bool nextpage) {
|
||||
|
||||
#if (TIME_SYNC_INTERVAL)
|
||||
timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource];
|
||||
portENTER_CRITICAL(&mux);
|
||||
TimePulseTick = false; // flip global variable pulse ticker
|
||||
portEXIT_CRITICAL(&mux);
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||
|
200
src/gpsread.cpp
200
src/gpsread.cpp
@ -6,38 +6,12 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
// we use NMEA ZDA sentence field 1 for time synchronization
|
||||
// ZDA gives time for preceding pps pulse
|
||||
// downsight is that it does not have a constant offset
|
||||
// thus precision is only +/- 1 second
|
||||
|
||||
TinyGPSPlus gps;
|
||||
TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time (hhmmss.ss)
|
||||
TinyGPSCustom gpsday(gps, "GPZDA", 2); // field 2 = day (01..31)
|
||||
TinyGPSCustom gpsmonth(gps, "GPZDA", 3); // field 3 = month (01..12)
|
||||
TinyGPSCustom gpsyear(gps, "GPZDA", 4); // field 4 = year (4-digit)
|
||||
static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n";
|
||||
TaskHandle_t GpsTask;
|
||||
|
||||
HardwareSerial GPS_Serial(1); // use UART #1
|
||||
static uint16_t nmea_txDelay_ms =
|
||||
(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS);
|
||||
|
||||
// helper functions to send UBX commands to ublox gps chip
|
||||
|
||||
/*
|
||||
// Print the UBX packet for debugging
|
||||
void printPacket(byte *packet, byte len) {
|
||||
char temp[3];
|
||||
|
||||
for (byte i = 0; i < len; i++) {
|
||||
sprintf(temp, "%.2X", packet[i]);
|
||||
ESP_LOGD(TAG, "%s", temp);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Send the packet specified to the receiver.
|
||||
void sendPacket(byte *packet, byte len) {
|
||||
|
||||
uint8_t CK_A = 0;
|
||||
@ -55,55 +29,85 @@ void sendPacket(byte *packet, byte len) {
|
||||
GPS_Serial.write(CK_B);
|
||||
}
|
||||
|
||||
// Send a packet to the receiver to restore default configuration.
|
||||
void restoreDefaults() {
|
||||
// CFG-CFG packet.
|
||||
// UBX CFG-CFG packet
|
||||
byte packet[] = {
|
||||
0xB5, // sync char 1
|
||||
0x62, // sync char 2
|
||||
0x06, // class
|
||||
0x09, // id
|
||||
0x0D, // length
|
||||
0x00, // length
|
||||
0x00, // .
|
||||
0b00011111, // clearmask
|
||||
0b00000110, // clearmask
|
||||
0x00, // clearmask
|
||||
0x00, // clearmask
|
||||
0x00, // savemask
|
||||
0x00, // savemask
|
||||
0x00, // savemask
|
||||
0b00000110, // .
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0x00, // savemask
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0b00011111, // loadmask
|
||||
0b00000110, // loadmask
|
||||
0x00, // loadmask
|
||||
0x00, // loadmask
|
||||
0b00000110, // .
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0b00010001 // devicemask
|
||||
};
|
||||
|
||||
sendPacket(packet, sizeof(packet));
|
||||
}
|
||||
|
||||
// Send a set of packets to the receiver to disable NMEA messages.
|
||||
void setTimePulse() {
|
||||
// UBX TIM-TP packet
|
||||
byte packet[] = {
|
||||
0xB5, // sync char 1
|
||||
0x62, // sync char 2
|
||||
0x06, // class
|
||||
0x07, // id
|
||||
0x14, // length
|
||||
0x40, // time interval for time pulse [us]
|
||||
0x42, // -> 1 sec = 1000000us
|
||||
0x0F, // .
|
||||
0x00, // .
|
||||
0xE8, // length of time pulse [us]
|
||||
0x03, // -> 1000us
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0x01, // status -> positive edge
|
||||
0x00, // timeRef -> UTC
|
||||
0b00000001, // syncMode asynchronized
|
||||
0x00, // reserved
|
||||
0x00, // antenna cable delay [ns]
|
||||
0x00, // .
|
||||
0x00, // receiver rf group delay [ns]
|
||||
0x00, // .
|
||||
0x00, // user time function delay [ns]
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0x00 // .
|
||||
};
|
||||
|
||||
sendPacket(packet, sizeof(packet));
|
||||
}
|
||||
|
||||
void disableNmea() {
|
||||
|
||||
// for tinygps++ we need only $GPGGA and $GPRMC
|
||||
// for getting time we use $GPZDA
|
||||
// we disable all other NMEA messages
|
||||
// thus, we disable all other NMEA messages
|
||||
|
||||
// Array of two bytes for CFG-MSG packets payload.
|
||||
byte messages[][2] = {{0xF0, 0x01}, {0xF0, 0x02}, {0xF0, 0x03}, {0xF0, 0x05},
|
||||
{0xF0, 0x06}, {0xF0, 0x07}, {0xF0, 0x09}, {0xF0, 0x0A},
|
||||
{0xF0, 0x0E}, {0xF1, 0x00}, {0xF1, 0x03}, {0xF1, 0x04},
|
||||
{0xF1, 0x05}, {0xF1, 0x06}};
|
||||
{0xF0, 0x06}, {0xF0, 0x07}, {0xF0, 0x08}, {0xF0, 0x09},
|
||||
{0xF0, 0x0A}, {0xF0, 0x0E}, {0xF1, 0x00}, {0xF1, 0x03},
|
||||
{0xF1, 0x04}, {0xF1, 0x05}, {0xF1, 0x06}};
|
||||
|
||||
// CFG-MSG packet buffer.
|
||||
// UBX CFG-MSG packet
|
||||
byte packet[] = {
|
||||
0xB5, // sync char 1
|
||||
0x62, // sync char 2
|
||||
0x06, // class
|
||||
0x01, // id
|
||||
0x03, // length
|
||||
0x00, // length
|
||||
0x00, // .
|
||||
0x00, // payload (first byte from messages array element)
|
||||
0x00, // payload (second byte from messages array element)
|
||||
0x00 // payload (zero to disable message)
|
||||
@ -124,25 +128,24 @@ void disableNmea() {
|
||||
}
|
||||
}
|
||||
|
||||
// Send a packet to the receiver to change baudrate to 115200.
|
||||
void changeBaudrate(uint32_t baudRate) {
|
||||
// CFG-PRT packet.
|
||||
// UBX CFG-PRT packet
|
||||
byte packet[] = {
|
||||
0xB5, // sync char 1
|
||||
0x62, // sync char 2
|
||||
0x06, // class
|
||||
0x00, // id
|
||||
0x14, // length
|
||||
0x00, // length
|
||||
0x00, // .
|
||||
0x01, // portID (UART 1)
|
||||
0x00, // reserved
|
||||
0x00, // txReady
|
||||
0x00, // .
|
||||
0b11010000, // UART mode: 8bit
|
||||
0b00001000, // UART mode: No Parity, 1 Stopbit
|
||||
0b11010000, // UART mode: 8N1
|
||||
0b00001000, // .
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
(byte)baudRate, // baudrate (4 bytes)
|
||||
(byte)baudRate, // baudrate
|
||||
(byte)(baudRate >> 8), // .
|
||||
(byte)(baudRate >> 16), // .
|
||||
(byte)(baudRate >> 24), // .
|
||||
@ -151,30 +154,9 @@ void changeBaudrate(uint32_t baudRate) {
|
||||
0b00000010, // output protocols: NMEA
|
||||
0x00000000, // .
|
||||
0x00, // reserved
|
||||
0x00, // reserved
|
||||
0x00, // reserved
|
||||
0x00 // reserved
|
||||
};
|
||||
|
||||
sendPacket(packet, sizeof(packet));
|
||||
}
|
||||
|
||||
// Send a packet to the receiver to change frequency to 100 ms.
|
||||
void changeFrequency() {
|
||||
// CFG-RATE packet.
|
||||
byte packet[] = {
|
||||
0xB5, // sync char 1
|
||||
0x62, // sync char 2
|
||||
0x06, // class
|
||||
0x08, // id
|
||||
0x06, // length
|
||||
0x00, // length
|
||||
0x64, // Measurement rate 100ms
|
||||
0x00, // Measurement rate
|
||||
0x01, // Measurement cycles
|
||||
0x00, // Measurement cycles
|
||||
0x00, // Alignment to reference time: UTC time
|
||||
0x00 // payload
|
||||
0x00, // .
|
||||
0x00, // .
|
||||
0x00 // .
|
||||
};
|
||||
|
||||
sendPacket(packet, sizeof(packet));
|
||||
@ -184,8 +166,11 @@ void changeFrequency() {
|
||||
int gps_init(void) {
|
||||
|
||||
ESP_LOGI(TAG, "Opening serial GPS");
|
||||
|
||||
GPS_Serial.begin(GPS_SERIAL);
|
||||
|
||||
restoreDefaults();
|
||||
delay(100);
|
||||
|
||||
changeBaudrate(GPS_BAUDRATE);
|
||||
delay(100);
|
||||
@ -193,8 +178,7 @@ int gps_init(void) {
|
||||
GPS_Serial.updateBaudRate(GPS_BAUDRATE);
|
||||
|
||||
disableNmea();
|
||||
changeFrequency();
|
||||
// enableNavTimeUTC();
|
||||
setTimePulse();
|
||||
|
||||
return 1;
|
||||
|
||||
@ -224,40 +208,38 @@ bool gps_hasfix() {
|
||||
// function to poll UTC time from GPS NMEA data; note: this is costly
|
||||
time_t get_gpstime(uint16_t *msec) {
|
||||
|
||||
// poll NMEA ZDA sentence
|
||||
GPS_Serial.print(ZDA_Request);
|
||||
// wait for gps NMEA answer
|
||||
// vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
|
||||
*msec = 0;
|
||||
|
||||
// did we get a current date & time?
|
||||
if (gpstime.isValid()) {
|
||||
if (gps.time.isValid() && gps.date.isValid() && gps.time.age() < 1000) {
|
||||
|
||||
uint32_t delay_ms =
|
||||
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
|
||||
uint32_t zdatime = atof(gpstime.value());
|
||||
|
||||
// convert UTC time from gps NMEA ZDA sentence to tm format
|
||||
// convert tinygps time format to struct 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
|
||||
gps_tm.tm_sec = gps.time.second();
|
||||
gps_tm.tm_min = gps.time.minute();
|
||||
gps_tm.tm_hour = gps.time.hour();
|
||||
gps_tm.tm_mday = gps.date.day();
|
||||
gps_tm.tm_mon = gps.date.month() - 1; // 1-12 -> 0-11
|
||||
gps_tm.tm_year = gps.date.year() - 1900; // 2000+ -> years since 1900
|
||||
|
||||
// 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 += (time_t)(delay_ms / 1000);
|
||||
*msec = delay_ms % 1000; // fractional seconds
|
||||
#ifdef GPS_INT
|
||||
// if we have a recent GPS PPS pulse, sync on top of next second
|
||||
if (millis() - lastPPS < 1000)
|
||||
*msec = (uint16_t)(millis() - lastPPS);
|
||||
else {
|
||||
ESP_LOGD(TAG, "no PPS from GPS");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "no valid GPS time");
|
||||
|
||||
return 0;
|
||||
|
||||
} // get_gpstime()
|
||||
@ -271,20 +253,24 @@ void gps_loop(void *pvParameters) {
|
||||
|
||||
while (cfg.payloadmask & GPS_DATA) {
|
||||
// feed GPS decoder with serial NMEA data from GPS device
|
||||
while (GPS_Serial.available())
|
||||
if (gps.encode(GPS_Serial.read()))
|
||||
break; // leave encode loop after each NMEA complete sentence
|
||||
while (GPS_Serial.available()) {
|
||||
if (gps.encode(GPS_Serial.read())) {
|
||||
|
||||
// show NMEA data, very noisy, useful only for debugging GPS
|
||||
// ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix:
|
||||
// %u", gps.passedChecksum(), gps.failedChecksum(), gps
|
||||
// .sentencesWithFix());
|
||||
// show NMEA data, very noisy, for debugging GPS
|
||||
// ESP_LOGV(
|
||||
// TAG,
|
||||
// "GPS NMEA data: chars %u / passed %u / failed: %u / with fix:
|
||||
// %u", gps.charsProcessed(), gps.passedChecksum(),
|
||||
// gps.failedChecksum(), gps.sentencesWithFix());
|
||||
|
||||
delay(5); // yield after each sentence to crack NMEA burst
|
||||
}
|
||||
} // read from serial buffer loop
|
||||
delay(5);
|
||||
} // inner while loop
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
} // outer while loop
|
||||
} // infinite while loop
|
||||
|
||||
} // gps_loop()
|
||||
|
||||
|
@ -62,7 +62,7 @@ triggers pps 1 sec impulse
|
||||
|
||||
ISRs fired by CPU or GPIO:
|
||||
DisplayIRQ <- esp32 timer 0
|
||||
CLOCKIRQ <- esp32 timer 1 or GPIO (RTC_INT or GPS_INT)
|
||||
CLOCKIRQ <- esp32 timer 1 or GPIO (RTC_INT)
|
||||
MatrixDisplayIRQ<- esp32 timer 3
|
||||
ButtonIRQ <- GPIO <- Button
|
||||
PMUIRQ <- GPIO <- PMU chip
|
||||
@ -492,8 +492,7 @@ void setup() {
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Starting Timekeeper...");
|
||||
_ASSERT(timepulse_init()); // setup pps timepulse
|
||||
timepulse_start(); // starts pps and cyclic time sync
|
||||
_ASSERT(timepulse_init()); // starts pps and cyclic time sync
|
||||
strcat_P(features, " TIME");
|
||||
|
||||
#endif // timesync
|
||||
|
@ -19,9 +19,8 @@ static const char TAG[] = __FILE__;
|
||||
// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced
|
||||
const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'};
|
||||
|
||||
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
DRAM_ATTR bool TimePulseTick = false;
|
||||
DRAM_ATTR unsigned long lastPPS = millis();
|
||||
timesource_t timeSource = _unsynced;
|
||||
TaskHandle_t ClockTask = NULL;
|
||||
hw_timer_t *ppsIRQ = NULL;
|
||||
@ -144,26 +143,23 @@ uint8_t timepulse_init() {
|
||||
// sntp_init();
|
||||
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
|
||||
|
||||
// use time pulse from GPS as time base with fixed 1Hz frequency
|
||||
// if we have, use PPS time pulse from GPS for syncing time on top of second
|
||||
#ifdef GPS_INT
|
||||
|
||||
// setup external interupt pin for rising edge GPS INT
|
||||
// setup external interupt pin for rising edge of GPS PPS
|
||||
pinMode(GPS_INT, INPUT_PULLDOWN);
|
||||
// setup external rtc 1Hz clock as pulse per second clock
|
||||
ESP_LOGI(TAG, "Timepulse: external (GPS)");
|
||||
return 1; // success
|
||||
attachInterrupt(digitalPinToInterrupt(GPS_INT), GPSIRQ, RISING);
|
||||
#endif
|
||||
|
||||
// use pulse from on board RTC chip as time base with fixed frequency
|
||||
#elif defined RTC_INT
|
||||
// if we have, use pulse from on board RTC chip as time base for calendar time
|
||||
#if defined RTC_INT
|
||||
|
||||
// setup external interupt pin for falling edge RTC INT
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
|
||||
// setup external rtc 1Hz clock as pulse per second clock
|
||||
// setup external rtc 1Hz clock pulse
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
|
||||
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
|
||||
I2C_MUTEX_UNLOCK();
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
|
||||
ESP_LOGI(TAG, "Timepulse: external (RTC)");
|
||||
return 1; // success
|
||||
} else {
|
||||
@ -173,33 +169,39 @@ uint8_t timepulse_init() {
|
||||
return 1; // success
|
||||
|
||||
#else
|
||||
// use ESP32 hardware timer as time base with adjustable frequency
|
||||
// use ESP32 hardware timer as time base for calendar time
|
||||
ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
|
||||
timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms
|
||||
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
|
||||
timerAlarmEnable(ppsIRQ);
|
||||
ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)");
|
||||
return 1; // success
|
||||
|
||||
#endif
|
||||
} // 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
|
||||
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
|
||||
#else // start internal clock esp32 hardware timer
|
||||
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
|
||||
timerAlarmEnable(ppsIRQ);
|
||||
#endif
|
||||
// start cyclic time sync
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||
|
||||
// get time if we don't have one
|
||||
if (timeSource != _set)
|
||||
setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
|
||||
// start cyclic time sync
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||
|
||||
} // timepulse_init
|
||||
|
||||
// interrupt service routine triggered by GPS PPS
|
||||
void IRAM_ATTR GPSIRQ(void) {
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
// take timestamp
|
||||
lastPPS = millis(); // last time of pps
|
||||
|
||||
// yield only if we should
|
||||
if (xHigherPriorityTaskWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
// interrupt service routine triggered by either pps or esp32 hardware timer
|
||||
// interrupt service routine triggered by esp32 hardware timer
|
||||
void IRAM_ATTR CLOCKIRQ(void) {
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
@ -212,11 +214,7 @@ void IRAM_ATTR CLOCKIRQ(void) {
|
||||
|
||||
// flip time pulse ticker, if needed
|
||||
#ifdef HAS_DISPLAY
|
||||
#if (defined GPS_INT || defined RTC_INT)
|
||||
portENTER_CRITICAL(&mux);
|
||||
TimePulseTick = !TimePulseTick; // flip global variable pulse ticker
|
||||
portEXIT_CRITICAL(&mux);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// yield only if we should
|
||||
|
Loading…
Reference in New Issue
Block a user