From fec7a6bf173ce6eb4d631a766d10a35438ec3309 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 28 Sep 2020 17:04:19 -0700 Subject: [PATCH] add air530 gps sleep support --- src/gps/Air530GPS.cpp | 87 ++++++++++++++++++++++++++++++++++++ src/gps/Air530GPS.h | 22 +++++++++ src/gps/GPS.cpp | 17 +++++++ src/gps/GPS.h | 16 +++++++ src/gps/NMEAGPS.cpp | 74 ++++++++++-------------------- src/gps/UBloxGPS.cpp | 4 +- src/gps/UBloxGPS.h | 2 - src/graphics/EInkDisplay.cpp | 2 +- src/main.cpp | 8 +++- variants/eink/variant.h | 2 + 10 files changed, 176 insertions(+), 58 deletions(-) create mode 100644 src/gps/Air530GPS.cpp create mode 100644 src/gps/Air530GPS.h diff --git a/src/gps/Air530GPS.cpp b/src/gps/Air530GPS.cpp new file mode 100644 index 000000000..6223c1fc7 --- /dev/null +++ b/src/gps/Air530GPS.cpp @@ -0,0 +1,87 @@ +#include "Air530GPS.h" +#include + +/* +Helpful translations from the Air530 GPS datasheet + +Sat acquision mode +捕获电流值@3.3v 42.6 mA + +sat tracking mode +跟踪电流值@3.3v 36.7 mA + +Low power mode +低功耗模式@3.3V 0.85 mA +(发送指令:$PGKC051,0) + +Super low power mode +超低功耗模式@3.3V 31 uA +(发送指令:$PGKC105,4) + +To exit sleep use WAKE pin + +Commands to enter sleep +6、Command: 105 +进入周期性低功耗模式 +Arguments: + +Arg1: “0”,正常运行模式 (normal mode) +“1”,周期超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (periodic low power tracking mode - keeps sat positions, use wake to wake up) +“2”,周期低功耗模式 (periodic low power mode) +“4”,直接进入超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (super low power consumption mode immediately, need WAKE to resume) +“8”,自动低功耗模式,可以通过串口唤醒 (automatic low power mode, wake by sending characters to serial port) +“9”, 自动超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (automatic low power tracking when possible, need wake pin to resume) + +(Arg 2 & 3 only valid if Arg1 is "1" or "2") +Arg2:运行时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用 +ON time in msecs + +Arg3:睡眠时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用 +Sleep time in msecs + +Example: +$PGKC105,8*3F +This will set automatic low power mode with waking when we send chars to the serial port. Possibly do this as soon as we get a +new location. When we wake again in a minute we send a character to wake up. + +*/ + + +void Air530GPS::sendCommand(const char *cmd) { + uint8_t sum = 0; + + // Skip the $ + assert(cmd[0] == '$'); + const char *p = cmd + 1; + while(*p) + sum ^= *p++; + + assert(_serial_gps); + + _serial_gps->write(cmd); + _serial_gps->printf("*%02x\r\n", sum); + + // DEBUG_MSG("xsum %02x\n", sum); +} + +void Air530GPS::sleep() { +#ifdef PIN_GPS_WAKE + digitalWrite(PIN_GPS_WAKE, 0); + pinMode(PIN_GPS_WAKE, OUTPUT); + sendCommand("$PGKC105,4"); +#endif +} + +/// wake the GPS into normal operation mode +void Air530GPS::wake() +{ +#if 0 +#ifdef PIN_GPS_WAKE + digitalWrite(PIN_GPS_WAKE, 1); + pinMode(PIN_GPS_WAKE, OUTPUT); +#endif +#else + // For power testing + sleep(); +#endif +} \ No newline at end of file diff --git a/src/gps/Air530GPS.h b/src/gps/Air530GPS.h new file mode 100644 index 000000000..bbb9f8003 --- /dev/null +++ b/src/gps/Air530GPS.h @@ -0,0 +1,22 @@ +#pragma once + +#include "NMEAGPS.h" + +/** + * A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading) + * + * When new data is available it will notify observers. + */ +class Air530GPS : public NMEAGPS +{ + protected: + /// If possible force the GPS into sleep/low power mode + virtual void sleep(); + + /// wake the GPS into normal operation mode + virtual void wake(); + + private: + /// Send a NMEA cmd with checksum + void sendCommand(const char *str); +}; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index b3ab28d29..f8b29de3d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -85,3 +85,20 @@ uint32_t getValidTime() { return timeSetFromGPS ? getTime() : 0; } + +/** + * 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 + */ +void GPS::setWantLocation(bool on) +{ + if (wantNewLocation != on) { + wantNewLocation = on; + DEBUG_MSG("WANT GPS=%d\n", on); + if (on) + wake(); + else + sleep(); + } +} \ No newline at end of file diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 409c6da9d..3b81b54f7 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -30,6 +30,8 @@ class GPS protected: bool hasValidLocation = false; // default to false, until we complete our first read + bool wantNewLocation = false; // true if we want a location right now + public: /** If !NULL we will use this serial port to construct our GPS */ static HardwareSerial *_serial_gps; @@ -62,10 +64,24 @@ class GPS /// Returns ture if we have acquired GPS lock. bool hasLock() const { return hasValidLocation; } + /** + * 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 + */ + void setWantLocation(bool on); + /** * 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() {} + +protected: + /// If possible force the GPS into sleep/low power mode + virtual void sleep() {} + + /// wake the GPS into normal operation mode + virtual void wake() {} }; extern GPS *gps; diff --git a/src/gps/NMEAGPS.cpp b/src/gps/NMEAGPS.cpp index 08918d6d8..2d8af6d72 100644 --- a/src/gps/NMEAGPS.cpp +++ b/src/gps/NMEAGPS.cpp @@ -1,50 +1,6 @@ #include "NMEAGPS.h" #include "configuration.h" -/* -Helpful translations from the Air530 GPS datasheet - -Sat acquision mode -捕获电流值@3.3v 42.6 mA - -sat tracking mode -跟踪电流值@3.3v 36.7 mA - -Low power mode -低功耗模式@3.3V 0.85 mA -(发送指令:$PGKC051,0) - -Super low power mode -超低功耗模式@3.3V 31 uA -(发送指令:$PGKC105,4) - -To exit sleep use WAKE pin - -Commands to enter sleep -6、Command: 105 -进入周期性低功耗模式 -Arguments: - -Arg1: “0”,正常运行模式 (normal mode) -“1”,周期超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (periodic low power tracking mode - keeps sat positions, use wake to wake up) -“2”,周期低功耗模式 (periodic low power mode) -“4”,直接进入超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (super low power consumption mode immediately, need WAKE to resume) -“8”,自动低功耗模式,可以通过串口唤醒 (automatic low power mode, wake by sending characters to serial port) -“9”, 自动超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (automatic low power tracking when possible, need wake pin to resume) - -(Arg 2 & 3 only valid if Arg1 is "1" or "2") -Arg2:运行时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用 -ON time in msecs - -Arg3:睡眠时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用 -Sleep time in msecs - -Example: -$PGKC105,8*3F -This will set automatic low power mode with waking when we send chars to the serial port. Possibly do this as soon as we get a new -location. When we wake again in a minute we send a character to wake up. - -*/ static int32_t toDegInt(RawDegrees d) { @@ -68,6 +24,7 @@ bool NMEAGPS::setup() void NMEAGPS::loop() { + // First consume any chars that have piled up at the receiver while (_serial_gps->available() > 0) { int c = _serial_gps->read(); // DEBUG_MSG("%c", c); @@ -78,11 +35,18 @@ void NMEAGPS::loop() 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 - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume - // serial chars at whatever rate) - lastUpdateMsec = now; + bool mustPublishUpdate = false; + if ((now - lastUpdateMsec) > 30 * 1000 && !wantNewLocation) { + // Ugly hack for now - limit update checks to once every 30 secs + setWantLocation(true); + mustPublishUpdate = + true; // Even if we don't have an update this time, we at least want to occasionally publish the current state + } + // Only bother looking at GPS state if we are interested in what it has to say + if (wantNewLocation) { auto ti = reader.time; auto d = reader.date; if (ti.isUpdated() && ti.isValid() && d.isValid()) { @@ -105,6 +69,8 @@ void NMEAGPS::loop() hasValidLocation = ((fixtype >= 1) && (fixtype <= 5)); if (reader.location.isUpdated()) { + lastUpdateMsec = now; + if (reader.altitude.isValid()) altitude = reader.altitude.meters(); @@ -112,6 +78,9 @@ void NMEAGPS::loop() auto loc = reader.location.value(); latitude = toDegInt(loc.lat); longitude = toDegInt(loc.lng); + + // Once we get a location we no longer desperately want an update + setWantLocation(false); } // Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it if (reader.hdop.isValid()) { @@ -128,11 +97,14 @@ void NMEAGPS::loop() // expect gps pos lat=37.520825, lon=-122.309162, alt=158 DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5); + mustPublishUpdate = true; } - // 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); + if (mustPublishUpdate) { + // 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); + } } } \ No newline at end of file diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 93e8a01cf..0d133891e 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -183,11 +183,11 @@ void UBloxGPS::doTask() if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only { if (hasValidLocation) { - wantNewLocation = false; + setWantLocation(false); // ublox.powerOff(); } } else // we didn't get a location update, go back to sleep and hope the characters show up - wantNewLocation = true; + setWantLocation(true); // Notify any status instances that are observing us const meshtastic::GPSStatus status = diff --git a/src/gps/UBloxGPS.h b/src/gps/UBloxGPS.h index 7a2d3cdba..03f2d2a1c 100644 --- a/src/gps/UBloxGPS.h +++ b/src/gps/UBloxGPS.h @@ -14,8 +14,6 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask { SFE_UBLOX_GPS ublox; - bool wantNewLocation = true; - CallbackObserver notifySleepObserver = CallbackObserver(this, &UBloxGPS::prepareSleep); public: diff --git a/src/graphics/EInkDisplay.cpp b/src/graphics/EInkDisplay.cpp index 56363c192..7a6e02df0 100644 --- a/src/graphics/EInkDisplay.cpp +++ b/src/graphics/EInkDisplay.cpp @@ -62,7 +62,7 @@ void EInkDisplay::display(void) uint32_t now = millis(); uint32_t sinceLast = now - lastDrawMsec; - if (framePtr && (sinceLast > 30 * 1000 || lastDrawMsec == 0)) { + if (framePtr && (sinceLast > 60 * 1000 || lastDrawMsec == 0)) { lastDrawMsec = now; // FIXME - only draw bits have changed (use backbuf similar to the other displays) diff --git a/src/main.cpp b/src/main.cpp index c53fdfeff..37e9077df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,7 +23,7 @@ #include "MeshRadio.h" #include "MeshService.h" -#include "NMEAGPS.h" +#include "Air530GPS.h" #include "NodeDB.h" #include "PowerFSM.h" #include "UBloxGPS.h" @@ -259,10 +259,14 @@ void setup() if (GPS::_serial_gps) { // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just // assume NMEA at 9600 baud. + // dumb NMEA access only work for serial GPSes) DEBUG_MSG("Hoping that NMEA might work\n"); - // dumb NMEA access only work for serial GPSes) +#ifdef HAS_AIR530_GPS + gps = new Air530GPS(); +#else gps = new NMEAGPS(); +#endif gps->setup(); } } diff --git a/variants/eink/variant.h b/variants/eink/variant.h index c0738746d..a399d51a7 100644 --- a/variants/eink/variant.h +++ b/variants/eink/variant.h @@ -208,6 +208,8 @@ External serial flash WP25R1635FZUIL0 #define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the CPU #define PIN_GPS_RX (32 + 8) // This is for bits going TOWARDS the GPS +#define HAS_AIR530_GPS + #define PIN_SERIAL1_RX PIN_GPS_TX #define PIN_SERIAL1_TX PIN_GPS_RX