+25
-9
@@ -93,27 +93,43 @@ void loop()
|
||||
WiFiClientSecure wifiClient = WiFiClientSecure();
|
||||
MqttClient mqttClient(wifiClient);
|
||||
|
||||
void mqttMessageHandler(int messageSize) {
|
||||
uint8_t mgaData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
|
||||
int mgaCount = 0;
|
||||
Serial.print(F("Pushed data from "));
|
||||
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
|
||||
mgaData[mgaCount++] = ch;
|
||||
if (mgaCount == sizeof(mgaData))
|
||||
mqttData[mqttCount++] = ch;
|
||||
|
||||
if (mqttCount == mqttLimit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mgaCount > 0)
|
||||
if (mqttCount > 0)
|
||||
{
|
||||
//Push MGA data to GNSS module over I2C
|
||||
myGNSS.pushRawData(mgaData, mgaCount, false);
|
||||
//Push KEYS or SPARTN data to GNSS module over I2C
|
||||
myGNSS.pushRawData(mqttData, mqttCount, false);
|
||||
lastReceived_ms = millis();
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mqttData;
|
||||
}
|
||||
|
||||
//Connect to MQTT broker, receive MGA, and push to ZED module over I2C
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
Feel like supporting open source hardware?
|
||||
Buy a board from SparkFun!
|
||||
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
|
||||
NEO-D9S: Coming soon!
|
||||
NEO-D9S Correction Data Receiver: https://www.sparkfun.com/products/19390
|
||||
|
||||
Hardware Connections:
|
||||
Use a Qwiic cable to connect the NEO-D9S L-Band corection data receiver to your board
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
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
|
||||
@@ -44,10 +45,137 @@
|
||||
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
|
||||
SFE_UBLOX_GNSS myGNSS;
|
||||
|
||||
//Global variables
|
||||
#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(ubxDataStruct->ebno);
|
||||
|
||||
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()
|
||||
@@ -58,17 +186,32 @@ void setup()
|
||||
|
||||
Wire.begin(); //Start I2C
|
||||
|
||||
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
|
||||
//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 GPS not detected at default I2C address. Please check wiring. Freezing."));
|
||||
while (1);
|
||||
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"));
|
||||
|
||||
Serial.println(F("u-blox module connected"));
|
||||
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
|
||||
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled.
|
||||
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));
|
||||
|
||||
myGNSS.setNavigationFrequency(1); //Set output in Hz.
|
||||
Serial.print(F("Connecting to local WiFi"));
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
@@ -81,6 +224,9 @@ void setup()
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
while (Serial.available()) Serial.read();
|
||||
|
||||
Serial.println(F("Press any key to start MQTT/SPARTN Client."));
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
@@ -88,38 +234,56 @@ 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."));
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
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) {
|
||||
uint8_t spartnData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
|
||||
int spartnCount = 0;
|
||||
Serial.print(F("Pushed data from "));
|
||||
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
|
||||
spartnData[spartnCount++] = ch;
|
||||
if (spartnCount == sizeof(spartnData))
|
||||
mqttData[mqttCount++] = ch;
|
||||
|
||||
if (mqttCount == mqttLimit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (spartnCount > 0)
|
||||
if (mqttCount > 0)
|
||||
{
|
||||
//Push KEYS or SPARTN data to GNSS module over I2C
|
||||
myGNSS.pushRawData(spartnData, spartnCount, false);
|
||||
myGNSS.pushRawData(mqttData, mqttCount, false);
|
||||
lastReceived_ms = millis();
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mqttData;
|
||||
}
|
||||
|
||||
//Connect to STARTN MQTT broker, receive RTCM, and push to ZED module over I2C
|
||||
@@ -153,12 +317,14 @@ void beginClient()
|
||||
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)
|
||||
{
|
||||
@@ -168,6 +334,9 @@ void beginClient()
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,19 @@ const char password[] = "<YOUR PASSWORD>";
|
||||
// Below infomation you can set after signing up with u-blox Thingstream portal
|
||||
// and after add a new New PointPerfect Thing
|
||||
// https://portal.thingstream.io/app/location-services/things
|
||||
// in the new PointPerfect Thing you go to the credentials page and copy past the values and certificate into this.
|
||||
// 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/key/ip";
|
||||
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"; // choice of {eu, us}
|
||||
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>";
|
||||
|
||||
+66
-1
@@ -10,7 +10,7 @@
|
||||
|
||||
This is a proof of concept to show how the UBX-RXM-PMP corrections control the accuracy.
|
||||
|
||||
You will need a Thingstream PointPerfect account to be able to access the SPARTN Credentials (IP Dynamic Keys).
|
||||
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?
|
||||
@@ -121,6 +121,67 @@ void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
|
||||
|
||||
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
|
||||
// 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(ubxDataStruct->ebno);
|
||||
|
||||
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);
|
||||
@@ -149,6 +210,8 @@ void setup()
|
||||
|
||||
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."
|
||||
@@ -164,6 +227,8 @@ void setup()
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
// You can set the information below after signing up with the u-blox Thingstream portal
|
||||
// and adding a new New PointPerfect Thing
|
||||
// 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 518418.
|
||||
// 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[] = "f742bd6b7248043177dd649141d8fb0b";
|
||||
const uint16_t currentKeyGPSWeek = 2192;
|
||||
const uint32_t currentKeyGPSToW = 518418;
|
||||
const char currentDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
|
||||
const uint16_t currentKeyGPSWeek = 2192; // Update this when you add new keys
|
||||
const uint32_t currentKeyGPSToW = 518400;
|
||||
|
||||
const uint8_t nextKeyLengthBytes = 16;
|
||||
const char nextDynamicKey[] = "8206........................29f4";
|
||||
const uint16_t nextKeyGPSWeek = 2196;
|
||||
const uint32_t nextKeyGPSToW = 518418;
|
||||
const char nextDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
|
||||
const uint16_t nextKeyGPSWeek = 2196; // Update this when you add new keys
|
||||
const uint32_t nextKeyGPSToW = 518400;
|
||||
|
||||
+459
@@ -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(ubxDataStruct->ebno);
|
||||
|
||||
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.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1); // Ensure UBX-RXM-PMP is enabled on the I2C port
|
||||
if (ok) ok = myLBand.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART1, 1); // Output UBX-RXM-PMP on UART1
|
||||
if (ok) ok = myLBand.setVal(UBLOX_CFG_UART2OUTPROT_UBX, 1); // Enable UBX output on UART2
|
||||
if (ok) ok = myLBand.setVal(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";
|
||||
@@ -40,6 +40,7 @@ UBX_NAV_AOPSTATUS_data_t KEYWORD1
|
||||
|
||||
UBX_RXM_PMP_data_t KEYWORD1
|
||||
UBX_RXM_PMP_message_data_t KEYWORD1
|
||||
UBX_RXM_COR_data_t KEYWORD1
|
||||
UBX_RXM_SFRBX_data_t KEYWORD1
|
||||
UBX_RXM_RAWX_data_t KEYWORD1
|
||||
|
||||
@@ -393,6 +394,8 @@ logAOPSTATUS KEYWORD2
|
||||
setRXMPMPcallbackPtr KEYWORD2
|
||||
setRXMPMPmessageCallbackPtr KEYWORD2
|
||||
|
||||
setRXMCORcallbackPtr KEYWORD2
|
||||
|
||||
getRXMSFRBX KEYWORD2
|
||||
setAutoRXMSFRBX KEYWORD2
|
||||
setAutoRXMSFRBXrate KEYWORD2
|
||||
@@ -772,6 +775,8 @@ UBX_NAV_TIMELS LITERAL1
|
||||
UBX_NAV_VELECEF LITERAL1
|
||||
UBX_NAV_VELNED LITERAL1
|
||||
|
||||
UBX_RXM_PMP LITERAL1
|
||||
UBX_RXM_COR LITERAL1
|
||||
UBX_RXM_RAWX LITERAL1
|
||||
UBX_RXM_SFRBX LITERAL1
|
||||
UBX_RXM_SPARTN LITERAL1
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=SparkFun u-blox GNSS Arduino Library
|
||||
version=2.2.6
|
||||
version=2.2.7
|
||||
author=SparkFun Electronics <techsupport@sparkfun.com>
|
||||
maintainer=SparkFun Electronics <sparkfun.com>
|
||||
sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules<br/><br/>
|
||||
|
||||
@@ -294,6 +294,16 @@ void SFE_UBLOX_GNSS::end(void)
|
||||
packetUBXRXMPMPmessage = NULL; // Redundant?
|
||||
}
|
||||
|
||||
if (packetUBXRXMCOR != NULL)
|
||||
{
|
||||
if (packetUBXRXMCOR->callbackData != NULL)
|
||||
{
|
||||
delete packetUBXRXMCOR->callbackData;
|
||||
}
|
||||
delete packetUBXRXMCOR;
|
||||
packetUBXRXMCOR = NULL; // Redundant?
|
||||
}
|
||||
|
||||
if (packetUBXRXMSFRBX != NULL)
|
||||
{
|
||||
if (packetUBXRXMSFRBX->callbackData != NULL)
|
||||
@@ -1333,6 +1343,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
|
||||
if ((packetUBXRXMPMP != NULL) || (packetUBXRXMPMPmessage != NULL))
|
||||
result = true;
|
||||
break;
|
||||
case UBX_RXM_COR:
|
||||
if (packetUBXRXMCOR != NULL)
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1504,6 +1518,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID)
|
||||
case UBX_RXM_PMP:
|
||||
maxSize = UBX_RXM_PMP_MAX_LEN;
|
||||
break;
|
||||
case UBX_RXM_COR:
|
||||
maxSize = UBX_RXM_COR_LEN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -3772,6 +3789,22 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
|
||||
packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid
|
||||
}
|
||||
}
|
||||
else if (msg->id == UBX_RXM_COR)
|
||||
{
|
||||
// Parse various byte fields into storage - but only if we have memory allocated for it
|
||||
if ((packetUBXRXMCOR != NULL) && (packetUBXRXMCOR->callbackData != NULL)
|
||||
//&& (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old'
|
||||
)
|
||||
{
|
||||
packetUBXRXMCOR->callbackData->version = extractByte(msg, 0);
|
||||
packetUBXRXMCOR->callbackData->ebno = extractByte(msg, 1);
|
||||
packetUBXRXMCOR->callbackData->statusInfo.all = extractLong(msg, 4);
|
||||
packetUBXRXMCOR->callbackData->msgType = extractInt(msg, 8);
|
||||
packetUBXRXMCOR->callbackData->msgSubType = extractInt(msg, 10);
|
||||
|
||||
packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid
|
||||
}
|
||||
}
|
||||
else if (msg->id == UBX_RXM_SFRBX)
|
||||
// Note: length is variable
|
||||
// Note: on protocol version 17: numWords is (0..16)
|
||||
@@ -5359,6 +5392,17 @@ void SFE_UBLOX_GNSS::checkCallbacks(void)
|
||||
packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
|
||||
}
|
||||
|
||||
if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage
|
||||
&& (packetUBXRXMCOR->callbackData != NULL) // If RAM has been allocated for the copy of the data
|
||||
&& (packetUBXRXMCOR->callbackPointerPtr != NULL) // If the pointer to the callback has been defined
|
||||
&& (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
|
||||
{
|
||||
// if (_printDebug == true)
|
||||
// _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM COR"));
|
||||
packetUBXRXMCOR->callbackPointerPtr(packetUBXRXMCOR->callbackData); // Call the callback
|
||||
packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
|
||||
}
|
||||
|
||||
if ((packetUBXRXMSFRBX != NULL) // If RAM has been allocated for message storage
|
||||
&& (packetUBXRXMSFRBX->callbackData != NULL) // If RAM has been allocated for the copy of the data
|
||||
&& (packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
|
||||
@@ -12316,6 +12360,49 @@ bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage()
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool SFE_UBLOX_GNSS::setRXMCORcallbackPtr(void (*callbackPointer)(UBX_RXM_COR_data_t *))
|
||||
{
|
||||
if (packetUBXRXMCOR == NULL)
|
||||
initPacketUBXRXMCOR(); // Check that RAM has been allocated for the data
|
||||
if (packetUBXRXMCOR == NULL) // Only attempt this if RAM allocation was successful
|
||||
return false;
|
||||
|
||||
if (packetUBXRXMCOR->callbackData == NULL) // Check if RAM has been allocated for the callback copy
|
||||
{
|
||||
packetUBXRXMCOR->callbackData = new UBX_RXM_COR_data_t; // Allocate RAM for the main struct
|
||||
}
|
||||
|
||||
if (packetUBXRXMCOR->callbackData == NULL)
|
||||
{
|
||||
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
|
||||
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||
_debugSerial->println(F("setAutoRXMCORcallbackPtr: RAM alloc failed!"));
|
||||
#endif
|
||||
return (false);
|
||||
}
|
||||
|
||||
packetUBXRXMCOR->callbackPointerPtr = callbackPointer;
|
||||
return (true);
|
||||
}
|
||||
|
||||
// PRIVATE: Allocate RAM for packetUBXRXMCOR and initialize it
|
||||
bool SFE_UBLOX_GNSS::initPacketUBXRXMCOR()
|
||||
{
|
||||
packetUBXRXMCOR = new UBX_RXM_COR_t; // Allocate RAM for the main struct
|
||||
if (packetUBXRXMCOR == NULL)
|
||||
{
|
||||
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
|
||||
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||
_debugSerial->println(F("initPacketUBXRXMCOR: RAM alloc failed!"));
|
||||
#endif
|
||||
return (false);
|
||||
}
|
||||
packetUBXRXMCOR->automaticFlags.flags.all = 0;
|
||||
packetUBXRXMCOR->callbackPointerPtr = NULL;
|
||||
packetUBXRXMCOR->callbackData = NULL;
|
||||
return (true);
|
||||
}
|
||||
|
||||
// ***** RXM SFRBX automatic support
|
||||
|
||||
bool SFE_UBLOX_GNSS::getRXMSFRBX(uint16_t maxWait)
|
||||
|
||||
@@ -624,7 +624,7 @@ typedef struct
|
||||
|
||||
const uint32_t SFE_UBLOX_DAYS_FROM_1970_TO_2020 = 18262; // Jan 1st 2020 Epoch = 1577836800 seconds
|
||||
const uint16_t SFE_UBLOX_DAYS_SINCE_2020[80] =
|
||||
{
|
||||
{
|
||||
0, 366, 731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
|
||||
3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
|
||||
7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862, 10227, 10593,
|
||||
@@ -632,10 +632,9 @@ const uint16_t SFE_UBLOX_DAYS_SINCE_2020[80] =
|
||||
14610, 14976, 15341, 15706, 16071, 16437, 16802, 17167, 17532, 17898,
|
||||
18263, 18628, 18993, 19359, 19724, 20089, 20454, 20820, 21185, 21550,
|
||||
21915, 22281, 22646, 23011, 23376, 23742, 24107, 24472, 24837, 25203,
|
||||
25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855
|
||||
};
|
||||
25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855};
|
||||
const uint16_t SFE_UBLOX_DAYS_SINCE_MONTH[2][12] =
|
||||
{
|
||||
{
|
||||
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, // Leap Year (Year % 4 == 0)
|
||||
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} // Normal Year
|
||||
};
|
||||
@@ -860,18 +859,17 @@ public:
|
||||
bool disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait = defaultMaxWait); // Turn off given RTCM message from a given port
|
||||
|
||||
// Functions used for RTK and base station setup
|
||||
// It is probably safe to assume that users of the RTK will be using I2C / Qwiic. So let's leave maxWait set to 250ms.
|
||||
bool getSurveyMode(uint16_t maxWait = 250); // Get the current TimeMode3 settings
|
||||
bool setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Control survey in mode
|
||||
bool setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Control survey in mode
|
||||
bool enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Begin Survey-In for NEO-M8P / ZED-F9x
|
||||
bool enableSurveyModeFull(uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Begin Survey-In for NEO-M8P / ZED-F9x
|
||||
bool disableSurveyMode(uint16_t maxWait = 250); // Stop Survey-In mode
|
||||
bool getSurveyMode(uint16_t maxWait = defaultMaxWait); // Get the current TimeMode3 settings
|
||||
bool setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode
|
||||
bool setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode
|
||||
bool enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Begin Survey-In for NEO-M8P / ZED-F9x
|
||||
bool enableSurveyModeFull(uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Begin Survey-In for NEO-M8P / ZED-F9x
|
||||
bool disableSurveyMode(uint16_t maxWait = defaultMaxWait); // Stop Survey-In mode
|
||||
// Given coordinates, put receiver into static position. Set latlong to true to pass in lat/long values instead of ecef.
|
||||
// For ECEF the units are: cm, 0.1mm, cm, 0.1mm, cm, 0.1mm
|
||||
// For Lat/Lon/Alt the units are: degrees^-7, degrees^-9, degrees^-7, degrees^-9, cm, 0.1mm
|
||||
bool setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong = false, uint16_t maxWait = 250);
|
||||
bool setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latLong = false, uint16_t maxWait = 250);
|
||||
bool setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong = false, uint16_t maxWait = defaultMaxWait);
|
||||
bool setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latLong = false, uint16_t maxWait = defaultMaxWait);
|
||||
bool setDGNSSConfiguration(sfe_ublox_dgnss_mode_e dgnssMode = SFE_UBLOX_DGNSS_MODE_FIXED, uint16_t maxWait = defaultMaxWait); // Set the DGNSS differential mode
|
||||
|
||||
// Read the module's protocol version
|
||||
@@ -954,23 +952,21 @@ public:
|
||||
|
||||
// General configuration (used only on protocol v27 and higher - ie, ZED-F9P)
|
||||
|
||||
// It is probably safe to assume that users of the ZED-F9P will be using I2C / Qwiic.
|
||||
// If they are using Serial then the higher baud rate will also help. So let's leave maxWait set to 250ms.
|
||||
uint32_t createKey(uint16_t group, uint16_t id, uint8_t size); // Form 32-bit key from group/id/size
|
||||
sfe_ublox_status_e getVal(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Load payload with response
|
||||
uint8_t getVal8(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
|
||||
uint16_t getVal16(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
|
||||
uint32_t getVal32(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
|
||||
uint64_t getVal64(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
|
||||
uint8_t getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
|
||||
uint16_t getVal16(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
|
||||
uint32_t getVal32(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
|
||||
uint64_t getVal64(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
|
||||
uint8_t setVal(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 16-bit value at a given group/id/size location
|
||||
uint8_t setVal8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 8-bit value at a given group/id/size location
|
||||
uint8_t setVal16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 16-bit value at a given group/id/size location
|
||||
uint8_t setVal32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 32-bit value at a given group/id/size location
|
||||
uint8_t setVal64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 64-bit value at a given group/id/size location
|
||||
sfe_ublox_status_e getVal(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Load payload with response
|
||||
uint8_t getVal8(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
|
||||
uint16_t getVal16(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
|
||||
uint32_t getVal32(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
|
||||
uint64_t getVal64(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
|
||||
uint8_t getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
|
||||
uint16_t getVal16(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
|
||||
uint32_t getVal32(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
|
||||
uint64_t getVal64(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
|
||||
uint8_t setVal(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 16-bit value at a given group/id/size location
|
||||
uint8_t setVal8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 8-bit value at a given group/id/size location
|
||||
uint8_t setVal16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 16-bit value at a given group/id/size location
|
||||
uint8_t setVal32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 32-bit value at a given group/id/size location
|
||||
uint8_t setVal64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 64-bit value at a given group/id/size location
|
||||
uint8_t newCfgValset8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 8-bit value
|
||||
uint8_t newCfgValset16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 16-bit value
|
||||
uint8_t newCfgValset32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 32-bit value
|
||||
@@ -979,10 +975,10 @@ public:
|
||||
uint8_t addCfgValset16(uint32_t keyID, uint16_t value); // Add a new KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket
|
||||
uint8_t addCfgValset32(uint32_t keyID, uint32_t value); // Add a new KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket
|
||||
uint8_t addCfgValset64(uint32_t keyID, uint64_t value); // Add a new KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket
|
||||
uint8_t sendCfgValset8(uint32_t keyID, uint8_t value, uint16_t maxWait = 250); // Add the final KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = 250); // Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = 250); // Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset64(uint32_t keyID, uint64_t value, uint16_t maxWait = 250); // Add the final KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset8(uint32_t keyID, uint8_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
uint8_t sendCfgValset64(uint32_t keyID, uint64_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
|
||||
|
||||
// get and set functions for all of the "automatic" message processing
|
||||
|
||||
@@ -1159,6 +1155,8 @@ public:
|
||||
bool setRXMPMPcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack!
|
||||
bool setRXMPMPmessageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_message_data_t *)); // Use this if you want all of the PMP message (including sync chars, checksum, etc.) to push to a GNSS
|
||||
|
||||
bool setRXMCORcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_COR_data_t *)); // RXM COR
|
||||
|
||||
bool getRXMSFRBX(uint16_t maxWait = defaultMaxWait); // RXM SFRBX
|
||||
bool setAutoRXMSFRBX(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic RXM SFRBX reports at the navigation frequency
|
||||
bool setAutoRXMSFRBX(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic RXM SFRBX reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
|
||||
@@ -1512,6 +1510,7 @@ public:
|
||||
|
||||
UBX_RXM_PMP_t *packetUBXRXMPMP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||
UBX_RXM_PMP_message_t *packetUBXRXMPMPmessage = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||
UBX_RXM_COR_t *packetUBXRXMCOR = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||
UBX_RXM_SFRBX_t *packetUBXRXMSFRBX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||
UBX_RXM_RAWX_t *packetUBXRXMRAWX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||
|
||||
@@ -1610,6 +1609,7 @@ private:
|
||||
bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it
|
||||
bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it
|
||||
bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it
|
||||
bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it
|
||||
bool initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it
|
||||
bool initPacketUBXRXMRAWX(); // Allocate RAM for packetUBXRXMRAWX and initialize it
|
||||
bool initPacketUBXCFGPRT(); // Allocate RAM for packetUBXCFGPRT and initialize it
|
||||
|
||||
@@ -1484,6 +1484,66 @@ typedef struct
|
||||
UBX_RXM_RAWX_data_t *callbackData;
|
||||
} UBX_RXM_RAWX_t;
|
||||
|
||||
// UBX-RXM-COR (0x02 0x34): Differential correction input status
|
||||
const uint16_t UBX_RXM_COR_LEN = 12;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t version; // Message version (0x01 for this version)
|
||||
uint8_t ebno; // Energy per bit to noise power spectral density ratio (Eb/N0): 2^-3 dB
|
||||
// 0: unknown. Reported only for protocol UBX-RXM-PMP (SPARTN) to monitor signal quality.
|
||||
uint8_t reserved0[2]; // Reserved
|
||||
union
|
||||
{
|
||||
uint32_t all;
|
||||
struct
|
||||
{
|
||||
uint32_t protocol : 5; // Input correction data protocol:
|
||||
// 0: Unknown
|
||||
// 1: RTCM3
|
||||
// 2: SPARTN (Secure Position Augmentation for Real Time Navigation)
|
||||
// 29: UBX-RXM-PMP (SPARTN)
|
||||
// 30: UBX-RXM-QZSSL6
|
||||
uint32_t errStatus : 2; // Error status of the received correction message content based on possibly available error codes or checksums:
|
||||
// 0: Unknown
|
||||
// 1: Error-free
|
||||
// 2: Erroneous
|
||||
uint32_t msgUsed : 2; // Status of receiver using the input message:
|
||||
// 0: Unknown
|
||||
// 1: Not used
|
||||
// 2: Used
|
||||
uint32_t correctionId : 16; // Identifier for the correction stream:
|
||||
// For RTCM 3: Reference station ID (DF003) of the received RTCM input message.
|
||||
// Valid range 0-4095.
|
||||
// For all other messages, reports 0xFFFF.
|
||||
// For other correction protocols 0xFFFF.
|
||||
uint32_t msgTypeValid : 1; // Validity of the msgType field. Set to False e.g. if the protocol does not define msgType.
|
||||
uint32_t msgSubTypeValid : 1; // Validity of the msgSubType field. Set to False e.g. if the protocol does not define subtype for the msgType.
|
||||
uint32_t msgInputHandle : 1; // Input handling support of the input message:
|
||||
// 0: Receiver does not have input handling support for this message
|
||||
// 1: Receiver has input handling support for this message
|
||||
uint32_t msgEncrypted : 2; // Encryption status of the input message:
|
||||
// 0: Unknown
|
||||
// 1: Not encrypted
|
||||
// 2: Encrypted
|
||||
uint32_t msgDecrypted : 2; // Decryption status of the input message:
|
||||
// 0: Unknown
|
||||
// 1: Not decrypted
|
||||
// 2: Successfully decrypted
|
||||
} bits;
|
||||
} statusInfo;
|
||||
uint16_t msgType; // Message type
|
||||
uint16_t msgSubType; // Message subtype
|
||||
} UBX_RXM_COR_data_t;
|
||||
|
||||
// The COR data can only be accessed via a callback. COR cannot be polled.
|
||||
typedef struct
|
||||
{
|
||||
ubxAutomaticFlags automaticFlags;
|
||||
void (*callbackPointerPtr)(UBX_RXM_COR_data_t *);
|
||||
UBX_RXM_COR_data_t *callbackData;
|
||||
} UBX_RXM_COR_t;
|
||||
|
||||
// UBX-RXM-PMP (0x02 0x72): PMP raw data (D9 modules)
|
||||
// There are two versions of this message but, fortunately, both have a max len of 528
|
||||
const uint16_t UBX_RXM_PMP_MAX_USER_DATA = 504;
|
||||
|
||||
Reference in New Issue
Block a user