-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
gps: fall back to generic NMEA when vendor probes go unanswered #11407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| { | ||
| "build": { | ||
| "arduino": { | ||
| "ldscript": "nrf52840_s140_v6.ld" | ||
| }, | ||
| "core": "nRF5", | ||
| "cpu": "cortex-m4", | ||
| "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", | ||
| "f_cpu": "64000000L", | ||
| "hwids": [ | ||
| ["0x239A", "0x8029"], | ||
| ["0x239A", "0x0029"], | ||
| ["0x239A", "0x002A"], | ||
| ["0x239A", "0x802A"] | ||
| ], | ||
| "usb_product": "GAT562 Mesh Tracker Pro", | ||
| "mcu": "nrf52840", | ||
| "variant": "gat562_mesh_tracker_pro", | ||
| "bsp": { | ||
| "name": "adafruit" | ||
| }, | ||
| "softdevice": { | ||
| "sd_flags": "-DS140", | ||
| "sd_name": "s140", | ||
| "sd_version": "6.1.1", | ||
| "sd_fwid": "0x00B6" | ||
| }, | ||
| "bootloader": { | ||
| "settings_addr": "0xFF000" | ||
| } | ||
| }, | ||
| "connectivity": ["bluetooth"], | ||
| "debug": { | ||
| "jlink_device": "nRF52840_xxAA", | ||
| "svd_path": "nrf52840.svd", | ||
| "openocd_target": "nrf52840-mdk-rs" | ||
| }, | ||
| "frameworks": ["arduino", "freertos"], | ||
| "name": "GAT562 Mesh Tracker Pro", | ||
| "upload": { | ||
| "maximum_ram_size": 248832, | ||
| "maximum_size": 815104, | ||
| "speed": 115200, | ||
| "protocol": "nrfutil", | ||
| "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"], | ||
| "use_1200bps_touch": true, | ||
| "require_upload_port": true, | ||
| "wait_for_upload_port": true | ||
| }, | ||
| "url": "http://www.gat-iot.com/", | ||
| "vendor": "GAT" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -71,6 +71,20 @@ static struct uBloxGnssModelInfo { | |||||||||||||||||||||||||||||||||||
| #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway | ||||||||||||||||||||||||||||||||||||
| #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // How long to listen for a streaming NMEA GNSS before giving up on the current baud rate | ||||||||||||||||||||||||||||||||||||
| #define GPS_NMEA_PROBE_TIMEOUT_MS 500 | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // True if the 5 chars following '$' identify a common NMEA GNSS sentence (e.g. GNGGA, GPRMC, GLGSV). | ||||||||||||||||||||||||||||||||||||
| // The first char is always 'G' for GNSS talkers (GP, GL, GA, GB, GN, BD, QZ, ...). | ||||||||||||||||||||||||||||||||||||
| static bool isKnownNMEASentence(const char *id) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| if (id[0] != 'G') | ||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||
| return strncmp(id + 2, "RMC", 3) == 0 || strncmp(id + 2, "GGA", 3) == 0 || strncmp(id + 2, "GSV", 3) == 0 || | ||||||||||||||||||||||||||||||||||||
| strncmp(id + 2, "GSA", 3) == 0 || strncmp(id + 2, "VTG", 3) == 0 || strncmp(id + 2, "GLL", 3) == 0 || | ||||||||||||||||||||||||||||||||||||
| strncmp(id + 2, "ZDA", 3) == 0; | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+79
to
+85
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Accept the non-
Proposed fix- if (id[0] != 'G')
+ const bool isKnownTalker = id[0] == 'G' || (id[0] == 'B' && id[1] == 'D') ||
+ (id[0] == 'Q' && id[1] == 'Z');
+ if (!isKnownTalker)
return false;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // For logging | ||||||||||||||||||||||||||||||||||||
| static const char *getGPSPowerStateString(GPSPowerState state) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -814,6 +828,11 @@ bool GPS::setup() | |||||||||||||||||||||||||||||||||||
| // enable RMC | ||||||||||||||||||||||||||||||||||||
| _serial_gps->write("$CFGMSG,0,4,1,1*1F\r\n"); | ||||||||||||||||||||||||||||||||||||
| delay(250); | ||||||||||||||||||||||||||||||||||||
| } else if (gnssModel == GNSS_MODEL_GENERIC_NMEA) { | ||||||||||||||||||||||||||||||||||||
| // A module that auto-streams standard NMEA but ignores vendor probe commands (e.g. the GAT562's | ||||||||||||||||||||||||||||||||||||
| // L76K in its default 9600 multi-GNSS mode). No chip-specific init is possible or needed: the | ||||||||||||||||||||||||||||||||||||
| // streaming sentences are fed straight to the TinyGPS++ parser. | ||||||||||||||||||||||||||||||||||||
| LOG_INFO("GNSS: using generic NMEA stream, skipping chip-specific init"); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| didSerialInit = true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1388,6 +1407,12 @@ GnssModel_t GPS::probe(int serialSpeed) | |||||||||||||||||||||||||||||||||||
| // Check that the returned response class and message ID are correct | ||||||||||||||||||||||||||||||||||||
| GPS_RESPONSE response = getACK(0x06, 0x08, 750); | ||||||||||||||||||||||||||||||||||||
| if (response == GNSS_RESPONSE_NONE) { | ||||||||||||||||||||||||||||||||||||
| if (probeForNMEA(GPS_NMEA_PROBE_TIMEOUT_MS)) { | ||||||||||||||||||||||||||||||||||||
| LOG_INFO("GNSS: no probe response, but NMEA stream detected at %d baud - using generic NMEA driver", | ||||||||||||||||||||||||||||||||||||
| serialSpeed); | ||||||||||||||||||||||||||||||||||||
| currentStep = 0; | ||||||||||||||||||||||||||||||||||||
| return GNSS_MODEL_GENERIC_NMEA; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1410
to
+1415
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- GPS.cpp relevant ranges ---'
sed -n '1280,1430p' src/gps/GPS.cpp
sed -n '1470,1610p' src/gps/GPS.cpp
printf '%s\n' '--- related declarations and constants ---'
rg -n -C 4 'GPS_NMEA_PROBE_TIMEOUT_MS|probeForNMEA|PCAS03|isKnownNMEASentence|GENERIC_NMEA|serialSpeed' src/gps/GPS.cpp src/gps
printf '%s\n' '--- repository status summary ---'
git diff --statRepository: meshtastic/firmware Length of output: 36658 🌐 Web query:
💡 Result: According to the Quectel L76K GNSS Protocol Specification, the $PCAS03 command is used to configure the output type and frequency of NMEA sentences [1][2][3]. Regarding your query about the "0" output rate: - Setting a parameter to "0" for any NMEA sentence (GGA, GLL, GSA, GSV, RMC, VTG, ZDA, or ANT) in the $PCAS03 command explicitly disables the output of that specific sentence [1][4][3]. - If you set a field to "0", the module will no longer output the corresponding NMEA sentence [1][2]. - Leaving a field empty (e.g., $PCAS03,1,,,,,,,,,0,002) instead of providing a value generally indicates that the previous configuration for that specific sentence should be retained [1][4]. For example, if you send $PCAS03,0,0,0,0,0,0,0,0,0,0,,,,0,0xx, you would effectively disable all primary NMEA sentence outputs listed in the command structure [1][3]. If you are experiencing "no output" after configuring the device, verify that you have not inadvertently set all sentence frequencies to 0, or that you have not misconfigured the serial port baud rate using the $PCAS01 command [5]. If the module is not outputting any data at all, ensure the hardware connection is correct and check the baud rate settings, as the PCAS03 command only controls the NMEA sentence frequency, not the physical serial port transmission itself [5]. Citations:
🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- probe call/state flow ---'
sed -n '500,545p' src/gps/GPS.cpp
sed -n '1385,1515p' src/gps/GPS.cpp
printf '%s\n' '--- PCAS03 references ---'
rg -n -C 3 '\$PCAS03|PCAS03' .
printf '%s\n' '--- NMEA predicate exact source ---'
sed -n '68,95p' src/gps/GPS.cppRepository: meshtastic/firmware Length of output: 10022 Probe for generic NMEA before disabling output. If a L76K accepts the all-zero 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); | ||||||||||||||||||||||||||||||||||||
| currentDelay = 2000; | ||||||||||||||||||||||||||||||||||||
| currentStep = 0; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1476,6 +1501,11 @@ GnssModel_t GPS::probe(int serialSpeed) | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| if (probeForNMEA(GPS_NMEA_PROBE_TIMEOUT_MS)) { | ||||||||||||||||||||||||||||||||||||
| LOG_INFO("GNSS: no probe response, but NMEA stream detected at %d baud - using generic NMEA driver", serialSpeed); | ||||||||||||||||||||||||||||||||||||
| currentStep = 0; | ||||||||||||||||||||||||||||||||||||
| return GNSS_MODEL_GENERIC_NMEA; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); | ||||||||||||||||||||||||||||||||||||
| currentDelay = 2000; | ||||||||||||||||||||||||||||||||||||
| currentStep = 0; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1534,6 +1564,35 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI | |||||||||||||||||||||||||||||||||||
| return GNSS_MODEL_UNKNOWN; // Return unknown on timeout | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Cheap listen for a live NMEA GNSS. Some modules (e.g. the GAT562's L76K in default 9600 multi-GNSS mode) | ||||||||||||||||||||||||||||||||||||
| // auto-stream standard NMEA sentences but never answer vendor probe commands. We look for a known sentence | ||||||||||||||||||||||||||||||||||||
| // type ($GNGGA, $GPRMC, $GPGSV, ...) within the listen window and treat that as a valid GNSS. | ||||||||||||||||||||||||||||||||||||
| bool GPS::probeForNMEA(unsigned long timeout) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| clearBuffer(); | ||||||||||||||||||||||||||||||||||||
| unsigned long start = millis(); | ||||||||||||||||||||||||||||||||||||
| char sentence[8] = {0}; | ||||||||||||||||||||||||||||||||||||
| uint8_t idx = 0; | ||||||||||||||||||||||||||||||||||||
| while (millis() - start < timeout) { | ||||||||||||||||||||||||||||||||||||
| if (_serial_gps->available()) { | ||||||||||||||||||||||||||||||||||||
| char c = _serial_gps->read(); | ||||||||||||||||||||||||||||||||||||
| if (c == '$') { | ||||||||||||||||||||||||||||||||||||
| idx = 0; | ||||||||||||||||||||||||||||||||||||
| memset(sentence, 0, sizeof(sentence)); | ||||||||||||||||||||||||||||||||||||
| } else if (idx < 5) { | ||||||||||||||||||||||||||||||||||||
| sentence[idx++] = c; | ||||||||||||||||||||||||||||||||||||
| if (idx == 5 && isKnownNMEASentence(sentence)) { | ||||||||||||||||||||||||||||||||||||
| #ifdef GPS_DEBUG | ||||||||||||||||||||||||||||||||||||
| LOG_DEBUG("NMEA stream detected: %s", sentence); | ||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| std::unique_ptr<GPS> GPS::createGps() | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| int8_t _rx_gpio = config.position.rx_gpio; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| ; GAT562 Mesh Tracker Pro with Trackball support | ||
| [env:gat562_mesh_tracker_pro] | ||
| extends = nrf52840_base | ||
| board = gat562_mesh_tracker_pro | ||
| board_level = extra | ||
| build_flags = ${nrf52840_base.build_flags} | ||
| -I variants/nrf52840/gat562_mesh_tracker_pro | ||
| -D GAT562_MESH_TRACKER_PRO | ||
| -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. | ||
| -DRADIOLIB_EXCLUDE_SX128X=1 | ||
| -DRADIOLIB_EXCLUDE_SX127X=1 | ||
| -DRADIOLIB_EXCLUDE_LR11X0=1 | ||
| build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/gat562_mesh_tracker_pro> | ||
| lib_deps = | ||
| ${nrf52840_base.lib_deps} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* | ||
| Copyright (c) 2014-2015 Arduino LLC. All right reserved. | ||
| Copyright (c) 2016 Sandeep Mistry All right reserved. | ||
| Copyright (c) 2018, Adafruit Industries (adafruit.com) | ||
|
|
||
| This library is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU Lesser General Public | ||
| License as published by the Free Software Foundation; either | ||
| version 2.1 of the License, or (at your option) any later version. | ||
|
|
||
| This library is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| See the GNU Lesser General Public License for more details. | ||
|
|
||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with this library; if not, write to the Free Software | ||
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| */ | ||
|
|
||
| #include "variant.h" | ||
| #include "nrf.h" | ||
| #include "wiring_constants.h" | ||
| #include "wiring_digital.h" | ||
|
|
||
| const uint32_t g_ADigitalPinMap[] = { | ||
| // P0 | ||
| 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | ||
|
|
||
| // P1 | ||
| 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; | ||
|
|
||
| void initVariant() | ||
| { | ||
| // LED1 & LED2 | ||
| pinMode(PIN_LED1, OUTPUT); | ||
| ledOff(PIN_LED1); | ||
|
|
||
| pinMode(PIN_LED2, OUTPUT); | ||
| ledOff(PIN_LED2); | ||
|
|
||
| // 3V3 Power Rail | ||
| pinMode(PIN_3V3_EN, OUTPUT); | ||
| digitalWrite(PIN_3V3_EN, HIGH); | ||
|
|
||
| // Initialize trackball pins as inputs with pullup | ||
| #ifdef HAS_TRACKBALL | ||
| pinMode(TB_UP, INPUT_PULLUP); | ||
| pinMode(TB_DOWN, INPUT_PULLUP); | ||
| pinMode(TB_LEFT, INPUT_PULLUP); | ||
| pinMode(TB_RIGHT, INPUT_PULLUP); | ||
| pinMode(TB_PRESS, INPUT_PULLUP); | ||
| #endif | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: meshtastic/firmware
Length of output: 50375
🏁 Script executed:
Repository: meshtastic/firmware
Length of output: 30262
🌐 Web query:
Quectel L76K protocol specification NMEA output interval 1 Hz PCAS03💡 Result:
In the Quectel L76K GNSS module, the NMEA output interval (positioning frequency) is configured using the PCAS02 command, not PCAS03 [1][2]. The PCAS03 command is specifically used to configure the output type and frequency (rate) of individual NMEA sentences [1][3]. PCAS02 Command (Positioning Frequency) To set the positioning frequency, use the $PCAS02 command [2]. The interval is specified in milliseconds [1]: - 1000 = 1 Hz - 500 = 2 Hz - 200 = 5 Hz Example: To set the positioning frequency to 1 Hz, use $PCAS02,1000*XX (where XX is the checksum). PCAS03 Command (NMEA Sentence Output) The PCAS03 command configures which NMEA sentences are output and how often (every Nth fix) they appear [1][4]. Its format is: $PCAS03,,,,,,,,,,,,,,*CS Parameters for PCAS03: - through : Define the output frequency for each sentence type. Setting these to 1 means outputting the sentence every fix; 0 means no output [1][5]. - Values 1–9 indicate the sentence will be output once every N position fixes [1][4]. - Leaving a field blank (null) keeps the previous configuration [1][3]. Note: When setting a positioning frequency higher than 1 Hz (i.e., interval < 1000), it is recommended to increase the serial baud rate to 115200 bps to ensure stable data transmission [6].
Citations:
🏁 Script executed:
Repository: meshtastic/firmware
Length of output: 350
Wait for at least one complete NMEA interval. When the L76K emits sentences at 1 Hz, a 500 ms window can expire before the next sentence arrives, causing the
No GNSS Modulepath. SetGPS_NMEA_PROBE_TIMEOUT_MSabove 1000 ms.🤖 Prompt for AI Agents