From 42331c9f43ebbdaa1f361332fb23f05699be2509 Mon Sep 17 00:00:00 2001 From: Felix Jirka Date: Fri, 7 Jun 2019 18:41:11 +0200 Subject: [PATCH 1/4] initial commit from arduino lib, just to presever changes made unvoluntarily --- .gitignore | 50 + LICENSE.md | 55 + README.md | 101 ++ .../Example10_ResetConfiguration.ino | 59 + .../Example11_UseUart/Example11_UseUart.ino | 98 + .../Example12_FactoryDefault_I2C.ino | 58 + .../Example12_FactoryDefaults_Serial.ino | 95 + .../Example13_AutoPVT/Example13_AutoPVT.ino | 83 + .../Example14_DebugOutput.ino | 80 + .../Example15_GetDateTime.ino | 95 + .../Example1_BasicNMEARead.ino | 51 + .../Example2_NMEAParsing.ino | 83 + .../Example3_GetPosition.ino | 84 + .../Example4_FixType/Example4_FixType.ino | 94 + .../Example5_SpeedHeadingPrecision.ino | 83 + .../Example6_OutputRate.ino | 91 + .../Example7_GetProtocolVersion.ino | 61 + .../Example8_ChangeI2CAddress.ino | 112 ++ .../Example9_AltitudeMSL.ino | 82 + .../Example1_EnableRTCM.ino | 77 + .../Example2_StartRTCMBase.ino | 153 ++ .../Example3_BaseWithLCD.ino | 185 ++ .../Example1_GetPositionAccuracy.ino | 77 + .../Example2_ValConfigurationMethod.ino | 60 + .../Example3_StartRTCMBase.ino | 161 ++ .../Example4_BaseWithLCD.ino | 200 +++ ...xample5_RelativePositioningInformation.ino | 125 ++ .../Example6_GetVal/Example6_GetVal.ino | 86 + .../Example7_SetVal/Example7_SetVal.ino | 75 + keywords.txt | 100 ++ library.properties | 9 + src/SparkFun_Ublox_Arduino_Library.cpp | 1577 +++++++++++++++++ src/SparkFun_Ublox_Arduino_Library.h | 438 +++++ 33 files changed, 4838 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 examples/Example10_ResetConfiguration/Example10_ResetConfiguration.ino create mode 100644 examples/Example11_UseUart/Example11_UseUart.ino create mode 100644 examples/Example12_FactoryDefault_I2C/Example12_FactoryDefault_I2C.ino create mode 100644 examples/Example12_FactoryDefaults_Serial/Example12_FactoryDefaults_Serial.ino create mode 100644 examples/Example13_AutoPVT/Example13_AutoPVT.ino create mode 100644 examples/Example14_DebugOutput/Example14_DebugOutput.ino create mode 100644 examples/Example15_GetDateTime/Example15_GetDateTime.ino create mode 100644 examples/Example1_BasicNMEARead/Example1_BasicNMEARead.ino create mode 100644 examples/Example2_NMEAParsing/Example2_NMEAParsing.ino create mode 100644 examples/Example3_GetPosition/Example3_GetPosition.ino create mode 100644 examples/Example4_FixType/Example4_FixType.ino create mode 100644 examples/Example5_SpeedHeadingPrecision/Example5_SpeedHeadingPrecision.ino create mode 100644 examples/Example6_OutputRate/Example6_OutputRate.ino create mode 100644 examples/Example7_GetProtocolVersion/Example7_GetProtocolVersion.ino create mode 100644 examples/Example8_ChangeI2CAddress/Example8_ChangeI2CAddress.ino create mode 100644 examples/Example9_AltitudeMSL/Example9_AltitudeMSL.ino create mode 100644 examples/NEO-M8P-2/Example1_EnableRTCM/Example1_EnableRTCM.ino create mode 100644 examples/NEO-M8P-2/Example2_StartRTCMBase/Example2_StartRTCMBase.ino create mode 100644 examples/NEO-M8P-2/Example3_BaseWithLCD/Example3_BaseWithLCD.ino create mode 100644 examples/ZED-F9P/Example1_GetPositionAccuracy/Example1_GetPositionAccuracy.ino create mode 100644 examples/ZED-F9P/Example2_ValConfigurationMethod/Example2_ValConfigurationMethod.ino create mode 100644 examples/ZED-F9P/Example3_StartRTCMBase/Example3_StartRTCMBase.ino create mode 100644 examples/ZED-F9P/Example4_BaseWithLCD/Example4_BaseWithLCD.ino create mode 100644 examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino create mode 100644 examples/ZED-F9P/Example6_GetVal/Example6_GetVal.ino create mode 100644 examples/ZED-F9P/Example7_SetVal/Example7_SetVal.ino create mode 100644 keywords.txt create mode 100644 library.properties create mode 100644 src/SparkFun_Ublox_Arduino_Library.cpp create mode 100644 src/SparkFun_Ublox_Arduino_Library.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4eedb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Visual Studio Code files +.vscode + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e64bd4e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,55 @@ +SparkFun License Information +============================ + +SparkFun uses two different licenses for our files — one for hardware and one for code. + +Hardware +--------- + +**SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).** + +Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode). + +You are free to: + +Share — copy and redistribute the material in any medium or format +Adapt — remix, transform, and build upon the material +for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. +ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. +No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. +Notices: + +You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. + + +Code +-------- + +**SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).** + +The MIT License (MIT) + +Copyright (c) 2016 SparkFun Electronics + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa2cceb --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +SparkFun Ublox Arduino Library +=========================================================== + + + + + + + + + + + + + + +
SparkFun GPS-RTK2 - ZED-F9P (GPS-15136)SparkFun GPS-RTK - NEO-M8P-2 (GPS-15005)SparkFun ZOE-M8Q Breakout (GPS-15193)SparkFun SAM-M8Q Breakout (GPS-15210)
+ +Ublox makes some incredible GPS receivers covering everything from low-cost, highly configurable modules such as the SAM-M8Q all the way up to the surveyor grade ZED-F9P with precision of the diameter of a dime. This library focuses on configuration and control of Ublox devices over I2C (called DDC by Ublox) and Serial. The UBX protocol is supported over both I2C and serial, and is a much easier and lighterweight interface to a GPS module. Stop parsing NMEA data! And simply ask for the datums you need. + +This library can be installed via the Arduino Library manager. Search for **SparkFun Ublox**. + +Want to help? Please do! We are always looking for ways to improve and build out features of this library. + +Thanks to: + +* We are always interested in adding SPI support with a checkUbloxSPI() function +* [trycoon](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/pull/7) for fixing the lack of I2C buffer length defines +* [tve](https://github.com/tve) for building out serial additions and examples +* [Redstoned](https://github.com/Redstoned) and [davidallenmann](https://github.com/davidallenmann) for adding PVT date and time + +Need a library for the Ublox and Particle? Checkout the [Particle library](https://github.com/aseelye/SparkFun_Ublox_Particle_Library) fork. + +Repository Contents +------------------- + +* **/examples** - Example sketches for the library (.ino). Run these from the Arduino IDE. +* **/src** - Source files for the library (.cpp, .h). +* **keywords.txt** - Keywords from this library that will be highlighted in the Arduino IDE. +* **library.properties** - General library properties for the Arduino package manager. + +Documentation +-------------- + +* **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library. + +Polling vs. auto-reporting +-------------------------- + +This library supports two modes of operation for getting navigation information with the `getPVT` +function (based on the `UBX_NAV_PVT` protocol packet): polling and auto-reporting. + +The standard method is for the sketch to call `getPVT` (or one of the `getLatitude`, `getLongitude`, +etc. methods) when it needs a fresh navigation solution. At that point the library sends a request +to the GPS to produce a fresh solution. The GPS then waits until the next measurement occurs (e.g. +once per second or as set using `setNavigationFrequency`) and then sends the fresh data. +The advantage of this method is that the data received is always fresh, the downside is that getPVT +can block until the next measurement is made by the GPS, e.g. up to 1 second if the nav frequency is +set to one second. + +An alternate method can be chosen using `setAutoPVT(true)` which instructs the GPS to send the +navigation information (`UBX_NAV_PVT` packet) as soon as it is produced. This is the way the older +NMEA navigation data has been used for years. The sketch continues to call `getPVT` as before but +under the hood the library returns the data of the last solution received from the GPS, which may be +a bit out of date (how much depends on the `setNavigationFrequency` value). + +The advantage of this method is that getPVT does not block: it returns true if new data is available +and false otherwise. The disadvantages are that the data may be a bit old and that buffering for +these spontaneus `UBX_NAV_PVT` packets is required (100 bytes each). When using Serial the buffering +is an issue because the std serial buffer is 32 or 64 bytes long depending on Arduino version. When +using I2C the buffering is not an issue because the GPS device has at least 1KB of internal buffering +(possibly as large as 4KB). + +As an example, assume that the GPS is set to produce 5 navigation +solutions per second and that the sketch only calls getPVT once a second, then the GPS will queue 5 +packets in its internal buffer (about 500 bytes) and the library will read those when getPVT is +called, update its internal copy of the nav data 5 times, and return `true` to the sketch. The +skecth calls `getLatitude`, etc. and retrieve the data of the most recent of those 5 packets. + +Products That Use This Library +--------------------------------- + +* [GPS-15136](https://www.sparkfun.com/products/15136) - SparkFun GPS-RTK2 ZED-F9P +* [GPS-15005](https://www.sparkfun.com/products/15005) - SparkFun GPS-RTK NEO-M8P-2 +* [GPS-15210](https://www.sparkfun.com/products/15210) - SparkFun GPS Breakout - Chip Antenna, SAM-M8Q (Qwiic) +* [GPS-15193](https://www.sparkfun.com/products/15193) - SparkFun GPS Breakout - Chip Antenna, ZOE-M8Q (Qwiic) +* [SPX-14980](https://www.sparkfun.com/products/14980) - SparkX GPS-RTK Black +* [SPX-15106](https://www.sparkfun.com/products/15106) - SparkX SAM-M8Q + +License Information +------------------- + +This product is _**open source**_! + +Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! + +Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license. + +Distributed as-is; no warranty is given. + +- Your friends at SparkFun. diff --git a/examples/Example10_ResetConfiguration/Example10_ResetConfiguration.ino b/examples/Example10_ResetConfiguration/Example10_ResetConfiguration.ino new file mode 100644 index 0000000..b5ebfac --- /dev/null +++ b/examples/Example10_ResetConfiguration/Example10_ResetConfiguration.ino @@ -0,0 +1,59 @@ +/* + Reading lat and long via UBX binary commands - no more NMEA parsing! + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to reset a module to its factory defaults. This includes + changing all the ports back to UBX+NMEA input/output and nav updates back to 1Hz. + + Helpful if you just need to get back to a sane state. + + Note: Long/lat are large numbers because they are * 10^7. To convert lat/long + to something google maps understands simply divide the numbers by 10,000,000. We + do this so that we don't have to use floating point numbers. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.factoryDefault(); //Return module to default settings (1Hz update, NMEA+UBX on all ports, etc) +} + +void loop() +{ + +} diff --git a/examples/Example11_UseUart/Example11_UseUart.ino b/examples/Example11_UseUart/Example11_UseUart.ino new file mode 100644 index 0000000..af8ad74 --- /dev/null +++ b/examples/Example11_UseUart/Example11_UseUart.ino @@ -0,0 +1,98 @@ +/* + Reading lat and long via UBX binary commands using UART @38400 baud - free from I2C + By: Nathan Seidle, Adapted from Example3_GetPosition by Thorsten von Eicken + SparkFun Electronics + Date: January 28rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the library and U-Blox for serial port use as well as + switching the module from the default 9600 baud to 38400. + + Note: Long/lat are large numbers because they are * 10^7. To convert lat/long + to something google maps understands simply divide the numbers by 10,000,000. We + do this so that we don't have to use floating point numbers. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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: + Connect the U-Blox serial TX pin to Uno pin 10 + Connect the U-Blox serial RX pin to Uno pin 11 + Open the serial monitor at 115200 baud to see the output +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +#include +SoftwareSerial mySerial(10, 11); // RX, TX. Pin 10 on Uno goes to TX pin on GPS module. + +long lastTime = 0; //Simple local timer. Limits amount of I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + //Assume that the U-Blox GPS is running at 9600 baud (the default) or at 38400 baud. + //Loop until we're in sync and then ensure it's at 38400 baud. + do { + Serial.println("GPS: trying 38400 baud"); + mySerial.begin(38400); + if (myGPS.begin(mySerial) == true) break; + + delay(100); + Serial.println("GPS: trying 9600 baud"); + mySerial.begin(9600); + if (myGPS.begin(mySerial) == true) { + Serial.println("GPS: connected at 9600 baud, switching to 38400"); + myGPS.setSerialRate(38400); + delay(100); + } else { + //myGPS.factoryReset(); + delay(2000); //Wait a bit before trying again to limit the Serial output + } + } while(1); + Serial.println("GPS serial connected"); + + myGPS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } +} diff --git a/examples/Example12_FactoryDefault_I2C/Example12_FactoryDefault_I2C.ino b/examples/Example12_FactoryDefault_I2C/Example12_FactoryDefault_I2C.ino new file mode 100644 index 0000000..64d823d --- /dev/null +++ b/examples/Example12_FactoryDefault_I2C/Example12_FactoryDefault_I2C.ino @@ -0,0 +1,58 @@ +/* + Send command to reset module over I2C + By: Nathan Seidle + Date: January 29rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to reset the U-Blox module to factory defaults over I2C. + + 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: + Connect the U-Blox serial port to Serial1 + If you're using an Uno or don't have a 2nd serial port (Serial1), consider using software serial + Open the serial monitor at 115200 baud to see the output +*/ + +#include //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + while (Serial.available()) Serial.read(); //Trash any incoming chars + Serial.println("Press a key to reset module to factory defaults"); + while (Serial.available() == false) ; //Wait for user to send character + + myGPS.factoryReset(); //Reset everything: baud rate, I2C address, update rate, everything. + + if (myGPS.begin() == false) //Attempt to re-connect + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + Serial.println("Unit has now been factory reset. Freezing..."); + while(1); +} + +void loop() +{ + +} diff --git a/examples/Example12_FactoryDefaults_Serial/Example12_FactoryDefaults_Serial.ino b/examples/Example12_FactoryDefaults_Serial/Example12_FactoryDefaults_Serial.ino new file mode 100644 index 0000000..7314124 --- /dev/null +++ b/examples/Example12_FactoryDefaults_Serial/Example12_FactoryDefaults_Serial.ino @@ -0,0 +1,95 @@ +/* + Test baud rate changes on serial, factory reset, and hard reset. + By: Thorsten von Eicken + Date: January 29rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to reset the U-Blox module to factory defaults over serial. + + 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: + Connect the U-Blox serial port to Serial1 + If you're using an Uno or don't have a 2nd serial port (Serial1), consider using software serial + Open the serial monitor at 115200 baud to see the output +*/ + +#include //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +int state = 0; // steps through auto-baud, reset, etc states + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); +} + +void loop() +{ + Serial.print("===== STATE "); + Serial.println(state); + switch (state) { + case 0: // auto-baud connection, then switch to 38400 and save config + do { + Serial.println("GPS: trying 38400 baud"); + Serial1.begin(38400); + if (myGPS.begin(Serial1)) break; + + delay(100); + Serial.println("GPS: trying 9600 baud"); + Serial1.begin(9600); + if (myGPS.begin(Serial1)) { + Serial.println("GPS: connected at 9600 baud, switching to 38400"); + myGPS.setSerialRate(38400); + delay(100); + } else { + delay(2000); //Wait a bit before trying again to limit the Serial output flood + } + } while(1); + myGPS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + Serial.println("GPS serial connected, saved config"); + state++; + break; + case 1: // hardReset, expect to see GPS back at 38400 baud + Serial.println("Issuing hardReset (cold start)"); + myGPS.hardReset(); + delay(1000); + Serial1.begin(38400); + if (myGPS.begin(Serial1)) { + Serial.println("Success."); + state++; + } else { + Serial.println("*** GPS did not respond at 38400 baud, starting over."); + state = 0; + } + break; + case 2: // factoryReset, expect to see GPS back at 9600 baud + Serial.println("Issuing factoryReset"); + myGPS.factoryReset(); + delay(2000); // takes more than one second... a loop to resync would be best + Serial1.begin(9600); + if (myGPS.begin(Serial1)) { + Serial.println("Success."); + state++; + } else { + Serial.println("*** GPS did not come back at 9600 baud, starting over."); + state = 0; + } + break; + case 3: // print version info + Serial.print("GPS protocol version: "); + Serial.print(myGPS.getProtocolVersionHigh()); + Serial.print('.'); + Serial.print(myGPS.getProtocolVersionLow()); + state = 0; + } + delay(1000); +} diff --git a/examples/Example13_AutoPVT/Example13_AutoPVT.ino b/examples/Example13_AutoPVT/Example13_AutoPVT.ino new file mode 100644 index 0000000..673f06a --- /dev/null +++ b/examples/Example13_AutoPVT/Example13_AutoPVT.ino @@ -0,0 +1,83 @@ +/* + Configuring the GPS to automatically send position reports over I2C + By: Nathan Seidle and Thorsten von Eicken + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the U-Blox GPS the send navigation reports automatically + and retrieving the latest one via getPVT. This eliminates the blocking in getPVT while the GPS + produces a fresh navigation solution at the expense of returning a slighly old solution. + + This can be used over serial or over I2C, this example shows the I2C use. With serial the GPS + simply outputs the UBX_NAV_PVT packet. With I2C it queues it into its internal I2C buffer (4KB in + size?) where it can be retrieved in the next I2C poll. + + 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 GPS and a BlackBoard + 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 GPS + +#include //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.setNavigationFrequency(2); //Produce two solutions per second + myGPS.setAutoPVT(true); //Tell the GPS to "send" each solution + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + // Calling getPVT returns true if there actually is a fresh navigation solution available. + if (myGPS.getPVT()) + { + Serial.println(); + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } else { + Serial.print("."); + delay(50); + } +} diff --git a/examples/Example14_DebugOutput/Example14_DebugOutput.ino b/examples/Example14_DebugOutput/Example14_DebugOutput.ino new file mode 100644 index 0000000..0e201d1 --- /dev/null +++ b/examples/Example14_DebugOutput/Example14_DebugOutput.ino @@ -0,0 +1,80 @@ +/* + Reading lat and long via UBX binary commands using UART @38400 baud - free from I2C + By: Nathan Seidle, Adapted from Example3_GetPosition by Thorsten von Eicken + SparkFun Electronics + Date: January 28rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the debug output from the library. + Debug shows various packet and status outputs. These prints can be directed + towards Serial (as in Serial.print) or any other port (Serial1, SerialUSB, etc). + + 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: + Connect the U-Blox serial TX pin to Uno pin 10 + Connect the U-Blox serial RX pin to Uno pin 11 + Open the serial monitor at 115200 baud to see the output +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + myGPS.enableDebugging(); //Enable debug messages over Serial (default) + //myGPS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB + +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } +} diff --git a/examples/Example15_GetDateTime/Example15_GetDateTime.ino b/examples/Example15_GetDateTime/Example15_GetDateTime.ino new file mode 100644 index 0000000..1a6676c --- /dev/null +++ b/examples/Example15_GetDateTime/Example15_GetDateTime.ino @@ -0,0 +1,95 @@ +/* + Getting time and date using Ublox commands + By: davidallenmann + SparkFun Electronics + Date: April 16th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for the current time and date. We also + turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic + dramatically. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial) + ; //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1) + ; + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + Serial.print(myGPS.getYear()); + Serial.print("-"); + Serial.print(myGPS.getMonth()); + Serial.print("-"); + Serial.print(myGPS.getDay()); + Serial.print(" "); + Serial.print(myGPS.getHour()); + Serial.print(":"); + Serial.print(myGPS.getMinute()); + Serial.print(":"); + Serial.println(myGPS.getSecond()); + + Serial.println(); + } +} diff --git a/examples/Example1_BasicNMEARead/Example1_BasicNMEARead.ino b/examples/Example1_BasicNMEARead/Example1_BasicNMEARead.ino new file mode 100644 index 0000000..dd2c96c --- /dev/null +++ b/examples/Example1_BasicNMEARead/Example1_BasicNMEARead.ino @@ -0,0 +1,51 @@ +/* + Read NMEA sentences over I2C using Ublox module SAM-M8Q, NEO-M8P, ZED-F9P, etc + By: Nathan Seidle + SparkFun Electronics + Date: August 22nd, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example reads the NMEA setences from the Ublox module over I2c and outputs + them to the serial port + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + //This will pipe all NMEA sentences to the serial port so we can see them + myGPS.setNMEAOutputPort(Serial); +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + delay(250); //Don't pound too hard on the I2C bus +} diff --git a/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino b/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino new file mode 100644 index 0000000..2ec4274 --- /dev/null +++ b/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino @@ -0,0 +1,83 @@ +/* + Read NMEA sentences over I2C using Ublox module SAM-M8Q, NEO-M8P, etc + By: Nathan Seidle + SparkFun Electronics + Date: August 22nd, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example reads the NMEA characters over I2C and pipes them to MicroNMEA + This example will output your current long/lat and satellites in view + + 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 + + For more MicroNMEA info see https://github.com/stevemarple/MicroNMEA + + Hardware Connections: + Plug a Qwiic cable into the GPS and a BlackBoard + 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 + Go outside! Wait ~25 seconds and you should see your lat/long +*/ + +#include //Needed for I2C to GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +#include //http://librarymanager/All#MicroNMEA +char nmeaBuffer[100]; +MicroNMEA nmea(nmeaBuffer, sizeof(nmeaBuffer)); + +void setup() +{ + Serial.begin(115200); + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + if(nmea.isValid() == true) + { + long latitude_mdeg = nmea.getLatitude(); + long longitude_mdeg = nmea.getLongitude(); + + Serial.print("Latitude (deg): "); + Serial.println(latitude_mdeg / 1000000., 6); + Serial.print("Longitude (deg): "); + Serial.println(longitude_mdeg / 1000000., 6); + } + else + { + Serial.print("No Fix - "); + Serial.print("Num. satellites: "); + Serial.println(nmea.getNumSatellites()); + } + + delay(250); //Don't pound too hard on the I2C bus +} + +//This function gets called from the SparkFun Ublox Arduino Library +//As each NMEA character comes in you can specify what to do with it +//Useful for passing to other libraries like tinyGPS, MicroNMEA, or even +//a buffer, radio, etc. +void SFE_UBLOX_GPS::processNMEA(char incoming) +{ + //Take the incoming char from the Ublox I2C port and pass it on to the MicroNMEA lib + //for sentence cracking + nmea.process(incoming); +} diff --git a/examples/Example3_GetPosition/Example3_GetPosition.ino b/examples/Example3_GetPosition/Example3_GetPosition.ino new file mode 100644 index 0000000..79ebc4b --- /dev/null +++ b/examples/Example3_GetPosition/Example3_GetPosition.ino @@ -0,0 +1,84 @@ +/* + Reading lat and long via UBX binary commands - no more NMEA parsing! + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for its lat/long/altitude. We also + turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic + dramatically. + + Note: Long/lat are large numbers because they are * 10^7. To convert lat/long + to something google maps understands simply divide the numbers by 10,000,000. We + do this so that we don't have to use floating point numbers. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } +} diff --git a/examples/Example4_FixType/Example4_FixType.ino b/examples/Example4_FixType/Example4_FixType.ino new file mode 100644 index 0000000..ec5038b --- /dev/null +++ b/examples/Example4_FixType/Example4_FixType.ino @@ -0,0 +1,94 @@ +/* + Get fix type and RTK fix type if available + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for fix type and RTK fix type. + The fix type is as follows: + 0 = no fix + 1 = dead reckoning (requires external sensors) + 2 = 2D (not quite enough satellites in view) + 3 = 3D (the standard fix) + 4 = GNSS + dead reckoning (requires external sensors) + 5 = Time fix only + + Additionally, if we are doing RTK, we can figure out if we have a floating + RTK solution or if we have been able to resolve a fixec solution (better precision). + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + Wire.setClock(400000); //Optional. Increase I2C clock speed to 400kHz. + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + + byte fixType = myGPS.getFixType(); + Serial.print(F(" Fix: ")); + if(fixType == 0) Serial.print(F("No fix")); + 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 == 4) Serial.print(F("GNSS+Dead reckoning")); + + byte RTK = myGPS.getCarrierSolutionType(); + Serial.print(" RTK: "); + Serial.print(RTK); + if (RTK == 1) Serial.print(F("High precision float fix!")); + if (RTK == 2) Serial.print(F("High precision fix!")); + + Serial.println(); + } + +} diff --git a/examples/Example5_SpeedHeadingPrecision/Example5_SpeedHeadingPrecision.ino b/examples/Example5_SpeedHeadingPrecision/Example5_SpeedHeadingPrecision.ino new file mode 100644 index 0000000..5cd17b6 --- /dev/null +++ b/examples/Example5_SpeedHeadingPrecision/Example5_SpeedHeadingPrecision.ino @@ -0,0 +1,83 @@ +/* + Get Speed/Heading and dilution of precision via UBX binary commands - no more NMEA parsing! + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for its lat/long/altitude. + + Note: Long/lat are large numbers because they are * 10^7. To convert lat/long + to something google maps understands simply divide the numbers by 1,000,000. We + do this so that we don't have to use floating point numbers. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + + long speed = myGPS.getGroundSpeed(); + Serial.print(F(" Speed: ")); + Serial.print(speed); + Serial.print(F(" (mm/s)")); + + long heading = myGPS.getHeading(); + Serial.print(F(" Heading: ")); + Serial.print(heading); + Serial.print(F(" (degrees * 10^-5)")); + + int pDOP = myGPS.getPDOP(); + Serial.print(F(" pDOP: ")); + Serial.print(pDOP / 100.0, 2); + + Serial.println(); + } +} diff --git a/examples/Example6_OutputRate/Example6_OutputRate.ino b/examples/Example6_OutputRate/Example6_OutputRate.ino new file mode 100644 index 0000000..464a25a --- /dev/null +++ b/examples/Example6_OutputRate/Example6_OutputRate.ino @@ -0,0 +1,91 @@ +/* + Set update rate to 10Hz + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to increase the output of the module from 1Hz to 4Hz. + The max output rate various from model to model. RTFM! But you cannot do harm + to the module. + + We also disable NMEA output on the I2C bus and use only UBX. This dramatically + decreases the amount of data that needs to be transmitted. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. +long startTime = 0; //Used to calc the actual update rate. +long updateCount = 0; //Used to calc the actual update rate. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + Wire.setClock(400000); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.setNavigationFrequency(10); //Set output to 10 times a second + + byte rate = myGPS.getNavigationFrequency(); //Get the update rate of this module + Serial.print("Current update rate:"); + Serial.println(rate); + + startTime = millis(); +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available. This is defined + //by the update freq. + if (millis() - lastTime > 25) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + + updateCount++; + + //Calculate the actual update rate based on the sketch start time and the + //number of updates we've received. + Serial.print(F(" Rate: ")); + Serial.print( updateCount / ((millis() - startTime) / 1000.0), 2); + Serial.print(F("Hz")); + + Serial.println(); + } +} diff --git a/examples/Example7_GetProtocolVersion/Example7_GetProtocolVersion.ino b/examples/Example7_GetProtocolVersion/Example7_GetProtocolVersion.ino new file mode 100644 index 0000000..38d1718 --- /dev/null +++ b/examples/Example7_GetProtocolVersion/Example7_GetProtocolVersion.ino @@ -0,0 +1,61 @@ +/* + Reading the protocol version of a Ublox module + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for its protocol version. + + Various modules have various protocol version. We've seen v18 up to v27. Depending + on the protocol version there are different commands available. This is a handy + way to predict which commands will or won't work. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + Serial.print(F("Version: ")); + byte versionHigh = myGPS.getProtocolVersionHigh(); + Serial.print(versionHigh); + Serial.print("."); + byte versionLow = myGPS.getProtocolVersionLow(); + Serial.print(versionLow); +} + +void loop() +{ + //Do nothing +} diff --git a/examples/Example8_ChangeI2CAddress/Example8_ChangeI2CAddress.ino b/examples/Example8_ChangeI2CAddress/Example8_ChangeI2CAddress.ino new file mode 100644 index 0000000..7756971 --- /dev/null +++ b/examples/Example8_ChangeI2CAddress/Example8_ChangeI2CAddress.ino @@ -0,0 +1,112 @@ +/* + Change the I2C address of a Ublox module using I2C + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to change the I2C address of a Ublox module + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Tracks the passing of 2000ms (2 seconds) + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + byte oldAddress = 0x42; //The default address for Ublox modules is 0x42 + byte newAddress = 0x3F; //Address you want to change to. Valid is 0x08 to 0x77. + + while (Serial.available()) Serial.read(); //Trash any incoming chars + Serial.print("Press a key to change address to 0x"); + Serial.println(newAddress, HEX); + while (Serial.available() == false) ; //Wait for user to send character + + if (myGPS.begin(Wire, oldAddress) == true) //Connect to the Ublox module using Wire port and the old address + { + Serial.print("GPS found at address 0x"); + Serial.println(oldAddress, HEX); + + myGPS.setI2CAddress(newAddress); //Change I2C address of this device + //Device's I2C address is stored to memory and loaded on each power-on + + if (myGPS.begin(Wire, newAddress) == true) + { + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + Serial.print("Address successfully changed to 0x"); + Serial.println(newAddress, HEX); + Serial.print("Now load another example sketch using .begin(Wire, 0x"); + Serial.print(newAddress, HEX); + Serial.println(") to use this GPS module"); + Serial.println("Freezing..."); + while (1); + } + } + + //Something went wrong, begin looking for the I2C device + Serial.println("Address change failed. Beginning an I2C scan."); + + Wire.begin(); +} + +void loop() { + + byte address; + int nDevices; + + Serial.println("Scanning..."); + + nDevices = 0; + for (address = 1; address < 127; address++ ) + { + Wire.beginTransmission(address); + byte error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print("I2C device found at address 0x"); + if (address < 16) + Serial.print("0"); + Serial.print(address, HEX); + Serial.println(" !"); + + nDevices++; + } + else if (error == 4) + { + Serial.print("Unknown error at address 0x"); + if (address < 16) + Serial.print("0"); + Serial.println(address, HEX); + } + } + + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else + Serial.println("done\n"); + + delay(5000); // wait 5 seconds for next scan +} diff --git a/examples/Example9_AltitudeMSL/Example9_AltitudeMSL.ino b/examples/Example9_AltitudeMSL/Example9_AltitudeMSL.ino new file mode 100644 index 0000000..fec2d09 --- /dev/null +++ b/examples/Example9_AltitudeMSL/Example9_AltitudeMSL.ino @@ -0,0 +1,82 @@ +/* + Reading two altitudes - Mean Sea Level and Ellipsode + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a Ublox module for its lat/long/altitude. + + getAltitude() reports mm above ellipsode model of the globe. There are some + instances where altitude above Mean Sea Level is better. This example shows how + to use getAltitudeMSL(). The difference varies but is ~20m. + Ellipsoid model: https://www.esri.com/news/arcuser/0703/geoid1of3.html + Difference between Ellipsoid Model and Mean Sea Level: https://eos-gnss.com/elevation-for-beginners/ + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Tracks the passing of 2000ms (2 seconds) + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + long altitudeMSL = myGPS.getAltitudeMSL(); + Serial.print(F(" AltMSL: ")); + Serial.print(altitudeMSL); + Serial.print(F(" (mm)")); + + Serial.println(); + } +} diff --git a/examples/NEO-M8P-2/Example1_EnableRTCM/Example1_EnableRTCM.ino b/examples/NEO-M8P-2/Example1_EnableRTCM/Example1_EnableRTCM.ino new file mode 100644 index 0000000..5bc257d --- /dev/null +++ b/examples/NEO-M8P-2/Example1_EnableRTCM/Example1_EnableRTCM.ino @@ -0,0 +1,77 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox NEO-M8P module + By: Nathan Seidle + SparkFun Electronics + Date: September 7th, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example sends the command to enable the four RTCM messages needed for RTK. This + is the first part of a larger tutorial and example to setup an RTK base station. + These commands are only accepted by the NEO-M8P module. + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while(!Serial); //Wait for user to open terminal + Serial.println("Ublox RTCM Enable Example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + while(Serial.available()) Serial.read(); //Clear any latent chars in serial buffer + Serial.println("Press any key to send commands to enable RTCM 3.x"); + while(Serial.available() == 0) ; //Wait for user to press a key + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + boolean response = true; + response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second + response &= myGPS.enableRTCMmessage(UBX_RTCM_1077, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1087, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds + + if (response == true) + { + Serial.println("RTCM messages enabled"); + } + else + { + Serial.println("RTCM failed to enable. Are you sure you have an NEO-M8P?"); + while(1); //Freeze + } + + //RTCM is now enabled but we haven't done a 'survey-in' + //See example 4 for the full Base RTK setup +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + delay(250); //Don't pound too hard on the I2C bus +} diff --git a/examples/NEO-M8P-2/Example2_StartRTCMBase/Example2_StartRTCMBase.ino b/examples/NEO-M8P-2/Example2_StartRTCMBase/Example2_StartRTCMBase.ino new file mode 100644 index 0000000..366a4d0 --- /dev/null +++ b/examples/NEO-M8P-2/Example2_StartRTCMBase/Example2_StartRTCMBase.ino @@ -0,0 +1,153 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox NEO-M8P-2 module + By: Nathan Seidle + SparkFun Electronics + Date: September 7th, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example does all steps to configure and enable a NEO-M8P-2 as a base station: + Begin Survey-In + Once we've achieved 2m accuracy and 300s have passed, survey is complete + Enable four RTCM messages + Begin outputting RTCM bytes + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox NEO-M8P-2 base station example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + while (Serial.available()) Serial.read(); //Clear any latent chars in serial buffer + Serial.println("Press any key to send commands to begin Survey-In"); + while (Serial.available() == 0) ; //Wait for user to press a key + + boolean response; + + //Check if Survey is in Progress before initiating one + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time) + if (response == false) + { + Serial.println("Failed to get Survey In status"); + while (1); //Freeze + } + + if (myGPS.svin.active == true) + { + Serial.print("Survey already in progress."); + } + else + { + //Start survey + response = myGPS.enableSurveyMode(300, 2.000); //Enable Survey in, 300 seconds, 2.0m + if (response == false) + { + Serial.println("Survey start failed"); + while (1); + } + Serial.println("Survey started. This will run until 300s has passed and less than 2m accuracy is achieved."); + } + + while(Serial.available()) Serial.read(); //Clear buffer + + //Begin waiting for survey to complete + while (myGPS.svin.valid == false) + { + if(Serial.available()) + { + byte incoming = Serial.read(); + if(incoming == 'x') + { + //Stop survey mode + response = myGPS.disableSurveyMode(); //Disable survey + Serial.println("Survey stopped"); + break; + } + } + + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time) + if (response == true) + { + Serial.print("Press x to end survey - "); + Serial.print("Time elapsed: "); + Serial.print((String)myGPS.svin.observationTime); + + Serial.print(" Accuracy: "); + Serial.print((String)myGPS.svin.meanAccuracy); + Serial.println(); + } + else + { + Serial.println("SVIN request failed"); + } + + delay(1000); + } + Serial.println("Survey valid!"); + + response = true; + response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second + response &= myGPS.enableRTCMmessage(UBX_RTCM_1077, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1087, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds + + if (response == true) + { + Serial.println("RTCM messages enabled"); + } + else + { + Serial.println("RTCM failed to enable. Are you sure you have an NEO-M8P?"); + while (1); //Freeze + } + + Serial.println("Base survey complete! RTCM now broadcasting."); +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + delay(250); //Don't pound too hard on the I2C bus +} + +//This function gets called from the SparkFun Ublox Arduino Library. +//As each RTCM byte comes in you can specify what to do with it +//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc. +void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) +{ + //Let's just pretty-print the HEX values for now + if (myGPS.rtcmFrameCounter % 16 == 0) Serial.println(); + Serial.print(" "); + if (incoming < 0x10) Serial.print("0"); + Serial.print(incoming, HEX); +} diff --git a/examples/NEO-M8P-2/Example3_BaseWithLCD/Example3_BaseWithLCD.ino b/examples/NEO-M8P-2/Example3_BaseWithLCD/Example3_BaseWithLCD.ino new file mode 100644 index 0000000..c498530 --- /dev/null +++ b/examples/NEO-M8P-2/Example3_BaseWithLCD/Example3_BaseWithLCD.ino @@ -0,0 +1,185 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox NEO-M8P-2 module + By: Nathan Seidle + SparkFun Electronics + Date: September 7th, 2018 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example does all steps to configure and enable a NEO-M8P-2 as a base station: + Begin Survey-In + Once we've achieved 2m accuracy and 300s have passed, survey is complete + Enable four RTCM messages + Begin outputting RTCM bytes + + 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 GPS and a BlackBoard + Plug a SerLCD onto the Qwiic bus + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Watch the output on the LCD or open the serial monitor at 115200 baud to see the output +*/ + +#define STAT_LED 13 + +#include //Needed for I2C to GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +#include //Click here to get the library: http://librarymanager/All#SparkFun_SerLCD +SerLCD lcd; // Initialize the library with default I2C address 0x72 + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox GPS I2C Test"); + + Wire.begin(); + + pinMode(STAT_LED, OUTPUT); + digitalWrite(STAT_LED, LOW); + + lcd.begin(Wire); //Set up the LCD for Serial communication at 9600bps + lcd.setBacklight(0x4B0082); //indigo, a kind of dark purplish blue + lcd.clear(); + lcd.print(F("LCD Ready")); + + myGPS.begin(Wire); + if (myGPS.isConnected() == false) + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + lcd.setCursor(0, 1); + lcd.print(F("No GPS detected")); + while (1); + } + + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + lcd.setCursor(0, 1); + lcd.print("GPS Detected"); + + //Check if Survey is in Progress before initiating one + boolean response; + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time) + if (response == false) + { + Serial.println(F("Failed to get Survey In status")); + while (1); //Freeze + } + + if (myGPS.svin.active == true) + { + Serial.print(F("Survey already in progress.")); + lcd.setCursor(0, 2); + lcd.print(F("Survey already going")); + } + else + { + //Start survey + response = myGPS.enableSurveyMode(300, 2.000); //Enable Survey in, 300 seconds, 2.0m + if (response == false) + { + Serial.println(F("Survey start failed")); + lcd.setCursor(0, 3); + lcd.print(F("Survey start failed")); + while (1); + } + Serial.println(F("Survey started. This will run until 300s has passed and less than 2m accuracy is achieved.")); + } + + while (Serial.available()) Serial.read(); //Clear buffer + + lcd.clear(); + lcd.print(F("Survey in progress")); + + //Begin waiting for survey to complete + while (myGPS.svin.valid == false) + { + if (Serial.available()) + { + byte incoming = Serial.read(); + if (incoming == 'x') + { + //Stop survey mode + response = myGPS.disableSurveyMode(); //Disable survey + Serial.println(F("Survey stopped")); + break; + } + } + + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time) + if (response == true) + { + Serial.print(F("Press x to end survey - ")); + Serial.print(F("Time elapsed: ")); + Serial.print((String)myGPS.svin.observationTime); + + lcd.setCursor(0, 1); + lcd.print(F("Elapsed: ")); + lcd.print((String)myGPS.svin.observationTime); + + Serial.print(F(" Accuracy: ")); + Serial.print((String)myGPS.svin.meanAccuracy); + Serial.println(); + + lcd.setCursor(0, 2); + lcd.print(F("Accuracy: ")); + lcd.print((String)myGPS.svin.meanAccuracy); + } + else + { + Serial.println(F("SVIN request failed")); + } + + delay(1000); + } + Serial.println(F("Survey valid!")); + + response = true; + response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, UBX_RTCM_I2C_PORT, 1); //Enable message 1005 to output through I2C port, message every second + response &= myGPS.enableRTCMmessage(UBX_RTCM_1077, UBX_RTCM_I2C_PORT, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1087, UBX_RTCM_I2C_PORT, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1230, UBX_RTCM_I2C_PORT, 10); //Enable message every 10 seconds + + if (response == true) + { + Serial.println(F("RTCM messages enabled")); + } + else + { + Serial.println(F("RTCM failed to enable. Are you sure you have an NEO-M8P?")); + while (1); //Freeze + } + + Serial.println(F("Base survey complete! RTCM now broadcasting.")); + lcd.clear(); + lcd.print(F("Transmitting RTCM")); +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + //Do anything you want. Call checkUblox() every second. NEO-M8P-2 has TX buffer of 4k bytes. + + delay(250); //Don't pound too hard on the I2C bus +} + +//This function gets called from the SparkFun Ublox Arduino Library. +//As each RTCM byte comes in you can specify what to do with it +//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc. +void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) +{ + //Let's just pretty-print the HEX values for now + if (myGPS.rtcmFrameCounter % 16 == 0) Serial.println(); + Serial.print(" "); + if (incoming < 0x10) Serial.print("0"); + Serial.print(incoming, HEX); +} diff --git a/examples/ZED-F9P/Example1_GetPositionAccuracy/Example1_GetPositionAccuracy.ino b/examples/ZED-F9P/Example1_GetPositionAccuracy/Example1_GetPositionAccuracy.ino new file mode 100644 index 0000000..b752d43 --- /dev/null +++ b/examples/ZED-F9P/Example1_GetPositionAccuracy/Example1_GetPositionAccuracy.ino @@ -0,0 +1,77 @@ +/* + Get the high position accuracy of the RTK enhanced position + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to inspect the accuracy of the high-precision + positional solution. + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + long accuracy = myGPS.getPositionAccuracy(); + Serial.print(F(" 3D Positional Accuracy: ")); + Serial.print(accuracy); + Serial.println(F("mm")); + } + +} diff --git a/examples/ZED-F9P/Example2_ValConfigurationMethod/Example2_ValConfigurationMethod.ino b/examples/ZED-F9P/Example2_ValConfigurationMethod/Example2_ValConfigurationMethod.ino new file mode 100644 index 0000000..cf00bc3 --- /dev/null +++ b/examples/ZED-F9P/Example2_ValConfigurationMethod/Example2_ValConfigurationMethod.ino @@ -0,0 +1,60 @@ +/* + Configuring Ublox Module using new VALGET / VALSET / VALDEL methods + By: Nathan Seidle + SparkFun Electronics + Date: January 3rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + Ublox depricated many -CFG messages and replaced them with new + VALGET, VALSET, VALDEL methods. This shows the basics of how to use + these methods. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + byte response; + response = myGPS.getVal(VAL_GROUP_I2C, VAL_ID_I2C_ADDRESS, VAL_GROUP_I2C_SIZE, VAL_LAYER_RAM); + Serial.print(F("I2C Address: 0x")); + Serial.println(response >> 1, HEX); //We have to shift by 1 to get the common '7-bit' I2C address format + + response = myGPS.getVal(VAL_GROUP_I2COUTPROT, VAL_ID_I2COUTPROT_NMEA, VAL_GROUP_I2COUTPROT_SIZE, VAL_LAYER_RAM); + Serial.print(F("Output NMEA over I2C port: 0x")); + Serial.print(response, HEX); +} + +void loop() +{ +} diff --git a/examples/ZED-F9P/Example3_StartRTCMBase/Example3_StartRTCMBase.ino b/examples/ZED-F9P/Example3_StartRTCMBase/Example3_StartRTCMBase.ino new file mode 100644 index 0000000..ddff4c2 --- /dev/null +++ b/examples/ZED-F9P/Example3_StartRTCMBase/Example3_StartRTCMBase.ino @@ -0,0 +1,161 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox ZED-F9P module + By: Nathan Seidle + SparkFun Electronics + Date: January 9th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example does all steps to configure and enable a ZED-F9P as a base station: + Begin Survey-In + Once we've achieved 2m accuracy and 300s have passed, survey is complete + Enable six RTCM messages + Begin outputting RTCM bytes + + 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 GPS and a BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox Base station example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + while (Serial.available()) Serial.read(); //Clear any latent chars in serial buffer + Serial.println("Press any key to send commands to begin Survey-In"); + while (Serial.available() == 0) ; //Wait for user to press a key + + boolean response = true; + response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second + response &= myGPS.enableRTCMmessage(UBX_RTCM_1074, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1084, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1094, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1124, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds + + //Use COM_PORT_UART1 for the above six messages to direct RTCM messages out UART1 + //COM_PORT_UART2, COM_PORT_USB, COM_PORT_SPI are also available + //For example: response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_UART1, 10); + + if (response == true) + { + Serial.println("RTCM messages enabled"); + } + else + { + Serial.println("RTCM failed to enable. Are you sure you have an ZED-F9P?"); + while (1); //Freeze + } + + //Check if Survey is in Progress before initiating one + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time) + if (response == false) + { + Serial.println("Failed to get Survey In status"); + while (1); //Freeze + } + + if (myGPS.svin.active == true) + { + Serial.print("Survey already in progress."); + } + else + { + //Start survey + //The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. + //response = myGPS.enableSurveyMode(300, 2.000); //Enable Survey in on NEO-M8P, 300 seconds, 2.0m + response = myGPS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m + if (response == false) + { + Serial.println("Survey start failed"); + while (1); + } + Serial.println("Survey started. This will run until 60s has passed and less than 5m accuracy is achieved."); + } + + while(Serial.available()) Serial.read(); //Clear buffer + + //Begin waiting for survey to complete + while (myGPS.svin.valid == false) + { + if(Serial.available()) + { + byte incoming = Serial.read(); + if(incoming == 'x') + { + //Stop survey mode + response = myGPS.disableSurveyMode(); //Disable survey + Serial.println("Survey stopped"); + break; + } + } + + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time) + if (response == true) + { + Serial.print("Press x to end survey - "); + Serial.print("Time elapsed: "); + Serial.print((String)myGPS.svin.observationTime); + + Serial.print(" Accuracy: "); + Serial.print((String)myGPS.svin.meanAccuracy); + Serial.println(); + } + else + { + Serial.println("SVIN request failed"); + } + + delay(1000); + } + Serial.println("Survey valid!"); + + Serial.println("Base survey complete! RTCM now broadcasting."); + + myGPS.setI2COutput(COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the I2C port to output UBX and RTCM sentences (not really an option, turns on NMEA as well) +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + delay(250); //Don't pound too hard on the I2C bus +} + +//This function gets called from the SparkFun Ublox Arduino Library. +//As each RTCM byte comes in you can specify what to do with it +//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc. +void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) +{ + //Let's just pretty-print the HEX values for now + if (myGPS.rtcmFrameCounter % 16 == 0) Serial.println(); + Serial.print(" "); + if (incoming < 0x10) Serial.print("0"); + Serial.print(incoming, HEX); +} diff --git a/examples/ZED-F9P/Example4_BaseWithLCD/Example4_BaseWithLCD.ino b/examples/ZED-F9P/Example4_BaseWithLCD/Example4_BaseWithLCD.ino new file mode 100644 index 0000000..40224f9 --- /dev/null +++ b/examples/ZED-F9P/Example4_BaseWithLCD/Example4_BaseWithLCD.ino @@ -0,0 +1,200 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox ZED-F9P module + By: Nathan Seidle + SparkFun Electronics + Date: January 9th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example does all steps to configure and enable a ZED-F9P as a base station: + Begin Survey-In + Once we've achieved 2m accuracy and 300s have passed, survey is complete + Enable six RTCM messages + Begin outputting RTCM bytes + + 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 GPS and a BlackBoard + Plug a SerLCD onto the Qwiic bus + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Watch the output on the LCD or open the serial monitor at 115200 baud to see the output +*/ + +#define STAT_LED 13 + +#include //Needed for I2C to GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +#include //Click here to get the library: http://librarymanager/All#SparkFun_SerLCD +SerLCD lcd; // Initialize the library with default I2C address 0x72 + +void setup() +{ + Serial.begin(115200); + while (!Serial) + ; //Wait for user to open terminal + Serial.println("Ublox GPS I2C Test"); + + Wire.begin(); + + pinMode(STAT_LED, OUTPUT); + digitalWrite(STAT_LED, LOW); + + lcd.begin(Wire); //Set up the LCD for Serial communication at 9600bps + lcd.setBacklight(0x4B0082); //indigo, a kind of dark purplish blue + lcd.clear(); + lcd.print(F("LCD Ready")); + + myGPS.begin(Wire); + if (myGPS.isConnected() == false) + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + lcd.setCursor(0, 1); + lcd.print(F("No GPS detected")); + while (1) + ; + } + + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + lcd.setCursor(0, 1); + lcd.print("GPS Detected"); + + //myGPS.setI2COutput(COM_TYPE_RTCM3); //Set the I2C port to output RTCM3 sentences (turn off NMEA noise) + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX sentences (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + boolean response = true; + response &= myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second + response &= myGPS.enableRTCMmessage(UBX_RTCM_1074, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1084, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1094, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1124, COM_PORT_I2C, 1); + response &= myGPS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds + if (response == true) + { + Serial.println(F("RTCM messages enabled")); + } + else + { + Serial.println(F("RTCM failed to enable. Are you sure you have an ZED-F9P? Freezing.")); + while (1) + ; //Freeze + } + + //Check if Survey is in Progress before initiating one + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time) + if (response == false) + { + Serial.println(F("Failed to get Survey In status. Freezing.")); + while (1) + ; //Freeze + } + + if (myGPS.svin.active == true) + { + Serial.print(F("Survey already in progress.")); + lcd.setCursor(0, 2); + lcd.print(F("Survey already going")); + } + else + { + //Start survey + response = myGPS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m + if (response == false) + { + Serial.println(F("Survey start failed")); + lcd.setCursor(0, 3); + lcd.print(F("Survey start failed. Freezing.")); + while (1) + ; + } + Serial.println(F("Survey started. This will run until 60s has passed and less than 5m accuracy is achieved.")); + } + + while (Serial.available()) + Serial.read(); //Clear buffer + + lcd.clear(); + lcd.print(F("Survey in progress")); + + //Begin waiting for survey to complete + while (myGPS.svin.valid == false) + { + if (Serial.available()) + { + byte incoming = Serial.read(); + if (incoming == 'x') + { + //Stop survey mode + response = myGPS.disableSurveyMode(); //Disable survey + Serial.println(F("Survey stopped")); + break; + } + } + + response = myGPS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time) + if (response == true) + { + Serial.print(F("Press x to end survey - ")); + Serial.print(F("Time elapsed: ")); + Serial.print((String)myGPS.svin.observationTime); + + lcd.setCursor(0, 1); + lcd.print(F("Elapsed: ")); + lcd.print((String)myGPS.svin.observationTime); + + Serial.print(F(" Accuracy: ")); + Serial.print((String)myGPS.svin.meanAccuracy); + Serial.println(); + + lcd.setCursor(0, 2); + lcd.print(F("Accuracy: ")); + lcd.print((String)myGPS.svin.meanAccuracy); + } + else + { + Serial.println(F("SVIN request failed")); + } + + delay(1000); + } + Serial.println(F("Survey valid!")); + + Serial.println(F("Base survey complete! RTCM now broadcasting.")); + lcd.clear(); + lcd.print(F("Transmitting RTCM")); + + myGPS.setI2COutput(COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the I2C port to output UBX and RTCM sentences (not really an option, turns on NMEA as well) + +} + +void loop() +{ + myGPS.checkUblox(); //See if new data is available. Process bytes as they come in. + + //Do anything you want. Call checkUblox() every second. ZED-F9P has TX buffer of 4k bytes. + + delay(250); //Don't pound too hard on the I2C bus +} + +//This function gets called from the SparkFun Ublox Arduino Library. +//As each RTCM byte comes in you can specify what to do with it +//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc. +void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) +{ + //Let's just pretty-print the HEX values for now + if (myGPS.rtcmFrameCounter % 16 == 0) + Serial.println(); + Serial.print(" "); + if (incoming < 0x10) + Serial.print("0"); + Serial.print(incoming, HEX); +} diff --git a/examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino b/examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino new file mode 100644 index 0000000..82b0525 --- /dev/null +++ b/examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino @@ -0,0 +1,125 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox ZED-F9P module + By: Nathan Seidle + SparkFun Electronics + Date: January 9th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query the module for RELPOS information in the NED frame. + It assumes you already have RTCM correction data being fed to the receiver. + + 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 GPS and a RedBoard Qwiic or BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox Base station example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } +} + +void loop() +{ + if (myGPS.getRELPOSNED() == true) + { + Serial.print("relPosN: "); + Serial.println(myGPS.relPosInfo.relPosN, 4); + Serial.print("relPosE: "); + Serial.println(myGPS.relPosInfo.relPosE, 4); + Serial.print("relPosD: "); + Serial.println(myGPS.relPosInfo.relPosD, 4); + + Serial.print("relPosLength: "); + Serial.println(myGPS.relPosInfo.relPosLength); + Serial.print("relPosHeading: "); + Serial.println(myGPS.relPosInfo.relPosHeading); + + Serial.print("relPosHPN: "); + Serial.println(myGPS.relPosInfo.relPosHPN); + Serial.print("relPosHPE: "); + Serial.println(myGPS.relPosInfo.relPosHPE); + Serial.print("relPosHPD: "); + Serial.println(myGPS.relPosInfo.relPosHPD); + Serial.print("relPosHPLength: "); + Serial.println(myGPS.relPosInfo.relPosHPLength); + + Serial.print("accN: "); + Serial.println(myGPS.relPosInfo.accN, 4); + Serial.print("accE: "); + Serial.println(myGPS.relPosInfo.accE, 4); + Serial.print("accD: "); + Serial.println(myGPS.relPosInfo.accD, 4); + + Serial.print("gnssFixOk: "); + if (myGPS.relPosInfo.gnssFixOk == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("diffSolution: "); + if (myGPS.relPosInfo.diffSoln == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("relPosValid: "); + if (myGPS.relPosInfo.relPosValid == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("carrier Solution Type: "); + if (myGPS.relPosInfo.carrSoln == 0) + Serial.println("None"); + else if (myGPS.relPosInfo.carrSoln == 1) + Serial.println("Float"); + else if (myGPS.relPosInfo.carrSoln == 2) + Serial.println("Fixed"); + + Serial.print("isMoving: "); + if (myGPS.relPosInfo.isMoving == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("refPosMiss: "); + if (myGPS.relPosInfo.refPosMiss == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("refObsMiss: "); + if (myGPS.relPosInfo.refObsMiss == true) + Serial.println("x"); + else + Serial.println(""); + } + else + Serial.println("RELPOS request failed"); + + delay(4000); +} diff --git a/examples/ZED-F9P/Example6_GetVal/Example6_GetVal.ino b/examples/ZED-F9P/Example6_GetVal/Example6_GetVal.ino new file mode 100644 index 0000000..a0d2766 --- /dev/null +++ b/examples/ZED-F9P/Example6_GetVal/Example6_GetVal.ino @@ -0,0 +1,86 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox ZED-F9P module + By: Nathan Seidle + SparkFun Electronics + Date: January 9th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + Ublox changed how to configure their modules in 2019. As of version 23 of the UBX protocol the + UBX-CFG commands are deprecated; they still work, they just recommend using VALSET, VALGET, and VALDEL + commands instead. This example shows how to use this new command structure. + + 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 GPS and a RedBoard Qwiic or BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox getVal example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.enableDebugging(); //Enable debug messages over Serial (default) + //myGPS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB + + uint8_t currentI2Caddress = myGPS.getVal8(0x20510001); + Serial.print("Current I2C address (should be 0x42): 0x"); + Serial.println(currentI2Caddress >> 1, HEX); //Ublox module returns a shifted 8-bit address. Make it 7-bit unshifted. + + while(1); + +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } +} diff --git a/examples/ZED-F9P/Example7_SetVal/Example7_SetVal.ino b/examples/ZED-F9P/Example7_SetVal/Example7_SetVal.ino new file mode 100644 index 0000000..8f7e6df --- /dev/null +++ b/examples/ZED-F9P/Example7_SetVal/Example7_SetVal.ino @@ -0,0 +1,75 @@ +/* + Send UBX binary commands to enable RTCM sentences on Ublox ZED-F9P module + By: Nathan Seidle + SparkFun Electronics + Date: January 9th, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + Ublox changed how to configure their modules in 2019. As of version 23 of the UBX protocol the + UBX-CFG commands are deprecated; they still work, they just recommend using VALSET, VALGET, and VALDEL + commands instead. This example shows how to use this new command structure. + + 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 GPS and a RedBoard Qwiic or BlackBoard + 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 GPS + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("Ublox getVal example"); + + Wire.begin(); + Wire.setClock(400000); //Increase I2C clock speed to 400kHz + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.enableDebugging(); //Enable debug messages over Serial (default) + //myGPS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB + + bool setValueSuccess; + + //These key values are hard coded. You can obtain them from the ZED-F9P interface description doc + //or from u-center's Messages->CFG->VALSET window. Keys must be 32-bit. + //setValueSuccess = myGPS.setVal(0x10930006, 0); //Enable high precision NMEA + setValueSuccess = myGPS.setVal(0x30210001, 100); //Set measurement rate to 100ms (10Hz update rate) + //setValueSuccess = myGPS.setVal(0x30210001, 1000); //Set measurement rate to 1000ms (1Hz update rate) + + //Below is the original way we enabled the RTCM message on the I2C port. After that, we show how to do the same + //but with setVal(). + //Original: myGPS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second + //setValueSuccess = myGPS.setVal(0x209102bd, 1); //Set output rate of msg 1005 over the I2C port to once per second + + if(setValueSuccess == true) + { + Serial.println("Value was successfully set"); + } + else + Serial.println("Value set failed"); + +} + +void loop() +{ + +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..458dde7 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,100 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SFE_UBLOX_GPS KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +isConnected KEYWORD2 +checkUblox KEYWORD2 +checkUbloxI2C KEYWORD2 +checkUbloxSerial KEYWORD2 + +process KEYWORD2 +processUBX KEYWORD2 +processRTCMframe KEYWORD2 +processRTCM KEYWORD2 +processUBXpacket KEYWORD2 +processNMEA KEYWORD2 + +calcChecksum KEYWORD2 +sendCommand KEYWORD2 +printPacket KEYWORD2 +setI2CAddress KEYWORD2 +setNMEAOutputPort KEYWORD2 + +setNavigationFrequency KEYWORD2 +getNavigationFrequency KEYWORD2 + +saveConfiguration KEYWORD2 +factoryDefault KEYWORD2 + +waitForResponse KEYWORD2 + +getPVT KEYWORD2 +getLatitude KEYWORD2 +getLongitude KEYWORD2 +getAltitude KEYWORD2 +getAltitudeMSL KEYWORD2 +getSIV KEYWORD2 +getFixType KEYWORD2 +getCarrierSolutionType KEYWORD2 +getGroundSpeed KEYWORD2 +getHeading KEYWORD2 +getPDOP KEYWORD2 + +setPortOutput KEYWORD2 +setPortInput KEYWORD2 +getPortSettings KEYWORD2 + +setI2COutput KEYWORD2 +setUART1Output KEYWORD2 +setUART2Output KEYWORD2 +setUSBOutput KEYWORD2 +setSPIOutput KEYWORD2 + +getVal8 KEYWORD2 +setVal KEYWORD2 + +getSurveyMode KEYWORD2 +setSurveyMode KEYWORD2 +enableSurveyMode KEYWORD2 +disableSurveyMode KEYWORD2 +getSurveyStatus KEYWORD2 + +enableRTCMmessage KEYWORD2 +disableRTCMmessage KEYWORD2 + +getPositionAccuracy KEYWORD2 + +getProtocolVersionHigh KEYWORD2 +getProtocolVersionLow KEYWORD2 +getProtocolVersion KEYWORD2 + +getRELPOSNED KEYWORD2 + +enableDebugging KEYWORD2 +disableDebugging KEYWORD2 + +factoryReset KEYWORD2 +setAutoPVT KEYWORD2 + +getYear KEYWORD2 +getMonth KEYWORD2 +getDay KEYWORD2 +getHour KEYWORD2 +getMinute KEYWORD2 +getSecond KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..0eb2e8e --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=SparkFun Ublox Arduino Library +version=1.4.1 +author=SparkFun Electronics +maintainer=SparkFun Electronics +sentence=Library for I2C and Serial Communication with Ublox modules +paragraph=An Arduino Library to enable both I2C and Serial communication for both NMEA reception and binary UBX sending to the module. Useful for interfacing to the SparkFun GPS-RTK2 ZED-F9P, SparkFun GPS-RTK NEO-M8P-2, the SparkFun SAM-M8Q, and the SparkFun ZEO-M8Q. Library also works with other Ublox 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. +category=Sensors +url=https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library +architectures=* diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp new file mode 100644 index 0000000..f2a4b7e --- /dev/null +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -0,0 +1,1577 @@ +/* + This is a library written for the Ublox NEO-M8P-2 + SparkFun sells these at its website: www.sparkfun.com + Do you like this library? Help support SparkFun. Buy a board! + https://www.sparkfun.com/products/14586 + + Written by Nathan Seidle @ SparkFun Electronics, September 6th, 2018 + + The NEO-M8P-2 is a powerful GPS receiver capable of calculating correction data + to achieve 2cm accuracy. + + This library handles the configuration of 'survey-in', RTCM messages, and to output + the RTCM messages to the user's selected stream + + https://github.com/sparkfun/SparkFun_RTK_Arduino_Library + + Development environment specifics: + Arduino IDE 1.8.5 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" + +SFE_UBLOX_GPS::SFE_UBLOX_GPS(void) +{ + // Constructor +} + +//Initialize the Serial port +boolean SFE_UBLOX_GPS::begin(TwoWire &wirePort, uint8_t deviceAddress) +{ + commType = COMM_TYPE_I2C; + _i2cPort = &wirePort; //Grab which port the user wants us to use + + //We expect caller to begin their I2C port, with the speed of their choice external to the library + //But if they forget, we start the hardware here. + + //We're moving away from the practice of starting Wire hardware in a library. This is to avoid cross platform issues. + //ie, there are some platforms that don't handle multiple starts to the wire hardware. Also, every time you start the wire + //hardware the clock speed reverts back to 100kHz regardless of previous Wire.setClocks(). + //_i2cPort->begin(); + + _gpsI2Caddress = deviceAddress; //Store the I2C address from user + + return (isConnected()); +} + +//Initialize the Serial port +boolean SFE_UBLOX_GPS::begin(Stream &serialPort) +{ + commType = COMM_TYPE_SERIAL; + _serialPort = &serialPort; //Grab which port the user wants us to use + + return (isConnected()); +} + +//Enable or disable the printing of sent/response HEX values. +//Use this in conjunction with 'Transport Logging' from the Universal Reader Assistant to see what they're doing that we're not +void SFE_UBLOX_GPS::enableDebugging(Stream &debugPort) +{ + _debugSerial = &debugPort; //Grab which port the user wants us to use for debugging + + _printDebug = true; //Should we print the commands we send? Good for debugging +} +void SFE_UBLOX_GPS::disableDebugging(void) +{ + _printDebug = false; //Turn off extra print statements +} + +void SFE_UBLOX_GPS::factoryReset() +{ + // Copy default settings to permanent + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_CFG; + packetCfg.len = 13; + packetCfg.startingSpot = 0; + for (uint8_t i = 0; i < 4; i++) + { + payloadCfg[0 + i] = 0xff; // clear mask: copy default config to permanent config + payloadCfg[4 + i] = 0x00; // save mask: don't save current to permanent + payloadCfg[8 + i] = 0x00; // load mask: don't copy permanent config to current + } + payloadCfg[12] = 0xff; // all forms of permanent memory + sendCommand(packetCfg, 0); // don't expect ACK + hardReset(); // cause factory default config to actually be loaded and used cleanly +} + +void SFE_UBLOX_GPS::hardReset() +{ + // Issue hard reset + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RST; + packetCfg.len = 4; + packetCfg.startingSpot = 0; + payloadCfg[0] = 0xff; // cold start + payloadCfg[1] = 0xff; // cold start + payloadCfg[2] = 0; // 0=HW reset + payloadCfg[3] = 0; // reserved + sendCommand(packetCfg, 0); // don't expect ACK +} + +//Changes the serial baud rate of the Ublox module, can't return success/fail 'cause ACK from modem +//is lost due to baud rate change +void SFE_UBLOX_GPS::setSerialRate(uint32_t baudrate, uint8_t uartPort, uint16_t maxWait) +{ + //Get the current config values for the UART port + getPortSettings(uartPort, maxWait); //This will load the payloadCfg array with current port settings + + if (_printDebug == true) + { + _debugSerial->print("Current baud rate: "); + _debugSerial->println(((uint32_t)payloadCfg[10] << 16) | ((uint32_t)payloadCfg[9] << 8) | payloadCfg[8]); + } + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 20; + packetCfg.startingSpot = 0; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[8] = baudrate; + payloadCfg[9] = baudrate >> 8; + payloadCfg[10] = baudrate >> 16; + payloadCfg[11] = baudrate >> 24; + + if (_printDebug == true) + { + _debugSerial->print("New baud rate:"); + _debugSerial->println(((uint32_t)payloadCfg[10] << 16) | ((uint32_t)payloadCfg[9] << 8) | payloadCfg[8]); + } + + sendCommand(packetCfg); +} + +//Changes the I2C address that the Ublox module responds to +//0x42 is the default but can be changed with this command +boolean SFE_UBLOX_GPS::setI2CAddress(uint8_t deviceAddress, uint16_t maxWait) +{ + //Get the current config values for the I2C port + getPortSettings(COM_PORT_I2C); //This will load the payloadCfg array with current port settings + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 20; + packetCfg.startingSpot = 0; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[4] = deviceAddress << 1; //DDC mode LSB + + if (sendCommand(packetCfg, maxWait) == true) + { + //Success! Now change our internal global. + _gpsI2Caddress = deviceAddress; //Store the I2C address from user + return (true); + } + return (false); +} + +//Want to see the NMEA messages on the Serial port? Here's how +void SFE_UBLOX_GPS::setNMEAOutputPort(Stream &nmeaOutputPort) +{ + _nmeaOutputPort = &nmeaOutputPort; //Store the port from user +} + +//Called regularly to check for available bytes on the user' specified port +boolean SFE_UBLOX_GPS::checkUblox() +{ + if (commType == COMM_TYPE_I2C) + checkUbloxI2C(); + else if (commType == COMM_TYPE_SERIAL) + checkUbloxSerial(); + return false; +} + +//Polls I2C for data, passing any new bytes to process() +boolean SFE_UBLOX_GPS::checkUbloxI2C() +{ + if (millis() - lastCheck >= I2C_POLLING_WAIT_MS) + { + //Get the number of bytes available from the module + uint16_t bytesAvailable = 0; + _i2cPort->beginTransmission(_gpsI2Caddress); + _i2cPort->write(0xFD); //0xFD (MSB) and 0xFE (LSB) are the registers that contain number of bytes available + if (_i2cPort->endTransmission(false) != 0) //Send a restart command. Do not release bus. + return (false); //Sensor did not ACK + + _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)2); + if (_i2cPort->available()) + { + uint8_t msb = _i2cPort->read(); + uint8_t lsb = _i2cPort->read(); + bytesAvailable = (uint16_t)msb << 8 | lsb; + } + + if (bytesAvailable == 0) + { + if (_printDebug == true) + { + _debugSerial->println("No bytes available"); + } + lastCheck = millis(); //Put off checking to avoid I2C bus traffic + return true; + } + + while (bytesAvailable) + { + _i2cPort->beginTransmission(_gpsI2Caddress); + _i2cPort->write(0xFF); //0xFF is the register to read general NMEA data from + if (_i2cPort->endTransmission(false) != 0) //Send a restart command. Do not release bus. + return (false); //Sensor did not ACK + + //Limit to 32 bytes or whatever the buffer limit is for given platform + uint16_t bytesToRead = bytesAvailable; + if (bytesToRead > I2C_BUFFER_LENGTH) + bytesToRead = I2C_BUFFER_LENGTH; + + _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)bytesToRead); + if (_i2cPort->available()) + { + for (uint16_t x = 0; x < bytesToRead; x++) + { + process(_i2cPort->read()); //Grab the actual character and process it + } + } + else + return (false); //Sensor did not respond + + bytesAvailable -= bytesToRead; + } + } + + return (true); + +} //end checkUbloxI2C() + +//Checks Serial for data, passing any new bytes to process() +boolean SFE_UBLOX_GPS::checkUbloxSerial() +{ + while (_serialPort->available()) + { + process(_serialPort->read()); + } + return (true); + +} //end checkUbloxSerial() + +//Processes NMEA and UBX binary sentences one byte at a time +//Take a given byte and file it into the proper array +void SFE_UBLOX_GPS::process(uint8_t incoming) +{ + + if (_printDebug == true) + { + //if (currentSentence == NONE && incoming == 0xB5) //UBX binary frames start with 0xB5, aka μ + // _debugSerial->println(); //Show new packet start + + //_debugSerial->print(" "); + //_debugSerial->print(incoming, HEX); + } + + if (currentSentence == NONE || currentSentence == NMEA) + { + if (incoming == 0xB5) //UBX binary frames start with 0xB5, aka μ + { + //This is the start of a binary sentence. Reset flags. + //We still don't know the response class + ubxFrameCounter = 0; + + rollingChecksumA = 0; //Reset our rolling checksums + rollingChecksumB = 0; + + currentSentence = UBX; + } + else if (incoming == '$') + { + currentSentence = NMEA; + } + else if (incoming == 0xD3) //RTCM frames start with 0xD3 + { + rtcmFrameCounter = 0; + currentSentence = RTCM; + } + else + { + //This character is unknown or we missed the previous start of a sentence + } + } + + //Depending on the sentence, pass the character to the individual processor + if (currentSentence == UBX) + { + //Decide what type of response this is + if (ubxFrameCounter == 0 && incoming != 0xB5) //ISO 'μ' + currentSentence = NONE; //Something went wrong. Reset. + else if (ubxFrameCounter == 1 && incoming != 0x62) //ASCII 'b' + currentSentence = NONE; //Something went wrong. Reset. + else if (ubxFrameCounter == 2) //Class + { + packetAck.counter = 0; + packetAck.valid = false; + packetCfg.counter = 0; + packetCfg.valid = false; + + //We can now identify the type of response + if (incoming == UBX_CLASS_ACK) + ubxFrameClass = CLASS_ACK; + else + ubxFrameClass = CLASS_NOT_AN_ACK; + } + + ubxFrameCounter++; + + //Depending on this frame's class, pass different structs and payload arrays + if (ubxFrameClass == CLASS_ACK) + processUBX(incoming, &packetAck); + else if (ubxFrameClass == CLASS_NOT_AN_ACK) + processUBX(incoming, &packetCfg); + } + else if (currentSentence == NMEA) + { + processNMEA(incoming); //Process each NMEA character + } + else if (currentSentence == RTCM) + { + processRTCMframe(incoming); //Deal with RTCM bytes + } +} + +//This is the default or generic NMEA processor. We're only going to pipe the data to serial port so we can see it. +//User could overwrite this function to pipe characters to nmea.process(c) of tinyGPS or MicroNMEA +//Or user could pipe each character to a buffer, radio, etc. +void SFE_UBLOX_GPS::processNMEA(char incoming) +{ + //If user has assigned an output port then pipe the characters there + if (_nmeaOutputPort != NULL) + _nmeaOutputPort->write(incoming); //Echo this byte to the serial port +} + +//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) +//RTCM packet structure is very odd. I never found RTCM STANDARD 10403.2 but +//http://d1.amobbs.com/bbs_upload782111/files_39/ourdev_635123CK0HJT.pdf is good +//https://dspace.cvut.cz/bitstream/handle/10467/65205/F3-BP-2016-Shkalikava-Anastasiya-Prenos%20polohove%20informace%20prostrednictvim%20datove%20site.pdf?sequence=-1 +//Lead me to: https://forum.u-blox.com/index.php/4348/how-to-read-rtcm-messages-from-neo-m8p +//RTCM 3.2 bytes look like this: +//Byte 0: Always 0xD3 +//Byte 1: 6-bits of zero +//Byte 2: 10-bits of length of this packet including the first two-ish header bytes, + 6. +//byte 3 + 4 bits: Msg type 12 bits +//Example: D3 00 7C 43 F0 ... / 0x7C = 124+6 = 130 bytes in this packet, 0x43F = Msg type 1087 +void SFE_UBLOX_GPS::processRTCMframe(uint8_t incoming) +{ + if (rtcmFrameCounter == 1) + { + rtcmLen = (incoming & 0x03) << 8; //Get the last two bits of this byte. Bits 8&9 of 10-bit length + } + else if (rtcmFrameCounter == 2) + { + rtcmLen |= incoming; //Bits 0-7 of packet length + rtcmLen += 6; //There are 6 additional bytes of what we presume is header, msgType, CRC, and stuff + } + /*else if (rtcmFrameCounter == 3) + { + rtcmMsgType = incoming << 4; //Message Type, MS 4 bits + } + else if (rtcmFrameCounter == 4) + { + rtcmMsgType |= (incoming >> 4); //Message Type, bits 0-7 + }*/ + + rtcmFrameCounter++; + + processRTCM(incoming); //Here is where we expose this byte to the user + + if (rtcmFrameCounter == rtcmLen) + { + //We're done! + currentSentence = NONE; //Reset and start looking for next sentence type + } +} + +//This function is called for each byte of an RTCM frame +//Ths user can overwrite this function and process the RTCM frame as they please +//Bytes can be piped to Serial or other interface. The consumer could be a radio or the internet (Ntrip broadcaster) +void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) +{ + //Radio.sendReliable((String)incoming); //An example of passing this byte to a radio + + //Serial.write(incoming); //An example of passing this byte out the serial port + + //Debug printing + // Serial.print(" "); + // if(incoming < 0x10) Serial.print("0"); + // if(incoming < 0x10) Serial.print("0"); + // Serial.print(incoming, HEX); + // if(rtcmFrameCounter % 16 == 0) Serial.println(); +} + +//Given a character, file it away into the uxb packet structure +//Set valid = true once sentence is completely received and passes CRC +//The payload portion of the packet can be 100s of bytes but the max array +//size is roughly 64 bytes. startingSpot can be set so we only record +//a subset of bytes within a larger packet. +void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) +{ + //Add all incoming bytes to the rolling checksum + //Stop at len+4 as this is the checksum bytes to that should not be added to the rolling checksum + if (incomingUBX->counter < incomingUBX->len + 4) + addToChecksum(incoming); + + if (incomingUBX->counter == 0) + { + incomingUBX->cls = incoming; + } + else if (incomingUBX->counter == 1) + { + incomingUBX->id = incoming; + } + else if (incomingUBX->counter == 2) //Len LSB + { + incomingUBX->len = incoming; + } + else if (incomingUBX->counter == 3) //Len MSB + { + incomingUBX->len |= incoming << 8; + } + else if (incomingUBX->counter == incomingUBX->len + 4) //ChecksumA + { + incomingUBX->checksumA = incoming; + } + else if (incomingUBX->counter == incomingUBX->len + 5) //ChecksumB + { + incomingUBX->checksumB = incoming; + + currentSentence = NONE; //We're done! Reset the sentence to being looking for a new start char + + //Validate this sentence + if (incomingUBX->checksumA == rollingChecksumA && incomingUBX->checksumB == rollingChecksumB) + { + if (_printDebug == true) + { + _debugSerial->print("Received: "); + printPacket(incomingUBX); + } + incomingUBX->valid = true; + processUBXpacket(incomingUBX); //We've got a valid packet, now do something with it + } + else + { + if (_printDebug == true) + { + _debugSerial->println("Checksum failed. Response too big?"); + } + } + } + else //Load this byte into the payload array + { + //If a UBX_NAV_PVT packet comes in asynchronously, we need to fudge the startingSpot + uint16_t startingSpot = incomingUBX->startingSpot; + if (incomingUBX->cls == UBX_CLASS_NAV && incomingUBX->id == UBX_NAV_PVT) + startingSpot = 0; + //Begin recording if counter goes past startingSpot + if ((incomingUBX->counter - 4) >= startingSpot) + { + //Check to see if we have room for this byte + if (((incomingUBX->counter - 4) - startingSpot) < MAX_PAYLOAD_SIZE) //If counter = 208, starting spot = 200, we're good to record. + incomingUBX->payload[incomingUBX->counter - 4 - startingSpot] = incoming; //Store this byte into payload array + } + } + + incomingUBX->counter++; +} + +//Once a packet has been received and validated, identify this packet's class/id and update internal flags +void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) +{ + switch (msg->cls) + { + case UBX_CLASS_ACK: + //We don't want to store ACK packets, just set commandAck flag + if (msg->id == UBX_ACK_ACK && msg->payload[0] == packetCfg.cls && msg->payload[1] == packetCfg.id) + { + //The ack we just received matched the CLS/ID of last packetCfg sent + if (_printDebug == true) + { + _debugSerial->println("Command sent/ack'd successfully"); + } + commandAck = true; + } + break; + + case UBX_CLASS_NAV: + if (msg->id == UBX_NAV_PVT && msg->len == 92) + { + //Parse various byte fields into global vars + constexpr int startingSpot = 0; //fixed value used in processUBX + + gpsYear = extractInt(4); + gpsMonth = extractByte(6); + gpsDay = extractByte(7); + gpsHour = extractByte(8); + gpsMinute = extractByte(9); + gpsSecond = extractByte(10); + + fixType = extractByte(20 - startingSpot); + carrierSolution = extractByte(21 - startingSpot) >> 6; //Get 6th&7th bits of this byte + SIV = extractByte(23 - startingSpot); + longitude = extractLong(24 - startingSpot); + latitude = extractLong(28 - startingSpot); + altitude = extractLong(32 - startingSpot); + altitudeMSL = extractLong(36 - startingSpot); + groundSpeed = extractLong(60 - startingSpot); + headingOfMotion = extractLong(64 - startingSpot); + pDOP = extractLong(76 - startingSpot); + + //Mark all datums as fresh (not read before) + moduleQueried.gpsYear = true; + moduleQueried.gpsMonth = true; + moduleQueried.gpsDay = true; + moduleQueried.gpsHour = true; + moduleQueried.gpsMinute = true; + moduleQueried.gpsSecond = true; + + moduleQueried.all = true; + moduleQueried.longitude = true; + moduleQueried.latitude = true; + moduleQueried.altitude = true; + moduleQueried.altitudeMSL = true; + moduleQueried.SIV = true; + moduleQueried.fixType = true; + moduleQueried.carrierSolution = true; + moduleQueried.groundSpeed = true; + moduleQueried.headingOfMotion = true; + moduleQueried.pDOP = true; + } + break; + } +} + +//Given a packet and payload, send everything including CRC bytes via I2C port +boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) +{ + commandAck = false; //We're about to send a command. Begin waiting for ack. + calcChecksum(&outgoingUBX); //Sets checksum A and B bytes of the packet + + if (_printDebug == true) + { + _debugSerial->print("Sending: "); + printPacket(&outgoingUBX); + } + if (commType == COMM_TYPE_I2C) + { + if (!sendI2cCommand(outgoingUBX, maxWait)) + return false; + } + else if (commType == COMM_TYPE_SERIAL) + { + sendSerialCommand(outgoingUBX); + } + + if (maxWait > 0) + { + //Give waitForResponse the cls/id to check for + return waitForResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + } + return true; +} + +boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) +{ + //Point at 0xFF data register + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes + _i2cPort->write(0xFF); + if (_i2cPort->endTransmission() != 0) //Don't release bus + return (false); //Sensor did not ACK + + //Write header bytes + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes + _i2cPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. + _i2cPort->write(UBX_SYNCH_2); //b + _i2cPort->write(outgoingUBX.cls); + _i2cPort->write(outgoingUBX.id); + _i2cPort->write(outgoingUBX.len & 0xFF); //LSB + _i2cPort->write(outgoingUBX.len >> 8); //MSB + if (_i2cPort->endTransmission(false) != 0) //Do not release bus + return (false); //Sensor did not ACK + + //Write payload. Limit the sends into 32 byte chunks + //This code based on ublox: https://forum.u-blox.com/index.php/20528/how-to-use-i2c-to-get-the-nmea-frames + uint16_t bytesToSend = outgoingUBX.len; + + //"The number of data bytes must be at least 2 to properly distinguish + //from the write access to set the address counter in random read accesses." + uint16_t startSpot = 0; + while (bytesToSend > 1) + { + uint8_t len = bytesToSend; + if (len > I2C_BUFFER_LENGTH) + len = I2C_BUFFER_LENGTH; + + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); + //_i2cPort->write(outgoingUBX.payload, len); //Write a portion of the payload to the bus + + for (uint16_t x = 0; x < len; x++) + _i2cPort->write(outgoingUBX.payload[startSpot + x]); //Write a portion of the payload to the bus + + if (_i2cPort->endTransmission(false) != 0) //Don't release bus + return (false); //Sensor did not ACK + + //*outgoingUBX.payload += len; //Move the pointer forward + startSpot += len; //Move the pointer forward + bytesToSend -= len; + } + + //Write checksum + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); + if (bytesToSend == 1) + _i2cPort->write(outgoingUBX.payload, 1); + _i2cPort->write(outgoingUBX.checksumA); + _i2cPort->write(outgoingUBX.checksumB); + + //All done transmitting bytes. Release bus. + if (_i2cPort->endTransmission() != 0) + return (false); //Sensor did not ACK + return (true); +} + +//Given a packet and payload, send everything including CRC bytesA via Serial port +void SFE_UBLOX_GPS::sendSerialCommand(ubxPacket outgoingUBX) +{ + //Write header bytes + _serialPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. + _serialPort->write(UBX_SYNCH_2); //b + _serialPort->write(outgoingUBX.cls); + _serialPort->write(outgoingUBX.id); + _serialPort->write(outgoingUBX.len & 0xFF); //LSB + _serialPort->write(outgoingUBX.len >> 8); //MSB + + //Write payload. + for (int i = 0; i < outgoingUBX.len; i++) + { + _serialPort->write(outgoingUBX.payload[i]); + } + + //Write checksum + _serialPort->write(outgoingUBX.checksumA); + _serialPort->write(outgoingUBX.checksumB); +} + +//Returns true if I2C device ack's +boolean SFE_UBLOX_GPS::isConnected() +{ + if (commType == COMM_TYPE_I2C) + { + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); + return _i2cPort->endTransmission() == 0; + } + else if (commType == COMM_TYPE_SERIAL) + { + // Query navigation rate to see whether we get a meaningful response + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RATE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + return sendCommand(packetCfg); + } + return false; +} + +//Given a message, calc and store the two byte "8-Bit Fletcher" checksum over the entirety of the message +//This is called before we send a command message +void SFE_UBLOX_GPS::calcChecksum(ubxPacket *msg) +{ + msg->checksumA = 0; + msg->checksumB = 0; + + msg->checksumA += msg->cls; + msg->checksumB += msg->checksumA; + + msg->checksumA += msg->id; + msg->checksumB += msg->checksumA; + + msg->checksumA += (msg->len & 0xFF); + msg->checksumB += msg->checksumA; + + msg->checksumA += (msg->len >> 8); + msg->checksumB += msg->checksumA; + + for (uint16_t i = 0; i < msg->len; i++) + { + msg->checksumA += msg->payload[i]; + msg->checksumB += msg->checksumA; + } +} + +//Given a message and a byte, add to rolling "8-Bit Fletcher" checksum +//This is used when receiving messages from module +void SFE_UBLOX_GPS::addToChecksum(uint8_t incoming) +{ + rollingChecksumA += incoming; + rollingChecksumB += rollingChecksumA; +} + +//Pretty prints the current ubxPacket +void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) +{ + if (_printDebug == true) + { + _debugSerial->print("CLS:"); + _debugSerial->print(packet->cls, HEX); + + _debugSerial->print(" ID:"); + _debugSerial->print(packet->id, HEX); + + //_debugSerial->print(" Len: 0x"); + //_debugSerial->print(packet->len, HEX); + + _debugSerial->print(" Payload:"); + + for (int x = 0; x < packet->len; x++) + { + _debugSerial->print(" "); + _debugSerial->print(packet->payload[x], HEX); + } + _debugSerial->println(); + } +} + +//=-=-=-=-=-=-=-= Specific commands =-=-=-=-=-=-=-==-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Poll the module until and ack is received +boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +{ + commandAck = false; //Reset flag + packetCfg.valid = false; //This will go true when we receive a response to the packet we sent + + unsigned long startTime = millis(); + while (millis() - startTime < maxTime) + { + checkUblox(); //See if new data is available. Process bytes as they come in. + + if (commandAck == true) + return (true); //If the packet we just sent was a CFG packet then we'll get an ACK + if (packetCfg.valid == true) + { + //Did we receive a config packet that matches the cls/id we requested? + if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) + { + if (_printDebug == true) + { + _debugSerial->println(F("CLS/ID match!")); + } + return (true); //If the packet we just sent was a NAV packet then we'll just get data back + } + else + { + if (_printDebug == true) + { + _debugSerial->print(F("Packet didn't match CLS/ID")); + printPacket(&packetCfg); + } + } + } + + delay(1); + } + + if (_printDebug == true) + { + _debugSerial->println(F("waitForResponse timeout")); + } + + return (false); +} + +//Save current configuration to flash and BBR (battery backed RAM) +//This still works but it is the old way of configuring ublox modules. See getVal and setVal for the new methods +boolean SFE_UBLOX_GPS::saveConfiguration(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_CFG; + packetCfg.len = 12; + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + packetCfg.payload[4] = 0xFF; //Set any bit in the saveMask field to save current config to Flash and BBR + + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + return (true); +} + +//Reset module to factory defaults +//This still works but it is the old way of configuring ublox modules. See getVal and setVal for the new methods +boolean SFE_UBLOX_GPS::factoryDefault(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_CFG; + packetCfg.len = 12; + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + packetCfg.payload[0] = 0xFF; //Set any bit in the clearMask field to clear saved config + packetCfg.payload[8] = 0xFF; //Set any bit in the loadMask field to discard current config and rebuild from lower non-volatile memory layers + + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + return (true); +} + +//Given a group, ID and size, return the value of this config spot +//The 32-bit key is put together from group/ID/size. See other getVal to send key directly. +//Configuration of modern Ublox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P +uint8_t SFE_UBLOX_GPS::getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait) +{ + //Create key + uint32_t key = 0; + key |= (uint32_t)id; + key |= (uint32_t)group << 16; + key |= (uint32_t)size << 28; + + if (_printDebug == true) + { + _debugSerial->print("key: 0x"); + _debugSerial->print(key, HEX); + _debugSerial->println(); + } + + return getVal8(key, layer, maxWait); +} + +//Given a key, return its value +//This function takes a full 32-bit key +//Default layer is BBR +//Configuration of modern Ublox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P +uint8_t SFE_UBLOX_GPS::getVal8(uint32_t key, uint8_t layer, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_VALGET; + packetCfg.len = 4 + 4 * 1; //While multiple keys are allowed, we will send only one key at a time + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + payloadCfg[0] = 0; //Message Version - set to 0 + payloadCfg[1] = layer; //By default we ask for the BBR layer + + //Load key into outgoing payload + payloadCfg[4] = key >> 8 * 0; //Key LSB + payloadCfg[5] = key >> 8 * 1; + payloadCfg[6] = key >> 8 * 2; + payloadCfg[7] = key >> 8 * 3; + + if (_printDebug == true) + { + _debugSerial->print("key: 0x"); + _debugSerial->print(key, HEX); + _debugSerial->println(); + } + + //Send VALGET command with this key + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + //Verify the response is the correct length as compared to what the user called (did the module respond with 8-bits but the user called getVal32?) + //Response is 8 bytes plus cfg data + //if(packet->len > 8+1) + + //Pull the requested value from the response + //Response starts at 4+1*N with the 32-bit key so the actual data we're looking for is at 8+1*N + return (extractByte(8)); +} + +//Given a key, set an 8-bit value +//This function takes a full 32-bit key +//Default layer is BBR +//Configuration of modern Ublox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P +uint8_t SFE_UBLOX_GPS::setVal(uint32_t key, uint16_t value, uint8_t layer, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_VALSET; + packetCfg.len = 4 + 4 + 2; //4 byte header, 4 byte key ID, 2 bytes of value + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + payloadCfg[0] = 0; //Message Version - set to 0 + payloadCfg[1] = layer; //By default we ask for the BBR layer + + //Load key into outgoing payload + payloadCfg[4] = key >> 8 * 0; //Key LSB + payloadCfg[5] = key >> 8 * 1; + payloadCfg[6] = key >> 8 * 2; + payloadCfg[7] = key >> 8 * 3; + + //Load user's value + payloadCfg[8] = value >> 8 * 0; //Value LSB + payloadCfg[9] = value >> 8 * 1; + + //Send VALSET command with this key and value + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + //All done + return (true); +} + +//Get the current TimeMode3 settings - these contain survey in statuses +boolean SFE_UBLOX_GPS::getSurveyMode(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_TMODE3; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + return (sendCommand(packetCfg, maxWait)); +} + +//Control Survey-In for NEO-M8P +boolean SFE_UBLOX_GPS::setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait) +{ + if (getSurveyMode() == false) //Ask module for the current TimeMode3 settings. Loads into payloadCfg. + return (false); + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_TMODE3; + packetCfg.len = 40; + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + //payloadCfg should be loaded with poll response. Now modify only the bits we care about + payloadCfg[2] = mode; //Set mode. Survey-In and Disabled are most common. + + payloadCfg[24] = observationTime & 0xFF; //svinMinDur in seconds + payloadCfg[25] = observationTime >> 8; //svinMinDur in seconds + + uint32_t svinAccLimit = requiredAccuracy * 10000; //Convert m to 0.1mm + payloadCfg[28] = svinAccLimit & 0xFF; //svinAccLimit in 0.1mm increments + payloadCfg[29] = svinAccLimit >> 8; + payloadCfg[30] = svinAccLimit >> 16; + + return (sendCommand(packetCfg, maxWait)); //Wait for ack +} + +//Begin Survey-In for NEO-M8P +boolean SFE_UBLOX_GPS::enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait) +{ + return (setSurveyMode(SVIN_MODE_ENABLE, observationTime, requiredAccuracy, maxWait)); +} + +//Stop Survey-In for NEO-M8P +boolean SFE_UBLOX_GPS::disableSurveyMode(uint16_t maxWait) +{ + return (setSurveyMode(SVIN_MODE_DISABLE, 0, 0, maxWait)); +} + +//Reads survey in status and sets the global variables +//for status, position valid, observation time, and mean 3D StdDev +//Returns true if commands was successful +boolean SFE_UBLOX_GPS::getSurveyStatus(uint16_t maxWait) +{ + //Reset variables + svin.active = false; + svin.valid = false; + svin.observationTime = 0; + svin.meanAccuracy = 0; + + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_SVIN; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + //We got a response, now parse the bits into the svin structure + svin.observationTime = extractLong(8); + + uint32_t tempFloat = extractLong(28); + svin.meanAccuracy = tempFloat / 10000.0; //Convert 0.1mm to m + + svin.valid = payloadCfg[36]; + svin.active = payloadCfg[37]; //1 if survey in progress, 0 otherwise + + return (true); +} + +//Given a message number turns on a message ID for output over a given portID (UART, I2C, SPI, USB, etc) +//To disable a message, set secondsBetween messages to 0 +//Note: This function will return false if the message is already enabled +//For base station RTK output we need to enable various sentences + +//NEO-M8P has four: +//1005 = 0xF5 0x05 - Stationary RTK reference ARP +//1077 = 0xF5 0x4D - GPS MSM7 +//1087 = 0xF5 0x57 - GLONASS MSM7 +//1230 = 0xF5 0xE6 - GLONASS code-phase biases, set to once every 10 seconds + +//ZED-F9P has six: +//1005, 1074, 1084, 1094, 1124, 1230 + +//Much of this configuration is not documented and instead discerned from u-center binary console +boolean SFE_UBLOX_GPS::enableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint8_t secondsBetweenMessages, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_MSG; + packetCfg.len = 8; + packetCfg.startingSpot = 0; + + //Clear packet payload + for (uint8_t x = 0; x < packetCfg.len; x++) + packetCfg.payload[x] = 0; + + packetCfg.payload[0] = UBX_RTCM_MSB; //MSB, always 0xF5. Interesting, these are not little endian + packetCfg.payload[1] = messageNumber; //LSB + packetCfg.payload[2 + portID] = secondsBetweenMessages; //Byte 2 is I2C, byte 3 is UART1, etc. + + return (sendCommand(packetCfg, maxWait)); +} + +//Disable a given message on a given port by setting secondsBetweenMessages to zero +boolean SFE_UBLOX_GPS::disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait) +{ + return (enableRTCMmessage(messageNumber, portID, 0, maxWait)); +} + +//Loads the payloadCfg array with the current protocol bits located the UBX-CFG-PRT register for a given port +boolean SFE_UBLOX_GPS::getPortSettings(uint8_t portID, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 1; + packetCfg.startingSpot = 0; + + payloadCfg[0] = portID; + + return (sendCommand(packetCfg, maxWait)); +} + +//Configure a given port to output UBX, NMEA, RTCM3 or a combination thereof +//Port 0=I2c, 1=UART1, 2=UART2, 3=USB, 4=SPI +//Bit:0 = UBX, :1=NMEA, :5=RTCM3 +boolean SFE_UBLOX_GPS::setPortOutput(uint8_t portID, uint8_t outStreamSettings, uint16_t maxWait) +{ + //Get the current config values for this port ID + if (getPortSettings(portID) == false) + return (false); //Something went wrong. Bail. + + //Yes, this is the depreciated way to do it but it's still supported on v27 so it + //covers both ZED-F9P (v27) and SAM-M8Q (v18) + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 20; + packetCfg.startingSpot = 0; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[14] = outStreamSettings; //OutProtocolMask LSB - Set outStream bits + + return (sendCommand(packetCfg, maxWait)); +} + +//Configure a given port to input UBX, NMEA, RTCM3 or a combination thereof +//Port 0=I2c, 1=UART1, 2=UART2, 3=USB, 4=SPI +//Bit:0 = UBX, :1=NMEA, :5=RTCM3 +boolean SFE_UBLOX_GPS::setPortInput(uint8_t portID, uint8_t inStreamSettings, uint16_t maxWait) +{ + //Get the current config values for this port ID + //This will load the payloadCfg array with current port settings + if (getPortSettings(portID) == false) + return (false); //Something went wrong. Bail. + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 20; + packetCfg.startingSpot = 0; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[12] = inStreamSettings; //InProtocolMask LSB - Set inStream bits + + return (sendCommand(packetCfg, maxWait)); +} + +//Configure a port to output UBX, NMEA, RTCM3 or a combination thereof +boolean SFE_UBLOX_GPS::setI2COutput(uint8_t comSettings, uint16_t maxWait) +{ + return (setPortOutput(COM_PORT_I2C, comSettings, maxWait)); +} +boolean SFE_UBLOX_GPS::setUART1Output(uint8_t comSettings, uint16_t maxWait) +{ + return (setPortOutput(COM_PORT_UART1, comSettings, maxWait)); +} +boolean SFE_UBLOX_GPS::setUART2Output(uint8_t comSettings, uint16_t maxWait) +{ + return (setPortOutput(COM_PORT_UART2, comSettings, maxWait)); +} +boolean SFE_UBLOX_GPS::setUSBOutput(uint8_t comSettings, uint16_t maxWait) +{ + return (setPortOutput(COM_PORT_USB, comSettings, maxWait)); +} +boolean SFE_UBLOX_GPS::setSPIOutput(uint8_t comSettings, uint16_t maxWait) +{ + return (setPortOutput(COM_PORT_SPI, comSettings, maxWait)); +} + +//Set the rate at which the module will give us an updated navigation solution +//Expects a number that is the updates per second. For example 1 = 1Hz, 2 = 2Hz, etc. +//Max is 40Hz(?!) +boolean SFE_UBLOX_GPS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait) +{ + //if(updateRate > 40) updateRate = 40; //Not needed: module will correct out of bounds values + + //Query the module for the latest lat/long + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RATE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) //This will load the payloadCfg array with current settings of the given register + return (false); //If command send fails then bail + + uint16_t measurementRate = 1000 / navFreq; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[0] = measurementRate & 0xFF; //measRate LSB + payloadCfg[1] = measurementRate >> 8; //measRate MSB + + return (sendCommand(packetCfg, maxWait)); +} + +//Get the rate at which the module is outputting nav solutions +uint8_t SFE_UBLOX_GPS::getNavigationFrequency(uint16_t maxWait) +{ + //Query the module for the latest lat/long + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RATE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) //This will load the payloadCfg array with current settings of the given register + return (0); //If command send fails then bail + + uint16_t measurementRate = 0; + + //payloadCfg is now loaded with current bytes. Get what we need + measurementRate = extractInt(0); //Pull from payloadCfg at measRate LSB + + measurementRate = 1000 / measurementRate; //This may return an int when it's a float, but I'd rather not return 4 bytes + return (measurementRate); +} + +//Enable or disable automatic navigation message generation by the GPS. This changes the way getPVT +//works. +boolean SFE_UBLOX_GPS::setAutoPVT(boolean enable, uint16_t maxWait) +{ + 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_PVT; + payloadCfg[2] = enable ? 1 : 0; // rate relative to navigation freq. + + bool ok = sendCommand(packetCfg, maxWait); + if (ok) + autoPVT = enable; + moduleQueried.all = false; + return ok; +} + +//Given a spot in the payload array, extract four bytes and build a long +uint32_t SFE_UBLOX_GPS::extractLong(uint8_t spotToStart) +{ + uint32_t val = 0; + val |= (int32_t)payloadCfg[spotToStart + 0] << 8 * 0; + val |= (int32_t)payloadCfg[spotToStart + 1] << 8 * 1; + val |= (int32_t)payloadCfg[spotToStart + 2] << 8 * 2; + val |= (int32_t)payloadCfg[spotToStart + 3] << 8 * 3; + return (val); +} + +//Given a spot in the payload array, extract two bytes and build an int +uint16_t SFE_UBLOX_GPS::extractInt(uint8_t spotToStart) +{ + uint16_t val = 0; + val |= (int16_t)payloadCfg[spotToStart + 0] << 8 * 0; + val |= (int16_t)payloadCfg[spotToStart + 1] << 8 * 1; + return (val); +} + +//Given a spot, extract byte the payload +uint8_t SFE_UBLOX_GPS::extractByte(uint8_t spotToStart) +{ + return (payloadCfg[spotToStart]); +} + +//Get the current year +uint16_t SFE_UBLOX_GPS::getYear(uint16_t maxWait) +{ + if (moduleQueried.gpsYear == false) + getPVT(); + moduleQueried.gpsYear = false; //Since we are about to give this to user, mark this data as stale + return (gpsYear); +} + +//Get the current month +uint8_t SFE_UBLOX_GPS::getMonth(uint16_t maxWait) +{ + if (moduleQueried.gpsMonth == false) + getPVT(); + moduleQueried.gpsMonth = false; //Since we are about to give this to user, mark this data as stale + return (gpsMonth); +} + +//Get the current year +uint8_t SFE_UBLOX_GPS::getDay(uint16_t maxWait) +{ + if (moduleQueried.gpsDay == false) + getPVT(); + moduleQueried.gpsDay = false; //Since we are about to give this to user, mark this data as stale + return (gpsDay); +} + +//Get the current year +uint8_t SFE_UBLOX_GPS::getHour(uint16_t maxWait) +{ + if (moduleQueried.gpsHour == false) + getPVT(); + moduleQueried.gpsHour = false; //Since we are about to give this to user, mark this data as stale + return (gpsHour); +} + +//Get the current year +uint8_t SFE_UBLOX_GPS::getMinute(uint16_t maxWait) +{ + if (moduleQueried.gpsMinute == false) + getPVT(); + moduleQueried.gpsMinute = false; //Since we are about to give this to user, mark this data as stale + return (gpsMinute); +} + +//Get the current year +uint8_t SFE_UBLOX_GPS::getSecond(uint16_t maxWait) +{ + if (moduleQueried.gpsSecond == false) + getPVT(); + moduleQueried.gpsSecond = false; //Since we are about to give this to user, mark this data as stale + return (gpsSecond); +} + +//Get the latest Position/Velocity/Time solution and fill all global variables +boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) +{ + if (autoPVT) + { + //The GPS is automatically reporting, we just check whether we got unread data + checkUblox(); + return moduleQueried.all; + } + else + { + //The GPS is not automatically reporting navigation position so we have to poll explicitly + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_PVT; + packetCfg.len = 0; + //packetCfg.startingSpot = 20; //Begin listening at spot 20 so we can record up to 20+MAX_PAYLOAD_SIZE = 84 bytes Note:now hard-coded in processUBX + + //The data is parsed as part of processing the response + return sendCommand(packetCfg, maxWait); + return (false); //If command send fails then bail + } +} + +//Get the current 3D high precision positional accuracy - a fun thing to watch +//Returns a long representing the 3D accuracy in millimeters +uint32_t SFE_UBLOX_GPS::getPositionAccuracy(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_HPPOSECEF; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) + return (0); //If command send fails then bail + + uint32_t tempAccuracy = extractLong(24); //We got a response, now extract a long beginning at a given position + + if ((tempAccuracy % 10) >= 5) + tempAccuracy += 5; //Round fraction of mm up to next mm if .5 or above + tempAccuracy /= 10; //Convert 0.1mm units to mm + + return (tempAccuracy); +} + +//Get the current 3D high precision positional accuracy - a fun thing to watch +//Returns a long representing the 3D accuracy in cm +uint32_t SFE_UBLOX_GPS::getPositionAccuracyNormal(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_POSECEF; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) + return (0); //If command send fails then bail + + uint32_t tempAccuracy = extractLong(16); //We got a response, now extract a long beginning at a given position + + return (tempAccuracy); +} +//Get the current latitude in degrees +//Returns a long representing the number of degrees *10^-7 +int32_t SFE_UBLOX_GPS::getLatitude(uint16_t maxWait) +{ + if (moduleQueried.latitude == false) + getPVT(); + moduleQueried.latitude = false; //Since we are about to give this to user, mark this data as stale + + return (latitude); +} + +//Get the current longitude in degrees +//Returns a long representing the number of degrees *10^-7 +int32_t SFE_UBLOX_GPS::getLongitude(uint16_t maxWait) +{ + if (moduleQueried.longitude == false) + getPVT(); + moduleQueried.longitude = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (longitude); +} + +//Get the current altitude in mm according to ellipsoid model +int32_t SFE_UBLOX_GPS::getAltitude(uint16_t maxWait) +{ + if (moduleQueried.altitude == false) + getPVT(); + moduleQueried.altitude = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (altitude); +} + +//Get the current altitude in mm according to mean sea level +//Ellipsoid model: https://www.esri.com/news/arcuser/0703/geoid1of3.html +//Difference between Ellipsoid Model and Mean Sea Level: https://eos-gnss.com/elevation-for-beginners/ +int32_t SFE_UBLOX_GPS::getAltitudeMSL(uint16_t maxWait) +{ + if (moduleQueried.altitudeMSL == false) + getPVT(); + moduleQueried.altitudeMSL = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (altitudeMSL); +} + +//Get the number of satellites used in fix +uint8_t SFE_UBLOX_GPS::getSIV(uint16_t maxWait) +{ + if (moduleQueried.SIV == false) + getPVT(); + moduleQueried.SIV = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (SIV); +} + +//Get the current fix type +//0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=GNSS, 5=Time fix +uint8_t SFE_UBLOX_GPS::getFixType(uint16_t maxWait) +{ + if (moduleQueried.fixType == false) + getPVT(); + moduleQueried.fixType = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (fixType); +} + +//Get the carrier phase range solution status +//Useful when querying module to see if it has high-precision RTK fix +//0=No solution, 1=Float solution, 2=Fixed solution +uint8_t SFE_UBLOX_GPS::getCarrierSolutionType(uint16_t maxWait) +{ + if (moduleQueried.carrierSolution == false) + getPVT(); + moduleQueried.carrierSolution = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (carrierSolution); +} + +//Get the ground speed in mm/s +int32_t SFE_UBLOX_GPS::getGroundSpeed(uint16_t maxWait) +{ + if (moduleQueried.groundSpeed == false) + getPVT(); + moduleQueried.groundSpeed = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (groundSpeed); +} + +//Get the heading of motion (as opposed to heading of car) in degrees * 10^-5 +int32_t SFE_UBLOX_GPS::getHeading(uint16_t maxWait) +{ + if (moduleQueried.headingOfMotion == false) + getPVT(); + moduleQueried.headingOfMotion = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (headingOfMotion); +} + +//Get the positional dillution of precision * 10^-2 +uint16_t SFE_UBLOX_GPS::getPDOP(uint16_t maxWait) +{ + if (moduleQueried.pDOP == false) + getPVT(); + moduleQueried.pDOP = false; //Since we are about to give this to user, mark this data as stale + moduleQueried.all = false; + + return (pDOP); +} + +//Get the current protocol version of the Ublox module we're communicating with +//This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) +uint8_t SFE_UBLOX_GPS::getProtocolVersionHigh(uint16_t maxWait) +{ + if (moduleQueried.versionNumber == false) + getProtocolVersion(); + moduleQueried.versionNumber = false; + return (versionHigh); +} + +//Get the current protocol version of the Ublox module we're communicating with +//This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) +uint8_t SFE_UBLOX_GPS::getProtocolVersionLow(uint16_t maxWait) +{ + if (moduleQueried.versionNumber == false) + getProtocolVersion(); + moduleQueried.versionNumber = false; + return (versionLow); +} + +//Get the current protocol version of the Ublox module we're communicating with +//This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) +boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) +{ + //Send packet with only CLS and ID, length of zero. This will cause the module to respond with the contents of that CLS/ID. + packetCfg.cls = UBX_CLASS_MON; + packetCfg.id = UBX_MON_VER; + + //We will send the command repeatedly, increasing the startingSpot as we go + //Then we look at each extension field of 30 bytes + for (uint8_t extensionNumber = 0; extensionNumber < 10; extensionNumber++) + { + packetCfg.len = 0; + packetCfg.startingSpot = 40 + (30 * extensionNumber); + + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + if (_printDebug == true) + { + _debugSerial->print("Extension "); + _debugSerial->print(extensionNumber); + _debugSerial->print(": "); + for (int location = 0; location < MAX_PAYLOAD_SIZE; location++) + { + if (payloadCfg[location] == '\0') + break; + _debugSerial->write(payloadCfg[location]); + } + _debugSerial->println(); + } + + //Now we need to find "PROTVER=18.00" in the incoming byte stream + if (payloadCfg[0] == 'P' && payloadCfg[6] == 'R') + { + versionHigh = (payloadCfg[8] - '0') * 10 + (payloadCfg[9] - '0'); //Convert '18' to 18 + versionLow = (payloadCfg[11] - '0') * 10 + (payloadCfg[12] - '0'); //Convert '00' to 00 + return (versionLow); + } + } + + moduleQueried.versionNumber = true; //Mark this data as new + + return (true); +} + +//Relative Positioning Information in NED frame +//Returns true if commands was successful +boolean SFE_UBLOX_GPS::getRELPOSNED(uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_RELPOSNED; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(packetCfg, maxWait) == false) + return (false); //If command send fails then bail + + //We got a response, now parse the bits + + uint16_t refStationID = extractInt(2); + Serial.print("refStationID: "); + Serial.println(refStationID); + + int32_t tempRelPos; + + tempRelPos = extractLong(8); + relPosInfo.relPosN = tempRelPos / 100.0; //Convert cm to m + + tempRelPos = extractLong(12); + relPosInfo.relPosE = tempRelPos / 100.0; //Convert cm to m + + tempRelPos = extractLong(16); + relPosInfo.relPosD = tempRelPos / 100.0; //Convert cm to m + + relPosInfo.relPosLength = extractLong(20); + relPosInfo.relPosHeading = extractLong(24); + + relPosInfo.relPosHPN = payloadCfg[32]; + relPosInfo.relPosHPE = payloadCfg[33]; + relPosInfo.relPosHPD = payloadCfg[34]; + relPosInfo.relPosHPLength = payloadCfg[35]; + + uint32_t tempAcc; + + tempAcc = extractLong(24); + relPosInfo.accN = tempAcc / 10000.0; //Convert 0.1 mm to m + + tempAcc = extractLong(28); + relPosInfo.accE = tempAcc / 10000.0; //Convert 0.1 mm to m + + tempAcc = extractLong(32); + relPosInfo.accD = tempAcc / 10000.0; //Convert 0.1 mm to m + + uint8_t flags = payloadCfg[36]; + + relPosInfo.gnssFixOk = flags & (1 << 0); + relPosInfo.diffSoln = flags & (1 << 1); + relPosInfo.relPosValid = flags & (1 << 2); + relPosInfo.carrSoln = (flags & (0b11 << 3)) >> 3; + relPosInfo.isMoving = flags & (1 << 5); + relPosInfo.refPosMiss = flags & (1 << 6); + relPosInfo.refObsMiss = flags & (1 << 7); + + return (true); +} \ No newline at end of file diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h new file mode 100644 index 0000000..2ca0cd0 --- /dev/null +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -0,0 +1,438 @@ +/* + This is a library written for the Ublox NEO-M8P-2 + SparkFun sells these at its website: www.sparkfun.com + Do you like this library? Help support SparkFun. Buy a board! + https://www.sparkfun.com/products/14586 + + Written by Nathan Seidle @ SparkFun Electronics, September 6th, 2018 + + The NEO-M8P-2 is a powerful GPS receiver capable of calculating correction data + to achieve 2cm accuracy. + + This library handles the configuration of 'survey-in', RTCM messages, and to output + the RTCM messages to the user's selected stream + + https://github.com/sparkfun/SparkFun_RTK_Arduino_Library + + Development environment specifics: + Arduino IDE 1.8.5 + + Modified by David Mann @ Loggerhead Instruments, 16 April 2019 + - Added support for parsing date and time + - Added functions getYear(), getMonth(), getDay(), getHour(), getMinute(), getSecond() + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SPARKFUN_UBLOX_ARDUINO_LIBRARY_H +#define SPARKFUN_UBLOX_ARDUINO_LIBRARY_H + +#if (ARDUINO >= 100) +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include + +//Platform specific configurations + +//Define the size of the I2C buffer based on the platform the user has +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + +//I2C_BUFFER_LENGTH is defined in Wire.H +#define I2C_BUFFER_LENGTH BUFFER_LENGTH + +#elif defined(__SAMD21G18A__) + +//SAMD21 uses RingBuffer.h +#define I2C_BUFFER_LENGTH SERIAL_BUFFER_SIZE + +//#elif __MK20DX256__ +//Teensy + +#endif + +#ifndef I2C_BUFFER_LENGTH + +//The catch-all default is 32 +#define I2C_BUFFER_LENGTH 32 + +#endif +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Registers +const uint8_t UBX_SYNCH_1 = 0xB5; +const uint8_t UBX_SYNCH_2 = 0x62; + +const uint8_t UBX_CLASS_NAV = 0x01; +const uint8_t UBX_CLASS_RXM = 0x02; +const uint8_t UBX_CLASS_INF = 0x04; +const uint8_t UBX_CLASS_ACK = 0x05; +const uint8_t UBX_CLASS_CFG = 0x06; +const uint8_t UBX_CLASS_UPD = 0x09; +const uint8_t UBX_CLASS_MON = 0x0A; +const uint8_t UBX_CLASS_AID = 0x0B; +const uint8_t UBX_CLASS_TIM = 0x0D; +const uint8_t UBX_CLASS_ESF = 0x10; +const uint8_t UBX_CLASS_MGA = 0x13; +const uint8_t UBX_CLASS_LOG = 0x21; +const uint8_t UBX_CLASS_SEC = 0x27; +const uint8_t UBX_CLASS_HNR = 0x28; + +const uint8_t UBX_CFG_PRT = 0x00; //Used to configure port specifics +const uint8_t UBX_CFG_RST = 0x04; //Used to reset device +const uint8_t UBX_CFG_RATE = 0x08; //Used to set port baud rates +const uint8_t UBX_CFG_CFG = 0x09; //Used to save current configuration +const uint8_t UBX_CFG_VALSET = 0x8A; //Used for config of higher version Ublox modules (ie protocol v27 and above) +const uint8_t UBX_CFG_VALGET = 0x8B; //Used for config of higher version Ublox modules (ie protocol v27 and above) +const uint8_t UBX_CFG_VALDEL = 0x8C; //Used for config of higher version Ublox modules (ie protocol v27 and above) + +const uint8_t UBX_CFG_TMODE3 = 0x71; //Used to enable Survey In Mode +const uint8_t SVIN_MODE_DISABLE = 0x00; +const uint8_t SVIN_MODE_ENABLE = 0x01; + +const uint8_t UBX_NAV_PVT = 0x07; //All the things! Position, velocity, time, PDOP, height, h/v accuracies, number of satellites +const uint8_t UBX_NAV_HPPOSECEF = 0x13; //Find our positional accuracy (high precision) +const uint8_t UBX_NAV_HPPOSLLH = 0x14; //Used for obtaining lat/long/alt in high precision +const uint8_t UBX_NAV_SVIN = 0x3B; //Used for checking Survey In status +const uint8_t UBX_NAV_RELPOSNED = 0x3C; //Relative Positioning Information in NED frame +const uint8_t UBX_NAV_POSECEF = 0x01; + +const uint8_t UBX_MON_VER = 0x04; //Used for obtaining Protocol Version +const uint8_t UBX_MON_TXBUF = 0x08; //Used for query tx buffer size/state + +//The following are used to enable RTCM messages +const uint8_t UBX_CFG_MSG = 0x01; +const uint8_t UBX_RTCM_MSB = 0xF5; //All RTCM enable commands have 0xF5 as MSB +const uint8_t UBX_RTCM_1005 = 0x05; //Stationary RTK reference ARP +const uint8_t UBX_RTCM_1074 = 0x4A; //GPS MSM4 +const uint8_t UBX_RTCM_1077 = 0x4D; //GPS MSM7 +const uint8_t UBX_RTCM_1084 = 0x54; //GLONASS MSM4 +const uint8_t UBX_RTCM_1087 = 0x57; //GLONASS MSM7 +const uint8_t UBX_RTCM_1094 = 0x5E; //Galileo MSM4 +const uint8_t UBX_RTCM_1124 = 0x7C; //BeiDou MSM4 +const uint8_t UBX_RTCM_1230 = 0xE6; //GLONASS code-phase biases, set to once every 10 seconds + +const uint8_t UBX_ACK_NACK = 0x00; +const uint8_t UBX_ACK_ACK = 0x01; + +//The following consts are used to configure the various ports and streams for those ports. See -CFG-PRT. +const uint8_t COM_PORT_I2C = 0; +const uint8_t COM_PORT_UART1 = 1; +const uint8_t COM_PORT_UART2 = 2; +const uint8_t COM_PORT_USB = 3; +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); + +//The following consts are used to generate KEY values for the advanced protocol functions of VELGET/SET/DEL +const uint8_t VAL_SIZE_1 = 0x01; //One bit +const uint8_t VAL_SIZE_8 = 0x02; //One byte +const uint8_t VAL_SIZE_16 = 0x03; //Two bytes +const uint8_t VAL_SIZE_32 = 0x04; //Four bytes +const uint8_t VAL_SIZE_64 = 0x05; //Eight bytes + +const uint8_t VAL_LAYER_RAM = 0; +const uint8_t VAL_LAYER_BBR = 1; +const uint8_t VAL_LAYER_FLASH = 2; +const uint8_t VAL_LAYER_DEFAULT = 7; + +//Below are various Groups, IDs, and sizes for various settings +//These can be used to call getVal/setVal/delVal +const uint8_t VAL_GROUP_I2COUTPROT = 0x72; +const uint8_t VAL_GROUP_I2COUTPROT_SIZE = VAL_SIZE_1; //All fields in I2C group are currently 1 bit + +const uint8_t VAL_ID_I2COUTPROT_UBX = 0x01; +const uint8_t VAL_ID_I2COUTPROT_NMEA = 0x02; +const uint8_t VAL_ID_I2COUTPROT_RTCM3 = 0x03; + +const uint8_t VAL_GROUP_I2C = 0x51; +const uint8_t VAL_GROUP_I2C_SIZE = VAL_SIZE_8; //All fields in I2C group are currently 1 byte + +const uint8_t VAL_ID_I2C_ADDRESS = 0x01; + +#ifndef MAX_PAYLOAD_SIZE + +#define MAX_PAYLOAD_SIZE 64 //Some commands are larger than 64 bytes but this covers most + +#endif + +//-=-=-=-=- UBX binary specific variables +typedef struct +{ + uint8_t cls; + uint8_t id; + uint16_t len; //Length of the payload. Does not include cls, id, or checksum bytes + uint16_t counter; //Keeps track of number of overall bytes received. Some responses are larger than 255 bytes. + uint16_t startingSpot; //The counter value needed to go past before we begin recording into payload array + uint8_t *payload; + uint8_t checksumA; //Given to us from module. Checked against the rolling calculated A/B checksums. + uint8_t checksumB; + boolean valid; //Goes true when both checksums pass +} ubxPacket; + +class SFE_UBLOX_GPS +{ +public: + SFE_UBLOX_GPS(void); + + //By default use the default I2C address, and use Wire port + boolean begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42); //Returns true if module is detected + //serialPort needs to be perviously initialized to correct baud rate + boolean begin(Stream &serialPort); //Returns true if module is detected + + boolean isConnected(); //Returns turn if device answers on _gpsI2Caddress address + + boolean checkUblox(); //Checks module with user selected commType + boolean checkUbloxI2C(); //Method for I2C polling of data, passing any new bytes to process() + boolean checkUbloxSerial(); //Method for serial polling of data, passing any new bytes to process() + + void process(uint8_t incoming); //Processes NMEA and UBX binary sentences one byte at a time + void processUBX(uint8_t incoming, ubxPacket *incomingUBX); //Given a character, file it away into the uxb packet structure + void processRTCMframe(uint8_t incoming); //Monitor the incoming bytes for start and length bytes + void processRTCM(uint8_t incoming) __attribute__((weak)); //Given rtcm byte, do something with it. User can overwrite if desired to pipe bytes to radio, internet, etc. + + void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags + void processNMEA(char incoming) __attribute__((weak)); //Given a NMEA character, do something with it. User can overwrite if desired to use something like tinyGPS or MicroNMEA libraries + + void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages + boolean sendCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); //Given a packet and payload, send everything including CRC bytes, return true if we got a response + boolean sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); + void sendSerialCommand(ubxPacket outgoingUBX); + + void printPacket(ubxPacket *packet); //Useful for debugging + + void factoryReset(); //Send factory reset sequence (i.e. load "default" configuration and perform hardReset) + void hardReset(); //Perform a reset leading to a cold start (zero info start-up) + + boolean setI2CAddress(uint8_t deviceAddress, uint16_t maxTime = 250); //Changes the I2C address of the Ublox module + void setSerialRate(uint32_t baudrate, uint8_t uartPort = COM_PORT_UART1, uint16_t maxTime = 250); //Changes the serial baud rate of the Ublox module, uartPort should be COM_PORT_UART1/2 + void setNMEAOutputPort(Stream &nmeaOutputPort); //Sets the internal variable for the port to direct NMEA characters to + + boolean setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = 250); //Set the number of nav solutions sent per second + uint8_t getNavigationFrequency(uint16_t maxWait = 250); //Get the number of nav solutions sent per second currently being output by module + boolean saveConfiguration(uint16_t maxWait = 250); //Save current configuration to flash and BBR (battery backed RAM) + boolean factoryDefault(uint16_t maxWait = 250); //Reset module to factory defaults + + boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until and ack is received + + boolean setAutoPVT(boolean enabled, uint16_t maxWait = 250); //Enable/disable automatic PVT reports at the navigation frequency + virtual boolean getPVT(uint16_t maxWait = 1000); //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. Retruns true if new PVT is available. + + int32_t getLatitude(uint16_t maxWait = 250); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. + int32_t getLongitude(uint16_t maxWait = 250); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. + int32_t getAltitude(uint16_t maxWait = 250); //Returns the current altitude in mm above ellipsoid + int32_t getAltitudeMSL(uint16_t maxWait = 250); //Returns the current altitude in mm above mean sea level + uint8_t getSIV(uint16_t maxWait = 250); //Returns number of sats used in fix + uint8_t getFixType(uint16_t maxWait = 250); //Returns the type of fix: 0=no, 3=3D, 4=GNSS+Deadreckoning + uint8_t getCarrierSolutionType(uint16_t maxWait = 250); //Returns RTK solution: 0=no, 1=float solution, 2=fixed solution + int32_t getGroundSpeed(uint16_t maxWait = 250); //Returns speed in mm/s + int32_t getHeading(uint16_t maxWait = 250); //Returns heading in degrees * 10^-7 + uint16_t getPDOP(uint16_t maxWait = 250); //Returns positional dillution of precision * 10^-2 + uint16_t getYear(uint16_t maxWait = 250); + uint8_t getMonth(uint16_t maxWait = 250); + uint8_t getDay(uint16_t maxWait = 250); + uint8_t getHour(uint16_t maxWait = 250); + uint8_t getMinute(uint16_t maxWait = 250); + uint8_t getSecond(uint16_t maxWait = 250); + + //Port configurations + boolean setPortOutput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = 250); //Configure a given port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setPortInput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = 250); //Configure a given port to input UBX, NMEA, RTCM3 or a combination thereof + boolean getPortSettings(uint8_t portID, uint16_t maxWait = 250); //Returns the current protocol bits in the UBX-CFG-PRT command for a given port + + boolean setI2COutput(uint8_t comSettings, uint16_t maxWait = 250); //Configure I2C port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setUART1Output(uint8_t comSettings, uint16_t maxWait = 250); //Configure UART1 port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setUART2Output(uint8_t comSettings, uint16_t maxWait = 250); //Configure UART2 port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setUSBOutput(uint8_t comSettings, uint16_t maxWait = 250); //Configure USB port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setSPIOutput(uint8_t comSettings, uint16_t maxWait = 250); //Configure SPI port to output UBX, NMEA, RTCM3 or a combination thereof + + //General configuration (used only on protocol v27 and higher - ie, ZED-F9P) + uint8_t getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_BBR, uint16_t maxWait = 250); //Returns the value at a given group/id/size location + uint8_t getVal8(uint32_t keyID, uint8_t layer = VAL_LAYER_BBR, uint16_t maxWait = 250); //Returns the value at a given group/id/size location + uint8_t setVal(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_BBR, uint16_t maxWait = 250); //Returns the value at a given group/id/size location + + //Functions used for RTK and base station setup + boolean getSurveyMode(uint16_t maxWait = 250); //Get the current TimeMode3 settings + boolean setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); //Control survey in mode + boolean enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); //Begin Survey-In for NEO-M8P + boolean disableSurveyMode(uint16_t maxWait = 250); //Stop Survey-In mode + + boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status and sets the global variables + boolean enableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint8_t secondsBetweenMessages, uint16_t maxWait = 250); //Given a message number turns on a message ID for output over given PortID + boolean disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait = 250); //Turn off given RTCM message from a given port + + uint32_t getPositionAccuracy(uint16_t maxWait = 500); //Returns the 3D accuracy of the current high-precision fix, in mm. Supported on NEO-M8P, ZED-F9P, + uint32_t getPositionAccuracyNormal(uint16_t maxWait = 500); + + uint8_t getProtocolVersionHigh(uint16_t maxWait = 1000); //Returns the PROTVER XX.00 from UBX-MON-VER register + uint8_t getProtocolVersionLow(uint16_t maxWait = 1000); //Returns the PROTVER 00.XX from UBX-MON-VER register + boolean getProtocolVersion(uint16_t maxWait = 1000); //Queries module, loads low/high bytes + + boolean getRELPOSNED(uint16_t maxWait = 1000); //Get Relative Positioning Information of the NED frame + + void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages + void disableDebugging(void); + + //Survey-in specific controls + struct svinStructure + { + boolean active; + boolean valid; + uint16_t observationTime; + float meanAccuracy; + } svin; + + //Relative Positioning Info in NED frame specific controls + struct frelPosInfoStructure + { + uint16_t refStationID; + + float relPosN; + float relPosE; + float relPosD; + + long relPosLength; + long relPosHeading; + + int8_t relPosHPN; + int8_t relPosHPE; + int8_t relPosHPD; + int8_t relPosHPLength; + + float accN; + float accE; + float accD; + + bool gnssFixOk; + bool diffSoln; + bool relPosValid; + uint8_t carrSoln; + bool isMoving; + bool refPosMiss; + bool refObsMiss; + } relPosInfo; + + //The major datums we want to globally store + uint16_t gpsYear; + uint8_t gpsMonth; + uint8_t gpsDay; + uint8_t gpsHour; + uint8_t gpsMinute; + uint8_t gpsSecond; + + int32_t latitude; //Degrees * 10^-7 (more accurate than floats) + int32_t longitude; //Degrees * 10^-7 (more accurate than floats) + int32_t altitude; //Number of mm above ellipsoid + int32_t altitudeMSL; //Number of mm above Mean Sea Level + uint8_t SIV; //Number of satellites used in position solution + uint8_t fixType; //Tells us when we have a solution aka lock + uint8_t carrierSolution; //Tells us when we have an RTK float/fixed solution + int32_t groundSpeed; //mm/s + int32_t headingOfMotion; //degrees * 10^-5 + uint16_t pDOP; //Positional dilution of precision + uint8_t versionLow; //Loaded from getProtocolVersion(). + uint8_t versionHigh; + + uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame + +private: + //Depending on the sentence type the processor will load characters into different arrays + enum SentenceTypes + { + NONE = 0, + NMEA, + UBX, + RTCM + } currentSentence = NONE; + + //Depending on the ubx binary response class, store binary responses into different places + enum classTypes + { + CLASS_NONE = 0, + CLASS_ACK, + CLASS_NOT_AN_ACK + } ubxFrameClass = CLASS_NONE; + + enum commTypes + { + COMM_TYPE_I2C = 0, + COMM_TYPE_SERIAL, + COMM_TYPE_SPI + } commType = COMM_TYPE_I2C; //Controls which port we look to for incoming bytes + + //Functions + uint32_t extractLong(uint8_t spotToStart); //Combine four bytes from payload into long + uint16_t extractInt(uint8_t spotToStart); //Combine two bytes from payload into int + uint8_t extractByte(uint8_t spotToStart); //Get byte from payload + void addToChecksum(uint8_t incoming); //Given an incoming byte, adjust rollingChecksumA/B + + //Variables + TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware + Stream *_serialPort; //The generic connection to user's chosen Serial hardware + Stream *_nmeaOutputPort = NULL; //The user can assign an output port to print NMEA sentences if they wish + Stream *_debugSerial; //The stream to send debug messages to if enabled + + uint8_t _gpsI2Caddress = 0x42; //Default 7-bit unshifted address of the ublox 6/7/8/M8/F9 series + //This can be changed using the ublox configuration software + + boolean _printDebug = false; //Flag to print the serial commands we are sending to the Serial port for debug + + //These are pointed at from within the ubxPacket + uint8_t payloadAck[2]; + uint8_t payloadCfg[MAX_PAYLOAD_SIZE]; + + //Init the packet structures and init them with pointers to the payloadAck and payloadCfg arrays + ubxPacket packetAck = {0, 0, 0, 0, 0, payloadAck, 0, 0, false}; + ubxPacket packetCfg = {0, 0, 0, 0, 0, payloadCfg, 0, 0, false}; + + const uint8_t I2C_POLLING_WAIT_MS = 25; //Limit checking of new characters to every X ms + unsigned long lastCheck = 0; + boolean autoPVT = false; //Whether autoPVT is enabled or not + boolean commandAck = false; //This goes true after we send a command and it's ack'd + uint8_t ubxFrameCounter; + + uint8_t rollingChecksumA; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes + uint8_t rollingChecksumB; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes + + //Create bit field for staleness of each datum in PVT we want to monitor + //moduleQueried.latitude goes true each time we call getPVT() + //This reduces the number of times we have to call getPVT as this can take up to ~1s per read + //depending on update rate + struct + { + uint16_t gpsYear : 1; + uint16_t gpsMonth : 1; + uint16_t gpsDay : 1; + uint16_t gpsHour : 1; + uint16_t gpsMinute : 1; + uint16_t gpsSecond : 1; + + uint16_t all : 1; + uint16_t longitude : 1; + uint16_t latitude : 1; + uint16_t altitude : 1; + uint16_t altitudeMSL : 1; + uint16_t SIV : 1; + uint16_t fixType : 1; + uint16_t carrierSolution : 1; + uint16_t groundSpeed : 1; + uint16_t headingOfMotion : 1; + uint16_t pDOP : 1; + uint16_t versionNumber : 1; + } moduleQueried; + + uint16_t rtcmLen = 0; +}; + +#endif From 8568540f846da5b2421150438a9631c43b79e4bb Mon Sep 17 00:00:00 2001 From: Felix Jirka Date: Fri, 7 Jun 2019 19:57:44 +0200 Subject: [PATCH 2/4] This changes allow to use the SFE_UBLOX_GPS to be used as data storage and disable the implicit update of data that happened previously even with autoPVT set to true Changes: - add implicitUpdate parameter to setAutoPVT to control when the actual processing of the data happens - add assumeAutoPVT method for use-cases where the receiver can't be controlled (no UART Tx line from MCU to receiver) - change getPVT to respect autoPVTImplicitUpdate==false - remove "virtual" from getPVT as it is not required anymore after the above changes --- src/SparkFun_Ublox_Arduino_Library.cpp | 31 ++++++++++++++++++++++++-- src/SparkFun_Ublox_Arduino_Library.h | 5 ++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index f2a4b7e..4694260 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -1174,9 +1174,29 @@ uint8_t SFE_UBLOX_GPS::getNavigationFrequency(uint16_t maxWait) return (measurementRate); } +//In case no config access to the GPS is possible and PVT is send cyclically already +//set config to suitable parameters +boolean SFE_UBLOX_GPS::assumeAutoPVT(boolean enabled, boolean implicitUpdate) +{ + boolean changes = autoPVT != enabled || autoPVTImplicitUpdate != implicitUpdate; + if(changes) + { + autoPVT = enabled; + autoPVTImplicitUpdate = implicitUpdate; + } + return changes; +} + //Enable or disable automatic navigation message generation by the GPS. This changes the way getPVT //works. boolean SFE_UBLOX_GPS::setAutoPVT(boolean enable, uint16_t maxWait) +{ + return setAutoPVT(enable, true, maxWait); +} + +//Enable or disable automatic navigation message generation by the GPS. This changes the way getPVT +//works. +boolean SFE_UBLOX_GPS::setAutoPVT(boolean enable, boolean implicitUpdate, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; @@ -1187,8 +1207,10 @@ boolean SFE_UBLOX_GPS::setAutoPVT(boolean enable, uint16_t maxWait) payloadCfg[2] = enable ? 1 : 0; // rate relative to navigation freq. bool ok = sendCommand(packetCfg, maxWait); - if (ok) + if (ok){ autoPVT = enable; + autoPVTImplicitUpdate = implicitUpdate; + } moduleQueried.all = false; return ok; } @@ -1276,12 +1298,17 @@ uint8_t SFE_UBLOX_GPS::getSecond(uint16_t maxWait) //Get the latest Position/Velocity/Time solution and fill all global variables boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) { - if (autoPVT) + if (autoPVT && autoPVTImplicitUpdate) { //The GPS is automatically reporting, we just check whether we got unread data checkUblox(); return moduleQueried.all; } + else if(autoPVT && !autoPVTImplicitUpdate) + { + //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 diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 2ca0cd0..6099879 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -226,8 +226,10 @@ class SFE_UBLOX_GPS boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until and ack is received + boolean assumeAutoPVT(boolean enabled, boolean implicitUpdate = true); //In case no config access to the GPS is possible and PVT is send cyclically already boolean setAutoPVT(boolean enabled, uint16_t maxWait = 250); //Enable/disable automatic PVT reports at the navigation frequency - virtual boolean getPVT(uint16_t maxWait = 1000); //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. Retruns true if new PVT is available. + boolean setAutoPVT(boolean enabled, boolean implicitUpdate, uint16_t maxWait = 250); //Enable/disable automatic PVT 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 + boolean getPVT(uint16_t maxWait = 1000); //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. Retruns true if new PVT is available. int32_t getLatitude(uint16_t maxWait = 250); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. int32_t getLongitude(uint16_t maxWait = 250); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. @@ -399,6 +401,7 @@ class SFE_UBLOX_GPS const uint8_t I2C_POLLING_WAIT_MS = 25; //Limit checking of new characters to every X ms unsigned long lastCheck = 0; boolean autoPVT = false; //Whether autoPVT is enabled or not + boolean autoPVTImplicitUpdate = true; // Whether autoPVT is triggered by accessing stale data (=true) or by a call to checkUblox (=false) boolean commandAck = false; //This goes true after we send a command and it's ack'd uint8_t ubxFrameCounter; From f6ca7b225d885f5b4a648019cb79347302454f67 Mon Sep 17 00:00:00 2001 From: Felix Jirka Date: Fri, 7 Jun 2019 20:08:48 +0200 Subject: [PATCH 3/4] cleanups (unrelated changes & coding style) --- src/SparkFun_Ublox_Arduino_Library.cpp | 19 ++----------------- src/SparkFun_Ublox_Arduino_Library.h | 2 -- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 4694260..6dfa587 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -1207,7 +1207,8 @@ boolean SFE_UBLOX_GPS::setAutoPVT(boolean enable, boolean implicitUpdate, uint16 payloadCfg[2] = enable ? 1 : 0; // rate relative to navigation freq. bool ok = sendCommand(packetCfg, maxWait); - if (ok){ + if (ok) + { autoPVT = enable; autoPVTImplicitUpdate = implicitUpdate; } @@ -1344,22 +1345,6 @@ uint32_t SFE_UBLOX_GPS::getPositionAccuracy(uint16_t maxWait) return (tempAccuracy); } -//Get the current 3D high precision positional accuracy - a fun thing to watch -//Returns a long representing the 3D accuracy in cm -uint32_t SFE_UBLOX_GPS::getPositionAccuracyNormal(uint16_t maxWait) -{ - packetCfg.cls = UBX_CLASS_NAV; - packetCfg.id = UBX_NAV_POSECEF; - packetCfg.len = 0; - packetCfg.startingSpot = 0; - - if (sendCommand(packetCfg, maxWait) == false) - return (0); //If command send fails then bail - - uint32_t tempAccuracy = extractLong(16); //We got a response, now extract a long beginning at a given position - - return (tempAccuracy); -} //Get the current latitude in degrees //Returns a long representing the number of degrees *10^-7 int32_t SFE_UBLOX_GPS::getLatitude(uint16_t maxWait) diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 6099879..a72c7c9 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -104,7 +104,6 @@ const uint8_t UBX_NAV_HPPOSECEF = 0x13; //Find our positional accuracy (high pre const uint8_t UBX_NAV_HPPOSLLH = 0x14; //Used for obtaining lat/long/alt in high precision const uint8_t UBX_NAV_SVIN = 0x3B; //Used for checking Survey In status const uint8_t UBX_NAV_RELPOSNED = 0x3C; //Relative Positioning Information in NED frame -const uint8_t UBX_NAV_POSECEF = 0x01; const uint8_t UBX_MON_VER = 0x04; //Used for obtaining Protocol Version const uint8_t UBX_MON_TXBUF = 0x08; //Used for query tx buffer size/state @@ -275,7 +274,6 @@ class SFE_UBLOX_GPS boolean disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait = 250); //Turn off given RTCM message from a given port uint32_t getPositionAccuracy(uint16_t maxWait = 500); //Returns the 3D accuracy of the current high-precision fix, in mm. Supported on NEO-M8P, ZED-F9P, - uint32_t getPositionAccuracyNormal(uint16_t maxWait = 500); uint8_t getProtocolVersionHigh(uint16_t maxWait = 1000); //Returns the PROTVER XX.00 from UBX-MON-VER register uint8_t getProtocolVersionLow(uint16_t maxWait = 1000); //Returns the PROTVER 00.XX from UBX-MON-VER register From 8f1b20bd792122bf3b5c587861aecb31a2e7fb02 Mon Sep 17 00:00:00 2001 From: Felix Jirka Date: Fri, 5 Jul 2019 17:35:14 +0200 Subject: [PATCH 4/4] update keywords.txt add Example16_AutoPVT_ExplicitUpdate.ino -> show how to use explicit updates by calling checkUblox add Example17_AssumeAutoPVTviaUart.ino -> show how to use a Rx only UART connection to receive PVT message --- .../Example16_AutoPVT_ExplicitUpdate.ino | 98 +++++++++++++++++++ .../Example17_AssumeAutoPVTviaUart.ino | 79 +++++++++++++++ keywords.txt | 4 +- 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 examples/Example16_AutoPVT_ExplicitUpdate/Example16_AutoPVT_ExplicitUpdate.ino create mode 100644 examples/Example17_AssumeAutoPVTviaUart/Example17_AssumeAutoPVTviaUart.ino diff --git a/examples/Example16_AutoPVT_ExplicitUpdate/Example16_AutoPVT_ExplicitUpdate.ino b/examples/Example16_AutoPVT_ExplicitUpdate/Example16_AutoPVT_ExplicitUpdate.ino new file mode 100644 index 0000000..3919959 --- /dev/null +++ b/examples/Example16_AutoPVT_ExplicitUpdate/Example16_AutoPVT_ExplicitUpdate.ino @@ -0,0 +1,98 @@ +/* + Configuring the GPS to automatically send position reports over I2C, with explicit data parsing calls + By: Nathan Seidle Thorsten von Eicken and Felix Jirka + SparkFun Electronics + Date: July 1st, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the U-Blox GPS the send navigation reports automatically + and retrieving the latest one via checkUblox when available. + This eliminates the implicit update in getPVT when accessing data fields twice. + Also this reduces the memory overhead of a separate buffer while introducing a slight error by inconsistencies because of the unsynchronized updates (on a multi core system). + + This can be used over serial or over I2C, this example shows the I2C use. With serial the GPS + simply outputs the UBX_NAV_PVT packet. With I2C it queues it into its internal I2C buffer (4KB in + size?) where it can be retrieved in the next I2C poll. + + 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 GPS and a BlackBoard + 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 GPS + +#include //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.setNavigationFrequency(2); //Produce two solutions per second + myGPS.setAutoPVT(true, false); //Tell the GPS to "send" each solution and the lib not to update stale data implicitly + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +/* + Calling getPVT would return false now (compare to Example 13 where it would return true), so we just use the data provided + If you are using a threaded OS eg. FreeRTOS on an ESP32, the explicit mode of autoPVT allows you to use the data provided on both cores and inside multiple threads + The data update in background creates an inconsistent state, but that should not cause issues for most applications as they usually won't change the GPS location significantly within a 2Hz - 5Hz update rate. + Also you could oversample (10Hz - 20Hz) the data to smooth out such issues... +*/ +void loop() +{ + static uint16_t counter = 0; + + if (counter % 10 == 0) { + // update your AHRS filter here for a ~100Hz update rate + // GPS data will be quasi static but data from your IMU will be changing + } + // debug output each half second + if (counter % 500 == 0) { + Serial.println(); + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } + // call checkUblox all 50ms to capture the gps data + if (counter % 50 == 0) { + myGPS.checkUblox(); + } + delay(1); + counter++; +} diff --git a/examples/Example17_AssumeAutoPVTviaUart/Example17_AssumeAutoPVTviaUart.ino b/examples/Example17_AssumeAutoPVTviaUart/Example17_AssumeAutoPVTviaUart.ino new file mode 100644 index 0000000..ce9a74b --- /dev/null +++ b/examples/Example17_AssumeAutoPVTviaUart/Example17_AssumeAutoPVTviaUart.ino @@ -0,0 +1,79 @@ +/* + Reading lat and long via UBX binary commands using an RX-only UART + By: Nathan Seidle, Adapted from Example11 by Felix Jirka + SparkFun Electronics + Date: July 2nd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the library for serial port use with a single wire connection using the assumeAutoPVT method. + Saving your pins for other stuff :-) + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 + + Preconditions: + U-Blox module is configured to send cyclical PVT message + Hardware Connections: + Connect the U-Blox serial TX pin to Rx of Serial2 (default: GPIO16) on your ESP32 + Open the serial monitor at 115200 baud to see the output +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example 17"); + + //Use any Serial port with at least a Rx Pin connected or a receive only version of SoftwareSerial here + //Assume that the U-Blox GPS is running at 9600 baud (the default) + Serial2.begin(9600); + // no need to check return value as internal call to isConnected() will not succeed + myGPS.begin(Serial2); + + // tell lib, we are expecting the module to send PVT messages by itself to our Rx pin + // you can set second parameter to "false" if you want to control the parsing and eviction of the data (need to call checkUblox cyclically) + myGPS.assumeAutoPVT(true, true); + +} + +void loop() +{ + // if implicit updates are allowed, this will trigger parsing the incoming messages + // and be true once a PVT message has been parsed + // In case you want to use explicit updates, wrap this in a timer and call checkUblox as often as needed, not to overflow your UART buffers + if (myGPS.getPVT()) + { + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } + else { + Serial.println(F("Wait for GPS data")); + delay(500); + } +} diff --git a/keywords.txt b/keywords.txt index 458dde7..4857e32 100644 --- a/keywords.txt +++ b/keywords.txt @@ -85,7 +85,9 @@ enableDebugging KEYWORD2 disableDebugging KEYWORD2 factoryReset KEYWORD2 -setAutoPVT KEYWORD2 + +setAutoPVT KEYWORD2 +assumeAutoPVT KEYWORD2 getYear KEYWORD2 getMonth KEYWORD2