From dab13d22977ab23b2ba503a3c0831c6c370fa6eb Mon Sep 17 00:00:00 2001 From: Max-Plastix Date: Wed, 26 Jan 2022 22:34:07 -0800 Subject: [PATCH] Major changes to support GPS-off sleep --- console-decoders/unified_decoder.js | 69 ++++++++++++--------- main/configuration.h | 95 ++++++++++++++++++++--------- main/gps.cpp | 47 ++++++++++---- main/gps.h | 5 +- main/screen.cpp | 6 +- main/screen.h | 2 +- 6 files changed, 148 insertions(+), 76 deletions(-) diff --git a/console-decoders/unified_decoder.js b/console-decoders/unified_decoder.js index 7ff83c1..a8f47c6 100644 --- a/console-decoders/unified_decoder.js +++ b/console-decoders/unified_decoder.js @@ -1,4 +1,4 @@ -// Decoder for CubeCell and Unified TTGO mappers, same as +// Decoder for MaxPlastix mappers, compatible with: // https://github.com/hkicko/CubeCell-GPS-Helium-Mapper // but without the Tracker payload. // @@ -8,31 +8,42 @@ // Battery is 1/100 of a volt, offset by 2v for a range of 2.00 to 4.56 volts. // function Decoder(bytes, port) { - var decoded = {}; - - var latitude = ((bytes[0]<<16)>>>0) + ((bytes[1]<<8)>>>0) + bytes[2]; - latitude = (latitude / 16777215.0 * 180) - 90; - - var longitude = ((bytes[3]<<16)>>>0) + ((bytes[4]<<8)>>>0) + bytes[5]; - longitude = (longitude / 16777215.0 * 360) - 180; - - switch (port) - { - case 2: - decoded.latitude = latitude; - decoded.longitude = longitude; - - var altValue = ((bytes[6]<<8)>>>0) + bytes[7]; - var sign = bytes[6] & (1 << 7); - if(sign) decoded.altitude = 0xFFFF0000 | altValue; - else decoded.altitude = altValue; - - decoded.speed = parseFloat((((bytes[8]))/1.609).toFixed(2)); - decoded.battery = parseFloat((bytes[9]/100 + 2).toFixed(2)); - decoded.sats = bytes[10]; - decoded.accuracy = 2.5; // Bogus Accuracy required by Cargo/Mapper integration - break; - } - - return decoded; - } \ No newline at end of file + var decoded = {}; + + var latitude = ((bytes[0] << 16) >>> 0) + ((bytes[1] << 8) >>> 0) + bytes[2]; + latitude = (latitude / 16777215.0 * 180) - 90; + + var longitude = ((bytes[3] << 16) >>> 0) + ((bytes[4] << 8) >>> 0) + bytes[5]; + longitude = (longitude / 16777215.0 * 360) - 180; + + switch (port) { + case 2: // Mapper! (Cargo and Heatmap too) + decoded.latitude = latitude; + decoded.longitude = longitude; + + var altValue = ((bytes[6] << 8) >>> 0) + bytes[7]; + var sign = bytes[6] & (1 << 7); + if (sign) decoded.altitude = 0xFFFF0000 | altValue; + else decoded.altitude = altValue; + + decoded.speed = parseFloat((((bytes[8])) / 1.609).toFixed(2)); + decoded.battery = parseFloat((bytes[9] / 100 + 2).toFixed(2)); + decoded.sats = bytes[10]; + decoded.accuracy = 2.5; // Bogus Accuracy required by Cargo/Mapper integration + break; + case 5: // System status + decoded.battery = parseFloat((bytes[6] / 100 + 2).toFixed(2)); + decoded.status = bytes[7]; + decoded.value = bytes[8]; + break; + case 6: // Lost GPS + decoded.last_latitude = latitude; + decoded.last_longitude = longitude; + decoded.battery = parseFloat((bytes[6] / 100 + 2).toFixed(2)); + decoded.sats = bytes[7]; + decoded.minutes = ((bytes[8] << 8) >>> 0) + bytes[9]; + break; + } + + return decoded; +} \ No newline at end of file diff --git a/main/configuration.h b/main/configuration.h index 0b497ee..82430fe 100644 --- a/main/configuration.h +++ b/main/configuration.h @@ -24,31 +24,72 @@ along with this program. If not, see . */ #pragma once +// ----------------------------------------------------------------------------- +// Version +// ----------------------------------------------------------------------------- +#define APP_NAME "MaxP Mapper" +#define APP_VERSION "1.7.1" + // ----------------------------------------------------------------------------- // CONFIGURATION // Stuff you might reasonably want to change is here: // ----------------------------------------------------------------------------- -// Select which T-Beam board is being used. Only uncomment one. -//#define T_BEAM_V07 // AKA Rev0 (first board released) -#define T_BEAM_V10 // AKA Rev1 (second board released), also for "v1.1" - -#define MIN_DIST 68.0 // Minimum distance in meters from the last sent location before we can send again. A hex is about 340m. -#define STATIONARY_TX_INTERVAL ( 2 * 60) // If no minimum movement, the LoRa frame will still be sent once every N seconds - -#define REST_WAIT (30 * 60) // If we still haven't moved in this many seconds, start sending even slower -#define REST_TX_INTERVAL (10 * 60) // Slow resting ping frequency in seconds - -#define SCREEN_IDLE_OFF_S (30) // If there are no Uplinks sent for this long, turn the screen off. - -#define BATTERY_LOW_VOLTAGE 3.4 // Below this voltage, power off until USB power allows charging - -#define LORAWAN_PORT 2 // FPort for Uplink messages -- must match Helium Console Decoder script! -#define LORAWAN_CONFIRMED_EVERY 0 // Send confirmed message for ACK every N messages (0 means never, 1 means always, 2 every-other-one..) -#define LORAWAN_SF DR_SF7 // Spreading factor (recommended DR_SF7 for network map purposes, DR_SF10 is slower/more-reach) +// All time settings here are SECONDS + +// Minimum Distance between Mapper reports. This is your MAIN knob to turn for more/fewer uplink packets. +// SMALLER distance: More packets, more dots on the map, more DC spent, more power consumed +// (If you set this to a very small value, it will still be rate-limited by how often your Region allows back-to-back Uplink packets.) +// LARGER distance: Fewer packets, might miss some hexes, conserves DC, battery might last longer +// Note that a hex is about 340m across. Ideally, you want at least two uplinks in each hex to map it. +#define MIN_DIST 70.0 // Minimum distance in meters from the last sent location before we send again. + +// If we are not moving at least MIN_DIST meters away from the last uplink, when should we send a redundant +// Mapper Uplink from the same location? This Heartbeat or ping isn't all that important for mapping, but might be +// useful for time-at-location tracking or other monitoring. You can safely set this value very high. +#define STATIONARY_TX_INTERVAL ( 5 * 60) // Send one uplink at least once every N seconds +#define NEVER_REST 0 // Change to 1 if you want to always send at THIS rate, with no slowing or sleeping. + +// After being stationary for a long while, we move to a slower heartbeat interval: +#define REST_WAIT (20 * 60) // If we still haven't moved in this many seconds, start sending even slower.. +#define REST_TX_INTERVAL (30 * 60) // Slow resting ping frequency in seconds + +// This last stage is a low-power sleep to conserve battery when the Mapper has not moved for a long time. +// This one is a difficult compromise: Waking up to boot & power on the GPS is not a fast operation, +// so we want to avoid it as much as possible. There is no other motion sensor, so if we make it too long, +// we miss the first minutes of each motion while sleeping. +// Note that USB Power will prevent this low-power sleep, and also wake us up from it. +// A button press will also wake from sleep, but takes some time to initialise and re-aquire +#define SLEEP_WAIT ( 2 * 60 * 60) // If we STILL haven't moved in this long, turn off the GPS to save power +// For a vehicle application where USB Power appears BEFORE motion, this can be set very high without missing anything: +#define SLEEP_TX_INTERVAL ( 1 * 60 * 60) // Wake up and check position every now and then to see if movement happened + +// When searching for a GPS Fix, we may never find one due to obstruction, noise, or reduced availability. +// Note that GPS Lost also counts as no-movement, so the Sleep tier above still applies +#define GPS_LOST_WAIT ( 5 * 60) // How long to wait for a GPS fix before declaring failure +#define GPS_LOST_PING (15 * 60) // Without GPS reception, how often to send a non-mapper status packet + +#define SCREEN_IDLE_OFF_S ( 2 * 60) // If there are no Uplinks or button presses sent for this long, turn the screen off. +#define MENU_TIMEOUT_S 5 // Seconds to wait before exiting the menu. + +// Below this voltage, power off until USB power allows charging. The PMIC also has a (safety) turn-off much lower than this. +// We use a conservative 3.3v here since the battery will last longer. +#define BATTERY_LOW_VOLTAGE 3.3 + +// Confirmed packets (ACK request) conflict with the function of a Mapper and should not normally be enabled. +// In areas of reduced coverage, the Mapper will try to send each packet six or more times with different SF/DR. +// This causes irregular results and the location updates are infrequent, unpredictable, and out of date. +#define LORAWAN_CONFIRMED_EVERY 0 // Request Confirmation message every N Uplinks (0 means never, 1 means always, 2 every-other-one..) + +// Spreading Factor (Data Rate) determines how long each 11-byte Mapper Uplink is on-air, and how observable it is. +// SF10 is about two seconds of transmission per packet, and the highest range, while SF7 is a good compromise +// for moving vehicles and reasonable mapping observations. +#define LORAWAN_SF DR_SF7 // Spreading factor (recommended DR_SF7 for network map purposes) // Deadzone defines a circular area where no map packets will originate. -// Set Radius to zero to disable, or leave it enabled to select center position from menu. +// This is useful to avoid sending many redundant packets in your own driveway or office, or just for local privacy. +// You can "re-center" the deadzone from the screen menu. +// Set Radius to zero to disable altogether. // (Thanks to @Woutch for the name) #ifndef DEADZONE_LAT #define DEADZONE_LAT 34.5678 @@ -60,26 +101,24 @@ along with this program. If not, see . #define DEADZONE_RADIUS_M 500 // meters #endif -// Uncomment to enable discarding network settings by long pressing second button -#define PREFS_DISCARD - -// ----------------------------------------------------------------------------- -// Version -// ----------------------------------------------------------------------------- - -#define APP_NAME "Helium TTGO" -#define APP_VERSION "1.6.5 MaxP" // ----------------------------------------------------------------------------- // Less common Configuration iteams // ----------------------------------------------------------------------------- +// Select which T-Beam board is being used. Only uncomment one. +//#define T_BEAM_V07 // AKA Rev0 (first board released) UNTESTED! Expect bugs. +#define T_BEAM_V10 // AKA Rev1 (second board released), this is the common "v1.1" + #define LOGO_DELAY 2000 // Time to show logo on first boot (ms) #define DEBUG_PORT Serial // Serial debug port #define SERIAL_BAUD 115200 // Serial debug baud rate (note that bootloader is fixed at 115200) -#define LORAWAN_ADR 0 // Do not enable ADR +// Never enable ADR on Mappers because they are moving, so we don't want to adjust +// anything based on packet reception. +#define LORAWAN_ADR 0 // Do not enable ADR + // If you are having difficulty sending messages to TTN after the first successful send, // uncomment the next option and experiment with values (~ 1 - 5) diff --git a/main/gps.cpp b/main/gps.cpp index a8c1eef..6985dd4 100644 --- a/main/gps.cpp +++ b/main/gps.cpp @@ -45,11 +45,29 @@ float gps_speed() { return _gps.speed.kmph(); } -boolean fresh_gps = false; +uint32_t gps_sentencesWithFix() { + return _gps.sentencesWithFix(); +} + +void gps_end(void) { + gpsSerial.end(); +} void gps_setup(void) { - gpsSerial.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); - gpsSerial.setRxBufferSize(2048); // Default is 256 + static boolean serial_ready = false; + if (serial_ready) { + gpsSerial.updateBaudRate(GPS_BAUDRATE); + } else { + gpsSerial.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); + gpsSerial.setRxBufferSize(2048); // Default is 256 + serial_ready = true; + } + // Drain any waiting garbage + while (gpsSerial.read() != -1) + ; + // Flush out line noise from our side + //char zeros[] = {0, 0, 0, 0, 0, 0}; + //gpsSerial.write(zeros, sizeof(zeros)); if (0) myGNSS.enableDebugging(); @@ -67,7 +85,7 @@ void gps_setup(void) { // Well, wasn't where we expected it changed_speed = true; - Serial.println("Trying 115200..."); + // Serial.println("Trying 115200..."); gpsSerial.updateBaudRate(115200); if (myGNSS.begin(gpsSerial)) { Serial.println("GPS found at 115200 baud"); @@ -75,7 +93,7 @@ void gps_setup(void) { continue; } - Serial.println("Trying 9600..."); + // Serial.println("Trying 9600..."); gpsSerial.updateBaudRate(9600); if (myGNSS.begin(gpsSerial)) { Serial.println("GPS found at 9600 baud"); @@ -83,7 +101,7 @@ void gps_setup(void) { continue; } - Serial.println("Trying 38400..."); + // Serial.println("Trying 38400..."); gpsSerial.updateBaudRate(38400); if (myGNSS.begin(gpsSerial)) { Serial.println("GPS found at 38400 baud"); @@ -91,7 +109,7 @@ void gps_setup(void) { continue; } - Serial.println("Trying 57600..."); + // Serial.println("Trying 57600..."); gpsSerial.updateBaudRate(57600); if (myGNSS.begin(gpsSerial)) { Serial.println("GPS found at 57600 baud"); @@ -128,11 +146,11 @@ void gps_setup(void) { myGNSS.disableNMEAMessage(UBX_NMEA_VLW, COM_PORT_UART1); myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_UART1); myGNSS.disableNMEAMessage(UBX_NMEA_ZDA, COM_PORT_UART1); -#endif +#endif - myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1); // Don't need SV list (on by default) - myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1); // For Speed - myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); // For Time & Location & SV count + myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1); // Don't need SV list (on by default) + myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1); // For Speed + myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); // For Time & Location & SV count if (changed_speed) myGNSS.saveConfiguration(); // Save the current settings to flash and BBR @@ -148,8 +166,11 @@ void gps_passthrough(void) { } } -void gps_loop(void) { +void gps_loop(boolean print_it) { while (gpsSerial.available()) { - _gps.encode(gpsSerial.read()); + char c = gpsSerial.read(); + if (print_it) + Serial.print(c); + _gps.encode(c); } } \ No newline at end of file diff --git a/main/gps.h b/main/gps.h index d64e09f..c2cf518 100644 --- a/main/gps.h +++ b/main/gps.h @@ -3,7 +3,7 @@ #include -void gps_loop(void); +void gps_loop(boolean print_it); void gps_setup(void); void gps_time(char *buffer, uint8_t size); float gps_latitude(void); @@ -15,4 +15,5 @@ uint8_t gps_sats(void); float gps_hdop(void); float gps_speed(void); void gps_passthrough(void); - +uint32_t gps_sentencesWithFix(void); +void gps_end(void); diff --git a/main/screen.cpp b/main/screen.cpp index da581db..37936dc 100644 --- a/main/screen.cpp +++ b/main/screen.cpp @@ -114,7 +114,7 @@ void screen_setup() { extern AXP20X_Class axp; // TODO: This is evil void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, int sats, - boolean in_deadzone, boolean stay_on) { + boolean in_deadzone, boolean stay_on, boolean never_rest) { if (!display) return; @@ -153,8 +153,8 @@ void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cache SATELLITE_IMAGE); // Second status row: - snprintf(buffer, sizeof(buffer), "%us %.0fm %c%c", tx_interval_s, min_dist_moved, in_deadzone ? 'D' : ' ', - stay_on ? 'S' : ' '); + snprintf(buffer, sizeof(buffer), "%us %.0fm %c%c%c", tx_interval_s, min_dist_moved, in_deadzone ? 'D' : ' ', + stay_on ? 'S' : ' ', never_rest ? 'N' : ' '); display->setTextAlignment(TEXT_ALIGN_LEFT); display->drawString(0, 12, buffer); diff --git a/main/screen.h b/main/screen.h index c492c2c..3f4eff7 100644 --- a/main/screen.h +++ b/main/screen.h @@ -10,7 +10,7 @@ void screen_update(void); void screen_body(boolean in_menu, const char *menu_prev, const char *menu_cur, const char *menu_next, boolean highlighted); void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, int sats, - boolean in_deadzone, boolean stay_on); + boolean in_deadzone, boolean stay_on, boolean never_rest); void screen_off(void); void screen_on(void);