From 7ca150bf07795e3d4fe6830c669b8e9710ca73f8 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 26 Sep 2020 06:50:54 -0700 Subject: [PATCH 01/20] personal notes about threading --- docs/software/TODO.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 4a63cf0407..514fda9cf8 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -2,6 +2,16 @@ You probably don't care about this section - skip to the next one. +Threading tasks: + +- Use https://github.com/ivanseidel/ArduinoThread? rather than full coroutines +- clean up main loop() +- check that we are mostly asleep, show which thread is causing us to wake +- +- use tickless idle on nrf52, and sleep X msec or until an interrupt occurs or the cooperative scheduling changes. https://devzone.nordicsemi.com/f/nordic-q-a/12363/nrf52-freertos-power-consumption-tickless-idle +- BAD IDEA: use vTaskDelay and https://www.freertos.org/xTaskAbortDelay.html if scheduling changes. (define INCLUDE_xTaskAbortDelay on ESP32 and NRF52 - seems impossible to find?) +- GOOD IDEA: use xSemaphoreTake to take a semaphore using a timeout. Expect semaphore to not be set, but set it to indicate scheduling has changed. + Nimble tasks: - readerror.txt stress test bug From 3a638090a22df6513f9716b4a01127b867517ee5 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 30 Sep 2020 07:47:16 -0700 Subject: [PATCH 02/20] update protos for #376 --- proto | 2 +- src/mesh/mesh.pb.c | 4 +++- src/mesh/mesh.pb.h | 44 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/proto b/proto index 5cdd7bff56..d8338eba86 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 5cdd7bff56b0ea54351e5ea0e358e864b061078f +Subproject commit d8338eba8666e3f9f9fe0cb6ee8c2cc47dbcade5 diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index 11ec3f49f5..d799c33d2c 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -27,7 +27,7 @@ PB_BIND(MeshPacket, MeshPacket, 2) PB_BIND(ChannelSettings, ChannelSettings, AUTO) -PB_BIND(RadioConfig, RadioConfig, AUTO) +PB_BIND(RadioConfig, RadioConfig, 2) PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2) @@ -60,3 +60,5 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO) + + diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index b726ab6e1c..4128f736f3 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -37,6 +37,20 @@ typedef enum _RegionCode { RegionCode_TW = 8 } RegionCode; +typedef enum _GpsOperation { + GpsOperation_GpsOpUnset = 0, + GpsOperation_GpsOpStationary = 1, + GpsOperation_GpsOpMobile = 2, + GpsOperation_GpsOpTimeOnly = 3, + GpsOperation_GpsOpDisabled = 4 +} GpsOperation; + +typedef enum _LocationSharing { + LocationSharing_LocUnset = 0, + LocationSharing_LocEnabled = 1, + LocationSharing_LocDisabled = 2 +} LocationSharing; + typedef enum _Data_Type { Data_Type_OPAQUE = 0, Data_Type_CLEAR_TEXT = 1, @@ -121,6 +135,10 @@ typedef struct _RadioConfig_UserPreferences { char wifi_password[64]; bool wifi_ap_mode; RegionCode region; + LocationSharing location_share; + GpsOperation gps_operation; + uint32_t gps_update_rate; + uint32_t gps_attempt_time; bool factory_reset; pb_size_t ignore_incoming_count; uint32_t ignore_incoming[3]; @@ -248,6 +266,14 @@ typedef struct _ToRadio { #define _RegionCode_MAX RegionCode_TW #define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1)) +#define _GpsOperation_MIN GpsOperation_GpsOpUnset +#define _GpsOperation_MAX GpsOperation_GpsOpDisabled +#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1)) + +#define _LocationSharing_MIN LocationSharing_LocUnset +#define _LocationSharing_MAX LocationSharing_LocDisabled +#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) + #define _Data_Type_MIN Data_Type_OPAQUE #define _Data_Type_MAX Data_Type_CLEAR_READACK #define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1)) @@ -266,7 +292,7 @@ typedef struct _ToRadio { #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}} +#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} #define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} @@ -282,7 +308,7 @@ typedef struct _ToRadio { #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}} +#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} #define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} @@ -341,6 +367,10 @@ typedef struct _ToRadio { #define RadioConfig_UserPreferences_wifi_password_tag 13 #define RadioConfig_UserPreferences_wifi_ap_mode_tag 14 #define RadioConfig_UserPreferences_region_tag 15 +#define RadioConfig_UserPreferences_location_share_tag 32 +#define RadioConfig_UserPreferences_gps_operation_tag 33 +#define RadioConfig_UserPreferences_gps_update_rate_tag 34 +#define RadioConfig_UserPreferences_gps_attempt_time_tag 36 #define RadioConfig_UserPreferences_factory_reset_tag 100 #define RadioConfig_UserPreferences_ignore_incoming_tag 103 #define RouteDiscovery_route_tag 2 @@ -498,6 +528,10 @@ X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \ X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \ X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \ X(a, STATIC, SINGULAR, UENUM, region, 15) \ +X(a, STATIC, SINGULAR, UENUM, location_share, 32) \ +X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \ +X(a, STATIC, SINGULAR, UINT32, gps_update_rate, 34) \ +X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \ X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \ X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) #define RadioConfig_UserPreferences_CALLBACK NULL @@ -635,11 +669,11 @@ extern const pb_msgdesc_t ManufacturingData_msg; #define SubPacket_size 274 #define MeshPacket_size 313 #define ChannelSettings_size 84 -#define RadioConfig_size 282 -#define RadioConfig_UserPreferences_size 193 +#define RadioConfig_size 302 +#define RadioConfig_UserPreferences_size 213 #define NodeInfo_size 132 #define MyNodeInfo_size 110 -#define DeviceState_size 5434 +#define DeviceState_size 5454 #define DebugString_size 258 #define FromRadio_size 322 #define ToRadio_size 316 From bacc6caf04ec9c7eba88533f3001ed16fbf699ae Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 1 Oct 2020 09:11:54 -0700 Subject: [PATCH 03/20] wip gps power fixes #376 --- .vscode/settings.json | 1 + src/PowerFSM.cpp | 2 +- src/gps/GPS.cpp | 89 +++++++++++++++++++++++-- src/gps/GPS.h | 72 +++++++++++++++----- src/gps/NMEAGPS.cpp | 150 +++++++++++++++++++++--------------------- src/gps/NMEAGPS.h | 25 ++++++- src/gps/UBloxGPS.cpp | 133 +++++++++++++++++-------------------- src/gps/UBloxGPS.h | 43 ++++++++---- 8 files changed, 329 insertions(+), 186 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cd2a5ca5c2..45052fa77d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -57,6 +57,7 @@ "HFSR", "Meshtastic", "NEMAGPS", + "NMEAGPS", "RDEF", "Ublox", "bkpt", diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 14f7b996b0..030e9a8a3b 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -91,7 +91,7 @@ static void lsIdle() static void lsExit() { // setGPSPower(true); // restore GPS power - gps->startLock(); + gps->forceWake(true); } static void nbEnter() diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f8b29de3dc..125c01a072 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1,6 +1,7 @@ #include "GPS.h" #include "configuration.h" +#include "sleep.h" #include #include @@ -86,19 +87,99 @@ uint32_t getValidTime() return timeSetFromGPS ? getTime() : 0; } +bool GPS::setup() +{ + notifySleepObserver.observe(¬ifySleep); + + return true; +} + /** * 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) +void GPS::setAwake(bool on) { - if (wantNewLocation != on) { - wantNewLocation = on; + if(!wakeAllowed && on) { + DEBUG_MSG("Inhibiting because !wakeAllowed\n"); + on = false; + } + + if (isAwake != on) { DEBUG_MSG("WANT GPS=%d\n", on); if (on) wake(); else sleep(); + + isAwake = on; + } +} + +void GPS::loop() +{ + 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(); + bool mustPublishUpdate = false; + + if ((now - lastUpdateMsec) > 30 * 1000 && !isAwake) { + // We now want to be awake - so wake up the GPS + setAwake(true); + + mustPublishUpdate = + true; // Even if we don't have an update this time, we at least want to occasionally publish the current state + } + + // While we are awake + if (isAwake) { + DEBUG_MSG("looking for location\n"); + bool gotTime = lookForTime(); + bool gotLoc = lookForLocation(); + + if (gotLoc) + hasValidLocation = true; + + mustPublishUpdate |= gotLoc; + + // Once we get a location we no longer desperately want an update + if (gotLoc) { + lastUpdateMsec = now; + setAwake(false); } -} \ No newline at end of file + } + + if (mustPublishUpdate) { + DEBUG_MSG("publishing GPS lock=%d\n", hasLock()); + + // 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); + } +} + +void GPS::forceWake(bool on) +{ + if (on) { + DEBUG_MSG("Looking for GPS lock\n"); + lastUpdateMsec = 0; // Force an update ASAP + wakeAllowed = true; + } else { + wakeAllowed = false; + setAwake(false); + } +} + +/// 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) +{ + forceWake(false); + + return 0; +} diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 3b81b54f7e..d3c6c05df3 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -27,11 +27,18 @@ void readFromRTC(); */ class GPS { - protected: + private: + uint32_t lastUpdateMsec = 0; + bool hasValidLocation = false; // default to false, until we complete our first read - bool wantNewLocation = false; // true if we want a location right now + bool isAwake = false; // true if we want a location right now + + bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is + CallbackObserver notifySleepObserver = CallbackObserver(this, &GPS::prepareSleep); + + protected: public: /** If !NULL we will use this serial port to construct our GPS */ static HardwareSerial *_serial_gps; @@ -48,7 +55,7 @@ class GPS bool isConnected = false; // Do we have a GPS we are talking to - virtual ~GPS() {} + virtual ~GPS() {} // FIXME, we really should unregister our sleep observer /** We will notify this observable anytime GPS state has changed meaningfully */ Observable newStatus; @@ -56,32 +63,61 @@ class GPS /** * Returns true if we succeeded */ - virtual bool setup() { return true; } + virtual bool setup(); - /// A loop callback for subclasses that need it. FIXME, instead just block on serial reads - virtual void loop() {} + virtual void loop(); /// 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 + * Restart our lock attempt - try to get and broadcast a GPS reading ASAP + * called after the CPU wakes from light-sleep state + * + * Or set to false, to disallow any sort of waking + * */ + void forceWake(bool on); + + protected: + /// If possible force the GPS into sleep/low power mode + virtual void sleep() {} + + /// wake the GPS into normal operation mode + virtual void wake() {} + + /** 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 */ - void setWantLocation(bool on); + virtual bool whileIdle() = 0; /** - * 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() {} + * 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() = 0; + + /** + * 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() = 0; -protected: - /// If possible force the GPS into sleep/low power mode - virtual void sleep() {} + private: + /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs + /// always returns 0 to indicate okay to sleep + int prepareSleep(void *unused); - /// wake the GPS into normal operation mode - virtual void wake() {} + /** + * 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 setAwake(bool on); }; extern GPS *gps; diff --git a/src/gps/NMEAGPS.cpp b/src/gps/NMEAGPS.cpp index 2d8af6d72a..ef17ee050a 100644 --- a/src/gps/NMEAGPS.cpp +++ b/src/gps/NMEAGPS.cpp @@ -1,7 +1,6 @@ #include "NMEAGPS.h" #include "configuration.h" - static int32_t toDegInt(RawDegrees d) { int32_t degMult = 10000000; // 1e7 @@ -18,93 +17,94 @@ bool NMEAGPS::setup() // FIXME - move into shared GPS code pinMode(PIN_GPS_PPS, INPUT); #endif + GPS::setup(); return true; } -void NMEAGPS::loop() +/** + * 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 + */ +bool NMEAGPS::lookForTime() { - // 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); - bool isValid = reader.encode(c); + auto ti = reader.time; + auto d = reader.date; + if (ti.isUpdated() && ti.isValid() && d.isValid()) { + /* 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; + perhapsSetRTC(t); - // if we have received valid NMEA claim we are connected - if (isValid) - isConnected = true; - } + return true; + } else + return false; +} - // If we are overdue for an update, turn on the GPS and at least publish the current status - uint32_t now = millis(); - 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 - } +/** + * 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 + */ +bool NMEAGPS::lookForLocation() +{ + bool foundLocation = false; - // 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()) { - /* 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; - perhapsSetRTC(t); - } + // uint8_t fixtype = reader.fixQuality(); + // hasValidLocation = ((fixtype >= 1) && (fixtype <= 5)); - uint8_t fixtype = reader.fixQuality(); - hasValidLocation = ((fixtype >= 1) && (fixtype <= 5)); + if (reader.location.isUpdated()) { + if (reader.altitude.isValid()) + altitude = reader.altitude.meters(); - if (reader.location.isUpdated()) { - lastUpdateMsec = now; + if (reader.location.isValid()) { + auto loc = reader.location.value(); + latitude = toDegInt(loc.lat); + longitude = toDegInt(loc.lng); + foundLocation = true; + } - if (reader.altitude.isValid()) - altitude = reader.altitude.meters(); + // 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()) { + dop = reader.hdop.value(); + } + if (reader.course.isValid()) { + heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 + } + if (reader.satellites.isValid()) { + numSatellites = reader.satellites.value(); + } - if (reader.location.isValid()) { - auto loc = reader.location.value(); - latitude = toDegInt(loc.lat); - longitude = toDegInt(loc.lng); + // 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); + } - // 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()) { - dop = reader.hdop.value(); - } - if (reader.course.isValid()) { - heading = - reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 - } - if (reader.satellites.isValid()) { - numSatellites = reader.satellites.value(); - } + return foundLocation; +} - // 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; - } +bool NMEAGPS::whileIdle() +{ + bool isValid = false; - 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); - } + // 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); + isValid |= reader.encode(c); } -} \ No newline at end of file + + return isValid; +} diff --git a/src/gps/NMEAGPS.h b/src/gps/NMEAGPS.h index b46aa0c65e..4847282381 100644 --- a/src/gps/NMEAGPS.h +++ b/src/gps/NMEAGPS.h @@ -14,10 +14,29 @@ class NMEAGPS : public GPS { TinyGPSPlus reader; - uint32_t lastUpdateMsec = 0; - public: virtual bool setup(); - virtual void loop(); + protected: + /** 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 + */ + virtual bool whileIdle(); + + /** + * 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(); }; diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 0d133891e2..de2116d3a3 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -1,11 +1,9 @@ #include "UBloxGPS.h" #include "error.h" -#include "sleep.h" #include -UBloxGPS::UBloxGPS() : concurrency::PeriodicTask() +UBloxGPS::UBloxGPS() { - notifySleepObserver.observe(¬ifySleep); } bool UBloxGPS::tryConnect() @@ -53,13 +51,15 @@ bool UBloxGPS::setup() if (isConnected) { DEBUG_MSG("Connected to UBLOX GPS successfully\n"); + GPS::setup(); + if (!setUBXMode()) recordCriticalError(UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug - concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device - return true; } else { + // Note: we do not call superclass setup in this case, because we dont want sleep observer registered + return false; } } @@ -120,53 +120,53 @@ bool UBloxGPS::factoryReset() return ok; } -/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs -int UBloxGPS::prepareSleep(void *unused) +/** + * 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 + */ +bool UBloxGPS::lookForTime() { - if (isConnected) - ublox.powerOff(); - - return 0; + if (ublox.getT(maxWait())) { + /* 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 = ublox.getSecond(0); + t.tm_min = ublox.getMinute(0); + t.tm_hour = ublox.getHour(0); + t.tm_mday = ublox.getDay(0); + t.tm_mon = ublox.getMonth(0) - 1; + t.tm_year = ublox.getYear(0) - 1900; + t.tm_isdst = false; + perhapsSetRTC(t); + return true; + } + else + { + return false; + } } -void UBloxGPS::doTask() +/** + * 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 + */ +bool UBloxGPS::lookForLocation() { - if (isConnected) { - // Consume all characters that have arrived - - uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix - - // if using i2c or serial look too see if any chars are ready - ublox.checkUblox(); // See if new data is available. Process bytes as they come in. - - // If we don't have a fix (a quick check), don't try waiting for a solution) - // Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions - // turn off for now - uint16_t maxWait = i2cAddress ? 300 : 0; // If using i2c we must poll with wait - fixtype = ublox.getFixType(maxWait); - DEBUG_MSG("GPS fix type %d\n", fixtype); - - // DEBUG_MSG("sec %d\n", ublox.getSecond()); - // DEBUG_MSG("lat %d\n", ublox.getLatitude()); - - // any fix that has time - - if (ublox.getT(maxWait)) { - /* 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 = ublox.getSecond(0); - t.tm_min = ublox.getMinute(0); - t.tm_hour = ublox.getHour(0); - t.tm_mday = ublox.getDay(0); - t.tm_mon = ublox.getMonth(0) - 1; - t.tm_year = ublox.getYear(0) - 1900; - t.tm_isdst = false; - perhapsSetRTC(t); - } + bool foundLocation = false; + // If we don't have a fix (a quick check), don't try waiting for a solution) + uint8_t fixtype = ublox.getFixType(maxWait()); + DEBUG_MSG("GPS fix type %d\n", fixtype); + + // we only notify if position has changed due to a new fix + if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait())) // rd fixes only + { latitude = ublox.getLatitude(0); longitude = ublox.getLongitude(0); altitude = ublox.getAltitudeMSL(0) / 1000; // in mm convert to meters @@ -176,33 +176,24 @@ void UBloxGPS::doTask() // bogus lat lon is reported as 0 or 0 (can be bogus just for one) // Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg! - hasValidLocation = + foundLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0); + } - // we only notify if position has changed due to a new fix - if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only - { - if (hasValidLocation) { - setWantLocation(false); - // ublox.powerOff(); - } - } else // we didn't get a location update, go back to sleep and hope the characters show up - setWantLocation(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); - } - - // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 10s until we have something - // over the serial - setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000); + return foundLocation; } -void UBloxGPS::startLock() +bool UBloxGPS::whileIdle() { - DEBUG_MSG("Looking for GPS lock\n"); - wantNewLocation = true; - setPeriod(1); + // if using i2c or serial look too see if any chars are ready + return ublox.checkUblox(); // See if new data is available. Process bytes as they come in. } + + +/// If possible force the GPS into sleep/low power mode +/// Note: ublox doesn't need a wake method, because as soon as we send chars to the GPS it will wake up +void UBloxGPS::sleep() { + if (isConnected) + ublox.powerOff(); +} + diff --git a/src/gps/UBloxGPS.h b/src/gps/UBloxGPS.h index 03f2d2a1c0..22da3332a6 100644 --- a/src/gps/UBloxGPS.h +++ b/src/gps/UBloxGPS.h @@ -1,6 +1,5 @@ #pragma once -#include "../concurrency/PeriodicTask.h" #include "GPS.h" #include "Observer.h" #include "SparkFun_Ublox_Arduino_Library.h" @@ -10,12 +9,10 @@ * * When new data is available it will notify observers. */ -class UBloxGPS : public GPS, public concurrency::PeriodicTask +class UBloxGPS : public GPS { SFE_UBLOX_GPS ublox; - CallbackObserver notifySleepObserver = CallbackObserver(this, &UBloxGPS::prepareSleep); - public: UBloxGPS(); @@ -24,13 +21,6 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask */ virtual bool setup(); - virtual void doTask(); - - /** - * 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(); - /** * Reset our GPS back to factory settings * @@ -38,10 +28,33 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask */ bool factoryReset(); + protected: + /** 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 + */ + virtual bool whileIdle(); + + /** + * 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(); + + /// If possible force the GPS into sleep/low power mode + virtual void sleep(); + private: - /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs - /// always returns 0 to indicate okay to sleep - int prepareSleep(void *unused); /// Attempt to connect to our GPS, returns false if no gps is present bool tryConnect(); @@ -49,4 +62,6 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask /// Switch to our desired operating mode and save the settings to flash /// returns true for success bool setUBXMode(); + + uint16_t maxWait() const { return i2cAddress ? 300 : 0; /*If using i2c we must poll with wait */ } }; From 19078738316d7d9285c51f682586f5af382894f5 Mon Sep 17 00:00:00 2001 From: geeksville Date: Thu, 1 Oct 2020 10:03:23 -0700 Subject: [PATCH 04/20] gps wip for #376 --- docs/software/gps-todo.txt | 36 ++++++++++++++++++++++++++++++++++++ platformio.ini | 6 ++++-- proto | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 docs/software/gps-todo.txt diff --git a/docs/software/gps-todo.txt b/docs/software/gps-todo.txt new file mode 100644 index 0000000000..85f233733f --- /dev/null +++ b/docs/software/gps-todo.txt @@ -0,0 +1,36 @@ +gps todo - bug 376 + +handle the various GPS modes + +handle wake and sleep times + +handle maxint parameters + +handle loss of lock at end of wake (gotLocThisTime) + +if not sharing location, don't even power up the GPS + +if we go to sleep without getting a location set hasValidLocation to false + +properly handle time only modes + +force gps sleep when in LightSleep and force wake only once <- confirm + +fix has_gps based on new logic + +add set router mode in python tool - it will also set GPS to stationary +(which will shrink DARK and NB period to zero and + make light_sleep very long) + +gps states + +Active - for gps_attempt_time seconds +Sleeping - for (gps_update_rate or sleep forever) seconds +ForcedSleep - PowerFSM says we don't want to use GPS right now +(no need for sleep forever state) + +gps triggers +GPS_TRIG_FORCE_SLEEP - from powerfsm +GPS_TRIG_FORCE_WAKE - from powerfsm +GPS_SETTINGS - if GPS settings changed, reset params and possibly become active + diff --git a/platformio.ini b/platformio.ini index 8dfc9dc5f7..74ed94ab40 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,8 +34,10 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n ; leave this commented out to avoid breaking Windows ;upload_port = /dev/ttyUSB0 ;monitor_port = /dev/ttyUSB0 -upload_port = /dev/cu.SLAB_USBtoUART -monitor_port = /dev/cu.SLAB_USBtoUART + +; geeksville: I think setting this should not be required - it breaks linux +;upload_port = /dev/cu.SLAB_USBtoUART +;monitor_port = /dev/cu.SLAB_USBtoUART ; the default is esptool ; upload_protocol = esp-prog diff --git a/proto b/proto index d8338eba86..c250aaa459 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit d8338eba8666e3f9f9fe0cb6ee8c2cc47dbcade5 +Subproject commit c250aaa459d7d6727b01b8f4d9f31d315f738d61 From ff9b49ddaa14bf638f476422285148d54e6995fa Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 5 Oct 2020 11:03:30 +0800 Subject: [PATCH 05/20] add lora32 schematic Signed-off-by: Kevin Hester --- docs/hardware/WIFI_LoRa_32_V2(868-915).PDF | Bin 0 -> 136026 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/hardware/WIFI_LoRa_32_V2(868-915).PDF diff --git a/docs/hardware/WIFI_LoRa_32_V2(868-915).PDF b/docs/hardware/WIFI_LoRa_32_V2(868-915).PDF new file mode 100644 index 0000000000000000000000000000000000000000..11d499e7d8e888268eb1742bfb1ef0a8e00c1dc8 GIT binary patch literal 136026 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