first commit
This commit is contained in:
commit
e6a42e28e9
4355 changed files with 4626605 additions and 0 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
@ -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.
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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)"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue