Add new files for version 1.0.1332

This commit is contained in:
Dmitry
2025-11-14 17:35:21 -05:00
parent af5732e71b
commit 395f1a9be7
16 changed files with 1035 additions and 0 deletions

90
src/atScreenLCDGfx.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include "config.h"
#ifdef USE_SCREEN_LCD_GFX
#include "atScreenLCDGfx.h"
#include "global.h"
cATScreenLCDGfx::cATScreenLCDGfx(atGlobal &global):cATScreen(global)
{
}
cATScreenLCDGfx::~cATScreenLCDGfx()
{
}
bool cATScreenLCDGfx::init()
{
#ifdef VEXT_EN
pinMode(VEXT_EN, OUTPUT);
digitalWrite(VEXT_EN, HIGH);
#endif
m_display = new DisplaySH1106_128x64_I2C(-1);
m_display->begin();
m_display->fill(BLACK);
m_display->setFixedFont(ssd1306xled_font6x8);
m_display->setColor(WHITE);
drawCentreString("Booting", m_display->width()/2, m_display->height()/2);
return true;
}
void cATScreenLCDGfx::showShutdownScreen()
{
m_display->fill(BLACK);
drawCentreString("Shutdown...", m_display->width()/2, m_display->height()/2);
}
void cATScreenLCDGfx::loop()
{
}
void cATScreenLCDGfx::drawCentreString(const char *buf, int x, int y) {
// Get the display's width and the string's dimensions
int16_t x1, y1;
uint16_t text_width, text_height;
NanoFont & font = m_display->getFont();
lcduint_t textHeight = 0;
lcduint_t textWidth = font.getTextSize(buf,&textHeight);
m_display->printFixed(x-textWidth/2,y-textHeight/2,buf,STYLE_NORMAL);
}
void cATScreenLCDGfx::showBootScreen(const char * msg1,const char * msg2)
{
m_display->fill(BLACK);
if(msg1)
drawCentreString(msg1, m_display->width()/2, 10);
}
void cATScreenLCDGfx::closeBootScreen()
{
m_display->fill(BLACK);
}
void cATScreenLCDGfx::beginMainScreen()
{
}
void cATScreenLCDGfx::screenOn()
{
m_display->getInterface().displayOn();
}
void cATScreenLCDGfx::screenOff()
{
m_display->getInterface().displayOff();
}
void cATScreenLCDGfx::endMainScreen()
{
char buffer[32];
//m_tft->fillScreen(TFT_BLACK);
snprintf(buffer,sizeof(buffer),"BAT: %0.2fV, SAT: %02d ", m_global.getPowerStatus()->getBatteryVoltageMv()/1000.0,m_global.m_gpsStatus->getNumSatellites());
m_display->printFixed(0, 10,buffer,STYLE_NORMAL);
}
#endif

27
src/atScreenLCDGfx.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include "atscreen.h"
#include "lcdgfx.h"
#include "lcdgfx_gui.h"
class cATScreenLCDGfx:public cATScreen
{
protected:
#ifdef LCD_SH1106
DisplaySH1106_128x64_I2C *m_display;
#endif
void drawCentreString(const char *buf, int x, int y);
public:
cATScreenLCDGfx(atGlobal &global);
virtual ~cATScreenLCDGfx();
virtual bool init();
virtual void loop();
virtual void showBootScreen(const char * msg1,const char * msg2);
virtual void closeBootScreen();
virtual void beginMainScreen();
virtual void endMainScreen();
virtual void screenOn();
virtual void screenOff();
virtual void showShutdownScreen();
};

82
src/atScreenTFTeSPI.cpp Normal file
View File

@@ -0,0 +1,82 @@
#include "config.h"
#ifdef USE_SCREEN_TFT_ESPI
#include "atScreenTFTeSPI.h"
#include "global.h"
cATScreenTFTeSPI::cATScreenTFTeSPI(atGlobal &global):cATScreen(global)
{
}
cATScreenTFTeSPI::~cATScreenTFTeSPI()
{
}
bool cATScreenTFTeSPI::init()
{
#ifdef VEXT_EN
pinMode(VEXT_EN, OUTPUT);
digitalWrite(VEXT_EN, HIGH);
#endif
digitalWrite(TFT_BL,HIGH);
m_tft = new TFT_eSPI(TFT_WIDTH, TFT_HEIGHT);
m_tft->init();
m_tft->setRotation(1);
m_tft->fillScreen(TFT_BLACK);
m_tft->setTextSize(1);
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
m_tft->drawCentreString("Booting", m_tft->width()/2, m_tft->height()/2, 2);
return true;
}
void cATScreenTFTeSPI::loop()
{
}
void cATScreenTFTeSPI::showBootScreen(const char * msg1,const char * msg2)
{
m_tft->setTextSize(1);
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
m_tft->fillScreen(TFT_BLACK);
if(msg1)
m_tft->drawCentreString(msg1, m_tft->width()/2, 10, 2);
}
void cATScreenTFTeSPI::closeBootScreen()
{
m_tft->fillScreen(TFT_BLACK);
}
void cATScreenTFTeSPI::beginMainScreen()
{
}
void cATScreenTFTeSPI::screenOn()
{
digitalWrite(TFT_BL,HIGH);
}
void cATScreenTFTeSPI::screenOff()
{
digitalWrite(TFT_BL,LOW);
}
void cATScreenTFTeSPI::endMainScreen()
{
char buffer[32];
m_tft->setTextSize(1);
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
//m_tft->fillScreen(TFT_BLACK);
snprintf(buffer,sizeof(buffer),"BAT: %0.2fV, SAT: %02d", m_global.getPowerStatus()->getBatteryVoltageMv()/1000.0,m_global.m_gpsStatus->getNumSatellites());
m_tft->drawString(buffer, 0, 10, 2);
}
#endif

21
src/atScreenTFTeSPI.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "atscreen.h"
#include "TFT_eSPI.h"
class cATScreenTFTeSPI:public cATScreen
{
protected:
TFT_eSPI *m_tft;
public:
cATScreenTFTeSPI(atGlobal &global);
virtual ~cATScreenTFTeSPI();
virtual bool init();
virtual void loop();
virtual void showBootScreen(const char * msg1,const char * msg2);
virtual void closeBootScreen();
virtual void beginMainScreen();
virtual void endMainScreen();
virtual void screenOn();
virtual void screenOff();
};

View File

@@ -0,0 +1,40 @@
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#include "../freertosinc.h"
#include <assert.h>
#ifdef HAS_FREE_RTOS
namespace concurrency
{
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary())
{
assert(semaphore);
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
{
vSemaphoreDelete(semaphore);
}
/**
* Returns false if we were interrupted
*/
bool BinarySemaphoreFreeRTOS::take(uint32_t msec)
{
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
}
void BinarySemaphoreFreeRTOS::give()
{
xSemaphoreGive(semaphore);
}
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
}
} // namespace concurrency
#endif

View File

@@ -0,0 +1,30 @@
#pragma once
#include "../freertosinc.h"
namespace concurrency
{
#ifdef HAS_FREE_RTOS
class BinarySemaphoreFreeRTOS
{
SemaphoreHandle_t semaphore;
public:
BinarySemaphoreFreeRTOS();
~BinarySemaphoreFreeRTOS();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif
} // namespace concurrency

View File

@@ -0,0 +1,28 @@
#include "concurrency/BinarySemaphorePosix.h"
#include "../freertosinc.h"
#ifndef HAS_FREE_RTOS
namespace concurrency
{
BinarySemaphorePosix::BinarySemaphorePosix() {}
BinarySemaphorePosix::~BinarySemaphorePosix() {}
/**
* Returns false if we timed out
*/
bool BinarySemaphorePosix::take(uint32_t msec)
{
delay(msec); // FIXME
return false;
}
void BinarySemaphorePosix::give() {}
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {}
} // namespace concurrency
#endif

View File

@@ -0,0 +1,30 @@
#pragma once
#include "../freertosinc.h"
namespace concurrency
{
#ifndef HAS_FREE_RTOS
class BinarySemaphorePosix
{
// SemaphoreHandle_t semaphore;
public:
BinarySemaphorePosix();
~BinarySemaphorePosix();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif
} // namespace concurrency

View File

@@ -0,0 +1,35 @@
#include "concurrency/InterruptableDelay.h"
#include "../freertosinc.h"
namespace concurrency
{
InterruptableDelay::InterruptableDelay() {}
InterruptableDelay::~InterruptableDelay() {}
/**
* Returns false if we were interrupted
*/
bool InterruptableDelay::delay(uint32_t msec)
{
// LOG_DEBUG("delay %u ", msec);
// sem take will return false if we timed out (i.e. were not interrupted)
bool r = semaphore.take(msec);
// LOG_DEBUG("interrupt=%d\n", r);
return !r;
}
void InterruptableDelay::interrupt()
{
semaphore.give();
}
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
}
} // namespace concurrency

View File

@@ -0,0 +1,41 @@
#pragma once
#include "../freertosinc.h"
#ifdef HAS_FREE_RTOS
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#define BinarySemaphore BinarySemaphoreFreeRTOS
#else
#include "concurrency/BinarySemaphorePosix.h"
#define BinarySemaphore BinarySemaphorePosix
#endif
namespace concurrency
{
/**
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt().
*
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
*
* This is implemented for FreeRTOS but should be easy to port to other operating systems.
*/
class InterruptableDelay
{
BinarySemaphore semaphore;
public:
InterruptableDelay();
~InterruptableDelay();
/**
* Returns false if we were interrupted
*/
bool delay(uint32_t msec);
void interrupt();
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
} // namespace concurrency

View File

@@ -0,0 +1,143 @@
#include "OSThread.h"
#include "../config.h"
#include "../freertosinc.h"
#include "memGet.h"
#include <assert.h>
namespace concurrency
{
/// Show debugging info for disabled threads
bool OSThread::showDisabled;
/// Show debugging info for threads when we run them
bool OSThread::showRun = false;
/// Show debugging info for threads we decide not to run;
bool OSThread::showWaiting = false;
const OSThread *OSThread::currentThread;
ThreadController mainController, timerController;
InterruptableDelay mainDelay;
void OSThread::setup()
{
mainController.ThreadName = "mainController";
timerController.ThreadName = "timerController";
}
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
: Thread(NULL, period), controller(_controller)
{
assertIsSetup();
ThreadName = _name;
if (controller) {
bool added = controller->add(this);
assert(added);
}
}
OSThread::~OSThread()
{
if (controller)
controller->remove(this);
}
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void OSThread::setIntervalFromNow(unsigned long _interval)
{
// Save interval
interval = _interval;
// Cache the next run based on the last_run
_cached_next_run = millis() + interval;
}
bool OSThread::shouldRun(unsigned long time)
{
bool r = Thread::shouldRun(time);
if (showRun && r) {
DEBUG_MSG("Thread %s: run\n", ThreadName.c_str());
}
if (showWaiting && enabled && !r) {
DEBUG_MSG("Thread %s: wait %lu\n", ThreadName.c_str(), interval);
}
if (showDisabled && !enabled) {
DEBUG_MSG("Thread %s: disabled\n", ThreadName.c_str());
}
return r;
}
void OSThread::run()
{
#ifdef DEBUG_HEAP
auto heap = memGet.getFreeHeap();
#endif
currentThread = this;
auto newDelay = runOnce();
#ifdef DEBUG_HEAP
auto newHeap = memGet.getFreeHeap();
if (newHeap < heap)
DEBUG_MSG("------ Thread %s leaked heap %d -> %d (%d) ------\n", ThreadName.c_str(), heap, newHeap, newHeap - heap);
if (heap < newHeap)
DEBUG_MSG("++++++ Thread %s freed heap %d -> %d (%d) ++++++\n", ThreadName.c_str(), heap, newHeap, newHeap - heap);
#endif
runned();
if (newDelay >= 0)
setInterval(newDelay);
currentThread = NULL;
}
int32_t OSThread::disable()
{
enabled = false;
setInterval(INT32_MAX);
return INT32_MAX;
}
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
bool hasBeenSetup;
void assertIsSetup()
{
/**
* Dear developer comrade - If this assert fails() that means you need to fix the following:
*
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
assert(hasBeenSetup);
}
} // namespace concurrency

View File

@@ -0,0 +1,90 @@
#pragma once
#include <cstdlib>
#include <stdint.h>
#include "Thread.h"
#include "ThreadController.h"
#include "concurrency/InterruptableDelay.h"
namespace concurrency
{
extern ThreadController mainController, timerController;
extern InterruptableDelay mainDelay;
#define RUN_SAME -1
/**
* @brief Base threading
*
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
*
* TODO FIXME @geeksville
*
* move more things into OSThreads
* remove lock/lockguard
*
* move typedQueue into concurrency
* remove freertos from typedqueue
*/
class OSThread : public Thread
{
ThreadController *controller;
/// Show debugging info for disabled threads
static bool showDisabled;
/// Show debugging info for threads when we run them
static bool showRun;
/// Show debugging info for threads we decide not to run;
static bool showWaiting;
public:
/// For debug printing only (might be null)
static const OSThread *currentThread;
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
virtual ~OSThread();
virtual bool shouldRun(unsigned long time);
static void setup();
int32_t disable();
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void setIntervalFromNow(unsigned long _interval);
protected:
/**
* The method that will be called each time our thread gets a chance to run
*
* Returns desired period for next invocation (or RUN_SAME for no change)
*/
virtual int32_t runOnce() = 0;
// Do not override this
virtual void run();
};
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
extern bool hasBeenSetup;
void assertIsSetup();
} // namespace concurrency

View File

@@ -0,0 +1,73 @@
/**
* @file memGet.cpp
* @brief Implementation of MemGet class that provides functions to get memory information.
*
* This file contains the implementation of MemGet class that provides functions to get
* information about free heap, heap size, free psram and psram size. The functions are
* implemented for ESP32 and NRF52 architectures. If the platform does not have heap
* management function implemented, the functions return UINT32_MAX or 0.
*/
#include "memGet.h"
#include "../freertosinc.h"
MemGet memGet;
/**
* Returns the amount of free heap memory in bytes.
* @return uint32_t The amount of free heap memory in bytes.
*/
uint32_t MemGet::getFreeHeap()
{
#ifdef ARCH_ESP32
return ESP.getFreeHeap();
#elif defined(ARCH_NRF52)
return dbgHeapFree();
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
#endif
}
/**
* Returns the size of the heap memory in bytes.
* @return uint32_t The size of the heap memory in bytes.
*/
uint32_t MemGet::getHeapSize()
{
#ifdef ARCH_ESP32
return ESP.getHeapSize();
#elif defined(ARCH_NRF52)
return dbgHeapTotal();
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
#endif
}
/**
* Returns the amount of free psram memory in bytes.
*
* @return The amount of free psram memory in bytes.
*/
uint32_t MemGet::getFreePsram()
{
#ifdef ARCH_ESP32
return ESP.getFreePsram();
#else
return 0;
#endif
}
/**
* @brief Returns the size of the PSRAM memory.
*
* @return uint32_t The size of the PSRAM memory.
*/
uint32_t MemGet::getPsramSize()
{
#ifdef ARCH_ESP32
return ESP.getPsramSize();
#else
return 0;
#endif
}

18
src/concurrency/memGet.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#ifndef _MT_MEMGET_H
#define _MT_MEMGET_H
#include <Arduino.h>
class MemGet
{
public:
uint32_t getFreeHeap();
uint32_t getHeapSize();
uint32_t getFreePsram();
uint32_t getPsramSize();
};
extern MemGet memGet;
#endif

241
src/gps/GenericGPS.cpp Normal file
View File

@@ -0,0 +1,241 @@
#include "GenericGPS.h"
#include "global.h"
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
static int32_t toDegInt(RawDegrees d)
{
int32_t degMult = 10000000; // 1e7
int32_t r = d.deg * degMult + d.billionths / 100;
if (d.negative)
r *= -1;
return r;
}
bool GenericGPS::setup()
{
#ifdef VEXT_EN
pinMode(VEXT_EN, OUTPUT);
digitalWrite(VEXT_EN, HIGH);
#endif
#ifdef VGNSS_CTRL
pinMode(VGNSS_CTRL, OUTPUT);
digitalWrite(VGNSS_CTRL, LOW);
#endif
delay(10);
#ifdef PIN_GPS_RESET
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
pinMode(PIN_GPS_RESET, OUTPUT);
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif
if(serialInitialized)
{
_serial_gps.end();
delay(10);
serialInitialized = false;
}
if(!serialInitialized)
{
_serial_gps.setRxBufferSize(2048);
#ifdef GPS_RX_PIN
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps.begin(GPS_BAUDRATE);
#endif
serialInitialized = true;
delay(30);
}
gnssModel = probe();
if (gnssModel == GNSS_MODEL_MTK) {
_serial_gps.write("$PCAS04,7*1E\r\n");
delay(250);
// only ask for RMC and GGA
_serial_gps.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
delay(250);
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
_serial_gps.write("$PCAS11,3*1E\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_UC6580) {
_serial_gps.write("$CFGSYS,h35155\r\n");
//_serial_gps.write("$CFGSYS,h15\r\n");
delay(250);
}
return true;
}
GnssModel_t GenericGPS::probe()
{
#if defined(GPS_L76K)
return GNSS_MODEL_MTK;
#elif defined(GPS_UC6580)
return GNSS_MODEL_UC6580;
#else
return GNSS_MODEL_UNKNOWN;
#endif
}
bool GenericGPS::whileIdle()
{
bool isValid = false;
while (_serial_gps.available() > 0) {
int c = _serial_gps.read();
// DEBUG_MSG("%c",c);
isValid |= reader.encode(c);
}
return isValid;
}
bool GenericGPS::lookForTime()
{
auto ti = reader.time;
auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
struct tm t;
t.tm_sec = ti.second();
t.tm_min = ti.minute();
t.tm_hour = ti.hour();
t.tm_mday = d.day();
t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
if (t.tm_mon > -1) {
perhapsSetRTC(t,ti.centisecond()*10);
return true;
} else
return false;
} else
return false;
}
bool GenericGPS::hasLock() const
{
if (fixQual >= 1 && fixQual <= 5) {
return true;
}
return false;
}
bool GenericGPS::lookForLocation()
{
// By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
fixQual = reader.fixQuality();
// check if GPS has an acceptable lock
if (!hasLock())
return false;
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
gsafixtype.age(),
#else
0,
#endif
reader.date.age(), reader.time.age());
#endif // GPS_EXTRAVERBOSE
// check if a complete GPS solution set is available for reading
// tinyGPSDatum::age() also includes isValid() test
// FIXME
if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
(reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
DEBUG_MSG("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
return false;
}
// Is this a new point or are we re-reading the previous one?
if (!reader.location.isUpdated())
return false;
// We know the solution is fresh and valid, so just read the data
auto loc = reader.location.value();
// Bail out EARLY to avoid overwriting previous good data (like #857)
if (toDegInt(loc.lat) > 900000000) {
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
#endif
return false;
}
if (toDegInt(loc.lng) > 1800000000) {
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
#endif
return false;
}
dop = reader.hdop.value();
latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng);
altitude = reader.altitude.meters();
// Nice to have, if available
if (reader.satellites.isUpdated()) {
numSatellites = reader.satellites.value();
}
if (reader.course.isUpdated() && reader.course.isValid()) {
if (reader.course.value() < 36000) { // sanity check
heading =
reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
} else {
DEBUG_MSG("BOGUS course.value() REJECTED: %d\n", reader.course.value());
}
}
return true;
}
void GenericGPS::shutdown()
{
}
int32_t GenericGPS::runOnce()
{
if (whileIdle()) {
// if we have received valid NMEA claim we are connected
isConnected = true;
};
// If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis();
if ((now - lastWhileActiveMsec) > 5000) {
lastWhileActiveMsec = now;
whileActive();
}
bool gotTime = false;
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
}
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
hasValidLocation = true;
}
DEBUG_MSG("GPS numSatellites %d, %d\n", numSatellites, fixQual);
const GPSStatus status = GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, hacc, heading, numSatellites);
newStatus.notifyObservers(&status);
return 1000;
}

46
src/gps/GenericGPS.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include "GPS.h"
#include "Observer.h"
#include "concurrency/OSThread.h"
#include "TinyGPS++.h"
typedef enum {
GNSS_MODEL_MTK,
GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN,
} GnssModel_t;
class GenericGPS : public GPS, private concurrency::OSThread
{
uint32_t lastWhileActiveMsec = 0;
TinyGPSPlus reader;
uint8_t fixQual = 0; // fix quality from GPGGA
uint32_t lastChecksumFailCount = 0;
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
bool serialInitialized = false;
public:
GenericGPS():concurrency::OSThread("GPS") {};
virtual ~GenericGPS(){};
GnssModel_t probe();
virtual bool setup();
virtual int32_t runOnce() override;
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock(){};
virtual void reset(){};
virtual void loop(){};
virtual void shutdown();
virtual bool whileIdle();
virtual void whileActive() {}
virtual bool lookForTime();
virtual bool lookForLocation();
virtual bool hasLock() const;
};