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,172 @@
/*
Get the high precision geodetic solution for latitude and longitude
By: Nathan Seidle
Modified by: Steven Rowland and Paul Clark
SparkFun Electronics
Date: April 17th, 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 inspect the accuracy of the high-precision
positional solution. Please see below for information about the units.
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
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
Wire.begin();
//myGNSS.enableDebugging(Serial);
if (myGNSS.begin(Wire) == 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(20); //Set output to 20 times a second
byte rate = myGNSS.getNavigationFrequency(); //Get the update rate of this module
Serial.print("Current update rate: ");
Serial.println(rate);
//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
// getHighResLatitude: returns the latitude from HPPOSLLH as an int32_t in degrees * 10^-7
// getHighResLatitudeHp: returns the high resolution component of latitude from HPPOSLLH as an int8_t in degrees * 10^-9
// getHighResLongitude: returns the longitude from HPPOSLLH as an int32_t in degrees * 10^-7
// getHighResLongitudeHp: returns the high resolution component of longitude from HPPOSLLH as an int8_t in degrees * 10^-9
// getElipsoid: returns the height above ellipsoid as an int32_t in mm
// getElipsoidHp: returns the high resolution component of the height above ellipsoid as an int8_t in mm * 10^-1
// getMeanSeaLevel: returns the height above mean sea level as an int32_t in mm
// getMeanSeaLevelHp: returns the high resolution component of the height above mean sea level as an int8_t in mm * 10^-1
// getHorizontalAccuracy: returns the horizontal accuracy estimate from HPPOSLLH as an uint32_t in mm * 10^-1
// If you want to use the high precision latitude and longitude with the full 9 decimal places
// you will need to use a 64-bit double - which is not supported on all platforms
// To allow this example to run on standard platforms, we cheat by converting lat and lon to integer and fractional degrees
// The high resolution altitudes can be converted into standard 32-bit float
// First, let's collect the position data
int32_t latitude = myGNSS.getHighResLatitude();
int8_t latitudeHp = myGNSS.getHighResLatitudeHp();
int32_t longitude = myGNSS.getHighResLongitude();
int8_t longitudeHp = myGNSS.getHighResLongitudeHp();
int32_t ellipsoid = myGNSS.getElipsoid();
int8_t ellipsoidHp = myGNSS.getElipsoidHp();
int32_t msl = myGNSS.getMeanSeaLevel();
int8_t mslHp = myGNSS.getMeanSeaLevelHp();
uint32_t accuracy = myGNSS.getHorizontalAccuracy();
// Defines storage for the lat and lon units integer and fractional parts
int32_t lat_int; // Integer part of the latitude in degrees
int32_t lat_frac; // Fractional part of the latitude
int32_t lon_int; // Integer part of the longitude in degrees
int32_t lon_frac; // Fractional part of the longitude
// Calculate the latitude and longitude integer and fractional parts
lat_int = latitude / 10000000; // Convert latitude from degrees * 10^-7 to Degrees
lat_frac = latitude - (lat_int * 10000000); // Calculate the fractional part of the latitude
lat_frac = (lat_frac * 100) + latitudeHp; // Now add the high resolution component
if (lat_frac < 0) // If the fractional part is negative, remove the minus sign
{
lat_frac = 0 - lat_frac;
}
lon_int = longitude / 10000000; // Convert latitude from degrees * 10^-7 to Degrees
lon_frac = longitude - (lon_int * 10000000); // Calculate the fractional part of the longitude
lon_frac = (lon_frac * 100) + longitudeHp; // Now add the high resolution component
if (lon_frac < 0) // If the fractional part is negative, remove the minus sign
{
lon_frac = 0 - lon_frac;
}
// Print the lat and lon
Serial.print("Lat (deg): ");
Serial.print(lat_int); // Print the integer part of the latitude
Serial.print(".");
printFractional(lat_frac, 9); // Print the fractional part of the latitude with leading zeros
Serial.print(", Lon (deg): ");
Serial.print(lon_int); // Print the integer part of the latitude
Serial.print(".");
printFractional(lon_frac, 9); // Print the fractional part of the latitude with leading zeros
Serial.println();
// Now define float storage for the heights and accuracy
float f_ellipsoid;
float f_msl;
float f_accuracy;
// Calculate the height above ellipsoid in mm * 10^-1
f_ellipsoid = (ellipsoid * 10) + ellipsoidHp;
// Now convert to m
f_ellipsoid = f_ellipsoid / 10000.0; // Convert from mm * 10^-1 to m
// Calculate the height above mean sea level in mm * 10^-1
f_msl = (msl * 10) + mslHp;
// Now convert to m
f_msl = f_msl / 10000.0; // Convert from mm * 10^-1 to m
// Convert the horizontal accuracy (mm * 10^-1) to a float
f_accuracy = accuracy;
// Now convert to m
f_accuracy = f_accuracy / 10000.0; // Convert from mm * 10^-1 to m
// Finally, do the printing
Serial.print("Ellipsoid (m): ");
Serial.print(f_ellipsoid, 4); // Print the ellipsoid with 4 decimal places
Serial.print(", Mean Sea Level(m): ");
Serial.print(f_msl, 4); // Print the mean sea level with 4 decimal places
Serial.print(", Accuracy (m): ");
Serial.println(f_accuracy, 4); // Print the accuracy with 4 decimal places
}
}
// Pretty-print the fractional part with leading zeros - without using printf
// (Only works with positive numbers)
void printFractional(int32_t fractional, uint8_t places)
{
if (places > 1)
{
for (uint8_t place = places - 1; place > 0; place--)
{
if (fractional < pow(10, place))
{
Serial.print("0");
}
}
}
Serial.print(fractional);
}

View file

@ -0,0 +1,146 @@
/*
Get the high precision geodetic solution for latitude and longitude using double
By: Nathan Seidle
Modified by: Paul Clark (PaulZC)
SparkFun Electronics
Date: April 17th, 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 inspect the accuracy of the high-precision
positional solution. Please see below for information about the units.
** This example will only work correctly on platforms which support 64-bit double **
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
Hardware Connections:
Plug a Qwiic cable into the GNSS and (e.g.) a Redboard Artemis https://www.sparkfun.com/products/15444
or an Artemis Thing Plus https://www.sparkfun.com/products/15574
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 myWire Wire // This will work on the Redboard Artemis and the Artemis Thing Plus using Qwiic
//#define myWire Wire1 // Uncomment this line if you are using the extra SCL1/SDA1 pins (D17 and D16) on the Thing Plus
#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
myWire.begin();
//myGNSS.enableDebugging(Serial); // Uncomment this line to enable debug messages
if (myGNSS.begin(myWire) == 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)
;
}
// Check that this platform supports 64-bit (8 byte) double
if (sizeof(double) < 8)
{
Serial.println(F("Warning! Your platform does not support 64-bit double."));
Serial.println(F("The latitude and longitude will be inaccurate."));
}
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
//myGNSS.setNavigationFrequency(20); //Set output to 20 times a second
byte rate = myGNSS.getNavigationFrequency(); //Get the update rate of this module
Serial.print("Current update rate: ");
Serial.println(rate);
//myGNSS.saveConfiguration(); //Save the current settings to flash and BBR
}
void loop()
{
//Query module only every second.
//The module only responds when a new position is available.
if (millis() - lastTime > 1000)
{
lastTime = millis(); //Update the timer
// getHighResLatitude: returns the latitude from HPPOSLLH as an int32_t in degrees * 10^-7
// getHighResLatitudeHp: returns the high resolution component of latitude from HPPOSLLH as an int8_t in degrees * 10^-9
// getHighResLongitude: returns the longitude from HPPOSLLH as an int32_t in degrees * 10^-7
// getHighResLongitudeHp: returns the high resolution component of longitude from HPPOSLLH as an int8_t in degrees * 10^-9
// getElipsoid: returns the height above ellipsoid as an int32_t in mm
// getElipsoidHp: returns the high resolution component of the height above ellipsoid as an int8_t in mm * 10^-1
// getMeanSeaLevel: returns the height above mean sea level as an int32_t in mm
// getMeanSeaLevelHp: returns the high resolution component of the height above mean sea level as an int8_t in mm * 10^-1
// getHorizontalAccuracy: returns the horizontal accuracy estimate from HPPOSLLH as an uint32_t in mm * 10^-1
// First, let's collect the position data
int32_t latitude = myGNSS.getHighResLatitude();
int8_t latitudeHp = myGNSS.getHighResLatitudeHp();
int32_t longitude = myGNSS.getHighResLongitude();
int8_t longitudeHp = myGNSS.getHighResLongitudeHp();
int32_t ellipsoid = myGNSS.getElipsoid();
int8_t ellipsoidHp = myGNSS.getElipsoidHp();
int32_t msl = myGNSS.getMeanSeaLevel();
int8_t mslHp = myGNSS.getMeanSeaLevelHp();
uint32_t accuracy = myGNSS.getHorizontalAccuracy();
// Defines storage for the lat and lon as double
double d_lat; // latitude
double d_lon; // longitude
// Assemble the high precision latitude and longitude
d_lat = ((double)latitude) / 10000000.0; // Convert latitude from degrees * 10^-7 to degrees
d_lat += ((double)latitudeHp) / 1000000000.0; // Now add the high resolution component (degrees * 10^-9 )
d_lon = ((double)longitude) / 10000000.0; // Convert longitude from degrees * 10^-7 to degrees
d_lon += ((double)longitudeHp) / 1000000000.0; // Now add the high resolution component (degrees * 10^-9 )
// Print the lat and lon
Serial.print("Lat (deg): ");
Serial.print(d_lat, 9);
Serial.print(", Lon (deg): ");
Serial.print(d_lon, 9);
// Now define float storage for the heights and accuracy
float f_ellipsoid;
float f_msl;
float f_accuracy;
// Calculate the height above ellipsoid in mm * 10^-1
f_ellipsoid = (ellipsoid * 10) + ellipsoidHp;
// Now convert to m
f_ellipsoid = f_ellipsoid / 10000.0; // Convert from mm * 10^-1 to m
// Calculate the height above mean sea level in mm * 10^-1
f_msl = (msl * 10) + mslHp;
// Now convert to m
f_msl = f_msl / 10000.0; // Convert from mm * 10^-1 to m
// Convert the horizontal accuracy (mm * 10^-1) to a float
f_accuracy = accuracy;
// Now convert to m
f_accuracy = f_accuracy / 10000.0; // Convert from mm * 10^-1 to m
// Finally, do the printing
Serial.print(", Ellipsoid (m): ");
Serial.print(f_ellipsoid, 4); // Print the ellipsoid with 4 decimal places
Serial.print(", Mean Sea Level (m): ");
Serial.print(f_msl, 4); // Print the mean sea level with 4 decimal places
Serial.print(", Accuracy (m): ");
Serial.println(f_accuracy, 4); // Print the accuracy with 4 decimal places
}
}

View file

@ -0,0 +1,77 @@
/*
Set the static position of the receiver.
By: SparkFun Electronics / Nathan Seidle
Date: September 26th, 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 set the static position of a receiver
using an Earth-Centered, Earth-Fixed (ECEF) location. This is the
output from a long (24 hour+) survey-in. Setting the static position
immediately causes the receiver to begin outputting RTCM data (if
enabled), perfect for setting up your own RTCM NTRIP caster or CORS.
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
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); // 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
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)
bool success = true;
//-1280208.308,-4716803.847,4086665.811 is SparkFun HQ so...
//Units are cm so 1234 = 12.34m
//success &= myGNSS.setStaticPosition(-128020831, -471680385, 408666581);
//Units are cm with a high precision extension so -1234.5678 should be called: (-123456, -78)
success &= myGNSS.setStaticPosition(-128020830, -80, -471680384, -70, 408666581, 10); //With high precision 0.1mm parts
//We can also set via lat/long
//40.09029751,-105.18507900,1560.238
//success &= myGNSS.setStaticPosition(400902975, -1051850790, 156024, true); //True at end enables lat/long input
//success &= myGNSS.setStaticPosition(400902975, 10, -1051850790, 0, 156023, 80, true);
if (!success) Serial.println(F("At least one call to setStaticPosition failed!"));
//Now let's use getVals to read back the data
//long ecefX = myGNSS.getVal32(0x40030003);
//Serial.print("ecefX: ");
//Serial.println(ecefX);
Serial.println(F("Done!"));
}
void loop()
{
}

View file

@ -0,0 +1,119 @@
/*
Configuring the GNSS to automatically send HPPOSLLH position reports over I2C
By: Paul Clark
Date: October 27th 2020
Based on an earlier example:
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 getHPPOSLLH. This eliminates the blocking in getHPPOSLLH 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_HPPOSLLH 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
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 lots of helpful debug messages
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to enable the minimum of helpful 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);
}
// 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
myGNSS.setNavigationFrequency(1); //Produce one solution per second
// The acid test: all four of these combinations should work seamlessly :-)
//myGNSS.setAutoPVT(false); // Library will poll each reading
//myGNSS.setAutoHPPOSLLH(false); // Library will poll each reading
//myGNSS.setAutoPVT(true); // Tell the GPS to "send" each solution automatically
//myGNSS.setAutoHPPOSLLH(false); // Library will poll each reading
//myGNSS.setAutoPVT(false); // Library will poll each reading
//myGNSS.setAutoHPPOSLLH(true); // Tell the GPS to "send" each hi res solution automatically
myGNSS.setAutoPVT(true); // Tell the GPS to "send" each solution automatically
myGNSS.setAutoHPPOSLLH(true); // Tell the GPS to "send" each hi res solution automatically
}
void loop()
{
// Calling getHPPOSLLH returns true if there actually is a fresh navigation solution available.
// Calling getPVT returns true if there actually is a fresh navigation solution available.
if ((myGNSS.getHPPOSLLH()) || (myGNSS.getPVT()))
{
Serial.println();
long highResLatitude = myGNSS.getHighResLatitude();
Serial.print(F("Hi Res Lat: "));
Serial.print(highResLatitude);
int highResLatitudeHp = myGNSS.getHighResLatitudeHp();
Serial.print(F(" "));
Serial.print(highResLatitudeHp);
long highResLongitude = myGNSS.getHighResLongitude();
Serial.print(F(" Hi Res Long: "));
Serial.print(highResLongitude);
int highResLongitudeHp = myGNSS.getHighResLongitudeHp();
Serial.print(F(" "));
Serial.print(highResLongitudeHp);
unsigned long horizAccuracy = myGNSS.getHorizontalAccuracy();
Serial.print(F(" Horiz accuracy: "));
Serial.print(horizAccuracy);
long latitude = myGNSS.getLatitude();
Serial.print(F(" Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.println(longitude);
}
else
{
Serial.print(".");
delay(50);
}
}

View file

@ -0,0 +1,150 @@
/*
Configuring the GNSS to automatically send HPPOSLLH position reports over I2C
and uses callbacks to process and display the data automatically
By: Paul Clark
Date: October 27th 2020
Based on an earlier example:
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 and uses callbacks to process and display the data automatically. No more polling!
This can be used over serial or over I2C, this example shows the I2C use. With serial the GNSS
simply outputs the UBX_NAV_HPPOSLLH 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
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;
// Callback: printHPdata will be called when new NAV HPPOSLLH data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_HPPOSLLH_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoHPPOSLLHcallback
// / _____ This _must_ be UBX_NAV_HPPOSLLH_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct)
{
Serial.println();
long highResLatitude = ubxDataStruct->lat;
Serial.print(F("Hi Res Lat: "));
Serial.print(highResLatitude);
int highResLatitudeHp = ubxDataStruct->latHp;
Serial.print(F(" "));
Serial.print(highResLatitudeHp);
long highResLongitude = ubxDataStruct->lon;
Serial.print(F(" Hi Res Long: "));
Serial.print(highResLongitude);
int highResLongitudeHp = ubxDataStruct->lonHp;
Serial.print(F(" "));
Serial.print(highResLongitudeHp);
float horizAccuracy = ((float)ubxDataStruct->hAcc) / 10000.0; // Convert hAcc from mm*0.1 to m
Serial.print(F(" Horiz accuracy: "));
Serial.println(horizAccuracy);
}
// 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 lots of helpful debug messages
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to enable the minimum of helpful 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);
}
// 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
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata
myGNSS.setAutoHPPOSLLHcallbackPtr(&printHPdata); // Enable automatic NAV HPPOSLLH messages with callback to printHPdata
}
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new data and process it. You could set up a timer interrupt to do this for you.
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
Serial.print(".");
delay(50);
}

View file

@ -0,0 +1,287 @@
/*
Use ESP32 WiFi to push RTCM data to RTK2Go (Caster) as a Server
By: SparkFun Electronics / Nathan Seidle
Date: December 14th, 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 gather RTCM data over I2C and push it to a casting service over WiFi.
It's confusing, but the Arduino is acting as a 'server' to a 'caster'. In this case we will
use RTK2Go.com as our caster because it is free. A rover (car, surveyor stick, etc) can
then connect to RTK2Go as a 'client' and get the RTCM data it needs.
You will need to register your mountpoint here: http://www.rtk2go.com/new-reservation/
(They'll probably block the credentials we include in this example)
To see if your mountpoint is active go here: http://rtk2go.com:2101/
This is a proof of concept. Serving RTCM to a caster over WiFi is useful when you need to
set up a high-precision base station.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/17369
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 "secrets.h"
WiFiClient ntripCaster;
#include <Wire.h>
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//Global Variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastSentRTCM_ms = 0; //Time of last data pushed to socket
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
uint32_t serverBytesSent = 0; //Just a running total
long lastReport_ms = 0; //Time of last report of bytes sent
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
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("Connecting to local WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.print("\nWiFi connected with IP: ");
Serial.println(WiFi.localIP());
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //UBX+RTCM3 is not a valid option so we enable all three.
myGNSS.setNavigationFrequency(1); //Set output in Hz. RTCM rarely benefits from >1Hz.
//Disable all NMEA sentences
bool response = true;
response &= myGNSS.disableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_GST, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C);
response &= myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
if (response == false)
{
Serial.println(F("Failed to disable NMEA. Freezing..."));
while (1)
;
}
else
Serial.println(F("NMEA disabled"));
//Enable necessary RTCM sentences
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through UART2, message every second
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1074, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1084, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1094, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1124, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds
if (response == false)
{
Serial.println(F("Failed to enable RTCM. Freezing..."));
while (1)
;
}
else
Serial.println(F("RTCM sentences enabled"));
//-1280208.308,-4716803.847,4086665.811 is SparkFun HQ so...
//Units are cm with a high precision extension so -1234.5678 should be called: (-123456, -78)
//For more infomation see Example12_setStaticPosition
//Note: If you leave these coordinates in place and setup your antenna *not* at SparkFun, your receiver
//will be very confused and fail to generate correction data because, well, you aren't at SparkFun...
//See this tutorial on getting PPP coordinates: https://learn.sparkfun.com/tutorials/how-to-build-a-diy-gnss-reference-station/all
response &= myGNSS.setStaticPosition(-128020830, -80, -471680384, -70, 408666581, 10); //With high precision 0.1mm parts
if (response == false)
{
Serial.println(F("Failed to enter static position. Freezing..."));
while (1)
;
}
else
Serial.println(F("Static position set"));
//Alternatively to setting a static position, you could do a survey-in
//but it takes much longer to start generating RTCM data. See Example4_BaseWithLCD
//myGNSS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m
//If you were setting up a full GNSS station, you would want to save these settings.
//Because setting an incorrect static position will disable the ability to get a lock, we will skip saving during this example
//if (myGNSS.saveConfiguration() == false) //Save the current settings to flash and BBR
// Serial.println(F("Module failed to save"));
Serial.println(F("Module configuration complete"));
}
void loop()
{
if (Serial.available())
beginServing();
Serial.println(F("Press any key to start serving"));
delay(1000);
}
void beginServing()
{
Serial.println("Begin transmitting to caster. Press any 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 (ntripCaster.connected() == false)
{
Serial.printf("Opening socket to %s\n", casterHost);
if (ntripCaster.connect(casterHost, casterPort) == true) //Attempt connection
{
Serial.printf("Connected to %s:%d\n", casterHost, casterPort);
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest,
SERVER_BUFFER_SIZE,
"SOURCE %s /%s\r\nSource-Agent: NTRIP SparkFun u-blox Server v1.0\r\n\r\n",
mountPointPW, mountPoint);
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripCaster.write(serverRequest, strlen(serverRequest));
//Wait for response
unsigned long timeout = millis();
while (ntripCaster.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println("Caster timed out!");
ntripCaster.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripCaster.available())
{
response[responseSpot++] = ntripCaster.read();
if (strstr(response, "200") > 0) //Look for 'ICY 200 OK'
connectionSuccess = true;
if (responseSpot == 512 - 1)
break;
}
response[responseSpot] = '\0';
if (connectionSuccess == false)
{
Serial.printf("Failed to connect to Caster: %s", response);
return;
}
} //End attempt to connect
else
{
Serial.println("Connection to host failed");
return;
}
} //End connected == false
if (ntripCaster.connected() == true)
{
delay(10);
while (Serial.available())
Serial.read(); //Flush any endlines or carriage returns
lastReport_ms = millis();
lastSentRTCM_ms = millis();
//This is the main sending loop. We scan for new ublox data but processRTCM() is where the data actually gets sent out.
while (1)
{
if (Serial.available())
break;
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
//Close socket if we don't have new data for 10s
//RTK2Go will ban your IP address if you abuse it. See http://www.rtk2go.com/how-to-get-your-ip-banned/
//So let's not leave the socket open/hanging without data
if (millis() - lastSentRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println("RTCM timeout. Disconnecting...");
ntripCaster.stop();
return;
}
delay(10);
//Report some statistics every 250
if (millis() - lastReport_ms > 250)
{
lastReport_ms += 250;
Serial.printf("Total sent: %d\n", serverBytesSent);
}
}
}
delay(10);
}
Serial.println("User pressed a key");
Serial.println("Disconnecting...");
ntripCaster.stop();
delay(10);
while (Serial.available())
Serial.read(); //Flush any endlines or carriage returns
}
//This function gets called from the SparkFun u-blox Arduino Library.
//As each RTCM byte comes in you can specify what to do with it
//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc.
void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming)
{
if (ntripCaster.connected() == true)
{
ntripCaster.write(incoming); //Send this byte to socket
serverBytesSent++;
lastSentRTCM_ms = millis();
}
}

View file

@ -0,0 +1,15 @@
//Your WiFi credentials
const char *ssid = "TRex";
const char *password = "hasBigTeeth";
//RTK2Go works well and is free
const char casterHost[] = "rtk2go.com";
const uint16_t casterPort = 2101;
const char mountPoint[] = "bldr_dwntwn2"; //The mount point you want to push data to
const char mountPointPW[] = "WR5wRo4H";
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char mountPoint[] = "MP1979d"; //The mount point you want to push data to
//const char mountPointPW[] = "296ynq";

View file

@ -0,0 +1,270 @@
/*
Use ESP32 WiFi to get RTCM data from RTK2Go (caster) as a Client
By: SparkFun Electronics / Nathan Seidle
Date: November 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 obtain RTCM data from a NTRIP Caster over WiFi
and push it over I2C to a ZED-F9x.
It's confusing, but the Arduino is acting as a 'client' to a 'caster'. In this case we will
use RTK2Go.com as our caster because it is free. See the NTRIPServer example to see how
to push RTCM data to the caster.
You will need to have a valid mountpoint available. To see available mountpoints go here: http://rtk2go.com:2101/
This is a proof of concept to show how to connect to a caster via HTTP. Using WiFi for a rover
is generally a bad idea because of limited WiFi range in the field.
For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <WiFi.h>
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//The ESP32 core has a built in base64 library but not every platform does
//We'll use an external lib if necessary.
#if defined(ARDUINO_ARCH_ESP32)
#include "base64.h" //Built-in ESP32 library
#else
#include <Base64.h> //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform
#endif
//Global variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastReceivedRTCM_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);
Serial.println(F("NTRIP 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_RTCM3); //Be sure RTCM3 input is enabled. UBX + RTCM3 is not a valid state.
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 NTRIP Client."));
delay(1000);
}
//Connect to NTRIP Caster, receive RTCM, and push to ZED module over I2C
void beginClient()
{
WiFiClient ntripClient;
long rtcmCount = 0;
Serial.println(F("Subscribing to Caster. 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. Limit to 5s between attempts.
if (ntripClient.connected() == false)
{
Serial.print(F("Opening socket to "));
Serial.println(casterHost);
if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
{
Serial.println(F("Connection to caster failed"));
return;
}
else
{
Serial.print(F("Connected to "));
Serial.print(casterHost);
Serial.print(F(": "));
Serial.println(casterPort);
Serial.print(F("Requesting NTRIP Data from mount point "));
Serial.println(mountPoint);
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest, SERVER_BUFFER_SIZE, "GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
mountPoint);
char credentials[512];
if (strlen(casterUser) == 0)
{
strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials));
}
else
{
//Pass base64 encoded user:pw
char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
Serial.print(F("Sending credentials: "));
Serial.println(userCredentials);
#if defined(ARDUINO_ARCH_ESP32)
//Encode with ESP32 built-in library
base64 b;
String strEncodedCredentials = b.encode(userCredentials);
char encodedCredentials[strEncodedCredentials.length() + 1];
strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array
snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials);
#else
//Encode with nfriendly library
int encodedLen = base64_enc_len(strlen(userCredentials));
char encodedCredentials[encodedLen]; //Create array large enough to house encoded data
base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed
#endif
}
strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
Serial.print(F("serverRequest size: "));
Serial.print(strlen(serverRequest));
Serial.print(F(" of "));
Serial.print(sizeof(serverRequest));
Serial.println(F(" bytes available"));
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripClient.write(serverRequest, strlen(serverRequest));
//Wait for response
unsigned long timeout = millis();
while (ntripClient.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripClient.available())
{
if (responseSpot == sizeof(response) - 1) break;
response[responseSpot++] = ntripClient.read();
if (strstr(response, "200") > 0) //Look for 'ICY 200 OK'
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
if (connectionSuccess == false)
{
Serial.print(F("Failed to connect to "));
Serial.print(casterHost);
Serial.print(F(": "));
Serial.println(response);
return;
}
else
{
Serial.print(F("Connected to "));
Serial.println(casterHost);
lastReceivedRTCM_ms = millis(); //Reset timeout
}
} //End attempt to connect
} //End connected == false
if (ntripClient.connected() == true)
{
uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
rtcmCount = 0;
//Print any available RTCM data
while (ntripClient.available())
{
//Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data
rtcmData[rtcmCount++] = ntripClient.read();
if (rtcmCount == sizeof(rtcmData)) break;
}
if (rtcmCount > 0)
{
lastReceivedRTCM_ms = millis();
//Push RTCM to GNSS module over I2C
myGNSS.pushRawData(rtcmData, rtcmCount, false);
Serial.print(F("RTCM pushed to ZED: "));
Serial.println(rtcmCount);
}
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceivedRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println(F("RTCM timeout. Disconnecting..."));
if (ntripClient.connected() == true)
ntripClient.stop();
return;
}
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
ntripClient.stop();
}

View file

@ -0,0 +1,17 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "hasBigTeeth";
//RTK2Go works well and is free
const char casterHost[] = "rtk2go.com";
const uint16_t casterPort = 2101;
const char casterUser[] = "myEmail@test.com"; //User must provide their own email address to use RTK2Go
const char casterUserPW[] = "";
const char mountPoint[] = "bldr_SparkFun1"; //The mount point you want to get data from
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char casterUser[] = "u99696"; //User name and pw must be obtained through their web portal
//const char casterUserPW[] = "466zez";
//const char mountPoint[] = "MP1979"; //The mount point you want to get data from

View file

@ -0,0 +1,402 @@
/*
Use ESP32 WiFi to get RTCM data from RTK2Go (caster) as a Client, and transmit GGA (needed for some Casters)
By: SparkFun Electronics / Nathan Seidle
Date: November 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 obtain RTCM data from a NTRIP Caster over WiFi
and push it over I2C to a ZED-F9x.
It's confusing, but the Arduino is acting as a 'client' to a 'caster'. In this case we will
use RTK2Go.com as our caster because it is free. See the NTRIPServer example to see how
to push RTCM data to the caster.
The rover's location will be broadcast to the Caster every 10s via GGA setence.
You will need to have a valid mountpoint available. To see available mountpoints go here: http://rtk2go.com:2101/
This is a proof of concept to show how to connect to a caster via HTTP.
For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
"In broad protocol terms, the NTRIP client must first connect (get an HTTP “OK” reply) and only then
should it send the sentence. NTRIP protocol revision 2 (which does not have very broad industry
acceptance at this time) does allow sending the sentence in the original header."
https://www.use-snip.com/kb/knowledge-base/subtle-issues-with-using-ntrip-client-nmea-183-strings/
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <WiFi.h>
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//The ESP32 core has a built in base64 library but not every platform does
//We'll use an external lib if necessary.
#if defined(ARDUINO_ARCH_ESP32)
#include "base64.h" //Built-in ESP32 library
#else
#include <Base64.h> //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform
#endif
//Global variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastReceivedRTCM_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
bool transmitLocation = true; //By default we will transmit the units location via GGA sentence.
int timeBetweenGGAUpdate_ms = 10000; //GGA is required for Rev2 NTRIP casters. Don't transmit but once every 10 seconds
long lastTransmittedGGA_ms = 0;
//Used for GGA sentence parsing from incoming NMEA
bool ggaSentenceStarted = false;
bool ggaSentenceComplete = false;
bool ggaTransmitComplete = false; //Goes true once we transmit GGA to the caster
char ggaSentence[128] = {0};
byte ggaSentenceSpot = 0;
int ggaSentenceEndSpot = 0;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NTRIP testing"));
Wire.begin(); //Start I2C
while (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //Be sure RTCM3 input is enabled. UBX + RTCM3 is not a valid state.
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); //Verify the GGA sentence is enabled
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 NTRIP Client."));
delay(1000);
}
//Connect to NTRIP Caster, receive RTCM, and push to ZED module over I2C
void beginClient()
{
WiFiClient ntripClient;
long rtcmCount = 0;
Serial.println(F("Subscribing to Caster. Press key to stop"));
delay(10); //Wait for any serial to arrive
while (Serial.available())
Serial.read(); //Flush
while (Serial.available() == 0)
{
myGNSS.checkUblox();
//Connect if we are not already. Limit to 5s between attempts.
if (ntripClient.connected() == false)
{
Serial.print(F("Opening socket to "));
Serial.println(casterHost);
if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
{
Serial.println(F("Connection to caster failed"));
return;
}
else
{
Serial.print(F("Connected to "));
Serial.print(casterHost);
Serial.print(F(": "));
Serial.println(casterPort);
Serial.print(F("Requesting NTRIP Data from mount point "));
Serial.println(mountPoint);
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest,
SERVER_BUFFER_SIZE,
"GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
mountPoint);
char credentials[512];
if (strlen(casterUser) == 0)
{
strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials));
}
else
{
//Pass base64 encoded user:pw
char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
Serial.print(F("Sending credentials: "));
Serial.println(userCredentials);
#if defined(ARDUINO_ARCH_ESP32)
//Encode with ESP32 built-in library
base64 b;
String strEncodedCredentials = b.encode(userCredentials);
char encodedCredentials[strEncodedCredentials.length() + 1];
strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array
#else
//Encode with nfriendly library
int encodedLen = base64_enc_len(strlen(userCredentials));
char encodedCredentials[encodedLen]; //Create array large enough to house encoded data
base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed
#endif
snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials);
}
strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
Serial.print(F("serverRequest size: "));
Serial.print(strlen(serverRequest));
Serial.print(F(" of "));
Serial.print(sizeof(serverRequest));
Serial.println(F(" bytes available"));
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripClient.write(serverRequest, strlen(serverRequest));
//Wait for response
unsigned long timeout = millis();
while (ntripClient.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripClient.available())
{
if (responseSpot == sizeof(response) - 1)
break;
response[responseSpot++] = ntripClient.read();
if (strstr(response, "200") > 0) //Look for '200 OK'
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
if (connectionSuccess == false)
{
Serial.print(F("Failed to connect to "));
Serial.println(casterHost);
return;
}
else
{
Serial.print(F("Connected to "));
Serial.println(casterHost);
lastReceivedRTCM_ms = millis(); //Reset timeout
ggaTransmitComplete = true; //Reset to start polling for new GGA data
}
} //End attempt to connect
} //End connected == false
if (ntripClient.connected() == true)
{
uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
rtcmCount = 0;
//Print any available RTCM data
while (ntripClient.available())
{
//Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data
rtcmData[rtcmCount++] = ntripClient.read();
if (rtcmCount == sizeof(rtcmData))
break;
}
if (rtcmCount > 0)
{
lastReceivedRTCM_ms = millis();
//Push RTCM to GNSS module over I2C
myGNSS.pushRawData(rtcmData, rtcmCount, false);
Serial.print(F("RTCM pushed to ZED: "));
Serial.println(rtcmCount);
}
}
//Provide the caster with our current position as needed
if (ntripClient.connected() == true && transmitLocation == true && (millis() - lastTransmittedGGA_ms) > timeBetweenGGAUpdate_ms && ggaSentenceComplete == true && ggaTransmitComplete == false)
{
Serial.print(F("Pushing GGA to server: "));
Serial.println(ggaSentence);
lastTransmittedGGA_ms = millis();
//Push our current GGA sentence to caster
ntripClient.print(ggaSentence);
ntripClient.print("\r\n");
ggaTransmitComplete = true;
//Wait for response
unsigned long timeout = millis();
while (ntripClient.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripClient.available())
{
if (responseSpot == sizeof(response) - 1)
break;
response[responseSpot++] = ntripClient.read();
if (strstr(response, "200") > 0) //Look for '200 OK'
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceivedRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println(F("RTCM timeout. Disconnecting..."));
if (ntripClient.connected() == true)
ntripClient.stop();
return;
}
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
ntripClient.stop();
}
//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
//We will look for and copy the GGA sentence
void SFE_UBLOX_GNSS::processNMEA(char incoming)
{
//Take the incoming char from the u-blox I2C port and check to see if we should record it or not
if (incoming == '$' && ggaTransmitComplete == true)
{
ggaSentenceStarted = true;
ggaSentenceSpot = 0;
ggaSentenceEndSpot = sizeof(ggaSentence);
ggaSentenceComplete = false;
}
if (ggaSentenceStarted == true)
{
ggaSentence[ggaSentenceSpot++] = incoming;
//Make sure we don't go out of bounds
if (ggaSentenceSpot == sizeof(ggaSentence))
{
//Start over
ggaSentenceStarted = false;
}
//Verify this is the GGA setence
else if (ggaSentenceSpot == 5 && incoming != 'G')
{
//Ignore this sentence, start over
ggaSentenceStarted = false;
}
else if (incoming == '*')
{
//We're near the end. Keep listening for two more bytes to complete the CRC
ggaSentenceEndSpot = ggaSentenceSpot + 2;
}
else if (ggaSentenceSpot == ggaSentenceEndSpot)
{
ggaSentence[ggaSentenceSpot] = '\0'; //Terminate this string
ggaSentenceComplete = true;
ggaTransmitComplete = false; //We are ready for transmission
//Serial.print("GGA Parsed - ");
//Serial.println(ggaSentence);
//Start over
ggaSentenceStarted = false;
}
}
}

View file

@ -0,0 +1,17 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "parachutes";
//RTK2Go works well and is free
const char casterHost[] = "rtk2go.com";
const uint16_t casterPort = 2101;
const char casterUser[] = "myEmail@test.com"; //User must provide their own email address to use RTK2Go
const char casterUserPW[] = "";
const char mountPoint[] = "bldr_SparkFun1"; //The mount point you want to get data from
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char casterUser[] = "u99696"; //User name and pw must be obtained through their web portal
//const char casterUserPW[] = "466zez";
//const char mountPoint[] = "MP1979"; //The mount point you want to get data from

View file

@ -0,0 +1,491 @@
/*
Use ESP32 WiFi to get RTCM data from Swift Navigation's Skylark caster as a Client, and transmit GGA using a callback
By: SparkFun Electronics / Nathan Seidle & Paul Clark
Date: January 13th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain RTCM data from a NTRIP Caster over WiFi and push it over I2C to a ZED-F9x.
It's confusing, but the Arduino is acting as a 'client' to a 'caster'.
In this case we will use Skylark. But you can of course use RTK2Go or Emlid's Caster too. Change secrets.h. as required.
The rover's location will be broadcast to the caster every 10s via GGA setence - automatically using a callback.
This is a proof of concept to show how to connect to a caster via HTTP and show how the corrections control the accuracy.
It's a fun thing to disconnect from the caster and watch the accuracy degrade. Then connect again and watch it recover!
For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
"In broad protocol terms, the NTRIP client must first connect (get an HTTP “OK” reply) and only then
should it send the sentence. NTRIP protocol revision 2 (which does not have very broad industry
acceptance at this time) does allow sending the sentence in the original header."
https://www.use-snip.com/kb/knowledge-base/subtle-issues-with-using-ntrip-client-nmea-183-strings/
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <WiFi.h>
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//The ESP32 core has a built in base64 library but not every platform does
//We'll use an external lib if necessary.
#if defined(ARDUINO_ARCH_ESP32)
#include "base64.h" //Built-in ESP32 library
#else
#include <Base64.h> //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform
#endif
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Global variables
unsigned long lastReceivedRTCM_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
const unsigned long maxTimeBeforeHangup_ms = 10000UL; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
bool transmitLocation = true; //By default we will transmit the unit's location via GGA sentence.
WiFiClient ntripClient; // The WiFi connection to the NTRIP server. This is global so pushGGA can see if we are connected.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: pushGPGGA will be called when new GPGGA NMEA data arrives
// See u-blox_structs.h for the full definition of NMEA_GGA_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback
// / _____ This _must_ be NMEA_GGA_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void pushGPGGA(NMEA_GGA_data_t *nmeaData)
{
//Provide the caster with our current position as needed
if ((ntripClient.connected() == true) && (transmitLocation == true))
{
Serial.print(F("Pushing GGA to server: "));
Serial.print((const char *)nmeaData->nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end
//Push our current GGA sentence to caster
ntripClient.print((const char *)nmeaData->nmea);
}
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NTRIP testing"));
Wire.begin(); //Start I2C
while (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //Be sure RTCM3 input is enabled. UBX + RTCM3 is not a valid state.
myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
myGNSS.setNavigationFrequency(1); //Set output in Hz.
// Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA
myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
myGNSS.setNMEAGPGGAcallbackPtr(&pushGPGGA); // Set up the callback for GPGGA
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 10); // Tell the module to output GGA every 10 seconds
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool keepTrying = true;
while (keepTrying)
{
Serial.print(F("Connecting to local WiFi"));
unsigned long startTime = millis();
WiFi.begin(ssid, password);
while ((WiFi.status() != WL_CONNECTED) && (millis() < (startTime + 10000))) // Timeout after 10 seconds
{
delay(500);
Serial.print(F("."));
}
Serial.println();
if (WiFi.status() == WL_CONNECTED)
keepTrying = false; // Connected!
else
{
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
}
Serial.print(F("WiFi connected with IP: "));
Serial.println(WiFi.localIP());
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
while (Serial.available()) // Empty the serial buffer
Serial.read();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
enum states // Use a 'state machine' to open and close the connection
{
open_connection,
push_data_and_wait_for_keypress,
close_connection,
waiting_for_keypress
};
static states state = open_connection;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
switch (state)
{
case open_connection:
Serial.println(F("Connecting to the NTRIP caster..."));
if (beginClient()) // Try to open the connection to the caster
{
Serial.println(F("Connected to the NTRIP caster! Press any key to disconnect..."));
state = push_data_and_wait_for_keypress; // Move on
}
else
{
Serial.print(F("Could not connect to the caster. Trying again in 5 seconds."));
for (int i = 0; i < 5; i++)
{
delay(1000);
Serial.print(F("."));
}
Serial.println();
}
break;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
case push_data_and_wait_for_keypress:
// If the connection has dropped or timed out, or if the user has pressed a key
if ((processConnection() == false) || (keyPressed()))
{
state = close_connection; // Move on
}
break;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
case close_connection:
Serial.println(F("Closing the connection to the NTRIP caster..."));
closeConnection();
Serial.println(F("Press any key to reconnect..."));
state = waiting_for_keypress; // Move on
break;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
case waiting_for_keypress:
// If the connection has dropped or timed out, or if the user has pressed a key
if (keyPressed())
state = open_connection; // Move on
break;
}
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Connect to NTRIP Caster. Return true is connection is successful.
bool beginClient()
{
Serial.print(F("Opening socket to "));
Serial.println(casterHost);
if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
{
Serial.println(F("Connection to caster failed"));
return (false);
}
else
{
Serial.print(F("Connected to "));
Serial.print(casterHost);
Serial.print(F(" : "));
Serial.println(casterPort);
Serial.print(F("Requesting NTRIP Data from mount point "));
Serial.println(mountPoint);
// Set up the server request (GET)
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest,
SERVER_BUFFER_SIZE,
"GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
mountPoint);
// Set up the credentials
char credentials[512];
if (strlen(casterUser) == 0)
{
strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials));
}
else
{
//Pass base64 encoded user:pw
char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
Serial.print(F("Sending credentials: "));
Serial.println(userCredentials);
#if defined(ARDUINO_ARCH_ESP32)
//Encode with ESP32 built-in library
base64 b;
String strEncodedCredentials = b.encode(userCredentials);
char encodedCredentials[strEncodedCredentials.length() + 1];
strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array
#else
//Encode with nfriendly library
int encodedLen = base64_enc_len(strlen(userCredentials));
char encodedCredentials[encodedLen]; //Create array large enough to house encoded data
base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed
#endif
snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials);
}
// Add the encoded credentials to the server request
strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
Serial.print(F("serverRequest size: "));
Serial.print(strlen(serverRequest));
Serial.print(F(" of "));
Serial.print(sizeof(serverRequest));
Serial.println(F(" bytes available"));
// Send the server request
Serial.println(F("Sending server request: "));
Serial.println(serverRequest);
ntripClient.write(serverRequest, strlen(serverRequest));
//Wait up to 5 seconds for response
unsigned long startTime = millis();
while (ntripClient.available() == 0)
{
if (millis() > (startTime + 5000))
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return (false);
}
delay(10);
}
//Check reply
int connectionResult = 0;
char response[512];
size_t responseSpot = 0;
while (ntripClient.available()) // Read bytes from the caster and store them
{
if (responseSpot == sizeof(response) - 1) // Exit the loop if we get too much data
break;
response[responseSpot++] = ntripClient.read();
if (connectionResult == 0) // Only print success/fail once
{
if (strstr(response, "200") != NULL) //Look for '200 OK'
{
connectionResult = 200;
}
if (strstr(response, "401") != NULL) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check your caster username and password."));
connectionResult = 401;
}
}
}
response[responseSpot] = '\0'; // NULL-terminate the response
//Serial.print(F("Caster responded with: ")); Serial.println(response); // Uncomment this line to see the full response
if (connectionResult != 200)
{
Serial.print(F("Failed to connect to "));
Serial.println(casterHost);
return (false);
}
else
{
Serial.print(F("Connected to: "));
Serial.println(casterHost);
lastReceivedRTCM_ms = millis(); //Reset timeout
}
} //End attempt to connect
return (true);
} // /beginClient
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Check for the arrival of any correction data. Push it to the GNSS.
//Return false if: the connection has dropped, or if we receive no data for maxTimeBeforeHangup_ms
bool processConnection()
{
if (ntripClient.connected() == true) // Check that the connection is still open
{
uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
size_t rtcmCount = 0;
//Collect any available RTCM data
while (ntripClient.available())
{
//Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data!
rtcmData[rtcmCount++] = ntripClient.read();
if (rtcmCount == sizeof(rtcmData))
break;
}
if (rtcmCount > 0)
{
lastReceivedRTCM_ms = millis();
//Push RTCM to GNSS module over I2C
myGNSS.pushRawData(rtcmData, rtcmCount);
Serial.print(F("Pushed "));
Serial.print(rtcmCount);
Serial.println(F(" RTCM bytes to ZED"));
}
}
else
{
Serial.println(F("Connection dropped!"));
return (false); // Connection has dropped - return false
}
//Timeout if we don't have new data for maxTimeBeforeHangup_ms
if ((millis() - lastReceivedRTCM_ms) > maxTimeBeforeHangup_ms)
{
Serial.println(F("RTCM timeout!"));
return (false); // Connection has timed out - return false
}
return (true);
} // /processConnection
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void closeConnection()
{
if (ntripClient.connected() == true)
{
ntripClient.stop();
}
Serial.println(F("Disconnected!"));
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Return true if a key has been pressed
bool keyPressed()
{
if (Serial.available()) // Check for a new key press
{
delay(100); // Wait for any more keystrokes to arrive
while (Serial.available()) // Empty the serial buffer
Serial.read();
return (true);
}
return (false);
}

View file

@ -0,0 +1,27 @@
//Your WiFi credentials
const char ssid[] = "yourSSID";
const char password[] = "yourPassword";
//RTK2Go works well and is free
//const char casterHost[] = "rtk2go.com";
//const uint16_t casterPort = 2101;
//const char casterUser[] = "myEmail@test.com"; //User must provide their own email address to use RTK2Go
//const char casterUserPW[] = "";
//const char mountPoint[] = "bldr_SparkFun1"; //The mount point you want to get data from
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char casterUser[] = "u99696"; //User name and pw must be obtained through their web portal
//const char casterUserPW[] = "466zez";
//const char mountPoint[] = "MP1979"; //The mount point you want to get data from
// Skylark (Swift Navigation) is awesome - but requires a subscription:
// https://www.swiftnav.com/skylark
// https://account.swiftnav.com/sign-up
// Use the promo-code ONEMONTHFREE for a free one month access to Skylark on one device
const char casterHost[] = "na.skylark.swiftnav.com"; // na = North Americs L1+L2; eu = Europe L1+L2
const uint16_t casterPort = 2101;
const char casterUser[] = "NTRIPusername+accountSubdomain"; // This is generated when you add a device to your Skylark account
const char casterUserPW[] = "devicePassword";
const char mountPoint[] = "CRS"; // The mount point you want to get data from. Select CRS (Cloud Reference Station) for the ZED-F9x

View file

@ -0,0 +1,346 @@
/*
Use ESP32 WiFi to get SPARTN data from PointPerfect (broker) as a Client
By: u-blox AG / Michael Ammann
Date: January 27th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain SPARTN 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 SSR correction service.
You will need to have a valid u-blox Thingstream account and have a PointPerfect Thing and payed plan.
Thingstream offers SSR corrections to SPARTN capable RTK receivers such as the u-blox ZED-F9 series
in continental Europe and US. Their Network is planned to be expanded to other regions over the next years.
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 SPARTN SSR correction.
Using WiFi for a rover is generally a bad idea because of limited WiFi range in the field.
You may use this exmaple in combination with a cell phone with hotspot mode enabled.
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!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Recommended Hardware:
MicroMod GNSS Carrier Board: https://www.sparkfun.com/products/17722
ESP32 Micromod https://www.sparkfun.com/products/16781
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;
#define OK(ok) (ok ? F(" -> OK") : F(" -> ERROR!")) // Convert uint8_t into OK/ERROR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//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
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 setAutoPVTcallbackPtr
// / _____ 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)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print((double)ubxDataStruct->ebno / 8, 3); //Convert to dB
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(F("PointPerfect testing"));
Wire.begin(); //Start I2C
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS module not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled.
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 0); // Use IP source (default). Change this to 1 for L-Band (PMP)
if (ok) ok = myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
if (ok) ok = myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the SPARTN data is being decrypted successfully
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
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();
Serial.println(F("Press any key to start MQTT/SPARTN Client."));
}
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/SPARTN Client."));
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
}
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 STARTN MQTT broker, receive RTCM, 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_KEY);
mqttClient.subscribe(MQTT_TOPIC_SPARTN);
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("SPARTN timeout. Disconnecting..."));
if (mqttClient.connected() == true)
mqttClient.stop();
return;
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
wifiClient.stop();
}

View file

@ -0,0 +1,45 @@
//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 paste 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 -> IP key distribution topic
const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/ip"; // This topic provides the IP only dynamic keys in UBX format
//const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/Lb"; // This topic provides the L-Band + IP dynamic keys in UBX format
// <Your PointPerfect Thing> -> Credentials -> IP correction topic for EU/US region
const char MQTT_TOPIC_SPARTN[] = "/pp/ip/us"; // This topic provides the SPARTN corrections for IP only: choice of {eu, us}
//const char MQTT_TOPIC_SPARTN[] = "/pp/Lb/us"; // This topic provides the SPARTN corrections for L-Band and L-Band + IP: choice of {eu, us}
// <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,300 @@
/*
Use the NEO-D9S L-Band receiver to provide corrections to a ZED-F9x via UBX-RXM-PMP messages
By: SparkFun Electronics / Paul Clark
Based on original code by: u-blox AG / Michael Ammann
Date: February 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 obtain SPARTN correction data from a NEO-D9S L-Band receiver and push it over I2C to a ZED-F9x.
This is a proof of concept to show how the UBX-RXM-PMP corrections control the accuracy.
If you are using the SparkFun Combo Board (SPX-20167), the correction data is transferred from the NEO to the ZED via UART2.
You don't need to push it over I2C. Doing so just gives the ZED twice as many correction messages.
Uncomment the "#define noPush" below to disable the I2C push.
You will need a Thingstream PointPerfect account to be able to access the SPARTN Credentials (L-Band or L-Band + IP Dynamic Keys).
Copy and paste the Current Key and Next Key into secrets.h.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S: https://www.sparkfun.com/products/19390
Combo Board: https://www.sparkfun.com/products/20167
Hardware Connections:
Use Qwiic cables to connect the NEO-D9S and ZED-F9x GNSS 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
*/
//#define noPush // Uncomment this line to disable pushing the correction data over I2C. Useful for the combo board which uses UART2 instead.
#include "secrets.h" // <- Copy and paste the Current Key and Next Key into secrets.h
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS; // ZED-F9x
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: pushRXMPMP will be called when new PMP data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_PMP_message_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMPMPmessageCallbackPtr
// / _____ This _must_ be UBX_RXM_PMP_message_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData)
{
//Extract the raw message payload length
uint16_t payloadLen = ((uint16_t)pmpData->lengthMSB << 8) | (uint16_t)pmpData->lengthLSB;
Serial.print(F("New RXM-PMP data received. Message payload length is "));
Serial.print(payloadLen);
#ifndef noPush
Serial.println(F(" Bytes. Pushing it to the GNSS..."));
//Push the PMP data to the GNSS
//The payload length could be variable, so we need to push the header and payload, then checksum
myGNSS.pushRawData(&pmpData->sync1, (size_t)payloadLen + 6); // Push the sync chars, class, ID, length and payload
myGNSS.pushRawData(&pmpData->checksumA, (size_t)2); // Push the checksum bytes
#else
Serial.println(F(" Bytes."));
#endif
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 setAutoPVTcallbackPtr
// / _____ 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)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print((double)ubxDataStruct->ebno / 8, 3); //Convert to dB
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NEO-D9S SPARTN Corrections"));
Wire.begin(); //Start I2C
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the ZED-F9x
//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 module not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
if (ok) ok = myGNSS.setPortInput(COM_PORT_UART1, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
if (ok) ok = myGNSS.setPortInput(COM_PORT_UART2, COM_TYPE_UBX | COM_TYPE_RTCM3 | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 1); // use LBAND PMP message
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
//Configure the SPARTN IP Dynamic Keys
//"When the receiver boots, the host should send 'current' and 'next' keys in one message." - Use setDynamicSPARTNKeys for this.
//"Every time the 'current' key is expired, 'next' takes its place."
//"Therefore the host should then retrieve the new 'next' key and send only that." - Use setDynamicSPARTNKey for this.
// The key can be provided in binary (uint8_t) format or in ASCII Hex (char) format, but in both cases keyLengthBytes _must_ represent the binary key length in bytes.
if (ok) ok = myGNSS.setDynamicSPARTNKeys(currentKeyLengthBytes, currentKeyGPSWeek, currentKeyGPSToW, currentDynamicKey,
nextKeyLengthBytes, nextKeyGPSWeek, nextKeyGPSToW, nextDynamicKey);
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the PMP data is being decrypted successfully
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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"));
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.setRXMPMPmessageCallbackPtr(&pushRXMPMP); // Call pushRXMPMP when new PMP data arrives. Push it to the GNSS
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop()
{
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
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.
}

View file

@ -0,0 +1,27 @@
// You can set the information below after signing up with the u-blox Thingstream portal
// and adding a new New PointPerfect Thing (L-Band or L-Band + IP)
// https://portal.thingstream.io/app/location-services/things
// In the new PointPerfect Thing, you go to the credentials tab and copy and paste the IP Dynamic Keys here.
//
// The keys are valid from a particular GPS Week Number and Time of Week.
// Looking at the credentials tab, the current key expires 23:59 Feb 11th 2022.
// This means the next key is valid _from_ Midnight Feb 12th 2022.
// That is GPS Week 2196. The GPS Time of Week in seconds is 518400.
// Working backwards, the current key became valid exactly 4 weeks earlier (Midnight Jan 15th 2022).
//
// See: https://www.labsat.co.uk/index.php/en/gps-time-calculator
//
// The keys are given as: 32 hexadecimal digits = 128 bits = 16 Bytes
//
// The next example shows how to retrieve the keys using ESP32 WiFi and MQTT.
// You can cut and paste the keys and GPS week/time-of-week from that example into here.
const uint8_t currentKeyLengthBytes = 16;
const char currentDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
const uint16_t currentKeyGPSWeek = 2254; // Update this when you add new keys
const uint32_t currentKeyGPSToW = 0;
const uint8_t nextKeyLengthBytes = 16;
const char nextDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
const uint16_t nextKeyGPSWeek = 2258; // Update this when you add new keys
const uint32_t nextKeyGPSToW = 0;

View file

@ -0,0 +1,76 @@
/*
Get the high position accuracy of the RTK enhanced position from HPPOSECEF
By: Nathan Seidle
SparkFun Electronics
Date: January 3rd, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to inspect the accuracy of the high-precision
positional solution.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the 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)"));
long accuracy = myGNSS.getPositionAccuracy();
Serial.print(F(" 3D Positional Accuracy: "));
Serial.print(accuracy);
Serial.println(F(" (mm)"));
}
}

View file

@ -0,0 +1,459 @@
/*
Use ESP32 WiFi to get the L-Band dynamic keys from PointPerfect, allowing a ZED-F9x to use
the PMP data from a NEO-D9S correction data receiver.
By: SparkFun / Paul Clark
Based on original code by: u-blox AG / Michael Ammann
Date: March 17th, 2022
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to obtain the L-Band dynamic keys from PointPerfect over ESP32 WiFi
and push them over I2C to a ZED-F9x. The ZED will then be able to decrypt the PMP correction data
from a NEO-D9S correction data receiver.
You can copy the keys directly from the Thingstream portal and paste them into your code - the
previous example shows how to do this - but calculating the "valid from" week and time is a chore.
This example requests the keys for you (using your client key and certificates) via MQTT.
It prints them too, so you can copy and paste them into the previous example if you wish.
You will need to have a valid u-blox Thingstream account and have a PointPerfect L-Band or L-Band + IP
Location Thing and payed plan.
Thingstream offers SSR corrections to SPARTN capable RTK receivers such as the u-blox ZED-F9 series
in continental Europe and US. Their Network is planned to be expanded to other regions over the next years.
To sign up, go to: https://portal.thingstream.io/app/location-services/things
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!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S Correction Data Receiver: https://www.sparkfun.com/products/19390
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Recommended Hardware:
MicroMod GNSS Carrier Board: https://www.sparkfun.com/products/17722
ESP32 Micromod https://www.sparkfun.com/products/16781
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; // ZED-F9x
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
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//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
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: pushRXMPMP will be called when new PMP data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_PMP_message_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMPMPmessageCallbackPtr
// / _____ This _must_ be UBX_RXM_PMP_message_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData)
{
//Extract the raw message payload length
uint16_t payloadLen = ((uint16_t)pmpData->lengthMSB << 8) | (uint16_t)pmpData->lengthLSB;
Serial.print(F("New RXM-PMP data received. Message payload length is "));
Serial.print(payloadLen);
Serial.println(F(" Bytes. Pushing it to the GNSS..."));
//Push the PMP data to the GNSS
//The payload length could be variable, so we need to push the header and payload, then checksum
myGNSS.pushRawData(&pmpData->sync1, (size_t)payloadLen + 6); // Push the sync chars, class, ID, length and payload
myGNSS.pushRawData(&pmpData->checksumA, (size_t)2); // Push the checksum bytes
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 setAutoPVTcallbackPtr
// / _____ 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)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print((double)ubxDataStruct->ebno / 8, 3); //Convert to dB
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(F("NEO-D9S SPARTN Corrections"));
Wire.begin(); //Start I2C
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the ZED-F9x
//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 module not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 1); // use LBAND PMP message
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the PMP data is being decrypted successfully
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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"));
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.setRXMPMPmessageCallbackPtr(&pushRXMPMP); // Call pushRXMPMP when new PMP data arrives. Push it to the GNSS
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi so we can request the dynamic keys via MQTT
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();
Serial.println(F("Press any key to start MQTT Client."));
}
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."));
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
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.
}
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();
if ((mqttData[0] == 0xB5) // Check if this is UBX-RXM-SPARTNKEY
&& (mqttData[1] == 0x62)
&& (mqttData[2] == 0x02) // Class: RXM
&& (mqttData[3] == 0x36)) // ID: SPARTNKEY
{
uint8_t numKeys = mqttData[7]; // Get the number of keys
uint8_t keyStart = 10 + (numKeys * 8); // Point to the start of the first key
for (uint8_t key = 0; key < numKeys; key++)
{
Serial.print(F("SPARTNKEY: "));
Serial.println(key);
Serial.print(F("Valid from GPS week number: "));
uint16_t validFromWno = ((uint16_t)mqttData[12 + (key * 8)]) | ((uint16_t)mqttData[13 + (key * 8)] << 8); // Little endian
Serial.println(validFromWno);
Serial.print(F("Valid from GPS time of week: "));
uint32_t validFromTow = ((uint32_t)mqttData[14 + (key * 8)]) | ((uint32_t)mqttData[15 + (key * 8)] << 8) | ((uint32_t)mqttData[16 + (key * 8)] << 16) | ((uint32_t)mqttData[17 + (key * 8)] << 24);
Serial.println(validFromTow);
uint8_t keyLengthBytes = mqttData[11 + (key * 8)];
Serial.print(F("Key length (bytes): "));
Serial.println(keyLengthBytes);
Serial.print(F("Key: \""));
for (uint8_t digit = 0; digit < keyLengthBytes; digit++)
{
Serial.print(mqttData[keyStart + digit] >> 4, HEX); // Print the key as ASCII Hex
Serial.print(mqttData[keyStart + digit] & 0x0F, HEX); // Print the key as ASCII Hex
}
Serial.println(F("\""));
keyStart += keyLengthBytes; // Update keyStart for the next key
}
}
}
}
delete[] mqttData;
}
//Connect to MQTT broker, receive dynamic keys 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_KEY);
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("MQTT timeout. Disconnecting..."));
if (mqttClient.connected() == true)
mqttClient.stop();
return;
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
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.
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
wifiClient.stop();
}

View file

@ -0,0 +1,40 @@
//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 (L-Band or L-Band + IP)
// https://portal.thingstream.io/app/location-services/things
// in the new PointPerfect Thing you go to the credentials page and copy paste 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 -> IP key distribution topic
//const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/ip"; // This topic provides the IP only dynamic keys in UBX format
const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/Lb"; // This topic provides the L-Band + IP dynamic keys in UBX format
// <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,121 @@
/*
Get the high precision ECEF coordinates using double
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 shows how to read the high-precision ECEF
positional solution. Please see below for information about the units.
** This example will only work correctly on platforms which support 64-bit double **
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
Hardware Connections:
Plug a Qwiic cable into the GNSS and (e.g.) a Redboard Artemis https://www.sparkfun.com/products/15444
or an Artemis Thing Plus https://www.sparkfun.com/products/15574
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 myWire Wire // This will work on the Redboard Artemis and the Artemis Thing Plus using Qwiic
//#define myWire Wire1 // Uncomment this line if you are using the extra SCL1/SDA1 pins (D17 and D16) on the Thing Plus
#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
myWire.begin();
//myGNSS.enableDebugging(Serial); // Uncomment this line to enable debug messages
if (myGNSS.begin(myWire) == 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)
;
}
// Check that this platform supports 64-bit (8 byte) double
if (sizeof(double) < 8)
{
Serial.println(F("Warning! Your platform does not support 64-bit double."));
Serial.println(F("The ECEF coordinates will be inaccurate."));
}
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.
//The module only responds when a new position is available.
if (millis() - lastTime > 1000)
{
lastTime = millis(); //Update the timer
// getHighResECEFX: returns the X coordinate from HPPOSECEF as an int32_t in cm
// getHighResECEFXHp: returns the high resolution component of the X coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
// getHighResECEFY: returns the Y coordinate from HPPOSECEF as an int32_t in cm
// getHighResECEFYHp: returns the high resolution component of the Y coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
// getHighResECEFZ: returns the Z coordinate from HPPOSECEF as an int32_t in cm
// getHighResECEFZHp: returns the high resolution component of the Z coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
// getPositionAccuracy: returns the position accuracy estimate from HPPOSLLH as an uint32_t in mm (note: not 0.1mm)
// First, let's collect the position data
int32_t ECEFX = myGNSS.getHighResECEFX();
int8_t ECEFXHp = myGNSS.getHighResECEFXHp();
int32_t ECEFY = myGNSS.getHighResECEFY();
int8_t ECEFYHp = myGNSS.getHighResECEFYHp();
int32_t ECEFZ = myGNSS.getHighResECEFZ();
int8_t ECEFZHp = myGNSS.getHighResECEFZHp();
uint32_t accuracy = myGNSS.getPositionAccuracy();
// Defines storage for the ECEF coordinates as double
double d_ECEFX;
double d_ECEFY;
double d_ECEFZ;
// Assemble the high precision coordinates
d_ECEFX = ((double)ECEFX) / 100.0; // Convert from cm to m
d_ECEFX += ((double)ECEFXHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
d_ECEFY = ((double)ECEFY) / 100.0; // Convert from cm to m
d_ECEFY += ((double)ECEFYHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
d_ECEFZ = ((double)ECEFZ) / 100.0; // Convert from cm to m
d_ECEFZ += ((double)ECEFZHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
// Print the coordinates with 4 decimal places (0.1mm)
Serial.print("X (m): ");
Serial.print(d_ECEFX, 4);
Serial.print(", Y (m): ");
Serial.print(d_ECEFY, 4);
Serial.print(", Z (m): ");
Serial.print(d_ECEFZ, 4);
// Now define float storage for the accuracy
float f_accuracy;
// Convert the horizontal accuracy (mm) to a float
f_accuracy = accuracy;
// Now convert to m
f_accuracy = f_accuracy / 1000.0; // Convert from mm to m
// Finally, do the printing
Serial.print(", Accuracy (m): ");
Serial.println(f_accuracy, 3); // Print the accuracy with 3 decimal places
}
}

View file

@ -0,0 +1,312 @@
/*
Use the NEO-D9C QZSS-L6 receiver to provide corrections to a ZED-F9x via UART
By: SparkFun Electronics / Paul Clark
Based on original code by: u-blox AG / Michael Ammann
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 configure a NEO-D9C QZSS-L6 receiver and have it send coorection data to a ZED-F9x via Serial (UART).
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....
As a work-around, this example expects the ZED-F9P to be connected via UART1 Serial (Teensy Serial1) to avoid a collision
on the I2C bus.
(Yes, OK, it is straight-forward to change the NEO-D9C's I2C address. But, with this example, you do not need to do that.)
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. See line 272.
Connections: e.g. for Teensy 4.0, Geosense D9CX1 NEO-D9C and SparkFun ZED-F9P:
Teensy 5V (Vin) -> ZED-F9P 5V -> D9CX1 V5V (JP1 Pin 2)
Teensy GND -> ZED-F9P GND -> D9CX1 GND (JP1 Pin 1)
Teensy SDA1 (17) -> D9CX1 SDA (JP1 Pin 5)
Teensy SCL1 (16) -> D9CX1 SCL (JP1 Pin 6)
Teensy Serial1 TX1 (1) -> ZED-F9P UART1 RX1
Teensy Serial1 RX1 (0) -> ZED-F9P UART1 TX1
D9CX1 UART1 TX1 (JP1 Pin 3) -> ZED-F9P UART2 RX2
D9CX1 UART1 RX1 (JP1 Pin 4) -> ZED-F9P UART2 TX2
The Teensy communicates with the NEO-D9C (D9CX1) via I2C on address 0x42
The Teensy communicates with the ZED-F9P via UART (Serial1 on Teensy, UART1 on ZED) to avoid the I2C address collision
The NEO-D9C corrections (UBX-RXM-QZSSL6) are sent from NEO UART1 to ZED UART2
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 Qwiic cables to connect the NEO-D9S and ZED-F9x GNSS 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 myGNSS; // ZED-F9x
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();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 setAutoPVTcallbackPtr
// / _____ 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)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print((double)ubxDataStruct->ebno / 8, 3); //Convert to dB
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NEO-D9C Corrections"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the ZED-F9x
Serial1.begin(38400); // The ZED-F9P is connected via Serial1 to UART1
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
while (myGNSS.begin(Serial1) == false) //Connect to the u-blox module using Serial1 and UART1
{
Serial.println(F("u-blox GNSS module not detected. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setUART1Output(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_UART2, COM_TYPE_UBX | COM_TYPE_RTCM3 | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled on UART2
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_UART1, 1); // Enable UBX-RXM-COR messages on UART1
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the QZSS-L6 data is being decrypted successfully
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the NEO-D9C QZSS-L6 receiver
Wire.begin(); //Start I2C
//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"));
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_UART1OUTPROT_UBX, 1); // Enable UBX output on UART1
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()
{
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
myQZSS.checkUblox(); // Check for the arrival of new QZSS-L6 data and process it.
myQZSS.checkCallbacks(); // Check if any LBand callbacks are waiting to be processed.
}

View file

@ -0,0 +1,105 @@
/*
Polling RXM RAWX reports over I2C
By: Paul Clark
SparkFun Electronics
Date: November 25th, 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 poll RXM RAWX reports from the u-blox module.
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;
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
// Because we are polling RAWX, we need to increase the size of packetCfg.payload
// RAWX packets can be over 2K bytes so let's allocate 3K bytes
if (!myGNSS.setPacketCfgPayloadSize(3000))
{
Serial.println(F("setPacketCfgPayloadSize failed. You will not be able to poll RAWX data. Freezing."));
while (1); // Do nothing more
}
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. Retrying..."));
delay(1000);
}
Serial.println(F("u-blox GNSS detected."));
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!)
}
void loop()
{
if (myGNSS.getRXMRAWX()) // Poll RAWX data
{
// Print the RAWX data, using a pointer to the RXM RAWX data stored in packetUBXRXMRAWX
printRAWX(&myGNSS.packetUBXRXMRAWX->data);
}
}
void printRAWX(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();
}
}

View file

@ -0,0 +1,63 @@
/*
Configuring u-blox Module using new VALGET / VALSET / VALDEL methods
Please see u-blox_config_keys.h for the definitions of _all_ of the configuration keys
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.
u-blox deprecated many -CFG messages and replaced them with new
VALGET, VALSET, VALDEL methods. This shows the basics of how to use
these methods.
Leave NMEA parsing behind. Now you can simply ask the module for the datums you want!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the 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);
}
byte response;
response = myGNSS.getVal8(UBLOX_CFG_I2C_ADDRESS, VAL_LAYER_RAM); // Get the I2C address (see u-blox_config_keys.h for details)
Serial.print(F("I2C Address: 0x"));
Serial.println(response >> 1, HEX); //We have to shift by 1 to get the common '7-bit' I2C address format
response = myGNSS.getVal8(UBLOX_CFG_I2COUTPROT_NMEA, VAL_LAYER_RAM); // Get the flag indicating is NMEA should be output on I2C
Serial.print(F("Output NMEA over I2C port: 0x"));
Serial.print(response, HEX);
}
void loop()
{
}

View file

@ -0,0 +1,195 @@
/*
Send UBX binary commands to enable RTCM sentences on u-blox ZED-F9P module
By: Nathan Seidle
SparkFun Electronics
Date: January 9th, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example does all steps to configure and enable a ZED-F9P as a base station:
Begin Survey-In
Once we've achieved 2m accuracy and 300s have passed, survey is complete
Enable six RTCM messages
Begin outputting RTCM bytes
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the 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;
//#define USE_SERIAL1 // Uncomment this line to push the RTCM data to Serial1
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("u-blox Base Station example"));
#ifdef USE_SERIAL1
// If our board supports it, we can output the RTCM data on Serial1
Serial1.begin(115200);
#endif
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);
}
// 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 | COM_TYPE_NMEA | COM_TYPE_RTCM3); // Ensure RTCM3 is enabled
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port settings to flash and BBR
while (Serial.available()) Serial.read(); //Clear any latent chars in serial buffer
Serial.println(F("Press any key to send commands to begin Survey-In"));
while (Serial.available() == 0) ; //Wait for user to press a key
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_1074, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1084, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1094, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1124, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds
//Use COM_PORT_UART1 for the above six messages to direct RTCM messages out UART1
//COM_PORT_UART2, COM_PORT_USB, COM_PORT_SPI are also available
//For example: response &= myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_UART1, 10);
if (response == true)
{
Serial.println(F("RTCM messages enabled"));
}
else
{
Serial.println(F("RTCM failed to enable. Are you sure you have an ZED-F9P?"));
while (1); //Freeze
}
//Check if Survey is in Progress before initiating one
// From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
// Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
// You can either read the data from packetUBXNAVSVIN directly
// or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; and getSurveyInMeanAccuracy
response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time)
if (response == false) // Check if fresh data was received
{
Serial.println(F("Failed to get Survey In status"));
while (1); //Freeze
}
if (myGNSS.getSurveyInActive() == true) // Use the helper function
//if (myGNSS.packetUBXNAVSVIN->data.active > 0) // Or we could read active directly
{
Serial.print(F("Survey already in progress."));
}
else
{
//Start survey
//The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info.
//response = myGNSS.enableSurveyMode(300, 2.000); //Enable Survey in on NEO-M8P, 300 seconds, 2.0m
response = myGNSS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m
//response = myGNSS.enableSurveyModeFull(86400, 2.000); //Enable Survey in, 24 hours, 2.0m
if (response == false)
{
Serial.println(F("Survey start failed. Freezing..."));
while (1);
}
Serial.println(F("Survey started. This will run until 60s has passed and less than 5m accuracy is achieved."));
}
while(Serial.available()) Serial.read(); //Clear buffer
//Begin waiting for survey to complete
while (myGNSS.getSurveyInValid() == false) // Call the helper function
//while (myGNSS.packetUBXNAVSVIN->data.valid == 0) // Or we could read valid directly
{
if(Serial.available())
{
byte incoming = Serial.read();
if(incoming == 'x')
{
//Stop survey mode
response = myGNSS.disableSurveyMode(); //Disable survey
Serial.println(F("Survey stopped"));
break;
}
}
// From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
// Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
// You can either read the data from packetUBXNAVSVIN directly
// or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; getSurveyInObservationTimeFull; and getSurveyInMeanAccuracy
response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time)
if (response == true) // Check if fresh data was received
{
Serial.print(F("Press x to end survey - "));
Serial.print(F("Time elapsed: "));
Serial.print((String)myGNSS.getSurveyInObservationTimeFull()); // Call the helper function
Serial.print(F(" ("));
Serial.print((String)myGNSS.packetUBXNAVSVIN->data.dur); // Read the survey-in duration directly from packetUBXNAVSVIN
Serial.print(F(") Accuracy: "));
Serial.print((String)myGNSS.getSurveyInMeanAccuracy()); // Call the helper function
Serial.print(F(" ("));
// Read the mean accuracy directly from packetUBXNAVSVIN and manually convert from mm*0.1 to m
float meanAcc = ((float)myGNSS.packetUBXNAVSVIN->data.meanAcc) / 10000.0;
Serial.print((String)meanAcc);
Serial.println(F(")"));
}
else
{
Serial.println(F("SVIN request failed"));
}
delay(1000);
}
Serial.println(F("Survey valid!"));
Serial.println(F("Base survey complete! RTCM now broadcasting."));
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the I2C port to output UBX and RTCM sentences (not really an option, turns on NMEA as well)
}
void loop()
{
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
}
//This function gets called from the SparkFun u-blox Arduino Library.
//As each RTCM byte comes in you can specify what to do with it
//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc.
void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming)
{
#ifdef USE_SERIAL1
//Push the RTCM data to Serial1
Serial1.write(incoming);
#endif
//Pretty-print the HEX values to Serial
if (myGNSS.rtcmFrameCounter % 16 == 0) Serial.println();
Serial.print(F(" "));
if (incoming < 0x10) Serial.print(F("0"));
Serial.print(incoming, HEX);
}

View file

@ -0,0 +1,209 @@
/*
Note: compiles OK with v2.0 but is untested. The previous example works fine though.
Send UBX binary commands to enable RTCM sentences on u-blox ZED-F9P module
By: Nathan Seidle
SparkFun Electronics
Date: January 9th, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example does all steps to configure and enable a ZED-F9P as a base station:
Begin Survey-In
Once we've achieved 2m accuracy and 300s have passed, survey is complete
Enable six RTCM messages
Begin outputting RTCM bytes
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a BlackBoard
Plug a SerLCD onto the Qwiic bus
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Watch the output on the LCD or open the serial monitor at 115200 baud to see the output
*/
#define STAT_LED 13
#include <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;
#include <SerLCD.h> //Click here to get the library: http://librarymanager/All#SparkFun_SerLCD
SerLCD lcd; // Initialize the library with default I2C address 0x72
void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("u-blox GNSS I2C Test"));
Wire.begin();
pinMode(STAT_LED, OUTPUT);
digitalWrite(STAT_LED, LOW);
lcd.begin(Wire); //Set up the LCD for Serial communication at 9600bps
lcd.setBacklight(0x4B0082); //indigo, a kind of dark purplish blue
lcd.clear();
lcd.print(F("LCD Ready"));
myGNSS.begin(Wire);
if (myGNSS.isConnected() == false)
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
lcd.setCursor(0, 1);
lcd.print(F("No GNSS detected"));
while (1)
;
}
Wire.setClock(400000); //Increase I2C clock speed to 400kHz
lcd.setCursor(0, 1);
lcd.print("GNSS Detected");
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); // Ensure RTCM3 is enabled
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port 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_1074, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1084, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1094, COM_PORT_I2C, 1);
response &= myGNSS.enableRTCMmessage(UBX_RTCM_1124, 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 ZED-F9P? Freezing."));
while (1)
; //Freeze
}
//Check if Survey is in Progress before initiating one
// From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
// Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
// You can either read the data from packetUBXNAVSVIN directly
// or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; and getSurveyInMeanAccuracy
response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time)
if (response == false)
{
Serial.println(F("Failed to get Survey In status. Freezing."));
while (1)
; //Freeze
}
if (myGNSS.getSurveyInActive() == true) // Use the helper function
{
Serial.print(F("Survey already in progress."));
lcd.setCursor(0, 2);
lcd.print(F("Survey already going"));
}
else
{
//Start survey
response = myGNSS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m
if (response == false)
{
Serial.println(F("Survey start failed"));
lcd.setCursor(0, 3);
lcd.print(F("Survey start failed. Freezing."));
while (1)
;
}
Serial.println(F("Survey started. This will run until 60s has passed and less than 5m accuracy is achieved."));
}
while (Serial.available())
Serial.read(); //Clear buffer
lcd.clear();
lcd.print(F("Survey in progress"));
//Begin waiting for survey to complete
while (myGNSS.getSurveyInValid() == false) // Call the helper function
{
if (Serial.available())
{
byte incoming = Serial.read();
if (incoming == 'x')
{
//Stop survey mode
response = myGNSS.disableSurveyMode(); //Disable survey
Serial.println(F("Survey stopped"));
break;
}
}
// From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
// Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
// You can either read the data from packetUBXNAVSVIN directly
// or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; and getSurveyInMeanAccuracy
response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time)
if (response == true)
{
Serial.print(F("Press x to end survey - "));
Serial.print(F("Time elapsed: "));
Serial.print((String)myGNSS.getSurveyInObservationTime()); // Call the helper function
lcd.setCursor(0, 1);
lcd.print(F("Elapsed: "));
lcd.print((String)myGNSS.getSurveyInObservationTime()); // Call the helper function
Serial.print(F(" Accuracy: "));
Serial.print((String)myGNSS.getSurveyInMeanAccuracy()); // Call the helper function
Serial.println();
lcd.setCursor(0, 2);
lcd.print(F("Accuracy: "));
lcd.print((String)myGNSS.getSurveyInMeanAccuracy()); // Call the helper function
}
else
{
Serial.println(F("SVIN request failed"));
}
delay(1000);
}
Serial.println(F("Survey valid!"));
Serial.println(F("Base survey complete! RTCM now broadcasting."));
lcd.clear();
lcd.print(F("Transmitting RTCM"));
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the I2C port to output UBX and RTCM sentences (not really an option, turns on NMEA as well)
}
void loop()
{
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
//Do anything you want. Call checkUblox() every second. ZED-F9P has TX buffer of 4k bytes.
delay(250); //Don't pound too hard on the I2C bus
}
//This function gets called from the SparkFun u-blox Arduino Library.
//As each RTCM byte comes in you can specify what to do with it
//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc.
void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming)
{
//Let's just pretty-print the HEX values for now
if (myGNSS.rtcmFrameCounter % 16 == 0)
Serial.println();
Serial.print(" ");
if (incoming < 0x10)
Serial.print("0");
Serial.print(incoming, HEX);
}

View file

@ -0,0 +1,175 @@
/*
Send UBX binary commands to enable RTCM sentences on u-blox ZED-F9P module
By: Nathan Seidle
SparkFun Electronics
Date: January 9th, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to query the module for RELPOS information in the NED frame.
It assumes you already have RTCM correction data being fed to the receiver.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard Qwiic or BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <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;
//#define USE_SERIAL1 // Uncomment this line to push the RTCM data from Serial1 to the module via I2C
size_t numBytes = 0; // Record the number os bytes received from Serial1
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("u-blox Base station example");
#ifdef USE_SERIAL1
// If our board supports it, we can receive the RTCM data on Serial1
Serial1.begin(115200);
#endif
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);
}
// Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
//myGNSS.factoryDefault(); delay(5000);
#ifdef USE_SERIAL1
Serial.print(F("Enabling UBX and RTCM input on I2C. Result: "));
Serial.print(myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_RTCM3)); //Enable UBX and RTCM input on I2C
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port settings to flash and BBR
#endif
}
void loop()
{
// From v2.0, the data from getRELPOSNED (UBX-NAV-RELPOSNED) is returned in UBX_NAV_RELPOSNED_t packetUBXNAVRELPOSNED
// Please see u-blox_structs.h for the full definition of UBX_NAV_RELPOSNED_t
// You can either read the data from packetUBXNAVRELPOSNED directly
// or can use the helper functions: getRelPosN/E/D; getRelPosAccN/E/D
if (myGNSS.getRELPOSNED() == true)
{
Serial.print("relPosN: ");
Serial.println(myGNSS.getRelPosN(), 4); // Use the helper functions to get the rel. pos. as m
Serial.print("relPosE: ");
Serial.println(myGNSS.getRelPosE(), 4);
Serial.print("relPosD: ");
Serial.println(myGNSS.getRelPosD(), 4);
Serial.print("relPosLength: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosLength);
Serial.print("relPosHeading: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosHeading);
Serial.print("relPosHPN: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosHPN);
Serial.print("relPosHPE: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosHPE);
Serial.print("relPosHPD: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosHPD);
Serial.print("relPosHPLength: ");
Serial.println(myGNSS.packetUBXNAVRELPOSNED->data.relPosHPLength);
Serial.print("accN: ");
Serial.println(myGNSS.getRelPosAccN(), 4); // Use the helper functions to get the rel. pos. accuracy as m
Serial.print("accE: ");
Serial.println(myGNSS.getRelPosAccE(), 4);
Serial.print("accD: ");
Serial.println(myGNSS.getRelPosAccD(), 4);
Serial.print("gnssFixOk: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.gnssFixOK == true)
Serial.println("x");
else
Serial.println("");
Serial.print("diffSolution: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.diffSoln == true)
Serial.println("x");
else
Serial.println("");
Serial.print("relPosValid: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.relPosValid == true)
Serial.println("x");
else
Serial.println("");
Serial.print("carrier Solution Type: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 0)
Serial.println("None");
else if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 1)
Serial.println("Float");
else if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 2)
Serial.println("Fixed");
Serial.print("isMoving: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.isMoving == true)
Serial.println("x");
else
Serial.println("");
Serial.print("refPosMiss: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.refPosMiss == true)
Serial.println("x");
else
Serial.println("");
Serial.print("refObsMiss: ");
if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.refObsMiss == true)
Serial.println("x");
else
Serial.println("");
}
else
Serial.println("RELPOS request failed");
for (int i = 0; i < 500; i++)
{
#ifdef USE_SERIAL1
uint8_t store[256];
while ((Serial1.available()) && (numBytes < 256)) // Check if data has been received
{
store[numBytes++] = Serial1.read(); // Read a byte from Serial1 and store it
}
if (numBytes > 0) // Check if data was received
{
//Serial.print("Pushing ");
//Serial.print(numBytes);
//Serial.println(" bytes via I2C");
//On processors which have large I2C buffers, like the ESP32, we can make the push more efficient by
//calling setI2CTransactionSize first to increase the maximum I2C transmission size
//(setI2CTransactionSize only needs to be called once, so it should be in setup, not loop)
//myGNSS.setI2CTransactionSize(128); // Send up to 128 bytes in one I2C transmission
//The ESP32 seems to have an issue when using a restarts to break up long RTCM pushes
//You may need to call pushRawData and set the optional 'stop' argument to true:
//myGNSS.pushRawData(((uint8_t *)&store), numBytes, true); // Push the RTCM data via I2C - always use stops on long RTCM pushes
myGNSS.pushRawData(((uint8_t *)&store), numBytes); // Push the RTCM data via I2C - using restarts to break up long I2C pushes
numBytes = 0; // Reset numBytes
}
#endif
delay(10);
}
}

View file

@ -0,0 +1,60 @@
/*
Get a device's I2C address using advanced getVal method
By: Nathan Seidle
SparkFun Electronics
Date: January 9th, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
u-blox changed how to configure their modules in 2019. As of version 23 of the UBX protocol the
UBX-CFG commands are deprecated; they still work, they just recommend using VALSET, VALGET, and VALDEL
commands instead. This example shows how to use this new command structure.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard Qwiic or BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <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("u-blox getVal 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)
;
}
//myGNSS.enableDebugging(); //Enable debug messages over Serial (default)
//myGNSS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB
uint8_t currentI2Caddress = myGNSS.getVal8(UBLOX_CFG_I2C_ADDRESS);
Serial.print("Current I2C address (should be 0x42): 0x");
Serial.println(currentI2Caddress >> 1, HEX); //u-blox module returns a shifted 8-bit address. Make it 7-bit unshifted.
}
void loop()
{
// Nothing to do here
}

View file

@ -0,0 +1,76 @@
/*
Send UBX binary commands to enable RTCM sentences on u-blox ZED-F9P module
By: Nathan Seidle
SparkFun Electronics
Date: January 9th, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
u-blox changed how to configure their modules in 2019. As of version 23 of the UBX protocol the
UBX-CFG commands are deprecated; they still work, they just recommend using VALSET, VALGET, and VALDEL
commands instead. This example shows how to use this new command structure.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard Qwiic or BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <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("u-blox getVal 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)
;
}
//myGNSS.enableDebugging(); //Enable debug messages over Serial (default)
//myGNSS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB
bool setValueSuccess;
//These key values are hard coded and defined in u-blox_config_keys.h.
//You can obtain them from the ZED-F9P interface description doc
//or from u-center's Messages->CFG->VALSET window. Keys must be 32-bit.
//setValueSuccess = myGNSS.setVal(UBLOX_CFG_NMEA_HIGHPREC, 0); //Enable high precision NMEA
//setValueSuccess = myGNSS.setVal(UBLOX_CFG_RATE_MEAS, 1000); //Set measurement rate to 100ms (10Hz update rate)
setValueSuccess = myGNSS.setVal(UBLOX_CFG_RATE_MEAS, 1000); //Set measurement rate to 1000ms (1Hz update rate)
//Below is the original way we enabled the RTCM message on the I2C port. After that, we show how to do the same
//but with setVal().
//Original: myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second
//setValueSuccess = myGNSS.setVal(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_I2C, 1); //Set output rate of msg 1005 over the I2C port to once per second
if (setValueSuccess == true)
{
Serial.println("Value was successfully set");
}
else
Serial.println("Value set failed");
}
void loop()
{
}

View file

@ -0,0 +1,98 @@
/*
Configuring port settings using the newer getVal/setVal methods
By: Nathan Seidle
SparkFun Electronics
Date: October 23rd, 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 query a u-blox module for its UART1 settings and
then change them if the settings aren't what we want.
Note: getVal/setVal/delVal are only support in u-blox protocol versions 27 and higher.
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 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> //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)
;
}
bool response = true;
//Read the settings from RAM (what the module is running right now, not BBR, Flash, or default)
uint8_t currentUART1Setting_ubx = myGNSS.getVal8(UBLOX_CFG_UART1INPROT_UBX);
uint8_t currentUART1Setting_nmea = myGNSS.getVal8(UBLOX_CFG_UART1INPROT_NMEA);
uint8_t currentUART1Setting_rtcm3 = myGNSS.getVal8(UBLOX_CFG_UART1INPROT_RTCM3X);
Serial.print("currentUART1Setting_ubx: ");
Serial.println(currentUART1Setting_ubx);
Serial.print("currentUART1Setting_nmea: ");
Serial.println(currentUART1Setting_nmea);
Serial.print("currentUART1Setting_rtcm3: ");
Serial.println(currentUART1Setting_rtcm3);
//Check if NMEA and RTCM are enabled for UART1
if (currentUART1Setting_ubx == 0 || currentUART1Setting_nmea == 0)
{
Serial.println("Updating UART1 configuration");
//setVal sets the values for RAM, BBR, and Flash automatically so no .saveConfiguration() is needed
response &= myGNSS.setVal8(UBLOX_CFG_UART1INPROT_UBX, 1); //Enable UBX on UART1 Input
response &= myGNSS.setVal8(UBLOX_CFG_UART1INPROT_NMEA, 1); //Enable NMEA on UART1 Input
response &= myGNSS.setVal8(UBLOX_CFG_UART1INPROT_RTCM3X, 0); //Disable RTCM on UART1 Input
if (response == false)
Serial.println("SetVal failed");
else
Serial.println("SetVal succeeded");
}
else
Serial.println("No port change needed");
//Change speed of UART2
uint32_t currentUART2Baud = myGNSS.getVal32(UBLOX_CFG_UART2_BAUDRATE);
Serial.print("currentUART2Baud: ");
Serial.println(currentUART2Baud);
if (currentUART2Baud != 57600)
{
response &= myGNSS.setVal32(UBLOX_CFG_UART2_BAUDRATE, 57600);
if (response == false)
Serial.println("SetVal failed");
else
Serial.println("SetVal succeeded");
}
else
Serial.println("No baud change needed");
Serial.println("Done");
}
void loop()
{
}

View file

@ -0,0 +1,101 @@
/*
Send UBX binary commands to enable RTCM sentences on u-blox ZED-F9P module
Based on Example7 By: Nathan Seidle
SparkFun Electronics
Updated by Paul Clark to demonstrate setVal8/16/32, newCfgValset8/16/32, addCfgValset8/16/32 and sendCfgValset8/16/32
Date: July 1st, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
u-blox changed how to configure their modules in 2019. As of version 23 of the UBX protocol the
UBX-CFG commands are deprecated; they still work, they just recommend using VALSET, VALGET, and VALDEL
commands instead. This example shows how to use this new command structure.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106
Hardware Connections:
Plug a Qwiic cable into the GNSS and a RedBoard Qwiic or BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/
#include <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("u-blox multi setVal 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)
;
}
//myGNSS.enableDebugging(); //Enable debug messages over Serial (default)
//myGNSS.enableDebugging(SerialUSB); //Enable debug messages over Serial USB
bool setValueSuccess = true;
//These key values are hard coded. You can obtain them from the ZED-F9P interface description doc
//or from u-center's Messages->CFG->VALSET window. Keys must be 32-bit.
//Choose setVal8, setVal16 or setVal32 depending on the required value data width (1, 2 or 4 bytes)
//L, U1, I1, E1 and X1 values are 8-bit
//U2, I2, E2 and X2 values are 16-bit
//U4, I4, R4, E4, X4 values are 32-bit
setValueSuccess &= myGNSS.setVal8(UBLOX_CFG_NMEA_HIGHPREC, 0); //Enable high precision NMEA (value is 8-bit (L / U1))
//setValueSuccess &= myGNSS.setVal16(UBLOX_CFG_RATE_MEAS, 200); //Set measurement rate to 100ms (10Hz update rate) (value is 16-bit (U2))
//setValueSuccess &= myGNSS.setVal16(UBLOX_CFG_RATE_MEAS, 200, VAL_LAYER_RAM); //Set rate setting in RAM only, instead of "ALL" (RAM, BBR and Flash)
setValueSuccess &= myGNSS.setVal16(UBLOX_CFG_RATE_MEAS, 1000); //Set measurement rate to 1000ms (1Hz update rate) (value is 16-bit (U2))
//Below is the original way we enabled a single RTCM message on the I2C port. After that, we show how to do the same
//but with multiple messages all in one go using newCfgValset, addCfgValset and sendCfgValset.
//Original: myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second
//If we will be sending a large number of key IDs and values, packetCfg could fill up before the CFG_VALSET is sent...
//There are three possible solutions:
// Increase the space available by calling myGNSS.setPacketCfgPayloadSize
// Monitor how much space is remaining by calling myGNSS.getCfgValsetSpaceRemaining. Call myGNSS.sendCfgValset(); before packetCfg becomes full.
// Call myGNSS.autoSendCfgValsetAtSpaceRemaining(16); . This will cause the existing CFG_VALSET to be send automatically and a new one created when packetCfg has less than 16 bytes remaining.
myGNSS.autoSendCfgValsetAtSpaceRemaining(16); // Trigger an auto-send when packetCfg has less than 16 bytes are remaining
//Begin with newCfgValset
setValueSuccess &= myGNSS.newCfgValset(); // Defaults to configuring the setting in Flash, RAM and BBR
//setValueSuccess &= myGNSS.newCfgValset(VAL_LAYER_RAM); //Set this and the following settings in RAM only instead of Flash/RAM/BBR
// Add KeyIDs and Values
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_I2C, 1); //Set output rate of msg 1005 over the I2C port to once per measurement (value is 8-bit (U1))
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1077_I2C, 1); //Set output rate of msg 1077 over the I2C port to once per measurement (value is 8-bit (U1))
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1087_I2C, 1); //Set output rate of msg 1087 over the I2C port to once per measurement (value is 8-bit (U1))
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1127_I2C, 1); //Set output rate of msg 1127 over the I2C port to once per measurement (value is 8-bit (U1))
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1097_I2C, 1); //Set output rate of msg 1097 over the I2C port to once per measurement (value is 8-bit (U1))
setValueSuccess &= myGNSS.addCfgValset8(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_I2C, 10); //Set output rate of msg 1230 over the I2C port to once every 10 measurements (value is 8-bit (U1))
// Send the packet using sendCfgValset
setValueSuccess &= myGNSS.sendCfgValset();
if (setValueSuccess == true)
{
Serial.println("Values were successfully set");
}
else
Serial.println("Value set failed");
}
void loop()
{
}