diff --git a/.github/workflows/compile-sketch.yml b/.github/workflows/compile-sketch.yml
index 8a225b7..dea8578 100644
--- a/.github/workflows/compile-sketch.yml
+++ b/.github/workflows/compile-sketch.yml
@@ -99,15 +99,6 @@ jobs:
libraries: |
- source-path: ./
sketch-paths: |
- - examples/Example10_AltitudeMSL
- - examples/Example11_ResetModule/Example1_FactoryDefaultviaI2C
- - examples/Example13_PVT/Example1_AutoPVT
- - examples/Example13_PVT/Example2_AutoPVT_ExplicitUpdate
- - examples/Example14_DebugOutput
- - examples/Example15_GetDateTime
- - examples/Example16_Nanosecond_MaxOutput
- - examples/Example18_PowerSaveMode
- - examples/Example19_DynamicModel
- examples/Example20_SendCustomCommand
enable-warnings-report: true
enable-deltas-report: true
diff --git a/Adding_New_Messages.md b/Adding_New_Messages.md
new file mode 100644
index 0000000..e5c6b98
--- /dev/null
+++ b/Adding_New_Messages.md
@@ -0,0 +1,166 @@
+## How to add new messages to the SparkFun u-blox GNSS Arduino Library
+
+Based on [this issue](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues/97), here is a summary of how to add new messages to the SparkFun u-blox GNSS Arduino Library with full "auto" support (for callbacks, logging, etc.).
+
+Looking at the issue, we see that the library is not supporting the UBX-NAV-PVAT (Navigation Position Velocity Attitude Time solution).
+PVAT is a new message added in version 1.21 of the HPS (High Precision Fusion) firmware and version 33.21 of the F9 Interface Description.
+This makes us wonder if more new messages have been added which should also be included?
+
+### Step 1: Check the Interface Description for new keys
+
+* Download the latest [interface description](https://www.u-blox.com/sites/default/files/F9-HPS-1.21_InterfaceDescription_UBX-21019746.pdf) from the [u-blox website](https://www.u-blox.com/en/product/zed-f9r-module#tab-documentation-resources)
+* Open the interface description in Adobe Acrobat Reader DC (the free version)
+* Do a ```File \ Save as Text...```
+* Save the file in ```Text (Accessible) (*.txt)``` format
+* Go make a cup of tea - this takes a while
+* Open the txt file in Notepad++ or another editor which supports Regular Expressions
+* The keys will have been saved as individual lines in the format: 0xnnnnnnnn space CR LF
+* So all we need to do is use a regex to delete everything else
+* Open Search \ Replace
+* Click the Search Mode - Regular Expression button
+* In the "Find what :" box enter: ```^(?!.*0x[\dabcdefABCDEF]{8}\s\r\n).*```
+* Clear the "Replace with :" box
+* Click "Replace All"
+* You are left with just the keys - and a bunch of empty lines, some of which contain form feeds (\f)
+* Delete the empty lines (\r\n) by replacing \r\n with nothing - don't panic, this takes a few seconds
+* Delete the form feeds by replacing \f with nothing
+* Finally replace the remaining spaces (\s) with \r\n
+* Delete any spurious lines left at the start of the file. E.g. ROM and BASE and 0x118B2060. These came from the General information section
+* The following line (0x10340014) is the first key from the "Configuration Reference" section
+* Search for that key number and you will find it again half way through the file. This second copy came from "Configuration Defaults"
+* Delete the duplicate keys from that line onwards
+* Save the file
+* Open it in a spreadsheet, e.g. LibreOffice Calc
+* Select the "A" column and click "Sort Ascending A-Z"
+* Save the file (as Text CSV)
+* Use KDiff3 or another diff package to see the new additions
+
+You can find the keys in the [keys folder](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/keys), saved as sorted text files.
+There are separate files for the P, R and T interfaces, plus a combined list (also sorted in ascending order).
+
+Comparing HPS 1.21 to HPS 1.20, we can see that the following keys have been added:
+
+* 0x10340014 CFG-BDS-USE_GEO_PRN
+* 0x10710005 CFG-I2CINPROT-SPARTN
+* 0x10730005 CFG-UART1INPROT-SPARTN
+* 0x10750005 CFG-UART2INPROT-SPARTN
+* 0x10770005 CFG-USBINPROT-SPARTN
+* 0x10790005 CFG-SPIINPROT-SPARTN
+* 0x20050035 CFG-TP-DRSTR_TP1
+* 0x20910605 CFG-MSGOUT-UBX_RXM_SPARTN_I2C
+* 0x20910606 CFG-MSGOUT-UBX_RXM_SPARTN_UART1
+* 0x20910607 CFG-MSGOUT-UBX_RXM_SPARTN_UART2
+* 0x20910608 CFG-MSGOUT-UBX_RXM_SPARTN_USB
+* 0x20910609 CFG-MSGOUT-UBX_RXM_SPARTN_SPI
+* 0x2091062a CFG-MSGOUT-UBX_NAV_PVAT_I2C
+* 0x2091062b CFG-MSGOUT-UBX_NAV_PVAT_UART1
+* 0x2091062c CFG-MSGOUT-UBX_NAV_PVAT_UART2
+* 0x2091062d CFG-MSGOUT-UBX_NAV_PVAT_USB
+* 0x2091062e CFG-MSGOUT-UBX_NAV_PVAT_SPI
+* 0x20910634 CFG-MSGOUT-UBX_SEC_SIG_I2C
+* 0x20910635 CFG-MSGOUT-UBX_SEC_SIG_UART1
+* 0x20910636 CFG-MSGOUT-UBX_SEC_SIG_UART2
+* 0x20910637 CFG-MSGOUT-UBX_SEC_SIG_USB
+* 0x20910638 CFG-MSGOUT-UBX_SEC_SIG_SPI
+
+Interestingly, we can also see that one key has been deleted:
+
+* 0x10530006 CFG-UART2-REMAP
+
+From this we can confirm - as documented by u-blox in the [Release Notes](https://www.u-blox.com/sites/default/files/ZED-F9R-02B_FW1.00HPS1.21_RN_UBX-21035491_1.3.pdf) -
+that HPS 1.21:
+
+* adds support for SPARTN (Safe Position Augmentation for Real-Time Navigation) correction messages
+* enables the use of BeiDou geostationary satellites (previously, this configuration item had a different name)
+* enables UBX-SEC-SIG message (signal security measures) as output across the different interfaces
+* enables UBX_NAV_PVAT message (navigation and altitude position) as output across the different interfaces
+
+There are also two new dynamic models, robotic lawn mower (11) and e-scooter model (12), which we need to add to the library.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/805aab18b6656513bfee473487a437754cd3965d) for the changes.
+
+### Step 2: Update the combined keys file
+
+Update [u-blox_config_keys_sorted.txt](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/keys/u-blox_config_keys_sorted.txt)
+to include the new keys.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/8895764f237ae494dcd0fa1ae942d487d2e1557f) for the changes.
+
+### Step 3: Update u-blox_config_keys.h
+
+Update [u-blox_config_keys.h](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/src/u-blox_config_keys.h) to include the new keys.
+Include the descriptions as defined in the Interface Description.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/3609da15f90a7a66b41524e77c6dc3dd76cd362c) for the changes.
+
+### Step 4: Add the new message struct to u-blox_struct.h
+
+The next step is to add the new struct for UBX-NAV-PVAT to u-blox_struct.h.
+
+The messages are in ascending class and ID order. So we add UBX-NAV-PVAT (0x01 0x17) after UBX-NAV-HPPOSLLH (0x01 0x14).
+
+The names and widths of the fields are taken directly from the interface definition.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/a4ba440c6240e0974c27f40b976a5ddf0fbdb9b6) for the changes.
+
+### Step 5: Update SparkFun_u-blox_GNSS_Arduino_Library.h
+
+Add the new message ID: ```const uint8_t UBX_NAV_PVAT = 0x17;```
+
+Add the new functions to provide "auto" support for UBX-NAV-PVAT: ```getNAVPVAT```, ```setAutoNAVPVAT```, ..., ```logNAVPVAT```
+
+Add new helper functions to access the most important fields: ```getVehicleRoll```, ..., ```getMotionHeading```
+
+Add the pointer to the struct storage: ```UBX_NAV_PVAT_t *packetUBXNAVPVAT = NULL;```
+
+Add the private init function: ```bool initPacketUBXNAVPVAT();```
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/423a1e2ccd418dd679257edc6edeec0bd3029052) for the changes.
+
+### Step 6: Update SparkFun_u-blox_GNSS_Arduino_Library.cpp
+
+Now we need to update SparkFun_u-blox_GNSS_Arduino_Library.cpp:
+
+#### Step 6.1: Update end()
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/35d225e3f1abb316eda3becb7f8e2eb04ff1d17c) for the changes.
+
+#### Step 6.2: Update checkAutomatic()
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/b746d8e2742961ede95e2d06d5db3a3a557e571d) for the changes.
+
+#### Step 6.3: Update processUBXpacket()
+
+Take time to double-check that you have used the correct data width, signed/unsigned and position for each field.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/8eecdd5044f810b0e2b567150ff63a17c219fe8e) for the changes.
+
+#### Step 6.4: Update checkCallbacks()
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/b53bffaa3ae12482cfb268f23796963d0b8519c9) for the changes.
+
+#### Step 6.5: Add the "auto" functions
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/e394ae003ad38117d150598774d0552059416473) for the changes.
+
+#### Step 6.6: Add the helper functions (if any)
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/318e76383e96d6676bbb57294c25e665c0d4a31f) for the changes.
+
+### Step 7: Add an example
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/06014dc95f1b9ffae4876fbacfb9390541d7c31d) for the changes.
+
+### Step 8: Update keywords.txt
+
+Add the new "auto" and helper functions to keywords.txt.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/4f0a0ca3c5e6420be9064b91702947c23104bd1b) for the changes.
+
+### Step 9: Update Theory.md
+
+Add the new message to the list of "auto" messages.
+
+See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/57f133259245d8071c73797e4be2ff630c2720ab) for the changes.
+
+That's all folks!
\ No newline at end of file
diff --git a/README.md b/README.md
index 98a4206..2a64007 100644
--- a/README.md
+++ b/README.md
@@ -21,12 +21,31 @@ u-blox makes some incredible GNSS receivers covering everything from low-cost, h
This library can be installed via the Arduino Library manager. Search for **SparkFun u-blox GNSS**.
-## v2.0
+## Automatic support for correction services like RTK2go, Emlid Caster and Skylark (Swift Navigation)
+
+RTK NTRIP corrections services often require you to send them your location in NMEA GPGGA format. v2.2 of the library makes this easy by providing get functions and automatic callbacks
+for both GPGGA and GNGGA messages. You can now instruct your module to output GPGGA (e.g.) every 10 seconds and then push it to the correction server directly from the callback. No more polling, no more parsing!
+
+v2.2 also includes two new functions useful for correction services:
+
+* ```setMainTalkerID``` : lets you change the NMEA Talker ID (prefix) from "GN" to "GP" - just in case your correction service really does need GPGGA, not GNGGA
+* ```setHighPrecisionMode``` : adds extra decimal places in the GGA messages, increasing the resolution of latitude, longitude and altitude
+
+Please see the new [Automatic_NMEA examples](./examples/Automatic_NMEA) for more details.
+
+We've also added a new [NTRIP Caster Client example](./examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback) showing how to use these new features to full effect.
+
+## AssistNowTM
+
+v2.1 of the library adds support for u-blox AssistNowTM Assisted GNSS (A-GNSS) which can dramatically reduce the time-to-first-fix. You can find further details in the [AssistNow Examples folder](./examples/AssistNow).
+
+## v2 vs. v1
This library is the new and improved version of the very popular SparkFun u-blox GNSS Arduino Library. v2.0 contains some big changes and improvements:
* Seamless support for "automatic" message delivery:
- * In v1.8, you could ask for the NAV PVT (Navigation Position Velocity Time) message to be delivered _automatically_, without polling. v2.0 adds automatic support for [**23 messages**](./Theory.md#auto-messages), covering the full range of: standard and High Precision position, velocity and time information; relative positioning; event capture with nanosecond time resolution; raw GNSS signal data including carrier phase; Sensor Fusion; and High Navigation Rate data.
+ * In v1.8, you could ask for the NAV PVT (Navigation Position Velocity Time) message to be delivered _automatically_, without polling. v2.0 adds automatic support for [**26 messages**](./Theory.md#auto-messages), covering the full range of: standard and High Precision position, velocity, attitude and time information; relative positioning; event capture with nanosecond time resolution; raw GNSS signal data including carrier phase; Sensor Fusion; and High Navigation Rate data.
+ * Don't see the message you really need? [Adding_New_Messages](./Adding_New_Messages.md) provides details on how to add "auto" support for your favourite message.
* Dynamic memory allocation with clearly-defined data storage structs for each message:
* There are no static 'global' variables to eat up your RAM. v2.0 automatically allocates memory for the automatic messages when they are enabled. You may find your total RAM use is lower with v2.0 than with v1.8.
* Each "auto" message has a clearly-defined [data storage struct](./src/u-blox_structs.h) which follows the u-blox protocol specification precisely.
@@ -53,9 +72,7 @@ Migrating to v2.0 is easy. There are two small changes all users will need to ma
If you are using the Dead Reckoning Sensor Fusion or High Dynamic Rate messages, you will need to make more small changes to your code. Please see the [dead reckoning examples](./examples/Dead_Reckoning) for more details. There is more detail available in [Theory.md](./Theory.md#migrating-your-code-to-v20) if you need it.
-## AssistNowTM
-
-v2.1.0 of the library adds support for u-blox AssistNowTM Assisted GNSS (A-GNSS) which can dramatically reduce the time-to-first-fix. You can find further details in the [AssistNow Examples folder](./examples/AssistNow).
+There is a [new example](./examples/Dead_Reckoning/Example8_getNAVPVAT) showing how to read the UBX-NAV-PVAT (Position, Velocity, Attitude, Time) with a single function call. UBX-NAV-PVAT has full "auto" callback and data-logging support too!
## Memory Usage
@@ -83,9 +100,9 @@ The SPI examples have their [own folder](./examples/SPI).
Please check the module datasheets for details on what clock speeds and data rates each module supports. The maximum clock speed is typically 5.5MHz and the maximum transfer rate is typically 125kBytes/s.
-## Max (400kHz) I2C Support
+## I2C Support
-To achieve 400kHz I2C speed please be sure to remove all pull-ups on the I2C bus. Most, if not all, u-blox modules include internal pull ups on the I2C lines (sometimes called DDC in their manuals). Cut all I2C pull up jumpers and/or remove them from peripheral boards. Otherwise, various data glitches can occur. See issues [38](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/38) and [40](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40) for more information. If possible, run the I2C bus at 100kHz.
+For I2C communication, please be sure to remove all additional pull-ups on the I2C bus. u-blox modules include internal pull-ups on the I2C lines (sometimes called DDC in their manuals). Cut all I2C pull-up jumpers and/or remove them from peripheral boards. Otherwise, various data glitches can occur. See issues [38](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/38) and [40](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40) for more information. We recommend running the I2C bus at 100kHz.
## Contributing
diff --git a/Theory.md b/Theory.md
index 5b67bc8..077048d 100644
--- a/Theory.md
+++ b/Theory.md
@@ -54,6 +54,7 @@ In v2.0, the full list of messages which can be processed and logged automatical
- UBX-NAV-VELNED (0x01 0x12): Velocity solution in NED frame
- UBX-NAV-HPPOSECEF (0x01 0x13): High precision position solution in ECEF
- UBX-NAV-HPPOSLLH (0x01 0x14): High precision geodetic position solution
+- UBX-NAV-PVAT (0x01 0x17): Navigation position velocity attitude time solution (**only with ADR or UDR products**)
- UBX-NAV-CLOCK (0x01 0x22): Clock solution
- UBX-NAV-SVIN (0x01 0x3B): Survey-in data (**only with High Precision GNSS products**)
- UBX-NAV-RELPOSNED (0x01 0x3C): Relative positioning information in NED frame (**only with High Precision GNSS products**)
@@ -70,6 +71,8 @@ In v2.0, the full list of messages which can be processed and logged automatical
- UBX-HNR-ATT (0x28 0x01): Attitude solution (**only with ADR or UDR products**)
- UBX-HNR-INS (0x28 0x02): Vehicle dynamics information (**only with ADR or UDR products**)
+Please see [Adding_New_Messages](./Adding_New_Messages.md) for details on how to add "auto" support for new messages.
+
Notes:
- UBX-NAV-POSLLH is not supported as UBX-NAV-PVT contains the same information
- UBX-NAV-TIMEUTC is not supported as UBX-NAV-PVT contains the same information
diff --git a/examples/Automatic_NMEA/Example1_getLatestNMEAGPGGA/Example1_getLatestNMEAGPGGA.ino b/examples/Automatic_NMEA/Example1_getLatestNMEAGPGGA/Example1_getLatestNMEAGPGGA.ino
new file mode 100644
index 0000000..0e66a56
--- /dev/null
+++ b/examples/Automatic_NMEA/Example1_getLatestNMEAGPGGA/Example1_getLatestNMEAGPGGA.ino
@@ -0,0 +1,123 @@
+/*
+ Get the GPGGA NMEA sentence using getLatestNMEAGPGGA
+ By: Paul Clark
+ SparkFun Electronics
+ Date: January 12th, 2021
+ License: MIT. See license file for more information but you can
+ basically do whatever you want with this code.
+
+ This example shows how to turn on/off the NMEA sentences being output over I2C.
+ It then demonstrates how to use the new getLatestNMEAGPGGA function to retrieve the latest GPGGA message.
+ getLatestNMEAGPGGA returns immediately - it is not blocking.
+ It returns:
+ 0 if no data is available
+ 1 if the data is valid but is stale (you have read it before)
+ 2 if the data is valid and fresh
+
+ If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
+ The library includes a getLatestNMEAGNGGA function too.
+ This example shows how to use both functions - and how to change the Talker ID so the GNGGA messages become GPGGA.
+
+ This example turns off all sentences except for GGA.
+
+ Feel like supporting open source hardware?
+ Buy a board from SparkFun!
+ ZED-F9P RTK2: https://www.sparkfun.com/products/15136
+ NEO-M8P RTK: https://www.sparkfun.com/products/15005
+ SAM-M8Q: https://www.sparkfun.com/products/15106
+
+ Hardware Connections:
+ Plug a Qwiic cable into the GNSS and a RedBoard
+ If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
+ Open the serial monitor at 115200 baud to see the output
+*/
+
+#include //Needed for I2C to GNSS
+
+#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
+SFE_UBLOX_GNSS myGNSS;
+
+void setup()
+{
+
+ Serial.begin(115200);
+ Serial.println(F("SparkFun u-blox GNSS Example"));
+
+ Wire.begin();
+
+ //myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
+
+ if (myGNSS.begin() == false)
+ {
+ Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
+ while (1)
+ ;
+ }
+
+ //Disable or enable various NMEA sentences over the I2C interface
+ myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
+ myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C); // Several of these are on by default on ublox board so let's disable them
+ myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
+ myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); // Leave only GGA enabled at current navigation rate
+
+ // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
+ myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
+ //myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
+
+ myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
+
+ //myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
+
+ Serial.println(F("Messages configured"));
+
+ //myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
+}
+
+void loop()
+{
+ // getLatestNMEAGPGGA calls checkUblox for us. We don't need to do it here
+
+ NMEA_GGA_data_t data; // Storage for the GPGGA data
+ uint8_t result = myGNSS.getLatestNMEAGPGGA(&data); // Get the latest GPGGA data (if any)
+
+ if (result == 0)
+ {
+ Serial.println(F("No GPGGA data available"));
+ }
+ else if (result == 1)
+ {
+ Serial.println(F("GPGGA data is available but is stale"));
+ }
+ else // if (result == 2)
+ {
+ // Data contains .length and .nmea
+ Serial.print(F("Latest GPGGA: Length: "));
+ Serial.print(data.length);
+ Serial.print(F("\tData: "));
+ Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
+ }
+
+ result = myGNSS.getLatestNMEAGNGGA(&data); // Get the latest GNGGA data (if any)
+
+ if (result == 0)
+ {
+ Serial.println(F("No GNGGA data available"));
+ }
+ else if (result == 1)
+ {
+ Serial.println(F("GNGGA data is available but is stale"));
+ }
+ else // if (result == 2)
+ {
+ // Data contains .length and .nmea
+ Serial.print(F("Latest GNGGA: Length: "));
+ Serial.print(data.length);
+ Serial.print(F("\tData: "));
+ Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
+ }
+
+ delay(250);
+}
diff --git a/examples/Automatic_NMEA/Example2_NMEA_GGA_Callbacks/Example2_NMEA_GGA_Callbacks.ino b/examples/Automatic_NMEA/Example2_NMEA_GGA_Callbacks/Example2_NMEA_GGA_Callbacks.ino
new file mode 100644
index 0000000..ca6380a
--- /dev/null
+++ b/examples/Automatic_NMEA/Example2_NMEA_GGA_Callbacks/Example2_NMEA_GGA_Callbacks.ino
@@ -0,0 +1,117 @@
+/*
+ Get the latest GPGGA / GNGGA NMEA sentence using callbacks
+ By: Paul Clark
+ SparkFun Electronics
+ Date: January 12th, 2021
+ License: MIT. See license file for more information but you can
+ basically do whatever you want with this code.
+
+ This example shows how to turn on/off the NMEA sentences being output over I2C.
+ It then demonstrates how to get the latest GPGGA or GNGGA message autonomously using callbacks.
+
+ If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
+ This example shows how to change the Talker ID so the GNGGA messages become GPGGA.
+ It also shows how to enable "high precision mode" to include extra decimal places in the GGA messages.
+
+ This example turns off all sentences except for GGA.
+
+ Feel like supporting open source hardware?
+ Buy a board from SparkFun!
+ ZED-F9P RTK2: https://www.sparkfun.com/products/15136
+ NEO-M8P RTK: https://www.sparkfun.com/products/15005
+ SAM-M8Q: https://www.sparkfun.com/products/15106
+
+ Hardware Connections:
+ Plug a Qwiic cable into the GNSS and a RedBoard
+ If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
+ Open the serial monitor at 115200 baud to see the output
+*/
+
+#include //Needed for I2C to GNSS
+
+#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
+SFE_UBLOX_GNSS myGNSS;
+
+// Callback: printGPGGA will be called when new GPGGA NMEA data arrives
+// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
+// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback
+// / _____ This _must_ be NMEA_GGA_data_t
+// | / _____ You can use any name you like for the struct
+// | | /
+// | | |
+void printGPGGA(NMEA_GGA_data_t nmeaData)
+{
+ Serial.print(F("\r\nGPGGA: Length: "));
+ Serial.print(nmeaData.length);
+ Serial.print(F("\tData: "));
+ Serial.print((const char *)nmeaData.nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
+}
+
+// Callback: printGNGGA will be called if new GNGGA NMEA data arrives
+// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
+// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNGGAcallback
+// / _____ This _must_ be NMEA_GGA_data_t
+// | / _____ You can use any name you like for the struct
+// | | /
+// | | |
+void printGNGGA(NMEA_GGA_data_t nmeaData)
+{
+ Serial.print(F("\r\nGNGGA: Length: "));
+ Serial.print(nmeaData.length);
+ Serial.print(F("\tData: "));
+ Serial.print((const char *)nmeaData.nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
+}
+
+void setup()
+{
+
+ Serial.begin(115200);
+ Serial.println(F("SparkFun u-blox GNSS Example"));
+
+ Wire.begin();
+
+ //myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
+
+ if (myGNSS.begin() == false)
+ {
+ Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
+ while (1)
+ ;
+ }
+
+ // Disable or enable various NMEA sentences over the I2C interface
+ myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
+ myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C); // Several of these are on by default on ublox board so let's disable them
+ myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
+ myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
+ myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); // Leave only GGA enabled at current navigation rate
+
+ // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
+ myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
+ //myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
+
+ myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
+
+ //myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
+
+ Serial.println(F("Messages configured"));
+
+ //myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
+
+ // Set up the callback for GPGGA
+ myGNSS.setNMEAGPGGAcallback(&printGPGGA);
+
+ // Set up the callback for GNGGA
+ myGNSS.setNMEAGNGGAcallback(&printGNGGA);
+}
+
+void loop()
+{
+ myGNSS.checkUblox(); // Check for the arrival of new data and process it.
+ myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
+
+ Serial.print(".");
+ delay(50);
+}
diff --git a/examples/Dead_Reckoning/Example8_getNAVPVAT/Example8_getNAVPVAT.ino b/examples/Dead_Reckoning/Example8_getNAVPVAT/Example8_getNAVPVAT.ino
new file mode 100644
index 0000000..cc8e6e4
--- /dev/null
+++ b/examples/Dead_Reckoning/Example8_getNAVPVAT/Example8_getNAVPVAT.ino
@@ -0,0 +1,111 @@
+/*
+ By: Paul CLark
+ SparkFun Electronics
+ Date: January, 2022
+ License: MIT. See license file for more information but you can
+ basically do whatever you want with this code.
+
+ Feel like supporting open source hardware?
+ Buy a board from SparkFun!
+ ZED-F9R: https://www.sparkfun.com/products/16344
+
+ Hardware Connections:
+ Plug a Qwiic cable into the GNSS and a Redboard Qwiic
+ If you don't have a platform with a Qwiic connection use the
+ SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
+ Open the serial monitor at 115200 baud to see the output
+
+ After calibrating the module and securing it to your vehicle such that it's
+ stable within 2 degrees, and the board is oriented correctly with regards to
+ the vehicle's frame, you can now read the vehicle's "attitude". The attitude
+ includes the vehicle's heading, pitch, and roll.
+
+*/
+
+#include //Needed for I2C to GNSS
+
+#include //http://librarymanager/All#SparkFun_u-blox_GNSS
+SFE_UBLOX_GNSS myGNSS;
+
+void setup()
+{
+ Serial.begin(115200);
+ while (!Serial); //Wait for user to open terminal
+ Serial.println(F("SparkFun u-blox Example"));
+
+ Wire.begin();
+
+ if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
+ {
+ Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
+ while (1);
+ }
+
+ myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
+}
+
+void loop()
+{
+ // PVAT data is produced at the navigation rate, so by default we'll get fresh data once per second
+ if (myGNSS.getNAVPVAT()) // Poll new PVAT
+ {
+ Serial.print(F("Roll: "));
+ Serial.print((float)myGNSS.getVehicleRoll() / 100000.0, 5); // Use the helper function to get the roll in degrees * 10^-5
+
+ Serial.print(F(" Pitch: "));
+ Serial.print((float)myGNSS.getVehiclePitch() / 100000.0, 5); // Use the helper function to get the pitch in degrees * 10^-5
+
+ Serial.print(F(" Heading: "));
+ Serial.print((float)myGNSS.getVehicleHeading() / 100000.0, 5); // Use the helper function to get the heading in degrees * 10^-5
+
+ // We don't have helper functions to extract the roll, pitch and heading valid flags from the PVAT message. But we can do it manually:
+
+ Serial.print(F(" Roll Valid: "));
+ Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehRollValid);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehRollValid = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.print(F(" Pitch Valid: "));
+ Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehPitchValid);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehPitchValid = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.print(F(" Heading Valid: "));
+ Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehHeadingValid);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehHeadingValid = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ // We don't have helper functions to extract the roll, pitch and heading accuracy from the PVAT message. But we can do it manually:
+
+ Serial.print(F(" Roll Acc: "));
+ Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accRoll) / 100, 2);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accRoll = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.print(F(" Pitch Acc: "));
+ Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accPitch) / 100, 2);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accPitch = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.print(F(" Heading Acc: "));
+ Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accHeading) / 100, 2);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accHeading = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ // We don't have helper functions to extract the lat and lon from the PVAT message. But we can do it manually:
+
+ Serial.print(F(" Lat: "));
+ Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.lat) / 10000000.0, 7);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.lat = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.print(F(" Lon: "));
+ Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.lon) / 10000000.0, 7);
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.lon = false; // Mark the data as stale
+ myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+
+ Serial.println();
+ }
+
+ delay(250);
+}
diff --git a/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/Example17_NTRIPClient_With_GGA_Callback.ino b/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/Example17_NTRIPClient_With_GGA_Callback.ino
new file mode 100644
index 0000000..91751e5
--- /dev/null
+++ b/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/Example17_NTRIPClient_With_GGA_Callback.ino
@@ -0,0 +1,497 @@
+/*
+ Use ESP32 WiFi to get RTCM data from Swift Navigation's Skylark caster as a Client, and transmit GGA using a callback
+ By: SparkFun Electronics / Nathan Seidle & Paul Clark
+ Date: January 13th, 2022
+ License: MIT. See license file for more information but you can
+ basically do whatever you want with this code.
+
+ This example shows how to obtain RTCM data from a NTRIP Caster over WiFi and push it over I2C to a ZED-F9x.
+ It's confusing, but the Arduino is acting as a 'client' to a 'caster'.
+ In this case we will use Skylark. But you can of course use RTK2Go or Emlid's Caster too. Change secrets.h. as required.
+
+ The rover's location will be broadcast to the caster every 10s via GGA setence - automatically using a callback.
+
+ This is a proof of concept to show how to connect to a caster via HTTP and show how the corrections control the accuracy.
+
+ It's a fun thing to disconnect from the caster and watch the accuracy degrade. Then connect again and watch it recover!
+
+ For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
+ please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
+
+ "In broad protocol terms, the NTRIP client must first connect (get an HTTP “OK” reply) and only then
+ should it send the sentence. NTRIP protocol revision 2 (which does not have very broad industry
+ acceptance at this time) does allow sending the sentence in the original header."
+ https://www.use-snip.com/kb/knowledge-base/subtle-issues-with-using-ntrip-client-nmea-183-strings/
+
+ Feel like supporting open source hardware?
+ Buy a board from SparkFun!
+ ZED-F9P RTK2: https://www.sparkfun.com/products/16481
+ RTK Surveyor: https://www.sparkfun.com/products/18443
+ RTK Express: https://www.sparkfun.com/products/18442
+
+ Hardware Connections:
+ Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
+ If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
+ Open the serial monitor at 115200 baud to see the output
+*/
+
+#include
+#include "secrets.h"
+
+#include //http://librarymanager/All#SparkFun_u-blox_GNSS
+SFE_UBLOX_GNSS myGNSS;
+
+//The ESP32 core has a built in base64 library but not every platform does
+//We'll use an external lib if necessary.
+#if defined(ARDUINO_ARCH_ESP32)
+#include "base64.h" //Built-in ESP32 library
+#else
+#include //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform
+#endif
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+//Global variables
+
+unsigned long lastReceivedRTCM_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
+const unsigned long maxTimeBeforeHangup_ms = 10000UL; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
+
+bool transmitLocation = true; //By default we will transmit the unit's location via GGA sentence.
+
+WiFiClient ntripClient; // The WiFi connection to the NTRIP server. This is global so pushGGA can see if we are connected.
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+// Callback: pushGPGGA will be called when new GPGGA NMEA data arrives
+// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
+// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback
+// / _____ This _must_ be NMEA_GGA_data_t
+// | / _____ You can use any name you like for the struct
+// | | /
+// | | |
+void pushGPGGA(NMEA_GGA_data_t nmeaData)
+{
+ //Provide the caster with our current position as needed
+ if ((ntripClient.connected() == true) && (transmitLocation == true))
+ {
+ Serial.print(F("Pushing GGA to server: "));
+ Serial.print((const char *)nmeaData.nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
+
+ //Push our current GGA sentence to caster
+ ntripClient.print((const char *)nmeaData.nmea);
+ }
+}
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+// Callback: printPVTdata will be called when new NAV PVT data arrives
+// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
+// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback
+// / _____ This _must_ be UBX_NAV_PVT_data_t
+// | / _____ You can use any name you like for the struct
+// | | /
+// | | |
+void printPVTdata(UBX_NAV_PVT_data_t ubxDataStruct)
+{
+ long latitude = ubxDataStruct.lat; // Print the latitude
+ Serial.print(F("Lat: "));
+ Serial.print(latitude / 10000000L);
+ Serial.print(F("."));
+ Serial.print(abs(latitude % 10000000L));
+
+ long longitude = ubxDataStruct.lon; // Print the longitude
+ Serial.print(F(" Long: "));
+ Serial.print(longitude / 10000000L);
+ Serial.print(F("."));
+ Serial.print(abs(longitude % 10000000L));
+
+ long altitude = ubxDataStruct.hMSL; // Print the height above mean sea level
+ Serial.print(F(" Height: "));
+ Serial.print(altitude);
+ Serial.print(F(" (mm)"));
+
+ uint8_t fixType = ubxDataStruct.fixType; // Print the fix type
+ Serial.print(F(" Fix: "));
+ Serial.print(fixType);
+ if (fixType == 0)
+ Serial.print(F(" (None)"));
+ else if (fixType == 1)
+ Serial.print(F(" (Dead Reckoning)"));
+ else if (fixType == 2)
+ Serial.print(F(" (2D)"));
+ else if (fixType == 3)
+ Serial.print(F(" (3D)"));
+ else if (fixType == 3)
+ Serial.print(F(" (GNSS + Dead Reckoning)"));
+ else if (fixType == 5)
+ Serial.print(F(" (Time Only)"));
+ else
+ Serial.print(F(" (UNKNOWN)"));
+
+ uint8_t carrSoln = ubxDataStruct.flags.bits.carrSoln; // Print the carrier solution
+ Serial.print(F(" Carrier Solution: "));
+ Serial.print(carrSoln);
+ if (carrSoln == 0)
+ Serial.print(F(" (None)"));
+ else if (carrSoln == 1)
+ Serial.print(F(" (Floating)"));
+ else if (carrSoln == 2)
+ Serial.print(F(" (Fixed)"));
+ else
+ Serial.print(F(" (UNKNOWN)"));
+
+ uint32_t hAcc = ubxDataStruct.hAcc; // Print the horizontal accuracy estimate
+ Serial.print(F(" Horizontal Accuracy Estimate: "));
+ Serial.print(hAcc);
+ Serial.print(F(" (mm)"));
+
+ Serial.println();
+}
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+void setup()
+{
+ Serial.begin(115200);
+ Serial.println(F("NTRIP testing"));
+
+ Wire.begin(); //Start I2C
+
+ while (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
+ {
+ Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
+ delay(2000);
+ //while (1);
+ }
+ Serial.println(F("u-blox module connected"));
+
+ myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
+ myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //Be sure RTCM3 input is enabled. UBX + RTCM3 is not a valid state.
+
+ myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
+
+ myGNSS.setNavigationFrequency(1); //Set output in Hz.
+
+ // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
+ myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
+
+ myGNSS.setNMEAGPGGAcallback(&pushGPGGA); // Set up the callback for GPGGA
+
+ myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 10); // Tell the module to output GGA every 10 seconds
+
+ myGNSS.setAutoPVTcallback(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
+
+ //myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ bool keepTrying = true;
+ while (keepTrying)
+ {
+ Serial.print(F("Connecting to local WiFi"));
+
+ unsigned long startTime = millis();
+ WiFi.begin(ssid, password);
+ while ((WiFi.status() != WL_CONNECTED) && (millis() < (startTime + 10000))) // Timeout after 10 seconds
+ {
+ delay(500);
+ Serial.print(F("."));
+ }
+ Serial.println();
+
+ if (WiFi.status() == WL_CONNECTED)
+ keepTrying = false; // Connected!
+ else
+ {
+ WiFi.disconnect(true);
+ WiFi.mode(WIFI_OFF);
+ }
+ }
+
+ Serial.print(F("WiFi connected with IP: "));
+ Serial.println(WiFi.localIP());
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ while (Serial.available()) // Empty the serial buffer
+ Serial.read();
+}
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+void loop()
+{
+ myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
+ myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
+
+ enum states // Use a 'state machine' to open and close the connection
+ {
+ open_connection,
+ push_data_and_wait_for_keypress,
+ close_connection,
+ waiting_for_keypress
+ };
+ static states state = open_connection;
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ switch (state)
+ {
+ case open_connection:
+ Serial.println(F("Connecting to the NTRIP caster..."));
+ if (beginClient()) // Try to open the connection to the caster
+ {
+ Serial.println(F("Connected to the NTRIP caster! Press any key to disconnect..."));
+ state = push_data_and_wait_for_keypress; // Move on
+ }
+ else
+ {
+ Serial.print(F("Could not connect to the caster. Trying again in 5 seconds."));
+ for (int i = 0; i < 5; i++)
+ {
+ delay(1000);
+ Serial.print(F("."));
+ }
+ Serial.println();
+ }
+ break;
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ case push_data_and_wait_for_keypress:
+ // If the connection has dropped or timed out, or if the user has pressed a key
+ if ((processConnection() == false) || (keyPressed()))
+ {
+ state = close_connection; // Move on
+ }
+ break;
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ case close_connection:
+ Serial.println(F("Closing the connection to the NTRIP caster..."));
+ closeConnection();
+ Serial.println(F("Press any key to reconnect..."));
+ state = waiting_for_keypress; // Move on
+ break;
+
+ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ case waiting_for_keypress:
+ // If the connection has dropped or timed out, or if the user has pressed a key
+ if (keyPressed())
+ state = open_connection; // Move on
+ break;
+ }
+
+}
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+//Connect to NTRIP Caster. Return true is connection is successful.
+bool beginClient()
+{
+ Serial.print(F("Opening socket to "));
+ Serial.println(casterHost);
+
+ if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
+ {
+ Serial.println(F("Connection to caster failed"));
+ return (false);
+ }
+ else
+ {
+ Serial.print(F("Connected to "));
+ Serial.print(casterHost);
+ Serial.print(F(" : "));
+ Serial.println(casterPort);
+
+ Serial.print(F("Requesting NTRIP Data from mount point "));
+ Serial.println(mountPoint);
+
+ // Set up the server request (GET)
+ const int SERVER_BUFFER_SIZE = 512;
+ char serverRequest[SERVER_BUFFER_SIZE];
+ snprintf(serverRequest,
+ SERVER_BUFFER_SIZE,
+ "GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
+ mountPoint);
+
+ // Set up the credentials
+ char credentials[512];
+ if (strlen(casterUser) == 0)
+ {
+ strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials));
+ }
+ else
+ {
+ //Pass base64 encoded user:pw
+ char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
+ snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
+
+ Serial.print(F("Sending credentials: "));
+ Serial.println(userCredentials);
+
+#if defined(ARDUINO_ARCH_ESP32)
+ //Encode with ESP32 built-in library
+ base64 b;
+ String strEncodedCredentials = b.encode(userCredentials);
+ char encodedCredentials[strEncodedCredentials.length() + 1];
+ strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array
+#else
+ //Encode with nfriendly library
+ int encodedLen = base64_enc_len(strlen(userCredentials));
+ char encodedCredentials[encodedLen]; //Create array large enough to house encoded data
+ base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed
+#endif
+
+ snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials);
+ }
+
+ // Add the encoded credentials to the server request
+ strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
+ strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
+
+ Serial.print(F("serverRequest size: "));
+ Serial.print(strlen(serverRequest));
+ Serial.print(F(" of "));
+ Serial.print(sizeof(serverRequest));
+ Serial.println(F(" bytes available"));
+
+ // Send the server request
+ Serial.println(F("Sending server request: "));
+ Serial.println(serverRequest);
+ ntripClient.write(serverRequest, strlen(serverRequest));
+
+ //Wait up to 5 seconds for response
+ unsigned long startTime = millis();
+ while (ntripClient.available() == 0)
+ {
+ if (millis() > (startTime + 5000))
+ {
+ Serial.println(F("Caster timed out!"));
+ ntripClient.stop();
+ return (false);
+ }
+ delay(10);
+ }
+
+ //Check reply
+ int connectionResult = 0;
+ char response[512];
+ size_t responseSpot = 0;
+ while (ntripClient.available()) // Read bytes from the caster and store them
+ {
+ if (responseSpot == sizeof(response) - 1) // Exit the loop if we get too much data
+ break;
+
+ response[responseSpot++] = ntripClient.read();
+
+ if (connectionResult == 0) // Only print success/fail once
+ {
+ if (strstr(response, "200") != NULL) //Look for '200 OK'
+ {
+ connectionResult = 200;
+ }
+ if (strstr(response, "401") != NULL) //Look for '401 Unauthorized'
+ {
+ Serial.println(F("Hey - your credentials look bad! Check your caster username and password."));
+ connectionResult = 401;
+ }
+ }
+ }
+ response[responseSpot] = '\0'; // NULL-terminate the response
+
+ //Serial.print(F("Caster responded with: ")); Serial.println(response); // Uncomment this line to see the full response
+
+ if (connectionResult != 200)
+ {
+ Serial.print(F("Failed to connect to "));
+ Serial.println(casterHost);
+ return (false);
+ }
+ else
+ {
+ Serial.print(F("Connected to: "));
+ Serial.println(casterHost);
+ lastReceivedRTCM_ms = millis(); //Reset timeout
+ }
+ } //End attempt to connect
+
+ return (true);
+} // /beginClient
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+//Check for the arrival of any correction data. Push it to the GNSS.
+//Return false if: the connection has dropped, or if we receive no data for maxTimeBeforeHangup_ms
+bool processConnection()
+{
+ if (ntripClient.connected() == true) // Check that the connection is still open
+ {
+ uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
+ size_t rtcmCount = 0;
+
+ //Collect any available RTCM data
+ while (ntripClient.available())
+ {
+ //Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data!
+ rtcmData[rtcmCount++] = ntripClient.read();
+ if (rtcmCount == sizeof(rtcmData))
+ break;
+ }
+
+ if (rtcmCount > 0)
+ {
+ lastReceivedRTCM_ms = millis();
+
+ //Push RTCM to GNSS module over I2C
+ myGNSS.pushRawData(rtcmData, rtcmCount);
+
+ Serial.print(F("Pushed "));
+ Serial.print(rtcmCount);
+ Serial.println(F(" RTCM bytes to ZED"));
+ }
+ }
+ else
+ {
+ Serial.println(F("Connection dropped!"));
+ return (false); // Connection has dropped - return false
+ }
+
+ //Timeout if we don't have new data for maxTimeBeforeHangup_ms
+ if ((millis() - lastReceivedRTCM_ms) > maxTimeBeforeHangup_ms)
+ {
+ Serial.println(F("RTCM timeout!"));
+ return (false); // Connection has timed out - return false
+ }
+
+ return (true);
+} // /processConnection
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+void closeConnection()
+{
+ if (ntripClient.connected() == true)
+ {
+ ntripClient.stop();
+ }
+ Serial.println(F("Disconnected!"));
+}
+
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+//Return true if a key has been pressed
+bool keyPressed()
+{
+ if (Serial.available()) // Check for a new key press
+ {
+ delay(100); // Wait for any more keystrokes to arrive
+ while (Serial.available()) // Empty the serial buffer
+ Serial.read();
+ return (true);
+ }
+
+ return (false);
+}
diff --git a/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/secrets.h b/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/secrets.h
new file mode 100644
index 0000000..9de9b71
--- /dev/null
+++ b/examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback/secrets.h
@@ -0,0 +1,27 @@
+//Your WiFi credentials
+const char ssid[] = "yourSSID";
+const char password[] = "yourPassword";
+
+//RTK2Go works well and is free
+//const char casterHost[] = "rtk2go.com";
+//const uint16_t casterPort = 2101;
+//const char casterUser[] = "myEmail@test.com"; //User must provide their own email address to use RTK2Go
+//const char casterUserPW[] = "";
+//const char mountPoint[] = "bldr_SparkFun1"; //The mount point you want to get data from
+
+//Emlid Caster also works well and is free
+//const char casterHost[] = "caster.emlid.com";
+//const uint16_t casterPort = 2101;
+//const char casterUser[] = "u99696"; //User name and pw must be obtained through their web portal
+//const char casterUserPW[] = "466zez";
+//const char mountPoint[] = "MP1979"; //The mount point you want to get data from
+
+// Skylark (Swift Navigation) is awesome - but requires a subscription:
+// https://www.swiftnav.com/skylark
+// https://account.swiftnav.com/sign-up
+// Use the promo-code ONEMONTHFREE for a free one month access to Skylark on one device
+const char casterHost[] = "na.skylark.swiftnav.com"; // na = North Americs L1+L2; eu = Europe L1+L2
+const uint16_t casterPort = 2101;
+const char casterUser[] = "NTRIPusername+accountSubdomain"; // This is generated when you add a device to your Skylark account
+const char casterUserPW[] = "devicePassword";
+const char mountPoint[] = "CRS"; // The mount point you want to get data from. Select CRS (Cloud Reference Station) for the ZED-F9x
diff --git a/keys/ZED-F9R-HPS120_Interfacedescription_UBX-19056845_keys_sorted.txt b/keys/F9-HPS-1.21_InterfaceDescription_UBX-21019746_keys_sorted.txt
similarity index 96%
rename from keys/ZED-F9R-HPS120_Interfacedescription_UBX-19056845_keys_sorted.txt
rename to keys/F9-HPS-1.21_InterfaceDescription_UBX-21019746_keys_sorted.txt
index aceab1f..ce066e9 100644
--- a/keys/ZED-F9R-HPS120_Interfacedescription_UBX-19056845_keys_sorted.txt
+++ b/keys/F9-HPS-1.21_InterfaceDescription_UBX-21019746_keys_sorted.txt
@@ -40,6 +40,7 @@
0x10310022
0x10310024
0x10310025
+0x10340014
0x10360002
0x10360003
0x10360004
@@ -50,7 +51,6 @@
0x10510003
0x10520005
0x10530005
-0x10530006
0x10640002
0x10640003
0x10640005
@@ -60,26 +60,31 @@
0x10710001
0x10710002
0x10710004
+0x10710005
0x10720001
0x10720002
0x10730001
0x10730002
0x10730004
+0x10730005
0x10740001
0x10740002
0x10750001
0x10750002
0x10750004
+0x10750005
0x10760001
0x10760002
0x10770001
0x10770002
0x10770004
+0x10770005
0x10780001
0x10780002
0x10790001
0x10790002
0x10790004
+0x10790005
0x107a0001
0x107a0002
0x10930003
@@ -114,6 +119,7 @@
0x2005000c
0x20050023
0x20050030
+0x20050035
0x20060008
0x20060009
0x20060015
@@ -486,6 +492,21 @@
0x20910402
0x20910403
0x20910404
+0x20910605
+0x20910606
+0x20910607
+0x20910608
+0x20910609
+0x2091062a
+0x2091062b
+0x2091062c
+0x2091062d
+0x2091062e
+0x20910634
+0x20910635
+0x20910636
+0x20910637
+0x20910638
0x20920001
0x20920002
0x20920003
diff --git a/keys/u-blox_config_keys_sorted.txt b/keys/u-blox_config_keys_sorted.txt
index 4da2918..8eee0f4 100644
--- a/keys/u-blox_config_keys_sorted.txt
+++ b/keys/u-blox_config_keys_sorted.txt
@@ -82,30 +82,35 @@
0x10710001
0x10710002
0x10710004
+0x10710005
0x10720001
0x10720002
0x10720004
0x10730001
0x10730002
0x10730004
+0x10730005
0x10740001
0x10740002
0x10740004
0x10750001
0x10750002
0x10750004
+0x10750005
0x10760001
0x10760002
0x10760004
0x10770001
0x10770002
0x10770004
+0x10770005
0x10780001
0x10780002
0x10780004
0x10790001
0x10790002
0x10790004
+0x10790005
0x107a0001
0x107a0002
0x107a0004
@@ -732,11 +737,21 @@
0x20910592
0x20910593
0x20910594
+0x20910605
+0x20910606
+0x20910607
+0x20910608
+0x20910609
0x20910610
0x20910611
0x20910612
0x20910613
0x20910614
+0x2091062a
+0x2091062b
+0x2091062c
+0x2091062d
+0x2091062e
0x20910634
0x20910635
0x20910636
diff --git a/keywords.txt b/keywords.txt
index 3aad233..b40d37e 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -140,6 +140,7 @@ setSurveyMode KEYWORD2
enableSurveyMode KEYWORD2
disableSurveyMode KEYWORD2
setStaticPosition KEYWORD2
+setDGNSSConfiguration KEYWORD2
getProtocolVersionHigh KEYWORD2
getProtocolVersionLow KEYWORD2
@@ -287,6 +288,15 @@ initPacketUBXNAVHPPOSLLH KEYWORD2
flushHPPOSLLH KEYWORD2
logNAVHPPOSLLH KEYWORD2
+getNAVPVAT KEYWORD2
+setAutoNAVPVAT KEYWORD2
+setAutoNAVPVAT KEYWORD2
+setAutoNAVPVATrate KEYWORD2
+setAutoNAVPVATcallback KEYWORD2
+assumeAutoNAVPVAT KEYWORD2
+flushNAVPVAT KEYWORD2
+logNAVPVAT KEYWORD2
+
getNAVCLOCK KEYWORD2
setAutoNAVCLOCK KEYWORD2
setAutoNAVCLOCKrate KEYWORD2
@@ -437,11 +447,6 @@ initPacketUBXHNRPVT KEYWORD2
flushHNRPVT KEYWORD2
logHNRPVT KEYWORD2
-setNMEALoggingMask KEYWORD2
-getNMEALoggingMask KEYWORD2
-setProcessNMEAMask KEYWORD2
-getProcessNMEAMask KEYWORD2
-
setNavigationFrequency KEYWORD2
getNavigationFrequency KEYWORD2
setMeasurementRate KEYWORD2
@@ -517,6 +522,11 @@ getMeanSeaLevelHp KEYWORD2
getHorizontalAccuracy KEYWORD2
getVerticalAccuracy KEYWORD2
+getVehicleRoll KEYWORD2
+getVehiclePitch KEYWORD2
+getVehicleHeading KEYWORD2
+getMotionHeading KEYWORD2
+
getSurveyInActive KEYWORD2
getSurveyInValid KEYWORD2
getSurveyInObservationTime KEYWORD2
@@ -545,6 +555,19 @@ getHNRroll KEYWORD2
getHNRpitch KEYWORD2
getHNRheading KEYWORD2
+setNMEALoggingMask KEYWORD2
+getNMEALoggingMask KEYWORD2
+setProcessNMEAMask KEYWORD2
+getProcessNMEAMask KEYWORD2
+
+setMainTalkerID KEYWORD2
+setHighPrecisionMode KEYWORD2
+
+getLatestNMEAGPGGA KEYWORD2
+setNMEAGPGGAcallback KEYWORD2
+getLatestNMEAGNGGA KEYWORD2
+setNMEAGNGGAcallback KEYWORD2
+
extractLong KEYWORD2
extractSignedLong KEYWORD2
extractInt KEYWORD2
@@ -652,6 +675,7 @@ UBX_NAV_HPPOSLLH LITERAL1
UBX_NAV_ODO LITERAL1
UBX_NAV_POSECEF LITERAL1
UBX_NAV_PVT LITERAL1
+UBX_NAV_PVAT LITERAL1
UBX_NAV_RELPOSNED LITERAL1
UBX_NAV_RESETODO LITERAL1
UBX_NAV_STATUS LITERAL1
@@ -662,6 +686,7 @@ UBX_NAV_VELNED LITERAL1
UBX_RXM_RAWX LITERAL1
UBX_RXM_SFRBX LITERAL1
+UBX_RXM_SPARTN LITERAL1
UBX_TIM_TM2 LITERAL1
@@ -706,6 +731,8 @@ DYN_MODEL_AIRBORNE2g LITERAL1
DYN_MODEL_AIRBORNE4g LITERAL1
DYN_MODEL_WRIST LITERAL1
DYN_MODEL_BIKE LITERAL1
+DYN_MODEL_MOWER LITERAL1
+DYN_MODEL_ESCOOTER LITERAL1
DYN_MODEL_UNKNOWN LITERAL1
SFE_UBLOX_GNSS_ID_GPS LITERAL1
@@ -727,3 +754,14 @@ SFE_UBLOX_MGA_ACK_INFOCODE_SIZE_MISMATCH LITERAL1
SFE_UBLOX_MGA_ACK_INFOCODE_NOT_STORED LITERAL1
SFE_UBLOX_MGA_ACK_INFOCODE_NOT_READY LITERAL1
SFE_UBLOX_MGA_ACK_INFOCODE_TYPE_UNKNOWN LITERAL1
+
+SFE_UBLOX_MAIN_TALKER_ID_DEFAULT LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GP LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GL LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GN LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GA LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GB LITERAL1
+SFE_UBLOX_MAIN_TALKER_ID_GQ LITERAL1
+
+SFE_UBLOX_DGNSS_MODE_FLOAT LITERAL1
+SFE_UBLOX_DGNSS_MODE_FIXED LITERAL1
diff --git a/library.properties b/library.properties
index c3d47aa..0a5a6a5 100644
--- a/library.properties
+++ b/library.properties
@@ -1,9 +1,9 @@
name=SparkFun u-blox GNSS Arduino Library
-version=2.1.5
+version=2.2.0
author=SparkFun Electronics
maintainer=SparkFun Electronics
-sentence=Library for I2C and Serial Communication with u-blox GNSS modules
-paragraph=An Arduino Library to enable I2C, Serial and SPI communication for both NMEA reception and binary UBX sending to u-blox modules. Useful for interfacing to the SparkFun GPS-RTK2 ZED-F9P, SparkFun GPS-RTK NEO-M8P-2, the SparkFun SAM-M8Q, and the SparkFun ZOE-M8Q. Library also works with other u-blox based boards.
The ZED-F9P and NEO-M8P-2 modules are top-of-the-line modules for high accuracy GNSS and GPS location solutions including RTK. The ZED-F9P is unique in that it is capable of both rover and base station operations allowing the module to become a base station and produce RTCM 3.x correction data.
+sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules
+paragraph=An Arduino Library to support the full range of u-blox GNSS modules, using both NMEA and UBX protocols over I2C, Serial and SPI. Useful for interfacing to the SparkFun GPS-RTK2 ZED-F9P, SparkFun GPS-RTK NEO-M8P-2, SparkFun SAM-M8Q, SparkFun ZOE-M8Q and all the other SparkFun u-blox GNSS Breakouts.
The ZED-F9P and NEO-M8P-2 modules are top-of-the-line modules for high accuracy GNSS and GPS location solutions including RTK. The ZED-F9P is unique in that it is capable of both rover and base station operations allowing the module to become a base station and produce RTCM 3.x correction data.
Need support for RTK NTRIP Caster services like RTK2go, Emlid Caster and Skylark? This library has functions, callbacks and tried-and-tested examples to let you push RTCM correction data to your module seamlessly!
category=Sensors
url=https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library
architectures=*
diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp
index c13e4db..ac25f36 100644
--- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp
+++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp
@@ -214,6 +214,16 @@ void SFE_UBLOX_GNSS::end(void)
packetUBXNAVHPPOSLLH = NULL; // Redundant?
}
+ if (packetUBXNAVPVAT != NULL)
+ {
+ if (packetUBXNAVPVAT->callbackData != NULL)
+ {
+ delete packetUBXNAVPVAT->callbackData;
+ }
+ delete packetUBXNAVPVAT;
+ packetUBXNAVPVAT = NULL; // Redundant?
+ }
+
if (packetUBXNAVCLOCK != NULL)
{
if (packetUBXNAVCLOCK->callbackData != NULL)
@@ -396,6 +406,26 @@ void SFE_UBLOX_GNSS::end(void)
packetUBXHNRPVT = NULL; // Redundant?
}
+ if (storageNMEAGPGGA != NULL)
+ {
+ if (storageNMEAGPGGA->callbackCopy != NULL)
+ {
+ delete storageNMEAGPGGA->callbackCopy;
+ }
+ delete storageNMEAGPGGA;
+ storageNMEAGPGGA = NULL; // Redundant?
+ }
+
+ if (storageNMEAGNGGA != NULL)
+ {
+ if (storageNMEAGNGGA->callbackCopy != NULL)
+ {
+ delete storageNMEAGNGGA->callbackCopy;
+ }
+ delete storageNMEAGNGGA;
+ storageNMEAGNGGA = NULL; // Redundant?
+ }
+
}
//Allow the user to change packetCfgPayloadSize. Handy if you want to process big messages like RAWX
@@ -1145,6 +1175,9 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
case UBX_NAV_HPPOSLLH:
if (packetUBXNAVHPPOSLLH != NULL) result = true;
break;
+ case UBX_NAV_PVAT:
+ if (packetUBXNAVPVAT != NULL) result = true;
+ break;
case UBX_NAV_CLOCK:
if (packetUBXNAVCLOCK != NULL) result = true;
break;
@@ -1669,6 +1702,24 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
_signsOfLife = isNMEAHeaderValid();
}
+ // Check if we have automatic storage for this message
+ if (isThisNMEAauto())
+ {
+ uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
+ uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
+ uint8_t nmeaMaxLength = getNMEAMaxLength();
+ *lengthPtr = 6; // Set the working copy length
+ memset(nmeaPtr, 0, nmeaMaxLength); // Clear the working copy
+ memcpy(nmeaPtr, &nmeaAddressField[0], 6); // Copy the start character and address field into the working copy
+ }
+ else
+ {
+ // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ // {
+ // _debugSerial->println(F("process: non-auto NMEA message"));
+ // }
+ }
+
// We've just received the end of the address field. Check if it is selected for logging
if (logThisNMEA())
{
@@ -1688,6 +1739,24 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer and/or pass it to processNMEA?
{
+ if (isThisNMEAauto())
+ {
+ uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
+ uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
+ uint8_t nmeaMaxLength = getNMEAMaxLength();
+ if (*lengthPtr < nmeaMaxLength)
+ {
+ *(nmeaPtr + *lengthPtr) = incoming; // Store the character
+ *lengthPtr = *lengthPtr + 1; // Increment the length
+ if (*lengthPtr == nmeaMaxLength)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ {
+ _debugSerial->println(F("process: NMEA buffer is full!"));
+ }
+ }
+ }
+ }
if (logThisNMEA())
storeFileBytes(&incoming, 1); // Add incoming to the file buffer
if (processThisNMEA())
@@ -1703,7 +1772,86 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
currentSentence = NONE; //Something went wrong. Reset.
if (nmeaByteCounter == 0) // Check if we are done
+ {
+ if (isThisNMEAauto())
+ {
+ uint8_t *workingLengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
+ uint8_t *workingNMEAPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
+ uint8_t nmeaMaxLength = getNMEAMaxLength();
+
+ // Check the checksum: the checksum is the exclusive-OR of all characters between the $ and the *
+ uint8_t nmeaChecksum = 0;
+ uint8_t charsChecked = 1; // Start after the $
+ uint8_t thisChar = '\0';
+ while ((charsChecked < (nmeaMaxLength - 1))
+ && (charsChecked < ((*workingLengthPtr) - 4))
+ && (thisChar != '*'))
+ {
+ thisChar = *(workingNMEAPtr + charsChecked); // Get a char from the working copy
+ if (thisChar != '*') // Ex-or the char into the checksum - but not if it is the '*'
+ nmeaChecksum ^= thisChar;
+ charsChecked++; // Increment the counter
+ }
+ if (thisChar == '*') // Make sure we found the *
+ {
+ uint8_t expectedChecksum1 = (nmeaChecksum >> 4) + '0';
+ if (expectedChecksum1 >= ':') // Handle Hex correctly
+ expectedChecksum1 += 'A' - ':';
+ uint8_t expectedChecksum2 = (nmeaChecksum & 0x0F) + '0';
+ if (expectedChecksum2 >= ':') // Handle Hex correctly
+ expectedChecksum2 += 'A' - ':';
+ if ((expectedChecksum1 == *(workingNMEAPtr + charsChecked))
+ && (expectedChecksum2 == *(workingNMEAPtr + charsChecked + 1)))
+ {
+ uint8_t *completeLengthPtr = getNMEACompleteLengthPtr(); // Get a pointer to the complete copy length
+ uint8_t *completeNMEAPtr = getNMEACompleteNMEAPtr(); // Get a pointer to the complete copy NMEA data
+ memset(completeNMEAPtr, 0, nmeaMaxLength); // Clear the previous complete copy
+ memcpy(completeNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the complete copy
+ *completeLengthPtr = *workingLengthPtr; // Update the length
+ nmeaAutomaticFlags *flagsPtr = getNMEAFlagsPtr(); // Get a pointer to the flags
+ nmeaAutomaticFlags flagsCopy = *flagsPtr;
+ flagsCopy.flags.bits.completeCopyValid = 1; // Set the complete copy valid flag
+ flagsCopy.flags.bits.completeCopyRead = 0; // Clear the complete copy read flag
+ *flagsPtr = flagsCopy; // Update the flags
+ // Callback
+ if (doesThisNMEAHaveCallback()) // Do we need to copy the data into the callback copy?
+ {
+ if (flagsCopy.flags.bits.callbackCopyValid == 0) // Has the callback copy valid flag been cleared (by checkCallbacks)
+ {
+ uint8_t *callbackLengthPtr = getNMEACallbackLengthPtr(); // Get a pointer to the callback copy length
+ uint8_t *callbackNMEAPtr = getNMEACallbackNMEAPtr(); // Get a pointer to the callback copy NMEA data
+ memset(callbackNMEAPtr, 0, nmeaMaxLength); // Clear the previous callback copy
+ memcpy(callbackNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the callback copy
+ *callbackLengthPtr = *workingLengthPtr; // Update the length
+ flagsCopy.flags.bits.callbackCopyValid = 1; // Set the callback copy valid flag
+ *flagsPtr = flagsCopy; // Update the flags
+ }
+ }
+ }
+ else
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ {
+ _debugSerial->print(F("process: NMEA checksum fail (2)! Expected "));
+ _debugSerial->write(expectedChecksum1);
+ _debugSerial->write(expectedChecksum2);
+ _debugSerial->print(F(" Got "));
+ _debugSerial->write(*(workingNMEAPtr + charsChecked));
+ _debugSerial->write(*(workingNMEAPtr + charsChecked + 1));
+ _debugSerial->println();
+ }
+ }
+ }
+ else
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ {
+ _debugSerial->println(F("process: NMEA checksum fail (1)!"));
+ }
+ }
+ }
currentSentence = NONE; // All done!
+ }
}
else if (currentSentence == RTCM)
{
@@ -1815,6 +1963,194 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming)
_nmeaOutputPort->write(incoming); //Echo this byte to the serial port
}
+// Check if the NMEA message (in nmeaAddressField) is "auto" (i.e. has RAM allocated for it)
+bool SFE_UBLOX_GNSS::isThisNMEAauto()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ if (storageNMEAGPGGA != NULL)
+ return true;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ if (storageNMEAGNGGA != NULL)
+ return true;
+ }
+
+ return false;
+}
+
+// Do we need to copy the data into the callback copy?
+bool SFE_UBLOX_GNSS::doesThisNMEAHaveCallback()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ if (storageNMEAGPGGA != NULL)
+ if (storageNMEAGPGGA->callbackCopy != NULL)
+ if (storageNMEAGPGGA->callbackPointer != NULL)
+ return true;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ if (storageNMEAGNGGA != NULL)
+ if (storageNMEAGNGGA->callbackCopy != NULL)
+ if (storageNMEAGNGGA->callbackPointer != NULL)
+ return true;
+ }
+
+ return false;
+}
+
+// Get a pointer to the working copy length
+uint8_t * SFE_UBLOX_GNSS::getNMEAWorkingLengthPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->workingCopy.length;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->workingCopy.length;
+ }
+
+ return NULL;
+}
+
+// Get a pointer to the working copy NMEA data
+uint8_t * SFE_UBLOX_GNSS::getNMEAWorkingNMEAPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->workingCopy.nmea[0];
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->workingCopy.nmea[0];
+ }
+
+ return NULL;
+}
+
+// Get a pointer to the complete copy length
+uint8_t * SFE_UBLOX_GNSS::getNMEACompleteLengthPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->completeCopy.length;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->completeCopy.length;
+ }
+
+ return NULL;
+}
+
+// Get a pointer to the complete copy NMEA data
+uint8_t * SFE_UBLOX_GNSS::getNMEACompleteNMEAPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->completeCopy.nmea[0];
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->completeCopy.nmea[0];
+ }
+
+ return NULL;
+}
+
+// Get a pointer to the callback copy length
+uint8_t * SFE_UBLOX_GNSS::getNMEACallbackLengthPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->callbackCopy->length;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->callbackCopy->length;
+ }
+
+ return NULL;
+}
+
+// Get a pointer to the callback copy NMEA data
+uint8_t * SFE_UBLOX_GNSS::getNMEACallbackNMEAPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->callbackCopy->nmea[0];
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->callbackCopy->nmea[0];
+ }
+
+ return NULL;
+}
+
+// Get the maximum length of this NMEA message
+uint8_t SFE_UBLOX_GNSS::getNMEAMaxLength()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return NMEA_GGA_MAX_LENGTH;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return NMEA_GGA_MAX_LENGTH;
+ }
+
+ return 0;
+}
+
+// Get a pointer to the automatic NMEA flags
+nmeaAutomaticFlags * SFE_UBLOX_GNSS::getNMEAFlagsPtr()
+{
+ char thisNMEA[] = "GPGGA";
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGPGGA->automaticFlags;
+ }
+
+ strcpy(thisNMEA, "GNGGA");
+ if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
+ {
+ return &storageNMEAGNGGA->automaticFlags;
+ }
+
+ return NULL;
+}
+
//We need to be able to identify an RTCM packet and then the length
//so that we know when the RTCM message is completely received and we then start
//listening for other sentences (like NMEA or UBX)
@@ -2478,6 +2814,69 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
}
}
}
+ else if (msg->id == UBX_NAV_PVAT && msg->len == UBX_NAV_PVAT_LEN)
+ {
+ //Parse various byte fields into storage - but only if we have memory allocated for it
+ if (packetUBXNAVPVAT != NULL)
+ {
+ packetUBXNAVPVAT->data.iTOW = extractLong(msg, 0);
+ packetUBXNAVPVAT->data.version = extractByte(msg, 4);
+ packetUBXNAVPVAT->data.valid.all = extractByte(msg, 5);
+ packetUBXNAVPVAT->data.year = extractInt(msg, 6);
+ packetUBXNAVPVAT->data.month = extractByte(msg, 8);
+ packetUBXNAVPVAT->data.day = extractByte(msg, 9);
+ packetUBXNAVPVAT->data.hour = extractByte(msg, 10);
+ packetUBXNAVPVAT->data.min = extractByte(msg, 11);
+ packetUBXNAVPVAT->data.sec = extractByte(msg, 12);
+ packetUBXNAVPVAT->data.tAcc = extractLong(msg, 16);
+ packetUBXNAVPVAT->data.nano = extractSignedLong(msg, 20); //Includes milliseconds
+ packetUBXNAVPVAT->data.fixType = extractByte(msg, 24);
+ packetUBXNAVPVAT->data.flags.all = extractByte(msg, 25);
+ packetUBXNAVPVAT->data.flags2.all = extractByte(msg, 26);
+ packetUBXNAVPVAT->data.numSV = extractByte(msg, 27);
+ packetUBXNAVPVAT->data.lon = extractSignedLong(msg, 28);
+ packetUBXNAVPVAT->data.lat = extractSignedLong(msg, 32);
+ packetUBXNAVPVAT->data.height = extractSignedLong(msg, 36);
+ packetUBXNAVPVAT->data.hMSL = extractSignedLong(msg, 40);
+ packetUBXNAVPVAT->data.hAcc = extractLong(msg, 44);
+ packetUBXNAVPVAT->data.vAcc = extractLong(msg, 48);
+ packetUBXNAVPVAT->data.velN = extractSignedLong(msg, 52);
+ packetUBXNAVPVAT->data.velE = extractSignedLong(msg, 56);
+ packetUBXNAVPVAT->data.velD = extractSignedLong(msg, 60);
+ packetUBXNAVPVAT->data.gSpeed = extractSignedLong(msg, 64);
+ packetUBXNAVPVAT->data.sAcc = extractLong(msg, 68);
+ packetUBXNAVPVAT->data.vehRoll = extractSignedLong(msg, 72);
+ packetUBXNAVPVAT->data.vehPitch = extractSignedLong(msg, 76);
+ packetUBXNAVPVAT->data.vehHeading = extractSignedLong(msg, 80);
+ packetUBXNAVPVAT->data.motHeading = extractSignedLong(msg, 84);
+ packetUBXNAVPVAT->data.accRoll = extractInt(msg, 88);
+ packetUBXNAVPVAT->data.accPitch = extractInt(msg, 90);
+ packetUBXNAVPVAT->data.accHeading = extractInt(msg, 92);
+ packetUBXNAVPVAT->data.magDec = extractSignedInt(msg, 94);
+ packetUBXNAVPVAT->data.magAcc = extractInt(msg, 96);
+ packetUBXNAVPVAT->data.errEllipseOrient = extractInt(msg, 98);
+ packetUBXNAVPVAT->data.errEllipseMajor = extractLong(msg, 100);
+ packetUBXNAVPVAT->data.errEllipseMinor = extractLong(msg, 104);
+
+ //Mark all datums as fresh (not read before)
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0xFFFFFFFF;
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0xFFFFFFFF;
+
+ //Check if we need to copy the data for the callback
+ if ((packetUBXNAVPVAT->callbackData != NULL) // If RAM has been allocated for the copy of the data
+ && (packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale
+ {
+ memcpy(&packetUBXNAVPVAT->callbackData->iTOW, &packetUBXNAVPVAT->data.iTOW, sizeof(UBX_NAV_PVAT_data_t));
+ packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid = true;
+ }
+
+ //Check if we need to copy the data into the file buffer
+ if (packetUBXNAVPVAT->automaticFlags.flags.bits.addToFileBuffer)
+ {
+ storePacket(msg);
+ }
+ }
+ }
else if (msg->id == UBX_NAV_CLOCK && msg->len == UBX_NAV_CLOCK_LEN)
{
//Parse various byte fields into storage - but only if we have memory allocated for it
@@ -4057,6 +4456,17 @@ void SFE_UBLOX_GNSS::checkCallbacks(void)
packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
}
+ if ((packetUBXNAVPVAT != NULL) // If RAM has been allocated for message storage
+ && (packetUBXNAVPVAT->callbackData != NULL) // If RAM has been allocated for the copy of the data
+ && (packetUBXNAVPVAT->callbackPointer != NULL) // If the pointer to the callback has been defined
+ && (packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
+ {
+ //if (_printDebug == true)
+ // _debugSerial->println(F("checkCallbacks: calling callback for NAV PVAT"));
+ packetUBXNAVPVAT->callbackPointer(*packetUBXNAVPVAT->callbackData); // Call the callback
+ packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
+ }
+
if ((packetUBXNAVCLOCK != NULL) // If RAM has been allocated for message storage
&& (packetUBXNAVCLOCK->callbackData != NULL) // If RAM has been allocated for the copy of the data
&& (packetUBXNAVCLOCK->callbackPointer != NULL) // If the pointer to the callback has been defined
@@ -4222,6 +4632,28 @@ void SFE_UBLOX_GNSS::checkCallbacks(void)
packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
}
+ if ((storageNMEAGPGGA != NULL) // If RAM has been allocated for message storage
+ && (storageNMEAGPGGA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data
+ && (storageNMEAGPGGA->callbackPointer != NULL) // If the pointer to the callback has been defined
+ && (storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid
+ {
+ // if (_printDebug == true)
+ // _debugSerial->println(F("checkCallbacks: calling callback for GPGGA"));
+ storageNMEAGPGGA->callbackPointer(*storageNMEAGPGGA->callbackCopy); // Call the callback
+ storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale
+ }
+
+ if ((storageNMEAGNGGA != NULL) // If RAM has been allocated for message storage
+ && (storageNMEAGNGGA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data
+ && (storageNMEAGNGGA->callbackPointer != NULL) // If the pointer to the callback has been defined
+ && (storageNMEAGNGGA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid
+ {
+ // if (_printDebug == true)
+ // _debugSerial->println(F("checkCallbacks: calling callback for GNGGA"));
+ storageNMEAGNGGA->callbackPointer(*storageNMEAGNGGA->callbackCopy); // Call the callback
+ storageNMEAGNGGA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale
+ }
+
checkCallbacksReentrant = false;
}
@@ -5803,6 +6235,23 @@ bool SFE_UBLOX_GNSS::setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, i
return (setStaticPosition(ecefXOrLat, 0, ecefYOrLon, 0, ecefZOrAlt, 0, latlong, maxWait));
}
+// Set the DGNSS differential mode
+bool SFE_UBLOX_GNSS::setDGNSSConfiguration(sfe_ublox_dgnss_mode_e dgnssMode, uint16_t maxWait)
+{
+ packetCfg.cls = UBX_CLASS_CFG;
+ packetCfg.id = UBX_CFG_DGNSS;
+ packetCfg.len = 4;
+ packetCfg.startingSpot = 0;
+
+ payloadCfg[0] = (uint8_t)dgnssMode;
+ payloadCfg[1] = 0; // reserved0
+ payloadCfg[2] = 0;
+ payloadCfg[3] = 0;
+
+ return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK
+}
+
+
// Module Protocol Version
//Get the current protocol version of the u-blox module we're communicating with
@@ -8697,6 +9146,165 @@ void SFE_UBLOX_GNSS::logNAVHPPOSLLH(bool enabled)
packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled;
}
+// ***** PVAT automatic support
+
+//Get the latest Position/Velocity/Time solution and fill all global variables
+bool SFE_UBLOX_GNSS::getNAVPVAT(uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Bail if the RAM allocation failed
+ return (false);
+
+ if (packetUBXNAVPVAT->automaticFlags.flags.bits.automatic && packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate)
+ {
+ checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_PVAT);
+ return packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all;
+ }
+ else if (packetUBXNAVPVAT->automaticFlags.flags.bits.automatic && !packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate)
+ {
+ //Someone else has to call checkUblox for us...
+ return (false);
+ }
+ else
+ {
+ //The GPS is not automatically reporting navigation position so we have to poll explicitly
+ packetCfg.cls = UBX_CLASS_NAV;
+ packetCfg.id = UBX_NAV_PVAT;
+ packetCfg.len = 0;
+ packetCfg.startingSpot = 0;
+
+ //The data is parsed as part of processing the response
+ sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait);
+
+ if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED)
+ return (true);
+
+ if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN)
+ {
+ return (true);
+ }
+
+ return (false);
+ }
+}
+
+//Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT
+//works.
+bool SFE_UBLOX_GNSS::setAutoNAVPVAT(bool enable, uint16_t maxWait)
+{
+ return setAutoNAVPVATrate(enable ? 1 : 0, true, maxWait);
+}
+
+//Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT
+//works.
+bool SFE_UBLOX_GNSS::setAutoNAVPVAT(bool enable, bool implicitUpdate, uint16_t maxWait)
+{
+ return setAutoNAVPVATrate(enable ? 1 : 0, implicitUpdate, maxWait);
+}
+
+//Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT
+//works.
+bool SFE_UBLOX_GNSS::setAutoNAVPVATrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Only attempt this if RAM allocation was successful
+ return false;
+
+ if (rate > 127) rate = 127;
+
+ packetCfg.cls = UBX_CLASS_CFG;
+ packetCfg.id = UBX_CFG_MSG;
+ packetCfg.len = 3;
+ packetCfg.startingSpot = 0;
+ payloadCfg[0] = UBX_CLASS_NAV;
+ payloadCfg[1] = UBX_NAV_PVAT;
+ payloadCfg[2] = rate; // rate relative to navigation freq.
+
+ bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK
+ if (ok)
+ {
+ packetUBXNAVPVAT->automaticFlags.flags.bits.automatic = (rate > 0);
+ packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate;
+ }
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+ return ok;
+}
+
+//Enable automatic navigation message generation by the GNSS. This changes the way getPVAT works.
+bool SFE_UBLOX_GNSS::setAutoNAVPVATcallback(void (*callbackPointer)(UBX_NAV_PVAT_data_t), uint16_t maxWait)
+{
+ // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually.
+ bool result = setAutoNAVPVAT(true, false, maxWait);
+ if (!result)
+ return (result); // Bail if setAutoPVAT failed
+
+ if (packetUBXNAVPVAT->callbackData == NULL) //Check if RAM has been allocated for the callback copy
+ {
+ packetUBXNAVPVAT->callbackData = new UBX_NAV_PVAT_data_t; //Allocate RAM for the main struct
+ }
+
+ if (packetUBXNAVPVAT->callbackData == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("setAutoNAVPVATcallback: RAM alloc failed!"));
+ return (false);
+ }
+
+ packetUBXNAVPVAT->callbackPointer = callbackPointer; // RAM has been allocated so now update the pointer
+
+ return (true);
+}
+
+//In case no config access to the GNSS is possible and PVAT is send cyclically already
+//set config to suitable parameters
+bool SFE_UBLOX_GNSS::assumeAutoNAVPVAT(bool enabled, bool implicitUpdate)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Only attempt this if RAM allocation was successful
+ return false;
+
+ bool changes = packetUBXNAVPVAT->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate;
+ if (changes)
+ {
+ packetUBXNAVPVAT->automaticFlags.flags.bits.automatic = enabled;
+ packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate;
+ }
+ return changes;
+}
+
+// PRIVATE: Allocate RAM for packetUBXNAVPVAT and initialize it
+bool SFE_UBLOX_GNSS::initPacketUBXNAVPVAT()
+{
+ packetUBXNAVPVAT = new UBX_NAV_PVAT_t; //Allocate RAM for the main struct
+ if (packetUBXNAVPVAT == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("initPacketUBXNAVPVAT: RAM alloc failed!"));
+ return (false);
+ }
+ packetUBXNAVPVAT->automaticFlags.flags.all = 0;
+ packetUBXNAVPVAT->callbackPointer = NULL;
+ packetUBXNAVPVAT->callbackData = NULL;
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0;
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0;
+ return (true);
+}
+
+//Mark all the PVAT data as read/stale. This is handy to get data alignment after CRC failure
+void SFE_UBLOX_GNSS::flushNAVPVAT()
+{
+ if (packetUBXNAVPVAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!)
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0; //Mark all datums as stale (read before)
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0;
+}
+
+//Log this data in file buffer
+void SFE_UBLOX_GNSS::logNAVPVAT(bool enabled)
+{
+ if (packetUBXNAVPVAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!)
+ packetUBXNAVPVAT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled;
+}
+
// ***** NAV CLOCK automatic support
bool SFE_UBLOX_GNSS::getNAVCLOCK(uint16_t maxWait)
@@ -11449,6 +12057,54 @@ void SFE_UBLOX_GNSS::logHNRPVT(bool enabled)
// ***** Helper Functions for NMEA Logging / Processing
+// Set the mainTalkerId used by NMEA messages - allows all NMEA messages except GSV to be prefixed with GP instead of GN
+bool SFE_UBLOX_GNSS::setMainTalkerID(sfe_ublox_talker_ids_e id, uint16_t maxWait)
+{
+ //Get the current extended NMEA protocol configuration (V1)
+ packetCfg.cls = UBX_CLASS_CFG;
+ packetCfg.id = UBX_CFG_NMEA;
+ packetCfg.len = 0;
+ packetCfg.startingSpot = 0;
+
+ //Ask module for the current settings. Loads into payloadCfg.
+ if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
+ return (false);
+
+ payloadCfg[9] = (uint8_t)id;
+
+ packetCfg.len = 20;
+ packetCfg.startingSpot = 0;
+
+ return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK
+}
+
+// Enable/Disable NMEA High Precision Mode - include extra decimal places in the Lat and Lon
+bool SFE_UBLOX_GNSS::setHighPrecisionMode(bool enable, uint16_t maxWait)
+{
+ //Get the current extended NMEA protocol configuration (V1)
+ packetCfg.cls = UBX_CLASS_CFG;
+ packetCfg.id = UBX_CFG_NMEA;
+ packetCfg.len = 0;
+ packetCfg.startingSpot = 0;
+
+ //Ask module for the current settings. Loads into payloadCfg.
+ if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
+ return (false);
+
+ if (enable)
+ {
+ payloadCfg[3] |= (1 << 3); // Set the highPrec flag
+ payloadCfg[3] &= ~((1 << 0) | (1 << 2)); // Clear the compat and limit82 flags
+ }
+ else
+ payloadCfg[3] &= ~(1 << 3); // Clear the highPrec flag
+
+ packetCfg.len = 20;
+ packetCfg.startingSpot = 0;
+
+ return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK
+}
+
// Log selected NMEA messages to file buffer - if the messages are enabled and if the file buffer exists
// User needs to call setFileBufferSize before .begin
void SFE_UBLOX_GNSS::setNMEALoggingMask(uint32_t messages)
@@ -11470,6 +12126,153 @@ uint32_t SFE_UBLOX_GNSS::getProcessNMEAMask()
return (_processNMEA.all);
}
+// Initiate automatic storage of NMEA GPGGA messages
+
+// Get the most recent GPGGA message
+// Return 0 if the message has not been received from the module
+// Return 1 if the data is valid but has been read before
+// Return 2 if the data is valid and is fresh/unread
+uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPGGA(NMEA_GGA_data_t *data)
+{
+ if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); //Check that RAM has been allocated for the message
+ if (storageNMEAGPGGA == NULL) //Bail if the RAM allocation failed
+ return (false);
+
+ checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID.
+
+ memcpy(data, &storageNMEAGPGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy
+
+ uint8_t result = 0;
+ if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid?
+ {
+ result = 1;
+ if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read?
+ {
+ result = 2;
+ storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read
+ }
+ }
+
+ return (result);
+}
+
+//Enable a callback on the arrival of a GPGGA message
+bool SFE_UBLOX_GNSS::setNMEAGPGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t))
+{
+ if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); //Check that RAM has been allocated for the message
+ if (storageNMEAGPGGA == NULL) //Bail if the RAM allocation failed
+ return (false);
+
+ if (storageNMEAGPGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy
+ {
+ storageNMEAGPGGA->callbackCopy = new NMEA_GGA_data_t;
+ }
+
+ if (storageNMEAGPGGA->callbackCopy == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("setNMEAGPGGAcallback: RAM alloc failed!"));
+ return (false);
+ }
+
+ storageNMEAGPGGA->callbackPointer = callbackPointer;
+ return (true);
+}
+
+// Private: allocate RAM for incoming NMEA GPGGA messages and initialize it
+bool SFE_UBLOX_GNSS::initStorageNMEAGPGGA()
+{
+ storageNMEAGPGGA = new NMEA_GPGGA_t; //Allocate RAM for the main struct
+ if (storageNMEAGPGGA == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("initStorageNMEAGPGGA: RAM alloc failed!"));
+ return (false);
+ }
+
+ storageNMEAGPGGA->workingCopy.length = 0; // Clear the data length
+ memset(storageNMEAGPGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
+ storageNMEAGPGGA->completeCopy.length = 0; // Clear the data length
+ memset(storageNMEAGPGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
+
+ storageNMEAGPGGA->callbackPointer = NULL; // Clear the callback pointers
+ storageNMEAGPGGA->callbackCopy = NULL;
+
+ storageNMEAGPGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread
+
+ return (true);
+}
+
+uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNGGA(NMEA_GGA_data_t *data)
+{
+ if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); //Check that RAM has been allocated for the message
+ if (storageNMEAGNGGA == NULL) //Bail if the RAM allocation failed
+ return (false);
+
+ checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID.
+
+ memcpy(data, &storageNMEAGNGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy
+
+ uint8_t result = 0;
+ if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid?
+ {
+ result = 1;
+ if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read?
+ {
+ result = 2;
+ storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read
+ }
+ }
+
+ return (result);
+}
+
+bool SFE_UBLOX_GNSS::setNMEAGNGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t))
+{
+ if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); //Check that RAM has been allocated for the message
+ if (storageNMEAGNGGA == NULL) //Bail if the RAM allocation failed
+ return (false);
+
+ if (storageNMEAGNGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy
+ {
+ storageNMEAGNGGA->callbackCopy = new NMEA_GGA_data_t;
+ }
+
+ if (storageNMEAGNGGA->callbackCopy == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("setNMEAGNGGAcallback: RAM alloc failed!"));
+ return (false);
+ }
+
+ storageNMEAGNGGA->callbackPointer = callbackPointer;
+ return (true);
+}
+
+// Private: allocate RAM for incoming NMEA GNGGA messages and initialize it
+bool SFE_UBLOX_GNSS::initStorageNMEAGNGGA()
+{
+ storageNMEAGNGGA = new NMEA_GNGGA_t; //Allocate RAM for the main struct
+ if (storageNMEAGNGGA == NULL)
+ {
+ if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
+ _debugSerial->println(F("initStorageNMEAGNGGA: RAM alloc failed!"));
+ return (false);
+ }
+
+ storageNMEAGNGGA->workingCopy.length = 0; // Clear the data length
+ memset(storageNMEAGNGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
+ storageNMEAGNGGA->completeCopy.length = 0; // Clear the data length
+ memset(storageNMEAGNGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
+
+ storageNMEAGNGGA->callbackPointer = NULL; // Clear the callback pointers
+ storageNMEAGNGGA->callbackCopy = NULL;
+
+ storageNMEAGNGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread
+
+ return (true);
+}
+
// ***** CFG RATE Helper Functions
//Set the rate at which the module will give us an updated navigation solution
@@ -12525,6 +13328,60 @@ uint32_t SFE_UBLOX_GNSS::getVerticalAccuracy(uint16_t maxWait)
return (packetUBXNAVHPPOSLLH->data.vAcc);
}
+// ***** PVAT Helper Functions
+
+int32_t SFE_UBLOX_GNSS::getVehicleRoll(uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Bail if the RAM allocation failed
+ return 0;
+
+ if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehRoll == false)
+ getNAVPVAT(maxWait);
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehRoll = false; //Since we are about to give this to user, mark this data as stale
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+ return (packetUBXNAVPVAT->data.vehRoll);
+}
+
+int32_t SFE_UBLOX_GNSS::getVehiclePitch(uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Bail if the RAM allocation failed
+ return 0;
+
+ if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehPitch == false)
+ getNAVPVAT(maxWait);
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehPitch = false; //Since we are about to give this to user, mark this data as stale
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+ return (packetUBXNAVPVAT->data.vehPitch);
+}
+
+int32_t SFE_UBLOX_GNSS::getVehicleHeading(uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Bail if the RAM allocation failed
+ return 0;
+
+ if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehHeading == false)
+ getNAVPVAT(maxWait);
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehHeading = false; //Since we are about to give this to user, mark this data as stale
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+ return (packetUBXNAVPVAT->data.vehHeading);
+}
+
+int32_t SFE_UBLOX_GNSS::getMotionHeading(uint16_t maxWait)
+{
+ if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); //Check that RAM has been allocated for the PVAT data
+ if (packetUBXNAVPVAT == NULL) //Bail if the RAM allocation failed
+ return 0;
+
+ if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.motHeading == false)
+ getNAVPVAT(maxWait);
+ packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.motHeading = false; //Since we are about to give this to user, mark this data as stale
+ packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
+ return (packetUBXNAVPVAT->data.motHeading);
+}
+
// ***** SVIN Helper Functions
bool SFE_UBLOX_GNSS::getSurveyInActive(uint16_t maxWait)
diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h
index e6406ac..71b1e1b 100644
--- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h
+++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h
@@ -355,6 +355,7 @@ const uint8_t UBX_NAV_ORB = 0x34; //GNSS Orbit Database Info
const uint8_t UBX_NAV_POSECEF = 0x01; //Position Solution in ECEF
const uint8_t UBX_NAV_POSLLH = 0x02; //Geodetic Position Solution
const uint8_t UBX_NAV_PVT = 0x07; //All the things! Position, velocity, time, PDOP, height, h/v accuracies, number of satellites. Navigation Position Velocity Time Solution.
+const uint8_t UBX_NAV_PVAT = 0x17; //Navigation position velocity attitude time solution (ZED-F9R only)
const uint8_t UBX_NAV_RELPOSNED = 0x3C; //Relative Positioning Information in NED frame
const uint8_t UBX_NAV_RESETODO = 0x10; //Reset odometer
const uint8_t UBX_NAV_SAT = 0x35; //Satellite Information
@@ -378,7 +379,8 @@ const uint8_t UBX_RXM_PMREQ = 0x41; //Requests a Power Management task (two diff
const uint8_t UBX_RXM_RAWX = 0x15; //Multi-GNSS Raw Measurement Data
const uint8_t UBX_RXM_RLM = 0x59; //Galileo SAR Short-RLM report (two different packet sizes)
const uint8_t UBX_RXM_RTCM = 0x32; //RTCM input status
-const uint8_t UBX_RXM_SFRBX = 0x13; //Boradcast Navigation Data Subframe
+const uint8_t UBX_RXM_SFRBX = 0x13; //Broadcast Navigation Data Subframe
+const uint8_t UBX_RXM_SPARTN = 0x33; //SPARTN input status
//Class: SEC
//The following are used to configure the SEC UBX messages (security feature messages). Descriptions from UBX messages overview (ZED_F9P Interface Description Document page 36)
@@ -437,6 +439,7 @@ const uint8_t COM_PORT_SPI = 4;
const uint8_t COM_TYPE_UBX = (1 << 0);
const uint8_t COM_TYPE_NMEA = (1 << 1);
const uint8_t COM_TYPE_RTCM3 = (1 << 5);
+const uint8_t COM_TYPE_SPARTN = (1 << 6);
// Configuration Sub-Section mask definitions for saveConfigSelective (UBX-CFG-CFG)
const uint32_t VAL_CFG_SUBSEC_IOPORT = 0x00000001; // ioPort - communications port settings (causes IO system reset!)
@@ -468,7 +471,9 @@ enum dynModel // Possible values for the dynamic platform model, which provide m
DYN_MODEL_AIRBORNE2g, //Airborne <2g acceleration. Recommended for typical airborne environments. No 2D position fixes supported.
DYN_MODEL_AIRBORNE4g, //Airborne <4g acceleration. Only recommended for extremely dynamic environments. No 2D position fixes supported.
DYN_MODEL_WRIST, // Not supported in protocol versions less than 18. Only recommended for wrist worn applications. Receiver will filter out arm motion.
- DYN_MODEL_BIKE, // Supported in protocol versions 19.2
+ DYN_MODEL_BIKE, // Supported in protocol versions 19.2. (not available in all products)
+ DYN_MODEL_MOWER, // Added in HPS 1.21 (not available in all products)
+ DYN_MODEL_ESCOOTER, // Added in HPS 1.21 (not available in all products)
DYN_MODEL_UNKNOWN = 255 // getDynamicModel will return 255 if sendCommand fails
};
@@ -517,6 +522,25 @@ enum sfe_ublox_mga_ack_infocode_e
SFE_UBLOX_MGA_ACK_INFOCODE_TYPE_UNKNOWN
};
+// The mainTalkerId, set by UBX-CFG-NMEA setMainTalkerID
+enum sfe_ublox_talker_ids_e
+{
+ SFE_UBLOX_MAIN_TALKER_ID_DEFAULT,
+ SFE_UBLOX_MAIN_TALKER_ID_GP,
+ SFE_UBLOX_MAIN_TALKER_ID_GL,
+ SFE_UBLOX_MAIN_TALKER_ID_GN,
+ SFE_UBLOX_MAIN_TALKER_ID_GA,
+ SFE_UBLOX_MAIN_TALKER_ID_GB,
+ SFE_UBLOX_MAIN_TALKER_ID_GQ
+};
+
+// The DGNSS differential mode
+enum sfe_ublox_dgnss_mode_e
+{
+ SFE_UBLOX_DGNSS_MODE_FLOAT = 2, // No attempts are made to fix ambiguities
+ SFE_UBLOX_DGNSS_MODE_FIXED // Ambiguities are fixed whenever possible
+};
+
//-=-=-=-=-
#ifndef MAX_PAYLOAD_SIZE
@@ -767,17 +791,17 @@ class SFE_UBLOX_GNSS
//Port configurations
bool getPortSettings(uint8_t portID, uint16_t maxWait = defaultMaxWait); //Returns the current protocol bits in the UBX-CFG-PRT command for a given port
- bool setPortOutput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to output UBX, NMEA, RTCM3 or a combination thereof
- bool setPortInput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to input UBX, NMEA, RTCM3 or a combination thereof
+ bool setPortOutput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
+ bool setPortInput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to input UBX, NMEA, RTCM3, SPARTN or a combination thereof
bool setI2CAddress(uint8_t deviceAddress, uint16_t maxTime = defaultMaxWait); //Changes the I2C address of the u-blox module
void setSerialRate(uint32_t baudrate, uint8_t uartPort = COM_PORT_UART1, uint16_t maxTime = defaultMaxWait); //Changes the serial baud rate of the u-blox module, uartPort should be COM_PORT_UART1/2
- bool setI2COutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure I2C port to output UBX, NMEA, RTCM3 or a combination thereof
- bool setUART1Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART1 port to output UBX, NMEA, RTCM3 or a combination thereof
- bool setUART2Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART2 port to output UBX, NMEA, RTCM3 or a combination thereof
- bool setUSBOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure USB port to output UBX, NMEA, RTCM3 or a combination thereof
- bool setSPIOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure SPI port to output UBX, NMEA, RTCM3 or a combination thereof
+ bool setI2COutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure I2C port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
+ bool setUART1Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART1 port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
+ bool setUART2Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART2 port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
+ bool setUSBOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure USB port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
+ bool setSPIOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure SPI port to output UBX, NMEA, RTCM3, SPARTN or a combination thereof
void setNMEAOutputPort(Stream &nmeaOutputPort); //Sets the internal variable for the port to direct NMEA characters to
//Reset to defaults
@@ -812,6 +836,7 @@ class SFE_UBLOX_GNSS
// For Lat/Lon/Alt the units are: degrees^-7, degrees^-9, degrees^-7, degrees^-9, cm, 0.1mm
bool setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong = false, uint16_t maxWait = 250);
bool setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latLong = false, uint16_t maxWait = 250);
+ bool setDGNSSConfiguration(sfe_ublox_dgnss_mode_e dgnssMode = SFE_UBLOX_DGNSS_MODE_FIXED, uint16_t maxWait = defaultMaxWait); // Set the DGNSS differential mode
//Read the module's protocol version
uint8_t getProtocolVersionHigh(uint16_t maxWait = defaultMaxWait); //Returns the PROTVER XX.00 from UBX-MON-VER register
@@ -983,7 +1008,7 @@ class SFE_UBLOX_GNSS
void flushNAVHPPOSECEF(); //Mark all the data as read/stale
void logNAVHPPOSECEF(bool enabled = true); // Log data to file buffer
- bool getHPPOSLLH(uint16_t maxWait = defaultMaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new HPPOSLLH is available.
+ bool getHPPOSLLH(uint16_t maxWait = defaultMaxWait); // NAV HPPOSLLH
bool setAutoHPPOSLLH(bool enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic HPPOSLLH reports at the navigation frequency
bool setAutoHPPOSLLH(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic HPPOSLLH reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
bool setAutoHPPOSLLHrate(uint8_t rate, bool implicitUpdate = true, uint16_t maxWait = defaultMaxWait); //Set the rate for automatic HPPOSLLH reports
@@ -992,6 +1017,15 @@ class SFE_UBLOX_GNSS
void flushHPPOSLLH(); //Mark all the HPPPOSLLH data as read/stale. This is handy to get data alignment after CRC failure
void logNAVHPPOSLLH(bool enabled = true); // Log data to file buffer
+ bool getNAVPVAT(uint16_t maxWait = defaultMaxWait); // NAV PVAT
+ bool setAutoNAVPVAT(bool enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic PVAT reports at the navigation frequency
+ bool setAutoNAVPVAT(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic PVAT reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
+ bool setAutoNAVPVATrate(uint8_t rate, bool implicitUpdate = true, uint16_t maxWait = defaultMaxWait); //Set the rate for automatic PVAT reports
+ bool setAutoNAVPVATcallback(void (*callbackPointer)(UBX_NAV_PVAT_data_t), uint16_t maxWait = defaultMaxWait); //Enable automatic PVAT reports at the navigation frequency. Data is accessed from the callback.
+ bool assumeAutoNAVPVAT(bool enabled, bool implicitUpdate = true); //In case no config access to the GPS is possible and PVAT is send cyclically already
+ void flushNAVPVAT(); //Mark all the PVAT data as read/stale
+ void logNAVPVAT(bool enabled = true); // Log data to file buffer
+
bool getNAVCLOCK(uint16_t maxWait = defaultMaxWait); // NAV CLOCK
bool setAutoNAVCLOCK(bool enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic clock reports at the navigation frequency
bool setAutoNAVCLOCK(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic clock reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
@@ -1153,14 +1187,6 @@ class SFE_UBLOX_GNSS
void flushHNRPVT(); //Mark all the data as read/stale
void logHNRPVT(bool enabled = true); // Log data to file buffer
- // Helper functions for NMEA logging
- void setNMEALoggingMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Add selected NMEA messages to file buffer - if enabled. Default to adding ALL messages to the file buffer
- uint32_t getNMEALoggingMask(); // Return which NMEA messages are selected for logging to the file buffer - if enabled
-
- // Helper functions to control which NMEA messages are passed to processNMEA
- void setProcessNMEAMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Control which NMEA messages are passed to processNMEA. Default to passing ALL messages
- uint32_t getProcessNMEAMask(); // Return which NMEA messages are passed to processNMEA
-
// Helper functions for CFG RATE
bool setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = defaultMaxWait); //Set the number of nav solutions sent per second
@@ -1256,6 +1282,13 @@ class SFE_UBLOX_GNSS
uint32_t getHorizontalAccuracy(uint16_t maxWait = defaultMaxWait);
uint32_t getVerticalAccuracy(uint16_t maxWait = defaultMaxWait);
+ // Helper functions for PVAT
+
+ int32_t getVehicleRoll(uint16_t maxWait = defaultMaxWait); // Returns vehicle roll in degrees * 10^-5
+ int32_t getVehiclePitch(uint16_t maxWait = defaultMaxWait); // Returns vehicle pitch in degrees * 10^-5
+ int32_t getVehicleHeading(uint16_t maxWait = defaultMaxWait); // Returns vehicle heading in degrees * 10^-5
+ int32_t getMotionHeading(uint16_t maxWait = defaultMaxWait); // Returns the motion heading in degrees * 10^-5
+
// Helper functions for SVIN
bool getSurveyInActive(uint16_t maxWait = defaultMaxWait);
@@ -1302,6 +1335,26 @@ class SFE_UBLOX_GNSS
float getHNRpitch(uint16_t maxWait = defaultMaxWait); // Returned as degrees
float getHNRheading(uint16_t maxWait = defaultMaxWait); // Returned as degrees
+ // Set the mainTalkerId used by NMEA messages - allows all NMEA messages except GSV to be prefixed with GP instead of GN
+ bool setMainTalkerID(sfe_ublox_talker_ids_e id = SFE_UBLOX_MAIN_TALKER_ID_DEFAULT, uint16_t maxWait = defaultMaxWait);
+
+ // Enable/Disable NMEA High Precision Mode - include extra decimal places in the Lat and Lon
+ bool setHighPrecisionMode(bool enable = true, uint16_t maxWait = defaultMaxWait);
+
+ // Helper functions for NMEA logging
+ void setNMEALoggingMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Add selected NMEA messages to file buffer - if enabled. Default to adding ALL messages to the file buffer
+ uint32_t getNMEALoggingMask(); // Return which NMEA messages are selected for logging to the file buffer - if enabled
+
+ // Helper functions to control which NMEA messages are passed to processNMEA
+ void setProcessNMEAMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Control which NMEA messages are passed to processNMEA. Default to passing ALL messages
+ uint32_t getProcessNMEAMask(); // Return which NMEA messages are passed to processNMEA
+
+ // Support for "auto" storage of NMEA messages
+ uint8_t getLatestNMEAGPGGA(NMEA_GGA_data_t *data); // Return the most recent GPGGA: 0 = no data, 1 = stale data, 2 = fresh data
+ bool setNMEAGPGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)); //Enable a callback on the arrival of a GPGGA message
+ uint8_t getLatestNMEAGNGGA(NMEA_GGA_data_t *data); // Return the most recent GNGGA: 0 = no data, 1 = stale data, 2 = fresh data
+ bool setNMEAGNGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)); //Enable a callback on the arrival of a GNGGA message
+
// Functions to extract signed and unsigned 8/16/32-bit data from a ubxPacket
// From v2.0: These are public. The user can call these to extract data from custom packets
uint32_t extractLong(ubxPacket *msg, uint8_t spotToStart); //Combine four bytes from payload into long
@@ -1324,6 +1377,7 @@ class SFE_UBLOX_GNSS
UBX_NAV_VELNED_t *packetUBXNAVVELNED = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_NAV_HPPOSECEF_t *packetUBXNAVHPPOSECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_NAV_HPPOSLLH_t *packetUBXNAVHPPOSLLH = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
+ UBX_NAV_PVAT_t *packetUBXNAVPVAT = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_NAV_CLOCK_t *packetUBXNAVCLOCK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_NAV_TIMELS_t *packetUBXNAVTIMELS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_NAV_SVIN_t *packetUBXNAVSVIN = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
@@ -1351,6 +1405,9 @@ class SFE_UBLOX_GNSS
UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_MGA_DBD_t *packetUBXMGADBD = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
+ NMEA_GPGGA_t *storageNMEAGPGGA = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
+ NMEA_GNGGA_t *storageNMEAGNGGA = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
+
uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame
private:
@@ -1408,6 +1465,7 @@ class SFE_UBLOX_GNSS
bool initPacketUBXNAVVELNED(); // Allocate RAM for packetUBXNAVVELNED and initialize it
bool initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it
bool initPacketUBXNAVHPPOSLLH(); // Allocate RAM for packetUBXNAVHPPOSLLH and initialize it
+ bool initPacketUBXNAVPVAT(); // Allocate RAM for packetUBXNAVPVAT and initialize it
bool initPacketUBXNAVCLOCK(); // Allocate RAM for packetUBXNAVCLOCK and initialize it
bool initPacketUBXNAVTIMELS(); // Allocate RAM for packetUBXNAVTIMELS and initialize it
bool initPacketUBXNAVSVIN(); // Allocate RAM for packetUBXNAVSVIN and initialize it
@@ -1429,6 +1487,9 @@ class SFE_UBLOX_GNSS
bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it
bool initPacketUBXMGADBD(); // Allocate RAM for packetUBXMGADBD and initialize it
+ bool initStorageNMEAGPGGA(); // Allocate RAM for incoming NMEA GPGGA messages and initialize it
+ bool initStorageNMEAGNGGA(); // Allocate RAM for incoming NMEA GNGGA messages and initialize it
+
//Variables
TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
Stream *_serialPort; //The generic connection to user's chosen Serial hardware
@@ -1503,6 +1564,17 @@ class SFE_UBLOX_GNSS
bool processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA
bool isNMEAHeaderValid(); // Return true if the six byte NMEA header appears valid. Used to set _signsOfLife
+ bool isThisNMEAauto(); // Check if the NMEA message (in nmeaAddressField) is "auto" (i.e. has RAM allocated for it)
+ bool doesThisNMEAHaveCallback(); // Do we need to copy the data into the callback copy?
+ uint8_t *getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
+ uint8_t *getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
+ uint8_t *getNMEACompleteLengthPtr(); // Get a pointer to the complete copy length
+ uint8_t *getNMEACompleteNMEAPtr(); // Get a pointer to the complete copy NMEA data
+ uint8_t *getNMEACallbackLengthPtr(); // Get a pointer to the callback copy length
+ uint8_t *getNMEACallbackNMEAPtr(); // Get a pointer to the callback copy NMEA data
+ uint8_t getNMEAMaxLength(); // Get the maximum length of this NMEA message
+ nmeaAutomaticFlags *getNMEAFlagsPtr(); // Get a pointer to the flags
+
uint16_t rtcmLen = 0;
// Flag to prevent reentry into checkCallbacks
diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h
index 6caba0f..14a2bb5 100644
--- a/src/u-blox_config_keys.h
+++ b/src/u-blox_config_keys.h
@@ -134,6 +134,7 @@ const uint32_t UBLOX_CFG_I2C_ENABLED = 0x10510003; // Flag to indicate if the I2
const uint32_t UBLOX_CFG_I2CINPROT_UBX = 0x10710001; // Flag to indicate if UBX should be an input protocol on I2C
const uint32_t UBLOX_CFG_I2CINPROT_NMEA = 0x10710002; // Flag to indicate if NMEA should be an input protocol on I2C
const uint32_t UBLOX_CFG_I2CINPROT_RTCM3X = 0x10710004; // Flag to indicate if RTCM3X should be an input protocol on I2C
+const uint32_t UBLOX_CFG_I2CINPROT_SPARTN = 0x10710005; // Flag to indicate if SPARTN should be an input protocol on I2C
//CFG-I2COUTPROT: Output protocol configuration of the I2C interface
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -550,6 +551,16 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SFRBX_SPI = 0x20910235; // Output rate
const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SFRBX_UART1 = 0x20910232; // Output rate of the UBX-RXM-SFRBX message on port UART1
const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SFRBX_UART2 = 0x20910233; // Output rate of the UBX-RXM-SFRBX message on port UART2
const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SFRBX_USB = 0x20910234; // Output rate of the UBX-RXM-SFRBX message on port USB
+const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SPARTN_I2C = 0x20910605; // Output rate of the UBX-RXM-SPARTN message on port I2C
+const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SPARTN_UART1 = 0x20910606; // Output rate of the UBX-RXM-SPARTN message on port UART1
+const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SPARTN_UART2 = 0x20910607; // Output rate of the UBX-RXM-SPARTN message on port UART2
+const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SPARTN_USB = 0x20910608; // Output rate of the UBX-RXM-SPARTN message on port USB
+const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_SPARTN_SPI = 0x20910609; // Output rate of the UBX-RXM-SPARTN message on port SPI
+const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_I2C = 0x20910634; // Output rate of the UBX-SEC-SIG message on port I2C
+const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_SPI = 0x20910638; // Output rate of the UBX-SEC-SIG message on port SPI
+const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART1 = 0x20910635; // Output rate of the UBX-SEC-SIG message on port UART1
+const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART2 = 0x20910636; // Output rate of the UBX-SEC-SIG message on port UART2
+const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_USB = 0x20910637; // Output rate of the UBX-SEC-SIG message on port USB
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_TM2_I2C = 0x20910178; // Output rate of the UBX-TIM-TM2 message on port I2C
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_TM2_SPI = 0x2091017c; // Output rate of the UBX-TIM-TM2 message on port SPI
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_TM2_UART1 = 0x20910179; // Output rate of the UBX-TIM-TM2 message on port UART1
@@ -566,7 +577,7 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_VRFY_UART1 = 0x20910093; // Output rat
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_VRFY_UART2 = 0x20910094; // Output rate of the UBX-TIM-VRFY message on port UART2
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_VRFY_USB = 0x20910095; // Output rate of the UBX-TIM-VRFY message on port USB
-//Additional CFG_MSGOUT keys for the ZED-F9R HPS120
+//Additional CFG_MSGOUT keys for the ZED-F9R HPS121
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_COV_I2C = 0x20910083; // Output rate of the UBX-NAV-COV message on port I2C
const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_COV_UART1 = 0x20910084; // Output rate of the UBX-NAV-COV message on port UART1
@@ -608,6 +619,12 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_EELL_UART1 = 0x20910314; // Output rate
const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_EELL_UART2 = 0x20910315; // Output rate of the UBX-NAV-EELL message on port UART2
const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_EELL_USB = 0x20910316; // Output rate of the UBX-NAV-EELL message on port USB
const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_EELL_SPI = 0x20910317; // Output rate of the UBX-NAV-EELL message on port SPI
+const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_PVAT_I2C = 0x2091062a; // Output rate of the UBX-NAV-PVAT message on port I2C
+const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_PVAT_UART1 = 0x2091062b; // Output rate of the UBX-NAV-PVAT message on port UART1
+const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_PVAT_UART2 = 0x2091062c; // Output rate of the UBX-NAV-PVAT message on port UART2
+const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_PVAT_USB = 0x2091062d; // Output rate of the UBX-NAV-PVAT message on port USB
+const uint32_t UBLOX_CFG_MSGOUT_UBX_NAV_PVAT_SPI = 0x2091062e; // Output rate of the UBX-NAV-PVAT message on port SPI
+
//Additional CFG_MSGOUT keys for the ZED-F9T
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -761,11 +778,6 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIGLOG_SPI = 0x2091068d; // Output rate
const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIGLOG_UART1 = 0x2091068a; // Output rate of the UBX-SEC-SIGLOG message on port UART1
const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIGLOG_UART2 = 0x2091068b; // Output rate of the UBX-SEC-SIGLOG message on port UART2
const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIGLOG_USB = 0x2091068c; // Output rate of the UBX-SEC-SIGLOG message on port USB
-const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_I2C = 0x20910634; // Output rate of the UBX-DBG-SKYMAP message on port I2C
-const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_SPI = 0x20910638; // Output rate of the UBX-SEC-SIG message on port SPI
-const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART1 = 0x20910635; // Output rate of the UBX-SEC-SIG message on port UART1
-const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART2 = 0x20910636; // Output rate of the UBX-SEC-SIG message on port UART2
-const uint32_t UBLOX_CFG_MSGOUT_UBX_SEC_SIG_USB = 0x20910637; // Output rate of the UBX-SEC-SIG message on port USB
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_SVIN_I2C = 0x20910097; // Output rate of the UBX-TIM-SVIN message on port I2C
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_SVIN_SPI = 0x2091009b; // Output rate of the UBX-TIM-SVIN message on port SPI
const uint32_t UBLOX_CFG_MSGOUT_UBX_TIM_SVIN_UART1 = 0x20910098; // Output rate of the UBX-TIM-SVIN message on port UART1
@@ -976,6 +988,7 @@ const uint32_t UBLOX_CFG_SPI_ENABLED = 0x10640006; // Flag to indicate if the SP
const uint32_t UBLOX_CFG_SPIINPROT_UBX = 0x10790001; // Flag to indicate if UBX should be an input protocol on SPI
const uint32_t UBLOX_CFG_SPIINPROT_NMEA = 0x10790002; // Flag to indicate if NMEA should be an input protocol on SPI
const uint32_t UBLOX_CFG_SPIINPROT_RTCM3X = 0x10790004; // Flag to indicate if RTCM3X should be an input protocol on SPI
+const uint32_t UBLOX_CFG_SPIINPROT_SPARTN = 0x10790005; // Flag to indicate if SPARTN should be an input protocol on SPI
//CFG-SPIOUTPROT: Output protocol configuration of the SPI interface
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -1062,6 +1075,7 @@ const uint32_t UBLOX_CFG_UART1_ENABLED = 0x10520005; // Flag to indicate if the
const uint32_t UBLOX_CFG_UART1INPROT_UBX = 0x10730001; // Flag to indicate if UBX should be an input protocol on UART1
const uint32_t UBLOX_CFG_UART1INPROT_NMEA = 0x10730002; // Flag to indicate if NMEA should be an input protocol on UART1
const uint32_t UBLOX_CFG_UART1INPROT_RTCM3X = 0x10730004; // Flag to indicate if RTCM3X should be an input protocol on UART1
+const uint32_t UBLOX_CFG_UART1INPROT_SPARTN = 0x10730005; // Flag to indicate if SPARTN should be an input protocol on UART1
//CFG-UART1OUTPROT: Output protocol configuration of the UART1 interface
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -1083,6 +1097,7 @@ const uint32_t UBLOX_CFG_UART2_REMAP = 0x10530006; // UART2 Remapping
const uint32_t UBLOX_CFG_UART2INPROT_UBX = 0x10750001; // Flag to indicate if UBX should be an input protocol on UART2
const uint32_t UBLOX_CFG_UART2INPROT_NMEA = 0x10750002; // Flag to indicate if NMEA should be an input protocol on UART2
const uint32_t UBLOX_CFG_UART2INPROT_RTCM3X = 0x10750004; // Flag to indicate if RTCM3X should be an input protocol on UART2
+const uint32_t UBLOX_CFG_UART2INPROT_SPARTN = 0x10750005; // Flag to indicate if SPARTN should be an input protocol on UART2
//CFG-UART2OUTPROT: Output protocol configuration of the UART2 interface
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -1115,6 +1130,7 @@ const uint32_t UBLOX_CFG_USB_SERIAL_NO_STR3 = 0x50650018; // Serial number strin
const uint32_t UBLOX_CFG_USBINPROT_UBX = 0x10770001; // Flag to indicate if UBX should be an input protocol on USB
const uint32_t UBLOX_CFG_USBINPROT_NMEA = 0x10770002; // Flag to indicate if NMEA should be an input protocol on USB
const uint32_t UBLOX_CFG_USBINPROT_RTCM3X = 0x10770004; // Flag to indicate if RTCM3X should be an input protocol on USB
+const uint32_t UBLOX_CFG_USBINPROT_SPARTN = 0x10770005; // Flag to indicate if SPARTN should be an input protocol on USB
//CFG-USBOUTPROT: Output protocol configuration of the USB interface
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h
index a749ce4..eb51598 100644
--- a/src/u-blox_structs.h
+++ b/src/u-blox_structs.h
@@ -753,6 +753,180 @@ typedef struct
UBX_NAV_HPPOSLLH_data_t *callbackData;
} UBX_NAV_HPPOSLLH_t;
+// UBX-NAV-PVAT (0x01 0x17): Navigation position velocity attitude time solution
+const uint16_t UBX_NAV_PVAT_LEN = 116;
+
+typedef struct
+{
+ uint32_t iTOW; // GPS time of week of the navigation epoch: ms
+ uint8_t version; // Message version (0x00 for this version)
+ union
+ {
+ uint8_t all;
+ struct
+ {
+ uint8_t validDate : 1; // 1 = valid UTC Date
+ uint8_t validTime : 1; // 1 = valid UTC time of day
+ uint8_t fullyResolved : 1; // 1 = UTC time of day has been fully resolved (no seconds uncertainty).
+ uint8_t validMag : 1; // 1 = valid magnetic declination
+ } bits;
+ } valid;
+ uint16_t year; // Year (UTC)
+ uint8_t month; // Month, range 1..12 (UTC)
+ uint8_t day; // Day of month, range 1..31 (UTC)
+ uint8_t hour; // Hour of day, range 0..23 (UTC)
+ uint8_t min; // Minute of hour, range 0..59 (UTC)
+ uint8_t sec; // Seconds of minute, range 0..60 (UTC)
+ uint8_t reserved0;
+ uint8_t reserved1[2];
+ uint32_t tAcc; // Time accuracy estimate (UTC): ns
+ int32_t nano; // Fraction of second, range -1e9 .. 1e9 (UTC): ns
+ uint8_t fixType; // GNSSfix Type:
+ // 0: no fix
+ // 1: dead reckoning only
+ // 2: 2D-fix
+ // 3: 3D-fix
+ // 4: GNSS + dead reckoning combined
+ // 5: time only fix
+ union
+ {
+ uint8_t all;
+ struct
+ {
+ uint8_t gnssFixOK : 1; // 1 = valid fix (i.e within DOP & accuracy masks)
+ uint8_t diffSoln : 1; // 1 = differential corrections were applied
+ uint8_t reserved : 1;
+ uint8_t vehRollValid : 1; // 1 = roll of vehicle is valid, only set if the receiver is in sensor fusion mode
+ uint8_t vehPitchValid : 1; // 1 = pitch of vehicle is valid, only set if the receiver is in sensor fusion mode
+ uint8_t vehHeadingValid : 1; // 1 = heading of vehicle is valid, only set if the receiver is in sensor fusion mode
+ uint8_t carrSoln : 2; // Carrier phase range solution status:
+ // 0: no carrier phase range solution
+ // 1: carrier phase range solution with floating ambiguities
+ // 2: carrier phase range solution with fixed ambiguities
+ } bits;
+ } flags;
+ union
+ {
+ uint8_t all;
+ struct
+ {
+ uint8_t reserved : 5;
+ uint8_t confirmedAvai : 1; // 1 = information about UTC Date and Time of Day validity confirmation is available
+ uint8_t confirmedDate : 1; // 1 = UTC Date validity could be confirmed
+ uint8_t confirmedTime : 1; // 1 = UTC Time of Day could be confirmed
+ } bits;
+ } flags2;
+ uint8_t numSV; // Number of satellites used in Nav Solution
+ int32_t lon; // Longitude: deg * 1e-7
+ int32_t lat; // Latitude: deg * 1e-7
+ int32_t height; // Height above ellipsoid: mm
+ int32_t hMSL; // Height above mean sea level: mm
+ uint32_t hAcc; // Horizontal accuracy estimate: mm
+ uint32_t vAcc; // Vertical accuracy estimate: mm
+ int32_t velN; // NED north velocity: mm/s
+ int32_t velE; // NED east velocity: mm/s
+ int32_t velD; // NED down velocity: mm/s
+ int32_t gSpeed; // Ground Speed (2-D): mm/s
+ uint32_t sAcc; // Speed accuracy estimate: mm/s
+ int32_t vehRoll; // Vehicle roll: 1e-5 deg
+ int32_t vehPitch; // Vehicle pitch: 1e-5 deg
+ int32_t vehHeading; // Vehicle heading: 1e-5 deg
+ int32_t motHeading; // Motion heading.: 1e-5 deg
+ uint16_t accRoll; // Vehicle roll accuracy (if null, roll angle is not available): 1e-2 deg
+ uint16_t accPitch; // Vehicle pitch accuracy (if null, pitch angle is not available): 1e-2 deg
+ uint16_t accHeading; // Vehicle heading accuracy (if null, heading angle is not available): 1e-2 deg
+ int16_t magDec; // Magnetic declination: 1e-2 deg
+ uint16_t magAcc; // Magnetic declination accuracy: 1e-2 deg
+ uint16_t errEllipseOrient; // Orientation of semi-major axis of error ellipse (degrees from true north): 1e-2 deg
+ uint32_t errEllipseMajor; // Semi-major axis of error ellipse: mm
+ uint32_t errEllipseMinor; // Semi-minor axis of error ellipse: mm
+ uint8_t reserved2[4];
+ uint8_t reserved3[4];
+} UBX_NAV_PVAT_data_t;
+
+typedef struct
+{
+ union
+ {
+ uint32_t all;
+ struct
+ {
+ uint32_t all : 1;
+
+ uint32_t iTOW : 1;
+ uint32_t version : 1;
+
+ uint32_t validDate : 1;
+ uint32_t validTime : 1;
+ uint32_t fullyResolved : 1;
+ uint32_t validMag : 1;
+
+ uint32_t year : 1;
+ uint32_t month : 1;
+ uint32_t day : 1;
+ uint32_t hour : 1;
+ uint32_t min : 1;
+ uint32_t sec : 1;
+
+ uint32_t tAcc : 1;
+ uint32_t nano : 1;
+ uint32_t fixType : 1;
+
+ uint32_t gnssFixOK : 1;
+ uint32_t diffSoln : 1;
+ uint32_t vehRollValid : 1;
+ uint32_t vehPitchValid : 1;
+ uint32_t vehHeadingValid : 1;
+ uint32_t carrSoln : 1;
+
+ uint32_t confirmedAvai : 1;
+ uint32_t confirmedDate : 1;
+ uint32_t confirmedTime : 1;
+
+ uint32_t numSV : 1;
+ uint32_t lon : 1;
+ uint32_t lat : 1;
+ uint32_t height : 1;
+ uint32_t hMSL : 1;
+ uint32_t hAcc : 1;
+ uint32_t vAcc : 1;
+ } bits;
+ } moduleQueried1;
+ union
+ {
+ uint32_t all;
+ struct
+ {
+ uint32_t velN : 1;
+ uint32_t velE : 1;
+ uint32_t velD : 1;
+ uint32_t gSpeed : 1;
+ uint32_t sAcc : 1;
+ uint32_t vehRoll : 1;
+ uint32_t vehPitch : 1;
+ uint32_t vehHeading : 1;
+ uint32_t motHeading : 1;
+ uint32_t accRoll : 1;
+ uint32_t accPitch : 1;
+ uint32_t accHeading : 1;
+ uint32_t magDec : 1;
+ uint32_t magAcc : 1;
+ uint32_t errEllipseOrient : 1;
+ uint32_t errEllipseMajor : 1;
+ uint32_t errEllipseMinor : 1;
+ } bits;
+ } moduleQueried2;
+} UBX_NAV_PVAT_moduleQueried_t;
+
+typedef struct
+{
+ ubxAutomaticFlags automaticFlags;
+ UBX_NAV_PVAT_data_t data;
+ UBX_NAV_PVAT_moduleQueried_t moduleQueried;
+ void (*callbackPointer)(UBX_NAV_PVAT_data_t);
+ UBX_NAV_PVAT_data_t *callbackData;
+} UBX_NAV_PVAT_t;
+
// UBX-NAV-TIMEUTC (0x01 0x21): UTC time solution
const uint16_t UBX_NAV_TIMEUTC_LEN = 20;
@@ -2058,4 +2232,52 @@ typedef struct
UBX_HNR_INS_data_t *callbackData;
} UBX_HNR_INS_t;
+// NMEA-specific structs
+
+//Additional flags and pointers that need to be stored with each message type
+struct nmeaAutomaticFlags
+{
+ union
+ {
+ uint8_t all;
+ struct
+ {
+ uint8_t completeCopyValid : 1; // Is the copy of the data struct used by the get function valid/fresh? 0 = invalid, 1 = valid
+ uint8_t completeCopyRead : 1; // Has the complete copy been read? 0 = unread, 1 = read
+ uint8_t callbackCopyValid : 1; // Is the copy of the data struct used by the callback valid/fresh? 0 = invalid/stale, 1 = valid/fresh
+ } bits;
+ } flags;
+};
+
+// The max length for NMEA messages should be 82 bytes, but GGA messages can exceed that if they include the
+// extra decimal places for "High Precision Mode".
+//
+// To be safe, let's allocate 100 bytes to store the GGA message
+
+const uint8_t NMEA_GGA_MAX_LENGTH = 100;
+
+typedef struct
+{
+ uint8_t length; // The number of bytes in nmea
+ uint8_t nmea[NMEA_GGA_MAX_LENGTH];
+} NMEA_GGA_data_t;
+
+typedef struct
+{
+ nmeaAutomaticFlags automaticFlags;
+ NMEA_GGA_data_t workingCopy; // Incoming data is added to the working copy
+ NMEA_GGA_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid
+ void (*callbackPointer)(NMEA_GGA_data_t);
+ NMEA_GGA_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy
+} NMEA_GPGGA_t;
+
+typedef struct
+{
+ nmeaAutomaticFlags automaticFlags;
+ NMEA_GGA_data_t workingCopy; // Incoming data is added to the working copy
+ NMEA_GGA_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid
+ void (*callbackPointer)(NMEA_GGA_data_t);
+ NMEA_GGA_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy
+} NMEA_GNGGA_t;
+
#endif