2020-02-06 07:39:21 -08:00
|
|
|
#pragma once
|
2024-03-25 05:33:57 -06:00
|
|
|
#include "configuration.h"
|
|
|
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
2020-02-06 07:39:21 -08:00
|
|
|
|
2020-07-10 11:43:14 -07:00
|
|
|
#include "GPSStatus.h"
|
2024-08-22 10:15:23 -07:00
|
|
|
#include "GpioLogic.h"
|
2020-07-10 11:43:14 -07:00
|
|
|
#include "Observer.h"
|
2023-09-16 23:10:10 -05:00
|
|
|
#include "TinyGPS++.h"
|
2020-10-10 09:57:57 +08:00
|
|
|
#include "concurrency/OSThread.h"
|
2023-09-23 23:45:35 -05:00
|
|
|
#include "input/RotaryEncoderInterruptImpl1.h"
|
|
|
|
|
#include "input/UpDownInterruptImpl1.h"
|
|
|
|
|
#include "modules/PositionModule.h"
|
|
|
|
|
|
|
|
|
|
// Allow defining the polarity of the ENABLE output. default is active high
|
|
|
|
|
#ifndef GPS_EN_ACTIVE
|
|
|
|
|
#define GPS_EN_ACTIVE 1
|
|
|
|
|
#endif
|
2020-05-04 11:15:05 -07:00
|
|
|
|
2023-01-21 06:32:41 -05:00
|
|
|
struct uBloxGnssModelInfo {
|
|
|
|
|
char swVersion[30];
|
|
|
|
|
char hwVersion[10];
|
2022-09-16 02:43:04 +08:00
|
|
|
uint8_t extensionNo;
|
2023-01-21 06:32:41 -05:00
|
|
|
char extension[10][30];
|
|
|
|
|
};
|
2022-09-16 02:43:04 +08:00
|
|
|
|
2024-04-16 16:03:51 +02:00
|
|
|
typedef enum {
|
|
|
|
|
GNSS_MODEL_ATGM336H,
|
|
|
|
|
GNSS_MODEL_MTK,
|
|
|
|
|
GNSS_MODEL_UBLOX,
|
|
|
|
|
GNSS_MODEL_UC6580,
|
|
|
|
|
GNSS_MODEL_UNKNOWN,
|
2024-08-09 09:18:18 +08:00
|
|
|
GNSS_MODEL_MTK_L76B,
|
|
|
|
|
GNSS_MODEL_AG3335
|
2024-04-16 16:03:51 +02:00
|
|
|
} GnssModel_t;
|
2022-09-16 02:43:04 +08:00
|
|
|
|
2023-09-02 04:25:18 -05:00
|
|
|
typedef enum {
|
|
|
|
|
GNSS_RESPONSE_NONE,
|
|
|
|
|
GNSS_RESPONSE_NAK,
|
|
|
|
|
GNSS_RESPONSE_FRAME_ERRORS,
|
|
|
|
|
GNSS_RESPONSE_OK,
|
|
|
|
|
} GPS_RESPONSE;
|
|
|
|
|
|
2024-06-08 02:41:46 +12:00
|
|
|
enum GPSPowerState : uint8_t {
|
2024-07-11 15:26:43 +12:00
|
|
|
GPS_ACTIVE, // Awake and want a position
|
|
|
|
|
GPS_IDLE, // Awake, but not wanting another position yet
|
|
|
|
|
GPS_SOFTSLEEP, // Physically powered on, but soft-sleeping
|
|
|
|
|
GPS_HARDSLEEP, // Physically powered off, but scheduled to wake
|
|
|
|
|
GPS_OFF // Powered off indefinitely
|
2024-06-08 02:41:46 +12:00
|
|
|
};
|
|
|
|
|
|
2020-06-20 18:59:41 -07:00
|
|
|
// Generate a string representation of DOP
|
|
|
|
|
const char *getDOPString(uint32_t dop);
|
|
|
|
|
|
2020-02-06 07:39:21 -08:00
|
|
|
/**
|
2024-08-11 20:06:38 +08:00
|
|
|
* A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading
|
2020-03-18 19:15:51 -07:00
|
|
|
*
|
2020-02-06 07:39:21 -08:00
|
|
|
* When new data is available it will notify observers.
|
|
|
|
|
*/
|
2020-10-10 09:57:57 +08:00
|
|
|
class GPS : private concurrency::OSThread
|
2020-02-06 07:39:21 -08:00
|
|
|
{
|
2023-09-16 23:10:10 -05:00
|
|
|
TinyGPSPlus reader;
|
|
|
|
|
uint8_t fixQual = 0; // fix quality from GPGGA
|
|
|
|
|
uint32_t lastChecksumFailCount = 0;
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
|
|
|
|
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
|
|
|
|
// via optional feature "custom fields", currently disabled (bug #525)
|
|
|
|
|
TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
|
|
|
|
|
TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
|
|
|
|
|
uint8_t fixType = 0; // fix type from GPGSA
|
|
|
|
|
#endif
|
2020-10-01 09:11:54 -07:00
|
|
|
private:
|
2023-09-10 22:21:14 -05:00
|
|
|
const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600};
|
2024-07-25 10:10:38 +08:00
|
|
|
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
2023-09-16 23:10:10 -05:00
|
|
|
uint32_t rx_gpio = 0;
|
|
|
|
|
uint32_t tx_gpio = 0;
|
|
|
|
|
|
2023-09-10 22:21:14 -05:00
|
|
|
int speedSelect = 0;
|
|
|
|
|
int probeTries = 2;
|
2020-10-01 09:11:54 -07:00
|
|
|
|
2021-10-24 12:38:35 +00:00
|
|
|
/**
|
|
|
|
|
* hasValidLocation - indicates that the position variables contain a complete
|
2024-01-07 09:35:19 -06:00
|
|
|
* GPS location, valid and fresh (< gps_update_interval + position_broadcast_secs)
|
2021-10-24 12:38:35 +00:00
|
|
|
*/
|
2020-05-04 11:15:05 -07:00
|
|
|
bool hasValidLocation = false; // default to false, until we complete our first read
|
2020-02-19 08:17:28 -08:00
|
|
|
|
2023-09-23 23:45:35 -05:00
|
|
|
bool isInPowersave = false;
|
2020-09-28 17:04:19 -07:00
|
|
|
|
2020-10-10 09:20:38 +08:00
|
|
|
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
|
|
|
|
|
|
|
|
|
bool hasGPS = false; // Do we have a GPS we are talking to
|
|
|
|
|
|
2023-09-10 22:21:14 -05:00
|
|
|
bool GPSInitFinished = false; // Init thread finished?
|
|
|
|
|
bool GPSInitStarted = false; // Init thread finished?
|
|
|
|
|
|
2024-06-15 01:28:01 +12:00
|
|
|
GPSPowerState powerState = GPS_OFF; // GPS_ACTIVE if we want a location right now
|
2024-06-08 02:41:46 +12:00
|
|
|
|
2020-10-10 09:20:38 +08:00
|
|
|
uint8_t numSatellites = 0;
|
|
|
|
|
|
2020-10-30 17:05:32 +08:00
|
|
|
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
2020-10-01 09:11:54 -07:00
|
|
|
|
2020-07-10 11:43:14 -07:00
|
|
|
public:
|
|
|
|
|
/** If !NULL we will use this serial port to construct our GPS */
|
2024-03-14 16:18:33 +01:00
|
|
|
#if defined(RPI_PICO_WAVESHARE)
|
|
|
|
|
static SerialUART *_serial_gps;
|
|
|
|
|
#else
|
2020-07-09 21:27:34 -07:00
|
|
|
static HardwareSerial *_serial_gps;
|
2024-03-14 16:18:33 +01:00
|
|
|
#endif
|
2023-10-01 20:39:18 -05:00
|
|
|
static uint8_t _message_PMREQ[];
|
2024-02-05 08:02:30 -07:00
|
|
|
static uint8_t _message_PMREQ_10[];
|
2023-09-10 22:21:14 -05:00
|
|
|
static const uint8_t _message_CFG_RXM_PSM[];
|
|
|
|
|
static const uint8_t _message_CFG_RXM_ECO[];
|
|
|
|
|
static const uint8_t _message_CFG_PM2[];
|
|
|
|
|
static const uint8_t _message_GNSS_7[];
|
2024-02-05 08:02:30 -07:00
|
|
|
static const uint8_t _message_GNSS_8[];
|
|
|
|
|
static const uint8_t _message_JAM_6_7[];
|
|
|
|
|
static const uint8_t _message_JAM_8[];
|
2023-09-10 22:21:14 -05:00
|
|
|
static const uint8_t _message_NAVX5[];
|
2024-02-05 08:02:30 -07:00
|
|
|
static const uint8_t _message_NAVX5_8[];
|
|
|
|
|
static const uint8_t _message_NMEA[];
|
2024-02-14 06:06:38 -07:00
|
|
|
static const uint8_t _message_DISABLE_TXT_INFO[];
|
2023-09-10 22:21:14 -05:00
|
|
|
static const uint8_t _message_1HZ[];
|
2024-01-30 16:38:31 -07:00
|
|
|
static const uint8_t _message_GLL[];
|
2023-09-10 22:21:14 -05:00
|
|
|
static const uint8_t _message_GSA[];
|
|
|
|
|
static const uint8_t _message_GSV[];
|
|
|
|
|
static const uint8_t _message_VTG[];
|
|
|
|
|
static const uint8_t _message_RMC[];
|
2024-01-30 16:38:31 -07:00
|
|
|
static const uint8_t _message_AID[];
|
2023-09-10 22:21:14 -05:00
|
|
|
static const uint8_t _message_GGA[];
|
|
|
|
|
static const uint8_t _message_PMS[];
|
|
|
|
|
static const uint8_t _message_SAVE[];
|
|
|
|
|
|
2024-02-14 06:06:38 -07:00
|
|
|
// VALSET Commands for M10
|
|
|
|
|
static const uint8_t _message_VALSET_PM[];
|
|
|
|
|
static const uint8_t _message_VALSET_PM_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_PM_BBR[];
|
|
|
|
|
static const uint8_t _message_VALSET_ITFM_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_ITFM_BBR[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[];
|
|
|
|
|
static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[];
|
|
|
|
|
static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[];
|
|
|
|
|
|
2024-04-16 16:03:51 +02:00
|
|
|
// CASIC commands for ATGM336H
|
|
|
|
|
static const uint8_t _message_CAS_CFG_RST_FACTORY[];
|
|
|
|
|
static const uint8_t _message_CAS_CFG_NAVX_CONF[];
|
|
|
|
|
static const uint8_t _message_CAS_CFG_RATE_1HZ[];
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_Position p = meshtastic_Position_init_default;
|
2021-09-05 15:10:06 +00:00
|
|
|
|
2024-08-22 10:15:23 -07:00
|
|
|
/** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced
|
|
|
|
|
* implementations. Those boards will set this public variable to a custom implementation.
|
|
|
|
|
*
|
|
|
|
|
* Normally set by GPS::createGPS()
|
|
|
|
|
*/
|
2024-08-28 10:44:46 -07:00
|
|
|
GpioVirtPin *enablePin = NULL;
|
2024-08-22 10:15:23 -07:00
|
|
|
|
2020-10-10 09:57:57 +08:00
|
|
|
GPS() : concurrency::OSThread("GPS") {}
|
|
|
|
|
|
2021-03-10 15:21:54 +08:00
|
|
|
virtual ~GPS();
|
2020-02-21 12:24:35 -08:00
|
|
|
|
2020-08-12 15:51:57 -07:00
|
|
|
/** We will notify this observable anytime GPS state has changed meaningfully */
|
2020-06-28 18:17:52 -07:00
|
|
|
Observable<const meshtastic::GPSStatus *> newStatus;
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2020-05-04 11:15:05 -07:00
|
|
|
/**
|
|
|
|
|
* Returns true if we succeeded
|
|
|
|
|
*/
|
2020-10-01 09:11:54 -07:00
|
|
|
virtual bool setup();
|
2020-05-04 17:39:57 -07:00
|
|
|
|
2023-09-23 23:45:35 -05:00
|
|
|
// re-enable the thread
|
|
|
|
|
void enable();
|
|
|
|
|
|
|
|
|
|
// Disable the thread
|
|
|
|
|
int32_t disable() override;
|
|
|
|
|
|
2024-02-01 15:24:39 -06:00
|
|
|
// toggle between enabled/disabled
|
|
|
|
|
void toggleGpsMode();
|
|
|
|
|
|
2024-07-11 15:26:43 +12:00
|
|
|
// Change the power state of the GPS - for power saving / shutdown
|
|
|
|
|
void setPowerState(GPSPowerState newState, uint32_t sleepMs = 0);
|
2023-09-23 23:45:35 -05:00
|
|
|
|
2021-09-02 13:11:11 +00:00
|
|
|
/// Returns true if we have acquired GPS lock.
|
|
|
|
|
virtual bool hasLock();
|
2020-03-26 09:24:53 -07:00
|
|
|
|
2022-05-21 22:38:33 +02:00
|
|
|
/// Returns true if there's valid data flow with the chip.
|
|
|
|
|
virtual bool hasFlow();
|
|
|
|
|
|
2020-10-10 09:20:38 +08:00
|
|
|
/// Return true if we are connected to a GPS
|
|
|
|
|
bool isConnected() const { return hasGPS; }
|
|
|
|
|
|
2024-02-01 15:24:39 -06:00
|
|
|
bool isPowerSaving() const { return config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED; }
|
2022-12-17 23:32:20 +01:00
|
|
|
|
2023-08-12 09:29:44 -05:00
|
|
|
// Empty the input buffer as quickly as possible
|
|
|
|
|
void clearBuffer();
|
|
|
|
|
|
2023-09-05 00:26:42 -04:00
|
|
|
// Create a ublox packet for editing in memory
|
|
|
|
|
uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
|
2024-04-16 16:03:51 +02:00
|
|
|
uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
|
2023-09-05 00:26:42 -04:00
|
|
|
|
2023-09-05 11:59:34 -04:00
|
|
|
// scratch space for creating ublox packets
|
|
|
|
|
uint8_t UBXscratch[250] = {0};
|
|
|
|
|
|
2023-10-03 12:05:40 -05:00
|
|
|
int rebootsSeen = 0;
|
|
|
|
|
|
2023-09-10 22:21:14 -05:00
|
|
|
int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis);
|
|
|
|
|
GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis);
|
|
|
|
|
GPS_RESPONSE getACK(const char *message, uint32_t waitMillis);
|
2020-10-05 15:29:26 +08:00
|
|
|
|
2024-04-16 16:03:51 +02:00
|
|
|
GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis);
|
|
|
|
|
|
2023-09-16 23:10:10 -05:00
|
|
|
virtual bool factoryReset();
|
|
|
|
|
|
|
|
|
|
// Creates an instance of the GPS class.
|
|
|
|
|
// Returns the new instance or null if the GPS is not present.
|
|
|
|
|
static GPS *createGps();
|
2023-09-12 16:46:46 -05:00
|
|
|
|
2024-07-11 15:26:43 +12:00
|
|
|
// Wake the GPS hardware - ready for an update
|
|
|
|
|
void up();
|
|
|
|
|
|
|
|
|
|
// Let the GPS hardware save power between updates
|
|
|
|
|
void down();
|
|
|
|
|
|
2023-09-10 22:21:14 -05:00
|
|
|
protected:
|
2020-05-04 11:15:05 -07:00
|
|
|
/**
|
2020-10-01 09:11:54 -07:00
|
|
|
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
|
|
|
|
* Override this method to check for new locations
|
|
|
|
|
*
|
|
|
|
|
* @return true if we've acquired a time
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
|
|
|
|
* Override this method to check for new locations
|
|
|
|
|
*
|
|
|
|
|
* @return true if we've acquired a new location
|
|
|
|
|
*/
|
2020-09-28 17:04:19 -07:00
|
|
|
|
2020-10-10 09:20:38 +08:00
|
|
|
/// Record that we have a GPS
|
|
|
|
|
void setConnected();
|
|
|
|
|
|
2023-09-16 23:10:10 -05:00
|
|
|
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
|
|
|
|
|
*
|
|
|
|
|
* Return true if we received a valid message from the GPS
|
|
|
|
|
*/
|
2024-07-11 15:26:43 +12:00
|
|
|
virtual bool whileActive();
|
2023-09-16 23:10:10 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
|
|
|
|
* Override this method to check for new locations
|
|
|
|
|
*
|
|
|
|
|
* @return true if we've acquired a time
|
|
|
|
|
*/
|
|
|
|
|
virtual bool lookForTime();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
|
|
|
|
* Override this method to check for new locations
|
|
|
|
|
*
|
|
|
|
|
* @return true if we've acquired a new location
|
|
|
|
|
*/
|
|
|
|
|
virtual bool lookForLocation();
|
|
|
|
|
|
2020-10-01 09:11:54 -07:00
|
|
|
private:
|
2020-10-30 17:05:32 +08:00
|
|
|
/// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
|
|
|
|
|
/// always returns 0 to indicate okay to sleep
|
|
|
|
|
int prepareDeepSleep(void *unused);
|
|
|
|
|
|
2023-07-02 02:20:40 +02:00
|
|
|
// Calculate checksum
|
2023-09-05 00:26:42 -04:00
|
|
|
void UBXChecksum(uint8_t *message, size_t length);
|
2024-04-16 16:03:51 +02:00
|
|
|
void CASChecksum(uint8_t *message, size_t length);
|
2023-07-02 02:20:40 +02:00
|
|
|
|
2024-07-11 15:26:43 +12:00
|
|
|
/** Set power with EN pin, if relevant
|
|
|
|
|
*/
|
|
|
|
|
void writePinEN(bool on);
|
|
|
|
|
|
|
|
|
|
/** Set the value of the STANDBY pin, if relevant
|
|
|
|
|
*/
|
|
|
|
|
void writePinStandby(bool standby);
|
|
|
|
|
|
|
|
|
|
/** Set GPS power with PMU, if relevant
|
2020-10-05 14:43:44 +08:00
|
|
|
*/
|
2024-07-11 15:26:43 +12:00
|
|
|
void setPowerPMU(bool on);
|
2020-10-05 14:43:44 +08:00
|
|
|
|
2024-07-11 15:26:43 +12:00
|
|
|
/** Set UBLOX power, if relevant
|
2020-10-05 14:43:44 +08:00
|
|
|
*/
|
2024-07-11 15:26:43 +12:00
|
|
|
void setPowerUBLOX(bool on, uint32_t sleepMs = 0);
|
2020-10-05 14:43:44 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tell users we have new GPS readings
|
|
|
|
|
*/
|
|
|
|
|
void publishUpdate();
|
2020-10-10 09:57:57 +08:00
|
|
|
|
2022-01-24 17:24:40 +00:00
|
|
|
virtual int32_t runOnce() override;
|
2022-09-16 02:43:04 +08:00
|
|
|
|
2023-01-21 06:32:41 -05:00
|
|
|
// Get GNSS model
|
2023-07-16 23:57:14 +02:00
|
|
|
String getNMEA();
|
2023-08-28 11:13:17 -05:00
|
|
|
GnssModel_t probe(int serialSpeed);
|
2022-09-16 02:43:04 +08:00
|
|
|
|
2023-01-21 06:32:41 -05:00
|
|
|
// delay counter to allow more sats before fixed position stops GPS thread
|
|
|
|
|
uint8_t fixeddelayCtr = 0;
|
|
|
|
|
|
2024-06-21 17:25:54 -05:00
|
|
|
const char *powerStateToString();
|
|
|
|
|
|
2022-09-16 02:43:04 +08:00
|
|
|
protected:
|
2023-07-08 16:30:52 -07:00
|
|
|
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
|
2020-02-06 07:39:21 -08:00
|
|
|
};
|
|
|
|
|
|
2024-03-25 05:33:57 -06:00
|
|
|
extern GPS *gps;
|
2024-08-22 10:15:23 -07:00
|
|
|
#endif // Exclude GPS
|