first commit

This commit is contained in:
Alejandro Guerrero 2026-09-27 17:21:39 +02:00
commit e6a42e28e9
4355 changed files with 4626605 additions and 0 deletions

View file

@ -0,0 +1,168 @@
## How to add new messages to the SparkFun u-blox GNSS Arduino Library
Based on [this issue](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues/97), here is a summary of how to add new messages to the SparkFun u-blox GNSS Arduino Library with full "auto" support (for callbacks, logging, etc.).
Looking at the issue, we see that the library is not supporting the UBX-NAV-PVAT (Navigation Position Velocity Attitude Time solution).
PVAT is a new message added in version 1.21 of the HPS (High Precision Fusion) firmware and version 33.21 of the F9 Interface Description.
This makes us wonder if more new messages have been added which should also be included?
### Step 1: Check the Interface Description for new keys
* Download the latest [interface description](https://www.u-blox.com/sites/default/files/F9-HPS-1.21_InterfaceDescription_UBX-21019746.pdf) from the [u-blox website](https://www.u-blox.com/en/product/zed-f9r-module#tab-documentation-resources)
* Open the interface description in Adobe Acrobat Reader DC (the free version)
* Do a ```File \ Save as Text...```
* Save the file in ```Text (Accessible) (*.txt)``` format
* Go make a cup of tea - this takes a while
* Open the txt file in Notepad++ or another editor which supports Regular Expressions
* The keys will have been saved as individual lines in the format: 0xnnnnnnnn space CR LF
* So all we need to do is use a regex to delete everything else
* Open Search \ Replace
* Click the Search Mode - Regular Expression button
* In the "Find what :" box enter: ```^(?!.*0x[\dabcdefABCDEF]{8}\s\r\n).*```
* Clear the "Replace with :" box
* Click "Replace All"
* You are left with just the keys - and a bunch of empty lines, some of which contain form feeds (\f)
* Delete the empty lines (\r\n) by replacing \r\n with nothing - don't panic, this takes a few seconds
* Delete the form feeds by replacing \f with nothing
* Finally replace the remaining spaces (\s) with \r\n
* Delete any spurious lines left at the start of the file. E.g. ROM and BASE and 0x118B2060. These came from the General information section
* The following line (0x10340014) is the first key from the "Configuration Reference" section
* Search for that key number and you will find it again half way through the file. This second copy came from "Configuration Defaults"
* Delete the duplicate keys from that line onwards
* Save the file
* Open it in a spreadsheet, e.g. LibreOffice Calc
* Select the "A" column and click "Sort Ascending A-Z"
* Save the file (as Text CSV)
* Use KDiff3 or another diff package to see the new additions
You can find the keys in the [keys folder](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/keys), saved as sorted text files.
There are separate files for the P, R and T interfaces, plus a combined list (also sorted in ascending order).
Comparing HPS 1.21 to HPS 1.20, we can see that the following keys have been added:
* 0x10340014 CFG-BDS-USE_GEO_PRN
* 0x10710005 CFG-I2CINPROT-SPARTN
* 0x10730005 CFG-UART1INPROT-SPARTN
* 0x10750005 CFG-UART2INPROT-SPARTN
* 0x10770005 CFG-USBINPROT-SPARTN
* 0x10790005 CFG-SPIINPROT-SPARTN
* 0x20050035 CFG-TP-DRSTR_TP1
* 0x20910605 CFG-MSGOUT-UBX_RXM_SPARTN_I2C
* 0x20910606 CFG-MSGOUT-UBX_RXM_SPARTN_UART1
* 0x20910607 CFG-MSGOUT-UBX_RXM_SPARTN_UART2
* 0x20910608 CFG-MSGOUT-UBX_RXM_SPARTN_USB
* 0x20910609 CFG-MSGOUT-UBX_RXM_SPARTN_SPI
* 0x2091062a CFG-MSGOUT-UBX_NAV_PVAT_I2C
* 0x2091062b CFG-MSGOUT-UBX_NAV_PVAT_UART1
* 0x2091062c CFG-MSGOUT-UBX_NAV_PVAT_UART2
* 0x2091062d CFG-MSGOUT-UBX_NAV_PVAT_USB
* 0x2091062e CFG-MSGOUT-UBX_NAV_PVAT_SPI
* 0x20910634 CFG-MSGOUT-UBX_SEC_SIG_I2C
* 0x20910635 CFG-MSGOUT-UBX_SEC_SIG_UART1
* 0x20910636 CFG-MSGOUT-UBX_SEC_SIG_UART2
* 0x20910637 CFG-MSGOUT-UBX_SEC_SIG_USB
* 0x20910638 CFG-MSGOUT-UBX_SEC_SIG_SPI
Interestingly, we can also see that one key has been deleted:
* 0x10530006 CFG-UART2-REMAP
From this we can confirm - as documented by u-blox in the [Release Notes](https://www.u-blox.com/sites/default/files/ZED-F9R-02B_FW1.00HPS1.21_RN_UBX-21035491_1.3.pdf) -
that HPS 1.21:
* adds support for SPARTN (Safe Position Augmentation for Real-Time Navigation) correction messages
* enables the use of BeiDou geostationary satellites (previously, this configuration item had a different name)
* enables UBX-SEC-SIG message (signal security measures) as output across the different interfaces
* enables UBX_NAV_PVAT message (navigation and altitude position) as output across the different interfaces
There are also two new dynamic models, robotic lawn mower (11) and e-scooter model (12), which we need to add to the library.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/805aab18b6656513bfee473487a437754cd3965d) for the changes.
### Step 2: Update the combined keys file
Update [u-blox_config_keys_sorted.txt](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/keys/u-blox_config_keys_sorted.txt)
to include the new keys.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/8895764f237ae494dcd0fa1ae942d487d2e1557f) for the changes.
### Step 3: Update u-blox_config_keys.h
Update [u-blox_config_keys.h](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/src/u-blox_config_keys.h) to include the new keys.
Include the descriptions as defined in the Interface Description.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/3609da15f90a7a66b41524e77c6dc3dd76cd362c) for the changes.
### Step 4: Add the new message struct to u-blox_struct.h
The next step is to add the new struct for UBX-NAV-PVAT to u-blox_struct.h.
The messages are in ascending class and ID order. So we add UBX-NAV-PVAT (0x01 0x17) after UBX-NAV-HPPOSLLH (0x01 0x14).
The names and widths of the fields are taken directly from the interface definition.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/a4ba440c6240e0974c27f40b976a5ddf0fbdb9b6) for the changes.
### Step 5: Update SparkFun_u-blox_GNSS_Arduino_Library.h
Add the new message ID: ```const uint8_t UBX_NAV_PVAT = 0x17;```
Add the new functions to provide "auto" support for UBX-NAV-PVAT: ```getNAVPVAT```, ```setAutoNAVPVAT```, ..., ```logNAVPVAT```
Add new helper functions to access the most important fields: ```getVehicleRoll```, ..., ```getMotionHeading```
Add the pointer to the struct storage: ```UBX_NAV_PVAT_t *packetUBXNAVPVAT = NULL;```
Add the private init function: ```bool initPacketUBXNAVPVAT();```
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/423a1e2ccd418dd679257edc6edeec0bd3029052) for the changes.
### Step 6: Update SparkFun_u-blox_GNSS_Arduino_Library.cpp
Now we need to update SparkFun_u-blox_GNSS_Arduino_Library.cpp:
#### Step 6.1: Update end()
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/35d225e3f1abb316eda3becb7f8e2eb04ff1d17c) for the changes.
#### Step 6.2: Update checkAutomatic()
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/b746d8e2742961ede95e2d06d5db3a3a557e571d) for the changes.
#### Step 6.3: Update getMaxPayloadSize()
#### Step 6.4: Update processUBXpacket()
Take time to double-check that you have used the correct data width, signed/unsigned and position for each field.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/8eecdd5044f810b0e2b567150ff63a17c219fe8e) for the changes.
#### Step 6.5: Update checkCallbacks()
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/b53bffaa3ae12482cfb268f23796963d0b8519c9) for the changes.
#### Step 6.6: Add the "auto" functions
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/e394ae003ad38117d150598774d0552059416473) for the changes.
#### Step 6.7: Add the helper functions (if any)
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/318e76383e96d6676bbb57294c25e665c0d4a31f) for the changes.
### Step 7: Add an example
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/06014dc95f1b9ffae4876fbacfb9390541d7c31d) for the changes.
### Step 8: Update keywords.txt
Add the new "auto" and helper functions to keywords.txt.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/4f0a0ca3c5e6420be9064b91702947c23104bd1b) for the changes.
### Step 9: Update Theory.md
Add the new message to the list of "auto" messages.
See [this commit](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/commit/57f133259245d8071c73797e4be2ff630c2720ab) for the changes.
That's all folks!

View file

@ -0,0 +1,6 @@
file(GLOB SOURCES src/*.cpp)
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS src
REQUIRES arduino
)

View file

@ -0,0 +1,20 @@
# How to Contribute
Thank you so *much* for offering to help out. We truly appreciate it.
If you'd like to contribute, start by searching through the [issues](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues) and [pull requests](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/pulls) to see whether someone else has raised a similar idea or question.
Please check the [closed issues](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues?q=is%3Aissue+is%3Aclosed)
and [closed pull requests](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/pulls?q=is%3Apr+is%3Aclosed) too - you may find that your issue or feature has already been discussed.
If you decide to add a feature to this library, please create a PR and follow these best practices:
* Change as little as possible. Do not submit a PR that changes 100 lines of whitespace. Break up into multiple PRs if necessary.
* If you've added a new feature document it with a simple example sketch. This serves both as a test of your PR and as a quick way for users to quickly learn how to use your new feature.
* If you add new functions also add them to _keywords.txt_ so that they are properly highlighted in Arduino. [Read more](https://www.arduino.cc/en/Hacking/libraryTutorial).
* **Important:** Please submit your PR using the [release_candidate branch](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/release_candidate). That way, we can merge and test your PR quickly without changing the _master_ branch
![Contributing.JPG](./img/Contributing.JPG)
## Style guide
Please read and follow the [Arduino API style guide](https://www.arduino.cc/en/Reference/APIStyleGuide). Also read and consider the [Arduino style guide](https://www.arduino.cc/en/Reference/StyleGuide).

View file

@ -0,0 +1,18 @@
### Subject of the issue
Describe your issue here. If you reference a datasheet please specify which one and in which section (ie, the protocol manual, section 5.1.2). Additionally, screenshots are easy to paste into github.
### Your workbench
* What development board or microcontroller are you using?
* What version of hardware or breakout board are you using?
* How is the breakout board wired to your microcontroller?
* How is everything being powered?
* Are there any additional details that may help us help you?
### Steps to reproduce
Tell us how to reproduce this issue. Please post stripped down example code demonstrating your issue.
### Expected behavior
Tell us what should happen
### Actual behavior
Tell us what happens instead

View file

@ -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.

View file

@ -0,0 +1,166 @@
# SparkFun u-blox Arduino GNSS Library
<table class="table table-hover table-striped table-bordered">
<tr align="center">
<td><a href="https://www.sparkfun.com/products/15136"><img src="https://cdn.sparkfun.com//assets/parts/1/3/5/1/4/15136-SparkFun_GPS-RTK2_Board_-_ZED-F9P__Qwiic_-03.jpg"></a></td>
<td><a href="https://www.sparkfun.com/products/15005"><img src="https://cdn.sparkfun.com//assets/parts/1/3/3/2/0/15005-SparkFun_GPS-RTK__Qwiic__-_NEO-M8P-2-00.jpg"></a></td>
<td><a href="https://www.sparkfun.com/products/15193"><img src="https://cdn.sparkfun.com//assets/parts/1/3/6/1/4/15193-SparkFun_GPS_Breakout_-_U.FL__ZOE-M8__Qwiic_-01.jpg"></a></td>
<td><a href="https://www.sparkfun.com/products/15210"><img src="https://cdn.sparkfun.com//assets/parts/1/3/6/4/8/15210-SparkFun_GPS_Breakout_-_Chip_Antenna__SAM-M8Q__Qwiic_-01.jpg"></a></td>
<td><a href="https://www.sparkfun.com/products/15733"><img src="https://cdn.sparkfun.com//assets/parts/1/4/3/2/2/15733-SparkFun_GPS_Breakout_-_NEO-M9N__Chip_Antenna__Qwiic_-01.jpg"></a></td>
</tr>
<tr align="center">
<td><a href="https://www.sparkfun.com/products/15136">SparkFun GPS-RTK2 - ZED-F9P (GPS-15136)</a></td>
<td><a href="https://www.sparkfun.com/products/15005">SparkFun GPS-RTK - NEO-M8P-2 (GPS-15005)</a></td>
<td><a href="https://www.sparkfun.com/products/15193">SparkFun ZOE-M8Q Breakout (GPS-15193)</a></td>
<td><a href="https://www.sparkfun.com/products/15210">SparkFun SAM-M8Q Breakout (GPS-15210)</a></td>
<td><a href="https://www.sparkfun.com/products/15733">SparkFun NEO-M9N Breakout (GPS-15733)</a></td>
</tr>
</table>
u-blox makes some incredible GNSS 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 supports configuration and control of u-blox devices over I<sup>2</sup>C (called DDC by u-blox), Serial and - as of v2.0.8 (thank you @aberridg) - SPI too! The UBX protocol is a much easier and lighterweight interface to a GNSS module. Stop polling messages and parsing NMEA data! Simply ask for the datums you need and receive an automatic callback when they arrive.
This library can be installed via the Arduino Library manager. Search for **SparkFun u-blox GNSS**.
## Automatic support for correction services like PointPerfect (u-blox), RTK2go, Emlid Caster and Skylark (Swift Navigation)
u-blox's PointPerfect GNSS augmentation service uses the secure MQTT protocol to download SPARTN format correction data, providing "3-6 cm accuracy and convergence within seconds". Please see the new [PointPerfect Client example](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/examples/ZED-F9P/Example18_PointPerfectClient) for more details.
v2.2.1 also supports L-band correction services using the new u-blox NEO-D9S correction data receiver. Please see the new [L-band Corrections example](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/examples/ZED-F9P/Example19_LBand_Corrections_with_NEO-D9S) for more details.
Other RTK NTRIP corrections services often require you to send them your location in NMEA GPGGA format. v2.2 of the library makes this easy by providing get functions and automatic callbacks
for both GPGGA and GNGGA messages. You can now instruct your module to output GPGGA (e.g.) every 10 seconds and then push it to the correction server directly from the callback. No more polling, no more parsing!
v2.2 also includes two new functions useful for correction services:
* ```setMainTalkerID``` : lets you change the NMEA Talker ID (prefix) from "GN" to "GP" - just in case your correction service really does need GPGGA, not GNGGA
* ```setHighPrecisionMode``` : adds extra decimal places in the GGA messages, increasing the resolution of latitude, longitude and altitude
Please see the new [Automatic_NMEA examples](./examples/Automatic_NMEA) for more details.
We've also added a new [NTRIP Caster Client example](./examples/ZED-F9P/Example17_NTRIPClient_With_GGA_Callback) showing how to use these new features to full effect.
## AssistNow<sup>TM</sup>
v2.1 of the library adds support for u-blox AssistNow<sup>TM</sup> Assisted GNSS (A-GNSS) which can dramatically reduce the time-to-first-fix. You can find further details in the [AssistNow Examples folder](./examples/AssistNow).
## v2 vs. v1
This library is the new and improved version of the very popular SparkFun u-blox GNSS Arduino Library. v2.0 contains some big changes and improvements:
* Seamless support for "automatic" message delivery:
* In v1.8, you could ask for the NAV PVT (Navigation Position Velocity Time) message to be delivered _automatically_, without polling. v2.0 adds automatic support for [**29 messages**](./Theory.md#auto-messages), covering the full range of: standard and High Precision position, velocity, attitude and time information; relative positioning; event capture with nanosecond time resolution; raw GNSS signal data including carrier phase; Sensor Fusion; and High Navigation Rate data.
* Don't see the message you really need? [Adding_New_Messages](./Adding_New_Messages.md) provides details on how to add "auto" support for your favourite message.
* Dynamic memory allocation with clearly-defined data storage structs for each message:
* There are no static 'global' variables to eat up your RAM. v2.0 automatically allocates memory for the automatic messages when they are enabled. You may find your total RAM use is lower with v2.0 than with v1.8.
* Each "auto" message has a clearly-defined [data storage struct](./src/u-blox_structs.h) which follows the u-blox protocol specification precisely.
* Callbacks:
* No more polling! Simply request the "auto" messages you need and receive an automatic callback when each message arrives.
* Please see the [callback examples](./examples/Callbacks) for more details.
* Built-in support for data logging:
* Want to log RXM SFRBX and RAWX data for Post-Processed Kinematics or Precise Point Positioning? You can absolutely do that! v2.0 provides built-in support for data logging, allowing you to log **any** of the "auto" messages simply and easily.
* Incoming "auto" data can be stored in a configurable ring buffer. You can then extract the data from the buffer and write it to (e.g.) SD card using your favorite SD library.
* Data is logged in u-blox UBX format which is compact and efficient. You can replay the data using [u-center](https://www.u-blox.com/en/product/u-center).
* Please see the [data logging examples](./examples/Data_Logging) for more details.
## Migrating to v2.0
Migrating to v2.0 is easy. There are two small changes all users will need to make:
* The name of the library class has changed from ```SFE_UBLOX_GPS``` to ```SFE_UBLOX_GNSS``` to reflect that the library supports all of the Global Navigation Satellite Systems:
* As a minimum, you need to change: ```SFE_UBLOX_GPS myGPS;```
* to: ```SFE_UBLOX_GNSS myGPS;```
* But we would encourage you to use ```SFE_UBLOX_GNSS myGNSS;```. You will see that all of the library examples now use ```myGNSS``` instead of ```myGPS```.
* The name of the library header and C++ files have changed too:
* Change: ```#include <SparkFun_Ublox_Arduino_Library.h>```
* to: ```#include <SparkFun_u-blox_GNSS_Arduino_Library.h>```
If you are using the Dead Reckoning Sensor Fusion or High Dynamic Rate messages, you will need to make more small changes to your code. Please see the [dead reckoning examples](./examples/Dead_Reckoning) for more details. There is more detail available in [Theory.md](./Theory.md#migrating-your-code-to-v20) if you need it.
There is a [new example](./examples/Dead_Reckoning/Example8_getNAVPVAT) showing how to read the UBX-NAV-PVAT (Position, Velocity, Attitude, Time) with a single function call. UBX-NAV-PVAT has full "auto" callback and data-logging support too!
## Memory Usage
The u-blox GNSS library has grown considerably over the years and now exceeds the available program memory on platforms like the ATmega328 (Arduino Uno).
If you want to reduce the amount of memory used by the library, you can edit the header file (_SparkFun_u-blox_GNSS_Arduino_Library.h_) and uncomment lines 60 and 63:
```
#define SFE_UBLOX_REDUCED_PROG_MEM // Uncommenting this line will delete the minor debug messages to save memory
```
```
#define SFE_UBLOX_DISABLE_AUTO_NMEA // Uncommenting this line will disable auto-NMEA support to save memory
```
**Please note:** the debug messages are automatically deleted and auto-NMEA support is automatically disabled on ARDUINO_AVR_UNO platforms. For other platforms, you will need to uncomment those lines manually.
On Windows, you will normally find _SparkFun_u-blox_GNSS_Arduino_Library.h_ in:
- Documents\Arduino\libraries\SparkFun_u-blox_GNSS_Arduino_Library\src
## SPI Support
In v2.0.8 we added support for SPI, based on a contribution by @aberridg. Thank you Andrew!
We have tested the SPI interface on as many platforms and modules as we could pull together. It works perfectly on most but not quite all combinations.
For reasons we don't understand yet, the ZED-F9P and Teensy 3.2 don't seem to get along. But Teensy 3.2 and the ZOE-M8Q do play nicely together.
If you notice a combination that does not seem to work, please raise an [issue](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues) and we will investigate.
The SPI examples have their [own folder](./examples/SPI).
Please check the module datasheets for details on what clock speeds and data rates each module supports. The maximum clock speed is typically 5.5MHz and the maximum transfer rate is typically 125kBytes/s.
## I<sup>2</sup>C Support
For I<sup>2</sup>C communication, please be sure to remove all additional pull-ups on the I<sup>2</sup>C bus. u-blox modules include internal pull-ups on the I<sup>2</sup>C lines (sometimes called DDC in their manuals). Cut all I<sup>2</sup>C pull-up jumpers and/or remove them from peripheral boards. Otherwise, various data glitches can occur. See issues [38](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/38) and [40](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40) for more information. We recommend running the I<sup>2</sup>C bus at 100kHz.
## Compatibility
v2 of the library provides support for generation 8, 9 and 10 u-blox GNSS modules. For generation 6 and 7, please see [this example (deprecated)](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/tree/master/examples/Series_6_7/Example1_GetPositionAndTime_Series_6_7).
## Contributing
If you would like to contribute to this library: please do, we truly appreciate it, but please follow [these guidelines](./CONTRIBUTING.md). Thanks!
## Repository Contents
* [**/examples**](./examples) - Example sketches for the library (.ino). Run these from the Arduino IDE.
* [**/src**](./src) - Source files for the library (.cpp, .h).
* [**keywords.txt**](./keywords.txt) - Keywords from this library that will be highlighted in the Arduino IDE.
* [**library.properties**](./library.properties) - General library properties for the Arduino package manager.
* [**CONTRIBUTING.md**](./CONTRIBUTING.md) - Guidelines on how to contribute to this library.
* [**Theory.md**](./Theory.md) - provides detail on how data is processed by the library.
* [**/Utils**](./Utils) - contains a Python utility which can check the contents of UBX log files.
## Documentation
* [**Installing an Arduino Library Guide**](https://learn.sparkfun.com/tutorials/installing-an-arduino-library) - Basic information on how to install an Arduino library.
## Theory
If you would like to learn more about how this library works, including the big changes we made in version 2.0, please see [**Theory.md**](./Theory.md) for full details.
## Products That Use This Library
* [GPS-16481](https://www.sparkfun.com/products/16481) - SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic)
* [GPS-15136](https://www.sparkfun.com/products/15136) - SparkFun GPS-RTK2 Board - ZED-F9P (Qwiic)
* [GPS-16344](https://www.sparkfun.com/products/16344) - SparkFun GPS-RTK Dead Reckoning Breakout - ZED-F9R (Qwiic)
* [GPS-15005](https://www.sparkfun.com/products/15005) - SparkFun GPS-RTK Board - NEO-M8P-2 (Qwiic)
* [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)
* [GPS-17285](https://www.sparkfun.com/products/17285) - SparkFun GPS Breakout - NEO-M9N, SMA (Qwiic)
* [GPS-15733](https://www.sparkfun.com/products/15733) - SparkFun GPS Breakout - NEO-M9N, Chip Antenna (Qwiic)
* [GPS-15712](https://www.sparkfun.com/products/15712) - SparkFun GPS Breakout - NEO-M9N, U.FL (Qwiic)
* [GPS-16329](https://www.sparkfun.com/products/16329) - SparkFun GPS Dead Reckoning Breakout - NEO-M8U (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.

View file

@ -0,0 +1,132 @@
## How I<sup>2</sup>C (aka DDC) communication works with a u-blox module
When the user calls one of the methods the library will poll the u-blox module for new data.
* Wait for a minimum of 25 ms between polls (configured dynamically when update rate is set)
* Write 0xFD to module
* Read two bytes (0xFD and 0xFE) for bytes available
* Otherwise, read number of bytes and process into NMEA, UBX, or RTCM frame.
* If checksum is valid, flag frame as complete.
This library was originally written to use the I<sup>2</sup>C interface but Serial has been implemented as well.
## How data is processed by this library
In Version 1 of this library, we tried to minimize memory usage by being very careful about how much RAM we allocated to UBX packet storage and processing. We used only three buffers or containers to store the incoming data: **packetBuf** (packetBuffer); **packetCfg** (packetConfiguration); and **packetAck** (packetAcknowledge). Incoming packets were stored in **packetBuf** initially and then diverted into **packetAck** or **packetCfg** as necessary. Once data was received and validated, it would be copied out of **packetCfg** and into 'global' variables with names like ```gpsSecond``` or ```latitude```. We also introduced the concept of _Polling vs. Auto-Reporting_ where messages like PVT (Position, Velocity, Time) could be generated and parsed "automatically". This meant that functions like ```getLatitude``` could be non-blocking, returning the most recent data and requesting fresh data when necessary. But it also meant that _polled_ messages could be _overwritten_ (in **packetCfg**) by any _auto-reported_ messages. The library dealt with this successfully, but it was a headache.
Version 1 had two main drawbacks. As time went on:
- the RAM use increased as we had to add new 'global' storage for each new data type
- the number of messages which needed "auto" processing through **packetCfg** became complex, requiring significant code changes each time a new "auto" message was added. (We started with NAV-PVT. Then came NAV-HPPOSLLH and NAV-DOP. Things got complicated when HNR-ATT, HNR-INS and HNR-PVT were added to the mix.)
Version 2 of the library does things differently. Whilst of course trying to keep the library backward-compatible as much as possible, we have taken a fresh approach:
- We have added **packetAuto** which is used to temporarily buffer expected auto-reported messages and prevents data from being overwritten in **packetCfg**.
- The payload for **packetAuto** is allocated dynamically in RAM and deleted after use.
- If insufficient RAM is available, the code falls back to using **packetCfg** to buffer the data instead.
- The library no longer uses 'global' (permanently-allocated) storage for the GNSS data. Instead:
- Each message type has a **typedef struct** defined which matches the format of the UBX message. (_typedef structs_ are just definitions, they don't occupy memory.) You can find the definitions in [_**u-blox_structs.h**_](./src/u-blox_structs.h).
- The struct allows each data field (latitude, longitude, etc.) to be read simply and easily using dot notation. Flags etc. are supported by bit definitions in the struct. The field names are as defined in the u-blox interface description.
- Storage for that message is only _allocated_ in RAM if/when required. The allocation is done using _new_ via a pointer to the struct.
- _Any_ message can be "auto" if required, but can be polled too.
- An optional _callback_ can be associated with the arrival of each message type. A simple scheduler ```checkCallbacks``` triggers the callbacks once I<sup>2</sup>C/Serial data reception is complete.
- This means that your code no longer needs to wait for the arrival of a message, you are able to request (e.g.) PVT or HNR data and your callback is called once the data arrives.
- The callbacks are not re-entrant.
- The callback receives a _copy_ of the data, so data reception and processing can continue while the callback is executing. Data integrity is preserved. You can call ```checkUblox()``` from inside a callback if needed.
- Incoming data can be copied to a separate buffer to allow automatic writing to a file on SD card, which will be useful for (e.g.) RAWX logging.
- Data is stored in a RingBuffer, the size of which can be set by calling ```setFileBufferSize``` _before_ ```.begin```.
- The default buffer size is zero - to save memory.
- To simplify SD card writing, data can be copied from the RingBuffer to a user-defined linear buffer first using ```extractFileBufferData```.
- Data reception and processing can continue during the SD write.
- User-defined code does the actual writing of data from the linear buffer to the SD card. The u-blox GNSS library itself does not perform the writing and so is not tied to any particular SD library.
- The logged files can be played back and analyzed with (e.g.) u-center or RTKLIB.
In terms of RAM, you may find that your total RAM use is lower using v2 compared to v1, but it does of course depend on how many message types are being processed. The downside to this is that it is difficult to know in advance how much RAM is required, since it is only allocated if/when required. If the processor runs out of RAM (i.e. the _new_ fails) then a debug error message is generated.
## "Auto" messages
In v2.0, the full list of messages which can be processed and logged automatically is:
- UBX-NAV-POSECEF (0x01 0x01): Position solution in ECEF
- UBX-NAV-STATUS (0x01 0x03): Receiver navigation status
- UBX-NAV-DOP (0x01 0x04): Dilution of precision
- UBX-NAV-ATT (0x01 0x05): Attitude solution (**only with ADR or UDR products**)
- UBX-NAV-PVT (0x01 0x07): Navigation position velocity time solution
- UBX-NAV-ODO (0x01 0x09): Odometer solution
- UBX-NAV-VELECEF (0x01 0x11): Velocity solution in ECEF
- UBX-NAV-VELNED (0x01 0x12): Velocity solution in NED frame
- UBX-NAV-HPPOSECEF (0x01 0x13): High precision position solution in ECEF
- UBX-NAV-HPPOSLLH (0x01 0x14): High precision geodetic position solution
- UBX-NAV-PVAT (0x01 0x17): Navigation position velocity attitude time solution (**only with ADR or UDR products**)
- UBX-NAV-TIMEUTC (0x01 0x21): UTC time solution
- UBX-NAV-CLOCK (0x01 0x22): Clock solution
- UBX-NAV-SAT (0x01 0x35): Satellite information
- UBX-NAV-SVIN (0x01 0x3B): Survey-in data (**only with High Precision GNSS products**)
- UBX-NAV-RELPOSNED (0x01 0x3C): Relative positioning information in NED frame (**only with High Precision GNSS products**)
- UBX-NAV-AOPSTATUS (0x01 0x60): AssistNow Autonomous status
- UBX-NAV-EOE (0x01 0x61): End of epoch
- UBX-RXM-SFRBX (0x02 0x13): Broadcast navigation data subframe
- UBX-RXM-RAWX (0x02 0x15): Multi-GNSS raw measurement data (**only with ADR or High Precision GNSS or Time Sync products**)
- UBX-TIM-TM2 (0x0D 0x03): Time mark data
- UBX-ESF-ALG (0x10 0x14): IMU alignment information (**only with ADR or UDR products**)
- UBX-ESF-INS (0x10 0x15): Vehicle dynamics information (**only with ADR or UDR products**)
- UBX-ESF-MEAS (0x10 0x02): External sensor fusion measurements (**only with ADR or UDR products**)
- UBX-ESF-RAW (0x10 0x03): Raw sensor measurements (**only with ADR or UDR products**)
- UBX-ESF-STATUS (0x10 0x10): External sensor fusion status (**only with ADR or UDR products**)
- UBX-HNR-PVT (0x28 0x00): High rate output of PVT solution (**only with ADR or UDR products**)
- UBX-HNR-ATT (0x28 0x01): Attitude solution (**only with ADR or UDR products**)
- UBX-HNR-INS (0x28 0x02): Vehicle dynamics information (**only with ADR or UDR products**)
Please see [Adding_New_Messages](./Adding_New_Messages.md) for details on how to add "auto" support for new messages.
Notes:
- UBX-NAV-POSLLH is not supported as UBX-NAV-PVT contains the same information
## Migrating your code to v2.0
Migrating to v2.0 is easy. There are two small changes all users will need to make:
* The name of the library class has changed from ```SFE_UBLOX_GPS``` to ```SFE_UBLOX_GNSS``` to reflect that the library supports all of the Global Navigation Satellite Systems:
* As a minimum, you need to change: ```SFE_UBLOX_GPS myGPS;```
* to: ```SFE_UBLOX_GNSS myGPS;```
* But we would encourage you to use ```SFE_UBLOX_GNSS myGNSS;```. You will see that all of the library examples now use ```myGNSS``` instead of ```myGPS```.
* The name of the library header and C++ files have changed too:
* Change: ```#include <SparkFun_Ublox_Arduino_Library.h>```
* to: ```#include <SparkFun_u-blox_GNSS_Arduino_Library.h>```
The biggest change in v2.0 is that data is now stored in a _struct_ which matches the u-blox interface description for that message. For example:
- In v1, the NAV PVT (Position Velocity Time) latitude and longitude were stored in 'global' _int32_t_ variables called ```latitude``` and ```longitude```
- In v2.0, the data is now stored in <strong>UBX_NAV_PVT_t *packetUBXNAVPVT</strong>
- ```myGPS.latitude``` becomes ```myGNSS.packetUBXNAVPVT->data.lat```
- ```myGPS.longitude``` becomes ```myGNSS.packetUBXNAVPVT->data.lon```
- The helper functions ```myGNSS.getLatitude()``` and ```myGNSS.getLongitude()``` are still available and work in the same way.
- In v1, the ESF Sensor Fusion data for the Dead Reckoning modules was stored in 'global' variables ```imuMeas```, ```ubloxSen``` and ```vehAtt```
- In v2.0, the data is now stored in:
- <strong>UBX_ESF_ALG_t *packetUBXESFALG</strong> contains the IMU alignment information (roll, pitch and yaw)
- <strong>UBX_ESF_INS_t *packetUBXESFINS</strong> contains the vehicle dynamics information (acceleration and angular rate)
- <strong>UBX_ESF_MEAS_t *packetUBXESFMEAS</strong> contains the sensor fusion measurements
- <strong>UBX_ESF_RAW_t *packetUBXESFRAW</strong> contains the raw sensor measurements
- <strong>UBX_ESF_STATUS_t *packetUBXESFSTATUS</strong> contains the sensor fusion status
- e.g. ```myGPS.imuMeas.fusionMode``` becomes ```myGNSS.packetUBXESFSTATUS->data.fusionMode```
- The helper functions ```getSensorFusionMeasurement```, ```getRawSensorMeasurement``` and ```getSensorFusionStatus``` can be used to extract the sensor data for an individual sensor
- "auto" data can be marked as stale by calling (e.g.) ```myGNSS.flushESFALG()```
- Please see the [**Dead_Reckoning/Example4_vehicleDynamics**](./examples/Dead_Reckoning/Example4_vehicleDynamics/Example4_vehicleDynamics.ino) example for more details
- In v1, the HNR (High Navigation Rate) data for the Dead Reckoning modules was stored in 'global' variables ```hnrAtt```, ```hnrVehDyn``` and ```hnrPVT```
- In v2.0, e.g.:
- ```myGPS.hnrAtt.roll``` becomes ```myGNSS.packetUBXHNRATT->data.roll```
- ```myGPS.hnrVehDyn.xAccel``` becomes ```myGNSS.packetUBXHNRINS->data.xAccel```
- ```myGPS.hnrPVT.lat``` becomes ```myGNSS.packetUBXHNRPVT->data.lat```
- "auto" data can be marked as stale by calling (e.g.) ```myGNSS.flushHNRATT()```
- Please see the [**Dead_Reckoning/Example6_getAutoHNRData**](./examples/Dead_Reckoning/Example6_getAutoHNRData/Example6_getAutoHNRData.ino) example for more details
Other changes include:
- In v1, NAV_RELPOSNED relPosN, relPosE and relPosD were returned as (float)m. In v2.0 they are returned via <strong>packetUBXNAVRELPOSNED->data.relPosN</strong> (etc.) as (int32_t)cm.
- New helper functions (```getRelPosN```, ```getRelPosE``` and ```getRelPosD```) provide backward-compatibility
- Please see the [**ZED-F9P/Example5_RelativePositioningInformation**](./examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino) example for more details
- In v1, NAV_RELPOSNED accN, accE and accD were returned as (float)m. In v2.0 they are returned via <strong>packetUBXNAVRELPOSNED->data.accN</strong> (etc.) as (uint32_t)mm*0.1.
- New helper functions (```getRelPosAccN```, ```getRelPosAccE``` and ```getRelPosAccD```) provide backward-compatibility
- Please see the [**ZED-F9P/Example5_RelativePositioningInformation**](./examples/ZED-F9P/Example5_RelativePositioningInformation/Example5_RelativePositioningInformation.ino) example for more details
- getSurveyStatus now returns data via <strong>UBX_NAV_SVIN_t *packetUBXNAVSVIN</strong>
- ```myGPS.svin.active``` becomes ```myGNSS.packetUBXNAVSVIN->data.active```
- ```myGPS.svin.valid``` becomes ```myGNSS.packetUBXNAVSVIN->data.valid```
- ```myGPS.svin.observationTime``` becomes ```myGNSS.packetUBXNAVSVIN->data.dur``` and is now uint32_t (not uint16_t)
- ```myGPS.svin.MeanAccuracy``` becomes ```myGNSS.packetUBXNAVSVIN->data.meanAcc``` and is now uint32_t * 0.1mm (not float * m)
- New helper functions (```getSurveyInActive```, ```getSurveyInValid```, ```getSurveyInObservationTime``` and ```getSurveyInMeanAccuracy```) provide backward-compatibility
- Please see the [**ZED-F9P/Example3_StartRTCMBase**](./examples/ZED-F9P/Example3_StartRTCMBase/Example3_StartRTCMBase.ino) example for more details

View file

@ -0,0 +1,464 @@
# Checks the integrity of u-blox binary files
# Written by: Paul Clark
# Last update: October 17th 2021
# Reads a UBX file and checks the integrity of both UBX and NMEA data
# Will rewind and re-sync if an error is found
# Will create a repaired file if desired
# SparkFun code, firmware, and software is released under the MIT License (http://opensource.org/licenses/MIT)
#
# The MIT License (MIT)
#
# Copyright (c) 2020 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.
import sys
import os
# Add byte to checksums sum1 and sum2
def csum(byte, sum1, sum2):
sum1 = sum1 + byte
sum2 = sum2 + sum1
sum1 = sum1 & 0xFF
sum2 = sum2 & 0xFF
return sum1,sum2
print('UBX Integrity Checker')
print()
filename = ''
if filename == '':
# Check if the bin filename was passed in argv
if len(sys.argv) > 1: filename = sys.argv[1]
# Find first .ubx file in the current directory
firstfile = ''
for root, dirs, files in os.walk("."):
if len(files) > 0:
if root == ".": # Comment this line to check sub-directories too
for afile in files:
if afile[-4:] == '.ubx':
if firstfile == '': firstfile = os.path.join(root, afile)
# Ask user for .bin filename offering firstfile as the default
if filename == '': filename = input('Enter the UBX filename (default: ' + firstfile + '): ') # Get the filename
if filename == '': filename = firstfile
# Ask user if the data contains NMEA messages
response = input('Could this file contain any NMEA messages? (Y/n): ') # Get the response
if (response == '') or (response == 'Y') or (response == 'y'):
containsNMEA = True
else:
containsNMEA = False
# Ask user if the file should be repaired
response = input('Do you want to repair the file? (y/N): ') # Get the response
if (response == '') or (response == 'N') or (response == 'n'):
repairFile = False
else:
repairFile = True
if (filename[-4] == '.'):
repairFilename = filename[:-4] + '.repair' + filename[-4:]
else:
repairFilename = filename + '.repair'
print()
print('Processing',filename)
print()
filesize = os.path.getsize(filename) # Record the file size
# Try to open file for reading
try:
fi = open(filename,"rb")
except:
raise Exception('Invalid file!')
# Try to open repair file for writing
if (repairFile):
try:
fo = open(repairFilename,"wb")
except:
raise Exception('Could not open repair file!')
processed = -1 # The number of bytes processed
messages = {} # The collected message types
keepGoing = True
# Sync 'state machine'
looking_for_B5_dollar = 0 # Looking for either a UBX 0xB5 or an NMEA '$'
looking_for_62 = 1 # Looking for a UBX 0x62 header byte
looking_for_class = 2 # Looking for UBX class byte
looking_for_ID = 3 # Looking for UBX ID byte
looking_for_length_LSB = 4 # Looking for UBX length bytes
looking_for_length_MSB = 5
processing_payload = 6 # Processing the payload. Keep going until length bytes have been processed
looking_for_checksum_A = 7 # Looking for UBX checksum bytes
looking_for_checksum_B = 8
sync_lost = 9 # Go into this state if sync is lost (bad checksum etc.)
looking_for_asterix = 10 # Looking for NMEA '*'
looking_for_csum1 = 11 # Looking for NMEA checksum bytes
looking_for_csum2 = 12
looking_for_term1 = 13 # Looking for NMEA terminating bytes (CR and LF)
looking_for_term2 = 14
ubx_nmea_state = sync_lost # Initialize the state machine
# Storage for UBX messages
ubx_length = 0
ubx_class = 0
ubx_ID = 0
ubx_checksum_A = 0
ubx_checksum_B = 0
ubx_expected_checksum_A = 0
ubx_expected_checksum_B = 0
longest_UBX = 0 # The length of the longest UBX message
longest_UBX_candidate = 0 # Candidate for the length of the longest valid UBX message
# Storage for NMEA messages
nmea_length = 0
nmea_char_1 = 0 # e.g. G
nmea_char_2 = 0 # e.g. P
nmea_char_3 = 0 # e.g. G
nmea_char_4 = 0 # e.g. G
nmea_char_5 = 0 # e.g. A
nmea_csum = 0
nmea_csum1 = 0
nmea_csum2 = 0
nmea_expected_csum1 = 0
nmea_expected_csum2 = 0
longest_NMEA = 0 # The length of the longest valid NMEA message
max_nmea_len = 128 # Maximum length for an NMEA message: use this to detect if we have lost sync while receiving an NMEA message
sync_lost_at = -1 # Record where we lost sync
rewind_to = -1 # Keep a note of where we should rewind to if sync is lost
rewind_attempts = 0 # Keep a note of how many rewinds have been attempted
max_rewinds = 100 # Abort after this many rewinds
rewind_in_progress = False # Flag to indicate if a rewind is in progress
resyncs = 0 # Record the number of successful resyncs
resync_in_progress = False # Flag to indicate if a resync is in progress
message_start_byte = 0 # Record where the latest message started (for resync reporting)
rewind_repair_file_to = 0 # Keep a note of where to rewind the repair file to if sync is lost
repaired_file_bytes = 0 # Keep a note of how many bytes have been written to the repair file
try:
while keepGoing:
# Read one byte from the file
fileBytes = fi.read(1)
if (len(fileBytes) == 0):
print('ERROR: Read zero bytes. End of file?! Or zero file size?!')
raise Exception('End of file?! Or zero file size?!')
c = fileBytes[0]
processed = processed + 1 # Keep a record of how many bytes have been read and processed
# Write the byte to the repair file if desired
if (repairFile):
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + 1
# Process data bytes according to ubx_nmea_state
# For UBX messages:
# Sync Char 1: 0xB5
# Sync Char 2: 0x62
# Class byte
# ID byte
# Length: two bytes, little endian
# Payload: length bytes
# Checksum: two bytes
# For NMEA messages:
# Starts with a '$'
# The next five characters indicate the message type (stored in nmea_char_1 to nmea_char_5)
# Message fields are comma-separated
# Followed by an '*'
# Then a two character checksum (the logical exclusive-OR of all characters between the $ and the * as ASCII hex)
# Ends with CR LF
# Only allow a new file to be opened when a complete packet has been processed and ubx_nmea_state has returned to "looking_for_B5_dollar"
# Or when a data error is detected (sync_lost)
# RXM_RAWX is class 0x02 ID 0x15
# RXM_SFRBF is class 0x02 ID 0x13
# TIM_TM2 is class 0x0d ID 0x03
# NAV_POSLLH is class 0x01 ID 0x02
# NAV_PVT is class 0x01 ID 0x07
# NAV-STATUS is class 0x01 ID 0x03
if (ubx_nmea_state == looking_for_B5_dollar) or (ubx_nmea_state == sync_lost):
if (c == 0xB5): # Have we found Sync Char 1 (0xB5) if we were expecting one?
if (ubx_nmea_state == sync_lost):
print("UBX Sync Char 1 (0xB5) found at byte "+str(processed)+". Checking for Sync Char 2")
ubx_nmea_state = looking_for_62 # Now look for Sync Char 2 (0x62)
message_start_byte = processed # Record the message start byte for resync reporting
elif (c == 0x24) and (containsNMEA == True): # Have we found an NMEA '$' if we were expecting one?
if (ubx_nmea_state == sync_lost):
print("NMEA $ found at byte "+str(processed)+". Attempting to process the message")
ubx_nmea_state = looking_for_asterix # Now keep going until we receive an asterix
nmea_length = 0 # Reset nmea_length then use it to check for excessive message length
nmea_csum = 0 # Reset the nmea_csum. Update it as each character arrives
nmea_char_1 = 0x30 # Reset the first five NMEA chars to something invalid
nmea_char_2 = 0x30
nmea_char_3 = 0x30
nmea_char_4 = 0x30
nmea_char_5 = 0x30
message_start_byte = processed # Record the message start byte for resync reporting
else:
#print("Was expecting Sync Char 0xB5 or an NMEA $ but did not receive one!")
if (c == 0x24):
print("Warning: * found at byte "+str(processed)+"! Are you sure this file does not contain NMEA messages?")
sync_lost_at = processed
ubx_nmea_state = sync_lost
elif (ubx_nmea_state == looking_for_62):
if (c == 0x62): # Have we found Sync Char 2 (0x62) when we were expecting one?
ubx_expected_checksum_A = 0 # Reset the expected checksum
ubx_expected_checksum_B = 0
ubx_nmea_state = looking_for_class # Now look for Class byte
else:
print("Panic!! Was expecting Sync Char 2 (0x62) but did not receive one!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
elif (ubx_nmea_state == looking_for_class):
ubx_class = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_nmea_state = looking_for_ID # Now look for ID byte
elif (ubx_nmea_state == looking_for_ID):
ubx_ID = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
message_type = '0x%02X 0x%02X'%(ubx_class,ubx_ID) # Record the message type
ubx_nmea_state = looking_for_length_LSB # Now look for length LSB
elif (ubx_nmea_state == looking_for_length_LSB):
ubx_length = c # Store the length LSB
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_nmea_state = looking_for_length_MSB # Now look for length MSB
elif (ubx_nmea_state == looking_for_length_MSB):
ubx_length = ubx_length + (c * 256) # Add the length MSB
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
longest_UBX_candidate = ubx_length + 8 # Update the longest UBX message length candidate. Include the header, class, ID, length and checksum bytes
rewind_to = processed # If we lose sync due to dropped bytes then rewind to here
ubx_nmea_state = processing_payload # Now look for payload bytes (length: ubx_length)
elif (ubx_nmea_state == processing_payload):
ubx_length = ubx_length - 1 # Decrement length by one
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
if (ubx_length == 0):
ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits
ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff
ubx_nmea_state = looking_for_checksum_A # If we have received length payload bytes, look for checksum bytes
elif (ubx_nmea_state == looking_for_checksum_A):
ubx_checksum_A = c
ubx_nmea_state = looking_for_checksum_B
elif (ubx_nmea_state == looking_for_checksum_B):
ubx_checksum_B = c
ubx_nmea_state = looking_for_B5_dollar # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error
if ((ubx_expected_checksum_A != ubx_checksum_A) or (ubx_expected_checksum_B != ubx_checksum_B)):
print("Panic!! UBX checksum error!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync.")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Valid UBX message was received. Check if we have seen this message type before
if message_type in messages:
messages[message_type] += 1 # if we have, increment its count
else:
messages[message_type] = 1 # if we have not, set its count to 1
if (longest_UBX_candidate > longest_UBX): # Update the longest UBX message length
longest_UBX = longest_UBX_candidate
rewind_in_progress = False # Clear rewind_in_progress
rewind_to = -1
if (resync_in_progress == True): # Check if we are resyncing
resync_in_progress = False # Clear the flag now that a valid message has been received
resyncs += 1 # Increment the number of successful resyncs
print("Sync successfully re-established at byte "+str(processed)+". The UBX message started at byte "+str(message_start_byte))
print()
if (repairFile):
fo.seek(rewind_repair_file_to) # Rewind the repaired file
repaired_file_bytes = rewind_repair_file_to
fi.seek(message_start_byte) # Copy the valid message into the repair file
repaired_bytes_to_write = processed - message_start_byte
fileBytes = fi.read(repaired_bytes_to_write)
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write
else:
if (repairFile):
rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost
# NMEA messages
elif (ubx_nmea_state == looking_for_asterix):
nmea_length = nmea_length + 1 # Increase the message length count
if (nmea_length > max_nmea_len): # If the length is greater than max_nmea_len, something bad must have happened (sync_lost)
print("Panic!! Excessive NMEA message length!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
continue
# If this is one of the first five characters, store it
if (nmea_length <= 5):
if (nmea_length == 1):
nmea_char_1 = c
rewind_to = processed # If we lose sync due to dropped bytes then rewind to here
elif (nmea_length == 2):
nmea_char_2 = c
elif (nmea_length == 3):
nmea_char_3 = c
elif (nmea_length == 4):
nmea_char_4 = c
else: # ubx_length == 5
nmea_char_5 = c
message_type = chr(nmea_char_1) + chr(nmea_char_2) + chr(nmea_char_3) + chr(nmea_char_4) + chr(nmea_char_5) # Record the message type
if (message_type == "PUBX,"): # Remove the comma from PUBX
message_type = "PUBX"
# Now check if this is an '*'
if (c == 0x2A):
# Asterix received
# Don't exOR it into the checksum
# Instead calculate what the expected checksum should be (nmea_csum in ASCII hex)
nmea_expected_csum1 = ((nmea_csum & 0xf0) >> 4) + 0x30 # Convert MS nibble to ASCII hex
if (nmea_expected_csum1 >= 0x3A): # : follows 9 so add 7 to convert to A-F
nmea_expected_csum1 += 7
nmea_expected_csum2 = (nmea_csum & 0x0f) + 0x30 # Convert LS nibble to ASCII hex
if (nmea_expected_csum2 >= 0x3A): # : follows 9 so add 7 to convert to A-F
nmea_expected_csum2 += 7
# Next, look for the first csum character
ubx_nmea_state = looking_for_csum1
continue # Don't include the * in the checksum
# Now update the checksum
# The checksum is the exclusive-OR of all characters between the $ and the *
nmea_csum = nmea_csum ^ c
elif (ubx_nmea_state == looking_for_csum1):
# Store the first NMEA checksum character
nmea_csum1 = c
ubx_nmea_state = looking_for_csum2
elif (ubx_nmea_state == looking_for_csum2):
# Store the second NMEA checksum character
nmea_csum2 = c
# Now check if the checksum is correct
if ((nmea_csum1 != nmea_expected_csum1) or (nmea_csum2 != nmea_expected_csum2)):
# The checksum does not match so sync_lost
print("Panic!! NMEA checksum error!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Checksum was valid so wait for the terminators
ubx_nmea_state = looking_for_term1
elif (ubx_nmea_state == looking_for_term1):
# Check if this is CR
if (c != 0x0D):
print("Panic!! NMEA CR not found!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
ubx_nmea_state = looking_for_term2
elif (ubx_nmea_state == looking_for_term2):
# Check if this is LF
if (c != 0x0A):
print("Panic!! NMEA LF not found!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Valid NMEA message was received. Check if we have seen this message type before
if message_type in messages:
messages[message_type] += 1 # if we have, increment its count
else:
messages[message_type] = 1 # if we have not, set its count to 1
if (nmea_length > longest_NMEA): # Update the longest NMEA message length
longest_NMEA = nmea_length
# LF was received so go back to looking for B5 or a $
ubx_nmea_state = looking_for_B5_dollar
rewind_in_progress = False # Clear rewind_in_progress
rewind_to = -1
if (resync_in_progress == True): # Check if we are resyncing
resync_in_progress = False # Clear the flag now that a valid message has been received
resyncs += 1 # Increment the number of successful resyncs
print("Sync successfully re-established at byte "+str(processed)+". The NMEA message started at byte "+str(message_start_byte))
print()
if (repairFile):
fo.seek(rewind_repair_file_to) # Rewind the repaired file
repaired_file_bytes = rewind_repair_file_to
fi.seek(message_start_byte) # Copy the valid message into the repair file
repaired_bytes_to_write = processed - message_start_byte
fileBytes = fi.read(repaired_bytes_to_write)
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write
else:
if (repairFile):
rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost
# Check if the end of the file has been reached
if (processed >= filesize - 1): keepGoing = False
# Check if we should attempt to rewind
# Don't rewind if we have not yet seen a valid message
# Don't rewind if a rewind is already in progress
if (ubx_nmea_state == sync_lost) and (len(messages) > 0) and (rewind_in_progress == False) and (rewind_to >= 0):
rewind_attempts += 1 # Increment the number of rewind attempts
if (rewind_attempts > max_rewinds): # Only rewind up to max_rewind times
print("Panic! Maximum rewind attempts reached! Aborting...")
keepGoing = False
else:
print("Sync has been lost. Currently processing byte "+str(processed)+". Rewinding to byte "+str(rewind_to))
fi.seek(rewind_to) # Rewind the file
processed = rewind_to - 1 # Rewind processed too! (-1 is needed as processed is incremented at the start of the loop)
rewind_in_progress = True # Flag that a rewind is in progress
finally:
fi.close() # Close the file
if (repairFile):
fo.close()
# Print the file statistics
print()
processed += 1
print('Processed',processed,'bytes')
print('File size was',filesize)
if (processed != filesize):
print('FILE SIZE MISMATCH!!')
print('Longest valid UBX message was %i bytes'%longest_UBX)
if (containsNMEA == True):
print('Longest valid NMEA message was %i characters'%longest_NMEA)
if len(messages) > 0:
print('Message types and totals were:')
for key in messages.keys():
print('Message type:',key,' Total:',messages[key])
if (resyncs > 0):
print('Number of successful resyncs:',resyncs)
print()
if (repairFile):
print('Repaired data written to:', repairFilename)
print()
print('Bye!')

View file

@ -0,0 +1,515 @@
# Aligns the rcvTow in RAWX messages in u-blox UBX binary files to the nearest decimalPlaces seconds
# Written by: Paul Clark
# Last update: August 17th 2022
# SparkFun code, firmware, and software is released under the MIT License (http://opensource.org/licenses/MIT)
#
# The MIT License (MIT)
#
# Copyright (c) 2022 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.
import sys
import os
import struct
# Add byte to checksums sum1 and sum2
def csum(byte, sum1, sum2):
sum1 = sum1 + byte
sum2 = sum2 + sum1
sum1 = sum1 & 0xFF
sum2 = sum2 & 0xFF
return sum1,sum2
print('UBX RAWX Aligner')
print()
filename = ''
if filename == '':
# Check if the bin filename was passed in argv
if len(sys.argv) > 1: filename = sys.argv[1]
# Find first .ubx file in the current directory
firstfile = ''
for root, dirs, files in os.walk("."):
if len(files) > 0:
if root == ".": # Comment this line to check sub-directories too
for afile in files:
if afile[-4:] == '.ubx':
if firstfile == '': firstfile = os.path.join(root, afile)
# Ask user for .bin filename offering firstfile as the default
if filename == '': filename = input('Enter the UBX filename (default: ' + firstfile + '): ') # Get the filename
if filename == '': filename = firstfile
# Ask user if the data contains NMEA messages
response = input('Could this file contain any NMEA messages? (Y/n): ') # Get the response
if (response == '') or (response == 'Y') or (response == 'y'):
containsNMEA = True
else:
containsNMEA = False
repairFile = True
if (filename[-4] == '.'):
repairFilename = filename[:-4] + '.aligned' + filename[-4:]
else:
repairFilename = filename + '.aligned'
decimalPlaces = 0 # Default to whole seconds
if len(sys.argv) > 2: decimalPlaces = sys.argv[2]
print()
print('Processing',filename)
print()
filesize = os.path.getsize(filename) # Record the file size
# Try to open file for reading
try:
fi = open(filename,"rb")
except:
raise Exception('Invalid file!')
# Try to open repair file for write and read
if (repairFile):
try:
fo = open(repairFilename,"w+b")
except:
raise Exception('Could not open aligned file!')
processed = -1 # The number of bytes processed
messages = {} # The collected message types
keepGoing = True
# Sync 'state machine'
looking_for_B5_dollar = 0 # Looking for either a UBX 0xB5 or an NMEA '$'
looking_for_62 = 1 # Looking for a UBX 0x62 header byte
looking_for_class = 2 # Looking for UBX class byte
looking_for_ID = 3 # Looking for UBX ID byte
looking_for_length_LSB = 4 # Looking for UBX length bytes
looking_for_length_MSB = 5
processing_payload = 6 # Processing the payload. Keep going until length bytes have been processed
looking_for_checksum_A = 7 # Looking for UBX checksum bytes
looking_for_checksum_B = 8
sync_lost = 9 # Go into this state if sync is lost (bad checksum etc.)
looking_for_asterix = 10 # Looking for NMEA '*'
looking_for_csum1 = 11 # Looking for NMEA checksum bytes
looking_for_csum2 = 12
looking_for_term1 = 13 # Looking for NMEA terminating bytes (CR and LF)
looking_for_term2 = 14
ubx_nmea_state = sync_lost # Initialize the state machine
# Storage for UBX messages
ubx_length = 0
ubx_length_LSB = 0
ubx_length_MSB = 0
ubx_class = 0
ubx_ID = 0
ubx_checksum_A = 0
ubx_checksum_B = 0
ubx_expected_checksum_A = 0
ubx_expected_checksum_B = 0
longest_UBX = 0 # The length of the longest UBX message
longest_UBX_candidate = 0 # Candidate for the length of the longest valid UBX message
# Storage for NMEA messages
nmea_length = 0
nmea_char_1 = 0 # e.g. G
nmea_char_2 = 0 # e.g. P
nmea_char_3 = 0 # e.g. G
nmea_char_4 = 0 # e.g. G
nmea_char_5 = 0 # e.g. A
nmea_csum = 0
nmea_csum1 = 0
nmea_csum2 = 0
nmea_expected_csum1 = 0
nmea_expected_csum2 = 0
longest_NMEA = 0 # The length of the longest valid NMEA message
max_nmea_len = 128 # Maximum length for an NMEA message: use this to detect if we have lost sync while receiving an NMEA message
sync_lost_at = -1 # Record where we lost sync
rewind_to = -1 # Keep a note of where we should rewind to if sync is lost
rewind_attempts = 0 # Keep a note of how many rewinds have been attempted
max_rewinds = 100 # Abort after this many rewinds
rewind_in_progress = False # Flag to indicate if a rewind is in progress
resyncs = 0 # Record the number of successful resyncs
resync_in_progress = False # Flag to indicate if a resync is in progress
message_start_byte = 0 # Record where the latest message started (for resync reporting)
rewind_repair_file_to = 0 # Keep a note of where to rewind the repair file to if sync is lost
repaired_file_bytes = 0 # Keep a note of how many bytes have been written to the repair file
repair_file_rawx_payload_start = 0 # Keep a note of where the RAWX payload starts (i.e. where the rcvTow R8 starts)
largest_rawx_alignment = 0.0 # Keep note of the largest alignment change
try:
while keepGoing:
# Read one byte from the file
fileBytes = fi.read(1)
if (len(fileBytes) == 0):
print('ERROR: Read zero bytes. End of file?! Or zero file size?!')
raise Exception('End of file?! Or zero file size?!')
c = fileBytes[0]
processed = processed + 1 # Keep a record of how many bytes have been read and processed
# Write the byte to the repair file if desired
if (repairFile):
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + 1
# Process data bytes according to ubx_nmea_state
# For UBX messages:
# Sync Char 1: 0xB5
# Sync Char 2: 0x62
# Class byte
# ID byte
# Length: two bytes, little endian
# Payload: length bytes
# Checksum: two bytes
# For NMEA messages:
# Starts with a '$'
# The next five characters indicate the message type (stored in nmea_char_1 to nmea_char_5)
# Message fields are comma-separated
# Followed by an '*'
# Then a two character checksum (the logical exclusive-OR of all characters between the $ and the * as ASCII hex)
# Ends with CR LF
# Only allow a new file to be opened when a complete packet has been processed and ubx_nmea_state has returned to "looking_for_B5_dollar"
# Or when a data error is detected (sync_lost)
# RXM_RAWX is class 0x02 ID 0x15
# RXM_SFRBF is class 0x02 ID 0x13
# TIM_TM2 is class 0x0d ID 0x03
# NAV_POSLLH is class 0x01 ID 0x02
# NAV_PVT is class 0x01 ID 0x07
# NAV-STATUS is class 0x01 ID 0x03
if (ubx_nmea_state == looking_for_B5_dollar) or (ubx_nmea_state == sync_lost):
if (c == 0xB5): # Have we found Sync Char 1 (0xB5) if we were expecting one?
if (ubx_nmea_state == sync_lost):
print("UBX Sync Char 1 (0xB5) found at byte "+str(processed)+". Checking for Sync Char 2")
ubx_nmea_state = looking_for_62 # Now look for Sync Char 2 (0x62)
message_start_byte = processed # Record the message start byte for resync reporting
elif (c == 0x24) and (containsNMEA == True): # Have we found an NMEA '$' if we were expecting one?
if (ubx_nmea_state == sync_lost):
print("NMEA $ found at byte "+str(processed)+". Attempting to process the message")
ubx_nmea_state = looking_for_asterix # Now keep going until we receive an asterix
nmea_length = 0 # Reset nmea_length then use it to check for excessive message length
nmea_csum = 0 # Reset the nmea_csum. Update it as each character arrives
nmea_char_1 = 0x30 # Reset the first five NMEA chars to something invalid
nmea_char_2 = 0x30
nmea_char_3 = 0x30
nmea_char_4 = 0x30
nmea_char_5 = 0x30
message_start_byte = processed # Record the message start byte for resync reporting
else:
#print("Was expecting Sync Char 0xB5 or an NMEA $ but did not receive one!")
if (c == 0x24):
print("Warning: * found at byte "+str(processed)+"! Are you sure this file does not contain NMEA messages?")
sync_lost_at = processed
ubx_nmea_state = sync_lost
elif (ubx_nmea_state == looking_for_62):
if (c == 0x62): # Have we found Sync Char 2 (0x62) when we were expecting one?
ubx_expected_checksum_A = 0 # Reset the expected checksum
ubx_expected_checksum_B = 0
ubx_nmea_state = looking_for_class # Now look for Class byte
else:
print("Panic!! Was expecting Sync Char 2 (0x62) but did not receive one!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
elif (ubx_nmea_state == looking_for_class):
ubx_class = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_nmea_state = looking_for_ID # Now look for ID byte
elif (ubx_nmea_state == looking_for_ID):
ubx_ID = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
message_type = '0x%02X 0x%02X'%(ubx_class,ubx_ID) # Record the message type
ubx_nmea_state = looking_for_length_LSB # Now look for length LSB
elif (ubx_nmea_state == looking_for_length_LSB):
ubx_length = c # Store the length LSB
ubx_length_LSB = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_nmea_state = looking_for_length_MSB # Now look for length MSB
elif (ubx_nmea_state == looking_for_length_MSB):
ubx_length = ubx_length + (c * 256) # Add the length MSB
ubx_length_MSB = c
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
longest_UBX_candidate = ubx_length + 8 # Update the longest UBX message length candidate. Include the header, class, ID, length and checksum bytes
rewind_to = processed # If we lose sync due to dropped bytes then rewind to here
ubx_nmea_state = processing_payload # Now look for payload bytes (length: ubx_length)
if (message_type == '0x02 0x15'): # Is this RAWX? If so, record the start of the payload
repair_file_rawx_payload_start = repaired_file_bytes
elif (ubx_nmea_state == processing_payload):
ubx_length = ubx_length - 1 # Decrement length by one
ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
if (ubx_length == 0):
ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits
ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff
ubx_nmea_state = looking_for_checksum_A # If we have received length payload bytes, look for checksum bytes
elif (ubx_nmea_state == looking_for_checksum_A):
ubx_checksum_A = c
ubx_nmea_state = looking_for_checksum_B
elif (ubx_nmea_state == looking_for_checksum_B):
ubx_checksum_B = c
ubx_nmea_state = looking_for_B5_dollar # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error
if ((ubx_expected_checksum_A != ubx_checksum_A) or (ubx_expected_checksum_B != ubx_checksum_B)):
print("Panic!! UBX checksum error!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync.")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Valid UBX message was received. Check if we have seen this message type before
if message_type in messages:
messages[message_type] += 1 # if we have, increment its count
else:
messages[message_type] = 1 # if we have not, set its count to 1
if (longest_UBX_candidate > longest_UBX): # Update the longest UBX message length
longest_UBX = longest_UBX_candidate
rewind_in_progress = False # Clear rewind_in_progress
rewind_to = -1
if (resync_in_progress == True): # Check if we are resyncing
resync_in_progress = False # Clear the flag now that a valid message has been received
resyncs += 1 # Increment the number of successful resyncs
print("Sync successfully re-established at byte "+str(processed)+". The UBX message started at byte "+str(message_start_byte))
print()
if (repairFile):
fo.seek(rewind_repair_file_to) # Rewind the repaired file
repaired_file_bytes = rewind_repair_file_to
fi.seek(message_start_byte) # Copy the valid message into the repair file
repaired_bytes_to_write = processed - message_start_byte
fileBytes = fi.read(repaired_bytes_to_write)
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write
# Note: alignment is skipped if resyncing is in progress
# You may need to run the code twice to align any skipped alignments
else:
if (repairFile):
rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost
if (message_type == '0x02 0x15'): # Is this RAWX? If so, do the alignment
ubx_expected_checksum_A = 0 # Reuse the expected checksum
ubx_expected_checksum_B = 0
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_class
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_ID
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_length_LSB
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_length_MSB
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
fo.seek(repair_file_rawx_payload_start) # Rewind the repair file
fileBytes = fo.read(8) # Read the rcvTow R8
rcvTow = struct.unpack('<d', fileBytes)[0] # Unpack the R8 (Little-endian)
rcvTowRounded = round(rcvTow, decimalPlaces) # Round to decimalPlaces
if (abs(rcvTow - rcvTowRounded) > largest_rawx_alignment):
largest_rawx_alignment = abs(rcvTow - rcvTowRounded) # Record the largest alignment change
fileBytes = struct.pack('<d', rcvTowRounded)
fo.seek(repair_file_rawx_payload_start) # Rewind the repair file
fo.write(fileBytes) # Write the rounded TOW to the repair file
for i in range(8): # Update the checksum
ubx_expected_checksum_A = ubx_expected_checksum_A + fileBytes[i]
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
bytesToRead = (ubx_length_MSB * 256) + ubx_length_LSB - 8
fileBytes = fo.read(bytesToRead)
for i in range(bytesToRead): # Update the checksum
ubx_expected_checksum_A = ubx_expected_checksum_A + fileBytes[i]
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits
ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff
fileBytes = struct.pack('BB', ubx_expected_checksum_A, ubx_expected_checksum_B)
fo.write(fileBytes) # Write the updated checksum
# NMEA messages
elif (ubx_nmea_state == looking_for_asterix):
nmea_length = nmea_length + 1 # Increase the message length count
if (nmea_length > max_nmea_len): # If the length is greater than max_nmea_len, something bad must have happened (sync_lost)
print("Panic!! Excessive NMEA message length!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
continue
# If this is one of the first five characters, store it
if (nmea_length <= 5):
if (nmea_length == 1):
nmea_char_1 = c
rewind_to = processed # If we lose sync due to dropped bytes then rewind to here
elif (nmea_length == 2):
nmea_char_2 = c
elif (nmea_length == 3):
nmea_char_3 = c
elif (nmea_length == 4):
nmea_char_4 = c
else: # ubx_length == 5
nmea_char_5 = c
message_type = chr(nmea_char_1) + chr(nmea_char_2) + chr(nmea_char_3) + chr(nmea_char_4) + chr(nmea_char_5) # Record the message type
if (message_type == "PUBX,"): # Remove the comma from PUBX
message_type = "PUBX"
# Now check if this is an '*'
if (c == 0x2A):
# Asterix received
# Don't exOR it into the checksum
# Instead calculate what the expected checksum should be (nmea_csum in ASCII hex)
nmea_expected_csum1 = ((nmea_csum & 0xf0) >> 4) + 0x30 # Convert MS nibble to ASCII hex
if (nmea_expected_csum1 >= 0x3A): # : follows 9 so add 7 to convert to A-F
nmea_expected_csum1 += 7
nmea_expected_csum2 = (nmea_csum & 0x0f) + 0x30 # Convert LS nibble to ASCII hex
if (nmea_expected_csum2 >= 0x3A): # : follows 9 so add 7 to convert to A-F
nmea_expected_csum2 += 7
# Next, look for the first csum character
ubx_nmea_state = looking_for_csum1
continue # Don't include the * in the checksum
# Now update the checksum
# The checksum is the exclusive-OR of all characters between the $ and the *
nmea_csum = nmea_csum ^ c
elif (ubx_nmea_state == looking_for_csum1):
# Store the first NMEA checksum character
nmea_csum1 = c
ubx_nmea_state = looking_for_csum2
elif (ubx_nmea_state == looking_for_csum2):
# Store the second NMEA checksum character
nmea_csum2 = c
# Now check if the checksum is correct
if ((nmea_csum1 != nmea_expected_csum1) or (nmea_csum2 != nmea_expected_csum2)):
# The checksum does not match so sync_lost
print("Panic!! NMEA checksum error!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Checksum was valid so wait for the terminators
ubx_nmea_state = looking_for_term1
elif (ubx_nmea_state == looking_for_term1):
# Check if this is CR
if (c != 0x0D):
print("Panic!! NMEA CR not found!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
ubx_nmea_state = looking_for_term2
elif (ubx_nmea_state == looking_for_term2):
# Check if this is LF
if (c != 0x0A):
print("Panic!! NMEA LF not found!")
print("Sync lost at byte "+str(processed)+". Attemting to re-sync")
sync_lost_at = processed
resync_in_progress = True
ubx_nmea_state = sync_lost
else:
# Valid NMEA message was received. Check if we have seen this message type before
if message_type in messages:
messages[message_type] += 1 # if we have, increment its count
else:
messages[message_type] = 1 # if we have not, set its count to 1
if (nmea_length > longest_NMEA): # Update the longest NMEA message length
longest_NMEA = nmea_length
# LF was received so go back to looking for B5 or a $
ubx_nmea_state = looking_for_B5_dollar
rewind_in_progress = False # Clear rewind_in_progress
rewind_to = -1
if (resync_in_progress == True): # Check if we are resyncing
resync_in_progress = False # Clear the flag now that a valid message has been received
resyncs += 1 # Increment the number of successful resyncs
print("Sync successfully re-established at byte "+str(processed)+". The NMEA message started at byte "+str(message_start_byte))
print()
if (repairFile):
fo.seek(rewind_repair_file_to) # Rewind the repaired file
repaired_file_bytes = rewind_repair_file_to
fi.seek(message_start_byte) # Copy the valid message into the repair file
repaired_bytes_to_write = processed - message_start_byte
fileBytes = fi.read(repaired_bytes_to_write)
fo.write(fileBytes)
repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write
else:
if (repairFile):
rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost
# Check if the end of the file has been reached
if (processed >= filesize - 1): keepGoing = False
# Check if we should attempt to rewind
# Don't rewind if we have not yet seen a valid message
# Don't rewind if a rewind is already in progress
if (ubx_nmea_state == sync_lost) and (len(messages) > 0) and (rewind_in_progress == False) and (rewind_to >= 0):
rewind_attempts += 1 # Increment the number of rewind attempts
if (rewind_attempts > max_rewinds): # Only rewind up to max_rewind times
print("Panic! Maximum rewind attempts reached! Aborting...")
keepGoing = False
else:
print("Sync has been lost. Currently processing byte "+str(processed)+". Rewinding to byte "+str(rewind_to))
fi.seek(rewind_to) # Rewind the file
processed = rewind_to - 1 # Rewind processed too! (-1 is needed as processed is incremented at the start of the loop)
rewind_in_progress = True # Flag that a rewind is in progress
finally:
fi.close() # Close the file
if (repairFile):
fo.close()
# Print the file statistics
print()
processed += 1
print('Processed',processed,'bytes')
print('File size was',filesize)
if (processed != filesize):
print('FILE SIZE MISMATCH!!')
print('Longest valid UBX message was %i bytes'%longest_UBX)
if (containsNMEA == True):
print('Longest valid NMEA message was %i characters'%longest_NMEA)
if len(messages) > 0:
print('Message types and totals were:')
for key in messages.keys():
print('Message type:',key,' Total:',messages[key])
if (resyncs > 0):
print('Number of successful resyncs:',resyncs)
print()
if (repairFile):
print('Aligned data written to:', repairFilename)
print('Largest alignment change:', largest_rawx_alignment)
if (resyncs > 0):
print('Note: alignment is skipped during resyncing')
print('You may need to run the code twice to align any skipped alignments')
print()
print('Bye!')

View file

@ -0,0 +1,188 @@
/*
Monitor AssistNow Autonomous data collection
By: SparkFun Electronics / Paul Clark
Date: November 29th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to enable and monitor AssistNow Autonomous data collection by the module.
A callback is used to monitor AssistNow Autonomous data availability for each satellite.
A second callback is used to print the AOPSTATUS status.
If your GNSS board has battery-backup for the RAM - and all SparkFun boards do! - then you can:
wait until the module has AssistNow Autonomous data for a few satellites;
power-cycle the board;
watch how fast it gets its first fix!
Note: this example will only work on boards which have plenty of RAM available.
The UBX-NAV-SAT information occupies several kBytes.
Note: this example will not work on the ZED-F9P. "The ZED-F9P supports AssistNow Online only."
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printSATdata will be called when new NAV SAT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_SAT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVSATcallback
// / _____ This _must_ be UBX_NAV_SAT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printSATdata(UBX_NAV_SAT_data_t *ubxDataStruct)
{
//Serial.println();
Serial.print(F("UBX-NAV-SAT contains data for "));
Serial.print(ubxDataStruct->header.numSvs);
if (ubxDataStruct->header.numSvs == 1)
Serial.println(F(" SV"));
else
Serial.println(F(" SVs"));
uint16_t numAopAvail = 0; // Count how many SVs have AssistNow Autonomous data available
for (uint16_t block = 0; block < ubxDataStruct->header.numSvs; block++) // For each SV
{
if (ubxDataStruct->blocks[block].flags.bits.aopAvail == 1) // If the aopAvail bit is set
numAopAvail++; // Increment the number of SVs
}
Serial.print(F("AssistNow Autonomous data is available for "));
Serial.print(numAopAvail);
if (numAopAvail == 1)
Serial.println(F(" SV"));
else
Serial.println(F(" SVs"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printAOPstatus will be called when new NAV AOPSTATUS data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_AOPSTATUS_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVAOPSTATUScallback
// / _____ This _must_ be UBX_NAV_AOPSTATUS_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printAOPstatus(UBX_NAV_AOPSTATUS_data_t *ubxDataStruct)
{
//Serial.println();
Serial.print(F("AOPSTATUS status is "));
Serial.println(ubxDataStruct->status);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
Serial.println();
long latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte fixType = ubxDataStruct->fixType; // Print the fix type
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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to see the 'major' debug messages on Serial
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable AssistNow Autonomous data collection.
if (myGNSS.setAopCfg(1) == true)
{
Serial.println(F("aopCfg enabled"));
}
else
{
Serial.println(F("Could not enable aopCfg. Please check wiring. Freezing."));
while (1);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable automatic UBX-NAV-SAT and UBX-NAV-AOPSTATUS messages and set up the callbacks
myGNSS.setNavigationFrequency(1); //Produce one solution per second
myGNSS.setAutoNAVSATcallbackPtr(&printSATdata); // Enable automatic NAV SAT messages with callback to printSATdata
myGNSS.setAutoAOPSTATUScallbackPtr(&printAOPstatus); // Enable automatic NAV AOPSTATUS messages with callback to printAOPstatus
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,207 @@
/*
Read the AssistNow Autonomous database from the module
By: SparkFun Electronics / Paul Clark
Date: November 29th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to enable, check the status of, and read the AssistNow Autonomous data from the module.
Note: this example will only work on boards which have plenty of RAM available.
The database can be several kBytes in length.
Note: this example will not work on the ZED-F9P. "The ZED-F9P supports AssistNow Online only."
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printSATdata will be called when new NAV SAT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_SAT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVSATcallback
// / _____ This _must_ be UBX_NAV_SAT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printSATdata(UBX_NAV_SAT_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("UBX-NAV-SAT contains data for "));
Serial.print(ubxDataStruct->header.numSvs);
if (ubxDataStruct->header.numSvs == 1)
Serial.println(F(" SV"));
else
Serial.println(F(" SVs"));
uint16_t numAopAvail = 0; // Count how many SVs have AssistNow Autonomous data available
for (uint16_t block = 0; block < ubxDataStruct->header.numSvs; block++) // For each SV
{
if (ubxDataStruct->blocks[block].flags.bits.aopAvail == 1) // If the aopAvail bit is set
numAopAvail++; // Increment the number of SVs
}
Serial.print(F("AssistNow Autonomous data is available for "));
Serial.print(numAopAvail);
if (numAopAvail == 1)
Serial.println(F(" SV"));
else
Serial.println(F(" SVs"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printAOPstatus will be called when new NAV AOPSTATUS data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_AOPSTATUS_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVAOPSTATUScallback
// / _____ This _must_ be UBX_NAV_AOPSTATUS_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printAOPstatus(UBX_NAV_AOPSTATUS_data_t *ubxDataStruct)
{
//Serial.println();
Serial.print(F("AOPSTATUS status is "));
Serial.println(ubxDataStruct->status);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to see helpful debug messages on Serial
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable AssistNow Autonomous data collection.
if (myGNSS.setAopCfg(1) == true)
{
Serial.println(F("aopCfg enabled"));
}
else
{
Serial.println(F("Could not enable aopCfg. Please check wiring. Freezing."));
while (1);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable automatic UBX-NAV-SAT and UBX-NAV-AOPSTATUS messages and set up the callbacks
myGNSS.setNavigationFrequency(1); //Produce one solution per second
myGNSS.setAutoNAVSATcallbackPtr(&printSATdata); // Enable automatic NAV SAT messages with callback to printSATdata
myGNSS.setAutoAOPSTATUScallbackPtr(&printAOPstatus); // Enable automatic NAV AOPSTATUS messages with callback to printAOPstatus
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Keep displaying NAV SAT and AOPSTATUS until the user presses a key
Serial.println(F("AssistNow Autonomous data collection is in progress. Press any key to quit and read the database."));
while (Serial.available()) Serial.read(); // Empty the serial buffer
while (!Serial.available()) // Wait for the arrival of a keypress
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}
Serial.println();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Disable the automatic UBX-NAV-SAT and UBX-NAV-AOPSTATUS messages
myGNSS.setAutoNAVSAT(false);
myGNSS.setAutoAOPSTATUS(false);
delay(1100);
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Read the AssistNow Autonomous database from the module and pretty-print it (so it can be copied and pasted into the next example)
#define MAX_DATABASE_LENGTH 32768 // Allocate 32kBytes to store the navigation database
size_t maxDatabaseLen = MAX_DATABASE_LENGTH;
uint8_t *database = new uint8_t[MAX_DATABASE_LENGTH]; // The database will be stored here
Serial.println(F("Storage has been allocated for the database.")); Serial.flush();
size_t actualDatabaseLen = myGNSS.readNavigationDatabase(database, maxDatabaseLen); // Read the database
Serial.print(F("The Navigation Database length was "));
Serial.println(actualDatabaseLen);
if (actualDatabaseLen == maxDatabaseLen)
Serial.println(F("There was not enough memory to store the entire database. Some data will have been lost!"));
// Pretty-print the database so it can be copied into the next example
Serial.println(F("Copy and paste the following into the next example, so you can write it back to the module:"));
Serial.println();
Serial.print(F("size_t databaseLen = "));
Serial.print(actualDatabaseLen);
Serial.println(F(";"));
Serial.print(F("const uint8_t database["));
Serial.print(actualDatabaseLen);
Serial.println(F("] = {"));
size_t i;
for(i = 0; i < actualDatabaseLen; i++)
{
if ((i % 32) == 0)
Serial.print(F(" 0x"));
if (*(database + i) < 0x10) // Print leading zero
Serial.print(F("0"));
Serial.print(*(database + i), HEX);
if (i == (actualDatabaseLen - 1))
Serial.println();
else if ((i % 32) == 31)
Serial.println(F(","));
else
Serial.print(F(", 0x"));
}
Serial.println(F("};"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Nothing to do here
}

View file

@ -0,0 +1,188 @@
/*
Write the AssistNow Autonomous database data to the module
By: SparkFun Electronics / Paul Clark
Date: December 1st, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to write the AssistNow Autonomous database date back to the module.
This example is written for the ESP32. A WiFi connection is used to get network time to pass to the module.
(You could use an RTC instead.)
Copy and paste the database data from the previous example into database.h.
Update secrets.h with your:
- WiFi credentials
Note: this example will not work on the ZED-F9P. "The ZED-F9P supports AssistNow Online only."
Note: this example works best if you have the GNSS RAM battery-backup disabled.
All SparkFun boards have battery-backup for the RAM which will means the database is retained if you disconnect the power.
The module will use the database data from the battery-backed RAM when you turn the power back on.
You will only see the improvement in the time-to-first-fix if you disable the battery first - or you are using a non-SparkFun
board that does not have the backup battery.
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include "database.h" // <- Copy and paste the database data from the previous example into database.h
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "time.h"
const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to see helpful debug messages on Serial
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Set the RTC using network time. (Code taken from the SimpleTime example.)
// Request the time from the NTP server and use it to set the ESP32's RTC.
configTime(0, 0, ntpServer); // Set the GMT and daylight offsets to zero. We need UTC, not local time.
struct tm timeinfo;
if(!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
}
else
{
Serial.println(&timeinfo, "Time is: %A, %B %d %Y %H:%M:%S");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the RTC time to the module
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
if(getLocalTime(&timeinfo))
{
// setUTCTimeAssistance uses a default time accuracy of 2 seconds which should be OK here.
// Have a look at the library source code for more details.
myGNSS.setUTCTimeAssistance(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
else
{
Serial.println("Failed to obtain time. This will not work well. The GNSS needs accurate time to start up quickly.");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow Autonomous data to the module
size_t bytesPushed = myGNSS.pushAssistNowData(database, databaseLen);
Serial.print(F("Pushed "));
Serial.print(bytesPushed);
Serial.println(F(" bytes of AssistNow Autonomous data to the module"));
if (bytesPushed != databaseLen)
Serial.println(F("Warning: bytesPushed does not match databaseLen! Maybe the database contains bad data? Or there was a communication error?"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Disconnect the WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Serial.println(F("WiFi disconnected"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,177 @@
// Paste the AssistNow Autonomous database data from the previous example here:
size_t databaseLen = 5480;
const uint8_t database[5480] = {
0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x02, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x01, 0x08, 0xC4, 0x04, 0x26, 0xFE, 0x02, 0x54, 0xBA, 0xA9,
0xFF, 0xDA, 0x8C, 0x05, 0xA3, 0xC1, 0xCD, 0x85, 0x86, 0x0A, 0x14, 0x20, 0xAA, 0xE6, 0xB4, 0xD3, 0x4F, 0x27, 0x12, 0xE0, 0xE6, 0xC3, 0xE4, 0x79, 0x0E, 0xA1, 0xD3, 0x49, 0xF4, 0xFF, 0xAF, 0xF7,
0x67, 0x2E, 0x93, 0xF9, 0x6A, 0x1B, 0x2D, 0x10, 0xD3, 0x49, 0xBD, 0xFF, 0x26, 0x01, 0x00, 0x1E, 0x81, 0x22, 0xA1, 0xE4, 0x2A, 0x00, 0x10, 0x50, 0x04, 0x00, 0xB7, 0xBF, 0xB5, 0x62, 0x13, 0x80,
0x54, 0x00, 0x01, 0x00, 0x00, 0x05, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x04, 0x1E, 0xC4, 0x44, 0x72, 0x2F, 0x02, 0x71, 0xAD, 0xA4, 0xFF, 0xE8, 0xAE, 0xFA,
0x42, 0x25, 0xD1, 0x0F, 0x0D, 0x03, 0xCB, 0x71, 0xCF, 0x12, 0x6F, 0x89, 0x0F, 0x27, 0xF3, 0x49, 0x7B, 0x29, 0x4B, 0xF1, 0x0C, 0xA1, 0xD4, 0x49, 0xF5, 0xFF, 0xB6, 0x03, 0x98, 0x36, 0xA0, 0x03,
0xBB, 0x0F, 0x72, 0x1D, 0xD4, 0x49, 0xEE, 0xFF, 0x17, 0x00, 0x00, 0xC0, 0x85, 0x22, 0x2F, 0xF0, 0x3D, 0x00, 0x3C, 0x50, 0x04, 0x00, 0x1A, 0x35, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00,
0x00, 0x07, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x06, 0x5B, 0xC4, 0x44, 0xD8, 0xEC, 0x02, 0x4E, 0x79, 0xAC, 0xFF, 0xE8, 0x8A, 0x1D, 0xBC, 0x9F, 0xE8, 0x86,
0xD9, 0x07, 0x45, 0xD0, 0x05, 0x6A, 0x05, 0x06, 0xC3, 0x26, 0xB5, 0x7D, 0x4A, 0xA2, 0x86, 0xCA, 0x0D, 0xA1, 0xD4, 0x49, 0x2B, 0x00, 0x62, 0x07, 0x63, 0x2F, 0xF5, 0x05, 0x1E, 0x1A, 0x98, 0x10,
0xD4, 0x49, 0x38, 0x00, 0xBA, 0xFF, 0x00, 0xCB, 0xBE, 0x22, 0xF4, 0x59, 0x09, 0x00, 0xB6, 0x50, 0x04, 0x00, 0x2E, 0x9B, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x09, 0x15, 0x0C,
0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x08, 0x5E, 0xC4, 0x44, 0xFA, 0x7B, 0x02, 0x4E, 0x1B, 0xA5, 0xFF, 0x03, 0x6E, 0x07, 0xF5, 0x25, 0xF3, 0xB8, 0x36, 0x01, 0xDB, 0x4F,
0x56, 0x3E, 0x8D, 0xA6, 0xD7, 0x26, 0x82, 0xFE, 0xA8, 0x4A, 0x7A, 0x44, 0x0D, 0xA1, 0xD4, 0x49, 0x05, 0x00, 0x7A, 0xFA, 0x1C, 0x37, 0xDC, 0xFA, 0x33, 0x0E, 0x7E, 0x1E, 0xD4, 0x49, 0xE5, 0xFF,
0xFC, 0xFF, 0x00, 0xB7, 0xBA, 0x22, 0x87, 0x2A, 0x34, 0x00, 0xBC, 0x50, 0x04, 0x00, 0xDF, 0x11, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x0B, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00,
0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0A, 0x2F, 0xC4, 0x44, 0xC7, 0x68, 0x00, 0xF6, 0x09, 0xAA, 0xFF, 0xED, 0xB8, 0x38, 0x24, 0x1C, 0xE8, 0xC5, 0x30, 0x00, 0xD0, 0x23, 0x17, 0xEC, 0xF7, 0x92,
0x2D, 0x27, 0x7C, 0x39, 0x2C, 0x6B, 0x66, 0xF5, 0x0D, 0xA1, 0xD4, 0x49, 0xEA, 0xFF, 0x27, 0xF9, 0x09, 0x2F, 0x2B, 0xFA, 0x6A, 0x1B, 0xFB, 0x0F, 0xD4, 0x49, 0x01, 0x00, 0x0F, 0x00, 0x00, 0xC1,
0x82, 0x22, 0x2A, 0x8A, 0x80, 0xFF, 0x5E, 0x50, 0x04, 0x00, 0x52, 0xBB, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x0D, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0x0C, 0x96, 0xC4, 0x44, 0x8E, 0xBD, 0x02, 0xE4, 0x39, 0xA6, 0xFF, 0xE8, 0x7E, 0xBE, 0x5F, 0xFE, 0xF3, 0xCA, 0xF0, 0x02, 0x5D, 0x9F, 0x84, 0x44, 0x3A, 0xC9, 0x6F, 0x27, 0xFD, 0x1B,
0xDB, 0x27, 0x60, 0x63, 0x0D, 0xA1, 0xD4, 0x49, 0x32, 0x00, 0x4B, 0xFA, 0xA6, 0x33, 0x2C, 0xFB, 0x5C, 0x10, 0xB8, 0x1C, 0xD4, 0x49, 0x0E, 0x00, 0x1E, 0x00, 0x00, 0x0D, 0xB9, 0x22, 0x3A, 0x4D,
0x07, 0x00, 0x2C, 0x51, 0x04, 0x00, 0x76, 0x2E, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x10, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0F, 0x22,
0xC4, 0x44, 0x47, 0xD2, 0x02, 0x47, 0xEA, 0xA3, 0xFF, 0xEA, 0x53, 0xBB, 0xBD, 0x37, 0xBE, 0x0A, 0x5B, 0x06, 0x85, 0x4F, 0x13, 0x98, 0x48, 0x33, 0x95, 0x27, 0xC8, 0x3B, 0xE7, 0x1B, 0x37, 0x8F,
0x0C, 0xA1, 0xD4, 0x49, 0xC9, 0xFF, 0x09, 0x0C, 0x98, 0x30, 0xB3, 0x0A, 0x16, 0x09, 0x14, 0x26, 0xD4, 0x49, 0xB3, 0xFF, 0x4E, 0x00, 0x00, 0xE0, 0x81, 0x22, 0x2D, 0xCD, 0x31, 0x00, 0x44, 0x50,
0x04, 0x00, 0x39, 0x9B, 0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x14, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x13, 0x1B, 0xC4, 0x44, 0x6A, 0xD2,
0x02, 0xE9, 0x7C, 0xA3, 0xFF, 0xEE, 0xD1, 0x59, 0xEE, 0xE7, 0x56, 0x56, 0xCC, 0x02, 0xE4, 0x2C, 0x39, 0x0E, 0xB6, 0x13, 0x56, 0x26, 0xDF, 0xE5, 0x41, 0x7C, 0xDA, 0x86, 0x0D, 0xA1, 0xD4, 0x49,
0xFE, 0xFF, 0x81, 0x04, 0x73, 0x3A, 0x0F, 0x04, 0x54, 0x10, 0x59, 0x1B, 0xD4, 0x49, 0x37, 0x00, 0xDF, 0xFF, 0x00, 0x21, 0x87, 0x22, 0x0B, 0xF8, 0x10, 0x00, 0x36, 0x50, 0x04, 0x00, 0x75, 0x76,
0xB5, 0x62, 0x13, 0x80, 0x54, 0x00, 0x01, 0x00, 0x00, 0x1D, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1C, 0x31, 0xC4, 0xC4, 0x58, 0xF9, 0x02, 0xFC, 0xD0, 0xA5,
0xFF, 0xEA, 0x04, 0x79, 0x3B, 0x17, 0xD6, 0x48, 0xEF, 0x00, 0xB9, 0x34, 0x1C, 0xC2, 0x1E, 0x93, 0x05, 0x28, 0xA0, 0x7E, 0x9D, 0x60, 0x7C, 0x63, 0x0C, 0xA1, 0xD4, 0x49, 0xD2, 0xFF, 0x51, 0xF4,
0xD6, 0x2D, 0xD9, 0xF5, 0x45, 0x08, 0x41, 0x27, 0xD4, 0x49, 0x23, 0x00, 0xFE, 0xFF, 0x00, 0x7C, 0xBC, 0x22, 0x15, 0x51, 0x31, 0x00, 0x62, 0x50, 0x04, 0x00, 0xEC, 0xE1, 0xB5, 0x62, 0x13, 0x80,
0x54, 0x00, 0x01, 0x00, 0x00, 0x1E, 0x15, 0x0C, 0x01, 0x0D, 0x3B, 0x00, 0x04, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1D, 0x22, 0xC4, 0x04, 0x53, 0x24, 0x02, 0xF4, 0x99, 0xA8, 0xFF, 0x08, 0x62, 0xE5,
0x6F, 0x9E, 0x9C, 0x48, 0xBC, 0x02, 0xB4, 0x07, 0x9B, 0x6A, 0x21, 0x82, 0x28, 0x26, 0x78, 0x77, 0x0C, 0x90, 0x2F, 0xF9, 0x0C, 0xA1, 0xD4, 0x49, 0xE5, 0xFF, 0x34, 0x08, 0x30, 0x33, 0xC0, 0x06,
0x34, 0x1B, 0x7C, 0x0E, 0xD4, 0x49, 0xF7, 0xFF, 0xEE, 0xFF, 0x00, 0x19, 0xBE, 0x22, 0x6F, 0xC0, 0x2F, 0x00, 0x44, 0x50, 0x04, 0x00, 0x93, 0x6D, 0xB5, 0x62, 0x13, 0x80, 0x38, 0x00, 0x01, 0x00,
0x06, 0x07, 0x15, 0x0C, 0x01, 0x0C, 0x21, 0x00, 0x02, 0x24, 0xE0, 0x4A, 0x01, 0x03, 0xA5, 0xB9, 0xC6, 0x04, 0x16, 0x44, 0x02, 0xC6, 0xE0, 0xD9, 0x21, 0x1C, 0xC0, 0xE0, 0xF8, 0xD5, 0x3D, 0x6E,
0x01, 0x56, 0x21, 0xC1, 0x0F, 0x39, 0xF8, 0x70, 0x2D, 0x10, 0x7F, 0xFF, 0x10, 0x38, 0xEE, 0xA5, 0x80, 0x00, 0x00, 0xE8, 0x55, 0x0C, 0x1C, 0xEB, 0xB5, 0x62, 0x13, 0x80, 0x38, 0x00, 0x01, 0x00,
0x06, 0x0F, 0x15, 0x0C, 0x01, 0x0C, 0x21, 0x00, 0x02, 0x24, 0xE0, 0x4A, 0x01, 0x03, 0xAD, 0xB9, 0xC6, 0x04, 0x6F, 0x28, 0x02, 0x17, 0x5C, 0xB7, 0xE5, 0xF7, 0x80, 0x1F, 0x46, 0x2A, 0xDE, 0xBE,
0xF0, 0x59, 0x45, 0x5C, 0xCE, 0x39, 0x75, 0x99, 0xF7, 0x10, 0x1B, 0x7B, 0xFF, 0x78, 0x18, 0x5D, 0xFE, 0x00, 0x00, 0xE8, 0x55, 0x07, 0xC8, 0x8F, 0xB5, 0x62, 0x13, 0x80, 0x38, 0x00, 0x01, 0x00,
0x06, 0x10, 0x15, 0x0C, 0x01, 0x0C, 0x21, 0x00, 0x02, 0x24, 0xE0, 0x4A, 0x01, 0x03, 0xAE, 0xB9, 0xC6, 0x84, 0x26, 0x63, 0x02, 0x0C, 0x60, 0x9D, 0xDA, 0x44, 0x5E, 0xE2, 0x3C, 0x27, 0xDD, 0xED,
0xF5, 0x3B, 0xDE, 0xC1, 0xDE, 0x39, 0xA7, 0xDD, 0xF9, 0x20, 0x11, 0x54, 0x2A, 0x80, 0xFB, 0x6C, 0x80, 0x00, 0xFF, 0xEF, 0x55, 0x06, 0x17, 0x43, 0xB5, 0x62, 0x13, 0x80, 0x38, 0x00, 0x01, 0x00,
0x06, 0x15, 0x15, 0x0C, 0x01, 0x0C, 0x21, 0x00, 0x02, 0x24, 0xE0, 0x4A, 0x01, 0x03, 0xB3, 0xB9, 0xC6, 0x04, 0x6F, 0xC3, 0x02, 0xF0, 0xC6, 0x33, 0xC1, 0x45, 0x3F, 0x28, 0xC9, 0xBB, 0xBF, 0x1B,
0x1B, 0xEC, 0x14, 0xF1, 0xF5, 0x39, 0x55, 0x25, 0x06, 0x30, 0x84, 0xAE, 0xC7, 0xA8, 0x89, 0x3D, 0x44, 0x00, 0xFD, 0xEF, 0x55, 0x0B, 0x89, 0xE0, 0xB5, 0x62, 0x13, 0x80, 0x38, 0x00, 0x01, 0x00,
0x06, 0x17, 0x15, 0x0C, 0x01, 0x0C, 0x21, 0x00, 0x02, 0x24, 0xE0, 0x4A, 0x01, 0x03, 0xB5, 0xB9, 0xC6, 0x04, 0x34, 0xBC, 0x02, 0xF5, 0xBE, 0x0D, 0x15, 0xEA, 0x21, 0xA3, 0xC8, 0xDB, 0x1E, 0x3A,
0x31, 0x5A, 0x38, 0xB6, 0x20, 0x39, 0x8B, 0x4A, 0xD9, 0x30, 0xDF, 0x66, 0xF8, 0xB8, 0x9D, 0x5B, 0x00, 0x02, 0xFF, 0xEF, 0x55, 0x0A, 0x20, 0x5A, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00,
0x00, 0x01, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x00, 0x7B, 0xB0, 0x04, 0xC5, 0xE4, 0x01, 0x73, 0x47, 0x5B, 0xAE, 0x1C, 0x18, 0x0D, 0xA1, 0x00, 0x65, 0xFD,
0x00, 0x7B, 0x41, 0x2C, 0xEA, 0x00, 0x14, 0x16, 0x24, 0x00, 0xF1, 0x02, 0x67, 0x00, 0x06, 0xEA, 0x3F, 0x8A, 0xA4, 0x7B, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x02, 0x15, 0x0C,
0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x01, 0x7B, 0xB0, 0x44, 0x66, 0x1F, 0x03, 0xC2, 0x82, 0xA8, 0x90, 0x0E, 0x1C, 0x0E, 0xA1, 0x00, 0x4E, 0xFD, 0x00, 0x7B, 0x4F, 0x99,
0xE6, 0x00, 0xF3, 0xEB, 0xC3, 0x00, 0xE8, 0x8A, 0x6E, 0x00, 0x5C, 0x05, 0x00, 0x8A, 0xDF, 0xF9, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x03, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x02, 0x7B, 0xB0, 0x04, 0x96, 0x8D, 0x01, 0xEB, 0x94, 0x1F, 0x20, 0x13, 0x6B, 0x0D, 0xA1, 0x00, 0x69, 0xFD, 0x00, 0x7B, 0xC1, 0x59, 0x14, 0x00, 0x8A, 0xAC,
0x28, 0x00, 0xDD, 0x24, 0x37, 0x00, 0xE0, 0xEF, 0x3F, 0x8A, 0x0F, 0xED, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x04, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0x03, 0x7B, 0xB0, 0x44, 0x8F, 0x51, 0x01, 0xFF, 0xCA, 0x0C, 0xA8, 0x0B, 0xD6, 0x0C, 0xA1, 0x00, 0x44, 0xFD, 0x00, 0x7B, 0x3F, 0x67, 0x40, 0x00, 0x77, 0x26, 0x80, 0x00, 0x3A, 0x7C,
0xB2, 0x00, 0x2E, 0x07, 0x00, 0x8A, 0xD8, 0xB6, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x05, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x04, 0x00,
0xB0, 0x14, 0xB8, 0x76, 0x03, 0xEA, 0xD0, 0x30, 0x92, 0x0A, 0xF1, 0x0C, 0xA1, 0x00, 0x25, 0xFD, 0xD4, 0x49, 0x71, 0xCF, 0x12, 0x00, 0x49, 0x7B, 0x29, 0x00, 0xFA, 0x42, 0x25, 0x00, 0xBE, 0xFF,
0x3F, 0x8A, 0xD6, 0xA9, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x06, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x05, 0x7B, 0xB0, 0x44, 0xBB, 0x76,
0x01, 0x15, 0x1F, 0x14, 0x47, 0x1C, 0xB5, 0x0C, 0xA1, 0x00, 0x63, 0xFD, 0x00, 0x7B, 0x16, 0xD6, 0xE9, 0x00, 0xEC, 0xC2, 0xD5, 0x00, 0xAB, 0x35, 0x77, 0x00, 0x8C, 0x18, 0x80, 0x8A, 0x81, 0xB1,
0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x07, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x06, 0x00, 0xB0, 0x14, 0x33, 0xF6, 0x03, 0x49, 0x98, 0x7D,
0xC9, 0x05, 0xCA, 0x0D, 0xA1, 0x00, 0x63, 0xFD, 0xD4, 0x49, 0xD0, 0x05, 0x6A, 0x00, 0x7D, 0x4A, 0xA2, 0x00, 0x1D, 0xBC, 0x9F, 0x00, 0x2B, 0x09, 0x00, 0x8A, 0x4B, 0xEC, 0xB5, 0x62, 0x13, 0x80,
0x34, 0x00, 0x02, 0x00, 0x00, 0x08, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x07, 0x7B, 0xB0, 0x04, 0x04, 0xE8, 0x01, 0xB3, 0x40, 0x38, 0xDD, 0x0E, 0x13, 0x0D,
0xA1, 0x00, 0x22, 0xFD, 0x00, 0x7B, 0x33, 0x39, 0xBE, 0x00, 0x60, 0x6D, 0x02, 0x00, 0xAF, 0xE0, 0xC5, 0x00, 0xCF, 0x07, 0x80, 0x8A, 0x59, 0x50, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00,
0x00, 0x09, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x08, 0x00, 0xB0, 0x14, 0x50, 0xB2, 0x03, 0x0A, 0x6B, 0x13, 0x14, 0x07, 0x44, 0x0D, 0xA1, 0x00, 0x28, 0xFD,
0xD4, 0x49, 0x4F, 0x56, 0x3E, 0x00, 0xFE, 0xA8, 0x4A, 0x00, 0x07, 0xF5, 0x25, 0x00, 0x85, 0x06, 0x00, 0x8A, 0x09, 0xB0, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0A, 0x15, 0x0C,
0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x09, 0x7B, 0xB0, 0x44, 0x94, 0x64, 0x01, 0x0C, 0xB3, 0x3B, 0x06, 0x13, 0x0D, 0x0D, 0xA1, 0x00, 0x6E, 0xFD, 0x00, 0x7B, 0x68, 0x3C,
0x14, 0x00, 0x15, 0x6E, 0x97, 0x00, 0x53, 0x99, 0x16, 0x00, 0xEF, 0xF6, 0xBF, 0x8A, 0xC1, 0xA9, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0B, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0A, 0x7B, 0xB0, 0x04, 0xF5, 0x0B, 0x01, 0xDE, 0x10, 0x03, 0x70, 0x0C, 0x97, 0x0D, 0xA1, 0xFF, 0x52, 0xFD, 0x00, 0x7B, 0x7A, 0x06, 0xEC, 0x00, 0xA3, 0x44,
0x6B, 0x00, 0xA1, 0xEB, 0xC8, 0x00, 0x11, 0xF8, 0xBF, 0x8A, 0xB4, 0x35, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0C, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0x0B, 0x7B, 0xB0, 0x04, 0xFD, 0x31, 0x01, 0x09, 0xE0, 0x44, 0xFA, 0x12, 0xD4, 0x0C, 0xA1, 0x00, 0x54, 0xFD, 0x00, 0x7B, 0xBC, 0x3F, 0x97, 0x00, 0x0C, 0x17, 0x32, 0x00, 0x5D, 0xE0,
0x30, 0x00, 0x72, 0xF7, 0xBF, 0x8A, 0x91, 0x2E, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0D, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0C, 0x00,
0xB0, 0x14, 0x82, 0xD3, 0x03, 0x6F, 0x0C, 0x2F, 0x96, 0x10, 0x63, 0x0D, 0xA1, 0x00, 0x31, 0xFD, 0xD4, 0x49, 0x9F, 0x84, 0x44, 0x00, 0x1B, 0xDB, 0x27, 0x00, 0xBE, 0x5F, 0xFE, 0x00, 0xE9, 0x08,
0x00, 0x8A, 0x4A, 0x74, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0E, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0D, 0x7B, 0xB0, 0x44, 0x3E, 0xB1,
0x01, 0x66, 0xD7, 0x09, 0x30, 0x08, 0xCD, 0x0C, 0xA1, 0x00, 0x4A, 0xFD, 0x00, 0x7B, 0x72, 0x05, 0x96, 0x00, 0x85, 0x34, 0x7E, 0x00, 0xFE, 0xFB, 0x2F, 0x00, 0xCC, 0xF7, 0xBF, 0x8A, 0x3C, 0x14,
0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x0F, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0E, 0x7B, 0xB0, 0x44, 0x92, 0xB0, 0x01, 0xBF, 0xF2, 0x71,
0x4F, 0xF7, 0x0E, 0x0D, 0xA1, 0x00, 0x26, 0xFD, 0x00, 0x7B, 0x66, 0x06, 0x3A, 0x00, 0xAE, 0x39, 0x2B, 0x00, 0xEF, 0x2D, 0x9C, 0x00, 0x95, 0x0F, 0x80, 0x8A, 0x3F, 0x35, 0xB5, 0x62, 0x13, 0x80,
0x34, 0x00, 0x02, 0x00, 0x00, 0x10, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x0F, 0x00, 0xB0, 0x14, 0x96, 0x34, 0x03, 0x8E, 0xB0, 0x65, 0xEC, 0x12, 0x8F, 0x0C,
0xA1, 0x00, 0x1F, 0xFD, 0xD4, 0x49, 0x4F, 0x13, 0x98, 0x00, 0x3B, 0xE7, 0x1B, 0x00, 0xBB, 0xBD, 0x37, 0x00, 0x39, 0xF6, 0x3F, 0x8A, 0xF3, 0x43, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00,
0x00, 0x11, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x10, 0x7B, 0xB0, 0x04, 0x5E, 0xE6, 0x01, 0xF3, 0xDF, 0x70, 0xA4, 0x18, 0x50, 0x0D, 0xA1, 0x00, 0x34, 0xFD,
0x00, 0x7B, 0x86, 0x84, 0xC1, 0x00, 0x0F, 0xBA, 0xC1, 0x00, 0xF9, 0x59, 0xC2, 0x00, 0x39, 0x0A, 0x80, 0x8A, 0x83, 0x21, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x12, 0x15, 0x0C,
0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x11, 0x7B, 0xB0, 0x04, 0x92, 0xB3, 0x01, 0xCD, 0x23, 0x11, 0x1A, 0x12, 0xC7, 0x0C, 0xA1, 0x00, 0x57, 0xFD, 0x00, 0x7B, 0x1B, 0x89,
0xEA, 0x00, 0x3E, 0x35, 0x7D, 0x00, 0x7D, 0xB8, 0x6F, 0x00, 0x2B, 0xF1, 0xBF, 0x8A, 0x1F, 0x89, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x13, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x12, 0x7B, 0xB0, 0x44, 0xDE, 0x29, 0x01, 0x15, 0x35, 0x49, 0xCA, 0x17, 0xF3, 0x0C, 0xA1, 0x00, 0x30, 0xFD, 0x00, 0x7B, 0xBE, 0x59, 0xC3, 0x00, 0xEA, 0xA7,
0x50, 0x00, 0xBF, 0x3E, 0x23, 0x00, 0x5C, 0x08, 0x80, 0x8A, 0x31, 0x50, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x14, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0x13, 0x00, 0xB0, 0x14, 0x3E, 0x72, 0x03, 0xA1, 0xC5, 0x2C, 0xFA, 0xFE, 0x86, 0x0D, 0xA1, 0x00, 0x1B, 0xFD, 0xD4, 0x49, 0x2C, 0x39, 0x0E, 0x00, 0xE5, 0x41, 0x7C, 0x00, 0x59, 0xEE,
0xE7, 0x00, 0x1F, 0xFA, 0x3F, 0x8A, 0x05, 0x19, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x15, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x14, 0x7B,
0xB0, 0x44, 0x9E, 0xFA, 0x01, 0xD6, 0x02, 0xC7, 0xCD, 0x0A, 0x8B, 0x0C, 0xA1, 0x00, 0x4C, 0xFD, 0x00, 0x7B, 0x38, 0x8D, 0xE6, 0x00, 0x0D, 0x36, 0xD5, 0x00, 0xBA, 0x5E, 0xC7, 0x00, 0xA0, 0x00,
0x80, 0x8A, 0x7F, 0x6E, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x16, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x15, 0x7B, 0xB0, 0x44, 0x9E, 0x17,
0x01, 0x24, 0x81, 0x37, 0xE8, 0xFC, 0xEA, 0x0C, 0xA1, 0x00, 0x4C, 0xFD, 0x00, 0x7B, 0xDE, 0x06, 0x10, 0x00, 0xD1, 0x69, 0xDC, 0x00, 0xC5, 0x18, 0x94, 0x00, 0x28, 0x16, 0x80, 0x8A, 0xBE, 0x20,
0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x17, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x16, 0x7B, 0xB0, 0x44, 0x2E, 0xDB, 0x01, 0x43, 0xB7, 0x0F,
0xCC, 0x0F, 0x25, 0x0D, 0xA1, 0x00, 0x68, 0xFD, 0x00, 0x7B, 0x32, 0x3D, 0x13, 0x00, 0x85, 0x6C, 0x73, 0x00, 0xE0, 0xD5, 0x4F, 0x00, 0x1B, 0xF8, 0xBF, 0x8A, 0x13, 0xA5, 0xB5, 0x62, 0x13, 0x80,
0x34, 0x00, 0x02, 0x00, 0x00, 0x18, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x17, 0x7B, 0xB0, 0x04, 0xAC, 0x23, 0x01, 0x3F, 0x2F, 0x63, 0xE2, 0xFA, 0x29, 0x0D,
0xA1, 0x00, 0x4A, 0xFD, 0x00, 0x7B, 0x90, 0xA3, 0x66, 0x00, 0x3C, 0x04, 0x20, 0x00, 0x7B, 0x4A, 0x74, 0x00, 0x1E, 0x09, 0x80, 0x8A, 0x67, 0xDD, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00,
0x00, 0x19, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x18, 0x7B, 0xB0, 0x04, 0xF3, 0x84, 0x01, 0x2A, 0xEE, 0x52, 0xCB, 0x0A, 0x9F, 0x0D, 0xA1, 0x00, 0x4E, 0xFD,
0x00, 0x7B, 0xFC, 0x28, 0x94, 0x00, 0x03, 0x3B, 0x27, 0x00, 0x5D, 0xC2, 0x28, 0x00, 0x06, 0x11, 0x00, 0x8A, 0xBF, 0x71, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x1A, 0x15, 0x0C,
0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x19, 0x7B, 0xB0, 0x44, 0x12, 0x8B, 0x01, 0xA4, 0xBC, 0x35, 0x56, 0xFE, 0xF0, 0x0C, 0xA1, 0x00, 0x43, 0xFD, 0x00, 0x7B, 0x47, 0x4D,
0x92, 0x00, 0xCE, 0xEF, 0x0C, 0x00, 0x55, 0x6B, 0x0E, 0x00, 0xA9, 0x08, 0x00, 0x8A, 0x09, 0xC6, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x1B, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1A, 0x7B, 0xB0, 0x44, 0x25, 0xAF, 0x01, 0xD4, 0x10, 0x50, 0x81, 0x14, 0xC4, 0x0C, 0xA1, 0x00, 0x29, 0xFD, 0x00, 0x7B, 0xA9, 0xFD, 0xBE, 0x00, 0x17, 0x91,
0x19, 0x00, 0x63, 0x98, 0xC1, 0x00, 0x12, 0xFF, 0x3F, 0x8A, 0x9F, 0x4C, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x1D, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0x1C, 0x00, 0xB0, 0x14, 0x3D, 0x4B, 0x03, 0x1C, 0xF4, 0x0E, 0xF2, 0x19, 0x63, 0x0C, 0xA1, 0x00, 0x2E, 0xFD, 0xD4, 0x49, 0x34, 0x1C, 0xC2, 0x00, 0x7E, 0x9D, 0x60, 0x00, 0x79, 0x3B,
0x17, 0x00, 0x2A, 0xF6, 0x3F, 0x8A, 0x99, 0x1E, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x1E, 0x15, 0x0C, 0x0F, 0x0C, 0x00, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1D, 0x00,
0xB0, 0x14, 0x9C, 0x70, 0x03, 0x50, 0xC4, 0x2B, 0x21, 0xFC, 0xF9, 0x0C, 0xA1, 0x00, 0x44, 0xFD, 0xD4, 0x49, 0x07, 0x9B, 0x6A, 0x00, 0x77, 0x0C, 0x90, 0x00, 0xE5, 0x6F, 0x9E, 0x00, 0xF8, 0xFD,
0x3F, 0x8A, 0x8C, 0x9F, 0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x1F, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1E, 0x7B, 0xB0, 0x04, 0x9F, 0xDA,
0x01, 0x5F, 0x6C, 0x55, 0x80, 0x08, 0xD4, 0x0C, 0xA1, 0x00, 0x5E, 0xFD, 0x00, 0x7B, 0xCB, 0xAE, 0x6A, 0x00, 0x9B, 0x04, 0x0E, 0x00, 0x96, 0x65, 0x3A, 0x00, 0x5F, 0x07, 0x80, 0x8A, 0xAA, 0xFA,
0xB5, 0x62, 0x13, 0x80, 0x34, 0x00, 0x02, 0x00, 0x00, 0x20, 0x15, 0x0C, 0x11, 0x13, 0x38, 0x1C, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x1F, 0x7B, 0xB0, 0x44, 0x7E, 0x41, 0x01, 0x92, 0xEF, 0x2A,
0x99, 0x09, 0x6C, 0x0D, 0xA1, 0x00, 0x40, 0xFD, 0x00, 0x7B, 0x3C, 0xB6, 0x3E, 0x00, 0x0C, 0xA2, 0x9E, 0x00, 0x27, 0x24, 0xDF, 0x00, 0xE0, 0xFF, 0xBF, 0x8A, 0xEB, 0xFB, 0xB5, 0x62, 0x13, 0x80,
0x24, 0x00, 0x02, 0x00, 0x01, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x23, 0x00, 0xB0, 0x44, 0x55, 0xF1, 0x01, 0x0A, 0x01, 0xB6, 0x8F, 0x10, 0x40, 0x8D,
0x04, 0x00, 0x8A, 0x08, 0x0E, 0x00, 0x92, 0x02, 0xB5, 0x62, 0x13, 0x80, 0x24, 0x00, 0x02, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x30, 0x00,
0xB0, 0x44, 0x66, 0xC5, 0x03, 0x31, 0x1B, 0xBF, 0xC2, 0x02, 0x40, 0x8D, 0x04, 0x00, 0x8A, 0x08, 0x0E, 0x00, 0x02, 0x61, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x01, 0x15, 0x0C,
0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x9F, 0xBD, 0xD2, 0x04, 0x7B, 0x94, 0x00, 0x0C, 0xC5, 0x40, 0x6B, 0x08, 0x38, 0xC3, 0x24, 0xFD, 0xC5, 0xC3, 0xA5, 0x57, 0xE1, 0x21,
0x3E, 0x01, 0x58, 0x1D, 0x78, 0x3A, 0x38, 0x13, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x02, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA0, 0xBD,
0xB2, 0x04, 0x91, 0x39, 0x00, 0x56, 0xDD, 0x3F, 0x2B, 0x17, 0x59, 0xC0, 0x82, 0xEF, 0x6C, 0x9A, 0xA8, 0x57, 0x7C, 0x9D, 0x72, 0x07, 0xBA, 0x27, 0x78, 0x3A, 0xB1, 0x11, 0xB5, 0x62, 0x13, 0x80,
0x2C, 0x00, 0x02, 0x00, 0x06, 0x03, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA1, 0xBD, 0xD2, 0x04, 0xBE, 0x31, 0x00, 0xB5, 0xD6, 0x3F, 0x6B, 0x19, 0x87, 0xDB,
0x80, 0xFE, 0x42, 0x0C, 0xAB, 0x57, 0xDE, 0xA0, 0x21, 0x07, 0xF9, 0x23, 0x74, 0x3A, 0x7E, 0x46, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x04, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA2, 0xBD, 0xB2, 0x04, 0x57, 0x31, 0x00, 0x4B, 0xBE, 0x3F, 0xAB, 0x21, 0x5E, 0x0B, 0x3F, 0xFC, 0x75, 0x74, 0xAD, 0x57, 0x5F, 0xB2, 0x07, 0x03, 0x1B, 0x2B,
0x70, 0x3A, 0xBB, 0x0F, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x05, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA3, 0xBD, 0xD2, 0x04, 0x33, 0x11,
0x00, 0x49, 0xAC, 0x3F, 0x6B, 0x28, 0xE3, 0x3A, 0x7D, 0xFD, 0x5F, 0xDB, 0xAF, 0x57, 0x9D, 0x88, 0x4D, 0x02, 0x72, 0x2B, 0x6C, 0x3A, 0x3E, 0x71, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00,
0x06, 0x06, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA4, 0xBD, 0xD2, 0x04, 0xEC, 0x1E, 0x00, 0xD0, 0xF5, 0x3F, 0x2B, 0x37, 0x2E, 0xF3, 0xBA, 0x80, 0x9A, 0xA6,
0xB2, 0x57, 0xF7, 0x5D, 0x55, 0x03, 0xF4, 0x1C, 0x68, 0x3A, 0x19, 0x49, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x07, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0xA5, 0xBD, 0xD2, 0x84, 0xCD, 0x4A, 0x02, 0x75, 0xE1, 0x3F, 0x6B, 0x39, 0x75, 0x51, 0x48, 0x81, 0x45, 0x3F, 0xA1, 0x57, 0x5D, 0xA0, 0x16, 0x06, 0x27, 0x24, 0x84, 0x3A, 0xA8, 0x47,
0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x08, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA6, 0xBD, 0xD2, 0x04, 0x3F, 0xAB, 0x00, 0xC5, 0xCE, 0x3F,
0xAB, 0x41, 0x2F, 0x8F, 0x06, 0x82, 0x68, 0x8F, 0xA3, 0x57, 0xB1, 0xB2, 0x4E, 0x07, 0x51, 0x24, 0x90, 0x3A, 0x81, 0x71, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x09, 0x15, 0x0C,
0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA7, 0xBD, 0xD2, 0x04, 0xEF, 0x93, 0x00, 0x15, 0x7A, 0x40, 0xAB, 0x4F, 0xD9, 0xD5, 0x4F, 0xFF, 0xD6, 0x42, 0xA5, 0x57, 0xAC, 0x90,
0x39, 0x06, 0x3F, 0x11, 0x94, 0x3B, 0xA2, 0xFC, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x0A, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA8, 0xBD,
0xD2, 0x84, 0x00, 0xC3, 0x00, 0xBA, 0x16, 0x40, 0x6B, 0x56, 0xF5, 0x1B, 0x8E, 0x82, 0xD8, 0x8B, 0xA7, 0x57, 0x1B, 0x06, 0xE9, 0x06, 0x2D, 0x21, 0x94, 0x3B, 0x71, 0x55, 0xB5, 0x62, 0x13, 0x80,
0x2C, 0x00, 0x02, 0x00, 0x06, 0x0B, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xA9, 0xBD, 0xD2, 0x04, 0xD3, 0x18, 0x00, 0x90, 0x2C, 0x40, 0x2B, 0x58, 0xCF, 0x2D,
0x8C, 0x7E, 0xEF, 0x12, 0xAA, 0x57, 0xD2, 0xB1, 0xAD, 0x01, 0x45, 0x27, 0x94, 0x3B, 0x8F, 0x20, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x0C, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xAA, 0xBD, 0xD2, 0x04, 0x7F, 0x3D, 0x00, 0x5E, 0x7A, 0x40, 0xEB, 0x67, 0x2C, 0x45, 0xCA, 0xF6, 0x48, 0xA8, 0xAC, 0x57, 0xF8, 0xD5, 0x66, 0x04, 0x36, 0x22,
0x94, 0x3B, 0x5B, 0x01, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x0D, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xAB, 0xBD, 0xD2, 0x84, 0xC1, 0x1E,
0x00, 0xC6, 0xFF, 0x3F, 0xAB, 0x6F, 0xFE, 0x69, 0x28, 0x82, 0xE4, 0x06, 0xAF, 0x57, 0x3D, 0x34, 0x38, 0x02, 0xFB, 0x20, 0x94, 0x3B, 0xC8, 0xB3, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00,
0x06, 0x0E, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xAC, 0xBD, 0xB2, 0x04, 0x41, 0xAE, 0x00, 0xFC, 0x51, 0x40, 0x6B, 0x76, 0x2D, 0x7B, 0xA6, 0xFD, 0x9C, 0x9F,
0xB1, 0x57, 0xDA, 0xAF, 0xDE, 0x01, 0xB5, 0x1B, 0x98, 0x3B, 0x8D, 0x75, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x0F, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0xAD, 0xBD, 0xD2, 0x04, 0x32, 0x99, 0x02, 0x17, 0x61, 0x40, 0x2B, 0x78, 0x76, 0xB4, 0xD3, 0xFC, 0x10, 0x45, 0xA0, 0x57, 0x67, 0xC7, 0xEF, 0x02, 0x93, 0x20, 0x90, 0x3B, 0xC3, 0x1E,
0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x10, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xAE, 0xBD, 0xD2, 0x04, 0x70, 0x64, 0x02, 0x10, 0xC5, 0x40,
0xEB, 0x87, 0x72, 0x87, 0xF1, 0x80, 0x8E, 0xF2, 0xA2, 0x57, 0x57, 0x0A, 0x76, 0x0C, 0xD9, 0x17, 0x88, 0x3B, 0x91, 0xAA, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x11, 0x15, 0x0C,
0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xAF, 0xBD, 0xD2, 0x04, 0x95, 0xB0, 0x00, 0x24, 0x2D, 0x40, 0x2B, 0x89, 0x3B, 0x4D, 0x5B, 0xF0, 0xAB, 0x5C, 0xA4, 0x57, 0x50, 0x95,
0x23, 0x04, 0xB1, 0x3A, 0x0C, 0x3A, 0x59, 0x64, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x12, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB0, 0xBD,
0xD2, 0x04, 0x61, 0xBD, 0x00, 0xC7, 0x14, 0x40, 0x6B, 0x97, 0x82, 0x8D, 0xD9, 0xFC, 0xB9, 0xB4, 0xA6, 0x57, 0x71, 0xE6, 0x0F, 0x05, 0x96, 0x40, 0x10, 0x3A, 0xD3, 0x7B, 0xB5, 0x62, 0x13, 0x80,
0x2C, 0x00, 0x02, 0x00, 0x06, 0x13, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB1, 0xBD, 0xD2, 0x04, 0x61, 0x35, 0x00, 0xC3, 0xC6, 0x3F, 0xEB, 0x98, 0x44, 0x01,
0x98, 0x85, 0xA8, 0xE9, 0xA8, 0x57, 0xC2, 0xA4, 0x0D, 0x01, 0xFD, 0x45, 0x10, 0x3A, 0x94, 0x41, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x14, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A,
0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB2, 0xBD, 0xD2, 0x04, 0xB1, 0x18, 0x00, 0x5D, 0x11, 0x40, 0xAB, 0xA0, 0xC8, 0xDF, 0x15, 0x82, 0xB1, 0xB9, 0xAB, 0x57, 0xF1, 0xE9, 0xC1, 0x03, 0x4A, 0x46,
0x10, 0x3A, 0xA7, 0x43, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x15, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB3, 0xBD, 0xD2, 0x04, 0x28, 0x00,
0x02, 0x5E, 0x5E, 0x40, 0x2B, 0xA9, 0x27, 0xA6, 0x73, 0x88, 0xE6, 0x73, 0xAE, 0x57, 0xCC, 0x8C, 0xAD, 0x02, 0x36, 0x3F, 0x14, 0x3A, 0xAF, 0x7C, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00,
0x06, 0x16, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB4, 0xBD, 0xD2, 0x04, 0x85, 0xEC, 0x00, 0x91, 0xEE, 0x3F, 0x6B, 0xB7, 0x72, 0xF2, 0xD1, 0x84, 0x7D, 0xBE,
0xB0, 0x57, 0xEE, 0x05, 0x09, 0x08, 0x71, 0x43, 0x18, 0x3A, 0x1D, 0x65, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x17, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A,
0x01, 0x03, 0xB5, 0xBD, 0xD2, 0x04, 0xB1, 0x72, 0x02, 0x0E, 0xDA, 0x3E, 0xEB, 0xB8, 0xE4, 0xB4, 0xAF, 0x80, 0x21, 0xAF, 0xB3, 0x57, 0x7C, 0x50, 0x49, 0x00, 0x24, 0x43, 0x18, 0x3A, 0x26, 0xE7,
0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x02, 0x00, 0x06, 0x18, 0x15, 0x0C, 0x15, 0x15, 0x00, 0x2A, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0xB6, 0xBD, 0xD2, 0x04, 0xE1, 0x9D, 0x00, 0xA2, 0x0C, 0x40,
0xAB, 0xC0, 0xEA, 0x39, 0xDD, 0xFD, 0x09, 0xEC, 0xA1, 0x57, 0x92, 0xAE, 0x92, 0x03, 0x96, 0x2B, 0x08, 0x3A, 0x64, 0xD1, 0xB5, 0x62, 0x13, 0x80, 0x78, 0x00, 0x03, 0x00, 0x00, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x19, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x07, 0x00, 0x8A, 0x08, 0x89, 0x08, 0x12, 0x07,
0x12, 0x00, 0xC1, 0x03, 0x00, 0x00, 0x19, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x32, 0x00, 0x00, 0x80, 0xB2, 0x00, 0x00, 0x80, 0xB3, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0xEC, 0x47, 0x00, 0x00,
0x60, 0xC8, 0x00, 0x00, 0x80, 0xC7, 0x00, 0x00, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x22, 0xB5, 0x62, 0x13, 0x80, 0x2C, 0x00, 0x03, 0x00, 0x06, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x4A, 0x01, 0x03, 0x12, 0x00, 0x06, 0x07, 0x11, 0x00, 0x06, 0x15, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xBD, 0x3A, 0xBD, 0x02, 0x92, 0xC7,
0x1E, 0x00, 0xBD, 0x3A, 0x00, 0x00, 0x83, 0xEB, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x02, 0x15, 0x0C, 0x04, 0x14, 0x0F, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x01, 0x5E,
0xAD, 0x14, 0x0D, 0xD9, 0x01, 0x99, 0x8A, 0x08, 0x57, 0xFD, 0x00, 0x00, 0x00, 0xC4, 0xA0, 0xFF, 0xAF, 0x13, 0x31, 0x04, 0x00, 0xED, 0x07, 0xBB, 0xE2, 0xC1, 0xE2, 0xA6, 0xA2, 0x5F, 0x18, 0xC2,
0x8E, 0x08, 0x42, 0xC9, 0xD5, 0x17, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x07, 0x20, 0x00, 0x50, 0xF8, 0xAB, 0xA6, 0xD0, 0xC2, 0x7C, 0xE2, 0xDC, 0x80, 0x36, 0x92, 0x07, 0x9C, 0x36, 0xA1, 0x2E,
0x78, 0x0C, 0x91, 0xCE, 0x7E, 0xE6, 0x3E, 0x63, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x05, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x04, 0x5E,
0xAD, 0x14, 0xBB, 0x54, 0x01, 0x85, 0x8A, 0x08, 0x58, 0xFD, 0x00, 0x00, 0x40, 0xC4, 0xA9, 0xFF, 0xAF, 0x13, 0xEE, 0xD1, 0x3F, 0xF4, 0xE8, 0xBA, 0x02, 0x42, 0xDB, 0xA6, 0xE2, 0x4F, 0x93, 0xCC,
0x86, 0x08, 0x5E, 0xE0, 0xFB, 0x01, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0xB2, 0xC2, 0xA1, 0xA2, 0x5A, 0xF1, 0x5A, 0x67, 0xD2, 0x32, 0xCB, 0x87, 0x73, 0xEA, 0xA6, 0x22, 0x7E, 0xCE,
0xB7, 0xF8, 0x59, 0xCB, 0x13, 0xCF, 0x90, 0x3D, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x07, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x06, 0x5E,
0xAD, 0x14, 0x1C, 0x84, 0x01, 0x3C, 0x8A, 0x08, 0x58, 0x01, 0x00, 0x00, 0x40, 0x44, 0x5B, 0x01, 0xA0, 0x13, 0xB1, 0x77, 0x00, 0xF4, 0xE8, 0xBA, 0x02, 0x02, 0xDC, 0xA6, 0x62, 0x0D, 0x9B, 0x10,
0x8D, 0x08, 0xE8, 0xB3, 0x12, 0x04, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0xD3, 0xB5, 0xB7, 0x0A, 0x9E, 0x12, 0x42, 0xA2, 0x14, 0xB9, 0xAD, 0xE8, 0xD6, 0xBA, 0xA1, 0x04, 0xEB, 0x56,
0xE5, 0x29, 0xD2, 0xF1, 0x3E, 0xD4, 0x51, 0xD3, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x09, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x08, 0x5E,
0xAD, 0x14, 0xAE, 0x75, 0x01, 0xEB, 0x8A, 0x08, 0x58, 0x7D, 0x00, 0x00, 0x40, 0x44, 0x27, 0x00, 0xA0, 0x13, 0x2A, 0x09, 0x80, 0x01, 0xE8, 0xBA, 0x02, 0xC2, 0xDC, 0xA6, 0x42, 0x7D, 0x43, 0x25,
0x97, 0x08, 0x0E, 0x55, 0xE8, 0x19, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0x34, 0x43, 0x60, 0xE2, 0x59, 0x42, 0xAE, 0xE1, 0xE5, 0x34, 0x86, 0x80, 0xA7, 0xDF, 0xA3, 0xE9, 0x4E, 0xB1,
0xF7, 0x28, 0xB4, 0xBC, 0x8B, 0xD9, 0x5E, 0x5F, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x0D, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x0C, 0x5E,
0xAD, 0x14, 0x79, 0x8D, 0x01, 0x81, 0x8A, 0x08, 0x58, 0x01, 0x00, 0x00, 0x40, 0x44, 0x91, 0x01, 0xA0, 0x13, 0x16, 0xD5, 0x3F, 0xF4, 0xE8, 0xBA, 0x02, 0x42, 0xDC, 0xA6, 0x62, 0x60, 0x15, 0x2D,
0x81, 0x08, 0x74, 0x9A, 0x0E, 0x09, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0x43, 0x1D, 0xEA, 0x22, 0xB7, 0x2E, 0xD8, 0x3E, 0x59, 0x3A, 0x55, 0x75, 0x09, 0xE7, 0x0A, 0x39, 0xC6, 0x02,
0x6D, 0xFD, 0xE5, 0xF4, 0x5E, 0xEB, 0x85, 0x41, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x10, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x0F, 0x5E,
0xAD, 0x14, 0xB3, 0xBB, 0x01, 0x4E, 0x8A, 0x08, 0x58, 0xFD, 0x00, 0x00, 0x40, 0xC4, 0x49, 0xFE, 0xAF, 0x13, 0x9A, 0xA4, 0x3F, 0xF5, 0xE8, 0xBA, 0x02, 0x02, 0xDC, 0xA6, 0xA2, 0x33, 0xC9, 0x6D,
0x90, 0x08, 0x5A, 0x9A, 0xE3, 0x00, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0x0D, 0xDD, 0x6D, 0xCD, 0xC2, 0x40, 0x3A, 0x97, 0x5C, 0x01, 0x02, 0x3D, 0x12, 0x48, 0x55, 0xDE, 0xBA, 0x35,
0x8E, 0xFB, 0xE1, 0xF2, 0x92, 0xCE, 0x5C, 0xFB, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x14, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x13, 0x5E,
0xAD, 0x14, 0x31, 0xC2, 0x01, 0x0F, 0x8A, 0x08, 0x58, 0x81, 0x00, 0x00, 0x40, 0x44, 0xF0, 0xFF, 0xAF, 0x13, 0x98, 0x87, 0x3F, 0xF7, 0xE8, 0xBA, 0x02, 0xC2, 0xDC, 0xA6, 0x42, 0x50, 0xFF, 0x9B,
0x8B, 0x08, 0x16, 0xF0, 0x21, 0x1C, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0x93, 0x50, 0xE6, 0x06, 0xCF, 0xB8, 0x8C, 0x20, 0xB6, 0x0C, 0x26, 0x4A, 0xC1, 0xC6, 0x7E, 0x22, 0xAC, 0xC6,
0x13, 0x14, 0x27, 0xFF, 0x18, 0xD2, 0xBA, 0xBA, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x1D, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x1C, 0x5E,
0xAD, 0x14, 0x13, 0x8D, 0x01, 0xBC, 0x8A, 0x08, 0x58, 0xFD, 0x00, 0x00, 0x40, 0xC4, 0x8F, 0xFE, 0xAF, 0x13, 0x52, 0xDC, 0x00, 0xF5, 0xE8, 0xBA, 0x02, 0xC2, 0xDC, 0xA6, 0xC2, 0x76, 0x7B, 0x55,
0x91, 0x08, 0x2A, 0xA2, 0xE2, 0x18, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0x0F, 0xB0, 0x0B, 0x57, 0x2E, 0x66, 0x57, 0xE9, 0x32, 0x5E, 0x4E, 0x14, 0x13, 0x5E, 0xFF, 0x22, 0xFA, 0x2A,
0x77, 0xDB, 0x2C, 0xC6, 0xC6, 0xDE, 0x74, 0x97, 0xB5, 0x62, 0x13, 0x80, 0x58, 0x00, 0x05, 0x00, 0x00, 0x1E, 0x15, 0x0C, 0x04, 0x14, 0x1E, 0x03, 0x06, 0x1E, 0xE0, 0x4A, 0x01, 0x03, 0x1D, 0x5E,
0xAD, 0x14, 0x8D, 0x68, 0x01, 0xBB, 0x8A, 0x08, 0x58, 0xFD, 0x00, 0x00, 0x40, 0x44, 0x28, 0xFF, 0xAF, 0x13, 0x5F, 0x5B, 0x00, 0x04, 0xE8, 0xBA, 0x02, 0x42, 0xDC, 0xA6, 0x22, 0x7D, 0x16, 0x76,
0x89, 0x08, 0xDE, 0x80, 0xDF, 0x08, 0x7A, 0x3A, 0x00, 0x00, 0xAA, 0x6C, 0x08, 0x20, 0xE3, 0x37, 0xB1, 0x7F, 0xE8, 0xDA, 0x6C, 0x66, 0x21, 0x07, 0x1F, 0x55, 0x27, 0x27, 0x42, 0x1B, 0x68, 0x58,
0x2E, 0x2E, 0x19, 0xA5, 0xC5, 0xE3, 0xBB, 0x38
};

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,325 @@
/*
Use ESP32 WiFi to get AssistNow Offline data from u-blox Thingstream
By: SparkFun Electronics / Paul Clark
Date: November 26th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Offline data from u-blox Thingstream over WiFi
and push it over I2C to a u-blox module.
The module still needs to be given time assistance to achieve a fast fix. This example
uses network time to do that. If you don't have a WiFi connection, you may have to use
a separate RTC to provide the time.
Note: AssistNow Offline is not supported by the ZED-F9P! "The ZED-F9P supports AssistNow Online only."
You will need to have a token to be able to access Thingstream. See the AssistNow README for more details.
Update secrets.h with your:
- WiFi credentials
- AssistNow token string
Uncomment the "#define USE_MGA_ACKs" below to test the more robust method of using the
UBX_MGA_ACK_DATA0 acknowledgements to confirm that each MGA message has been accepted.
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
//#define USE_MGA_ACKs // Uncomment this line to use the UBX_MGA_ACK_DATA0 acknowledgements
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
const char assistNowServer[] = "https://offline-live1.services.u-blox.com";
//const char assistNowServer[] = "https://offline-live2.services.u-blox.com"; // Alternate server
const char getQuery[] = "GetOfflineData.ashx?";
const char tokenPrefix[] = "token=";
const char tokenSuffix[] = ";";
const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal
const char getFormat[] = "format=mga;"; // Data format. Leave set to mga for M8 onwards. Can be aid.
const char getPeriod[] = "period=1;"; // Optional. The number of weeks into the future that the data will be valid. Can be 1-5. Default = 4.
const char getMgaResolution[] = "resolution=1;"; // Optional. Data resolution: 1 = every day; 2 = every other day; 3 = every 3rd day.
//Note: always use resolution=1. findMGAANOForDate does not yet support finding the 'closest' date. It needs an exact match.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "time.h"
const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Set the RTC using network time. (Code taken from the SimpleTime example.)
// Request the time from the NTP server and use it to set the ESP32's RTC.
configTime(0, 0, ntpServer); // Set the GMT and daylight offsets to zero. We need UTC, not local time.
struct tm timeinfo;
if(!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
}
else
{
Serial.println(&timeinfo, "Time is: %A, %B %d %Y %H:%M:%S");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use HTTP GET to receive the AssistNow_Online data
const int URL_BUFFER_SIZE = 256;
char theURL[URL_BUFFER_SIZE]; // This will contain the HTTP URL
int payloadSize = 0; // This will be updated with the length of the data we get from the server
String payload; // This will store the data we get from the server
// Assemble the URL
// Note the slash after the first %s (assistNowServer)
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getFormat,
getPeriod,
getMgaResolution
);
Serial.print(F("HTTP URL is: "));
Serial.println(theURL);
HTTPClient http;
http.begin(theURL);
int httpCode = http.GET(); // HTTP GET
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\r\n", httpCode);
// If the GET was successful, read the data
if(httpCode == HTTP_CODE_OK) // Check for code 200
{
payloadSize = http.getSize();
Serial.printf("Server returned %d bytes\r\n", payloadSize);
payload = http.getString(); // Get the payload
// Pretty-print the payload as HEX
/*
int i;
for(i = 0; i < payloadSize; i++)
{
if (payload[i] < 0x10) // Print leading zero
Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
if ((i % 16) == 15)
Serial.println();
}
if ((i % 16) != 15)
Serial.println();
*/
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\r\n", http.errorToString(httpCode).c_str());
}
http.end();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Find where the AssistNow data for today starts and ends
size_t todayStart = 0; // Default to sending all the data
size_t tomorrowStart = (size_t)payloadSize;
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
if (payloadSize > 0)
{
if(getLocalTime(&timeinfo))
{
// Find the start of today's data
todayStart = myGNSS.findMGAANOForDate(payload, (size_t)payloadSize, timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (todayStart < (size_t)payloadSize)
{
Serial.print(F("Found the data for today starting at location "));
Serial.println(todayStart);
}
else
{
Serial.println("Could not find the data for today. This will not work well. The GNSS needs help to start up quickly.");
}
// Find the start of tomorrow's data
tomorrowStart = myGNSS.findMGAANOForDate(payload, (size_t)payloadSize, timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, 1);
if (tomorrowStart < (size_t)payloadSize)
{
Serial.print(F("Found the data for tomorrow starting at location "));
Serial.println(tomorrowStart);
}
else
{
Serial.println("Could not find the data for tomorrow. (Today's data may be the last?)");
}
}
else
{
Serial.println("Failed to obtain time. This will not work well. The GNSS needs accurate time to start up quickly.");
}
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the RTC time to the module
if(getLocalTime(&timeinfo)) // Get the local time again, just to make sure we are using the most accurate time
{
// setUTCTimeAssistance uses a default time accuracy of 2 seconds which should be OK here.
// Have a look at the library source code for more details.
myGNSS.setUTCTimeAssistance(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
else
{
Serial.println("Failed to obtain time. This will not work well. The GNSS needs accurate time to start up quickly.");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow data for today to the module - without the time
if (payloadSize > 0)
{
#ifndef USE_MGA_ACKs
// ***** Don't use the UBX_MGA_ACK_DATA0 messages *****
// Push the AssistNow data for today. Don't use UBX_MGA_ACK_DATA0's. Use the default delay of 7ms between messages.
myGNSS.pushAssistNowData(todayStart, true, payload, tomorrowStart - todayStart);
#else
// ***** Use the UBX_MGA_ACK_DATA0 messages *****
// Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data
myGNSS.setAckAiding(1);
// Speed things up by setting setI2CpollingWait to 1ms
myGNSS.setI2CpollingWait(1);
// Push the AssistNow data for today.
myGNSS.pushAssistNowData(todayStart, true, payload, tomorrowStart - todayStart, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100);
// Set setI2CpollingWait to 125ms to avoid pounding the I2C bus
myGNSS.setI2CpollingWait(125);
#endif
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Disconnect the WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Serial.println(F("WiFi disconnected"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,236 @@
/*
Use ESP32 WiFi to get AssistNow Online data from u-blox Thingstream
By: SparkFun Electronics / Paul Clark
Date: November 24th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Online data from u-blox Thingstream over WiFi
and push it over I2C to a u-blox module.
You will need to have a token to be able to access Thingstream. See the AssistNow README for more details.
Update secrets.h with your:
- WiFi credentials
- AssistNow token string
Uncomment the "#define USE_MGA_ACKs" below to test the more robust method of using the
UBX_MGA_ACK_DATA0 acknowledgements to confirm that each MGA message has been accepted.
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
//#define USE_MGA_ACKs // Uncomment this line to use the UBX_MGA_ACK_DATA0 acknowledgements
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
const char assistNowServer[] = "https://online-live1.services.u-blox.com";
//const char assistNowServer[] = "https://online-live2.services.u-blox.com"; // Alternate server
const char getQuery[] = "GetOnlineData.ashx?";
const char tokenPrefix[] = "token=";
const char tokenSuffix[] = ";";
const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal
const char getDataType[] = "datatype=eph,alm,aux;"; // Data type can be: eph,alm,aux,pos
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use HTTP GET to receive the AssistNow_Online data
const int URL_BUFFER_SIZE = 256;
char theURL[URL_BUFFER_SIZE]; // This will contain the HTTP URL
int payloadSize = 0; // This will be updated with the length of the data we get from the server
String payload; // This will store the data we get from the server
// Assemble the URL
// Note the slash after the first %s (assistNowServer)
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getDataType);
Serial.print(F("HTTP URL is: "));
Serial.println(theURL);
HTTPClient http;
http.begin(theURL);
int httpCode = http.GET(); // HTTP GET
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\r\n", httpCode);
// If the GET was successful, read the data
if(httpCode == HTTP_CODE_OK) // Check for code 200
{
payloadSize = http.getSize();
Serial.printf("Server returned %d bytes\r\n", payloadSize);
payload = http.getString(); // Get the payload
// Pretty-print the payload as HEX
/*
int i;
for(i = 0; i < payloadSize; i++)
{
if (payload[i] < 0x10) // Print leading zero
Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
if ((i % 16) == 15)
Serial.println();
}
if ((i % 16) != 15)
Serial.println();
*/
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\r\n", http.errorToString(httpCode).c_str());
}
http.end();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow data to the module
if (payloadSize > 0)
{
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
#ifndef USE_MGA_ACKs
// ***** Don't use the UBX_MGA_ACK_DATA0 messages *****
// Push all the AssistNow data. Don't use UBX_MGA_ACK_DATA0's. Use the default delay of 7ms between messages.
myGNSS.pushAssistNowData(payload, (size_t)payloadSize);
#else
// ***** Use the UBX_MGA_ACK_DATA0 messages *****
// Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data
myGNSS.setAckAiding(1);
// Speed things up by setting setI2CpollingWait to 1ms
myGNSS.setI2CpollingWait(1);
// Push all the AssistNow data.
// We have called setAckAiding(1) to instruct the module to return MGA-ACK messages.
// So, we could set the pushAssistNowData mgaAck parameter to SFE_UBLOX_MGA_ASSIST_ACK_YES.
// But, just for giggles, let's use SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE just to confirm that the
// MGA-ACK messages are actually enabled.
// Wait for up to 100ms for each ACK to arrive! 100ms is a bit excessive... 7ms is nearer the mark.
myGNSS.pushAssistNowData(payload, (size_t)payloadSize, SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE, 100);
// Set setI2CpollingWait to 125ms to avoid pounding the I2C bus
myGNSS.setI2CpollingWait(125);
#endif
}
Serial.println(F("Here we go!"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,254 @@
/*
Use ESP32 WiFi to get AssistNow Online data from u-blox Thingstream
By: SparkFun Electronics / Paul Clark
Date: November 24th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Online data from u-blox Thingstream over WiFi
and push it over I2C to a u-blox module.
The AssistNow Online data is valid for 2-4 hours, so it can be re-used.
BUT you need to provide the time assistance separately.
This example shows how to do that.
The ESP32's RTC is set from network time.
The RTC time is pushed to the module using setUTCTimeAssistance,
followed by the AssistNow data (without time).
You will need to have a token to be able to access Thingstream. See the AssistNow README for more details.
Update secrets.h with your:
- WiFi credentials
- AssistNow token string
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
const char assistNowServer[] = "https://online-live1.services.u-blox.com";
//const char assistNowServer[] = "https://online-live2.services.u-blox.com"; // Alternate server
const char getQuery[] = "GetOnlineData.ashx?";
const char tokenPrefix[] = "token=";
const char tokenSuffix[] = ";";
const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal
const char getDataType[] = "datatype=eph,alm,aux;"; // Data type can be: eph,alm,aux,pos
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "time.h"
const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Set the RTC using network time. (Code taken from the SimpleTime example.)
// Request the time from the NTP server and use it to set the ESP32's RTC.
configTime(0, 0, ntpServer); // Set the GMT and daylight offsets to zero. We need UTC, not local time.
struct tm timeinfo;
if(!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
}
else
{
Serial.println(&timeinfo, "Time is: %A, %B %d %Y %H:%M:%S");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use HTTP GET to receive the AssistNow_Online data
const int URL_BUFFER_SIZE = 256;
char theURL[URL_BUFFER_SIZE]; // This will contain the HTTP URL
int payloadSize = 0; // This will be updated with the length of the data we get from the server
String payload; // This will store the data we get from the server
// Assemble the URL
// Note the slash after the first %s (assistNowServer)
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getDataType);
Serial.print(F("HTTP URL is: "));
Serial.println(theURL);
HTTPClient http;
http.begin(theURL);
int httpCode = http.GET(); // HTTP GET
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\r\n", httpCode);
// If the GET was successful, read the data
if(httpCode == HTTP_CODE_OK) // Check for code 200
{
payloadSize = http.getSize();
Serial.printf("Server returned %d bytes\r\n", payloadSize);
payload = http.getString(); // Get the payload
// Pretty-print the payload as HEX
/*
int i;
for(i = 0; i < payloadSize; i++)
{
if (payload[i] < 0x10) // Print leading zero
Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
if ((i % 16) == 15)
Serial.println();
}
if ((i % 16) != 15)
Serial.println();
*/
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\r\n", http.errorToString(httpCode).c_str());
}
http.end();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the RTC time to the module
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
if(getLocalTime(&timeinfo))
{
// setUTCTimeAssistance uses a default time accuracy of 2 seconds which should be OK here.
// Have a look at the library source code for more details.
myGNSS.setUTCTimeAssistance(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
else
{
Serial.println("Failed to obtain time. This will not work well. The GNSS needs accurate time to start up quickly.");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow data to the module - WITHOUT THE TIME
if (payloadSize > 0)
{
// Push the AssistNow data. Don't use UBX_MGA_ACK_DATA0's. Use the default delay of 7ms between messages.
// The 'true' parameter tells pushAssistNowData not to push any time data from the payload.
myGNSS.pushAssistNowData(true, payload, (size_t)payloadSize);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Disconnect the WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Serial.println(F("WiFi disconnected"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,299 @@
/*
Use ESP32 WiFi to get AssistNow Online data from u-blox Thingstream
By: SparkFun Electronics / Paul Clark
Date: November 24th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Online data from u-blox Thingstream over WiFi
and push it over I2C to a u-blox module.
This example shows how to provide initial position assistance. Uncomment #define USE_SERVER_ASSISTANCE
below to include the position in the AssistNow data request, instead of using setPositionAssistanceLLH.
You will need to have a token to be able to access Thingstream. See the AssistNow README for more details.
Update secrets.h with your:
- WiFi credentials
- AssistNow token string
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
//#define USE_SERVER_ASSISTANCE // Uncomment this line to include the position in the AssistNow data request
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
const char assistNowServer[] = "https://online-live1.services.u-blox.com";
//const char assistNowServer[] = "https://online-live2.services.u-blox.com"; // Alternate server
const char getQuery[] = "GetOnlineData.ashx?";
const char tokenPrefix[] = "token=";
const char tokenSuffix[] = ";";
const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal
const char getDataType[] = "datatype=eph,alm,aux;"; // Data type can be: eph,alm,aux,pos
#ifdef USE_SERVER_ASSISTANCE
const char useLatitude[] = "lat=55.0;"; // Use an approximate latitude of 55 degrees north. Replace this with your latitude.
const char useLongitude[] = "lon=-1.0;"; // Use an approximate longitude of 1 degree west. Replace this with your longitude.
const char useAlt[] = "alt=100;"; // Use an approximate latitude of 100m above WGS84. Replace this with your altitude.
const char usePosAcc[] = "pacc=100000;"; // Use a position accuracy of 100000m (100km)
#endif
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "time.h"
const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Start I2C. Connect to the GNSS.
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Set the RTC using network time. (Code taken from the SimpleTime example.)
// Request the time from the NTP server and use it to set the ESP32's RTC.
configTime(0, 0, ntpServer); // Set the GMT and daylight offsets to zero. We need UTC, not local time.
struct tm timeinfo;
if(!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
}
else
{
Serial.println(&timeinfo, "Time is: %A, %B %d %Y %H:%M:%S");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use HTTP GET to receive the AssistNow_Online data
const int URL_BUFFER_SIZE = 256;
char theURL[URL_BUFFER_SIZE]; // This will contain the HTTP URL
int payloadSize = 0; // This will be updated with the length of the data we get from the server
String payload; // This will store the data we get from the server
// Assemble the URL
// Note the slash after the first %s (assistNowServer)
#ifdef USE_SERVER_ASSISTANCE
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getDataType,
useLatitude,
useLongitude,
useAlt,
usePosAcc);
#else
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getDataType);
#endif
Serial.print(F("HTTP URL is: "));
Serial.println(theURL);
HTTPClient http;
http.begin(theURL);
int httpCode = http.GET(); // HTTP GET
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\r\n", httpCode);
// If the GET was successful, read the data
if(httpCode == HTTP_CODE_OK) // Check for code 200
{
payloadSize = http.getSize();
Serial.printf("Server returned %d bytes\r\n", payloadSize);
payload = http.getString(); // Get the payload
// Pretty-print the payload as HEX
/*
int i;
for(i = 0; i < payloadSize; i++)
{
if (payload[i] < 0x10) // Print leading zero
Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
if ((i % 16) == 15)
Serial.println();
}
if ((i % 16) != 15)
Serial.println();
*/
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\r\n", http.errorToString(httpCode).c_str());
}
http.end();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Speed things up by setting setI2CpollingWait to 1ms
myGNSS.setI2CpollingWait(1);
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data
myGNSS.setAckAiding(1);
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the RTC time to the module
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
if(getLocalTime(&timeinfo))
{
// Provide time assistance. Use the UBX_MGA_ACK_DATA0 acknowledgements. Set tAccS to 2 seconds.
myGNSS.setUTCTimeAssistance(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, 0, 2, 0, 0, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100);
}
else
{
Serial.println("Failed to obtain time. This will not work well. The GNSS needs accurate time to start up quickly.");
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// If desired - push initial position assistance to the module
#ifndef USE_SERVER_ASSISTANCE
// Use 55 degrees (*10^7) north, 1 degree (*10^7) west, 100m (10000cm) altitude, 100km (10000000cm) accuracy. Replace these with your position.
// The units for lat and lon are degrees * 1e-7 (WGS84)
// The units for alt (WGS84) and posAcc (stddev) are cm.
myGNSS.setPositionAssistanceLLH(550000000, -10000000, 10000, 10000000, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100);
// We could use setPositionAssistanceXYZ instead if needed.
#endif
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow data to the module - WITHOUT THE TIME
if (payloadSize > 0)
{
// Push the AssistNow data. Use the UBX_MGA_ACK_DATA0 acknowledgements.
// The 'true' parameter tells pushAssistNowData not to push any time data from the payload.
myGNSS.pushAssistNowData(true, payload, (size_t)payloadSize, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Disconnect the WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Serial.println(F("WiFi disconnected"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Avoid pounding the I2C bus by setting setI2CpollingWait to 125ms
myGNSS.setI2CpollingWait(125);
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,232 @@
/*
Use ESP32 WiFi to get AssistNow Online data from u-blox Thingstream
By: SparkFun Electronics / Paul Clark
Date: November 24th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Online data from u-blox Thingstream over WiFi
and push it to a u-blox module using Serial.
You will need to have a token to be able to access Thingstream. See the AssistNow README for more details.
Update secrets.h with your:
- WiFi credentials
- AssistNow token string
Uncomment the "#define USE_MGA_ACKs" below to test the more robust method of using the
UBX_MGA_ACK_DATA0 acknowledgements to confirm that each MGA message has been accepted.
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
//#define USE_MGA_ACKs // Uncomment this line to use the UBX_MGA_ACK_DATA0 acknowledgements
#include <WiFi.h>
#include <HTTPClient.h>
#include "secrets.h"
const char assistNowServer[] = "https://online-live1.services.u-blox.com";
//const char assistNowServer[] = "https://online-live2.services.u-blox.com"; // Alternate server
const char getQuery[] = "GetOnlineData.ashx?";
const char tokenPrefix[] = "token=";
const char tokenSuffix[] = ";";
const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal
const char getDataType[] = "datatype=eph,alm,aux;"; // Data type can be: eph,alm,aux,pos
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#define mySerial Serial1 // Use Serial1 to communicate with the GNSS module
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println(F("AssistNow Example"));
while (Serial.available()) Serial.read(); // Empty the serial buffer
Serial.println(F("Press any key to begin..."));
while (!Serial.available()); // Wait for a keypress
mySerial.begin(9600); // Use 9600 baud (for u-blox M8)
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to the GNSS.
if (myGNSS.begin(mySerial) == false) //Connect to the Ublox module using mySerial
{
Serial.println(F("u-blox GPS not detected. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use HTTP GET to receive the AssistNow_Online data
const int URL_BUFFER_SIZE = 256;
char theURL[URL_BUFFER_SIZE]; // This will contain the HTTP URL
int payloadSize = 0; // This will be updated with the length of the data we get from the server
String payload; // This will store the data we get from the server
// Assemble the URL
// Note the slash after the first %s (assistNowServer)
snprintf(theURL, URL_BUFFER_SIZE, "%s/%s%s%s%s%s%s",
assistNowServer,
getQuery,
tokenPrefix,
myAssistNowToken,
tokenSuffix,
getGNSS,
getDataType);
Serial.print(F("HTTP URL is: "));
Serial.println(theURL);
HTTPClient http;
http.begin(theURL);
int httpCode = http.GET(); // HTTP GET
// httpCode will be negative on error
if(httpCode > 0)
{
// HTTP header has been sent and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\r\n", httpCode);
// If the GET was successful, read the data
if(httpCode == HTTP_CODE_OK) // Check for code 200
{
payloadSize = http.getSize();
Serial.printf("Server returned %d bytes\r\n", payloadSize);
payload = http.getString(); // Get the payload
// Pretty-print the payload as HEX
/*
int i;
for(i = 0; i < payloadSize; i++)
{
if (payload[i] < 0x10) // Print leading zero
Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
if ((i % 16) == 15)
Serial.println();
}
if ((i % 16) != 15)
Serial.println();
*/
}
}
else
{
Serial.printf("[HTTP] GET... failed, error: %s\r\n", http.errorToString(httpCode).c_str());
}
http.end();
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Push the AssistNow data to the module
if (payloadSize > 0)
{
// Uncomment the next line to enable the 'major' debug messages on Serial so you can see what AssistNow data is being sent
//myGNSS.enableDebugging(Serial, true);
#ifndef USE_MGA_ACKs
// ***** Don't use the UBX_MGA_ACK_DATA0 messages *****
// Push all the AssistNow data. Don't use UBX_MGA_ACK_DATA0's. Use the default delay of 7ms between messages.
myGNSS.pushAssistNowData(payload, (size_t)payloadSize);
#else
// ***** Use the UBX_MGA_ACK_DATA0 messages *****
// Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data
myGNSS.setAckAiding(1);
// Push all the AssistNow data.
// We have called setAckAiding(1) to instruct the module to return MGA-ACK messages.
// So, we could set the pushAssistNowData mgaAck parameter to SFE_UBLOX_MGA_ASSIST_ACK_YES.
// But, just for giggles, let's use SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE just to confirm that the
// MGA-ACK messages are actually enabled.
// Wait for up to 100ms for each ACK to arrive! 100ms is a bit excessive... 7ms is nearer the mark.
myGNSS.pushAssistNowData(payload, (size_t)payloadSize, SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE, 100);
#endif
}
Serial.println(F("Here we go!"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
// Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
Serial.println();
}

View file

@ -0,0 +1,6 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//Your AssistNow token
const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ";

View file

@ -0,0 +1,186 @@
/*
Use ESP32 WiFi to get AssistNow Online (MGA) data from PointPerfect (broker) as a Client using MQTT
By: Paul Clark / SparkFun
Date: March 9th, 2022
Based on original code by: u-blox AG / Michael Ammann
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain AssistNow Online (MGA) data from a PointPerfect Broker over WiFi
and push it over I2C to a ZED-F9x.
It's confusing, but the Arduino is acting as a 'client' to the PointPerfect service.
You will need to have a valid u-blox Thingstream account and have a PointPerfect Thing and payed plan.
To sign up, go to: https://portal.thingstream.io/app/location-services/things
This is a proof of concept to show how to connect via MQTT to get AssistNow MGA data.
For more information about MQTT, SPARTN and PointPerfect Correction Services
please see: https://www.u-blox.com/en/product/pointperfect
Feel like supporting open source hardware?
Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
SparkFun GPS Breakout - ZOE-M8Q (Qwiic): https://www.sparkfun.com/products/15193
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoMqttClient.h> // Click here to get the library: http://librarymanager/All#ArduinoMqttClient
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//Global variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastReceived_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(F("PointPerfect AssistNow testing"));
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN);
myGNSS.setNavigationFrequency(1); //Set output in Hz.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.print(F("WiFi connected with IP: "));
Serial.println(WiFi.localIP());
while (Serial.available()) Serial.read();
}
void loop()
{
if (Serial.available())
{
beginClient();
while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
}
Serial.println(F("Press any key to start MQTT Client."));
delay(1000);
}
WiFiClientSecure wifiClient = WiFiClientSecure();
MqttClient mqttClient(wifiClient);
void mqttMessageHandler(int messageSize)
{
const uint16_t mqttLimit = 512;
uint8_t *mqttData = new uint8_t[mqttLimit]; // Allocate memory to hold the MQTT data
if (mqttData == NULL)
{
Serial.println(F("Memory allocation for mqttData failed!"));
return;
}
Serial.print(F("Pushing data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
{
uint16_t mqttCount = 0;
while (mqttClient.available())
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
mqttData[mqttCount++] = ch;
if (mqttCount == mqttLimit)
break;
}
if (mqttCount > 0)
{
//Push KEYS or SPARTN data to GNSS module over I2C
myGNSS.pushRawData(mqttData, mqttCount, false);
lastReceived_ms = millis();
}
}
delete[] mqttData;
}
//Connect to MQTT broker, receive MGA, and push to ZED module over I2C
void beginClient()
{
Serial.println(F("Subscribing to Broker. Press key to stop"));
delay(10); //Wait for any serial to arrive
while (Serial.available()) Serial.read(); //Flush
while (Serial.available() == 0)
{
//Connect if we are not already
if (wifiClient.connected() == false)
{
// Connect to AWS IoT
wifiClient.setCACert(AWS_CERT_CA);
wifiClient.setCertificate(AWS_CERT_CRT);
wifiClient.setPrivateKey(AWS_CERT_PRIVATE);
mqttClient.setId(MQTT_CLIENT_ID);
mqttClient.setKeepAliveInterval(60*1000);
mqttClient.setConnectionTimeout( 5*1000);
if (!mqttClient.connect(AWS_IOT_ENDPOINT, AWS_IOT_PORT)) {
Serial.print(F("MQTT connection failed! Error code = "));
Serial.println(mqttClient.connectError());
return;
} else {
Serial.println(F("You're connected to the PointPerfect MQTT broker: "));
Serial.println(AWS_IOT_ENDPOINT);
// Subscribe to MQTT and register a callback
Serial.println(F("Subscribe to Topics"));
mqttClient.onMessage(mqttMessageHandler);
mqttClient.subscribe(MQTT_TOPIC_ASSISTNOW);
lastReceived_ms = millis();
} //End attempt to connect
} //End connected == false
else {
mqttClient.poll();
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceived_ms > maxTimeBeforeHangup_ms)
{
Serial.println(F("Timeout. Disconnecting..."));
if (mqttClient.connected() == true)
mqttClient.stop();
return;
}
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
wifiClient.stop();
}

View file

@ -0,0 +1,39 @@
//Your WiFi credentials
const char ssid[] = "<YOUR SSID>";
const char password[] = "<YOUR PASSWORD>";
// Below infomation you can set after signing up with u-blox Thingstream portal
// and after add a new New PointPerfect Thing
// https://portal.thingstream.io/app/location-services/things
// in the new PointPerfect Thing you go to the credentials page and copy past the values and certificate into this.
// <Your PointPerfect Thing> -> Credentials -> Hostname
const char AWS_IOT_ENDPOINT[] = "pp.services.u-blox.com";
const unsigned short AWS_IOT_PORT = 8883;
// <Your PointPerfect Thing> -> Credentials -> AssistNow (MGA) topic
const char MQTT_TOPIC_ASSISTNOW[] = "/pp/ubx/mga";
// <Your PointPerfect Thing> -> Credentials -> Client Id
static const char MQTT_CLIENT_ID[] = "<ADD YOUR CLIENT ID HERE>";
// <Your PointPerfect Thing> -> Credentials -> Amazon Root Certificate
static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
<ADD YOUR CERTICICATE HERE>
-----END CERTIFICATE-----
)EOF";
// <Your PointPerfect Thing> -> Credentials -> Client Certificate
static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
<ADD YOUR CERTICICATE HERE>
-----END CERTIFICATE-----
)KEY";
// Get this from Thingstream Portal
// <Your PointPerfect Thing> -> Credentials -> Client Key
static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
<ADD YOUR KEY HERE>
-----END RSA PRIVATE KEY-----
)KEY";

View file

@ -0,0 +1,222 @@
# SparkFun u-blox Arduino GNSS Library - AssistNow<sup>TM</sup>
v2.1.0 of the library adds support for u-blox [AssistNow<sup>TM</sup> Assisted GNSS (A-GNSS)](https://www.u-blox.com/en/product/assistnow) which can dramatically reduce the time-to-first-fix.
To use AssistNow Online or AssistNow Offline, you will need a token to access the u-blox Thingstream server. See [below](#AssistNow-Service-Token) for details.
## AssistNow<sup>TM</sup> Online
With AssistNow Online, an Internet connected host downloads assistance data from the u-blox AssistNow Online service to the receiver at system start-up. AssistNow Online data is valid for 2 - 4 hours; beyond that fresh data must be downloaded.
Please see the [AssistNow_Online](./AssistNow_Online) examples for more details. These examples were written for the ESP32, but will run on other platforms too.
The new functions we've added to the library to support AssistNow Online are described [Support for AssistNow below](#Support-for-AssistNow).
## AssistNow<sup>TM</sup> Offline
With the AssistNow Offline service, users can download long-term orbit data over the Internet at their convenience. The orbit data can be stored in the memory of the application processor. The function requires no connectivity at system start-up, enabling a position fix within seconds, even when no network is available. AssistNow Offline offers augmentation for up to 35 days.
Please see the [AssistNow_Offline](./AssistNow_Offline) examples for more details. These examples were written for the ESP32, but will run on other platforms too.
**Note: AssistNow Offline is not supported by the ZED-F9P. "The ZED-F9P supports AssistNow Online only."**
The new functions we've added to the library to support AssistNow Offline are described [Support for AssistNow](#Support-for-AssistNow) and [Additional Support for AssistNow Offline](#Additional-Support-for-AssistNow-Offline).
## AssistNow<sup>TM</sup> Autonomous
AssistNow Autonomous provides aiding information without the need for a host or external network connection. Based on previous broadcast satellite ephemeris data downloaded to and stored by the GNSS receiver, AssistNow Autonomous automatically generates accurate predictions of satellite orbital data (“AssistNow Autonomous data”) that is usable for future GNSS position fixes.
The benefits of AssistNow Autonomous are:
* Faster fix in situations where GNSS satellite signals are weak
* No connectivity required
* Compatible with AssistNow Online (can work stand-alone, or in tandem with AssistNow Online service)
* No integration effort; calculations are done in the background, transparent to the user
AssistNow Autonomous offers augmentation for up to 6 days.
Please see the [AssistNow_Autonomous](./AssistNow_Autonomous) examples for more details.
**Note: AssistNow Autonomous does not work on the ZED-F9P. "The ZED-F9P supports AssistNow Online only."**
The new functions we've added to the library to support AssistNow Autonomous are described [Support for AssistNow Autonomous below](#Support-for-AssistNow-Autonomous).
## AssistNow Service Token
To be able to use AssistNow Online or AssistNow Offline, you will need a token to access the u-blox Thingstream server.
The following u-blox resources contain useful information:
* [AssistNow - u-blox A-GNSS services](https://www.u-blox.com/en/product/assistnow)
* [AssistNow Product Summary](https://www.u-blox.com/sites/default/files/products/documents/AssistNow_ProductSummary_UBX-13003352.pdf)
* [AssistNow User Guide](https://www.u-blox.com/sites/default/files/products/documents/MultiGNSS-Assistance_UserGuide_%28UBX-13004360%29.pdf)
* [Thingstream Pricing](https://portal.thingstream.io/pricing)
You can apply for a _free_ AssistNow Service Evaluation Token by completing the request form:
* [AssistNow Service evaluation token request form](https://www.u-blox.com/en/assistnow-service-evaluation-token-request-form)
The _free_ AssistNow Developer token entitles you to:
* AssistNow Online Developer: 100K free location requests per month. Capped.
* AssistNow Offline Developer: 20K free location requests per month. Capped.
* CellLocate Developer: 5K free location requests per month. Capped.
The free token will expire after 90 days, but you can continue to use it beyond that by registering it on [Thingstream](https://portal.thingstream.io/).
## Initial Position Assistance
You can further decrease the time-to-first-fix by providing the receiver's approximate position - if known. There are two ways to do this:
* The position can be specified when requesting AssistNow Online data from the server:
* include the key name ```lat``` with the approximate user latitude in WGS 84 expressed in degrees and fractional degrees. Must be in range -90 to 90. Example: ```lat=47.2;```
* include the key name ```lon``` with the approximate user longitude in WGS 84 expressed in degrees and fractional degrees. Must be in range -180 to 180. Example: ```lon=8.55;```
* include the key name ```alt``` with the approximate user altitude above WGS 84 Ellipsoid in meters. If this value is not provided, the server assumes an altitude of 0 meters. Must be in range -1000 to 50000
* include the key name ```pacc``` with the approximate accuracy of submitted position in meters. If this value is not provided, the server assumes an accuracy of 300 km. Must be in range 0 to 6000000
* the position assistance data will then be automatically included in the AssistNow Online data
* Provide initial position assistance data by calling one of:
* <b>bool setPositionAssistanceXYZ(int32_t ecefX, int32_t ecefY, int32_t ecefZ, uint32_t posAcc, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* The units for ```ecefX/Y/Z``` and ```posAcc``` (stddev) are cm
* <b>bool setPositionAssistanceLLH(int32_t lat, int32_t lon, int32_t alt, uint32_t posAcc, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* The units for ```lat``` and ```lon``` are degrees * 1e-7 (WGS84). The units for ```alt``` (WGS84) and ```posAcc``` (stddev) are cm (not m)
## Support for AssistNow
```pushAssistNowData``` allows AssistNow Online, Offline or Autonomous data to be pushed to the module. As the ESP32 HTTP GET function returns a ```String```, we've included overloaded functions which allow you to pass the data as a ```String``` or as ```const uint8_t *```.
The String-based function declarations are:
* <b>size_t pushAssistNowData(const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* <b>size_t pushAssistNowData(bool skipTime, const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* <b>size_t pushAssistNowData(size_t offset, bool skipTime, const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
The const uint8_t * function declarations are:
* <b>size_t pushAssistNowData(const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* <b>size_t pushAssistNowData(bool skipTime, const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
* <b>size_t pushAssistNowData(size_t offset, bool skipTime, const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
```dataBytes``` is a pointer to the AssistNow data.
<br>
```numDataBytes``` is the length of the AssistNow data.
<br>
```pushAssistNowData``` pushes individual packets of data to the u-blox module. Sending all of the data contiguously would overload the module, so ```pushAssistNowData``` can either:
* insert a small delay between each packet (the default is 7ms)
* or use the ```UBX-MGA-ACK-DATA0``` acknowledgement message to acknowledge each packet
```mgaAck``` controls which method is used.
* if ```mgaAck``` is ```SFE_UBLOX_MGA_ASSIST_ACK_NO``` (**default**), a delay of ```maxWait``` milliseconds is inserted between each packet. ```maxWait``` defaults to 7ms.
* if ```mgaAck``` is ```SFE_UBLOX_MGA_ASSIST_ACK_YES```, acknowledgement messages will be expected with a _timeout_ of ```maxWait``` milliseconds. The default timeout is again 7ms, but you can change this if required by passing a different value.
* if ```mgaAck``` is ```SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE```, the code will poll the module to enquire if the acknowledgement messages are enabled. If they are, they will be used. If not, a delay is used.
```setAckAiding``` enables or disables the acknowledgement messages. By default they are disabled. ```setAckAiding(1)``` will enable them. ```setAckAiding(0)``` will disable them again.
* <b>bool setAckAiding(uint8_t ackAiding, uint16_t maxWait);</b>
```getAckAiding``` returns 1 if the acknowledgement messages are enabled, 0 if they are disabled. 255 indicates an error or timeout.
* <b>uint8_t getAckAiding(uint16_t maxWait);</b>
```pushAssistNowData``` returns the number of _bytes_ pushed (not the number of _packets_). The return value should be equal to ```numDataBytes``` if all data was valid and pushed successfully.
AssistNow Online data is valid for 2-4 hours. 'Stale' data can be re-used but:
* ```pushAssistNowData``` needs to be told to skip the time information contained in the AssistNow data
* the user needs to provide the module with UTC time separately
The ```skipTime``` parameter tells ```pushAssistNowData``` to skip any time information in the data. ```skipTime``` is bool. Set it to ```true``` to skip the time information.
<br>
UTC time can be pushed to the module first by calling ```setUTCTimeAssistance```:
* <b>bool setUTCTimeAssistance(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, uint32_t nanos, uint16_t tAccS, uint32_t tAccNs, uint8_t source, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait);</b>
Only the ```year```, ```month```, ```day```, ```hour```, ```minute``` and ```second``` parameters are mandatory. The others default to sensible values. Again ```mgaAck``` and ```maxWait``` control if a delay is used when configuring the time, or if an acknowledgement message will be expected.
<br>
```nanos``` (nanoseconds), ```tAccS``` (time accuracy estimate (seconds)), ```tAccNs``` (time accuracy estimate (nanoseconds)) and ```source``` (if a clock signal will be provided on EXT_INT) are optional, but are available for advanced users.
<br>
```year``` numbering starts at 0; 2021 is 2021, not 121 (years since 1900). ```month``` and ```day``` numbering starts at 1, not 0.
<br>
Call ```setUTCTimeAssistance``` _before_ ```pushAssistNowData```.
## Additional Support for AssistNow Offline
AssistNow Offline data downloaded from the u-blox server can contain 1-5 weeks of data. However, only the data for _today_ should be pushed the module. Sending data for past or future days will confuse the module.
```findMGAANOForDate``` can be used to find the location of the start of the UBX-MGA-ANO data for the specified date within the offline data. That location can then be passed to ```pushAssistNowData``` using the ```offset``` parameter.
* <b>size_t findMGAANOForDate(const uint8_t *dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture);</b>
The sequence of events is:
* call ```findMGAANOForDate``` passing the ```year```, ```month``` and ```day``` for today. ```findMGAANOForDate``` will return the location / offset of the data for today within the offline data.
* call ```findMGAANOForDate``` again passing the ```year```, ```month``` and ```day``` for _today_ but also set ```daysIntoFuture``` to 1. ```findMGAANOForDate``` will then return the location / offset of the data for _tomorrow_ (one day into the future).
* call ```pushAssistNowData``` setting:
* ```offset``` to the location (offset) of today's data within the offline data
* ```skipTime``` to ```true```
* ```numDataBytes``` to ((tomorrow's location) - (today's location)). Only the offline data for today will be pushed.
```findMGAANOForDate``` will return a value of numDataBytes if the data for the chosen day cannot be found.
<br>
Again, call ```setUTCTimeAssistance``` _before_ ```pushAssistNowData```.
## Support for AssistNow Autonomous
AssistNow Autonomous is disabled by default. You can enable it by calling ```setAopCfg``` and check if it is enabled by calling ```getAopCfg```:
* <b>uint8_t getAopCfg(uint16_t maxWait);</b>
* <b>bool SFE_UBLOX_GNSS::setAopCfg(uint8_t aopCfg, uint16_t aopOrbMaxErr, uint16_t maxWait)</b>
```getAopCfg``` will return 1 if AssistNow Autonomous is enabled, 0 if disabled. 255 indicates an error or timeout.
```setAopCfg``` has two parameters:
* set ```aopCfg``` to 1 to enable AssistNow Autonomous, or 0 to disable it
* ```aopOrbMaxErr``` is used to set the 'lifetime' of the AssistNow data. It is recommended to set aopOrbMaxErr to 0 (the default value). This instructs the module to use the firmware default value that corresponds to a default orbit data validity of approximately three days (for GPS satellites observed once) and up to six days (for GPS and GLONASS satellites observed multiple times over a period of at least half a day).
Once AssistNow Autonomous is enabled, you can monitor its status via the ```status``` field in the UBX-NAV-AOPSTATUS message. You can read the ```status``` by calling the helper function ```getAOPSTATUSstatus```. It will return zero when the AssistNow Autonomous data collection is idle. Non-zero values indicate that data collection is in progress. Only power-off the receiver when the subsystem is idle (that is, when the status shows a steady zero).
* <b>uint8_t getAOPSTATUSstatus(uint16_t maxWait);</b>
* <b>uint8_t getAOPSTATUSuseAOP(uint16_t maxWait);</b>
We have included full 'auto' support for UBX-NAV-AOPSTATUS, so you can have the message delivered periodically, add a callback for it, and/or log it to the file buffer:
* <b>bool getAOPSTATUS(uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUS(bool enabled, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUS(bool enabled, bool implicitUpdate, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUScallback(void (*callbackPointer)(UBX_NAV_AOPSTATUS_data_t), uint16_t maxWait);</b>
* <b>bool assumeAutoAOPSTATUS(bool enabled, bool implicitUpdate);</b>
* <b>void flushAOPSTATUS();</b>
* <b>void logAOPSTATUS(bool enabled);</b>
You can also monitor the AssistNow Autonomous satellite information via the UBX-NAV-SAT message. Again, we have included full 'auto' support for UBX-NAV-SAT. UBX-NAV-SAT contains useful information for each individual satellite which the module has acquired: carrier to noise ratio (signal strength); elevation; azimuth; pseudorange residual; quality indication, health; ephemeris available; almanac available; **AssistNow Offline data availability**; and more. The data can be analyzed using a callback. Please see the AssistNowAutonomous examples for more details.
* <b>bool getNAVSAT(uint16_t maxWait);</b>
* <b>bool setAutoNAVSAT(bool enabled, uint16_t maxWait);</b>
* <b>bool setAutoNAVSAT(bool enabled, bool implicitUpdate, uint16_t maxWait);</b>
* <b>bool setAutoNAVSATrate(uint8_t rate, bool implicitUpdate = true, uint16_t maxWait);</b>
* <b>bool setAutoNAVSATcallback(void (*callbackPointer)(UBX_NAV_NAVSAT_data_t), uint16_t maxWait);</b>
* <b>bool assumeAutoNAVSAT(bool enabled, bool implicitUpdate);</b>
* <b>void flushNAVSAT();</b>
* <b>void logNAVSAT(bool enabled);</b>
The AssistNow Autonomous data is stored in the module's RAM memory. If that RAM is Battery-Backed - all SparkFun GNSS boards include battery back-up - then the data will be available after the module is powered down and powered back up again. However, you can also read (poll) the navigation database and store the contents in processor memory. ```readNavigationDatabase``` allows you to do that:
* <b>size_t readNavigationDatabase(uint8_t *dataBytes, size_t maxNumDataBytes, uint16_t maxWait);</b>
Data is written to ```dataBytes```. Set ```maxNumDataBytes``` to the (maximum) size of dataBytes. If the database exceeds maxNumDataBytes, the excess bytes will be lost.
```readNavigationDatabase``` returns the number of database bytes written to ```dataBytes```. The return value will be equal to ```maxNumDataBytes``` if excess data was received.
```readNavigationDatabase``` will timeout after ```maxWait``` milliseconds - in case the final UBX-MGA-ACK was missed.
You can then write the database back into the module using ```pushAssistNowData```. Don't forget to call ```setUTCTimeAssistance``` _before_ ```pushAssistNowData```.
Note: UBX-MGA-DBD messages are only intended to be sent back to the same receiver that generated them. They are firmware-specific.

View file

@ -0,0 +1,125 @@
/*
Get the GPGGA NMEA sentence using getLatestNMEAGPGGA
By: Paul Clark
SparkFun Electronics
Date: January 12th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
** Please note: this example will not run on Arduino Uno. See https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/README.md#memory-usage
This example shows how to turn on/off the NMEA sentences being output over I2C.
It then demonstrates how to use the new getLatestNMEAGPGGA function to retrieve the latest GPGGA message.
getLatestNMEAGPGGA returns immediately - it is not blocking.
It returns:
0 if no data is available
1 if the data is valid but is stale (you have read it before)
2 if the data is valid and fresh
If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
The library includes a getLatestNMEAGNGGA function too.
This example shows how to use both functions - and how to change the Talker ID so the GNGGA messages become GPGGA.
This example turns off all sentences except for GGA.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
Serial.println(F("SparkFun u-blox GNSS Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
//Disable or enable various NMEA sentences over the I2C interface
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C); // Several of these are on by default on ublox board so let's disable them
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); // Leave only GGA enabled at current navigation rate
// Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
//myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
Serial.println(F("Messages configured"));
//myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
}
void loop()
{
// getLatestNMEAGPGGA calls checkUblox for us. We don't need to do it here
NMEA_GGA_data_t data; // Storage for the GPGGA data
uint8_t result = myGNSS.getLatestNMEAGPGGA(&data); // Get the latest GPGGA data (if any)
if (result == 0)
{
Serial.println(F("No GPGGA data available"));
}
else if (result == 1)
{
Serial.println(F("GPGGA data is available but is stale"));
}
else // if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GPGGA: Length: "));
Serial.print(data.length);
Serial.print(F("\tData: "));
Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
}
result = myGNSS.getLatestNMEAGNGGA(&data); // Get the latest GNGGA data (if any)
if (result == 0)
{
Serial.println(F("No GNGGA data available"));
}
else if (result == 1)
{
Serial.println(F("GNGGA data is available but is stale"));
}
else // if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GNGGA: Length: "));
Serial.print(data.length);
Serial.print(F("\tData: "));
Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
}
delay(250);
}

View file

@ -0,0 +1,119 @@
/*
Get the latest GPGGA / GNGGA NMEA sentence using callbacks
By: Paul Clark
SparkFun Electronics
Date: January 12th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
** Please note: this example will not run on Arduino Uno. See https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/README.md#memory-usage
This example shows how to turn on/off the NMEA sentences being output over I2C.
It then demonstrates how to get the latest GPGGA or GNGGA message autonomously using callbacks.
If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
This example shows how to change the Talker ID so the GNGGA messages become GPGGA.
It also shows how to enable "high precision mode" to include extra decimal places in the GGA messages.
This example turns off all sentences except for GGA.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printGPGGA will be called when new GPGGA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback
// / _____ This _must_ be NMEA_GGA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGPGGA(NMEA_GGA_data_t *nmeaData)
{
Serial.print(F("\r\nGPGGA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGNGGA will be called if new GNGGA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNGGAcallback
// / _____ This _must_ be NMEA_GGA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGNGGA(NMEA_GGA_data_t *nmeaData)
{
Serial.print(F("\r\nGNGGA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
void setup()
{
Serial.begin(115200);
Serial.println(F("SparkFun u-blox GNSS Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
// Disable or enable various NMEA sentences over the I2C interface
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C); // Several of these are on by default on ublox board so let's disable them
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); // Leave only GGA enabled at current navigation rate
// Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
//myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
Serial.println(F("Messages configured"));
//myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
// Set up the callback for GPGGA
myGNSS.setNMEAGPGGAcallbackPtr(&printGPGGA);
// Set up the callback for GNGGA
myGNSS.setNMEAGNGGAcallbackPtr(&printGNGGA);
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,183 @@
/*
Get the NMEA sentence using getLatestNMEAGPxxx (GGA, VTG, RMC, ZDA)
By: Paul Clark
SparkFun Electronics
Date: March 2nd, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
** Please note: this example will not run on Arduino Uno. See https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/README.md#memory-usage
This example shows how to turn on/off the NMEA sentences being output over I2C.
It then demonstrates how to use the new getLatestNMEAGPxxx function to retrieve the latest GPGGA message.
getLatestNMEAGPxxx returns immediately - it is not blocking.
It returns:
0 if no data is available
1 if the data is valid but is stale (you have read it before)
2 if the data is valid and fresh
If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
The library includes getLatestNMEAGNxxx functions too.
This example shows how to use both functions - and how to change the Talker ID so the GNGGA messages become GPGGA 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:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
Serial.println(F("SparkFun u-blox GNSS Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
//Disable or enable various NMEA sentences over the I2C interface
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_ZDA, COM_PORT_I2C);
// Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
//myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
Serial.println(F("Messages configured"));
//myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
}
void loop()
{
// getLatestNMEAGPGGA calls checkUblox for us. We don't need to do it here
NMEA_GGA_data_t dataGGA; // Storage for the GPGGA data
uint8_t result = myGNSS.getLatestNMEAGPGGA(&dataGGA); // Get the latest GPGGA data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GPGGA: Length: "));
Serial.print(dataGGA.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataGGA.nmea); // .nmea is printable (NULL-terminated)
}
result = myGNSS.getLatestNMEAGNGGA(&dataGGA); // Get the latest GNGGA data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GNGGA: Length: "));
Serial.print(dataGGA.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataGGA.nmea); // .nmea is printable (NULL-terminated)
}
// getLatestNMEAGPVTG calls checkUblox for us. We don't need to do it here
NMEA_VTG_data_t dataVTG; // Storage for the GPVTG data
result = myGNSS.getLatestNMEAGPVTG(&dataVTG); // Get the latest GPVTG data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GPVTG: Length: "));
Serial.print(dataVTG.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataVTG.nmea); // .nmea is printable (NULL-terminated)
}
result = myGNSS.getLatestNMEAGNVTG(&dataVTG); // Get the latest GNVTG data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GNVTG: Length: "));
Serial.print(dataVTG.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataVTG.nmea); // .nmea is printable (NULL-terminated)
}
// getLatestNMEAGPRMC calls checkUblox for us. We don't need to do it here
NMEA_RMC_data_t dataRMC; // Storage for the GPRMC data
result = myGNSS.getLatestNMEAGPRMC(&dataRMC); // Get the latest GPRMC data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GPRMC: Length: "));
Serial.print(dataRMC.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataRMC.nmea); // .nmea is printable (NULL-terminated)
}
result = myGNSS.getLatestNMEAGNRMC(&dataRMC); // Get the latest GNRMC data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GNRMC: Length: "));
Serial.print(dataRMC.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataRMC.nmea); // .nmea is printable (NULL-terminated)
}
// getLatestNMEAGPZDA calls checkUblox for us. We don't need to do it here
NMEA_ZDA_data_t dataZDA; // Storage for the GPZDA data
result = myGNSS.getLatestNMEAGPZDA(&dataZDA); // Get the latest GPZDA data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GPZDA: Length: "));
Serial.print(dataZDA.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataZDA.nmea); // .nmea is printable (NULL-terminated)
}
result = myGNSS.getLatestNMEAGNZDA(&dataZDA); // Get the latest GNZDA data (if any)
if (result == 2)
{
// Data contains .length and .nmea
Serial.print(F("Latest GNZDA: Length: "));
Serial.print(dataZDA.length);
Serial.print(F("\tData: "));
Serial.print((const char *)dataZDA.nmea); // .nmea is printable (NULL-terminated)
}
delay(250);
}

View file

@ -0,0 +1,226 @@
/*
Get the latest GGA, VTG, RMC, ZDA NMEA sentence using callbacks
By: Paul Clark
SparkFun Electronics
Date: March 2nd, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
** Please note: this example will not run on Arduino Uno. See https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/blob/main/README.md#memory-usage
This example shows how to turn on/off the NMEA sentences being output over I2C.
It then demonstrates how to get the latest GGA, VTG, RMC or ZDA message autonomously using callbacks.
If the module is using multiple GNSS constellations, the GGA message will be prefixed with Talker ID "GN" instead of "GP".
This example shows how to change the Talker ID so the GNGGA messages become GPGGA.
It also shows how to enable "high precision mode" to include extra decimal places in the GGA messages.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printGPGGA will be called when new GPGGA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback
// / _____ This _must_ be NMEA_GGA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGPGGA(NMEA_GGA_data_t *nmeaData)
{
Serial.print(F("\r\nGPGGA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGNGGA will be called if new GNGGA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNGGAcallback
// / _____ This _must_ be NMEA_GGA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGNGGA(NMEA_GGA_data_t *nmeaData)
{
Serial.print(F("\r\nGNGGA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGPVTG will be called when new GPVTG NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_VTG_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPVTGcallback
// / _____ This _must_ be NMEA_VTG_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGPVTG(NMEA_VTG_data_t *nmeaData)
{
Serial.print(F("\r\nGPVTG: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGNVTG will be called if new GNVTG NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_VTG_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNVTGcallback
// / _____ This _must_ be NMEA_VTG_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGNVTG(NMEA_VTG_data_t *nmeaData)
{
Serial.print(F("\r\nGNVTG: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGPRMC will be called when new GPRMC NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_RMC_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPRMCcallback
// / _____ This _must_ be NMEA_RMC_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGPRMC(NMEA_RMC_data_t *nmeaData)
{
Serial.print(F("\r\nGPRMC: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGNRMC will be called if new GNRMC NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_RMC_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNRMCcallback
// / _____ This _must_ be NMEA_RMC_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGNRMC(NMEA_RMC_data_t *nmeaData)
{
Serial.print(F("\r\nGNRMC: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGPZDA will be called when new GPZDA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_ZDA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPZDAcallback
// / _____ This _must_ be NMEA_ZDA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGPZDA(NMEA_ZDA_data_t *nmeaData)
{
Serial.print(F("\r\nGPZDA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
// Callback: printGNZDA will be called if new GNZDA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_ZDA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGNZDAcallback
// / _____ This _must_ be NMEA_ZDA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printGNZDA(NMEA_ZDA_data_t *nmeaData)
{
Serial.print(F("\r\nGNZDA: Length: "));
Serial.print(nmeaData->length);
Serial.print(F("\tData: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
}
void setup()
{
Serial.begin(115200);
Serial.println(F("SparkFun u-blox GNSS Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
// Disable or enable various NMEA sentences over the I2C interface
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C);
myGNSS.enableNMEAMessage(UBX_NMEA_ZDA, COM_PORT_I2C);
// Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
//myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_DEFAULT); // Uncomment this line to restore the default main talker ID
myGNSS.setHighPrecisionMode(true); // Enable High Precision Mode - include extra decimal places in the GGA messages
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
Serial.println(F("Messages configured"));
//myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
// Set up the callback for GPGGA
myGNSS.setNMEAGPGGAcallbackPtr(&printGPGGA);
// Set up the callback for GNGGA
myGNSS.setNMEAGNGGAcallbackPtr(&printGNGGA);
// Set up the callback for GPVTG
myGNSS.setNMEAGPVTGcallbackPtr(&printGPVTG);
// Set up the callback for GNVTG
myGNSS.setNMEAGNVTGcallbackPtr(&printGNVTG);
// Set up the callback for GPRMC
myGNSS.setNMEAGPRMCcallbackPtr(&printGPRMC);
// Set up the callback for GNRMC
myGNSS.setNMEAGNRMCcallbackPtr(&printGNRMC);
// Set up the callback for GPZDA
myGNSS.setNMEAGPZDAcallbackPtr(&printGPZDA);
// Set up the callback for GNZDA
myGNSS.setNMEAGNZDAcallbackPtr(&printGNZDA);
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,196 @@
/*
Callback Example: ESF RAW (100Hz!)
By: Paul Clark
SparkFun Electronics
Date: September 8th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the External Sensor Fusion RAW IMU sensor messages on the NEO-M8U / ZED-F9R and
uses callbacks to process and display the ESF data automatically.
Notes:
On the ZED-F9R, each ESF RAW message contains _one_ set of IMU sensor data: seven readings in total (3 x Accel, 3 x Gyro, 1 x Temperature).
However, on the NEO-M8U, each message contains _ten_ sets of IMU sensor data, seventy readings in total.
The NEO-M8U data is all timestamped and it is possible to reconstruct the full data stream, you just need to do it
ten samples at a time...
Also, note that the sensor data is 24-bit signed (two's complement). You need to be careful when converting to int32_t.
Data will arrive at 100Hz! (10Hz x 10 on the NEO-M8U)
400kHz I2C is essential...
Serial printing needs to be kept short and the baud rate needs to be at least 230400.
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printESFRAWdata will be called when new ESF RAW data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_RAW_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFRAWcallback
// / _____ This _must_ be UBX_ESF_RAW_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printESFRAWdata(UBX_ESF_RAW_data_t *ubxDataStruct)
{
// ubxDataStruct->numEsfRawBlocks indicates how many sensor readings the UBX_ESF_RAW_data_t contains.
// On the ZED-F9R, numEsfRawBlocks will be 7: 3 x Accel, 3 x Gyro, 1 x Temperature.
// On the NEO-M8U, numEsfRawBlocks will be 70: 10 sets of sensor data. The sensor time tag (sTag)
// indicates the timing of each sample.
// Serial output will be approx. 110 bytes depending on how many digits are in the sensor readings.
// To keep up, Serial needs to be running at 100k baud minimum. 230400 is recommended.
uint32_t sTag = 0xFFFFFFFF; // Sensor time tag
// Only print the first seven sensor readings (on the NEO-M8U)
for (uint8_t i = 0; (i < ubxDataStruct->numEsfRawBlocks) && (i < 7); i++)
// For fun, and to prove it works, uncomment use this line instead to get the full 100Hz data on the NEO-M8U
//for (uint8_t i = 0; i < ubxDataStruct->numEsfRawBlocks; i++)
{
// Print sTag the first time - and also if it changes
if (sTag != ubxDataStruct->data[i].sTag)
{
sTag = ubxDataStruct->data[i].sTag;
Serial.print(F("Time:"));
Serial.println(sTag);
}
// Print the sensor data type
// From the M8 interface description:
// 0: None
// 1-4: Reserved
// 5: z-axis gyroscope angular rate deg/s * 2^-12 signed
// 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 11: speed m/s * 1e-3 signed
// 12: gyroscope temperature deg Celsius * 1e-2 signed
// 13: y-axis gyroscope angular rate deg/s * 2^-12 signed
// 14: x-axis gyroscope angular rate deg/s * 2^-12 signed
// 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed
// 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed
// 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed
switch (ubxDataStruct->data[i].data.bits.dataType)
{
case 5:
Serial.print(F("Zgyr:"));
break;
case 12:
Serial.print(F("Temp:"));
break;
case 13:
Serial.print(F("Ygyr:"));
break;
case 14:
Serial.print(F("Xgyr:"));
break;
case 16:
Serial.print(F("Xacc:"));
break;
case 17:
Serial.print(F("Yacc:"));
break;
case 18:
Serial.print(F("Zacc:"));
break;
default:
break;
}
// Gyro data
if ((ubxDataStruct->data[i].data.bits.dataType == 5) || (ubxDataStruct->data[i].data.bits.dataType == 13) || (ubxDataStruct->data[i].data.bits.dataType == 14))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float
rate /= 256.0; // Divide by 256 to undo the shift
rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s
Serial.println(rate);
}
// Accelerometer data
else if ((ubxDataStruct->data[i].data.bits.dataType == 16) || (ubxDataStruct->data[i].data.bits.dataType == 17) || (ubxDataStruct->data[i].data.bits.dataType == 18))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float force = signedUnsigned.signed32; // Extract the signed data. Convert to float
force /= 256.0; // Divide by 256 to undo the shift
force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2
Serial.println(force);
}
// Gyro Temperature
else if (ubxDataStruct->data[i].data.bits.dataType == 12)
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float
temperature /= 256.0; // Divide by 256 to undo the shift
temperature *= 0.01; // Convert from C * 1e-2 to C
Serial.println(temperature);
}
}
}
void setup()
{
Serial.begin(230400); // <--- Use >> 100k baud (see notes above)
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
Wire.setClock(400000); // <-- Use 400kHz I2C (ESSENTIAL)
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setI2CpollingWait(5); //Allow checkUblox to poll I2C data every 5ms to keep up with the ESF RAW messages
if (myGNSS.setAutoESFRAWcallbackPtr(&printESFRAWdata) == true) // Enable automatic ESF RAW messages with callback to printESFRAWdata
Serial.println(F("setAutoESFRAWcallback successful"));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
}

View file

@ -0,0 +1,209 @@
/*
u-blox Example: ESF RAW (100Hz!)
By: Paul Clark
SparkFun Electronics
Date: September 8th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the External Sensor Fusion RAW IMU sensor messages on the NEO-M8U / ZED-F9R and
shows how to access the ESF data in the loop - without using the callback.
Notes:
On the ZED-F9R, each ESF RAW message contains _one_ set of IMU sensor data: seven readings in total (3 x Accel, 3 x Gyro, 1 x Temperature).
However, on the NEO-M8U, each message contains _ten_ sets of IMU sensor data, seventy readings in total.
The NEO-M8U data is all timestamped and it is possible to reconstruct the full data stream, you just need to do it
ten samples at a time...
Also, note that the sensor data is 24-bit signed (two's complement). You need to be careful when converting to int32_t.
Data will arrive at 100Hz! (10Hz x 10 on the NEO-M8U)
400kHz I2C is essential...
Serial printing needs to be kept short and the baud rate needs to be at least 230400.
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printESFRAWdata will be called when new ESF RAW data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_RAW_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFRAWcallback
// / _____ This _must_ be UBX_ESF_RAW_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printESFRAWdata(UBX_ESF_RAW_data_t *ubxDataStruct)
{
Serial.println(F("Hey! The ESF RAW callback has been called!"));
}
void setup()
{
Serial.begin(230400); // <--- Use >> 100k baud (see notes above)
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
Wire.setClock(400000); // <-- Use 400kHz I2C (ESSENTIAL)
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setI2CpollingWait(5); //Allow checkUblox to poll I2C data every 5ms to keep up with the ESF RAW messages
if (myGNSS.setAutoESFRAWcallbackPtr(&printESFRAWdata) == true) // Enable automatic ESF RAW messages with callback to printESFRAWdata
Serial.println(F("setAutoESFRAWcallback successful"));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// Check if new ESF RAW data has arrived:
// If myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid is true, it indicates new ESF RAW data has been received and has been copied.
// automaticFlags.flags.bits.callbackCopyValid will be cleared automatically when the callback is called.
if (myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == true)
{
// But, we can manually clear the callback flag too. This will prevent the callback from being called!
myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid = false; // Comment this line if you still want the callback to be called
// myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks indicates how many sensor readings the UBX_ESF_RAW_data_t contains.
// On the ZED-F9R, numEsfRawBlocks will be 7: 3 x Accel, 3 x Gyro, 1 x Temperature.
// On the NEO-M8U, numEsfRawBlocks will be 70: 10 sets of sensor data. The sensor time tag (sTag)
// indicates the timing of each sample.
// Serial output will be approx. 110 bytes depending on how many digits are in the sensor readings.
// To keep up, Serial needs to be running at 100k baud minimum. 230400 is recommended.
uint32_t sTag = 0xFFFFFFFF; // Sensor time tag
// Only print the first seven sensor readings (on the NEO-M8U)
for (uint8_t i = 0; (i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks) && (i < 7); i++)
// For fun, and to prove it works, uncomment use this line instead to get the full 100Hz data on the NEO-M8U
//for (uint8_t i = 0; i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks; i++)
{
// Print sTag the first time - and also if it changes
if (sTag != myGNSS.packetUBXESFRAW->callbackData->data[i].sTag)
{
sTag = myGNSS.packetUBXESFRAW->callbackData->data[i].sTag;
Serial.print(F("Time:"));
Serial.println(sTag);
}
// Print the sensor data type
// From the M8 interface description:
// 0: None
// 1-4: Reserved
// 5: z-axis gyroscope angular rate deg/s * 2^-12 signed
// 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 11: speed m/s * 1e-3 signed
// 12: gyroscope temperature deg Celsius * 1e-2 signed
// 13: y-axis gyroscope angular rate deg/s * 2^-12 signed
// 14: x-axis gyroscope angular rate deg/s * 2^-12 signed
// 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed
// 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed
// 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed
switch (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType)
{
case 5:
Serial.print(F("Zgyr:"));
break;
case 12:
Serial.print(F("Temp:"));
break;
case 13:
Serial.print(F("Ygyr:"));
break;
case 14:
Serial.print(F("Xgyr:"));
break;
case 16:
Serial.print(F("Xacc:"));
break;
case 17:
Serial.print(F("Yacc:"));
break;
case 18:
Serial.print(F("Zacc:"));
break;
default:
break;
}
// Gyro data
if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 5) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 13) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 14))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float
rate /= 256.0; // Divide by 256 to undo the shift
rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s
Serial.println(rate);
}
// Accelerometer data
else if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 16) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 17) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 18))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float force = signedUnsigned.signed32; // Extract the signed data. Convert to float
force /= 256.0; // Divide by 256 to undo the shift
force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2
Serial.println(force);
}
// Gyro Temperature
else if (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 12)
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float
temperature /= 256.0; // Divide by 256 to undo the shift
temperature *= 0.01; // Convert from C * 1e-2 to C
Serial.println(temperature);
}
}
}
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. There will not be any in this example, unless you commented the line above
}

View file

@ -0,0 +1,224 @@
/*
u-blox Example: ESF MEAS (Wheel Ticks)
By: Paul Clark
SparkFun Electronics
Date: September 8th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the External Sensor Fusion MEAS sensor messages on the NEO-M8U / ZED-F9R and
shows how to access the ESF data in the loop - without using the callback.
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printESFMEASdata will be called when new ESF MEAS data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_MEAS_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFMEAScallback
// / _____ This _must_ be UBX_ESF_MEAS_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printESFMEASdata(UBX_ESF_MEAS_data_t *ubxDataStruct)
{
Serial.println(F("Hey! The ESF MEAS callback has been called!"));
}
void setup()
{
Serial.begin(230400); // <-- Use a fast baud rate to avoid the Serial prints slowing the code
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
Wire.setClock(400000); // <-- Use 400kHz I2C
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
if (myGNSS.setAutoESFMEAScallbackPtr(&printESFMEASdata) == true) // Enable automatic ESF MEAS messages with callback to printESFMEASdata
Serial.println(F("setAutoESFMEAScallback successful"));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// Check if new ESF MEAS data has arrived:
// If myGNSS.packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid is true, it indicates new ESF MEAS data has been received and has been copied.
// automaticFlags.flags.bits.callbackCopyValid will be cleared automatically when the callback is called.
if (myGNSS.packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid == true)
{
// But, we can manually clear the callback flag too. This will prevent the callback from being called!
myGNSS.packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid = false; // Comment this line if you still want the callback to be called
// Print the timeTag
Serial.print(F("Time: "));
Serial.println(myGNSS.packetUBXESFMEAS->callbackData->timeTag);
// myGNSS.packetUBXESFMEAS->callbackData->flags.bits.numMeas indicates how many sensor groups the UBX_ESF_MEAS_data_t contains.
for (uint8_t i = 0; i < myGNSS.packetUBXESFMEAS->callbackData->flags.bits.numMeas; i++)
{
// Print the sensor data type
// From the M8 interface description:
// 0: None
// 1-4: Reserved
// 5: z-axis gyroscope angular rate deg/s * 2^-12 signed
// 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
// 11: speed m/s * 1e-3 signed
// 12: gyroscope temperature deg Celsius * 1e-2 signed
// 13: y-axis gyroscope angular rate deg/s * 2^-12 signed
// 14: x-axis gyroscope angular rate deg/s * 2^-12 signed
// 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed
// 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed
// 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed
switch (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType)
{
case 5:
Serial.print(F("Z Gyro: "));
break;
case 6:
Serial.print(F("Front Left: "));
break;
case 7:
Serial.print(F("Front Right: "));
break;
case 8:
Serial.print(F("Rear Left: "));
break;
case 9:
Serial.print(F("Rear Right: "));
break;
case 10:
Serial.print(F("Speed Ticks: "));
break;
case 11:
Serial.print(F("Speed: "));
break;
case 12:
Serial.print(F("Temp: "));
break;
case 13:
Serial.print(F("Y Gyro: "));
break;
case 14:
Serial.print(F("X Gyro: "));
break;
case 16:
Serial.print(F("X Accel: "));
break;
case 17:
Serial.print(F("Y Accel: "));
break;
case 18:
Serial.print(F("Z Accel: "));
break;
default:
break;
}
// Tick data
if ((myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType >= 6) && (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType <= 10))
{
if ((myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField & (1 << 23)) > 0)
Serial.print(F("-")); // Backward
else
Serial.print(F("+")); // Forward
Serial.println(myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField & 0x007FFFFF);
}
// Speed
else if (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 11)
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float speed = signedUnsigned.signed32; // Extract the signed data. Convert to float
speed /= 256.0; // Divide by 256 to undo the shift
speed *= 0.001; // Convert from m/s * 1e-3 to m/s
Serial.println(speed, 3);
}
// Gyro data
else if ((myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 5) || (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 13) || (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 14))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float
rate /= 256.0; // Divide by 256 to undo the shift
rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s
Serial.println(rate);
}
// Accelerometer data
else if ((myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 16) || (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 17) || (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 18))
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float force = signedUnsigned.signed32; // Extract the signed data. Convert to float
force /= 256.0; // Divide by 256 to undo the shift
force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2
Serial.println(force);
}
// Gyro Temperature
else if (myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataType == 12)
{
union
{
int32_t signed32;
uint32_t unsigned32;
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
signedUnsigned.unsigned32 = myGNSS.packetUBXESFMEAS->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float
temperature /= 256.0; // Divide by 256 to undo the shift
temperature *= 0.01; // Convert from C * 1e-2 to C
Serial.println(temperature);
}
}
}
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. There will not be any in this example, unless you commented the line above
}

View file

@ -0,0 +1,104 @@
/*
Configuring the GNSS to automatically send position reports over I2C and display them using a callback
By: Paul Clark
SparkFun Electronics
Date: December 30th, 2020
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 GNSS to send navigation reports automatically
and access the data via a callback. No more polling!
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("Time: ")); // Print the time
uint8_t hms = ubxDataStruct->hour; // Print the hours
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = ubxDataStruct->min; // Print the minutes
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = ubxDataStruct->sec; // Print the seconds
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F("."));
unsigned long millisecs = ubxDataStruct->iTOW % 1000; // Print the milliseconds
if (millisecs < 100) Serial.print(F("0")); // Print the trailing zeros correctly
if (millisecs < 10) Serial.print(F("0"));
Serial.print(millisecs);
long latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F(" Lat: "));
Serial.print(latitude);
long longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height above MSL: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,110 @@
/*
Configuring the GNSS to automatically send odometer reports over I2C and display the data using a callback
By: Paul Clark
SparkFun Electronics
Date: March 20th, 2023
License: MIT. See license file for more information.
This example shows how to configure the u-blox GNSS to send odometer reports automatically
and display the data via a callback. No more polling!
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printODOdata will be called when new NAV ODO data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_ODO_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVODOcallback
// / _____ This _must_ be UBX_NAV_ODO_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printODOdata(UBX_NAV_ODO_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("TOW: ")); // Print the Time Of Week
unsigned long iTOW = ubxDataStruct->iTOW; // iTOW is in milliseconds
Serial.print(iTOW);
Serial.print(F(" (ms)"));
Serial.print(F(" Distance: "));
unsigned long distance = ubxDataStruct->distance; // Print the distance
Serial.print(distance);
Serial.print(F(" (m)"));
Serial.print(F(" Total Distance: "));
unsigned long totalDistance = ubxDataStruct->totalDistance; // Print the total distance
Serial.print(totalDistance);
Serial.println(F(" (m)"));
}
void setup()
{
delay(1000);
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring."));
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one solution per second
//By default, the odometer is disabled. We need to enable it.
//We can enable it using the default settings:
myGNSS.enableOdometer();
//Or we can configure it using our own settings, by performing a read-modify-write:
uint8_t flags; // Odometer/Low-speed COG filter flags
uint8_t odoCfg; // Odometer filter settings
uint8_t cogMaxSpeed; // Speed below which course-over-ground (COG) is computed with the low-speed COG filter : m/s * 0.1
uint8_t cogMaxPosAcc; // Maximum acceptable position accuracy for computing COG with the low-speed COG filter
uint8_t velLpGain; // Velocity low-pass filter level
uint8_t cogLpGain; // COG low-pass filter level
if (myGNSS.getOdometerConfig(&flags, &odoCfg, &cogMaxSpeed, &cogMaxPosAcc, &velLpGain, &cogLpGain))
{
flags = UBX_CFG_ODO_USE_ODO; // Enable the odometer
odoCfg = UBX_CFG_ODO_CAR; // Use the car profile (others are RUN, CYCLE, SWIM, CUSTOM)
myGNSS.setOdometerConfig(flags, odoCfg, cogMaxSpeed, cogMaxPosAcc, velLpGain, cogLpGain); // Set the configuration
}
else
Serial.println("Could not read odometer config!");
//myGNSS.resetOdometer(); //Uncomment this line to reset the odometer
myGNSS.setAutoNAVODOcallbackPtr(&printODOdata); // Enable automatic NAV ODO messages with callback to printODOdata
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,115 @@
/*
Configuring the GNSS to automatically send TIM TM2 reports over I2C and display the data using a callback
By: Paul Clark
SparkFun Electronics
Date: December 30th, 2020
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 GNSS to send TIM TM2 reports automatically
and display the data via a callback. No more polling!
Connecting the PPS (Pulse Per Second) breakout pin to the INT (Interrupt) pin with a jumper wire
will cause a TIM TM2 message to be produced once per second. You can then study the timing of the
pulse edges with nanosecond resolution!
Note: TIM TM2 can only capture the timing of one rising edge and one falling edge per
navigation solution. So with setNavigationFrequency set to 1Hz, we can only see the timing
of one rising and one falling edge per second. If the frequency of the signal on the INT pin
is higher than 1Hz, we will only be able to see the timing of the most recent edges.
However, the module can count the number of rising edges too, at rates faster than the navigation rate.
TIM TM2 messages are only produced when a rising or falling edge is detected on the INT pin.
If you disconnect your PPS to INT jumper wire, the messages will stop.
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
NEO-M9N: https://www.sparkfun.com/products/17285
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
int dotsPrinted = 0; // Print dots in rows of 50 while waiting for a TIM TM2 message
// Callback: printTIMTM2data will be called when new TIM TM2 data arrives
// See u-blox_structs.h for the full definition of UBX_TIM_TM2_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoTIMTM2callback
// / _____ This _must_ be UBX_TIM_TM2_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printTIMTM2data(UBX_TIM_TM2_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("newFallingEdge: ")); // 1 if a new falling edge was detected
Serial.print(ubxDataStruct->flags.bits.newFallingEdge);
Serial.print(F(" newRisingEdge: ")); // 1 if a new rising edge was detected
Serial.print(ubxDataStruct->flags.bits.newRisingEdge);
Serial.print(F(" Rising Edge Counter: ")); // Rising edge counter
Serial.print(ubxDataStruct->count);
Serial.print(F(" towMsR: ")); // Time Of Week of rising edge (ms)
Serial.print(ubxDataStruct->towMsR);
Serial.print(F(" towSubMsR: ")); // Millisecond fraction of Time Of Week of rising edge in nanoseconds
Serial.print(ubxDataStruct->towSubMsR);
Serial.print(F(" towMsF: ")); // Time Of Week of falling edge (ms)
Serial.print(ubxDataStruct->towMsF);
Serial.print(F(" towSubMsF: ")); // Millisecond fraction of Time Of Week of falling edge in nanoseconds
Serial.println(ubxDataStruct->towSubMsF);
dotsPrinted = 0; // Reset dotsPrinted
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one solution per second
myGNSS.setAutoTIMTM2callbackPtr(&printTIMTM2data); // Enable automatic TIM TM2 messages with callback to printTIMTM2data
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
if (++dotsPrinted > 50)
{
Serial.println();
dotsPrinted = 0;
}
}

View file

@ -0,0 +1,117 @@
/*
By: Paul Clark
SparkFun Electronics
Date: December, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the High Navigation Rate on the NEO-M8U and then
reads and displays the attitude solution, vehicle dynamics information
and high rate position, velocity and time.
This example uses callbacks to process the HNR data automatically. No more polling!
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printHNRATTdata will be called when new HNR ATT data arrives
// See u-blox_structs.h for the full definition of UBX_HNR_ATT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoHNRATTcallback
// / _____ This _must_ be UBX_HNR_ATT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printHNRATTdata(UBX_HNR_ATT_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("Roll: ")); // Print selected data
Serial.print((float)ubxDataStruct->roll / 100000.0, 2); // Convert roll to degrees
Serial.print(F(" Pitch: "));
Serial.print((float)ubxDataStruct->pitch / 100000.0, 2); // Convert pitch to degrees
Serial.print(F(" Heading: "));
Serial.println((float)ubxDataStruct->heading / 100000.0, 2); // Convert heading to degrees
}
// Callback: printHNRINSdata will be called when new HNR INS data arrives
// See u-blox_structs.h for the full definition of UBX_HNR_INS_data_t
void printHNRINSdata(UBX_HNR_INS_data_t *ubxDataStruct)
{
Serial.print(F("xAccel: ")); // Print selected data
Serial.print(ubxDataStruct->xAccel);
Serial.print(F(" yAccel: "));
Serial.print(ubxDataStruct->yAccel);
Serial.print(F(" zAccel: "));
Serial.println(ubxDataStruct->zAccel);
}
// Callback: printHNRPVTdata will be called when new HNR PVT data arrives
// See u-blox_structs.h for the full definition of UBX_HNR_PVT_data_t
void printHNRPVTdata(UBX_HNR_PVT_data_t *ubxDataStruct)
{
Serial.print(F("ns: ")); // Print selected data
Serial.print(ubxDataStruct->nano);
Serial.print(F(" Lat: "));
Serial.print(ubxDataStruct->lat);
Serial.print(F(" Lon: "));
Serial.println(ubxDataStruct->lon);
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
if (myGNSS.setHNRNavigationRate(10) == true) //Set the High Navigation Rate to 10Hz
Serial.println(F("setHNRNavigationRate was successful"));
else
Serial.println(F("setHNRNavigationRate was NOT successful"));
if (myGNSS.setAutoHNRATTcallbackPtr(&printHNRATTdata) == true) // Enable automatic HNR ATT messages with callback to printHNRATTdata
Serial.println(F("setAutoHNRATTcallback successful"));
if (myGNSS.setAutoHNRINScallbackPtr(&printHNRINSdata) == true) // Enable automatic HNR INS messages with callback to printHNRINSdata
Serial.println(F("setAutoHNRINScallback successful"));
if (myGNSS.setAutoHNRPVTcallbackPtr(&printHNRPVTdata) == true) // Enable automatic HNR PVT messages with callback to printHNRPVTdata
Serial.println(F("setAutoHNRPVTcallback successful"));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(25);
}

View file

@ -0,0 +1,179 @@
/*
By: Paul Clark
SparkFun Electronics
Date: December, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the External Sensor Fusion messages on the NEO-M8U and
uses callbacks to process and display the ESF data automatically. No more polling!
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printESFALGdata will be called when new ESF ALG data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_ALG_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFALGcallback
// / _____ This _must_ be UBX_ESF_ALG_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printESFALGdata(UBX_ESF_ALG_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("TOW: ")); // Print the Time Of Week
unsigned long iTOW = ubxDataStruct->iTOW; // iTOW is in milliseconds
Serial.print(iTOW);
Serial.print(F(" (ms)"));
Serial.print(F(" Roll: ")); // Print selected data
Serial.print((float)ubxDataStruct->roll / 100.0, 2); // Convert roll to degrees
Serial.print(F(" Pitch: "));
Serial.print((float)ubxDataStruct->pitch / 100.0, 2); // Convert pitch to degrees
Serial.print(F(" Yaw: "));
Serial.print((float)ubxDataStruct->yaw / 100.0, 2); // Convert yaw to degrees
Serial.println(F(" (Degrees)"));
}
// Callback: printESFINSdata will be called when new ESF INS data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_INS_data_t
void printESFINSdata(UBX_ESF_INS_data_t *ubxDataStruct)
{
Serial.print(F("xAccel: ")); // Print selected data
Serial.print(ubxDataStruct->xAccel);
Serial.print(F(" yAccel: "));
Serial.print(ubxDataStruct->yAccel);
Serial.print(F(" zAccel: "));
Serial.print(ubxDataStruct->zAccel);
Serial.println(F(" (m/s^2)"));
}
// Callback: printESFMEASdata will be called when new ESF MEAS data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_MEAS_data_t
// and UBX_ESF_MEAS_sensorData_t
void printESFMEASdata(UBX_ESF_MEAS_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("id: ")); // Print selected data
Serial.print(ubxDataStruct->id);
Serial.print(F(" numMeas: "));
Serial.println(ubxDataStruct->flags.bits.numMeas);
for (uint8_t num = 0; num < ubxDataStruct->flags.bits.numMeas; num++) // For each sensor
{
Serial.print(F("Sensor "));
Serial.print(num);
UBX_ESF_MEAS_sensorData_t sensorData;
myGNSS.getSensorFusionMeasurement(&sensorData, *ubxDataStruct, num); // Extract the data for one sensor
Serial.print(F(": Type: "));
Serial.print(sensorData.data.bits.dataType);
Serial.print(F(" Data: "));
Serial.println(sensorData.data.bits.dataField);
}
}
// Callback: printESFSTATUSdata will be called when new ESF STATUS data arrives
// See u-blox_structs.h for the full definition of UBX_ESF_STATUS_data_t
// and UBX_ESF_STATUS_sensorStatus_t
void printESFSTATUSdata(UBX_ESF_STATUS_data_t *ubxDataStruct)
{
Serial.print(F("fusionMode: ")); // Print selected data
Serial.print(ubxDataStruct->fusionMode);
Serial.print(F(" numSens: "));
Serial.println(ubxDataStruct->numSens);
for (uint8_t num = 0; num < ubxDataStruct->numSens; num++) // For each sensor
{
Serial.print(F("Sensor "));
Serial.print(num);
UBX_ESF_STATUS_sensorStatus_t sensorStatus;
myGNSS.getSensorFusionStatus(&sensorStatus, *ubxDataStruct, num); // Extract the data for one sensor
Serial.print(F(": Type: "));
Serial.print(sensorStatus.sensStatus1.bits.type);
Serial.print(F(" Used: "));
Serial.print(sensorStatus.sensStatus1.bits.used);
Serial.print(F(" Ready: "));
Serial.print(sensorStatus.sensStatus1.bits.ready);
Serial.print(F(" Calib Status: "));
Serial.print(sensorStatus.sensStatus2.bits.calibStatus);
Serial.print(F(" Noisy: "));
Serial.println(sensorStatus.faults.bits.noisyMeas);
}
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one solution per second
myGNSS.setHNRNavigationRate(1); //Set the High Navigation Rate to 1Hz
myGNSS.setI2CpollingWait(50); //Allow checkUblox to poll I2C data every 50ms to keep up with the ESF MEAS messages
if (myGNSS.setAutoESFALGcallbackPtr(&printESFALGdata) == true) // Enable automatic ESF ALG messages with callback to printESFALGdata
Serial.println(F("setAutoESFALGcallback successful"));
if (myGNSS.setAutoESFINScallbackPtr(&printESFINSdata) == true) // Enable automatic ESF INS messages with callback to printESFINSdata
Serial.println(F("setAutoESFINScallback successful"));
if (myGNSS.setAutoESFMEAScallbackPtr(&printESFMEASdata) == true) // Enable automatic ESF MEAS messages with callback to printESFMEASdata
Serial.println(F("setAutoESFMEAScallback successful"));
if (myGNSS.setAutoESFSTATUScallbackPtr(&printESFSTATUSdata) == true) // Enable automatic ESF STATUS messages with callback to printESFSTATUSdata
Serial.println(F("setAutoESFSTATUScallback successful"));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(25);
}

View file

@ -0,0 +1,107 @@
/*
Configuring the GNSS to automatically send RXM RZWX reports over I2C and display them using a callback
By: Paul Clark
SparkFun Electronics
Date: March 11th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send RXM RAWX reports automatically
and access the data via a callback. No more polling!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: newRAWX will be called when new RXM RAWX data arrives
// See u-blox_structs.h for the full definition of UBX_RXMRAWX_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoRXMRAWXcallback
// / _____ This _must_ be UBX_RXM_RAWX_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newRAWX(UBX_RXM_RAWX_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("New RAWX data received. It contains "));
Serial.print(ubxDataStruct->header.numMeas); // Print numMeas (Number of measurements / blocks)
Serial.println(F(" data blocks:"));
for (uint8_t block = 0; block < ubxDataStruct->header.numMeas; block++) // For each block
{
Serial.print(F("GNSS ID: "));
if (ubxDataStruct->blocks[block].gnssId < 100) Serial.print(F(" ")); // Align the gnssId
if (ubxDataStruct->blocks[block].gnssId < 10) Serial.print(F(" ")); // Align the gnssId
Serial.print(ubxDataStruct->blocks[block].gnssId);
Serial.print(F(" SV ID: "));
if (ubxDataStruct->blocks[block].svId < 100) Serial.print(F(" ")); // Align the svId
if (ubxDataStruct->blocks[block].svId < 10) Serial.print(F(" ")); // Align the svId
Serial.print(ubxDataStruct->blocks[block].svId);
if (sizeof(double) == 8) // Check if our processor supports 64-bit double
{
// Convert prMes from uint8_t[8] to 64-bit double
// prMes is little-endian
double pseudorange;
memcpy(&pseudorange, &ubxDataStruct->blocks[block].prMes, 8);
Serial.print(F(" PR: "));
Serial.print(pseudorange, 3);
// Convert cpMes from uint8_t[8] to 64-bit double
// cpMes is little-endian
double carrierPhase;
memcpy(&carrierPhase, &ubxDataStruct->blocks[block].cpMes, 8);
Serial.print(F(" m CP: "));
Serial.print(carrierPhase, 3);
Serial.print(F(" cycles"));
}
Serial.println();
}
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F, so we need to disable the "7F" check in checkUbloxI2C
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one solution per second (RAWX produces a _lot_ of data!)
myGNSS.setAutoRXMRAWXcallbackPtr(&newRAWX); // Enable automatic RXM RAWX messages with callback to newRAWX
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,121 @@
/*
Configuring the GNSS to automatically send NAV SAT reports over I2C and display them using a callback
By: Paul Clark
SparkFun Electronics
Date: December 1st, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send NAV SAT reports automatically
and access the data via a callback. No more polling!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: newNAVSAT will be called when new NAV SAT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_SAT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVSATcallback
// / _____ This _must_ be UBX_NAV_SAT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newNAVSAT(UBX_NAV_SAT_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("New NAV SAT data received. It contains data for "));
Serial.print(ubxDataStruct->header.numSvs);
if (ubxDataStruct->header.numSvs == 1)
Serial.println(F(" SV."));
else
Serial.println(F(" SVs."));
// Just for giggles, print the signal strength for each SV as a barchart
for (uint16_t block = 0; block < ubxDataStruct->header.numSvs; block++) // For each SV
{
switch (ubxDataStruct->blocks[block].gnssId) // Print the GNSS ID
{
case 0:
Serial.print(F("GPS "));
break;
case 1:
Serial.print(F("SBAS "));
break;
case 2:
Serial.print(F("Galileo "));
break;
case 3:
Serial.print(F("BeiDou "));
break;
case 4:
Serial.print(F("IMES "));
break;
case 5:
Serial.print(F("QZSS "));
break;
case 6:
Serial.print(F("GLONASS "));
break;
default:
Serial.print(F("UNKNOWN "));
break;
}
Serial.print(ubxDataStruct->blocks[block].svId); // Print the SV ID
if (ubxDataStruct->blocks[block].svId < 10) Serial.print(F(" "));
else if (ubxDataStruct->blocks[block].svId < 100) Serial.print(F(" "));
else Serial.print(F(" "));
// Print the signal strength as a bar chart
for (uint8_t cno = 0; cno < ubxDataStruct->blocks[block].cno; cno++)
Serial.print(F("="));
Serial.println();
}
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one solution per second
myGNSS.setAutoNAVSATcallbackPtr(&newNAVSAT); // Enable automatic NAV SAT messages with callback to newNAVSAT
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,127 @@
/*
Configuring the GNSS to automatically send NAV SVIN reports over I2C and display them using a callback
By: Paul Clark
SparkFun Electronics
Date: April 4th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send NAV SVIN reports automatically
and access the data via a callback. No more polling!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: newNAVSVIN will be called when new NAV SVIN data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_SVIN_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoNAVSVINcallbackPtr
// / _____ This _must_ be UBX_NAV_SVIN_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newNAVSVIN(UBX_NAV_SVIN_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("Survey-in is "));
if (ubxDataStruct->active == 0)
Serial.print(F("not "));
Serial.println(F("in progress"));
Serial.print(F("Survey-in position is "));
if (ubxDataStruct->valid == 0)
Serial.print(F("not "));
Serial.println(F("valid"));
Serial.print(F("Survey-in observation time (s): "));
Serial.println(ubxDataStruct->dur);
Serial.print(F("ECEF position (cm): "));
Serial.print(ubxDataStruct->meanX);
Serial.print(F(" ("));
if (ubxDataStruct->meanXHP >= 0)
Serial.print(F("+"));
Serial.print((float)ubxDataStruct->meanXHP * 0.01); // Convert 0.1mm to cm
Serial.print(F("), "));
Serial.print(ubxDataStruct->meanY);
Serial.print(F(" ("));
if (ubxDataStruct->meanYHP >= 0)
Serial.print(F("+"));
Serial.print((float)ubxDataStruct->meanYHP * 0.01); // Convert 0.1mm to cm
Serial.print(F("), "));
Serial.print(ubxDataStruct->meanZ);
Serial.print(F(" ("));
if (ubxDataStruct->meanZHP >= 0)
Serial.print(F("+"));
Serial.print((float)ubxDataStruct->meanZHP * 0.01); // Convert 0.1mm to cm
Serial.println(F(")"));
Serial.print(F("Mean position accuracy (cm): "));
Serial.println((float)ubxDataStruct->meanAcc * 0.01); // Convert 0.1mm to cm
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("u-blox Base Station example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port settings to flash and BBR
// Set up the callback for NAV SVIN. This will enable SVIN messages at the navigation rate
myGNSS.setAutoNAVSVINcallbackPtr(&newNAVSVIN);
while (Serial.available()) Serial.read(); //Clear the serial buffer
Serial.println(F("Press any key to begin Survey-In"));
}
void loop()
{
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
myGNSS.checkCallbacks(); //Process any waiting callbacks
if (Serial.available()) // Check if user has pressed a key
{
bool success = myGNSS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m
//bool success = myGNSS.enableSurveyModeFull(86400, 2.000); //Enable Survey in, 24 hours, 2.0m
Serial.println();
if (success)
{
Serial.println(F("Survey-In started!"));
}
else
{
Serial.println(F("Survey start failed!"));
}
while (Serial.available()) Serial.read(); //Clear the serial buffer
}
}

View file

@ -0,0 +1,120 @@
/*
Configuring the GNSS to automatically send position reports over I2C and display them using a callback
By: Paul Clark
SparkFun Electronics
Date: April 15th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to access the callback data from the main loop.
The simple way to check if new data is available is to use a global flag: set it in the callback, check it and clear it in the main loop.
Or, you can be more sophisticated and use the callback flags themselves.
This example shows how to use the sophisticated method.
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 <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: callbackPVT will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallbackPtr
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void callbackPVT(UBX_NAV_PVT_data_t *ubxDataStruct)
{
Serial.println(F("Hey! The NAV PVT callback has been called!"));
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVTcallbackPtr(&callbackPVT); // Enable automatic NAV PVT messages with callback to callbackPVT
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// Check if new NAV PVT data has been received:
// If myGNSS.packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid is true, it indicates new PVT data has been received and has been copied.
// automaticFlags.flags.bits.callbackCopyValid will be cleared automatically when the callback is called.
if (myGNSS.packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid == true)
{
// But, we can manually clear the callback flag too. This will prevent the callback from being called!
myGNSS.packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Comment this line if you still want the callback to be called
Serial.println();
Serial.print(F("Time: ")); // Print the time
uint8_t hms = myGNSS.packetUBXNAVPVT->callbackData->hour; // Print the hours
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = myGNSS.packetUBXNAVPVT->callbackData->min; // Print the minutes
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = myGNSS.packetUBXNAVPVT->callbackData->sec; // Print the seconds
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F("."));
unsigned long millisecs = myGNSS.packetUBXNAVPVT->callbackData->iTOW % 1000; // Print the milliseconds
if (millisecs < 100) Serial.print(F("0")); // Print the trailing zeros correctly
if (millisecs < 10) Serial.print(F("0"));
Serial.print(millisecs);
long latitude = myGNSS.packetUBXNAVPVT->callbackData->lat; // Print the latitude
Serial.print(F(" Lat: "));
Serial.print(latitude);
long longitude = myGNSS.packetUBXNAVPVT->callbackData->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.packetUBXNAVPVT->callbackData->hMSL; // Print the height above mean sea level
Serial.print(F(" Height above MSL: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. There will not be any in this example, unless you commented the line above
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,256 @@
/*
Configuring the GNSS to automatically send NAV PVT reports over I2C and log them to file on SD card
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send NAV PVT reports automatically
and log the data to SD card in UBX format.
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
Data is logged in u-blox UBX format. Please see the u-blox protocol specification for more details.
You can replay and analyze the data using u-center:
https://www.u-blox.com/en/product/u-center
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
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define packetLength 100 // NAV PVT is 92 + 8 bytes in length (including the sync chars, class, id, length and checksum bytes)
uint8_t *myBuffer; // Use myBuffer to hold the data while we write it to SD card
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("Time: ")); // Print the time
uint8_t hms = ubxDataStruct->hour; // Print the hours
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = ubxDataStruct->min; // Print the minutes
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F(":"));
hms = ubxDataStruct->sec; // Print the seconds
if (hms < 10) Serial.print(F("0")); // Print a leading zero if required
Serial.print(hms);
Serial.print(F("."));
unsigned long millisecs = ubxDataStruct->iTOW % 1000; // Print the milliseconds
if (millisecs < 100) Serial.print(F("0")); // Print the trailing zeros correctly
if (millisecs < 10) Serial.print(F("0"));
Serial.print(millisecs);
long latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F(" Lat: "));
Serial.print(latitude);
long longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height above MSL: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin(); // Start I2C communication with the GNSS
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "NAV_PVT.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("NAV_PVT.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful GNSS debug messages on Serial
// NAV PVT messages are 100 bytes long.
// In this example, the data will arrive no faster than one message per second.
// So, setting the file buffer size to 301 bytes should be more than adequate.
// I.e. room for three messages plus an empty tail byte.
myGNSS.setFileBufferSize(301); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// (This will also disable any "auto" messages that were enabled and saved by other examples and reduce the load on the I2C bus)
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata
myGNSS.logNAVPVT(); // Enable NAV PVT data logging
myBuffer = new uint8_t[packetLength]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
if (myGNSS.fileBufferAvailable() >= packetLength) // Check to see if a new packetLength-byte NAV PVT message has been stored
{
myGNSS.extractFileBufferData(myBuffer, packetLength); // Extract exactly packetLength bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, packetLength); // Write exactly packetLength bytes from myBuffer to the ubxDataFile on the SD card
//printBuffer(myBuffer); // Uncomment this line to print the data as Hexadecimal bytes
}
if (Serial.available()) // Check if the user wants to stop logging
{
myFile.close(); // Close the data file
Serial.println(F("\r\nLogging stopped. Freezing..."));
while(1); // Do nothing more
}
Serial.print(".");
delay(50);
}
// Print the buffer contents as Hexadecimal bytes
// You should see:
// SYNC CHAR 1: 0xB5
// SYNC CHAR 2: 0x62
// CLASS: 0x01 for NAV
// ID: 0x07 for PVT
// LENGTH: 2-bytes Little Endian (0x5C00 = 92 bytes for NAV PVT)
// PAYLOAD: LENGTH bytes
// CHECKSUM_A
// CHECKSUM_B
// Please see the u-blox protocol specification for more details
void printBuffer(uint8_t *ptr)
{
for (int i = 0; i < packetLength; i++)
{
if (ptr[i] < 16) Serial.print("0"); // Print a leading zero if required
Serial.print(ptr[i], HEX); // Print the byte as Hexadecimal
Serial.print(" ");
}
Serial.println();
}

View file

@ -0,0 +1,267 @@
/*
Configuring the GNSS to automatically send TIM TM2 reports over I2C and log them to file on SD card
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send TIM TM2 reports automatically
and log the data to SD card in UBX format.
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
Connecting the PPS (Pulse Per Second) breakout pin to the INT (Interrupt) pin with a jumper wire
will cause a TIM TM2 message to be produced once per second. You can then study the timing of the
pulse edges with nanosecond resolution!
Note: TIM TM2 can only capture the timing of one rising edge and one falling edge per
navigation solution. So with setNavigationFrequency set to 1Hz, we can only see the timing
of one rising and one falling edge per second. If the frequency of the signal on the INT pin
is higher than 1Hz, we will only be able to see the timing of the most recent edges.
However, the module can count the number of rising edges too, at rates faster than the navigation rate.
TIM TM2 messages are only produced when a rising or falling edge is detected on the INT pin.
If you disconnect your PPS to INT jumper wire, the messages will stop.
Data is logged in u-blox UBX format. Please see the u-blox protocol specification for more details.
You can replay and analyze the data using u-center:
https://www.u-blox.com/en/product/u-center
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
NEO-M9N: https://www.sparkfun.com/products/17285
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define packetLength 36 // TIM TM2 is 28 + 8 bytes in length (including the sync chars, class, id, length and checksum bytes)
uint8_t *myBuffer; // Use myBuffer to hold the data while we write it to SD card
int dotsPrinted = 0; // Print dots in rows of 50 while waiting for a TIM TM2 message
// Callback: printTIMTM2data will be called when new TIM TM2 data arrives
// See u-blox_structs.h for the full definition of UBX_TIM_TM2_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoTIMTM2callback
// / _____ This _must_ be UBX_TIM_TM2_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printTIMTM2data(UBX_TIM_TM2_data_t *ubxDataStruct)
{
Serial.println();
Serial.print(F("newFallingEdge: ")); // 1 if a new falling edge was detected
Serial.print(ubxDataStruct->flags.bits.newFallingEdge);
Serial.print(F(" newRisingEdge: ")); // 1 if a new rising edge was detected
Serial.print(ubxDataStruct->flags.bits.newRisingEdge);
Serial.print(F(" Rising Edge Counter: ")); // Rising edge counter
Serial.print(ubxDataStruct->count);
Serial.print(F(" towMsR: ")); // Time Of Week of rising edge (ms)
Serial.print(ubxDataStruct->towMsR);
Serial.print(F(" towSubMsR: ")); // Millisecond fraction of Time Of Week of rising edge in nanoseconds
Serial.print(ubxDataStruct->towSubMsR);
Serial.print(F(" towMsF: ")); // Time Of Week of falling edge (ms)
Serial.print(ubxDataStruct->towMsF);
Serial.print(F(" towSubMsF: ")); // Millisecond fraction of Time Of Week of falling edge in nanoseconds
Serial.println(ubxDataStruct->towSubMsF);
dotsPrinted = 0; // Reset dotsPrinted
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin(); // Start I2C communication with the GNSS
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "TIM_TM2.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("TIM_TM2.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful GNSS debug messages on Serial
// TIM TM2 messages are 36 bytes long.
// In this example, the data will arrive no faster than one message per second.
// So, setting the file buffer size to 109 bytes should be more than adequate.
// I.e. room for three messages plus an empty tail byte.
myGNSS.setFileBufferSize(109); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// (This will also disable any "auto" messages that were enabled and saved by other examples and reduce the load on the I2C bus)
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second
myGNSS.setAutoTIMTM2callbackPtr(&printTIMTM2data); // Enable automatic TIM TM2 messages with callback to printTIMTM2data
myGNSS.logTIMTM2(); // Enable TIM TM2 data logging
myBuffer = new uint8_t[packetLength]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
if (myGNSS.fileBufferAvailable() >= packetLength) // Check to see if a new packetLength-byte TIM TM2 message has been stored
{
myGNSS.extractFileBufferData(myBuffer, packetLength); // Extract exactly packetLength bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, packetLength); // Write exactly packetLength bytes from myBuffer to the ubxDataFile on the SD card
//printBuffer(myBuffer); // Uncomment this line to print the data
}
if (Serial.available()) // Check if the user wants to stop logging
{
myFile.close(); // Close the data file
Serial.println(F("\r\nLogging stopped. Freezing..."));
while(1); // Do nothing more
}
Serial.print("."); // Print dots in rows of 50
delay(50);
if (++dotsPrinted > 50)
{
Serial.println();
dotsPrinted = 0;
}
}
// Print the buffer contents as Hexadecimal
// You should see:
// SYNC CHAR 1: 0xB5
// SYNC CHAR 2: 0x62
// CLASS: 0x0D for TIM
// ID: 0x03 for TM2
// LENGTH: 2-bytes Little Endian (0x1C00 = 28 bytes for TIM TM2)
// PAYLOAD: LENGTH bytes
// CHECKSUM_A
// CHECKSUM_B
// Please see the u-blox protocol specification for more details
void printBuffer(uint8_t *ptr)
{
for (int i = 0; i < packetLength; i++)
{
if (ptr[i] < 16) Serial.print("0"); // Print a leading zero if required
Serial.print(ptr[i], HEX); // Print the byte as Hexadecimal
Serial.print(" ");
}
Serial.println();
}

View file

@ -0,0 +1,297 @@
/*
Configuring the GNSS to automatically send RXM SFRBX and RAWX reports over I2C and log them to file on SD card
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send RXM SFRBX and RAWX reports automatically
and log the data to SD card in UBX format.
** Please note: this example will only work on u-blox ADR or High Precision GNSS or Time Sync products **
** Please note: this example will only work on processors like the Artemis which have plenty of RAM available **
Data is logged in u-blox UBX format. Please see the u-blox protocol specification for more details.
You can replay and analyze the data using u-center:
https://www.u-blox.com/en/product/u-center
Or you can use (e.g.) RTKLIB to analyze the data and extract your precise location or produce
Post-Processed Kinematic data:
https://rtklibexplorer.wordpress.com/
http://rtkexplorer.com/downloads/rtklib-code/
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
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
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes
#define fileBufferSize 16384 // Allocate 16KBytes of RAM for UBX message storage
uint8_t *myBuffer; // Use myBuffer to hold the data while we write it to SD card
unsigned long lastPrint; // Record when the last Serial print took place
// Note: we'll keep a count of how many SFRBX and RAWX messages arrive - but the count will not be completely accurate.
// If two or more SFRBX messages arrive together as a group and are processed by one call to checkUblox, the count will
// only increase by one.
int numSFRBX = 0; // Keep count of how many SFRBX message groups have been received (see note above)
int numRAWX = 0; // Keep count of how many RAWX message groups have been received (see note above)
// Callback: newSFRBX will be called when new RXM SFRBX data arrives
// See u-blox_structs.h for the full definition of UBX_RXMSFRBX_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoRXMSFRBXcallback
// / _____ This _must_ be UBX_RXM_SFRBX_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newSFRBX(UBX_RXM_SFRBX_data_t *ubxDataStruct)
{
numSFRBX++; // Increment the count
}
// Callback: newRAWX will be called when new RXM RAWX data arrives
// See u-blox_structs.h for the full definition of UBX_RXMRAWX_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoRXMRAWXcallback
// / _____ This _must_ be UBX_RXM_RAWX_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newRAWX(UBX_RXM_RAWX_data_t *ubxDataStruct)
{
numRAWX++; // Increment the count
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card
digitalWrite(LED_BUILTIN, LOW);
Wire.begin(); // Start I2C communication
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "RXM_RAWX.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("RXM_RAWX.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial
//myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial
myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F, so we need to disable the "7F" check in checkUbloxI2C
// RAWX messages can be over 2KBytes in size, so we need to make sure we allocate enough RAM to hold all the data.
// SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough
// to hold the backlog of data if/when this happens.
// getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained.
myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// (This will also disable any "auto" messages that were enabled and saved by other examples and reduce the load on the I2C bus)
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second (that's plenty for Precise Point Positioning)
myGNSS.setAutoRXMSFRBXcallbackPtr(&newSFRBX); // Enable automatic RXM SFRBX messages with callback to newSFRBX
myGNSS.logRXMSFRBX(); // Enable RXM SFRBX data logging
myGNSS.setAutoRXMRAWXcallbackPtr(&newRAWX); // Enable automatic RXM RAWX messages with callback to newRAWX
myGNSS.logRXMRAWX(); // Enable RXM RAWX data logging
myBuffer = new uint8_t[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
lastPrint = millis(); // Initialize lastPrint
}
void loop()
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card
myGNSS.extractFileBufferData(myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card
// In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (millis() > (lastPrint + 1000)) // Print the message count once per second
{
Serial.print(F("Number of message groups received: SFRBX: ")); // Print how many message groups have been received (see note above)
Serial.print(numSFRBX);
Serial.print(F(" RAWX: "));
Serial.println(numRAWX);
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now)
//Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets
//Serial.println(maxBufferBytes);
if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full
{
Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost."));
}
lastPrint = millis(); // Update lastPrint
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (Serial.available()) // Check if the user wants to stop logging
{
uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer
while (remainingBytes > 0) // While there is still data in the file buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card
uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time
if (bytesToWrite > sdWriteSize)
{
bytesToWrite = sdWriteSize;
}
myGNSS.extractFileBufferData(myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card
remainingBytes -= bytesToWrite; // Decrement remainingBytes
}
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off
myFile.close(); // Close the data file
Serial.println(F("Logging stopped. Freezing..."));
while(1); // Do nothing more
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
}

View file

@ -0,0 +1,271 @@
/*
Configuring the GNSS to automatically send RXM SFRBX and RAWX reports over I2C and log them to file on SD card
** without using callbacks **
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send RXM SFRBX and RAWX reports automatically
and log the data to SD card in UBX format ** without using callbacks **
** Please note: this example will only work on u-blox ADR or High Precision GNSS or Time Sync products **
** Please note: this example will only work on processors like the Artemis which have plenty of RAM available **
Data is logged in u-blox UBX format. Please see the u-blox protocol specification for more details.
You can replay and analyze the data using u-center:
https://www.u-blox.com/en/product/u-center
Or you can use (e.g.) RTKLIB to analyze the data and extract your precise location or produce
Post-Processed Kinematic data:
https://rtklibexplorer.wordpress.com/
http://rtkexplorer.com/downloads/rtklib-code/
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
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
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes
#define fileBufferSize 16384 // Allocate 16KBytes of RAM for UBX message storage
uint8_t *myBuffer; // A buffer to hold the data while we write it to SD card
unsigned long lastPrint; // Record when the last Serial print took place
unsigned long bytesWritten = 0; // Record how many bytes have been written to SD card
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card
digitalWrite(LED_BUILTIN, LOW);
Wire.begin(); // Start I2C communication
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "RXM_RAWX.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("RXM_RAWX.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial
//myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial
myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F, so we need to disable the "7F" check in checkUbloxI2C
// RAWX messages can be over 2KBytes in size, so we need to make sure we allocate enough RAM to hold all the data.
// SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough
// to hold the backlog of data if/when this happens.
// getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained.
myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// (This will also disable any "auto" messages that were enabled and saved by other examples and reduce the load on the I2C bus)
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second (that's plenty for Precise Point Positioning)
myGNSS.setAutoRXMSFRBX(true, false); // Enable automatic RXM SFRBX messages: without callback; without implicit update
myGNSS.logRXMSFRBX(); // Enable RXM SFRBX data logging
myGNSS.setAutoRXMRAWX(true, false); // Enable automatic RXM RAWX messages: without callback; without implicit update
myGNSS.logRXMRAWX(); // Enable RXM RAWX data logging
myBuffer = new uint8_t[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
lastPrint = millis(); // Initialize lastPrint
}
void loop()
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card
myGNSS.extractFileBufferData(myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += sdWriteSize; // Update bytesWritten
// In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (millis() > (lastPrint + 1000)) // Print bytesWritten once per second
{
Serial.print(F("The number of bytes written to SD card is ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now)
//Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets
//Serial.println(maxBufferBytes);
if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full
{
Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost."));
}
lastPrint = millis(); // Update lastPrint
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (Serial.available()) // Check if the user wants to stop logging
{
uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer
while (remainingBytes > 0) // While there is still data in the file buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card
uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time
if (bytesToWrite > sdWriteSize)
{
bytesToWrite = sdWriteSize;
}
myGNSS.extractFileBufferData(myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += bytesToWrite; // Update bytesWritten
remainingBytes -= bytesToWrite; // Decrement remainingBytes
}
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off
Serial.print(F("The total number of bytes written to SD card is ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
myFile.close(); // Close the data file
Serial.println(F("Logging stopped. Freezing..."));
while(1); // Do nothing more
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
}

View file

@ -0,0 +1,296 @@
/*
Configuring the GNSS to automatically send RXM SFRBX and RAWX reports over I2C and log them to file on SD card
without using callbacks and ** as fast as your module can go! **
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send RXM SFRBX and RAWX reports automatically
and log the data to SD card in UBX format without using callbacks and ** as fast as your module can go! **
** Please note: this example will only work on u-blox ADR or High Precision GNSS or Time Sync products **
** Please note: this example will only work on processors like the Artemis which have plenty of RAM available **
Data is logged in u-blox UBX format. Please see the u-blox protocol specification for more details.
You can replay and analyze the data using u-center:
https://www.u-blox.com/en/product/u-center
Or you can use (e.g.) RTKLIB to analyze the data and extract your precise location or produce
Post-Processed Kinematic data:
https://rtklibexplorer.wordpress.com/
http://rtkexplorer.com/downloads/rtklib-code/
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
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
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes
#define fileBufferSize 32768 // Allocate 32KBytes of RAM for UBX message storage
uint8_t *myBuffer; // A buffer to hold the data while we write it to SD card
unsigned long lastPrint; // Record when the last Serial print took place
unsigned long bytesWritten = 0; // Record how many bytes have been written to SD card
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card
digitalWrite(LED_BUILTIN, LOW);
Wire.begin(); // Start I2C communication
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "Fast_RXM.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("Fast_RXM.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial
//myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial
myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F, so we need to disable the "7F" check in checkUbloxI2C
// RAWX messages can be over 2KBytes in size, so we need to make sure we allocate enough RAM to hold all the data.
// SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough
// to hold the backlog of data if/when this happens.
// getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained.
myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// (This will also disable any "auto" messages that were enabled and saved by other examples and reduce the load on the I2C bus)
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
// Modules like the ZED-F9P can produce RAW navigation data at rates of up to 25Hz but not while using all of the GNSS constellations.
// Please consult the data sheet for the Performance figures for your module.
// In this example we make sure GPS is enabled and then disable Galileo, GLONASS, BeiDou, SBAS and QZSS to achieve 25Hz.
myGNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_GPS); // Make sure GPS is enabled (we must leave at least one major GNSS enabled!)
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_SBAS); // Disable SBAS
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_GALILEO); // Disable Galileo
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_BEIDOU); // Disable BeiDou
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_IMES); // Disable IMES
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_QZSS); // Disable QZSS
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_GLONASS); // Disable GLONASS
delay(2000); // Give the module some extra time to get ready
//Produce 7 navigation solutions per second. That's a lot of RAWX data - especially when using both GPS bands L1 and L2.
//The SD library and card need to be able to cope with the data rate too. You may need a faster SD library to go above 7Hz.
myGNSS.setNavigationFrequency(7);
myGNSS.setAutoRXMSFRBX(true, false); // Enable automatic RXM SFRBX messages: without callback; without implicit update
myGNSS.logRXMSFRBX(); // Enable RXM SFRBX data logging
myGNSS.setAutoRXMRAWX(true, false); // Enable automatic RXM RAWX messages: without callback; without implicit update
myGNSS.logRXMRAWX(); // Enable RXM RAWX data logging
myBuffer = new uint8_t[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
lastPrint = millis(); // Initialize lastPrint
}
void loop()
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card
myGNSS.extractFileBufferData(myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += sdWriteSize; // Update bytesWritten
// In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (millis() > (lastPrint + 1000)) // Print bytesWritten once per second
{
Serial.print(F("The number of bytes written to SD card is: ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now)
//Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets
//Serial.println(maxBufferBytes);
if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full
{
Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost."));
}
lastPrint = millis(); // Update lastPrint
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (Serial.available()) // Check if the user wants to stop logging
{
myGNSS.setAutoRXMSFRBX(false, false); // Disable the automatic RXM SFRBX messages
myGNSS.setAutoRXMRAWX(false, false); // Disable the automatic RXM RAWX messages
delay(1000); // Allow time for any remaining messages to arrive
myGNSS.checkUblox(); // Process any remaining data
uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer
while (remainingBytes > 0) // While there is still data in the file buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card
uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time
if (bytesToWrite > sdWriteSize)
{
bytesToWrite = sdWriteSize;
}
myGNSS.extractFileBufferData(myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += bytesToWrite; // Update bytesWritten
remainingBytes -= bytesToWrite; // Decrement remainingBytes
}
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off
Serial.print(F("The total number of bytes written to SD card is: ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Show how full the file buffer has been (not how full it is now)
Serial.print(F("The maximum number of bytes which the file buffer has contained is: "));
Serial.println(maxBufferBytes);
myFile.close(); // Close the data file
Serial.println(F("Logging stopped. Freezing..."));
while(1); // Do nothing more
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
}

View file

@ -0,0 +1,265 @@
/*
Demonstrate how to log NMEA and UBX data simultaneously
By: Paul Clark
SparkFun Electronics
Date: October 18th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send PVT reports automatically
and log those and any incoming NMEA messages to SD card in UBX format
** Please note: this example will only work on processors like the Artemis which have plenty of RAM available **
This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor
but can be adapted by changing the chip select pin and SPI definitions:
https://www.sparkfun.com/products/16829
https://www.sparkfun.com/products/16401
Hardware Connections:
Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide
Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw.
Connect your GNSS breakout to the Carrier Board using a Qwiic cable.
Connect an antenna to your GNSS board if required.
Insert a formatted micro-SD card into the socket on the Carrier Board.
Connect the Carrier Board to your computer using a USB-C cable.
Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3
This code has been tested using version 2.2.0 of the Apollo3 boards on Arduino IDE 1.8.13.
Select "Artemis MicroMod Processor" as the board type.
Press upload to upload the code onto the Artemis.
Open the Serial Monitor at 115200 baud to see the output.
To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on
both the MicroMod Data Logging Carrier Board and the u-blox module breakout.
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
ZOE-M8Q: https://www.sparkfun.com/products/15193
SAM-M8Q: https://www.sparkfun.com/products/15210
*/
#include <SPI.h>
#include <SD.h>
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
File myFile; //File that all GNSS data is written to
//Define the microSD (SPI) Chip Select pin. Adjust for your processor if necessary.
#if defined(ARDUINO_ARCH_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1 or v2
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
#define sdChipSelect SPI_CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v2
#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_MICROMOD) // Check for the Artemis MicroMod Processor Board on Apollo3 v1
#define sdChipSelect CS // SPI (microSD) Chip Select for the Artemis MicroMod Processor Board on Apollo3 v1
#else
#define sdChipSelect CS // Catch-all for the other Artemis Boards - change this if required to match your hardware
#endif
#else
#define sdChipSelect CS // Catch-all for all non-Artemis boards - change this if required to match your hardware
#endif
#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes
#define fileBufferSize 16384 // Allocate 16KBytes of RAM for UBX message storage
uint8_t *myBuffer; // A buffer to hold the data while we write it to SD card
unsigned long lastPrint; // Record when the last Serial print took place
unsigned long bytesWritten = 0; // Record how many bytes have been written to SD card
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card
digitalWrite(LED_BUILTIN, LOW);
Wire.begin(); // Start I2C communication
// On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors
#if defined(AM_PART_APOLLO3) // Check for SparkFun Apollo3 (Artemis) v1
Wire.setPullups(0); // Disable the internal I2C pull-ups on Apollo3 v1
#elif defined(ARDUINO_ARCH_APOLLO3) // Else check for SparkFun Apollo3 (Artemis) (v2)
#if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_MM_PB) // Check for the Artemis MicroMod Processor Board on Apollo3 v2
// On Apollo3 v2 we can still disable the pull-ups but we need to do it manually
// The IOM and pin numbers here are specific to the Artemis MicroMod Processor Board
am_hal_gpio_pincfg_t sclPinCfg = g_AM_BSP_GPIO_IOM4_SCL; // Artemis MicroMod Processor Board uses IOM4 for I2C communication
am_hal_gpio_pincfg_t sdaPinCfg = g_AM_BSP_GPIO_IOM4_SDA;
sclPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE; // Disable the pull-ups
sdaPinCfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_NONE;
pin_config(PinName(39), sclPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 39 for SCL
pin_config(PinName(40), sdaPinCfg); // Artemis MicroMod Processor Board uses Pin/Pad 40 for SDA
#endif
#endif
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
Serial.println("Initializing SD card...");
// See if the card is present and can be initialized:
if (!SD.begin(sdChipSelect))
{
Serial.println("Card failed, or not present. Freezing...");
// don't do anything more:
while (1);
}
Serial.println("SD card initialized.");
// Create or open a file called "PVT_NMEA.ubx" on the SD card.
// If the file already exists, the new data is appended to the end of the file.
myFile = SD.open("PVT_NMEA.ubx", FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to create UBX data file! Freezing..."));
while (1);
}
//myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial
//myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial
//myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F. Uncomment this line to disable the "7F" check in checkUbloxI2C
// SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough
// to hold the backlog of data if/when this happens.
// getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained.
myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing..."));
while (1);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
// This will (re)enable the standard NMEA messages too
// This will also disable any "auto" UBX messages that were enabled and saved by other examples and reduce the load on the I2C bus
//myGNSS.factoryDefault(); delay(5000);
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both UBX and NMEA messages
//myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second
myGNSS.setAutoPVT(true, false); // Enable automatic NAV PVT messages: without callback; without implicit update
myGNSS.logNAVPVT(); // Enable NAV PVT data logging
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 1); // Ensure the GxGGA (Global positioning system fix data) message is enabled. Send every measurement.
myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C, 1); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every measurement.
myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C, 1); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every measurement.
myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_ALL); // Enable logging of all enabled NMEA messages
//myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_GGA | SFE_UBLOX_FILTER_NMEA_GSA); // Or we can, for example, log only GxGGA & GxGSA and ignore GxGSV
myBuffer = new uint8_t[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card
Serial.println(F("Press any key to stop logging."));
lastPrint = millis(); // Initialize lastPrint
}
void loop()
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card
myGNSS.extractFileBufferData(myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += sdWriteSize; // Update bytesWritten
// In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (millis() > (lastPrint + 1000)) // Print bytesWritten once per second
{
Serial.print(F("The number of bytes written to SD card is ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now)
//Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets
//Serial.println(maxBufferBytes);
if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full
{
Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost."));
}
lastPrint = millis(); // Update lastPrint
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (Serial.available()) // Check if the user wants to stop logging
{
uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer
while (remainingBytes > 0) // While there is still data in the file buffer
{
digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card
uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time
if (bytesToWrite > sdWriteSize)
{
bytesToWrite = sdWriteSize;
}
myGNSS.extractFileBufferData(myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card
bytesWritten += bytesToWrite; // Update bytesWritten
remainingBytes -= bytesToWrite; // Decrement remainingBytes
}
digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off
Serial.print(F("The total number of bytes written to SD card is ")); // Print how many bytes have been written to SD card
Serial.println(bytesWritten);
myFile.close(); // Close the data file
Serial.println(F("Logging stopped. Freezing..."));
while(1); // Do nothing more
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
}

View file

@ -0,0 +1,340 @@
/*
Configuring the GNSS to automatically send RXM SFRBX and RAWX reports over SPI and log them to file on SD card using full 4-bit SDIO
By: Paul Clark
SparkFun Electronics
Date: October 20th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the u-blox GNSS to send RXM SFRBX and RAWX reports automatically
and log the data to SD card in UBX format.
This code is written for the OpenLog ESP32 (DEV-20594) - coming soon!
Hardware set-up:
Close the DSEL jumper on the ZED-F9P breakout - to select SPI mode
Connect:
OpenLog ESP32 : ZED-F9P
GND GND
3V3_SW 3V3
SCK (18) SCK
PICO (23) PICO (MOSI)
POCI (19) POCI (MISO)
33 CS
** Please note: this example will only work on u-blox ADR or High Precision GNSS or Time Sync products **
Data is logged in u-blox UBX format.
Feel like supporting open source hardware?
Buy a board from SparkFun!
OpenLog ESP32: https://www.sparkfun.com/products/20594
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
*/
#define GNSS_CS 33 // Connect the ZED-F9P CS pin to OpenLog ESP32 pin 33
#define EN_3V3_SW 32 // The 3.3V_SW regulator Enable pin is connected to D32
#define STAT_LED 25 // The OpenLog ESP32 STAT LED is connected to pin 25
#define IMU_CS 5 // The ISM330 IMU CS is connected to pin 5
#define MAG_CS 27 // The MMC5983 Mag CS is connected to pin 27
#include "FS.h"
#include "SD_MMC.h"
File myFile;
#include <SPI.h>
#define spiPort SPI
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#define sdWriteSize 2048 // Write data to the SD card in blocks of n*512 bytes
#define fileBufferSize 65530 // Allocate just under 64KBytes of RAM for UBX message storage
#define navRate 20 // Set the Nav Rate (Frequency) to 20Hz
//#define ubxOnly // Uncomment this line to log UBX (RAWX and SFRBX) only
uint8_t *myBuffer; // Use myBuffer to hold the data while we write it to SD card
unsigned long lastPrint; // Record when the last Serial print took place
// Note: we'll keep a count of how many SFRBX and RAWX messages arrive - but the count will not be completely accurate.
// If two or more SFRBX messages arrive together as a group and are processed by one call to checkUblox, the count will
// only increase by one.
int numSFRBX = 0; // Keep count of how many SFRBX message groups have been received (see note above)
int numRAWX = 0; // Keep count of how many RAWX message groups have been received (see note above)
// Callback: newSFRBX will be called when new RXM SFRBX data arrives
// See u-blox_structs.h for the full definition of UBX_RXMSFRBX_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoRXMSFRBXcallback
// / _____ This _must_ be UBX_RXM_SFRBX_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newSFRBX(UBX_RXM_SFRBX_data_t *ubxDataStruct)
{
numSFRBX++; // Increment the count
}
// Callback: newRAWX will be called when new RXM RAWX data arrives
// See u-blox_structs.h for the full definition of UBX_RXMRAWX_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoRXMRAWXcallback
// / _____ This _must_ be UBX_RXM_RAWX_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void newRAWX(UBX_RXM_RAWX_data_t *ubxDataStruct)
{
numRAWX++; // Increment the count
}
void setup()
{
Serial.begin(115200);
pinMode(STAT_LED, OUTPUT); // Flash the STAT LED each time we write to the SD card
digitalWrite(STAT_LED, LOW);
pinMode(GNSS_CS, OUTPUT);
digitalWrite(GNSS_CS, HIGH);
pinMode(IMU_CS, OUTPUT);
digitalWrite(IMU_CS, HIGH);
pinMode(MAG_CS, OUTPUT);
digitalWrite(MAG_CS, HIGH);
spiPort.begin();
// Do a fake transaction to initialize the SPI pins
spiPort.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
spiPort.transfer(0);
spiPort.endTransaction();
pinMode(EN_3V3_SW, OUTPUT); // Enable power for the microSD card and GNSS
digitalWrite(EN_3V3_SW, HIGH);
delay(3000); // Allow time for the GNSS and SD card to start up and for Tera Term to reconnect
Serial.println(F("SparkFun OpenLog ESP32 GNSS Logging : SPI and SDIO"));
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Initialize the GNSS
Serial.println(F("Initializing the GNSS..."));
//myGNSS.enableDebugging(); // Uncomment this line to see helpful debug messages on Serial
myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin
// Connect to the u-blox module using SPI port, csPin and speed setting
// ublox devices generally work up to 5MHz. We'll use 4MHz for this example:
bool begun = false;
do
{
begun = myGNSS.begin(spiPort, GNSS_CS, 4000000);
if (!begun)
{
Serial.println(F("u-blox GNSS not detected on SPI bus."));
delay(1000);
}
}
while (!begun);
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Wait for a 3D fix. Get the date and time for the log file
//myGNSS.factoryDefault(); delay(5000); // Uncomment this line to reset the module back to its factory defaults
#ifdef ubxOnly
myGNSS.setSPIOutput(COM_TYPE_UBX); //Set the SPI port to output only UBX
#else
myGNSS.setSPIOutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the SPI port to output both UBX and NMEA messages
#endif
//myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR
Serial.print(F("Waiting for a 3D fix"));
uint8_t fix = 0;
do
{
fix = myGNSS.getFixType();
delay(1000);
Serial.print(F("."));
}
while ( fix != 3 ); // Wait for a 3D fix
Serial.println();
uint16_t y = myGNSS.getYear();
uint8_t M = myGNSS.getMonth();
uint8_t d = myGNSS.getDay();
uint8_t h = myGNSS.getHour();
uint8_t m = myGNSS.getMinute();
uint8_t s = myGNSS.getSecond();
char szBuffer[40] = {'\0'};
snprintf(szBuffer, sizeof(szBuffer), "/%04d%02d%02d%02d%02d%02d.ubx", y, M, d, h, m, s);
Serial.println(F("GNSS initialized."));
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Initialize the SD card. Open the log file
Serial.println(F("Initializing SD card..."));
// Begin the SD card
if(!SD_MMC.begin())
{
Serial.println(F("Card mount failed. Freezing..."));
while(1);
}
// Open the log file for writing
Serial.printf("Log file is: %s\r\n", szBuffer);
myFile = SD_MMC.open((const char *)szBuffer, FILE_WRITE);
if(!myFile)
{
Serial.println(F("Failed to open log file for writing. Freezing..."));
while(1);
}
Serial.println(F("SD card initialized."));
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Wait for a key press
while (Serial.available()) // Make sure the Serial buffer is empty
{
Serial.read();
}
Serial.println(F("Press any key to start logging."));
while (!Serial.available()) // Wait for the user to press a key
{
; // Do nothing
}
delay(100); // Wait, just in case multiple characters were sent
while (Serial.available()) // Empty the Serial buffer
{
Serial.read();
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable RAWX and SFRBX
myGNSS.setAutoRXMSFRBXcallbackPtr(&newSFRBX); // Enable automatic RXM SFRBX messages with callback to newSFRBX
myGNSS.logRXMSFRBX(); // Enable RXM SFRBX data logging
myGNSS.setAutoRXMRAWXcallbackPtr(&newRAWX); // Enable automatic RXM RAWX messages with callback to newRAWX
myGNSS.logRXMRAWX(); // Enable RXM RAWX data logging
myBuffer = new uint8_t[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card
#ifndef ubxOnly
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_SPI, navRate); // Ensure the GxGGA (Global positioning system fix data) message is enabled. Send every second.
myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_SPI, navRate); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every second.
myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_SPI, navRate); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every second.
myGNSS.enableNMEAMessage(UBX_NMEA_GST, COM_PORT_SPI, navRate); // Ensure the GxGST (Position error statistics) message is enabled. Send every second.
myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_SPI, navRate); // Ensure the GxRMC (Recommended minimum: position, velocity and time) message is enabled. Send every second.
myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_GGA | SFE_UBLOX_FILTER_NMEA_GSA | SFE_UBLOX_FILTER_NMEA_GSV | SFE_UBLOX_FILTER_NMEA_GST | SFE_UBLOX_FILTER_NMEA_RMC); // Log only these NMEA messages
#endif
myGNSS.setNavigationFrequency(navRate); // Set navigation rate
Serial.println(F("Press any key to stop logging."));
lastPrint = millis(); // Initialize lastPrint
}
void loop()
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer
{
digitalWrite(STAT_LED, HIGH); // Flash the STAT LED each time we write to the SD card
myGNSS.extractFileBufferData(myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card
// In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
digitalWrite(STAT_LED, LOW); // Turn the STAT LED off again
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (millis() > (lastPrint + 1000)) // Print the message count once per second
{
uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now)
float bufferHigh = 100.0 * (float)maxBufferBytes / (float)fileBufferSize;
Serial.print(F("Message groups received: SFRBX: ")); // Print how many message groups have been received (see note above)
Serial.print(numSFRBX);
Serial.print(F(" RAWX: "));
Serial.print(numRAWX);
Serial.print(F(" \tBuffer high tide: "));
Serial.print(bufferHigh, 1); // It is a fun thing to watch how full the buffer gets
if (bufferHigh > 90.)
Serial.println(F("%!!"));
else if (bufferHigh > 80.)
Serial.println(F("%!"));
else
Serial.println(F("%"));
lastPrint = millis(); // Update lastPrint
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (Serial.available()) // Check if the user wants to stop logging
{
uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer
while (remainingBytes > 0) // While there is still data in the file buffer
{
digitalWrite(STAT_LED, HIGH); // Flash the STAT LED while we write to the SD card
uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time
if (bytesToWrite > sdWriteSize)
{
bytesToWrite = sdWriteSize;
}
myGNSS.extractFileBufferData(myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer
myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card
remainingBytes -= bytesToWrite; // Decrement remainingBytes
}
digitalWrite(STAT_LED, LOW); // Turn the STAT LED off
myFile.close(); // Close the data file
myGNSS.setNavigationFrequency(1); // Set navigation rate to 1Hz
myGNSS.disableMessage(UBX_CLASS_RXM, UBX_RXM_RAWX, COM_PORT_SPI);
myGNSS.disableMessage(UBX_CLASS_RXM, UBX_RXM_SFRBX, COM_PORT_SPI);
myGNSS.setSPIOutput(COM_TYPE_UBX | COM_TYPE_NMEA); // Re-enable NMEA
Serial.println(F("Logging stopped. Freezing..."));
while(1); // Do nothing more
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
}

View file

@ -0,0 +1,75 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GNSS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
To take advantage of the internal IMU of either the Dead Reckoning GNSS
boards (ZED-F9R, NEO-M8U), you must first calibrate it. This includes securing the GNSS module
to your vehicle so that it is stable within 2 degrees and that the frame of
reference of the board is consistent with the picture outlined in the
Receiver-Description-Prot-Spec Datasheet under Automotive/Untethered Dead
Reckoning. You may also check either the ZED-F9R or NEO-M8U Hookup Guide for
more information. After the board is secure, you'll need to put the module
through certain conditions for proper calibration: acceleration, turning,
stopping for a few minutes, getting to a speed over 30km/h all under a clear sky
with good GNSS signal. This example simply looks at the
"fusionMode" status which indicates whether the SparkFun Dead Reckoning is
initializing - 0, calibrated - 1, or if an error has occurred - 2,3.
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.resetIMUalignment(); // Uncomment this line to reset the IMU alignment
}
void loop()
{
// ESF data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getEsfInfo()) // Poll new ESF STATUS data
{
Serial.print(F("Fusion Mode: "));
Serial.print(myGNSS.packetUBXESFSTATUS->data.fusionMode);
if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 0)
Serial.println(F(" Sensor is initializing..."));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 1)
Serial.println(F(" Sensor is calibrated!"));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 2)
Serial.println(F(" Sensor fusion is suspended!"));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 3)
Serial.println(F(" Sensor fusion is disabled!"));
}
delay(250);
}

View file

@ -0,0 +1,91 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GNSS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module, also known as "Fusion Mode", you can get
data directly from the IMU. This data is integrated directly into the GNSS
output, but is provided by the module as well.
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
if (myGNSS.getEsfInfo()){
Serial.print(F("Fusion Mode: "));
Serial.println(myGNSS.packetUBXESFSTATUS->data.fusionMode);
if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 1){
Serial.println(F("Fusion Mode is Initialized!"));
}
else {
Serial.println(F("Fusion Mode is either disabled or not initialized!"));
Serial.println(F("Please see the previous example for more information."));
}
}
}
void loop()
{
// ESF data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getEsfIns()) // Poll new ESF INS data
{
Serial.print(F("X Ang Rate: "));
Serial.print(myGNSS.packetUBXESFINS->data.xAngRate);
Serial.print(F(" Y Ang Rate: "));
Serial.print(myGNSS.packetUBXESFINS->data.yAngRate);
Serial.print(F(" Z Ang Rate: "));
Serial.print(myGNSS.packetUBXESFINS->data.zAngRate);
Serial.print(F(" X Accel: "));
Serial.print(myGNSS.packetUBXESFINS->data.xAccel);
Serial.print(F(" Y Accel: "));
Serial.print(myGNSS.packetUBXESFINS->data.yAccel);
Serial.print(F(" Z Accel: "));
Serial.print(myGNSS.packetUBXESFINS->data.zAccel);
// These values also have "validity checks" that can be provided by the
// ublox library by reading bitfield0
Serial.print(F(" Validity: "));
Serial.print(myGNSS.packetUBXESFINS->data.bitfield0.bits.xAngRateValid);
Serial.print(myGNSS.packetUBXESFINS->data.bitfield0.bits.yAngRateValid);
Serial.print(myGNSS.packetUBXESFINS->data.bitfield0.bits.zAngRateValid);
Serial.print(myGNSS.packetUBXESFINS->data.bitfield0.bits.xAccelValid);
Serial.print(myGNSS.packetUBXESFINS->data.bitfield0.bits.yAccelValid);
Serial.println(myGNSS.packetUBXESFINS->data.bitfield0.bits.zAccelValid);
}
delay(250);
}

View file

@ -0,0 +1,125 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GNSS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module, also known as "Fusion Mode", you can get
data directly from the IMU. This example code walks you through trouble
shooting or identifying the different states of any individual
"external" (which include internal) sensors you've hooked up (vehicle speed
sensor) or the internal IMU used by the modules. You can see if the sensor is
being used, if it's calibrated, ready, what data type it returns, the state
of the measurement etc.
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// GetEsfInfo also gets the number of sensors used by the ublox module, this
// includes (in the case of the ZED-F9R) wheel tick input from the vehicle
// speed sensor attached to the module.
if (myGNSS.getEsfInfo()){
Serial.print(F("Fusion Mode: "));
Serial.println(myGNSS.packetUBXESFSTATUS->data.fusionMode);
if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 1){
Serial.println(F("Fusion Mode is Initialized!"));
}
else {
Serial.println(F("Fusion Mode is either disabled or not initialized!"));
Serial.println(F("Please see the previous example for more information."));
}
}
}
void loop()
{
// ESF data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getEsfInfo()) // Poll new ESF STATUS data
{
UBX_ESF_STATUS_sensorStatus_t sensorStatus; // Create storage for the individual sensor status
//See ublox receiver description or our hookup guide for information on the return values
Serial.println(F(" "));
Serial.println(F(" C "));
Serial.println(F(" a "));
Serial.println(F(" l "));
Serial.println(F(" i "));
Serial.println(F(" b M "));
Serial.println(F(" r i N"));
Serial.println(F(" a B s o"));
Serial.println(F(" S t T B a s i"));
Serial.println(F("S e B i i a d e s"));
Serial.println(F("e n e o m d d y"));
Serial.println(F("n s i I n e T "));
Serial.println(F("s o n s M i M M"));
Serial.println(F("o r g S S e m e e"));
Serial.println(F("r R t t a e a a"));
Serial.println(F(" T U e a a s s s"));
Serial.println(F("N y s a t t u T u u"));
Serial.println(F("o p e d u u r a r r"));
Serial.println(F(". e d y s s e g e e"));
Serial.println(F(" "));
for(uint8_t i = 0; i < myGNSS.packetUBXESFSTATUS->data.numSens; i++)
{
myGNSS.getSensorFusionStatus(&sensorStatus, i); // Extract the individual sensor data for this sensor
Serial.print(i); Serial.print(F(" ")); // Print the sensor number
// Print the sensor type
Serial.print(sensorStatus.sensStatus1.bits.type);
if (sensorStatus.sensStatus1.bits.type < 10) Serial.print(F(" "));
Serial.print(F(" "));
Serial.print(sensorStatus.sensStatus1.bits.used); Serial.print(F(" ")); // Print the used flag
Serial.print(sensorStatus.sensStatus1.bits.ready); Serial.print(F(" ")); // Print the ready flag
Serial.print(sensorStatus.sensStatus2.bits.calibStatus); Serial.print(F(" ")); // Print the calibration status
Serial.print(sensorStatus.sensStatus2.bits.timeStatus); Serial.print(F(" ")); // Print the time status
Serial.print(sensorStatus.faults.bits.badMeas); Serial.print(F(" ")); // Print the bad measurement flag
Serial.print(sensorStatus.faults.bits.badTTag); Serial.print(F(" ")); // Print the time tag flag
Serial.print(sensorStatus.faults.bits.missingMeas); Serial.print(F(" ")); // Print the missing measurement flag
Serial.print(sensorStatus.faults.bits.noisyMeas); // Print the noisy measure flag
Serial.println();
}
}
delay(250);
}

View file

@ -0,0 +1,95 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GNSS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
getEsfAlignment (UBX-ESF-ALG) reports the status and alignment angles of the IMU within the vehicle.
These define the rotation of the IMU frame within the vehicle (installation frame) - not the heading
of the vehicle itself. The vehicle attitude solution is reported separately by getNAVATT (UBX-NAV-ATT).
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setESFAutoAlignment(true); //Enable Automatic IMU-mount Alignment
if (myGNSS.getEsfInfo()){
Serial.print(F("Fusion Mode: "));
Serial.println(myGNSS.packetUBXESFSTATUS->data.fusionMode);
if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 1){
Serial.println(F("Fusion Mode is Initialized!"));
}
else {
Serial.println(F("Fusion Mode is either disabled or not initialized!"));
Serial.println(F("Please see the previous example for more information."));
}
}
}
void loop()
{
// ESF data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getEsfAlignment()) // Poll new ESF ALG data
{
Serial.print(F("IMU-Mount Alignment: On/Off: "));
Serial.print(myGNSS.packetUBXESFALG->data.flags.bits.autoMntAlgOn);
Serial.print(F(" Status: "));
Serial.print(myGNSS.packetUBXESFALG->data.flags.bits.status);
Serial.print(F(" Roll: "));
Serial.print(myGNSS.getESFroll(), 2); // Use the helper function to get the roll in degrees
Serial.print(F(" Pitch: "));
Serial.print(myGNSS.getESFpitch(), 2); // Use the helper function to get the pitch in degrees
Serial.print(F(" Yaw: "));
Serial.print(myGNSS.getESFyaw(), 2); // Use the helper function to get the yaw in degrees
Serial.print(F(" Errors: "));
Serial.print(myGNSS.packetUBXESFALG->data.error.bits.tiltAlgError);
Serial.print(myGNSS.packetUBXESFALG->data.error.bits.yawAlgError);
Serial.println(myGNSS.packetUBXESFALG->data.error.bits.angleError);
}
if (myGNSS.getNAVATT()) // Poll new NAV ATT data
{
Serial.print(F("Vehicle Attitude: Roll: "));
Serial.print(myGNSS.getATTroll(), 2); // Use the helper function to get the roll in degrees
Serial.print(F(" Pitch: "));
Serial.print(myGNSS.getATTpitch(), 2); // Use the helper function to get the pitch in degrees
Serial.print(F(" Heading: "));
Serial.println(myGNSS.getATTheading(), 2); // Use the helper function to get the heading in degrees
}
delay(250);
}

View file

@ -0,0 +1,94 @@
/*
By: Paul Clark
SparkFun Electronics
Date: December, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the High Navigation Rate on the NEO-M8U and then
polls and displays the attitude solution, vehicle dynamics information
and high rate position, velocity and time.
This example polls the high rate data.
(The next example uses "autoHNR" to receive the HNR data automatically.)
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("Warning! u-blox GPS did not begin correctly."));
Serial.println(F("(This may be because the I2C port is busy with HNR messages.)"));
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
if (myGNSS.setHNRNavigationRate(10) == true) //Set the High Navigation Rate to 10Hz
Serial.println(F("setHNRNavigationRate was successful"));
else
Serial.println(F("setHNRNavigationRate was NOT successful"));
myGNSS.setAutoHNRATT(false); //Make sure auto HNR attitude messages are disabled
myGNSS.setAutoHNRINS(false); //Make sure auto HNR vehicle dynamics messages are disabled
myGNSS.setAutoHNRPVT(false); //Make sure auto HNR PVT messages are disabled
}
void loop()
{
// Poll and print selected HNR data
if (myGNSS.getHNRAtt(125) == true) // Request HNR Att data using a 125ms timeout
{
Serial.print(F("Roll: "));
Serial.print(myGNSS.getHNRroll(), 2); // Use the helper function to get the roll in degrees
Serial.print(F(" Pitch: "));
Serial.print(myGNSS.getHNRpitch(), 2); // Use the helper function to get the pitch in degrees
Serial.print(F(" Heading: "));
Serial.println(myGNSS.getHNRheading(), 2); // Use the helper function to get the heading in degrees
}
if (myGNSS.getHNRDyn(125) == true) // Request HNR Dyn data using a 125ms timeout
{
Serial.print(F("xAccel: "));
Serial.print(myGNSS.packetUBXHNRINS->data.xAccel);
Serial.print(F(" yAccel: "));
Serial.print(myGNSS.packetUBXHNRINS->data.yAccel);
Serial.print(F(" zAccel: "));
Serial.println(myGNSS.packetUBXHNRINS->data.zAccel);
}
if (myGNSS.getHNRPVT(125) == true) // Request HNR PVT data using a 125ms timeout
{
Serial.print(F("ns: "));
Serial.print(myGNSS.packetUBXHNRPVT->data.nano);
Serial.print(F(" Lat: "));
Serial.print(myGNSS.packetUBXHNRPVT->data.lat);
Serial.print(F(" Lon: "));
Serial.println(myGNSS.packetUBXHNRPVT->data.lon);
}
}

View file

@ -0,0 +1,107 @@
/*
By: Paul Clark
SparkFun Electronics
Date: December, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the High Navigation Rate on the NEO-M8U and then
reads and displays the attitude solution, vehicle dynamics information
and high rate position, velocity and time.
This example uses "autoHNR" to receive the HNR data automatically.
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
bool usingAutoHNRAtt = false;
bool usingAutoHNRDyn = false;
bool usingAutoHNRPVT = false;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("Warning! u-blox GPS did not begin correctly."));
Serial.println(F("(This may be because the I2C port is busy with HNR messages.)"));
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
if (myGNSS.setHNRNavigationRate(10) == true) //Set the High Navigation Rate to 10Hz
Serial.println(F("setHNRNavigationRate was successful"));
else
Serial.println(F("setHNRNavigationRate was NOT successful"));
usingAutoHNRAtt = myGNSS.setAutoHNRATT(true); //Attempt to enable auto HNR attitude messages
if (usingAutoHNRAtt)
Serial.println(F("AutoHNRATT successful"));
usingAutoHNRDyn = myGNSS.setAutoHNRINS(true); //Attempt to enable auto HNR vehicle dynamics messages
if (usingAutoHNRDyn)
Serial.println(F("AutoHNRINS successful"));
usingAutoHNRPVT = myGNSS.setAutoHNRPVT(true); //Attempt to enable auto HNR PVT messages
if (usingAutoHNRPVT)
Serial.println(F("AutoHNRPVT successful"));
}
void loop()
{
if (usingAutoHNRAtt && (myGNSS.getHNRAtt() == true)) // If setAutoHNRAtt was successful and new data is available
{
Serial.print(F("Roll: ")); // Print selected data
Serial.print(myGNSS.getHNRroll(), 2); // Use the helper function to get the roll in degrees
Serial.print(F(" Pitch: "));
Serial.print(myGNSS.getHNRpitch(), 2); // Use the helper function to get the pitch in degrees
Serial.print(F(" Heading: "));
Serial.println(myGNSS.getHNRheading(), 2); // Use the helper function to get the heading in degrees
myGNSS.flushHNRATT(); // Mark data as stale
}
if (usingAutoHNRDyn && (myGNSS.getHNRDyn() == true)) // If setAutoHNRDyn was successful and new data is available
{
Serial.print(F("xAccel: ")); // Print selected data
Serial.print(myGNSS.packetUBXHNRINS->data.xAccel);
Serial.print(F(" yAccel: "));
Serial.print(myGNSS.packetUBXHNRINS->data.yAccel);
Serial.print(F(" zAccel: "));
Serial.println(myGNSS.packetUBXHNRINS->data.zAccel);
myGNSS.flushHNRINS(); // Mark data as stale
}
if (usingAutoHNRPVT && (myGNSS.getHNRPVT() == true)) // If setAutoHNRPVT was successful and new data is available
{
Serial.print(F("ns: ")); // Print selected data
Serial.print(myGNSS.packetUBXHNRPVT->data.nano);
Serial.print(F(" Lat: "));
Serial.print(myGNSS.packetUBXHNRPVT->data.lat);
Serial.print(F(" Lon: "));
Serial.println(myGNSS.packetUBXHNRPVT->data.lon);
myGNSS.flushHNRPVT(); // Mark data as stale
}
}

View file

@ -0,0 +1,116 @@
/*
By: Nathan Seidle
SparkFun Electronics
Date: August, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example configures the AutoAlignment option for the IMU.
The ZED-F9R Integration guide recommends enabling Auto Alignment once
the device has been attached to the vehicle's frame.
Enabling auto-alignment will cause the the sensor fusion status
to begin initialization. After driving around a few turns, the sensors
should enter 'Calibrated' state. See example 1 for fusion state or
monitor UBX-ESF-STATUS.
As of writing the ZED-F9R is using HPS v1.2 firmware. Please update using u-center if necessary.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9R: https://www.sparkfun.com/products/16344
ZED-F9R pHat: https://www.sparkfun.com/products/16475
NEO-M8U: https://www.sparkfun.com/products/16329
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/17912)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GPS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("Warning! u-blox GPS did not begin correctly."));
Serial.println(F("(This may be because the I2C port is busy with HNR messages.)"));
}
bool esfAutoAlignment = myGNSS.getESFAutoAlignment();
Serial.print(F("esfAutoAlignment: "));
if (esfAutoAlignment == true)
Serial.println(F("True"));
else
Serial.println(F("False"));
myGNSS.setESFAutoAlignment(true); //Enable UBX-CFG-ESFALG Automatic IMU-mount Alignment
myGNSS.setAutoHNRATT(false); //Make sure auto HNR attitude messages are disabled
myGNSS.setAutoHNRINS(false); //Make sure auto HNR vehicle dynamics messages are disabled
myGNSS.setAutoHNRPVT(false); //Make sure auto HNR PVT messages are disabled
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
}
void loop()
{
// ESF data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getEsfInfo()) // Poll new ESF STATUS data
{
Serial.print(F("Fusion Mode: "));
Serial.print(myGNSS.packetUBXESFSTATUS->data.fusionMode);
if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 0)
Serial.println(F(" Sensor is initializing..."));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 1)
Serial.println(F(" Sensor is calibrated!"));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 2)
Serial.println(F(" Sensor fusion is suspended!"));
else if (myGNSS.packetUBXESFSTATUS->data.fusionMode == 3)
Serial.println(F(" Sensor fusion is disabled!"));
}
// Poll and print selected HNR data
if (myGNSS.getHNRAtt(125) == true) // Request HNR Att data using a 125ms timeout
{
Serial.print(F("Roll: "));
Serial.print(myGNSS.getHNRroll(), 2); // Use the helper function to get the roll in degrees
Serial.print(F(" Pitch: "));
Serial.print(myGNSS.getHNRpitch(), 2); // Use the helper function to get the pitch in degrees
Serial.print(F(" Heading: "));
Serial.println(myGNSS.getHNRheading(), 2); // Use the helper function to get the heading in degrees
}
if (myGNSS.getHNRDyn(125) == true) // Request HNR Dyn data using a 125ms timeout
{
Serial.print(F("xAccel: "));
Serial.print(myGNSS.packetUBXHNRINS->data.xAccel);
Serial.print(F(" yAccel: "));
Serial.print(myGNSS.packetUBXHNRINS->data.yAccel);
Serial.print(F(" zAccel: "));
Serial.println(myGNSS.packetUBXHNRINS->data.zAccel);
}
if (myGNSS.getHNRPVT(125) == true) // Request HNR PVT data using a 125ms timeout
{
Serial.print(F("ns: "));
Serial.print(myGNSS.packetUBXHNRPVT->data.nano);
Serial.print(F(" Lat: "));
Serial.print(myGNSS.packetUBXHNRPVT->data.lat);
Serial.print(F(" Lon: "));
Serial.println(myGNSS.packetUBXHNRPVT->data.lon);
}
}

View file

@ -0,0 +1,111 @@
/*
By: Paul CLark
SparkFun Electronics
Date: January, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GNSS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module and securing it to your vehicle such that it's
stable within 2 degrees, and the board is oriented correctly with regards to
the vehicle's frame, you can now read the vehicle's "attitude". The attitude
includes the vehicle's heading, pitch, and roll.
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
}
void loop()
{
// PVAT data is produced at the navigation rate, so by default we'll get fresh data once per second
if (myGNSS.getNAVPVAT()) // Poll new PVAT
{
Serial.print(F("Roll: "));
Serial.print((float)myGNSS.getVehicleRoll() / 100000.0, 5); // Use the helper function to get the roll in degrees * 10^-5
Serial.print(F(" Pitch: "));
Serial.print((float)myGNSS.getVehiclePitch() / 100000.0, 5); // Use the helper function to get the pitch in degrees * 10^-5
Serial.print(F(" Heading: "));
Serial.print((float)myGNSS.getVehicleHeading() / 100000.0, 5); // Use the helper function to get the heading in degrees * 10^-5
// We don't have helper functions to extract the roll, pitch and heading valid flags from the PVAT message. But we can do it manually:
Serial.print(F(" Roll Valid: "));
Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehRollValid);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehRollValid = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.print(F(" Pitch Valid: "));
Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehPitchValid);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehPitchValid = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.print(F(" Heading Valid: "));
Serial.print(myGNSS.packetUBXNAVPVAT->data.flags.bits.vehHeadingValid);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.vehHeadingValid = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
// We don't have helper functions to extract the roll, pitch and heading accuracy from the PVAT message. But we can do it manually:
Serial.print(F(" Roll Acc: "));
Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accRoll) / 100, 2);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accRoll = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.print(F(" Pitch Acc: "));
Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accPitch) / 100, 2);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accPitch = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.print(F(" Heading Acc: "));
Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.accHeading) / 100, 2);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.accHeading = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
// We don't have helper functions to extract the lat and lon from the PVAT message. But we can do it manually:
Serial.print(F(" Lat: "));
Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.lat) / 10000000.0, 7);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.lat = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.print(F(" Lon: "));
Serial.print(((float)myGNSS.packetUBXNAVPVAT->data.lon) / 10000000.0, 7);
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.lon = false; // Mark the data as stale
myGNSS.packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false;
Serial.println();
}
delay(250);
}

View file

@ -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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
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 u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
}
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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
long altitudeMSL = myGNSS.getAltitudeMSL();
Serial.print(F(" AltMSL: "));
Serial.print(altitudeMSL);
Serial.print(F(" (mm)"));
Serial.println();
}
}

View file

@ -0,0 +1,60 @@
/*
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:
Plug a Qwiic cable into the GNSS 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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
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
myGNSS.factoryReset(); //Reset everything: baud rate, I2C address, update rate, everything.
delay(5000); // Wait while the module restarts
while (myGNSS.begin() == false) //Attempt to re-connect
{
delay(1000);
Serial.println(F("Attempting to re-connect to u-blox GNSS..."));
}
Serial.println("Unit has now been factory reset. Freezing...");
while(1); // Do nothing more
}
void loop()
{
}

View file

@ -0,0 +1,110 @@
/*
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 a Uno or don't have a 2nd serial port (Serial1), use SoftwareSerial instead (see below)
Open the serial monitor at 115200 baud to see the output
*/
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <SoftwareSerial.h>
//#define mySerial Serial1 // Uncomment this line to connect via Serial1
// - or -
SoftwareSerial mySerial(10, 11); // Uncomment this line to connect via SoftwareSerial(RX, TX). Connect pin 10 to GNSS TX pin.
#define defaultRate 9600 // Uncomment this line if you are using an M8 - which defaults to 9600 Baud on UART1
// - or -
//#define defaultRate 38400 // Uncomment this line if you are using an F9 - which defaults to 38400 Baud on UART1
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 u-blox Example");
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
}
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("GNSS: trying 38400 baud");
mySerial.begin(38400);
if (myGNSS.begin(mySerial)) break;
delay(100);
Serial.println("GNSS: trying 9600 baud");
mySerial.begin(9600);
if (myGNSS.begin(mySerial)) {
Serial.println("GNSS: connected at 9600 baud, switching to 38400");
myGNSS.setSerialRate(38400);
delay(100);
} else {
delay(2000); //Wait a bit before trying again to limit the Serial output flood
}
} while(1);
myGNSS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only
myGNSS.saveConfiguration(); //Save the current settings to flash and BBR
Serial.println("GNSS serial connected, saved config");
state++;
break;
case 1: // hardReset, expect to see GNSS back at 38400 baud
Serial.println("Issuing hardReset (cold start)");
myGNSS.hardReset();
delay(2000);
mySerial.begin(38400);
if (myGNSS.begin(mySerial)) {
Serial.println("Success.");
state++;
} else {
Serial.println("*** GNSS did not respond at 38400 baud, starting over.");
state = 0;
}
break;
case 2: // factoryReset, expect to see GNSS back at defaultRate baud
Serial.println("Issuing factoryReset");
myGNSS.factoryReset();
delay(5000); // takes more than one second... a loop to resync would be best
mySerial.begin(defaultRate);
if (myGNSS.begin(mySerial)) {
Serial.println("Success.");
state++;
} else {
Serial.println("*** GNSS did not come back at defaultRate baud, starting over.");
state = 0;
}
break;
case 3: // print version info
// Note: this may fail on boards like the UNO (ATmega328P) with modules like the ZED-F9P
// because getProtocolVersion returns a lot of data - more than the UNO's serial buffer can hold
Serial.print("GNSS protocol version: ");
Serial.print(myGNSS.getProtocolVersionHigh());
Serial.print('.');
Serial.println(myGNSS.getProtocolVersionLow());
Serial.println("All finished! Freezing...");
while(1);
}
delay(1000);
}

View file

@ -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_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX. Pin 10 on Uno goes to TX pin on GNSS module.
long lastTime = 0; //Simple local timer. Limits amount of I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
//Assume that the U-Blox GNSS 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("GNSS: trying 38400 baud");
mySerial.begin(38400);
if (myGNSS.begin(mySerial) == true) break;
delay(100);
Serial.println("GNSS: trying 9600 baud");
mySerial.begin(9600);
if (myGNSS.begin(mySerial) == true) {
Serial.println("GNSS: connected at 9600 baud, switching to 38400");
myGNSS.setSerialRate(38400);
delay(100);
} else {
//myGNSS.factoryReset();
delay(2000); //Wait a bit before trying again to limit the Serial output
}
} while(1);
Serial.println("GNSS serial connected");
myGNSS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
}

View file

@ -0,0 +1,141 @@
/*
Configuring the GNSS 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 GNSS the send navigation reports automatically
and retrieving the latest one via getPVT. This eliminates the blocking in getPVT while the GNSS
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 GNSS
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVT(true); //Tell the GNSS to "send" each solution
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
}
void loop()
{
// Calling getPVT returns true if there actually is a fresh navigation solution available.
// Start the reading only when valid LLH is available
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false))
{
Serial.println();
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
int PDOP = myGNSS.getPDOP();
Serial.print(F(" PDOP: "));
Serial.print(PDOP);
Serial.print(F(" (10^-2)"));
int nedNorthVel = myGNSS.getNedNorthVel();
Serial.print(F(" VelN: "));
Serial.print(nedNorthVel);
Serial.print(F(" (mm/s)"));
int nedEastVel = myGNSS.getNedEastVel();
Serial.print(F(" VelE: "));
Serial.print(nedEastVel);
Serial.print(F(" (mm/s)"));
int nedDownVel = myGNSS.getNedDownVel();
Serial.print(F(" VelD: "));
Serial.print(nedDownVel);
Serial.print(F(" (mm/s)"));
int verticalAccEst = myGNSS.getVerticalAccEst();
Serial.print(F(" VAccEst: "));
Serial.print(verticalAccEst);
Serial.print(F(" (mm)"));
int horizontalAccEst = myGNSS.getHorizontalAccEst();
Serial.print(F(" HAccEst: "));
Serial.print(horizontalAccEst);
Serial.print(F(" (mm)"));
int speedAccEst = myGNSS.getSpeedAccEst();
Serial.print(F(" SpeedAccEst: "));
Serial.print(speedAccEst);
Serial.print(F(" (mm/s)"));
int headAccEst = myGNSS.getHeadingAccEst();
Serial.print(F(" HeadAccEst: "));
Serial.print(headAccEst);
Serial.print(F(" (degrees * 10^-5)"));
if (myGNSS.getHeadVehValid() == true) {
int headVeh = myGNSS.getHeadVeh();
Serial.print(F(" HeadVeh: "));
Serial.print(headVeh);
Serial.print(F(" (degrees * 10^-5)"));
int magDec = myGNSS.getMagDec();
Serial.print(F(" MagDec: "));
Serial.print(magDec);
Serial.print(F(" (degrees * 10^-2)"));
int magAcc = myGNSS.getMagAcc();
Serial.print(F(" MagAcc: "));
Serial.print(magAcc);
Serial.print(F(" (degrees * 10^-2)"));
}
Serial.println();
} else {
Serial.print(".");
delay(50);
}
}

View file

@ -0,0 +1,103 @@
/*
Configuring the GNSS 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 GNSS 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 GNSS
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVT(true, false); //Tell the GNSS to "send" each solution and the lib not to update stale data implicitly
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
}
/*
Calling getPVT would return false now (compare to previous example 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 GNSS 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
// GNSS 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
// call checkUblox all 50ms to capture the GNSS data
if (counter % 50 == 0)
{
myGNSS.checkUblox();
}
delay(1);
counter++;
}

View file

@ -0,0 +1,107 @@
/*
Configuring the GNSS to automatically send position reports over Serial
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 U-Blox GNSS the send navigation reports automatically
and retrieving the latest one via getPVT. This eliminates the blocking in getPVT while the GNSS
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 Serial use. With serial the GNSS
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.
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 GNSS 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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <SoftwareSerial.h>
//#define mySerial Serial2 // Uncomment this line to connect via Serial2
// - or -
SoftwareSerial mySerial(10, 11); // Uncomment this line to connect via SoftwareSerial(RX, TX). Connect pin 10 to GNSS TX pin.
//#define baudRate 9600 // Uncomment this line to select 9600 Baud for the M8
// - or -
#define baudRate 38400 // Uncomment this line to select 38400 Baud for the F9
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
mySerial.begin(baudRate); // Start the Serial port
if (myGNSS.begin(mySerial) == false) //Connect to the u-blox module using Serial
{
Serial.println(F("u-blox GNSS not detected. Please check wiring. Freezing."));
while (1);
}
myGNSS.setUART1Output(COM_TYPE_UBX); //Set the UART1 port to output UBX only (turn off NMEA noise)
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVT(true); //Tell the GNSS to "send" each solution
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
}
void loop()
{
// getPVT will return true if there actually is a fresh navigation solution available.
// Important note: the PVT message is 100 bytes long. We need to call getPVT often enough
// to prevent serial buffer overflows on boards like the original RedBoard / UNO.
// At 38400 Baud, the 100 PVT bytes will arrive in 26ms.
// On the RedBoard, we need to call getPVT every 5ms to keep up.
if (myGNSS.getPVT())
{
Serial.println();
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
else
{
delay(5); // Delay for 5ms only
static int counter = 0; // Print a dot every 50ms
counter++;
if (counter > 10)
{
Serial.print(".");
counter = 0;
}
}
}

View file

@ -0,0 +1,110 @@
/*
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 :-)
This example only works correctly if the module has already been configured is sending PVT messages via its UART1 TX pin.
Run the previous example to enable the auto PVT messages if you need to.
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
Or, switch to SoftwareSerial
Open the serial monitor at 115200 baud to see the output
*/
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <SoftwareSerial.h>
//#define mySerial Serial2 // Uncomment this line to connect via Serial2
// - or -
SoftwareSerial mySerial(10, 11); // Uncomment this line to connect via SoftwareSerial(RX, TX). Connect pin 10 to GNSS TX pin.
//#define baudRate 9600 // Uncomment this line to select 9600 Baud for the M8
// - or -
#define baudRate 38400 // Uncomment this line to select 38400 Baud for the F9
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
//Use any Serial port with at least a RX Pin connected or a receive only version of SoftwareSerial here
//Assume that the U-Blox GNSS is running at baudRate baud
mySerial.begin(baudRate);
// No need to check return value as internal call to isConnected() will not succeed
myGNSS.begin(mySerial);
// Tell the library 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)
myGNSS.assumeAutoPVT(true, true);
}
void loop()
{
// If implicit updates are enabled, calling getPVT will trigger parsing of the incoming messages
// and return 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.
//
// Important note: the PVT message is 100 bytes long. We need to call getPVT often enough
// to prevent serial buffer overflows on boards like the original RedBoard / UNO.
// At 38400 Baud, the 100 PVT bytes will arrive in 26ms.
// On the RedBoard, we need to call getPVT every 5ms to keep up.
if (myGNSS.getPVT())
{
Serial.println();
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
else
{
delay(5); // Delay for 5ms only
static int counter = 0; // Print a dot every 50ms
counter++;
if (counter > 10)
{
Serial.print(".");
counter = 0;
}
}
}

View file

@ -0,0 +1,107 @@
/*
Debug Output
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).
You can also limit the debug messages to the "critical" ones by adding an extra argument.
The debug messages can be disabled again by calling disableDebugging()
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
unsigned long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
int counter = 0; // Disable the debug messages when counter reaches 20
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
myGNSS.enableDebugging(); //Enable all the debug messages over Serial (default)
//myGNSS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB
//myGNSS.enableDebugging(Serial, true); //Enable only the critical debug messages over Serial
}
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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.print(F(" "));
Serial.print(myGNSS.getYear());
Serial.print(F("-"));
Serial.print(myGNSS.getMonth());
Serial.print(F("-"));
Serial.print(myGNSS.getDay());
Serial.print(F(" "));
Serial.print(myGNSS.getHour());
Serial.print(F(":"));
Serial.print(myGNSS.getMinute());
Serial.print(F(":"));
Serial.println(myGNSS.getSecond());
Serial.println();
counter++; // Increment counter
if (counter == 20)
{
myGNSS.disableDebugging(); // Disable the debug messages when counter reaches 20
}
}
}

View file

@ -0,0 +1,107 @@
/*
Getting time and date using u-blox 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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
Serial.print(myGNSS.getYear());
Serial.print("-");
Serial.print(myGNSS.getMonth());
Serial.print("-");
Serial.print(myGNSS.getDay());
Serial.print(" ");
Serial.print(myGNSS.getHour());
Serial.print(":");
Serial.print(myGNSS.getMinute());
Serial.print(":");
Serial.print(myGNSS.getSecond());
Serial.print(" Time is ");
if (myGNSS.getTimeValid() == false)
{
Serial.print("not ");
}
Serial.print("valid Date is ");
if (myGNSS.getDateValid() == false)
{
Serial.print("not ");
}
Serial.print("valid");
Serial.println();
}
}

View file

@ -0,0 +1,109 @@
/*
Getting time and date using u-blox 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 u-blox 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.
Note: you will need to set your Serial Monitor to 500000 Baud to see the output
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(500000); //Increase serial speed to maximize
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
Wire.setClock(400000); // Increase I2C clock speed to 400kHz
//myGNSS.enableDebugging(); //Uncomment this line to enable debug messages over Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
myGNSS.setNavigationFrequency(5); //Set output to 5 times a second
byte rate = myGNSS.getNavigationFrequency(); //Get the update rate of this module
Serial.print("Current update rate: ");
Serial.println(rate);
}
void loop()
{
// Calling getPVT returns true if there actually is a fresh navigation solution available.
if (myGNSS.getPVT())
{
lastTime = millis(); //Update the timer
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.print(" ");
Serial.print(myGNSS.getYear());
Serial.print("-");
Serial.print(myGNSS.getMonth());
Serial.print("-");
Serial.print(myGNSS.getDay());
Serial.print(" ");
Serial.print(myGNSS.getHour());
Serial.print(":");
Serial.print(myGNSS.getMinute());
Serial.print(":");
Serial.print(myGNSS.getSecond());
Serial.print(" nanoseconds: ");
Serial.print(myGNSS.getNanosecond()); // Nanoseconds can be negative
myGNSS.flushPVT();
Serial.println();
}
}

View file

@ -0,0 +1,118 @@
/*
Getting time and date using u-blox commands
By: Nathan Seidle
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 use the Millisecond and Nanosecond output as well as increase the
I2C speed (100 to 400kHz), and serial output (115200 to 500kbps).
Note: you will need to set your Serial Monitor to 500000 Baud to see the output
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(500000); //Increase serial speed to maximize
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
Wire.setClock(400000); // Increase I2C clock speed to 400kHz
//myGNSS.enableDebugging(); //Uncomment this line to enable debug messages over Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Note: not all u-blox modules can output solutions at 10Hz - or not while tracking all satellite constellations
// If the rate drops back to 1Hz, you're asking too much of your module
myGNSS.setNavigationFrequency(10); //Set output to 10 times a second
byte rate = myGNSS.getNavigationFrequency(); //Get the update rate of this module
Serial.print("Current update rate:");
Serial.println(rate);
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
}
void loop()
{
// Calling getPVT returns true if there actually is a fresh navigation solution available.
if (myGNSS.getPVT())
{
lastTime = millis(); //Update the timer
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.print(" ");
Serial.print(myGNSS.getYear());
Serial.print("-");
Serial.print(myGNSS.getMonth());
Serial.print("-");
Serial.print(myGNSS.getDay());
Serial.print(" ");
Serial.print(myGNSS.getHour());
Serial.print(":");
Serial.print(myGNSS.getMinute());
Serial.print(":");
Serial.print(myGNSS.getSecond());
Serial.print(".");
//Pretty print leading zeros
int mseconds = myGNSS.getMillisecond();
if (mseconds < 100)
Serial.print("0");
if (mseconds < 10)
Serial.print("0");
Serial.print(mseconds);
Serial.print(" nanoseconds: ");
Serial.print(myGNSS.getNanosecond());
Serial.println();
}
}

View file

@ -0,0 +1,169 @@
/*
u-blox M8 geofence example
Written by Paul Clark (PaulZC)
10th December 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example demonstrates how to use the addGeofence and getGeofenceState functions
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/15210
ZOE-M8Q: https://www.sparkfun.com/products/15193
This example powers up the GNSS and reads the fix.
Once a valid 3D fix has been found, the code reads the latitude and longitude.
The code then sets four geofences around that position with a radii of 5m, 10m, 15m and 20m with 95% confidence.
The code then monitors the geofence status.
The LED will be illuminated if you are inside the _combined_ geofence (i.e. within the 20m radius).
This code has been tested on the ZOE-M8Q.
*/
#define LED LED_BUILTIN // Change this if your LED is on a different pin
#include <Wire.h> // Needed for I2C
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
pinMode(LED, OUTPUT);
// Set up the I2C pins
Wire.begin();
// Start the console serial port
Serial.begin(115200);
while (!Serial); // Wait for the user to open the serial monitor
delay(100);
Serial.println();
Serial.println();
Serial.println(F("u-blox M8 geofence example"));
Serial.println();
Serial.println();
delay(1000); // Let the GNSS power up
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
//myGNSS.enableDebugging(); // Enable debug messages
myGNSS.setI2COutput(COM_TYPE_UBX); // Limit I2C output to UBX (disable the NMEA noise)
Serial.println(F("Waiting for a 3D fix..."));
byte fixType = 0;
while (fixType < 3)
{
fixType = myGNSS.getFixType(); // Get the fix type
Serial.print(F("Fix: ")); // Print it
Serial.print(fixType);
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"));
else if(fixType == 5) Serial.print(F(" = Time only"));
Serial.println();
delay(1000);
}
Serial.println(F("3D fix found!"));
long latitude = myGNSS.getLatitude(); // Get the latitude in degrees * 10^-7
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude(); // Get the longitude in degrees * 10^-7
Serial.print(F(" Long: "));
Serial.println(longitude);
uint32_t radius = 500; // Set the radius to 5m (radius is in m * 10^-2 i.e. cm)
byte confidence = 2; // Set the confidence level: 0=none, 1=68%, 2=95%, 3=99.7%, 4=99.99%
// Call clearGeofences() to clear all existing geofences.
Serial.print(F("Clearing any existing geofences. clearGeofences returned: "));
Serial.println(myGNSS.clearGeofences());
// It is possible to define up to four geofences.
// Call addGeofence up to four times to define them.
Serial.println(F("Setting the geofences:"));
Serial.print(F("addGeofence for geofence 1 returned: "));
Serial.println(myGNSS.addGeofence(latitude, longitude, radius, confidence));
radius = 1000; // 10m
Serial.print(F("addGeofence for geofence 2 returned: "));
Serial.println(myGNSS.addGeofence(latitude, longitude, radius, confidence));
radius = 1500; // 15m
Serial.print(F("addGeofence for geofence 3 returned: "));
Serial.println(myGNSS.addGeofence(latitude, longitude, radius, confidence));
radius = 2000; // 20m
Serial.print(F("addGeofence for geofence 4 returned: "));
Serial.println(myGNSS.addGeofence(latitude, longitude, radius, confidence));
}
void loop()
{
geofenceState currentGeofenceState; // Create storage for the geofence state
bool result = myGNSS.getGeofenceState(currentGeofenceState);
Serial.print(F("getGeofenceState returned: ")); // Print the combined state
Serial.print(result); // Get the geofence state
if (!result) // If getGeofenceState did not return true
{
Serial.println(F(".")); // Tidy up
return; // and go round the loop again
}
Serial.print(F(". status is: ")); // Print the status
Serial.print(currentGeofenceState.status);
Serial.print(F(". numFences is: ")); // Print the numFences
Serial.print(currentGeofenceState.numFences);
Serial.print(F(". combState is: ")); // Print the combined state
Serial.print(currentGeofenceState.combState);
if (currentGeofenceState.combState == 0)
{
Serial.print(F(" = Unknown"));
digitalWrite(LED, LOW);
}
if (currentGeofenceState.combState == 1)
{
Serial.print(F(" = Inside"));
digitalWrite(LED, HIGH);
}
else if (currentGeofenceState.combState == 2)
{
Serial.print(F(" = Outside"));
digitalWrite(LED, LOW);
}
Serial.print(F(". The individual states are: ")); // Print the state of each geofence
for(int i = 0; i < currentGeofenceState.numFences; i++)
{
if (i > 0) Serial.print(F(","));
Serial.print(currentGeofenceState.states[i]);
}
Serial.println();
delay(1000);
}

View file

@ -0,0 +1,163 @@
/*
Power Save Mode
By: Paul Clark (PaulZC)
Date: April 22nd, 2020
Based extensively on Example3_GetPosition
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 put the u-blox module into power save mode and then
query its lat/long/altitude. We also turn off the NMEA output on the I2C port.
This decreases the amount of I2C traffic dramatically.
** When it is able to ** the module will reduce its current draw.
For the ZOE-M8Q with a passive antenna, you should see the current drop
from (approx.) 25-28mA to (approx.) 9mA when power save mode kicks in.
Note: this will fail on the ZED (protocol version >= 27) as UBX-CFG-RXM is not supported
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: Uncomment this line to save the current settings to flash and BBR
Serial.println("Power save example.");
Serial.println("1) Enable power saving");
Serial.println("2) Disable power saving");
}
void loop()
{
if (Serial.available())
{
byte incoming = Serial.read();
if (incoming == '1')
{
// Put the GNSS into power save mode
// (If you want to disable power save mode, call myGNSS.powerSaveMode(false) instead)
// This will fail on the ZED (protocol version >= 27) as UBX-CFG-RXM is not supported
if (myGNSS.powerSaveMode()) // Defaults to true
Serial.println(F("Power Save Mode enabled."));
else
Serial.println(F("*** Power Save Mode FAILED ***"));
}
else if (incoming == '2')
{
//Go to normal power mode (not power saving mode)
if (myGNSS.powerSaveMode(false))
Serial.println(F("Power Save Mode disabled."));
else
Serial.println(F("*** Power Save Disable FAILED ***"));
}
// Read and print the new low power mode
uint8_t lowPowerMode = myGNSS.getPowerSaveMode();
if (lowPowerMode == 255)
{
Serial.println(F("*** getPowerSaveMode FAILED ***"));
}
else
{
Serial.print(F("The low power mode is: "));
Serial.print(lowPowerMode);
if (lowPowerMode == 0)
{
Serial.println(F(" (Continuous)"));
}
else if (lowPowerMode == 1)
{
Serial.println(F(" (Power Save)"));
}
else if (lowPowerMode == 4)
{
Serial.println(F(" (Continuous)"));
}
else
{
Serial.println(F(" (Unknown!)"));
}
}
}
//Query module every 10 seconds so it is easier to monitor the current draw
if (millis() - lastTime > 10000)
{
lastTime = millis(); //Update the timer
byte fixType = myGNSS.getFixType(); // Get the fix type
Serial.print(F("Fix: "));
Serial.print(fixType);
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)"));
long latitude = myGNSS.getLatitude();
Serial.print(F(" Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
Serial.println();
}
}

View file

@ -0,0 +1,117 @@
/*
Set Dynamic Model
By: Paul Clark (PaulZC)
Date: April 22nd, 2020
Based extensively on Example3_GetPosition
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 u-blox module's dynamic platform model and then
query its lat/long/altitude. We also turn off the NMEA output on the I2C port.
This decreases the amount of I2C traffic dramatically.
Possible values for the dynamic model are: PORTABLE, STATIONARY, PEDESTRIAN, AUTOMOTIVE,
SEA, AIRBORNE1g, AIRBORNE2g, AIRBORNE4g, WRIST, BIKE
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// If we are going to change the dynamic platform model, let's do it here.
// Possible values are:
// PORTABLE, STATIONARY, PEDESTRIAN, AUTOMOTIVE, SEA, AIRBORNE1g, AIRBORNE2g, AIRBORNE4g, WRIST, BIKE
if (myGNSS.setDynamicModel(DYN_MODEL_PORTABLE) == false) // Set the dynamic model to PORTABLE
{
Serial.println(F("*** Warning: setDynamicModel failed ***"));
}
else
{
Serial.println(F("Dynamic platform model changed successfully!"));
}
// Let's read the new dynamic model to see if it worked
uint8_t newDynamicModel = myGNSS.getDynamicModel();
if (newDynamicModel == DYN_MODEL_UNKNOWN)
{
Serial.println(F("*** Warning: getDynamicModel failed ***"));
}
else
{
Serial.print(F("The new dynamic model is: "));
Serial.println(newDynamicModel);
}
//myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); //Uncomment this line to save only the NAV 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
Serial.println();
}
}

View file

@ -0,0 +1,54 @@
/*
Read NMEA sentences over I2C using u-blox 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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
//This will pipe all NMEA sentences to the serial port so we can see them
myGNSS.setNMEAOutputPort(Serial);
}
void loop()
{
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
delay(250); //Don't pound too hard on the I2C bus
}

View file

@ -0,0 +1,172 @@
/*
Send Custom Command
By: Paul Clark (PaulZC)
Date: April 20th, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how you can create and send a custom UBX packet
using the SparkFun u-blox library.
Previously it was possible to create and send a custom packet
through the library but it would always appear to timeout as
some of the internal functions referred to the internal private
struct packetCfg.
The most recent version of the library allows sendCommand to
use a custom packet as if it were packetCfg and so:
- sendCommand will return a sfe_ublox_status_e enum as if
it had been called from within the library
- the custom packet will be updated with data returned by the module
(previously this was not possible from outside the library)
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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
*/
#define NAV_RATE 5 // The new navigation rate in Hz (measurements per second)
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200); // You may need to increase this for high navigation rates!
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Let's configure the module's navigation rate as if we were using setNavigationFrequency
// Let's create our custom packet
uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes. MAX_PAYLOAD_SIZE defaults to 256. The CFG_RATE payload is only 6 bytes!
// setPacketCfgPayloadSize tells the library how many bytes our customPayload can hold.
// It is more memory-efficient to call setPacketCfgPayloadSize before .begin (to avoid creating a new buffer, copying across
// the contents of the old buffer and then deleting the old buffer). But let's call it here just to prove that we can.
myGNSS.setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
// The next line creates and initialises the packet information which wraps around the payload
ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
// The structure of ubxPacket is:
// uint8_t cls : The message Class
// uint8_t id : The message 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 : The payload
// uint8_t checksumA : Given to us by the module. Checked against the rolling calculated A/B checksums.
// uint8_t checksumB
// sfe_ublox_packet_validity_e valid : Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
// sfe_ublox_packet_validity_e classAndIDmatch : Goes from NOT_DEFINED to VALID or NOT_VALID when the Class and ID match the requestedClass and requestedID
// sendCommand will return:
// SFE_UBLOX_STATUS_DATA_RECEIVED if the data we requested was read / polled successfully
// SFE_UBLOX_STATUS_DATA_SENT if the data we sent was writted successfully (ACK'd)
// Other values indicate errors. Please see the sfe_ublox_status_e enum for further details.
// Referring to the u-blox M8 Receiver Description and Protocol Specification we see that
// the navigation rate is configured using the UBX-CFG-RATE message. So let's load our
// custom packet with the correct information so we can read (poll / get) the current settings.
customCfg.cls = UBX_CLASS_CFG; // This is the message Class
customCfg.id = UBX_CFG_RATE; // This is the message ID
customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings
customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing)
// We also need to tell sendCommand how long it should wait for a reply
uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100)
// Now let's read the current navigation rate. The results will be loaded into customCfg.
if (myGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
{
Serial.println(F("sendCommand (poll / get) failed! Freezing..."));
while (1)
;
}
// Referring to the message definition for UBX-CFG-RATE we see that the measurement rate
// is stored in payload bytes 0 and 1 as a uint16_t in LSB-first (little endian) format
uint16_t rate = (customPayload[1] << 8) | customPayload[0]; // Extract the current rate (ms)
float f_rate = 1000.0 / ((float)rate); // Convert the navigation rate to Hz (measurements per second)
// Print the current measurement rate
Serial.print(F("The current measurement rate is: "));
Serial.println(f_rate, 1);
// Let's change it
rate = 1000 / NAV_RATE; // Load the new value into rate
customPayload[0] = rate & 0xFF; // Store it in the payload
customPayload[1] = rate >> 8;
// Print the new measurement rate
Serial.print(F("The new measurement rate will be: "));
Serial.println(NAV_RATE);
// We don't need to update customCfg.len as it will have been set to 6
// when sendCommand read the data
// Now we write the custom packet back again to change the setting
if (myGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // This time we are only expecting an ACK
{
Serial.println(F("sendCommand (set) failed! Freezing."));
while (1)
;
}
else
{
Serial.println(F("Navigation rate updated. Here we go..."));
}
myGNSS.setAutoPVT(true); // Enable AutoPVT. The module will generate measurements automatically without being polled.
//myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); //Uncomment this line to save only the NAV settings to flash and BBR
}
void loop()
{
//Query the module as fast as possible
int32_t latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
int32_t longitude = myGNSS.getLongitude();
Serial.print(F(" Lon: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
int32_t altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
uint16_t milliseconds = myGNSS.getMillisecond();
Serial.print(F(" Milliseconds: "));
Serial.print(milliseconds);
Serial.println();
}

View file

@ -0,0 +1,197 @@
/*
Module Info - extracts and prints the full module information from UBX_MON_VER
using a custom command.
By: @mayopan
Date: May 9th, 2020
Based on:
Send Custom Command
By: Paul Clark (PaulZC)
Date: April 20th, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Previously it was possible to create and send a custom packet
through the library but it would always appear to timeout as
some of the internal functions referred to the internal private
struct packetCfg.
The most recent version of the library allows sendCommand to
use a custom packet as if it were packetCfg and so:
- sendCommand will return a sfe_ublox_status_e enum as if
it had been called from within the library
- the custom packet will be updated with data returned by the module
(previously this was not possible from outside the library)
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#define MAX_PAYLOAD_SIZE 384 // Override MAX_PAYLOAD_SIZE for getModuleInfo which can return up to 348 bytes
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
// Extend the class for getModuleInfo
class SFE_UBLOX_GPS_ADD : public SFE_UBLOX_GNSS
{
public:
bool getModuleInfo(uint16_t maxWait = 1100); //Queries module, texts
struct minfoStructure // Structure to hold the module info (uses 341 bytes of RAM)
{
char swVersion[30];
char hwVersion[10];
uint8_t extensionNo = 0;
char extension[10][30];
} minfo;
};
SFE_UBLOX_GPS_ADD myGNSS;
void setup()
{
Serial.begin(115200); // You may need to increase this for high navigation rates!
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
// setPacketCfgPayloadSize tells the library how many bytes our customPayload can hold.
// If we call it after the .begin, the library will attempt to resize the existing 256 byte payload buffer
// by creating a new buffer, copying across the contents of the old buffer, and then delete the old buffer.
// This uses a lot of RAM and causes the code to fail on the ATmega328P. (We are also allocating another 341 bytes for minfo.)
// To keep the code ATmega328P compliant - don't call setPacketCfgPayloadSize after .begin. Call it here instead.
myGNSS.setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
Serial.print(F("Polling module info"));
if (myGNSS.getModuleInfo(1100) == false) // Try to get the module info
{
Serial.print(F("getModuleInfo failed! Freezing..."));
while (1)
;
}
Serial.println();
Serial.println();
Serial.println(F("Module Info : "));
Serial.print(F("Soft version: "));
Serial.println(myGNSS.minfo.swVersion);
Serial.print(F("Hard version: "));
Serial.println(myGNSS.minfo.hwVersion);
Serial.print(F("Extensions:"));
Serial.println(myGNSS.minfo.extensionNo);
for (int i = 0; i < myGNSS.minfo.extensionNo; i++)
{
Serial.print(" ");
Serial.println(myGNSS.minfo.extension[i]);
}
Serial.println();
Serial.println(F("Done!"));
}
void loop()
{
}
bool SFE_UBLOX_GPS_ADD::getModuleInfo(uint16_t maxWait)
{
myGNSS.minfo.hwVersion[0] = 0;
myGNSS.minfo.swVersion[0] = 0;
for (int i = 0; i < 10; i++)
myGNSS.minfo.extension[i][0] = 0;
myGNSS.minfo.extensionNo = 0;
// Let's create our custom packet
uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes
// setPacketCfgPayloadSize tells the library how many bytes our customPayload can hold.
// If we call it here, after the .begin, the library will attempt to resize the existing 256 byte payload buffer
// by creating a new buffer, copying across the contents of the old buffer, and then delete the old buffer.
// This uses a lot of RAM and causes the code to fail on the ATmega328P. (We are also allocating another 341 bytes for minfo.)
// To keep the code ATmega328P compliant - don't call setPacketCfgPayloadSize here. Call it before .begin instead.
//myGNSS.setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
// The next line creates and initialises the packet information which wraps around the payload
ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
// The structure of ubxPacket is:
// uint8_t cls : The message Class
// uint8_t id : The message 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 : The payload
// uint8_t checksumA : Given to us by the module. Checked against the rolling calculated A/B checksums.
// uint8_t checksumB
// sfe_ublox_packet_validity_e valid : Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
// sfe_ublox_packet_validity_e classAndIDmatch : Goes from NOT_DEFINED to VALID or NOT_VALID when the Class and ID match the requestedClass and requestedID
// sendCommand will return:
// SFE_UBLOX_STATUS_DATA_RECEIVED if the data we requested was read / polled successfully
// SFE_UBLOX_STATUS_DATA_SENT if the data we sent was writted successfully (ACK'd)
// Other values indicate errors. Please see the sfe_ublox_status_e enum for further details.
// Referring to the u-blox M8 Receiver Description and Protocol Specification we see that
// the module information can be read using the UBX-MON-VER message. So let's load our
// custom packet with the correct information so we can read (poll / get) the module information.
customCfg.cls = UBX_CLASS_MON; // This is the message Class
customCfg.id = UBX_MON_VER; // This is the message ID
customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings
customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing)
// Now let's send the command. The module info is returned in customPayload
if (sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (false); //If command send fails then bail
// Now let's extract the module info from customPayload
uint16_t position = 0;
for (int i = 0; i < 30; i++)
{
minfo.swVersion[i] = customPayload[position];
position++;
}
for (int i = 0; i < 10; i++)
{
minfo.hwVersion[i] = customPayload[position];
position++;
}
while (customCfg.len >= position + 30)
{
for (int i = 0; i < 30; i++)
{
minfo.extension[minfo.extensionNo][i] = customPayload[position];
position++;
}
minfo.extensionNo++;
if (minfo.extensionNo > 9)
break;
}
return (true); //Success!
}

View file

@ -0,0 +1,88 @@
/*
Powering off a ublox GNSS module
By: bjorn
unsurv.org
Date: July 20th, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows you how to turn off the ublox module to lower the power consumption.
There are two functions: one just specifies a duration in milliseconds the other also specifies a pin on the GNSS device to wake it up with.
By driving a voltage from LOW to HIGH or HIGH to LOW on the chosen module pin you wake the device back up.
Note: Doing so on the INT0 pin when using the regular powerOff(durationInMs) function will wake the device anyway. (tested on SAM-M8Q)
Note: While powered off, you should not query the device for data or it might wake up. This can be used to wake the device but is not recommended.
Works best when also putting your microcontroller to sleep.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a BlackBoard.
To force the device to wake up you need to connect to a pin (for example INT0) seperately on the module.
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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// define a digital pin capable of driving HIGH and LOW
#define WAKEUP_PIN 5
// Possible GNSS interrupt pins for powerOffWithInterrupt are:
// VAL_RXM_PMREQ_WAKEUPSOURCE_UARTRX = uartrx
// VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 = extint0 (default)
// VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT1 = extint1
// VAL_RXM_PMREQ_WAKEUPSOURCE_SPICS = spics
// These values can be or'd (|) together to enable interrupts on multiple pins
void wakeUp() {
Serial.print("-- waking up module via pin " + String(WAKEUP_PIN));
Serial.println(" on your microcontroller --");
digitalWrite(WAKEUP_PIN, LOW);
delay(1000);
digitalWrite(WAKEUP_PIN, HIGH);
delay(1000);
digitalWrite(WAKEUP_PIN, LOW);
}
void setup() {
pinMode(WAKEUP_PIN, OUTPUT);
digitalWrite(WAKEUP_PIN, LOW);
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
// Powering off for 20s, you should see the power consumption drop.
Serial.println("-- Powering off module for 20s --");
myGNSS.powerOff(20000);
//myGNSS.powerOffWithInterrupt(20000, VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0);
delay(10000);
// After 10 seconds wake the device via the specified pin on your microcontroller and module.
wakeUp();
}
void loop() {
//Do nothing
}

View file

@ -0,0 +1,120 @@
/*
Time Pulse Parameters - Bullet Time (https://en.wikipedia.org/wiki/Bullet_time)
By: Paul Clark (PaulZC)
Date: January 13th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to change the time pulse parameters and configure the TIMEPULSE (PPS)
pin to produce a pulse once per second but with an adjustable delay. You could use this to
trigger multiple cameras and replicate the "bullet time" effect.
The SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic) (https://www.sparkfun.com/products/16481)
has solder pads which will let you connect an SMA connector to the TIMEPULSE signal. Need an
accurate timelapse camera shutter signal? This is the product for you!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include "SparkFun_u-blox_GNSS_Arduino_Library.h" //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Create storage for the time pulse parameters
UBX_CFG_TP5_data_t timePulseParameters;
// Get the time pulse parameters
if (myGNSS.getTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("getTimePulseParameters failed! Freezing..."));
while (1) ; // Do nothing more
}
// Print the CFG TP5 version
Serial.print(F("UBX_CFG_TP5 version: "));
Serial.println(timePulseParameters.version);
timePulseParameters.tpIdx = 0; // Select the TIMEPULSE pin
//timePulseParameters.tpIdx = 1; // Or we could select the TIMEPULSE2 pin instead, if the module has one
// We can configure the time pulse pin to produce a defined frequency or period
// Here is how to set the period:
// Let's say that we want our pulse-per-second to be as accurate as possible. So, let's tell the module
// to generate no signal while it is _locking_ to GNSS time. We want the signal to start only when the module is
// _locked_ to GNSS time.
timePulseParameters.freqPeriod = 0; // Set the frequency/period to zero
timePulseParameters.pulseLenRatio = 0; // Set the pulse ratio to zero
// When the module is _locked_ to GNSS time, make it generate a 0.1 second pulse once per second
timePulseParameters.freqPeriodLock = 1000000; // Set the period to 1,000,000 us
timePulseParameters.pulseLenRatioLock = 100000; // Set the pulse length to 0.1s (100,000 us)
timePulseParameters.flags.bits.polarity = 1; // Set the polarity to "1" (high for 0.1s, low for 0.9s, rising edge at top of second)
// We can use userConfigDelay to delay the pulse for each camera. The delay needs to be negative for this example.
// We can delay the pulse by +/- 2^31 nanoseconds (+/- 2.147 seconds)
//timePulseParameters.userConfigDelay = 0; // Camera 1: delay the pulse by 0ns
//timePulseParameters.userConfigDelay = -100000000; // Camera 2: delay the pulse by 0.1s (100,000,000 ns)
//timePulseParameters.userConfigDelay = -200000000; // Camera 3: delay the pulse by 0.2s (200,000,000 ns)
timePulseParameters.userConfigDelay = -300000000; // Camera 4: delay the pulse by 0.3s (300,000,000 ns)
//timePulseParameters.userConfigDelay = -400000000; // Camera 5: delay the pulse by 0.4s (400,000,000 ns)
//timePulseParameters.userConfigDelay = -500000000; // Camera 6: delay the pulse by 0.5s (500,000,000 ns)
//timePulseParameters.userConfigDelay = -600000000; // Camera 7: delay the pulse by 0.6s (600,000,000 ns)
//timePulseParameters.userConfigDelay = -700000000; // Camera 8: delay the pulse by 0.7s (700,000,000 ns)
//timePulseParameters.userConfigDelay = -800000000; // Camera 9: delay the pulse by 0.8s (800,000,000 ns)
//timePulseParameters.userConfigDelay = -900000000; // Camera 10: delay the pulse by 0.9s (900,000,000 ns)
timePulseParameters.flags.bits.active = 1; // Make sure the active flag is set to enable the time pulse. (Set to 0 to disable.)
timePulseParameters.flags.bits.lockedOtherSet = 1; // Tell the module to use freqPeriod while locking and freqPeriodLock when locked to GNSS time
timePulseParameters.flags.bits.isFreq = 0; // Tell the module that we want to set the period (not the frequency)
timePulseParameters.flags.bits.isLength = 1; // Tell the module that pulseLenRatio is a length (in us) - not a duty cycle
// Now set the time pulse parameters
if (myGNSS.setTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("setTimePulseParameters failed!"));
}
else
{
Serial.println(F("Success!"));
}
// Finally, save the time pulse parameters in battery-backed memory so the pulse will automatically restart at power on
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); // Save the configuration
}
void loop()
{
// Nothing to do here
}

View file

@ -0,0 +1,101 @@
/*
Time Pulse Parameters - Frequency
By: Paul Clark (PaulZC)
Date: January 13th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to change the time pulse parameters and configure the TIMEPULSE (PPS)
pin to produce a 1kHz squarewave
The SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic) (https://www.sparkfun.com/products/16481)
has solder pads which will let you connect an SMA connector to the TIMEPULSE signal. Need an
accurate frequency or clock source for your latest project? This is the product for you!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include "SparkFun_u-blox_GNSS_Arduino_Library.h" //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Create storage for the time pulse parameters
UBX_CFG_TP5_data_t timePulseParameters;
// Get the time pulse parameters
if (myGNSS.getTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("getTimePulseParameters failed! Freezing..."));
while (1) ; // Do nothing more
}
// Print the CFG TP5 version
Serial.print(F("UBX_CFG_TP5 version: "));
Serial.println(timePulseParameters.version);
timePulseParameters.tpIdx = 0; // Select the TIMEPULSE pin
//timePulseParameters.tpIdx = 1; // Or we could select the TIMEPULSE2 pin instead, if the module has one
// We can configure the time pulse pin to produce a defined frequency or period
// Here is how to set the frequency:
// While the module is _locking_ to GNSS time, make it generate 2kHz
timePulseParameters.freqPeriod = 2000; // Set the frequency/period to 2000Hz
timePulseParameters.pulseLenRatio = 0x55555555; // Set the pulse ratio to 1/3 * 2^32 to produce 33:67 mark:space
// When the module is _locked_ to GNSS time, make it generate 1kHz
timePulseParameters.freqPeriodLock = 1000; // Set the frequency/period to 1000Hz
timePulseParameters.pulseLenRatioLock = 0x80000000; // Set the pulse ratio to 1/2 * 2^32 to produce 50:50 mark:space
timePulseParameters.flags.bits.active = 1; // Make sure the active flag is set to enable the time pulse. (Set to 0 to disable.)
timePulseParameters.flags.bits.lockedOtherSet = 1; // Tell the module to use freqPeriod while locking and freqPeriodLock when locked to GNSS time
timePulseParameters.flags.bits.isFreq = 1; // Tell the module that we want to set the frequency (not the period)
timePulseParameters.flags.bits.isLength = 0; // Tell the module that pulseLenRatio is a ratio / duty cycle (* 2^-32) - not a length (in us)
timePulseParameters.flags.bits.polarity = 1; // Tell the module that we want the rising edge at the top of second. (Set to 0 for falling edge.)
// Now set the time pulse parameters
if (myGNSS.setTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("setTimePulseParameters failed!"));
}
else
{
Serial.println(F("Success!"));
}
}
void loop()
{
// Nothing to do here
}

View file

@ -0,0 +1,108 @@
/*
Time Pulse Parameters - Period
By: Paul Clark (PaulZC)
Date: January 13th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to change the time pulse parameters and configure the TIMEPULSE (PPS)
pin to produce a 1 second pulse every 30 seconds. What's really cool is that if you run this
example on two GNSS boards, the pulses are precisely synchronised!
The SparkFun GPS-RTK-SMA Breakout - ZED-F9P (Qwiic) (https://www.sparkfun.com/products/16481)
has solder pads which will let you connect an SMA connector to the TIMEPULSE signal. Need an
accurate timelapse camera shutter signal? This is the product for you!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include "SparkFun_u-blox_GNSS_Arduino_Library.h" //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Create storage for the time pulse parameters
UBX_CFG_TP5_data_t timePulseParameters;
// Get the time pulse parameters
if (myGNSS.getTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("getTimePulseParameters failed! Freezing..."));
while (1) ; // Do nothing more
}
// Print the CFG TP5 version
Serial.print(F("UBX_CFG_TP5 version: "));
Serial.println(timePulseParameters.version);
timePulseParameters.tpIdx = 0; // Select the TIMEPULSE pin
//timePulseParameters.tpIdx = 1; // Or we could select the TIMEPULSE2 pin instead, if the module has one
// We can configure the time pulse pin to produce a defined frequency or period
// Here is how to set the period:
// Let's say that we want our 1 pulse every 30 seconds to be as accurate as possible. So, let's tell the module
// to generate no signal while it is _locking_ to GNSS time. We want the signal to start only when the module is
// _locked_ to GNSS time.
timePulseParameters.freqPeriod = 0; // Set the frequency/period to zero
timePulseParameters.pulseLenRatio = 0; // Set the pulse ratio to zero
// When the module is _locked_ to GNSS time, make it generate a 1 second pulse every 30 seconds
// (Although the period can be a maximum of 2^32 microseconds (over one hour), the upper limit appears to be around 33 seconds)
timePulseParameters.freqPeriodLock = 30000000; // Set the period to 30,000,000 us
timePulseParameters.pulseLenRatioLock = 1000000; // Set the pulse length to 1,000,000 us
timePulseParameters.flags.bits.active = 1; // Make sure the active flag is set to enable the time pulse. (Set to 0 to disable.)
timePulseParameters.flags.bits.lockedOtherSet = 1; // Tell the module to use freqPeriod while locking and freqPeriodLock when locked to GNSS time
timePulseParameters.flags.bits.isFreq = 0; // Tell the module that we want to set the period (not the frequency)
timePulseParameters.flags.bits.isLength = 1; // Tell the module that pulseLenRatio is a length (in us) - not a duty cycle
timePulseParameters.flags.bits.polarity = 1; // Tell the module that we want the rising edge at the top of second. (Set to 0 for falling edge.)
// Now set the time pulse parameters
if (myGNSS.setTimePulseParameters(&timePulseParameters) == false)
{
Serial.println(F("setTimePulseParameters failed!"));
}
else
{
Serial.println(F("Success!"));
}
// Finally, save the time pulse parameters in battery-backed memory so the pulse will automatically restart at power on
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); // Save the configuration
}
void loop()
{
// Nothing to do here
}

View file

@ -0,0 +1,117 @@
/*
Getting Unix Epoch Time and micros using u-blox commands
By: UT2UH
Date: March 31th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to query a u-blox module for the current time and date as Unix Epoch uint32_t type to avoid time.h dependency.
We also turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic dramatically.
Note: this example works best on modules like the ZED_F9P. Modules like the ZOE_M8Q do not support confirmedTime.
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
// Uncomment the next line if you need to completely reset your module
//myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
Serial.println(F("Compare Unix Epoch given with reference one from https://www.epochconverter.com/"));
}
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
// getUnixEpoch marks the PVT data as stale so you will get Unix time and PVT time on alternate seconds
uint32_t us; //microseconds returned by getUnixEpoch()
uint32_t epoch = myGNSS.getUnixEpoch();
Serial.print(F("Unix Epoch rounded: "));
Serial.print(epoch, DEC);
epoch = myGNSS.getUnixEpoch(us);
Serial.print(F(" Exact Unix Epoch: "));
Serial.print(epoch, DEC);
Serial.print(F(" micros: "));
Serial.println(us, DEC);
Serial.print(myGNSS.getYear());
Serial.print(F("-"));
Serial.print(myGNSS.getMonth());
Serial.print(F("-"));
Serial.print(myGNSS.getDay());
Serial.print(F(" "));
Serial.print(myGNSS.getHour());
Serial.print(F(":"));
Serial.print(myGNSS.getMinute());
Serial.print(F(":"));
Serial.print(myGNSS.getSecond());
Serial.print(F(" Time is "));
if (myGNSS.getTimeFullyResolved() == false)
{
Serial.print(F("not fully resolved but "));
} else {
Serial.print(F("fully resolved and "));
}
if (myGNSS.getTimeValid() == false)
{
Serial.print(F("not "));
}
Serial.print(F("valid "));
if (myGNSS.getConfirmedTime() == false)
{
Serial.print(F("but not "));
} else {
Serial.print(F("and "));
}
Serial.print(F("confirmed"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.println(SIV);
}
}

View file

@ -0,0 +1,120 @@
/*
Demonstrate get/setMeasurementRate and get/setNavigationRate
By: Paul Clark
SparkFun Electronics
Date: March 30th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to slow down the measurement and navigation rates.
This should run on any GNSS module but has only been tested on the ZED_F9P and ZOE_M8Q.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
unsigned long lastTime = 0; //Simple local timer. Used to calc the message interval.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
// Uncomment the next line if you need to completely reset your module
//myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
// Begin by printing the current measurement rate and navigation rate
uint16_t rate = myGNSS.getMeasurementRate(); //Get the measurement rate of this module
Serial.print("Current measurement interval (ms): ");
Serial.println(rate);
rate = myGNSS.getNavigationRate(); //Get the navigation rate of this module
Serial.print("Current navigation ratio (cycles): ");
Serial.println(rate);
// The measurement rate is the elapsed time between GNSS measurements, which defines the rate
// e.g. 100 ms => 10 Hz, 1000 ms => 1 Hz, 10000 ms => 0.1 Hz.
// Let's set the measurement rate (interval) to 5 seconds = 5000 milliseconds
if (myGNSS.setMeasurementRate(5000) == false)
{
Serial.println(F("Could not set the measurement rate. Freezing."));
while (1);
}
// setMeasurementRate will set i2cPollingWait to a quarter of the interval
// Let's override that so we can poll the module more frequently and avoid timeouts
myGNSS.setI2CpollingWait(25); // Set i2cPollingWait to 25ms
// The navigation rate is the ratio between the number of measurements and the number of navigation solutions
// e.g. 5 means five measurements for every navigation solution. Maximum value is 127
// Let's set the navigation rate (ratio) to 12 to produce a solution every minute
if (myGNSS.setNavigationRate(12) == false)
{
Serial.println(F("Could not set the navigation rate. Freezing."));
while (1);
}
// Read and print the updated measurement rate and navigation rate
rate = myGNSS.getMeasurementRate(); //Get the measurement rate of this module
Serial.print("New measurement interval (ms): ");
Serial.println(rate);
rate = myGNSS.getNavigationRate(); //Get the navigation rate of this module
Serial.print("New navigation ratio (cycles): ");
Serial.println(rate);
lastTime = millis();
}
void loop()
{
// i2cPollingWait will prevent us from thrashing the I2C bus
if (myGNSS.getPVT()) //Check for new Position, Velocity, Time data. getPVT returns true if new data is available.
{
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
//Calculate the interval since the last message
Serial.print(F(" Interval: "));
Serial.print(((float)(millis() - lastTime)) / 1000.0, 2);
Serial.print(F("s"));
Serial.println();
lastTime = millis(); //Update lastTime
}
}

View file

@ -0,0 +1,91 @@
/*
Demonstrating how to use "end"
By: Paul Clark
SparkFun Electronics
Date: April 1st, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to use the end function.
End will stop all automatic message processing and free (nearly) all used RAM.
The file buffer is deleted (if it exists).
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
myGNSS.end(); // Call end now just because we can - it won't do much as we haven't used any automatic messages
}
void loop()
{
// Allocate 128 bytes for file storage - this checks that issue #20 has been resolved
// Allocating only 128 bytes will let this code run on the ATmega328P
// If your processor has plenty of RAM, you can increase this to something useful like 16KB
myGNSS.setFileBufferSize(128);
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected. Freezing."));
while (1);
}
Serial.print(F("The file buffer size is: "));
Serial.println(myGNSS.getFileBufferSize());
// Request Position, Velocity, Time
// RAM will be allocated for PVT message processing
// getPVT will return true is fresh PVT data was received and processed
if (myGNSS.getPVT())
{
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
// Calling end will free the RAM allocated for file storage and PVT processing
// Calling end is optional. You can comment the next line if you want to.
myGNSS.end();
}

View file

@ -0,0 +1,106 @@
/*
Configuring the GNSS to produce multiple messages at different rates
By: Paul Clark
SparkFun Electronics
Date: April 1st, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the U-Blox GNSS to output multiple messages at different rates:
PVT is output every second;
POSECEF is output every five seconds;
VELNED is output every ten seconds.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setMeasurementRate(1000); //Produce a measurement every 1000ms
myGNSS.setNavigationRate(1); //Produce a navigation solution every measurement
myGNSS.setAutoPVTrate(1); //Tell the GNSS to send the PVT solution every measurement
myGNSS.setAutoNAVPOSECEFrate(5); //Tell the GNSS to send each POSECEF solution every 5th measurement
myGNSS.setAutoNAVVELNEDrate(10); //Tell the GNSS to send each VELNED solution every 10th measurement
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
}
void loop()
{
// Calling getPVT returns true if there actually is a fresh navigation solution available.
if (myGNSS.getPVT())
{
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
// Calling getNAVPOSECEF returns true if there actually is a fresh position solution available.
if (myGNSS.getNAVPOSECEF())
{
Serial.print(F("ecefX: "));
Serial.print((float)myGNSS.packetUBXNAVPOSECEF->data.ecefX / 100.0, 2); // convert ecefX to m
Serial.print(F(" ecefY: "));
Serial.print((float)myGNSS.packetUBXNAVPOSECEF->data.ecefY / 100.0, 2); // convert ecefY to m
Serial.print(F(" ecefZ: "));
Serial.print((float)myGNSS.packetUBXNAVPOSECEF->data.ecefZ / 100.0, 2); // convert ecefY to m
Serial.println(F(" (m)"));
myGNSS.flushNAVPOSECEF(); //Mark all the data as read/stale so we get fresh data next time
}
// Calling getNAVVELNED returns true if there actually is fresh velocity data available.
if (myGNSS.getNAVVELNED())
{
Serial.print(F("velN: "));
Serial.print((float)myGNSS.packetUBXNAVVELNED->data.velN / 100.0, 2); // convert velN to m/s
Serial.print(F(" velE: "));
Serial.print((float)myGNSS.packetUBXNAVVELNED->data.velE / 100.0, 2); // convert velE to m/s
Serial.print(F(" velD: "));
Serial.print((float)myGNSS.packetUBXNAVVELNED->data.velD / 100.0, 2); // convert velD to m/s
Serial.println(F(" (m/s)"));
myGNSS.flushNAVVELNED(); //Mark all the data as read/stale so we get fresh data next time
}
}

View file

@ -0,0 +1,151 @@
/*
Getting leap second event info as SNTP Leap Indicator, time to a leap second event and the number of leap seconds since GPS epoch
By: UT2UH
Date: April 14th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to query a u-blox module for the leap second event info to cast to SNTP Leap Indicator enumeration.
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
typedef enum {
LI_NO_WARNING, //Time leaping not scheduled
LI_LAST_MINUTE_61_SEC, //Last minute has 61 seconds
LI_LAST_MINUTE_59_SEC, //Last minute has 59 seconds
LI_ALARM_CONDITION //The NTP server's clock not synchronized
} ntp_LI_e;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
// Uncomment the next line if you need to completely reset your module
//myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
Serial.println(F("Compare Unix Epoch given with reference one from https://www.epochconverter.com/"));
}
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
// getUnixEpoch marks the PVT data as stale so you will get Unix time and PVT time on alternate seconds
uint32_t us; //microseconds returned by getUnixEpoch()
uint32_t epoch = myGNSS.getUnixEpoch();
Serial.print(F("Unix Epoch rounded: "));
Serial.print(epoch, DEC);
epoch = myGNSS.getUnixEpoch(us);
Serial.print(F(" Exact Unix Epoch: "));
Serial.print(epoch, DEC);
Serial.print(F(" micros: "));
Serial.println(us, DEC);
int32_t timeToLeapSecEvent;
ntp_LI_e leapIndicator = (ntp_LI_e)myGNSS.getLeapIndicator(timeToLeapSecEvent);
Serial.print(F("NTP LI: "));
Serial.print(leapIndicator, DEC);
switch (leapIndicator){
case LI_NO_WARNING:
Serial.print(F(" - No event scheduled"));
break;
case LI_LAST_MINUTE_61_SEC:
Serial.print(F(" - last minute will end at 23:60"));
break;
case LI_LAST_MINUTE_59_SEC:
Serial.print(F(" - last minute will end at 23:58"));
break;
case LI_ALARM_CONDITION:
default:
Serial.print(F(" - Unknown (clock not synchronized)"));
break;
}
if (timeToLeapSecEvent < 0)
{
Serial.print(F(". Time since the last leap second event: "));
Serial.println(timeToLeapSecEvent * -1, DEC);
}
else
{
Serial.print(F(". Time to the next leap second event: "));
Serial.println(timeToLeapSecEvent, DEC);
}
sfe_ublox_ls_src_e leapSecSource;
Serial.print(F("Leap seconds since GPS Epoch (Jan 6th, 1980): "));
Serial.print(myGNSS.getCurrentLeapSeconds(leapSecSource), DEC);
switch (leapSecSource){
case SFE_UBLOX_LS_SRC_DEFAULT:
Serial.print(F(" - hardcoded"));
break;
case SFE_UBLOX_LS_SRC_GLONASS:
Serial.print(F(" - derived from GPS and GLONASS time difference"));
break;
case SFE_UBLOX_LS_SRC_GPS:
Serial.print(F(" - according to GPS"));
break;
case SFE_UBLOX_LS_SRC_SBAS:
Serial.print(F(" - according to SBAS"));
break;
case SFE_UBLOX_LS_SRC_BEIDOU:
Serial.print(F(" - according to BeiDou"));
break;
case SFE_UBLOX_LS_SRC_GALILEO:
Serial.print(F(" - according to Galileo"));
break;
case SFE_UBLOX_LS_SRC_AIDED:
Serial.print(F(" - last minute will end at 23:58"));
break;
case SFE_UBLOX_LS_SRC_CONFIGURED:
Serial.print(F(" - as configured)"));
break;
case SFE_UBLOX_LS_SRC_UNKNOWN:
default:
Serial.print(F(" - source unknown"));
break;
}
Serial.println();
}
Serial.println();
}

View file

@ -0,0 +1,144 @@
/*
Get the jamming state and indication
By: Paul Clark
SparkFun Electronics
Date: March 2nd, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to enable the jamming / interference monitor and read the
jamming state and information.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
Serial.println(F("SparkFun u-blox GNSS Example"));
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
// Enable the jamming / interference monitor
UBX_CFG_ITFM_data_t jammingConfig; // Create storage for the jamming configuration
if (myGNSS.getJammingConfiguration(&jammingConfig)) // Read the jamming configuration
{
Serial.print(F("The jamming / interference monitor is "));
if (jammingConfig.config.bits.enable == 0) // Check if the monitor is already enabled
Serial.print(F("not "));
Serial.println(F("enabled"));
if (jammingConfig.config.bits.enable == 0) // Check if the monitor is already enabled
{
Serial.print(F("Enabling the jamming / interference monitor: "));
(jammingConfig.config.bits.enable = 1); // Enable the monitor
if (myGNSS.setJammingConfiguration(&jammingConfig)) // Set the jamming configuration
Serial.println(F("success"));
else
Serial.println(F("failed!"));
}
}
}
void loop()
{
// Create storage to hold the hardware status
// See the definition of UBX_MON_HW_data_t in u-blox_structs.h for more details
UBX_MON_HW_data_t hwStatus;
if (myGNSS.getHWstatus(&hwStatus)) // Read the hardware status
{
Serial.println(F("Hardware status (UBX_MON_HW):"));
Serial.print(F("Jamming state: "));
Serial.print(hwStatus.flags.bits.jammingState);
if (hwStatus.flags.bits.jammingState == 0)
Serial.println(F(" = unknown / disabled"));
else if (hwStatus.flags.bits.jammingState == 1)
Serial.println(F(" = ok"));
else if (hwStatus.flags.bits.jammingState == 2)
Serial.println(F(" = warning"));
else // if (hwStatus.flags.bits.jammingState == 3)
Serial.println(F(" = critical!"));
Serial.print(F("Noise level: "));
Serial.println(hwStatus.noisePerMS);
Serial.print(F("AGC monitor: "));
Serial.println(hwStatus.agcCnt);
Serial.print(F("CW jamming indicator: "));
Serial.println(hwStatus.jamInd);
Serial.println();
}
// Create storage to hold the RF information from a ZED-F9
// See the definition of UBX_MON_RF_data_t in u-blox_structs.h for more details
UBX_MON_RF_data_t rfInformation;
// Read the RF information from the ZED-F9n. Allow 2 seconds for the data to be returned. Will time out on M8 modules
if (myGNSS.getRFinformation(&rfInformation, 2000))
{
Serial.print(F("The UBX_MON_RF message contains "));
Serial.print(rfInformation.header.nBlocks); // Print how many information blocks were returned. Should be 0, 1 or 2
Serial.println(F(" information blocks"));
for (uint8_t block = 0; block < rfInformation.header.nBlocks; block++)
{
Serial.print(F("Block ID: "));
Serial.print(rfInformation.blocks[block].blockId);
if (rfInformation.blocks[block].blockId == 0)
Serial.println(F(" = L1"));
else // if (rfInformation.blocks[block].blockId == 1)
Serial.println(F(" = L2 / L5"));
Serial.print(F("Jamming state: "));
Serial.print(rfInformation.blocks[block].flags.bits.jammingState);
if (rfInformation.blocks[block].flags.bits.jammingState == 0)
Serial.println(F(" = unknown / disabled"));
else if (rfInformation.blocks[block].flags.bits.jammingState == 1)
Serial.println(F(" = ok"));
else if (rfInformation.blocks[block].flags.bits.jammingState == 2)
Serial.println(F(" = warning"));
else // if (rfInformation.blocks[block].flags.bits.jammingState == 3)
Serial.println(F(" = critical!"));
Serial.print(F("Noise level: "));
Serial.println(rfInformation.blocks[block].noisePerMS);
Serial.print(F("AGC monitor: "));
Serial.println(rfInformation.blocks[block].agcCnt);
Serial.print(F("CW jamming indicator: "));
Serial.println(rfInformation.blocks[block].jamInd);
}
Serial.println();
}
}

View file

@ -0,0 +1,90 @@
/*
Read NMEA sentences over I2C using u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
#include <MicroNMEA.h> //http://librarymanager/All#MicroNMEA
char nmeaBuffer[100];
MicroNMEA nmea(nmeaBuffer, sizeof(nmeaBuffer));
void setup()
{
Serial.begin(115200);
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_ALL); // Make sure the library is passing all NMEA messages to processNMEA
myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_GGA); // Or, we can be kind to MicroNMEA and _only_ pass the GGA messages to it
}
void loop()
{
myGNSS.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);
nmea.clear(); // Clear the MicroNMEA storage to make sure we are getting fresh data
}
else
{
Serial.println("Waiting for fresh data");
}
delay(250); //Don't pound too hard on the I2C bus
}
//This function gets called from the SparkFun u-blox 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_GNSS::processNMEA(char incoming)
{
//Take the incoming char from the u-blox I2C port and pass it on to the MicroNMEA lib
//for sentence cracking
nmea.process(incoming);
}

View file

@ -0,0 +1,128 @@
/*
NEO-D9S L-Band receiver example
By: SparkFun Electronics / Paul Clark
Date: March 7th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to display the NEO-D9S's received signal imbalance and magnitude, plus a summary of any received PMP data.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S Correction Data Receiver: https://www.sparkfun.com/products/19390
Hardware Connections:
Use a Qwiic cable to connect the NEO-D9S L-Band corection data receiver to your board
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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myLBand; // NEO-D9S
const uint32_t myLBandFreq = 1556290000; // Uncomment this line to use the US SPARTN 1.8 service
//const uint32_t myLBandFreq = 1545260000; // Uncomment this line to use the EU SPARTN 1.8 service
#define OK(ok) (ok ? F(" -> OK") : F(" -> ERROR!")) // Convert uint8_t into OK/ERROR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMPMP will be called when new PMP data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_PMP_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMPMPcallbackPtr
// / _____ This _must_ be UBX_RXM_PMP_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMPMP(UBX_RXM_PMP_data_t *pmpData)
{
Serial.println(F("New PMP data received:"));
Serial.print(F("PMP message version: "));
Serial.println(pmpData->version);
Serial.print(F("numBytesUserData : "));
Serial.println(pmpData->numBytesUserData);
Serial.print(F("serviceIdentifier: 0x"));
Serial.println(pmpData->serviceIdentifier, HEX);
Serial.print(F("uniqueWordBitErrors: "));
Serial.println(pmpData->uniqueWordBitErrors);
Serial.print(F("fecBits: "));
Serial.println(pmpData->fecBits);
Serial.print(F("ebno (dB): "));
Serial.println((double)pmpData->ebno / 8, 3); //Convert ebno to dB
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NEO-D9S Example"));
Wire.begin(); //Start I2C
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the NEO-D9S L-Band receiver
//myLBand.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
while (myLBand.begin(Wire, 0x43) == false) //Connect to the u-blox NEO-D9S using Wire port. The D9S default I2C address is 0x43 (not 0x42)
{
Serial.println(F("u-blox NEO-D9S not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox NEO-D9S connected"));
uint8_t ok = myLBand.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, myLBandFreq); // Default 1539812500 Hz
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SEARCH_WINDOW, 2200); // Default 2200 Hz
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_SERVICE_ID, 0); // Default 1
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SERVICE_ID, 21845); // Default 50821
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_DATA_RATE, 2400); // Default 2400 bps
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_DESCRAMBLER, 1); // Default 1
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_DESCRAMBLER_INIT, 26969); // Default 23560
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_PRESCRAMBLING, 0); // Default 0
if (ok) ok = myLBand.setVal64(UBLOX_CFG_PMP_UNIQUE_WORD, 16238547128276412563ull);
if (ok) ok = myLBand.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1); // Ensure UBX-RXM-PMP is enabled on the I2C port
if (ok) ok = myLBand.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART1, 1); // Output UBX-RXM-PMP on UART1
if (ok) ok = myLBand.setVal8(UBLOX_CFG_UART2OUTPROT_UBX, 1); // Enable UBX output on UART2
if (ok) ok = myLBand.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 1); // Output UBX-RXM-PMP on UART2
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART1_BAUDRATE, 38400); // match baudrate with ZED default
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART2_BAUDRATE, 38400); // match baudrate with ZED default
Serial.print(F("L-Band: configuration "));
Serial.println(OK(ok));
myLBand.softwareResetGNSSOnly(); // Do a restart
myLBand.setRXMPMPcallbackPtr(&printRXMPMP); // Call printRXMPMP when new PMP data arrives
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
myLBand.checkUblox(); // Check for the arrival of new PMP data and process it.
myLBand.checkCallbacks(); // Check if any LBand callbacks are waiting to be processed.
UBX_MON_HW2_data_t hwStatus; // Create storage for the HW2 extended hardware status
if (myLBand.getHW2status(&hwStatus)) // Request the extended hardware status
{
// Print the signal imbalance and magnitude
Serial.print(F("Signal imbalance and magnitude: ofsI: "));
Serial.print(hwStatus.ofsI);
Serial.print(F(" magI: "));
Serial.print(hwStatus.magI);
Serial.print(F(" ofsQ: "));
Serial.print(hwStatus.ofsQ);
Serial.print(F(" magQ: "));
Serial.println(hwStatus.magQ);
}
}

View file

@ -0,0 +1,152 @@
/*
Calculating the Great Circle Distance and Course to a target position
By: Paul Clark
SparkFun Electronics
Date: September 21st, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to query a u-blox module for its latitude/longitude and then
calculate the Great Circle Distance and Course to a target location.
Thanks! distanceBetween and courseTo were taken from Mikal Hart's TinyGPSPlus:
https://github.com/mikalhart/TinyGPSPlus/blob/ca29434514a5c5172bd807af0608df7f296582a2/src/TinyGPS%2B%2B.cpp#L285-L328
Note: Lat/Long are large numbers because they are degrees * 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.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
//#include <math.h> //Uncomment if required. May be needed for sqrt, atan2, etc..
double distanceBetween(long lat1_l, long long1_l, long lat2_l, long long2_l)
{
// returns distance in meters between two positions, both specified
// as signed decimal-degrees latitude and longitude. Uses great-circle
// distance computation for hypothetical sphere of radius 6372795 meters.
// Because Earth is no exact sphere, rounding errors may be up to 0.5%.
// Courtesy of Maarten Lamers
double lat1 = (double)lat1_l / 10000000.0; // Convert lat and long to degrees
double long1 = (double)long1_l / 10000000.0;
double lat2 = (double)lat2_l / 10000000.0;
double long2 = (double)long2_l / 10000000.0;
double delta = radians(long1-long2);
double sdlong = sin(delta);
double cdlong = cos(delta);
lat1 = radians(lat1);
lat2 = radians(lat2);
double slat1 = sin(lat1);
double clat1 = cos(lat1);
double slat2 = sin(lat2);
double clat2 = cos(lat2);
delta = (clat1 * slat2) - (slat1 * clat2 * cdlong);
delta = sq(delta);
delta += sq(clat2 * sdlong);
delta = sqrt(delta);
double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong);
delta = atan2(delta, denom);
return delta * 6372795;
}
double courseTo(long lat1_l, long long1_l, long lat2_l, long long2_l)
{
// returns course in degrees (North=0, West=270) from position 1 to position 2,
// both specified as signed decimal-degrees latitude and longitude.
// Because Earth is no exact sphere, calculated course may be off by a tiny fraction.
// Courtesy of Maarten Lamers
double lat1 = (double)lat1_l / 10000000.0; // Convert lat and long to degrees
double long1 = (double)long1_l / 10000000.0;
double lat2 = (double)lat2_l / 10000000.0;
double long2 = (double)long2_l / 10000000.0;
double dlon = radians(long2-long1);
lat1 = radians(lat1);
lat2 = radians(lat2);
double a1 = sin(dlon) * cos(lat2);
double a2 = sin(lat1) * cos(lat2) * cos(dlon);
a2 = cos(lat1) * sin(lat2) - a2;
a2 = atan2(a1, a2);
if (a2 < 0.0)
{
a2 += TWO_PI;
}
return degrees(a2);
}
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.println(F(" (degrees * 10^-7)"));
static const long TARGET_LAT = 400909142, TARGET_LON = -1051849833; // SparkFun's location: degrees * 10^-7 (40.091 N, 105.185 W)
double distanceToTarget = distanceBetween(
latitude,
longitude,
TARGET_LAT,
TARGET_LON);
Serial.print(F("Distance to target: "));
Serial.print(distanceToTarget, 2);
Serial.print(F(" (m) "));
double courseToTarget = courseTo(
latitude,
longitude,
TARGET_LAT,
TARGET_LON);
Serial.print(F("Course to target: "));
Serial.print(courseToTarget, 1);
Serial.println(F(" (degrees)"));
}
}

View file

@ -0,0 +1,136 @@
/*
NEO-D9C QZSS-L6 receiver example
By: SparkFun Electronics / Paul Clark
Date: September 23rd, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to display a summary of the NEO-D9C's UBX-RXM-QZSSL6 data.
It also enables UBX-RXM-QZSSL6 message output on both UART1 and UART2 at 38400 baud
so you can feed the corrections directly to (e.g.) a ZED-F9P.
We believe the NEO-D9C's I2C address should be 0x43 (like the NEO-D9S). But, reported by users in Japan,
the initial NEO-D9C's use address 0x42 - which is the same as the ZED-F9P.
If you have one of the initial NEO-D9C's, the address 0x42 should work for you.
If you have a newer or upgraded NEO-D9C, then you may need to change to 0x43. See line 100.
Also, again reported by users in Japan, the initial NEO-D9C's do not support UBX-CFG-PRT.
The library uses UBX-CFG-PRT inside .begin (.isConnected) to check if the module is connected.
This then fails with the initial NEO-D9C's.
The work-around is to set the .begin assumeSuccess parameter to true.
With newer NEO-D9C's this work-around may not be necessary. Again see line 100.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S L-Band Correction Data Receiver: https://www.sparkfun.com/products/19390
Hardware Connections:
Use a Qwiic cable to connect the NEO-D9C QZSS-L6 corection data receiver to your board
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 <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myQZSS; // NEO-D9C
#define OK(ok) (ok ? F(" -> OK") : F(" -> ERROR!")) // Convert uint8_t into OK/ERROR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMQZSSL6 will be called when new QZSS-L6 data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_QZSSL6_message_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMQZSSL6messageCallbackPtr
// / _____ This _must_ be UBX_RXM_QZSSL6_message_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMQZSSL6(UBX_RXM_QZSSL6_message_data_t *qzssL6Data)
{
Serial.println(F("New QZSS-L6 data received:"));
Serial.print(F("Message version: "));
Serial.println(qzssL6Data->payload[0]);
Serial.print(F("Satellite Identifier: "));
Serial.println(qzssL6Data->payload[1]);
Serial.print(F("Carrier / Noise: "));
double cno = (0.00390625 * ((double)qzssL6Data->payload[2])) + ((double)qzssL6Data->payload[3]);
Serial.println(cno, 1);
Serial.print(F("Bit Errors Corrected: "));
Serial.println(qzssL6Data->payload[9]);
uint16_t chInfo = (((uint16_t)qzssL6Data->payload[11]) << 8) | qzssL6Data->payload[10];
uint16_t errStatus = ((chInfo >> 12) & 0x3);
Serial.print(F("Receiver Channel: "));
Serial.println((chInfo >> 8) & 0x3);
Serial.print(F("Message Name: L6"));
Serial.println(((chInfo >> 10) & 0x1) == 0 ? F("D") : F("E"));
Serial.print(F("Error Status: "));
if (errStatus == 1)
Serial.println("error-free");
else if (errStatus == 2)
Serial.println("erroneous");
else
Serial.println("unknown");
Serial.print(F("Channel Name: "));
Serial.println(((chInfo >> 14) & 0x3) == 0 ? F("A") : F("B"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NEO-D9C Example"));
Wire.begin(); //Start I2C
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the NEO-D9C QZSS-L6 receiver
//myQZSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
// For the initial NEO-D9C's: connect using address 0x42; set the assumeSuccess parameter to true
while (myQZSS.begin(Wire, 0x42, 1100, true) == false)
// For newer NEO-D9C's: use address 0x43; leave assumeSuccess set to false (default)
//while (myQZSS.begin(Wire, 0x43) == false)
{
Serial.println(F("u-blox NEO-D9C not detected at selected I2C address. Please check wiring and I2C address."));
delay(2000);
}
Serial.println(F("u-blox NEO-D9C connected"));
uint8_t ok = myQZSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C, 1); // Output QZSS-L6 message on the I2C port
Serial.print(F("QZSS-L6: I2C configuration "));
Serial.println(OK(ok));
if (ok) ok = myQZSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART1, 1); // Output QZSS-L6 message on UART1
if (ok) ok = myQZSS.setVal32(UBLOX_CFG_UART1_BAUDRATE, 38400); // Match UART1 baudrate with ZED
Serial.print(F("QZSS-L6: UART1 configuration "));
Serial.println(OK(ok));
if (ok) ok = myQZSS.setVal8(UBLOX_CFG_UART2OUTPROT_UBX, 1); // Enable UBX output on UART2
if (ok) ok = myQZSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2, 1); // Output QZSS-L6 message on UART2
if (ok) ok = myQZSS.setVal32(UBLOX_CFG_UART2_BAUDRATE, 38400); // Match UART2 baudrate with ZED
Serial.print(F("QZSS-L6: UART2 configuration "));
Serial.println(OK(ok));
myQZSS.setRXMQZSSL6messageCallbackPtr(&printRXMQZSSL6); // Call printRXMQZSSL6 when new QZSS-L6 data arrives
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
myQZSS.checkUblox(); // Check for the arrival of new QZSS-L6 data and process it.
myQZSS.checkCallbacks(); // Check if any QZSS-L6 callbacks are waiting to be processed.
}

View file

@ -0,0 +1,207 @@
/*
Configure Time & Frequency Sync manager (UBX-CFG-SMGR)
By: Danylo Ulianych
SparkFun Electronics
Date: March 6th, 2024
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example reads / sets UBX-CFG-SMGR configuration and prints UBX-TIM-SMEAS messages.
Works only with Time & Frequency Sync products like LEA-M8F, 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:
Plug a Qwiic cable into the GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// Callback: printTIMSMEASdata will be called when new TIM SMEA data arrives
// See u-blox_structs.h for the full definition of UBX_TIM_SMEAS_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoTIMTM2callback
// / _____ This _must_ be UBX_TIM_SMEAS_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printTIMSMEASdata(UBX_TIM_SMEAS_data_t smea)
{
Serial.print("UBX-TIM-SMEAS:");
Serial.printf("\n version: %u", smea.version);
Serial.printf("\n numMeas: %u", smea.numMeas);
Serial.printf("\n iTOW: %lu", smea.iTOW);
for (int i = 0; i < smea.numMeas; i++) {
Serial.printf("\n sourceId %u:", smea.data[i].sourceId);
Serial.printf("\n flags:");
Serial.printf("\n freqValid %u", smea.data[i].flags.bits.freqValid);
Serial.printf("\n phaseValid %u", smea.data[i].flags.bits.phaseValid);
Serial.printf("\n phaseOffsetFrac %d", smea.data[i].phaseOffsetFrac);
Serial.printf("\n phaseUncFrac %u", smea.data[i].phaseUncFrac);
Serial.printf("\n phaseOffset %ld", smea.data[i].phaseOffset);
Serial.printf("\n phaseUnc %lu", smea.data[i].phaseUnc);
Serial.printf("\n freqOffset %ld", smea.data[i].freqOffset);
Serial.printf("\n freqUnc %lu", smea.data[i].freqUnc);
}
}
UBX_CFG_SMGR_data_t convertRawBufToCfgSmgr(const ubxPacket* msg) {
UBX_CFG_SMGR_data_t smgr;
if (msg->len < sizeof(UBX_CFG_SMGR_data_t)) {
Serial.printf("Payload message size (%zu) is too small to be converted to UBX_CFG_SMGR_data_t\n", msg->len);
return smgr;
}
smgr.version = SFE_UBLOX_GNSS::extractByte(msg, 0);
smgr.minGNSSFix = SFE_UBLOX_GNSS::extractByte(msg, 1);
smgr.maxFreqChangeRate = SFE_UBLOX_GNSS::extractInt(msg, 2);
smgr.maxPhaseCorrRate = SFE_UBLOX_GNSS::extractInt(msg, 4);
smgr.freqTolerance = SFE_UBLOX_GNSS::extractInt(msg, 8);
smgr.timeTolerance = SFE_UBLOX_GNSS::extractInt(msg, 10);
smgr.messageCfg.all = SFE_UBLOX_GNSS::extractInt(msg, 12);
smgr.maxSlewRate = SFE_UBLOX_GNSS::extractInt(msg, 14);
smgr.flags.all = SFE_UBLOX_GNSS::extractLong(msg, 16);
return smgr;
}
void printUbxCfgSmgr(const UBX_CFG_SMGR_data_t& smgr) {
Serial.printf("\nUBX-CFG-SMGR:");
Serial.printf("\n version %u (0x%02x)", smgr.version, smgr.version);
Serial.printf("\n minGNSSFix %u (0x%02x)", smgr.minGNSSFix, smgr.minGNSSFix);
Serial.printf("\n maxFreqChangeRate %u (0x%02x)", smgr.maxFreqChangeRate, smgr.maxFreqChangeRate);
Serial.printf("\n maxPhaseCorrRate %u (0x%02x)", smgr.maxPhaseCorrRate, smgr.maxPhaseCorrRate);
Serial.printf("\n freqTolerance %u (0x%02x)", smgr.freqTolerance, smgr.freqTolerance);
Serial.printf("\n timeTolerance %u (0x%02x)", smgr.timeTolerance, smgr.timeTolerance);
Serial.printf("\n messageCfg:");
Serial.printf("\n measInternal: %u", smgr.messageCfg.bits.measInternal);
Serial.printf("\n measGNSS: %u", smgr.messageCfg.bits.measGNSS);
Serial.printf("\n measEXTINT0: %u", smgr.messageCfg.bits.measEXTINT0);
Serial.printf("\n measEXTINT1: %u", smgr.messageCfg.bits.measEXTINT1);
Serial.printf("\n maxSlewRate %u (0x%02x)", smgr.maxSlewRate, smgr.maxSlewRate);
Serial.printf("\n flags:");
Serial.printf("\n disableInternal: %u", smgr.flags.bits.disableInternal);
Serial.printf("\n disableExternal: %u", smgr.flags.bits.disableExternal);
Serial.printf("\n preferenceMode: %u", smgr.flags.bits.preferenceMode);
Serial.printf("\n enableGNSS: %u", smgr.flags.bits.enableGNSS);
Serial.printf("\n enableEXTINT0: %u", smgr.flags.bits.enableEXTINT0);
Serial.printf("\n enableEXTINT1: %u", smgr.flags.bits.enableEXTINT1);
Serial.printf("\n enableHostMeasInt: %u", smgr.flags.bits.enableHostMeasInt);
Serial.printf("\n enableHostMeasExt: %u", smgr.flags.bits.enableHostMeasExt);
Serial.printf("\n useAnyFix: %u", smgr.flags.bits.useAnyFix);
Serial.printf("\n disableMaxSlewRate: %u", smgr.flags.bits.disableMaxSlewRate);
Serial.printf("\n issueFreqWarn: %u", smgr.flags.bits.issueFreqWarn);
Serial.printf("\n issueTimeWarn: %u", smgr.flags.bits.issueTimeWarn);
Serial.printf("\n TPCoherent: %u", smgr.flags.bits.TPCoherent);
Serial.printf("\n disableOffset: %u", smgr.flags.bits.disableOffset);
Serial.println("\n");
}
void setup()
{
Serial.begin(115200);
while (!Serial); // wait for Serial ready
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); // ignore NMEA messages
// setPacketCfgPayloadSize tells the library how many bytes our customPayload can hold.
// It is more memory-efficient to call setPacketCfgPayloadSize before .begin (to avoid creating a new buffer, copying across
// the contents of the old buffer and then deleting the old buffer). But let's call it here just to prove that we can.
myGNSS.setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes. MAX_PAYLOAD_SIZE defaults to 256. The CFG_RATE payload is only 6 bytes!
// The next line creates and initialises the packet information which wraps around the payload
ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
// The structure of ubxPacket is:
// uint8_t cls : The message Class
// uint8_t id : The message 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 : The payload
// uint8_t checksumA : Given to us by the module. Checked against the rolling calculated A/B checksums.
// uint8_t checksumB
// sfe_ublox_packet_validity_e valid : Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
// sfe_ublox_packet_validity_e classAndIDmatch : Goes from NOT_DEFINED to VALID or NOT_VALID when the Class and ID match the requestedClass and requestedID
// sendCommand will return:
// SFE_UBLOX_STATUS_DATA_RECEIVED if the data we requested was read / polled successfully
// SFE_UBLOX_STATUS_DATA_SENT if the data we sent was writted successfully (ACK'd)
// Other values indicate errors. Please see the sfe_ublox_status_e enum for further details.
// Referring to the u-blox M8 Receiver Description and Protocol Specification we see that
// the navigation rate is configured using the UBX-CFG-RATE message. So let's load our
// custom packet with the correct information so we can read (poll / get) the current settings.
customCfg.cls = UBX_CLASS_CFG; // This is the message Class
customCfg.id = UBX_CFG_SMGR; // This is the message ID
customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings
customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing)
// We also need to tell sendCommand how long it should wait for a reply
uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100)
// Now let's read the current UBX-CFG-SMGR settings. The results will be loaded into customCfg.
if (myGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
{
Serial.println(F("sendCommand (poll / get) failed! Freezing..."));
while (1)
;
}
UBX_CFG_SMGR_data_t cfgSmgrPayload = convertRawBufToCfgSmgr(&customCfg);
printUbxCfgSmgr(cfgSmgrPayload);
cfgSmgrPayload.minGNSSFix = 5; // update the min no. of GNSS fixes to start freq/phase sync
cfgSmgrPayload.flags.bits.useAnyFix = 1; // use any fix
// update the raw payload buffer
memmove(customPayload, &cfgSmgrPayload, sizeof(UBX_CFG_SMGR_data_t));
// Now let's set the updated settings.
if (myGNSS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // We are expecting data and an ACK
{
Serial.println(F("sendCommand set failed! Freezing..."));
while (1)
;
}
Serial.println("UBX-CFG-SMGR successfully updated");
myGNSS.setAutoTIMSMEAcallback(&printTIMSMEASdata);
// Enable info/warns messages
// myGNSS.setVal8(UBLOX_CFG_INFMSG_UBX_I2C, 1);
}
void loop()
{
myGNSS.checkUblox(); //See if new UBX data is available. Process bytes as they come in.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
delay(250); //Don't pound too hard on the I2C bus
}

View file

@ -0,0 +1,86 @@
/*
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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
}

View file

@ -0,0 +1,96 @@
/*
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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
//Wire.setClock(400000); //Optional. Increase I2C clock speed to 400kHz.
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
}
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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
byte fixType = myGNSS.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"));
else if(fixType == 5) Serial.print(F("Time only"));
byte RTK = myGNSS.getCarrierSolutionType();
Serial.print(" RTK: ");
Serial.print(RTK);
if (RTK == 0) Serial.print(F(" (No solution)"));
else if (RTK == 1) Serial.print(F(" (High precision floating fix)"));
else if (RTK == 2) Serial.print(F(" (High precision fix)"));
Serial.println();
}
}

View file

@ -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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
}
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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
long speed = myGNSS.getGroundSpeed();
Serial.print(F(" Speed: "));
Serial.print(speed);
Serial.print(F(" (mm/s)"));
long heading = myGNSS.getHeading();
Serial.print(F(" Heading: "));
Serial.print(heading);
Serial.print(F(" (degrees * 10^-5)"));
int pDOP = myGNSS.getPDOP();
Serial.print(F(" pDOP: "));
Serial.print(pDOP / 100.0, 2); // Convert pDOP scaling from 0.01 to 1
Serial.println();
}
}

View file

@ -0,0 +1,79 @@
/*
Turn on/off various NMEA sentences.
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 turn on/off the NMEA sentences being output
over UART1. We use the I2C interface on the u-blox module for configuration
but you won't see any output from this sketch. You'll need to hook up
a Serial Basic or other USB to Serial device to UART1 on your u-blox module
to see the output.
This example turns off all sentences except for the GPGGA and GPVTG sentences.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
Hookup a Serial Basic (https://www.sparkfun.com/products/15096) to UART1 on the u-blox module. Open a terminal at 57600bps
and see GPGGA and GPVTG sentences.
*/
#include <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
unsigned long lastGNSSsend = 0;
void setup()
{
Serial.begin(115200); // Serial debug output over USB visible from Arduino IDE
Serial.println("Example showing how to enable/disable certain NMEA sentences");
Wire.begin();
if (myGNSS.begin() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}
//Disable or enable various NMEA sentences over the UART1 interface
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_UART1); //Several of these are on by default on ublox board so let's disable them
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1);
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); //Only leaving GGA & VTG enabled at current navigation rate
myGNSS.enableNMEAMessage(UBX_NMEA_VTG, COM_PORT_UART1);
//Here's the advanced configure method
//Some of the other examples in this library enable the PVT message so let's disable it
myGNSS.configureMessage(UBX_CLASS_NAV, UBX_NAV_PVT, COM_PORT_UART1, 0); //Message Class, ID, and port we want to configure, sendRate of 0 (disable).
myGNSS.setUART1Output(COM_TYPE_NMEA); //Turn off UBX and RTCM sentences on the UART1 interface
myGNSS.setSerialRate(57600); //Set UART1 to 57600bps.
//myGNSS.saveConfiguration(); //Optional: Save these settings to NVM
Serial.println(F("Messages configured. NMEA now being output over the UART1 port on the u-blox module at 57600bps."));
}
void loop()
{
if (millis() - lastGNSSsend > 200)
{
myGNSS.checkUblox(); //See if new data is available, but we don't want to get NMEA here. Go check UART1.
lastGNSSsend = millis();
}
}

View file

@ -0,0 +1,94 @@
/*
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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
unsigned long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
unsigned long startTime = 0; //Used to calc the actual update rate.
unsigned 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 u-blox Example");
Wire.begin();
// Increase I2C clock speed to 400kHz to cope with the high navigation rate
// (We normally recommend running the bus at 100kHz)
Wire.setClock(400000);
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setNavigationFrequency(5); //Set output to 5 times a second
uint8_t rate = myGNSS.getNavigationFrequency(); //Get the update rate of this module
Serial.print("Current update rate: ");
Serial.println(rate);
startTime = millis();
}
void loop()
{
//Query module every 25 ms. 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 = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.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();
}
}

View file

@ -0,0 +1,61 @@
/*
Reading the protocol version of a u-blox 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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Wire.begin();
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
Serial.print(F("Version: "));
byte versionHigh = myGNSS.getProtocolVersionHigh();
Serial.print(versionHigh);
Serial.print(".");
byte versionLow = myGNSS.getProtocolVersionLow();
Serial.print(versionLow);
}
void loop()
{
//Do nothing
}

View file

@ -0,0 +1,81 @@
/*
Reading the protocol version of a u-blox 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 u-blox module for its protocol version.
Note: this may fail on boards like the UNO (ATmega328P) with modules like the ZED-F9P
because getProtocolVersion returns a lot of data - more than the UNO's serial buffer can hold
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 GNSS 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 <SoftwareSerial.h>
#define mySerial Serial1 // Uncomment this line to connect via Serial1
// - or -
//SoftwareSerial mySerial(10, 11); // Uncomment this line to connect via SoftwareSerial(RX, TX). Connect pin 10 to GNSS TX pin.
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
Serial.println("Trying 38400 baud");
mySerial.begin(38400);
if (myGNSS.begin(mySerial))
{
Serial.println("GNSS connected at 38400 baud");
}
else
{
Serial.println("Trying 9600 baud");
mySerial.begin(9600);
if (myGNSS.begin(mySerial))
{
Serial.println("GNSS connected at 9600 baud");
}
else
{
Serial.println("Could not connect to GNSS. Freezing...");
while(1); // Do nothing more
}
}
Serial.print(F("Version: "));
byte versionHigh = myGNSS.getProtocolVersionHigh();
Serial.print(versionHigh);
Serial.print(".");
byte versionLow = myGNSS.getProtocolVersionLow();
Serial.print(versionLow);
}
void loop()
{
//Do nothing
}

View file

@ -0,0 +1,116 @@
/*
Change the I2C address of a u-blox 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 u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
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 u-blox Example");
Wire.begin();
byte oldAddress = 0x42; //The default address for u-blox 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
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
if (myGNSS.begin(Wire, oldAddress) == true) //Connect to the u-blox module using Wire port and the old address
{
Serial.print("GNSS found at address 0x");
Serial.println(oldAddress, HEX);
myGNSS.setI2CAddress(newAddress); //Change I2C address of this device
//Device's I2C address is stored to memory and loaded on each power-on
delay(2000); // Allow time for the change to take
if (myGNSS.begin(Wire, newAddress) == true)
{
myGNSS.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 probably 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
}

View file

@ -0,0 +1,77 @@
/*
Send UBX binary commands to enable RTCM sentences on u-blox 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 GNSS 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 <Wire.h> //Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
void setup()
{
Serial.begin(115200);
while(!Serial); //Wait for user to open terminal
Serial.println(F("u-blox RTCM Enable Example"));
Wire.begin();
Wire.setClock(400000); //Increase I2C clock speed to 400kHz
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}
while(Serial.available()) Serial.read(); //Clear any latent chars in serial buffer
Serial.println(F("Press any key to send commands to enable RTCM 3.x"));
while(Serial.available() == 0) ; //Wait for user to press a key
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); // Ensure RTCM3 is enabled
myGNSS.saveConfiguration(); //Save the current settings to flash and BBR
bool response = true;
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1077, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1087, COM_PORT_I2C, 1);
response &= myGNSS.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 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()
{
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
delay(250); //Don't pound too hard on the I2C bus
}

Some files were not shown because too many files have changed in this diff Show more