2020-02-06 15:39:21 +00:00
# include "GPS.h"
2020-10-05 06:43:44 +00:00
# include "NodeDB.h"
2020-10-07 23:46:20 +00:00
# include "RTC.h"
2020-03-18 16:16:44 +00:00
# include "configuration.h"
2020-10-01 16:11:54 +00:00
# include "sleep.h"
2020-04-15 03:22:27 +00:00
# include <assert.h>
2020-02-06 15:39:21 +00:00
2020-07-10 18:43:14 +00:00
// If we have a serial GPS port it will not be null
2020-04-15 03:22:27 +00:00
# ifdef GPS_RX_PIN
2020-05-04 18:15:05 +00:00
HardwareSerial _serial_gps_real ( GPS_SERIAL_NUM ) ;
2020-07-10 04:27:34 +00:00
HardwareSerial * GPS : : _serial_gps = & _serial_gps_real ;
2020-10-15 05:47:10 +00:00
# elif defined(NRF52840_XXAA) || defined(NRF52833_XXAA)
2020-07-10 04:27:34 +00:00
// Assume NRF52840
HardwareSerial * GPS : : _serial_gps = & Serial1 ;
2020-04-15 03:22:27 +00:00
# else
2020-07-10 04:27:34 +00:00
HardwareSerial * GPS : : _serial_gps = NULL ;
2020-04-15 03:22:27 +00:00
# endif
2020-02-20 04:02:57 +00:00
2020-07-10 21:57:33 +00:00
# ifdef GPS_I2C_ADDRESS
2020-07-11 03:17:20 +00:00
uint8_t GPS : : i2cAddress = GPS_I2C_ADDRESS ;
2020-07-10 21:54:32 +00:00
# else
2020-07-10 18:43:14 +00:00
uint8_t GPS : : i2cAddress = 0 ;
2020-07-10 21:54:32 +00:00
# endif
2020-07-10 18:43:14 +00:00
2020-05-04 18:15:05 +00:00
GPS * gps ;
2020-03-14 01:44:14 +00:00
2020-11-07 01:15:28 +00:00
/// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once.
static bool didSerialInit ;
2020-10-25 09:07:54 +00:00
bool GPS : : setupGPS ( )
{
2020-11-07 01:15:28 +00:00
if ( _serial_gps & & ! didSerialInit ) {
didSerialInit = true ;
2020-10-25 09:07:54 +00:00
# ifdef GPS_RX_PIN
_serial_gps - > begin ( GPS_BAUDRATE , SERIAL_8N1 , GPS_RX_PIN , GPS_TX_PIN ) ;
# else
_serial_gps - > begin ( GPS_BAUDRATE ) ;
# endif
# ifndef NO_ESP32
_serial_gps - > setRxBufferSize ( 2048 ) ; // the default is 256
# endif
}
return true ;
}
2020-10-01 16:11:54 +00:00
bool GPS : : setup ( )
{
2020-10-17 05:15:12 +00:00
// Master power for the GPS
# ifdef PIN_GPS_EN
digitalWrite ( PIN_GPS_EN , PIN_GPS_EN ) ;
pinMode ( PIN_GPS_EN , OUTPUT ) ;
# endif
2020-10-24 10:40:47 +00:00
# ifdef PIN_GPS_RESET
digitalWrite ( PIN_GPS_RESET , 1 ) ; // assert for 10ms
pinMode ( PIN_GPS_RESET , OUTPUT ) ;
delay ( 10 ) ;
digitalWrite ( PIN_GPS_RESET , 0 ) ;
# endif
2020-10-05 07:29:26 +00:00
setAwake ( true ) ; // Wake GPS power before doing any init
bool ok = setupGPS ( ) ;
2020-10-01 16:11:54 +00:00
2020-10-30 09:05:32 +00:00
if ( ok ) {
2020-10-05 07:29:26 +00:00
notifySleepObserver . observe ( & notifySleep ) ;
2020-10-30 09:05:32 +00:00
notifyDeepSleepObserver . observe ( & notifyDeepSleep ) ;
}
2020-10-05 07:29:26 +00:00
return ok ;
2020-10-01 16:11:54 +00:00
}
2020-10-13 06:43:28 +00:00
// Allow defining the polarity of the WAKE output. default is active high
# ifndef GPS_WAKE_ACTIVE
# define GPS_WAKE_ACTIVE 1
# endif
2020-10-13 05:59:06 +00:00
void GPS : : wake ( )
{
# ifdef PIN_GPS_WAKE
2020-10-13 06:43:28 +00:00
digitalWrite ( PIN_GPS_WAKE , GPS_WAKE_ACTIVE ) ;
2020-10-13 05:59:06 +00:00
pinMode ( PIN_GPS_WAKE , OUTPUT ) ;
# endif
}
void GPS : : sleep ( ) {
# ifdef PIN_GPS_WAKE
2020-10-13 06:43:28 +00:00
digitalWrite ( PIN_GPS_WAKE , GPS_WAKE_ACTIVE ? 0 : 1 ) ;
2020-10-13 05:59:06 +00:00
pinMode ( PIN_GPS_WAKE , OUTPUT ) ;
# endif
}
2020-10-10 01:20:38 +00:00
/// Record that we have a GPS
void GPS : : setConnected ( )
{
if ( ! hasGPS ) {
hasGPS = true ;
shouldPublish = true ;
}
}
void GPS : : setNumSatellites ( uint8_t n )
{
if ( n ! = numSatellites ) {
numSatellites = n ;
shouldPublish = true ;
}
}
2020-09-29 00:04:19 +00:00
/**
* Switch the GPS into a mode where we are actively looking for a lock , or alternatively switch GPS into a low power mode
*
* calls sleep / wake
*/
2020-10-01 16:11:54 +00:00
void GPS : : setAwake ( bool on )
2020-09-29 00:04:19 +00:00
{
2020-10-05 06:43:44 +00:00
if ( ! wakeAllowed & & on ) {
2020-10-01 16:11:54 +00:00
DEBUG_MSG ( " Inhibiting because !wakeAllowed \n " ) ;
on = false ;
}
if ( isAwake ! = on ) {
2020-09-29 00:04:19 +00:00
DEBUG_MSG ( " WANT GPS=%d \n " , on ) ;
2020-10-05 06:43:44 +00:00
if ( on ) {
lastWakeStartMsec = millis ( ) ;
2020-09-29 00:04:19 +00:00
wake ( ) ;
2020-10-05 06:43:44 +00:00
} else {
lastSleepStartMsec = millis ( ) ;
2020-09-29 00:04:19 +00:00
sleep ( ) ;
2020-10-05 06:43:44 +00:00
}
2020-10-01 16:11:54 +00:00
isAwake = on ;
}
}
2020-10-05 21:34:56 +00:00
GpsOperation GPS : : getGpsOp ( ) const
{
auto op = radioConfig . preferences . gps_operation ;
if ( op = = GpsOperation_GpsOpUnset )
op = ( radioConfig . preferences . location_share = = LocationSharing_LocDisabled ) ? GpsOperation_GpsOpTimeOnly
: GpsOperation_GpsOpMobile ;
return op ;
}
2020-10-05 06:43:44 +00:00
/** Get how long we should stay looking for each aquisition in msecs
*/
uint32_t GPS : : getWakeTime ( ) const
{
uint32_t t = radioConfig . preferences . gps_attempt_time ;
2020-10-05 21:34:56 +00:00
if ( t = = UINT32_MAX )
return t ; // already maxint
2020-10-05 06:43:44 +00:00
if ( t = = 0 )
2020-12-21 03:38:03 +00:00
t = radioConfig . preferences . is_router ? 5 * 60 : 15 * 60 ; // Allow up to 15 mins for each attempt (probably will be much less if we can find sats) or less if a router
2020-10-05 06:43:44 +00:00
t * = 1000 ; // msecs
return t ;
}
/** Get how long we should sleep between aqusition attempts in msecs
*/
uint32_t GPS : : getSleepTime ( ) const
{
uint32_t t = radioConfig . preferences . gps_update_interval ;
2020-10-05 22:27:46 +00:00
auto op = getGpsOp ( ) ;
2020-10-07 23:46:20 +00:00
bool gotTime = ( getRTCQuality ( ) > = RTCQualityGPS ) ;
if ( ( gotTime & & op = = GpsOperation_GpsOpTimeOnly ) | | ( op = = GpsOperation_GpsOpDisabled ) )
2020-10-05 22:27:46 +00:00
t = UINT32_MAX ; // Sleep forever now
2020-10-05 21:34:56 +00:00
if ( t = = UINT32_MAX )
return t ; // already maxint
2020-12-21 03:38:03 +00:00
if ( t = = 0 ) // default - unset in preferences
t = radioConfig . preferences . is_router ? 24 * 60 * 60 : 2 * 60 ; // 2 mins or once per day for routers
2020-10-05 06:43:44 +00:00
t * = 1000 ;
return t ;
}
void GPS : : publishUpdate ( )
{
2020-10-10 01:20:38 +00:00
if ( shouldPublish ) {
shouldPublish = false ;
DEBUG_MSG ( " publishing GPS lock=%d \n " , hasLock ( ) ) ;
2020-10-05 06:43:44 +00:00
2020-10-10 01:20:38 +00:00
// Notify any status instances that are observing us
const meshtastic : : GPSStatus status =
meshtastic : : GPSStatus ( hasLock ( ) , isConnected ( ) , latitude , longitude , altitude , dop , heading , numSatellites ) ;
newStatus . notifyObservers ( & status ) ;
}
2020-10-05 06:43:44 +00:00
}
2020-10-10 01:57:57 +00:00
int32_t GPS : : runOnce ( )
2020-10-01 16:11:54 +00:00
{
if ( whileIdle ( ) ) {
// if we have received valid NMEA claim we are connected
2020-10-10 01:20:38 +00:00
setConnected ( ) ;
2020-10-01 16:11:54 +00:00
}
// If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis ( ) ;
2020-10-05 21:34:56 +00:00
auto sleepTime = getSleepTime ( ) ;
if ( ! isAwake & & sleepTime ! = UINT32_MAX & & ( now - lastSleepStartMsec ) > sleepTime ) {
2020-10-01 16:11:54 +00:00
// We now want to be awake - so wake up the GPS
setAwake ( true ) ;
}
// While we are awake
if ( isAwake ) {
2020-10-05 06:43:44 +00:00
// DEBUG_MSG("looking for location\n");
2020-10-07 22:23:53 +00:00
if ( ( now - lastWhileActiveMsec ) > 5000 ) {
2020-10-05 22:07:30 +00:00
lastWhileActiveMsec = now ;
whileActive ( ) ;
}
2020-10-01 16:11:54 +00:00
2020-10-05 06:43:44 +00:00
// If we've already set time from the GPS, no need to ask the GPS
2020-10-10 01:20:38 +00:00
bool gotTime = ( getRTCQuality ( ) > = RTCQualityGPS ) ;
if ( ! gotTime & & lookForTime ( ) ) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true ;
shouldPublish = true ;
}
2020-10-05 06:43:44 +00:00
bool gotLoc = lookForLocation ( ) ;
2020-10-10 01:20:38 +00:00
if ( gotLoc & & ! hasValidLocation ) { // declare that we have location ASAP
hasValidLocation = true ;
shouldPublish = true ;
}
2020-10-01 16:11:54 +00:00
2020-10-05 06:43:44 +00:00
// We've been awake too long - force sleep
2020-10-05 21:34:56 +00:00
auto wakeTime = getWakeTime ( ) ;
bool tooLong = wakeTime ! = UINT32_MAX & & ( now - lastWakeStartMsec ) > wakeTime ;
2020-10-01 16:11:54 +00:00
// Once we get a location we no longer desperately want an update
2020-10-05 21:34:56 +00:00
// or if we got a time and we are in GpsOpTimeOnly mode
2020-10-07 03:44:30 +00:00
// DEBUG_MSG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
2020-10-07 23:46:20 +00:00
if ( ( gotLoc & & gotTime ) | | tooLong | | ( gotTime & & getGpsOp ( ) = = GpsOperation_GpsOpTimeOnly ) ) {
2020-10-01 16:11:54 +00:00
2020-10-05 06:43:44 +00:00
if ( tooLong ) {
// we didn't get a location during this ack window, therefore declare loss of lock
hasValidLocation = false ;
}
2020-10-01 16:11:54 +00:00
2020-10-05 06:43:44 +00:00
setAwake ( false ) ;
2020-10-10 01:20:38 +00:00
shouldPublish = true ; // publish our update for this just finished acquisition window
2020-10-05 06:43:44 +00:00
}
2020-10-01 16:11:54 +00:00
}
2020-10-10 01:20:38 +00:00
// If state has changed do a publish
publishUpdate ( ) ;
2020-10-10 01:57:57 +00:00
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
return isAwake ? 100 : 5000 ;
2020-10-01 16:11:54 +00:00
}
void GPS : : forceWake ( bool on )
{
if ( on ) {
2020-10-05 22:27:46 +00:00
DEBUG_MSG ( " Allowing GPS lock \n " ) ;
2020-10-05 21:34:56 +00:00
// lastSleepStartMsec = 0; // Force an update ASAP
2020-10-01 16:11:54 +00:00
wakeAllowed = true ;
} else {
wakeAllowed = false ;
2020-10-07 22:23:53 +00:00
// Note: if the gps was already awake, we DO NOT shut it down, because we want to allow it to complete its lock
// attempt even if we are in light sleep. Once the attempt succeeds (or times out) we'll then shut it down.
// setAwake(false);
2020-10-01 16:11:54 +00:00
}
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS : : prepareSleep ( void * unused )
{
2020-10-30 09:05:32 +00:00
DEBUG_MSG ( " GPS prepare sleep! \n " ) ;
2020-10-01 16:11:54 +00:00
forceWake ( false ) ;
return 0 ;
}
2020-10-30 09:05:32 +00:00
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS : : prepareDeepSleep ( void * unused )
{
DEBUG_MSG ( " GPS deep sleep! \n " ) ;
// For deep sleep we also want abandon any lock attempts (because we want minimum power)
setAwake ( false ) ;
return 0 ;
}