From 9b309fe0a035b72c3569c0595daab22cf5e077fe Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 08:09:08 -0700 Subject: [PATCH 1/7] Use int based lat/long from now on in the device code for https://github.com/meshtastic/Meshtastic-device/issues/124 --- docs/software/nrf52-TODO.md | 2 +- proto | 2 +- src/GPS.cpp | 8 ++++---- src/GPS.h | 3 +-- src/mesh/MeshService.cpp | 7 +++---- src/mesh/mesh.pb.c | 8 -------- src/mesh/mesh.pb.h | 28 ++++++++++++++-------------- src/screen.cpp | 11 +++++++---- 8 files changed, 31 insertions(+), 38 deletions(-) diff --git a/docs/software/nrf52-TODO.md b/docs/software/nrf52-TODO.md index 5d9c75775..6bc5fb0d2 100644 --- a/docs/software/nrf52-TODO.md +++ b/docs/software/nrf52-TODO.md @@ -9,7 +9,7 @@ Minimum items needed to make sure hardware is good. - plug in correct variants for the real board - Use the PMU driver on real hardware - add a NEMA based GPS driver to test GPS -- Use new radio driver on real hardware - possibly start with https://os.mbed.com/teams/Semtech/code/SX126xLib/ +- Use new radio driver on real hardware - Use UC1701 LCD driver on real hardware. Still need to create at startup and probe on SPI - test the LEDs - test the buttons diff --git a/proto b/proto index bd002e5a1..cabbdf51e 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit bd002e5a144f209e42c97b64fea9a05a2e513b28 +Subproject commit cabbdf51ed365b72ab995ad24b075269627f58ad diff --git a/src/GPS.cpp b/src/GPS.cpp index a6a1e8eef..f78ad906c 100644 --- a/src/GPS.cpp +++ b/src/GPS.cpp @@ -48,7 +48,7 @@ void GPS::setup() isConnected = ublox.begin(_serial_gps); if (isConnected) { - DEBUG_MSG("Connected to GPS successfully\n"); + DEBUG_MSG("Connected to UBLOX GPS successfully\n"); bool factoryReset = false; bool ok; @@ -191,10 +191,10 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only { // we only notify if position has changed - latitude = ublox.getLatitude() * 1e-7; - longitude = ublox.getLongitude() * 1e-7; + latitude = ublox.getLatitude(); + longitude = ublox.getLongitude(); altitude = ublox.getAltitude() / 1000; // in mm convert to meters - DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude, longitude, altitude); + DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 if (hasValidLocation) { diff --git a/src/GPS.h b/src/GPS.h index 912356c42..c3c403278 100644 --- a/src/GPS.h +++ b/src/GPS.h @@ -15,7 +15,7 @@ class GPS : public PeriodicTask, public Observable SFE_UBLOX_GPS ublox; public: - double latitude, longitude; + uint32_t latitude, longitude; // as an int mult by 1e-7 to get value as double uint32_t altitude; bool isConnected; // Do we have a GPS we are talking to @@ -29,7 +29,6 @@ class GPS : public PeriodicTask, public Observable void setup(); - virtual void doTask(); /// If we haven't yet set our RTC this boot, set it from a GPS derived time diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 71eb5f37c..1b05e0c51 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -269,8 +269,7 @@ void MeshService::sendToMesh(MeshPacket *p) if (p->to == nodeDB.getNodeNum()) { DEBUG_MSG("Dropping locally processed message\n"); releaseToPool(p); - } - else { + } else { // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it if (router.send(p) != ERRNO_OK) { DEBUG_MSG("No radio was able to send packet, discarding...\n"); @@ -333,8 +332,8 @@ int MeshService::onGPSChanged(void *unused) if (gps.latitude != 0 || gps.longitude != 0) { if (gps.altitude != 0) pos.altitude = gps.altitude; - pos.latitude = gps.latitude; - pos.longitude = gps.longitude; + pos.latitude_i = gps.latitude; + pos.longitude_i = gps.longitude; pos.time = gps.getValidTime(); } diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index 096f28896..0b2c5b8ce 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -55,11 +55,3 @@ PB_BIND(ToRadio, ToRadio, 2) -#ifndef PB_CONVERT_DOUBLE_FLOAT -/* On some platforms (such as AVR), double is really float. - * To be able to encode/decode double on these platforms, you need. - * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. - */ -PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) -#endif - diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index bf79305b8..7f25f1c99 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -66,11 +66,11 @@ typedef struct _MyNodeInfo { } MyNodeInfo; typedef struct _Position { - double latitude; - double longitude; int32_t altitude; int32_t battery_level; uint32_t time; + int32_t latitude_i; + int32_t longitude_i; } Position; typedef struct _RadioConfig_UserPreferences { @@ -237,8 +237,8 @@ typedef struct _ToRadio { #define MyNodeInfo_error_code_tag 7 #define MyNodeInfo_error_address_tag 8 #define MyNodeInfo_error_count_tag 9 -#define Position_latitude_tag 1 -#define Position_longitude_tag 2 +#define Position_latitude_i_tag 7 +#define Position_longitude_i_tag 8 #define Position_altitude_tag 3 #define Position_battery_level_tag 4 #define Position_time_tag 6 @@ -297,11 +297,11 @@ typedef struct _ToRadio { /* Struct field encoding specification for nanopb */ #define Position_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, DOUBLE, latitude, 1) \ -X(a, STATIC, SINGULAR, DOUBLE, longitude, 2) \ X(a, STATIC, SINGULAR, INT32, altitude, 3) \ X(a, STATIC, SINGULAR, INT32, battery_level, 4) \ -X(a, STATIC, SINGULAR, UINT32, time, 6) +X(a, STATIC, SINGULAR, UINT32, time, 6) \ +X(a, STATIC, SINGULAR, INT32, latitude_i, 7) \ +X(a, STATIC, SINGULAR, INT32, longitude_i, 8) #define Position_CALLBACK NULL #define Position_DEFAULT NULL @@ -486,21 +486,21 @@ extern const pb_msgdesc_t ToRadio_msg; #define ToRadio_fields &ToRadio_msg /* Maximum encoded size of messages (where known) */ -#define Position_size 46 +#define Position_size 50 #define Data_size 256 #define User_size 72 /* RouteDiscovery_size depends on runtime parameters */ -#define SubPacket_size 383 -#define MeshPacket_size 425 +#define SubPacket_size 387 +#define MeshPacket_size 429 #define ChannelSettings_size 44 #define RadioConfig_size 120 #define RadioConfig_UserPreferences_size 72 -#define NodeInfo_size 138 +#define NodeInfo_size 142 #define MyNodeInfo_size 85 -#define DeviceState_size 18925 +#define DeviceState_size 19185 #define DebugString_size 258 -#define FromRadio_size 434 -#define ToRadio_size 428 +#define FromRadio_size 438 +#define ToRadio_size 432 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/screen.cpp b/src/screen.cpp index a390520c4..0b3f22fb2 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -280,7 +280,7 @@ static float estimatedHeading(double lat, double lon) /// valid lat/lon static bool hasPosition(NodeInfo *n) { - return n->has_position && (n->position.latitude != 0 || n->position.longitude != 0); + return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); } /// We will skip one node - the one for us, so we just blindly loop over all @@ -288,6 +288,9 @@ static bool hasPosition(NodeInfo *n) static size_t nodeIndex; static int8_t prevFrame = -1; +/// Convert an integer GPS coords to a floating point +#define DegD(i) (i * 1e-7) + static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { // We only advance our nodeIndex if the frame # has changed - because @@ -334,7 +337,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum()); if (ourNode && hasPosition(ourNode) && hasPosition(node)) { Position &op = ourNode->position, &p = node->position; - float d = latLongToMeter(p.latitude, p.longitude, op.latitude, op.longitude); + float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); if (d < 2000) snprintf(distStr, sizeof(distStr), "%.0f m", d); else @@ -342,8 +345,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ // FIXME, also keep the guess at the operators heading and add/substract // it. currently we don't do this and instead draw north up only. - float bearingToOther = bearing(p.latitude, p.longitude, op.latitude, op.longitude); - float myHeading = estimatedHeading(p.latitude, p.longitude); + float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i)); headingRadian = bearingToOther - myHeading; } else { // Debug info for gps lock errors From ecf528f9b6b1211cc2f92909cb7aaaf2bc6dbbd7 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 10:23:47 -0700 Subject: [PATCH 2/7] move gps before refactoring --- src/{ => gps}/GPS.cpp | 0 src/{ => gps}/GPS.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => gps}/GPS.cpp (100%) rename src/{ => gps}/GPS.h (100%) diff --git a/src/GPS.cpp b/src/gps/GPS.cpp similarity index 100% rename from src/GPS.cpp rename to src/gps/GPS.cpp diff --git a/src/GPS.h b/src/gps/GPS.h similarity index 100% rename from src/GPS.h rename to src/gps/GPS.h From 933d5424da90113a152aae44df141915ffaf8401 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 11:15:05 -0700 Subject: [PATCH 3/7] abstract out the UBlox GPS driver --- .vscode/settings.json | 2 + platformio.ini | 2 +- src/PowerFSM.cpp | 2 +- src/esp32/MeshBluetoothService.cpp | 2 +- src/gps/GPS.cpp | 171 ++--------------------------- src/gps/GPS.h | 62 +++++------ src/gps/UBloxGPS.cpp | 153 ++++++++++++++++++++++++++ src/gps/UBloxGPS.h | 41 +++++++ src/main.cpp | 13 ++- src/mesh/MeshService.cpp | 28 ++--- src/mesh/NodeDB.cpp | 2 +- src/mesh/Router.cpp | 2 +- src/sleep.cpp | 9 +- 13 files changed, 263 insertions(+), 226 deletions(-) create mode 100644 src/gps/UBloxGPS.cpp create mode 100644 src/gps/UBloxGPS.h diff --git a/.vscode/settings.json b/.vscode/settings.json index dfe3b542f..bfef8191b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,7 +50,9 @@ "cassert": "cpp" }, "cSpell.words": [ + "Blox", "Meshtastic", + "Ublox", "descs", "protobufs" ] diff --git a/platformio.ini b/platformio.ini index 936a8e6c3..b64bcf002 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,7 +31,7 @@ board_build.partitions = partition-table.csv ; note: we add src to our include search path so that lmic_project_config can override ; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc -build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map +build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map -DAXP_DEBUG_PORT=Serial -DHW_VERSION_${sysenv.COUNTRY} -DAPP_VERSION=${sysenv.APP_VERSION} diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 4c4944b0f..ed6eaf4ff 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -87,7 +87,7 @@ static void lsIdle() static void lsExit() { // setGPSPower(true); // restore GPS power - gps.startLock(); + gps->startLock(); } static void nbEnter() diff --git a/src/esp32/MeshBluetoothService.cpp b/src/esp32/MeshBluetoothService.cpp index 9b3f14804..ecda8bf40 100644 --- a/src/esp32/MeshBluetoothService.cpp +++ b/src/esp32/MeshBluetoothService.cpp @@ -178,7 +178,7 @@ class MyNodeInfoCharacteristic : public ProtobufCharacteristic void onRead(BLECharacteristic *c) { // update gps connection state - myNodeInfo.has_gps = gps.isConnected; + myNodeInfo.has_gps = gps->isConnected; ProtobufCharacteristic::onRead(c); diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f78ad906c..9273a6ed2 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -6,89 +6,23 @@ #include #ifdef GPS_RX_PIN -HardwareSerial _serial_gps(GPS_SERIAL_NUM); +HardwareSerial _serial_gps_real(GPS_SERIAL_NUM); +HardwareSerial &GPS::_serial_gps = _serial_gps_real; #else // Assume NRF52 -HardwareSerial &_serial_gps = Serial1; +HardwareSerial &GPS::_serial_gps = Serial1; #endif bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep -GPS gps; +GPS *gps; // stuff that really should be in in the instance instead... static uint32_t timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock -static bool wantNewLocation = true; - -GPS::GPS() : PeriodicTask() {} - -void GPS::setup() -{ - PeriodicTask::setup(); - - readFromRTC(); // read the main CPU RTC at first - -#ifdef GPS_RX_PIN - _serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); -#else - _serial_gps.begin(GPS_BAUDRATE); -#endif - // _serial_gps.setRxBufferSize(1024); // the default is 256 - // ublox.enableDebugging(Serial); - - // note: the lib's implementation has the wrong docs for what the return val is - // it is not a bool, it returns zero for success - isConnected = ublox.begin(_serial_gps); - - // try a second time, the ublox lib serial parsing is buggy? - if (!isConnected) - isConnected = ublox.begin(_serial_gps); - - if (isConnected) { - DEBUG_MSG("Connected to UBLOX GPS successfully\n"); - - bool factoryReset = false; - bool ok; - if (factoryReset) { - // It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have - // GPS_TX connected) - ublox.factoryReset(); - delay(2000); - isConnected = ublox.begin(_serial_gps); - DEBUG_MSG("Factory reset success=%d\n", isConnected); - if (isConnected) { - ublox.assumeAutoPVT(true, true); // Just parse NEMA for now - } - } else { - ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API - assert(ok); - ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low - assert(ok); - // ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M - // assert(ok); - // ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds - // assert(ok); - ok = ublox.powerSaveMode(); // use power save mode - assert(ok); - } - ok = ublox.saveConfiguration(3000); - assert(ok); - } else { - // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just - // assume NEMA at 9600 baud. - DEBUG_MSG("ERROR: No bidirectional GPS found, hoping that it still might work\n"); - - // tell lib, we are expecting the module to send PVT messages by itself to our Rx pin - // you can set second parameter to "false" if you want to control the parsing and eviction of the data (need to call - // checkUblox cyclically) - ublox.assumeAutoPVT(true, true); - } -} - -void GPS::readFromRTC() +void readFromRTC() { struct timeval tv; /* btw settimeofday() is helpfull here too*/ @@ -102,7 +36,7 @@ void GPS::readFromRTC() } /// If we haven't yet set our RTC this boot, set it from a GPS derived time -void GPS::perhapsSetRTC(const struct timeval *tv) +void perhapsSetRTC(const struct timeval *tv) { if (!timeSetFromGPS) { timeSetFromGPS = true; @@ -118,101 +52,12 @@ void GPS::perhapsSetRTC(const struct timeval *tv) #include -uint32_t GPS::getTime() +uint32_t getTime() { return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs; } -uint32_t GPS::getValidTime() +uint32_t getValidTime() { return timeSetFromGPS ? getTime() : 0; } - -/// Returns true if we think the board can enter deep or light sleep now (we might be trying to get a GPS lock) -bool GPS::canSleep() -{ - return true; // we leave GPS on during sleep now, so sleep is okay !wantNewLocation; -} - -/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs -void GPS::prepareSleep() -{ - if (isConnected) - ublox.powerOff(); -} - -void GPS::doTask() -{ - uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix - - if (isConnected) { - // Consume all characters that have arrived - - // getPVT automatically calls checkUblox - 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 - // fixtype = ublox.getFixType(); - DEBUG_MSG("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 (!timeSetFromGPS && ublox.getT()) { - struct timeval tv; - - /* 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(); - t.tm_min = ublox.getMinute(); - t.tm_hour = ublox.getHour(); - t.tm_mday = ublox.getDay(); - t.tm_mon = ublox.getMonth() - 1; - t.tm_year = ublox.getYear() - 1900; - t.tm_isdst = false; - time_t res = mktime(&t); - tv.tv_sec = res; - tv.tv_usec = 0; // time.centisecond() * (10 / 1000); - - DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); - if (t.tm_year < 0 || t.tm_year >= 300) - DEBUG_MSG("Ignoring invalid GPS time\n"); - else - perhapsSetRTC(&tv); - } - - if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only - { - // we only notify if position has changed - latitude = ublox.getLatitude(); - longitude = ublox.getLongitude(); - altitude = ublox.getAltitude() / 1000; // in mm convert to meters - DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); - - hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 - if (hasValidLocation) { - wantNewLocation = false; - notifyObservers(NULL); - // ublox.powerOff(); - } - } else // we didn't get a location update, go back to sleep and hope the characters show up - wantNewLocation = true; - - // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over - // the serial - setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000); -} - -void GPS::startLock() -{ - DEBUG_MSG("Looking for GPS lock\n"); - wantNewLocation = true; - setPeriod(1); -} diff --git a/src/gps/GPS.h b/src/gps/GPS.h index c3c403278..7a1a79a04 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -2,54 +2,50 @@ #include "Observer.h" #include "PeriodicTask.h" -#include "SparkFun_Ublox_Arduino_Library.h" #include "sys/time.h" +/// If we haven't yet set our RTC this boot, set it from a GPS derived time +void perhapsSetRTC(const struct timeval *tv); + +/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero +uint32_t getTime(); + +/// Return time since 1970 in secs. If we don't have a GPS lock return zero +uint32_t getValidTime(); + +void readFromRTC(); + /** * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) * * When new data is available it will notify observers. */ -class GPS : public PeriodicTask, public Observable +class GPS : public Observable { - SFE_UBLOX_GPS ublox; + protected: + bool hasValidLocation = false; // default to false, until we complete our first read + + static HardwareSerial &_serial_gps; public: - uint32_t latitude, longitude; // as an int mult by 1e-7 to get value as double - uint32_t altitude; - bool isConnected; // Do we have a GPS we are talking to + uint32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double + uint32_t altitude = 0; + bool isConnected = false; // Do we have a GPS we are talking to - GPS(); + virtual ~GPS() {} - /// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero - uint32_t getTime(); - - /// Return time since 1970 in secs. If we don't have a GPS lock return zero - uint32_t getValidTime(); - - void setup(); - - virtual void doTask(); - - /// If we haven't yet set our RTC this boot, set it from a GPS derived time - void perhapsSetRTC(const struct timeval *tv); - - /// Returns true if we think the board can enter deep or light sleep now (we might be trying to get a GPS lock) - bool canSleep(); - - /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs - void prepareSleep(); - - /// Restart our lock attempt - try to get and broadcast a GPS reading ASAP - void startLock(); + /** + * Returns true if we succeeded + */ + virtual bool setup() = 0; /// Returns ture if we have acquired GPS lock. bool hasLock() const { return hasValidLocation; } - private: - void readFromRTC(); - - bool hasValidLocation = false; // default to false, until we complete our first read + /** + * 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() {} }; -extern GPS gps; +extern GPS *gps; diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp new file mode 100644 index 000000000..881fd2977 --- /dev/null +++ b/src/gps/UBloxGPS.cpp @@ -0,0 +1,153 @@ +#include "UBloxGPS.h" +#include "sleep.h" +#include + +UBloxGPS::UBloxGPS() : PeriodicTask() +{ + notifySleepObserver.observe(¬ifySleep); +} + +bool UBloxGPS::setup() +{ + PeriodicTask::setup(); + +#ifdef GPS_RX_PIN + _serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); +#else + _serial_gps.begin(GPS_BAUDRATE); +#endif + // _serial_gps.setRxBufferSize(1024); // the default is 256 + // ublox.enableDebugging(Serial); + + // note: the lib's implementation has the wrong docs for what the return val is + // it is not a bool, it returns zero for success + isConnected = ublox.begin(_serial_gps); + + // try a second time, the ublox lib serial parsing is buggy? + if (!isConnected) + isConnected = ublox.begin(_serial_gps); + + if (isConnected) { + DEBUG_MSG("Connected to UBLOX GPS successfully\n"); + + bool factoryReset = false; + bool ok; + if (factoryReset) { + // It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have + // GPS_TX connected) + ublox.factoryReset(); + delay(2000); + isConnected = ublox.begin(_serial_gps); + DEBUG_MSG("Factory reset success=%d\n", isConnected); + if (isConnected) { + ublox.assumeAutoPVT(true, true); // Just parse NEMA for now + } + } else { + ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API + assert(ok); + ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low + assert(ok); + // ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M + // assert(ok); + // ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds + // assert(ok); + ok = ublox.powerSaveMode(); // use power save mode + assert(ok); + } + ok = ublox.saveConfiguration(3000); + assert(ok); + + return true; + } else { + // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just + // assume NEMA at 9600 baud. + DEBUG_MSG("ERROR: No bidirectional GPS found, hoping that it still might work\n"); + + return false; + } +} + +/// 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) +{ + if (isConnected) + ublox.powerOff(); + + return 0; +} + +void UBloxGPS::doTask() +{ + uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix + + if (isConnected) { + // Consume all characters that have arrived + + // getPVT automatically calls checkUblox + 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 + // fixtype = ublox.getFixType(); + DEBUG_MSG("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()) { + struct timeval tv; + + /* 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(); + t.tm_min = ublox.getMinute(); + t.tm_hour = ublox.getHour(); + t.tm_mday = ublox.getDay(); + t.tm_mon = ublox.getMonth() - 1; + t.tm_year = ublox.getYear() - 1900; + t.tm_isdst = false; + time_t res = mktime(&t); + tv.tv_sec = res; + tv.tv_usec = 0; // time.centisecond() * (10 / 1000); + + DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); + if (t.tm_year < 0 || t.tm_year >= 300) + DEBUG_MSG("Ignoring invalid GPS time\n"); + else + perhapsSetRTC(&tv); + } + + if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only + { + // we only notify if position has changed + latitude = ublox.getLatitude(); + longitude = ublox.getLongitude(); + altitude = ublox.getAltitude() / 1000; // in mm convert to meters + DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); + + hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 + if (hasValidLocation) { + wantNewLocation = false; + notifyObservers(NULL); + // ublox.powerOff(); + } + } else // we didn't get a location update, go back to sleep and hope the characters show up + wantNewLocation = true; + + // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over + // the serial + setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000); +} + +void UBloxGPS::startLock() +{ + DEBUG_MSG("Looking for GPS lock\n"); + wantNewLocation = true; + setPeriod(1); +} diff --git a/src/gps/UBloxGPS.h b/src/gps/UBloxGPS.h new file mode 100644 index 000000000..39b125981 --- /dev/null +++ b/src/gps/UBloxGPS.h @@ -0,0 +1,41 @@ +#pragma once + +#include "GPS.h" +#include "Observer.h" +#include "PeriodicTask.h" +#include "SparkFun_Ublox_Arduino_Library.h" + +/** + * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) + * + * When new data is available it will notify observers. + */ +class UBloxGPS : public GPS, public PeriodicTask +{ + SFE_UBLOX_GPS ublox; + + bool wantNewLocation = true; + + CallbackObserver notifySleepObserver = CallbackObserver(this, &UBloxGPS::prepareSleep); + + public: + UBloxGPS(); + + /** + * Returns true if we succeeded + */ + 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(); + + 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); +}; diff --git a/src/main.cpp b/src/main.cpp index 7e1b39ab7..b70ddb99f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,13 +21,13 @@ */ -#include "GPS.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" #include "Periodic.h" #include "PowerFSM.h" #include "Router.h" +#include "UBloxGPS.h" #include "configuration.h" #include "error.h" #include "power.h" @@ -188,8 +188,13 @@ void setup() screen.print("Started...\n"); - // Init GPS - gps.setup(); + readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) + + // Init GPS - first try ublox + gps = new UBloxGPS(); + if (!gps->setup()) { + // FIXME - fallback to NEMA + } service.init(); @@ -306,7 +311,7 @@ void loop() screen.debug()->setChannelNameStatus(channelSettings.name); screen.debug()->setPowerStatus(powerStatus); // TODO(#4): use something based on hdop to show GPS "signal" strength. - screen.debug()->setGPSStatus(gps.hasLock() ? "ok" : ":("); + screen.debug()->setGPSStatus(gps->hasLock() ? "good" : "bad"); // No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in) // i.e. don't just keep spinning in loop as fast as we can. diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 1b05e0c51..7e9fc4486 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -84,7 +84,7 @@ void MeshService::init() sendOwnerPeriod.setup(); nodeDB.init(); - gpsObserver.observe(&gps); + gpsObserver.observe(gps); packetReceivedObserver.observe(&router.notifyPacketReceived); } @@ -153,7 +153,7 @@ void MeshService::handleIncomingPosition(const MeshPacket *mp) tv.tv_sec = secs; tv.tv_usec = 0; - gps.perhapsSetRTC(&tv); + perhapsSetRTC(&tv); } } else { DEBUG_MSG("Ignoring incoming packet - not a position\n"); @@ -165,7 +165,7 @@ int MeshService::handleFromRadio(const MeshPacket *mp) powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping // If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work) - if (!gps.isConnected) + if (!gps->isConnected) handleIncomingPosition(mp); else { DEBUG_MSG("Ignoring incoming time, because we have a GPS\n"); @@ -234,8 +234,8 @@ void MeshService::handleToRadio(MeshPacket &p) if (p.id == 0) p.id = generatePacketId(); // If the phone didn't supply one, then pick one - p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone - // (so we update our nodedb for the local node) + p.rx_time = getValidTime(); // Record the time the packet arrived from the phone + // (so we update our nodedb for the local node) // Send the packet into the mesh @@ -258,7 +258,7 @@ void MeshService::sendToMesh(MeshPacket *p) // nodes shouldn't trust it anyways) Note: for now, we allow a device with a local GPS to include the time, so that gpsless // devices can get time. if (p->has_payload && p->payload.has_position) { - if (!gps.isConnected) { + if (!gps->isConnected) { DEBUG_MSG("Stripping time %u from position send\n", p->payload.position.time); p->payload.position.time = 0; } else @@ -286,7 +286,7 @@ MeshPacket *MeshService::allocForSending() p->from = nodeDB.getNodeNum(); p->to = NODENUM_BROADCAST; p->id = generatePacketId(); - p->rx_time = gps.getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp + p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp return p; } @@ -315,7 +315,7 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) p->payload.has_position = true; p->payload.position = node->position; p->payload.want_response = wantReplies; - p->payload.position.time = gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid. + p->payload.position.time = getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid. sendToMesh(p); } @@ -329,12 +329,12 @@ int MeshService::onGPSChanged(void *unused) Position &pos = p->payload.position; // !zero or !zero lat/long means valid - if (gps.latitude != 0 || gps.longitude != 0) { - if (gps.altitude != 0) - pos.altitude = gps.altitude; - pos.latitude_i = gps.latitude; - pos.longitude_i = gps.longitude; - pos.time = gps.getValidTime(); + if (gps->latitude != 0 || gps->longitude != 0) { + if (gps->altitude != 0) + pos.altitude = gps->altitude; + pos.latitude_i = gps->latitude; + pos.longitude_i = gps->longitude; + pos.time = getValidTime(); } // We limit our GPS broadcasts to a max rate diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0559754a3..73b3391d6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -246,7 +246,7 @@ const NodeInfo *NodeDB::readNextInfo() /// Given a node, return how many seconds in the past (vs now) that we last heard from it uint32_t sinceLastSeen(const NodeInfo *n) { - uint32_t now = gps.getTime(); + uint32_t now = getTime(); uint32_t last_seen = n->position.time; int delta = (int)(now - last_seen); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index b5a34a801..6e5734e59 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -68,7 +68,7 @@ void Router::handleReceived(MeshPacket *p) { // FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function // Also, we should set the time from the ISR and it should have msec level resolution - p->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone + p->rx_time = getValidTime(); // store the arrival timestamp for the phone DEBUG_MSG("Notifying observers of received packet fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); notifyPacketReceived.notifyObservers(p); diff --git a/src/sleep.cpp b/src/sleep.cpp index 9693a8fc6..4f5fa2fdc 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -29,6 +29,7 @@ extern AXP20X_Class axp; Observable preflightSleep; /// Called to tell observers we are now entering sleep and you should prepare. Must return 0 +/// notifySleep will be called for light or deep sleep, notifyDeepSleep is only called for deep sleep Observable notifySleep, notifyDeepSleep; // deep sleep support @@ -125,12 +126,6 @@ static bool doPreflightSleep() /// Tell devices we are going to sleep and wait for them to handle things static void waitEnterSleep() { - /* - former hardwired code - now moved into notifySleep callbacks: - // Put radio in sleep mode (will still draw power but only 0.2uA) - service.radio.radioIf.sleep(); - */ - uint32_t now = millis(); while (!doPreflightSleep()) { delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) @@ -144,7 +139,6 @@ static void waitEnterSleep() // Code that still needs to be moved into notifyObservers Serial.flush(); // send all our characters before we stop cpu clock setBluetoothEnable(false); // has to be off before calling light sleep - gps.prepareSleep(); // abandon in-process parsing notifySleep.notifyObservers(NULL); } @@ -157,6 +151,7 @@ void doDeepSleep(uint64_t msecToWake) // not using wifi yet, but once we are this is needed to shutoff the radio hw // esp_wifi_stop(); waitEnterSleep(); + notifySleep.notifyObservers(NULL); // also tell the regular sleep handlers notifyDeepSleep.notifyObservers(NULL); screen.setOn(false); // datasheet says this will draw only 10ua From 101eef5495c9cbefdfbc3301f604e73476d2b576 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 11:21:24 -0700 Subject: [PATCH 4/7] oops lat/lon need to be signed ;-) --- proto | 2 +- src/gps/GPS.h | 4 ++-- src/mesh/mesh.pb.h | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/proto b/proto index cabbdf51e..b35e7fb17 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit cabbdf51ed365b72ab995ad24b075269627f58ad +Subproject commit b35e7fb17e80a9761145d69a288a9e87af862cab diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 7a1a79a04..1c25ec2f4 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -28,8 +28,8 @@ class GPS : public Observable static HardwareSerial &_serial_gps; public: - uint32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double - uint32_t altitude = 0; + int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double + int32_t altitude = 0; bool isConnected = false; // Do we have a GPS we are talking to virtual ~GPS() {} diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 7f25f1c99..ba2293a0a 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -300,8 +300,8 @@ typedef struct _ToRadio { X(a, STATIC, SINGULAR, INT32, altitude, 3) \ X(a, STATIC, SINGULAR, INT32, battery_level, 4) \ X(a, STATIC, SINGULAR, UINT32, time, 6) \ -X(a, STATIC, SINGULAR, INT32, latitude_i, 7) \ -X(a, STATIC, SINGULAR, INT32, longitude_i, 8) +X(a, STATIC, SINGULAR, SINT32, latitude_i, 7) \ +X(a, STATIC, SINGULAR, SINT32, longitude_i, 8) #define Position_CALLBACK NULL #define Position_DEFAULT NULL @@ -486,21 +486,21 @@ extern const pb_msgdesc_t ToRadio_msg; #define ToRadio_fields &ToRadio_msg /* Maximum encoded size of messages (where known) */ -#define Position_size 50 +#define Position_size 40 #define Data_size 256 #define User_size 72 /* RouteDiscovery_size depends on runtime parameters */ -#define SubPacket_size 387 -#define MeshPacket_size 429 +#define SubPacket_size 377 +#define MeshPacket_size 419 #define ChannelSettings_size 44 #define RadioConfig_size 120 #define RadioConfig_UserPreferences_size 72 -#define NodeInfo_size 142 +#define NodeInfo_size 132 #define MyNodeInfo_size 85 -#define DeviceState_size 19185 +#define DeviceState_size 18535 #define DebugString_size 258 -#define FromRadio_size 438 -#define ToRadio_size 432 +#define FromRadio_size 428 +#define ToRadio_size 422 #ifdef __cplusplus } /* extern "C" */ From c2be6c4068d474c17f4cf22951101efde93dfd10 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 17:39:57 -0700 Subject: [PATCH 5/7] WIP on #124 --- .vscode/settings.json | 1 + docs/software/nrf52-TODO.md | 3 +- platformio.ini | 3 +- src/gps/GPS.cpp | 18 ++++++++++++ src/gps/GPS.h | 6 +++- src/gps/NEMAGPS.cpp | 56 +++++++++++++++++++++++++++++++++++++ src/gps/NEMAGPS.h | 19 +++++++++++++ src/gps/UBloxGPS.cpp | 50 ++++++++++++--------------------- src/main.cpp | 9 +++++- 9 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 src/gps/NEMAGPS.cpp create mode 100644 src/gps/NEMAGPS.h diff --git a/.vscode/settings.json b/.vscode/settings.json index bfef8191b..ebed64343 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,6 +52,7 @@ "cSpell.words": [ "Blox", "Meshtastic", + "NEMAGPS", "Ublox", "descs", "protobufs" diff --git a/docs/software/nrf52-TODO.md b/docs/software/nrf52-TODO.md index 6bc5fb0d2..59a467fef 100644 --- a/docs/software/nrf52-TODO.md +++ b/docs/software/nrf52-TODO.md @@ -9,7 +9,7 @@ Minimum items needed to make sure hardware is good. - plug in correct variants for the real board - Use the PMU driver on real hardware - add a NEMA based GPS driver to test GPS -- Use new radio driver on real hardware +- Use new radio driver on real hardware - Use UC1701 LCD driver on real hardware. Still need to create at startup and probe on SPI - test the LEDs - test the buttons @@ -24,6 +24,7 @@ Minimum items needed to make sure hardware is good. Needed to be fully functional at least at the same level of the ESP32 boards. At this point users would probably want them. +- stop polling for GPS characters, instead stay blocked on read in a thread - increase preamble length? - will break other clients? so all devices must update - enable BLE DFU somehow - set appversion/hwversion diff --git a/platformio.ini b/platformio.ini index b64bcf002..61b61d677 100644 --- a/platformio.ini +++ b/platformio.ini @@ -74,7 +74,8 @@ lib_deps = https://github.com/meshtastic/arduino-fsm.git https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git https://github.com/meshtastic/RadioLib.git - + https://github.com/meshtastic/TinyGPSPlus.git + ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] src_filter = diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 9273a6ed2..d8d73784e 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -50,6 +50,24 @@ void perhapsSetRTC(const struct timeval *tv) } } +void perhapsSetRTC(struct tm &t) +{ + /* 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). + */ + time_t res = mktime(&t); + struct timeval tv; + tv.tv_sec = res; + tv.tv_usec = 0; // time.centisecond() * (10 / 1000); + + DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); + if (t.tm_year < 0 || t.tm_year >= 300) + DEBUG_MSG("Ignoring invalid GPS time\n"); + else + perhapsSetRTC(&tv); +} + #include uint32_t getTime() diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 1c25ec2f4..3eb972843 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -6,6 +6,7 @@ /// If we haven't yet set our RTC this boot, set it from a GPS derived time void perhapsSetRTC(const struct timeval *tv); +void perhapsSetRTC(struct tm &t); /// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero uint32_t getTime(); @@ -37,7 +38,10 @@ class GPS : public Observable /** * Returns true if we succeeded */ - virtual bool setup() = 0; + virtual bool setup() { return true; } + + /// A loop callback for subclasses that need it. FIXME, instead just block on serial reads + virtual void loop() {} /// Returns ture if we have acquired GPS lock. bool hasLock() const { return hasValidLocation; } diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp new file mode 100644 index 000000000..74cdf7606 --- /dev/null +++ b/src/gps/NEMAGPS.cpp @@ -0,0 +1,56 @@ +#include "NEMAGPS.h" +#include "configuration.h" + +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; +} + +void NEMAGPS::loop() +{ + while (_serial_gps.available() > 0) { + int c = _serial_gps.read(); + Serial.write(c); + 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 (reader.altitude.isUpdated() || reader.location.isUpdated()) { // probably get updated at the same time + if (reader.altitude.isValid()) + altitude = reader.altitude.meters(); + + auto loc = reader.location; + if (loc.isValid()) { + latitude = toDegInt(loc.rawLat()); + longitude = toDegInt(loc.rawLng()); + } + + // expect gps pos lat=37.520825, lon=-122.309162, alt=158 + DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); + + hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 + if (hasValidLocation) + notifyObservers(NULL); + } +} \ No newline at end of file diff --git a/src/gps/NEMAGPS.h b/src/gps/NEMAGPS.h new file mode 100644 index 000000000..ddaf77ee2 --- /dev/null +++ b/src/gps/NEMAGPS.h @@ -0,0 +1,19 @@ +#pragma once + +#include "GPS.h" +#include "Observer.h" +#include "PeriodicTask.h" +#include "TinyGPS++.h" + +/** + * A gps class thatreads from a NEMA GPS stream (and FIXME - eventually keeps the gps powered down except when reading) + * + * When new data is available it will notify observers. + */ +class NEMAGPS : public GPS +{ + TinyGPSPlus reader; + + public: + virtual void loop(); +}; diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 881fd2977..54f7810ee 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -9,8 +9,6 @@ UBloxGPS::UBloxGPS() : PeriodicTask() bool UBloxGPS::setup() { - PeriodicTask::setup(); - #ifdef GPS_RX_PIN _serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); #else @@ -30,18 +28,18 @@ bool UBloxGPS::setup() if (isConnected) { DEBUG_MSG("Connected to UBLOX GPS successfully\n"); - bool factoryReset = false; + bool factoryReset = true; bool ok; if (factoryReset) { // It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have // GPS_TX connected) ublox.factoryReset(); - delay(2000); + delay(3000); isConnected = ublox.begin(_serial_gps); DEBUG_MSG("Factory reset success=%d\n", isConnected); - if (isConnected) { - ublox.assumeAutoPVT(true, true); // Just parse NEMA for now - } + ok = ublox.saveConfiguration(3000); + assert(ok); + return false; } else { ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API assert(ok); @@ -57,12 +55,10 @@ bool UBloxGPS::setup() ok = ublox.saveConfiguration(3000); assert(ok); + PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device + return true; } else { - // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just - // assume NEMA at 9600 baud. - DEBUG_MSG("ERROR: No bidirectional GPS found, hoping that it still might work\n"); - return false; } } @@ -80,26 +76,24 @@ void UBloxGPS::doTask() { uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix - if (isConnected) { - // Consume all characters that have arrived + assert(isConnected); - // getPVT automatically calls checkUblox - ublox.checkUblox(); // See if new data is available. Process bytes as they come in. + // Consume all characters that have arrived - // 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 - // fixtype = ublox.getFixType(); - DEBUG_MSG("fix type %d\n", fixtype); - } + // getPVT automatically calls checkUblox + 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 + // fixtype = ublox.getFixType(); + DEBUG_MSG("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()) { - struct timeval tv; - /* 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). @@ -112,15 +106,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s t.tm_mon = ublox.getMonth() - 1; t.tm_year = ublox.getYear() - 1900; t.tm_isdst = false; - time_t res = mktime(&t); - tv.tv_sec = res; - tv.tv_usec = 0; // time.centisecond() * (10 / 1000); - - DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); - if (t.tm_year < 0 || t.tm_year >= 300) - DEBUG_MSG("Ignoring invalid GPS time\n"); - else - perhapsSetRTC(&tv); + perhapsSetRTC(t); } if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only diff --git a/src/main.cpp b/src/main.cpp index b70ddb99f..3971ae9bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "MeshRadio.h" #include "MeshService.h" +#include "NEMAGPS.h" #include "NodeDB.h" #include "Periodic.h" #include "PowerFSM.h" @@ -193,7 +194,12 @@ void setup() // Init GPS - first try ublox gps = new UBloxGPS(); if (!gps->setup()) { - // FIXME - fallback to NEMA + // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just + // assume NEMA at 9600 baud. + DEBUG_MSG("ERROR: No UBLOX GPS found, hoping that NEMA might work\n"); + delete gps; + gps = new NEMAGPS(); + gps->setup(); } service.init(); @@ -263,6 +269,7 @@ void loop() { uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop? + gps->loop(); // FIXME, remove from main, instead block on read router.loop(); powerFSM.run_machine(); service.loop(); From dcd1f7478a510c454f32099ab946c383368ac884 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 20:02:43 -0700 Subject: [PATCH 6/7] fix 124 - we now fallback to nema if we can't talk ublox protocol to the GPS. Though we are super power inefficient about it so TODO/FIXME someday to decrease our power draw. --- docs/software/nrf52-TODO.md | 2 +- src/gps/GPS.cpp | 2 +- src/gps/NEMAGPS.cpp | 69 +++++++++++++++++++++---------------- src/gps/NEMAGPS.h | 2 ++ src/gps/UBloxGPS.cpp | 2 +- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/docs/software/nrf52-TODO.md b/docs/software/nrf52-TODO.md index 59a467fef..a60cf2af9 100644 --- a/docs/software/nrf52-TODO.md +++ b/docs/software/nrf52-TODO.md @@ -8,7 +8,6 @@ Minimum items needed to make sure hardware is good. - use "variants" to get all gpio bindings - plug in correct variants for the real board - Use the PMU driver on real hardware -- add a NEMA based GPS driver to test GPS - Use new radio driver on real hardware - Use UC1701 LCD driver on real hardware. Still need to create at startup and probe on SPI - test the LEDs @@ -101,6 +100,7 @@ Nice ideas worth considering someday... - DONE neg 7 error code from receive - DONE remove unused sx1262 lib from github - at boot we are starting our message IDs at 1, rather we should start them at a random number. also, seed random based on timer. this could be the cause of our first message not seen bug. +- add a NEMA based GPS driver to test GPS ``` diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index d8d73784e..bb2d30b54 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -61,7 +61,7 @@ void perhapsSetRTC(struct tm &t) tv.tv_sec = res; tv.tv_usec = 0; // time.centisecond() * (10 / 1000); - DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); + // DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); if (t.tm_year < 0 || t.tm_year >= 300) DEBUG_MSG("Ignoring invalid GPS time\n"); else diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index 74cdf7606..7d19f8869 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -12,45 +12,54 @@ static int32_t toDegInt(RawDegrees d) void NEMAGPS::loop() { + while (_serial_gps.available() > 0) { int c = _serial_gps.read(); - Serial.write(c); + // Serial.write(c); 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); - } + 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; - if (reader.altitude.isUpdated() || reader.location.isUpdated()) { // probably get updated at the same time - if (reader.altitude.isValid()) - altitude = reader.altitude.meters(); + 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); - auto loc = reader.location; - if (loc.isValid()) { - latitude = toDegInt(loc.rawLat()); - longitude = toDegInt(loc.rawLng()); + isConnected = true; // we seem to have a real GPS (but not necessarily a lock) } - // expect gps pos lat=37.520825, lon=-122.309162, alt=158 - DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); + if (reader.location.isUpdated()) { + if (reader.altitude.isValid()) + altitude = reader.altitude.meters(); - hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 - if (hasValidLocation) - notifyObservers(NULL); + 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 NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude); + + hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 + if (hasValidLocation) + notifyObservers(NULL); + } } } \ No newline at end of file diff --git a/src/gps/NEMAGPS.h b/src/gps/NEMAGPS.h index ddaf77ee2..5bea0d41f 100644 --- a/src/gps/NEMAGPS.h +++ b/src/gps/NEMAGPS.h @@ -13,6 +13,8 @@ class NEMAGPS : public GPS { TinyGPSPlus reader; + + uint32_t lastUpdateMsec = 0; public: virtual void loop(); diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 54f7810ee..560c52fa8 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -28,7 +28,7 @@ bool UBloxGPS::setup() if (isConnected) { DEBUG_MSG("Connected to UBLOX GPS successfully\n"); - bool factoryReset = true; + bool factoryReset = false; bool ok; if (factoryReset) { // It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have From 95df7dd8dc6e50ab369dbb529cbe28b71494169d Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 4 May 2020 20:04:44 -0700 Subject: [PATCH 7/7] 0.6.2 --- bin/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/version.sh b/bin/version.sh index 6b0158eb3..585b24030 100644 --- a/bin/version.sh +++ b/bin/version.sh @@ -1,3 +1,3 @@ -export VERSION=0.6.1 \ No newline at end of file +export VERSION=0.6.2 \ No newline at end of file