Add support for automatic NMEA
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
Get the GPGGA NMEA sentence using getLatestNMEAGPGGA
|
||||||
|
By: Paul Clark
|
||||||
|
SparkFun Electronics
|
||||||
|
Date: January 12th, 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 turn on/off the NMEA sentences being output over I2C.
|
||||||
|
It then demonstrates how to use the new getLatestNMEAGPGGA function to retrieve the latest GPGGA message.
|
||||||
|
getLatestNMEAGPGGA returns immediately - it is not blocking.
|
||||||
|
It returns:
|
||||||
|
0 if no data is available
|
||||||
|
1 if the data is valid but is stale (you have read it before)
|
||||||
|
2 if the data is valid and fresh
|
||||||
|
|
||||||
|
This example turns off all sentences except for GPGGA.
|
||||||
|
|
||||||
|
Feel like supporting open source hardware?
|
||||||
|
Buy a board from SparkFun!
|
||||||
|
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
|
||||||
|
NEO-M8P RTK: https://www.sparkfun.com/products/15005
|
||||||
|
SAM-M8Q: https://www.sparkfun.com/products/15106
|
||||||
|
|
||||||
|
Hardware Connections:
|
||||||
|
Plug a Qwiic cable into the GNSS and a RedBoard
|
||||||
|
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
|
||||||
|
Open the serial monitor at 115200 baud to see the output
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Wire.h> //Needed for I2C to GNSS
|
||||||
|
|
||||||
|
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
|
||||||
|
SFE_UBLOX_GNSS myGNSS;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("SparkFun u-blox GNSS Example"));
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
|
||||||
|
|
||||||
|
if (myGNSS.begin() == false)
|
||||||
|
{
|
||||||
|
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Disable or enable various NMEA sentences over the I2C interface
|
||||||
|
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
|
||||||
|
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_I2C); // Several of these are on by default on ublox board so let's disable them
|
||||||
|
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C);
|
||||||
|
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C);
|
||||||
|
myGNSS.disableNMEAMessage(UBX_NMEA_RMC, COM_PORT_I2C);
|
||||||
|
myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_I2C);
|
||||||
|
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); // Leave only GGA enabled at current navigation rate
|
||||||
|
|
||||||
|
//myGNSS.saveConfiguration(); //Optional: Save these settings to NVM
|
||||||
|
|
||||||
|
Serial.println(F("Messages configured"));
|
||||||
|
|
||||||
|
//myGNSS.setNMEAOutputPort(Serial); // Uncomment this line to echo all NMEA data to Serial for debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// getLatestNMEAGPGGA calls checkUblox for us. We don't need to do it here
|
||||||
|
|
||||||
|
NMEA_GGA_data_t data; // Storage for the GPGGA data
|
||||||
|
uint8_t result = myGNSS.getLatestNMEAGPGGA(&data); // Get the latest GPGGA data (if any)
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
Serial.println(F("No GPGGA data available"));
|
||||||
|
}
|
||||||
|
else if (result == 1)
|
||||||
|
{
|
||||||
|
Serial.println(F("GPGGA data is available but is stale"));
|
||||||
|
}
|
||||||
|
else if (result == 2)
|
||||||
|
{
|
||||||
|
// Data contains .length and .nmea
|
||||||
|
Serial.print(F("NMEA: Length: "));
|
||||||
|
Serial.print(data.length);
|
||||||
|
Serial.print(F("\tData: "));
|
||||||
|
Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(F("result == "));
|
||||||
|
Serial.print(result);
|
||||||
|
Serial.println(F(". This should be impossible!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = myGNSS.getLatestNMEAGNGGA(&data); // Get the latest GNGGA data (if any)
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
Serial.println(F("No GNGGA data available"));
|
||||||
|
}
|
||||||
|
else if (result == 1)
|
||||||
|
{
|
||||||
|
Serial.println(F("GNGGA data is available but is stale"));
|
||||||
|
}
|
||||||
|
else if (result == 2)
|
||||||
|
{
|
||||||
|
// Data contains .length and .nmea
|
||||||
|
Serial.print(F("NMEA: Length: "));
|
||||||
|
Serial.print(data.length);
|
||||||
|
Serial.print(F("\tData: "));
|
||||||
|
Serial.println((const char *)data.nmea); // .nmea is printable (NULL-terminated)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(F("result == "));
|
||||||
|
Serial.print(result);
|
||||||
|
Serial.println(F(". This should be impossible!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
@@ -406,6 +406,26 @@ void SFE_UBLOX_GNSS::end(void)
|
|||||||
packetUBXHNRPVT = NULL; // Redundant?
|
packetUBXHNRPVT = NULL; // Redundant?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (storageNMEAGPGGA != NULL)
|
||||||
|
{
|
||||||
|
if (storageNMEAGPGGA->callbackCopy != NULL)
|
||||||
|
{
|
||||||
|
delete storageNMEAGPGGA->callbackCopy;
|
||||||
|
}
|
||||||
|
delete storageNMEAGPGGA;
|
||||||
|
storageNMEAGPGGA = NULL; // Redundant?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageNMEAGNGGA != NULL)
|
||||||
|
{
|
||||||
|
if (storageNMEAGNGGA->callbackCopy != NULL)
|
||||||
|
{
|
||||||
|
delete storageNMEAGNGGA->callbackCopy;
|
||||||
|
}
|
||||||
|
delete storageNMEAGNGGA;
|
||||||
|
storageNMEAGNGGA = NULL; // Redundant?
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allow the user to change packetCfgPayloadSize. Handy if you want to process big messages like RAWX
|
//Allow the user to change packetCfgPayloadSize. Handy if you want to process big messages like RAWX
|
||||||
@@ -1682,6 +1702,15 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
|
|||||||
_signsOfLife = isNMEAHeaderValid();
|
_signsOfLife = isNMEAHeaderValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we have automatic storage for this message
|
||||||
|
if (isThisNMEAauto())
|
||||||
|
{
|
||||||
|
uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
|
||||||
|
uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
|
||||||
|
*lengthPtr = 6; // Set the working copy length
|
||||||
|
memcpy(nmeaPtr, &nmeaAddressField[0], 6); // Copy the start character and address field into the working copy
|
||||||
|
}
|
||||||
|
|
||||||
// We've just received the end of the address field. Check if it is selected for logging
|
// We've just received the end of the address field. Check if it is selected for logging
|
||||||
if (logThisNMEA())
|
if (logThisNMEA())
|
||||||
{
|
{
|
||||||
@@ -1701,6 +1730,24 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
|
|||||||
|
|
||||||
if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer and/or pass it to processNMEA?
|
if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer and/or pass it to processNMEA?
|
||||||
{
|
{
|
||||||
|
if (isThisNMEAauto())
|
||||||
|
{
|
||||||
|
uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
|
||||||
|
uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
|
||||||
|
uint8_t nmeaMaxLength = getNMEAMaxLength();
|
||||||
|
if (*lengthPtr < nmeaMaxLength)
|
||||||
|
{
|
||||||
|
*(nmeaPtr + *lengthPtr) = incoming; // Store the character
|
||||||
|
*lengthPtr = *lengthPtr + 1; // Increment the length
|
||||||
|
if (*lengthPtr == nmeaMaxLength)
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
{
|
||||||
|
_debugSerial->println(F("process: NMEA buffer is full!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (logThisNMEA())
|
if (logThisNMEA())
|
||||||
storeFileBytes(&incoming, 1); // Add incoming to the file buffer
|
storeFileBytes(&incoming, 1); // Add incoming to the file buffer
|
||||||
if (processThisNMEA())
|
if (processThisNMEA())
|
||||||
@@ -1716,8 +1763,86 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
|
|||||||
currentSentence = NONE; //Something went wrong. Reset.
|
currentSentence = NONE; //Something went wrong. Reset.
|
||||||
|
|
||||||
if (nmeaByteCounter == 0) // Check if we are done
|
if (nmeaByteCounter == 0) // Check if we are done
|
||||||
|
{
|
||||||
|
if (isThisNMEAauto())
|
||||||
|
{
|
||||||
|
uint8_t *workingLengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
|
||||||
|
uint8_t *workingNMEAPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
|
||||||
|
uint8_t nmeaMaxLength = getNMEAMaxLength();
|
||||||
|
// Check the checksum: the checksum is the exclusive-OR of all characters between the $ and the *
|
||||||
|
uint8_t nmeaChecksum = 0;
|
||||||
|
uint8_t charsChecked = 1; // Start after the $
|
||||||
|
uint8_t thisChar = '\0';
|
||||||
|
while ((charsChecked < (nmeaMaxLength - 1))
|
||||||
|
&& (charsChecked < ((*workingLengthPtr) - 4))
|
||||||
|
&& (thisChar != '*'))
|
||||||
|
{
|
||||||
|
thisChar = *(workingNMEAPtr + charsChecked); // Get a char from the working copy
|
||||||
|
if (thisChar != '*') // Ex-or the char into the checksum - but not if it is the '*'
|
||||||
|
nmeaChecksum ^= thisChar;
|
||||||
|
charsChecked++; // Increment the counter
|
||||||
|
}
|
||||||
|
if (thisChar == '*') // Make sure we found the *
|
||||||
|
{
|
||||||
|
uint8_t expectedChecksum1 = (nmeaChecksum >> 4) + '0';
|
||||||
|
if (expectedChecksum1 >= ':') // Handle Hex correctly
|
||||||
|
expectedChecksum1 += 'A' - ':';
|
||||||
|
uint8_t expectedChecksum2 = (nmeaChecksum & 0x0F) + '0';
|
||||||
|
if (expectedChecksum2 >= ':') // Handle Hex correctly
|
||||||
|
expectedChecksum2 += 'A' - ':';
|
||||||
|
if ((expectedChecksum1 == *(workingNMEAPtr + charsChecked))
|
||||||
|
&& (expectedChecksum2 == *(workingNMEAPtr + charsChecked + 1)))
|
||||||
|
{
|
||||||
|
uint8_t *completeLengthPtr = getNMEACompleteLengthPtr(); // Get a pointer to the complete copy length
|
||||||
|
uint8_t *completeNMEAPtr = getNMEACompleteNMEAPtr(); // Get a pointer to the complete copy NMEA data
|
||||||
|
memset(completeNMEAPtr, 0, nmeaMaxLength); // Clear the previous complete copy
|
||||||
|
memcpy(completeNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the complete copy
|
||||||
|
*completeLengthPtr = *workingLengthPtr; // Update the length
|
||||||
|
nmeaAutomaticFlags *flagsPtr = getNMEAFlagsPtr(); // Get a pointer to the flags
|
||||||
|
nmeaAutomaticFlags flagsCopy = *flagsPtr;
|
||||||
|
flagsCopy.flags.bits.completeCopyValid = 1; // Set the complete copy valid flag
|
||||||
|
flagsCopy.flags.bits.completeCopyRead = 0; // Clear the complete copy read/stale flag
|
||||||
|
*flagsPtr = flagsCopy; // Update the flags
|
||||||
|
// Callback
|
||||||
|
if (doesThisNMEAHaveCallback()) // Do we need to copy the data into the callback copy?
|
||||||
|
{
|
||||||
|
if (flagsCopy.flags.bits.callbackCopyValid == 0) // Has the callback copy valid flag been cleared (by checkCallbacks)
|
||||||
|
{
|
||||||
|
uint8_t *callbackLengthPtr = getNMEACallbackLengthPtr(); // Get a pointer to the callback copy length
|
||||||
|
uint8_t *callbackNMEAPtr = getNMEACallbackNMEAPtr(); // Get a pointer to the callback copy NMEA data
|
||||||
|
memset(callbackNMEAPtr, 0, nmeaMaxLength); // Clear the previous callback copy
|
||||||
|
memcpy(callbackNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the callback copy
|
||||||
|
*callbackLengthPtr = *workingLengthPtr; // Update the length
|
||||||
|
flagsCopy.flags.bits.callbackCopyValid = 1; // Set the callback copy valid flag
|
||||||
|
*flagsPtr = flagsCopy; // Update the flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
{
|
||||||
|
_debugSerial->print(F("process: NMEA checksum fail (2)! Expected "));
|
||||||
|
_debugSerial->write(expectedChecksum1);
|
||||||
|
_debugSerial->write(expectedChecksum2);
|
||||||
|
_debugSerial->print(F(" Got "));
|
||||||
|
_debugSerial->write(*(workingNMEAPtr + charsChecked));
|
||||||
|
_debugSerial->write(*(workingNMEAPtr + charsChecked + 1));
|
||||||
|
_debugSerial->println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
{
|
||||||
|
_debugSerial->println(F("process: NMEA checksum fail (1)!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
currentSentence = NONE; // All done!
|
currentSentence = NONE; // All done!
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (currentSentence == RTCM)
|
else if (currentSentence == RTCM)
|
||||||
{
|
{
|
||||||
processRTCMframe(incoming); //Deal with RTCM bytes
|
processRTCMframe(incoming); //Deal with RTCM bytes
|
||||||
@@ -1828,6 +1953,194 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming)
|
|||||||
_nmeaOutputPort->write(incoming); //Echo this byte to the serial port
|
_nmeaOutputPort->write(incoming); //Echo this byte to the serial port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the NMEA message (in nmeaAddressField) is "auto" (i.e. has RAM allocated for it)
|
||||||
|
bool SFE_UBLOX_GNSS::isThisNMEAauto()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
if (storageNMEAGPGGA != NULL)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
if (storageNMEAGNGGA != NULL)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to copy the data into the callback copy?
|
||||||
|
bool SFE_UBLOX_GNSS::doesThisNMEAHaveCallback()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
if (storageNMEAGPGGA != NULL)
|
||||||
|
if (storageNMEAGPGGA->callbackCopy != NULL)
|
||||||
|
if (storageNMEAGPGGA->callbackPointer != NULL)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
if (storageNMEAGNGGA != NULL)
|
||||||
|
if (storageNMEAGNGGA->callbackCopy != NULL)
|
||||||
|
if (storageNMEAGNGGA->callbackPointer != NULL)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the working copy length
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEAWorkingLengthPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->workingCopy.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->workingCopy.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the working copy NMEA data
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEAWorkingNMEAPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->workingCopy.nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->workingCopy.nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the complete copy length
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEACompleteLengthPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->completeCopy.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->completeCopy.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the complete copy NMEA data
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEACompleteNMEAPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->completeCopy.nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->completeCopy.nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the callback copy length
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEACallbackLengthPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->callbackCopy->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->callbackCopy->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the callback copy NMEA data
|
||||||
|
uint8_t * SFE_UBLOX_GNSS::getNMEACallbackNMEAPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->callbackCopy->nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->callbackCopy->nmea[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the maximum length of this NMEA message
|
||||||
|
uint8_t SFE_UBLOX_GNSS::getNMEAMaxLength()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return NMEA_GGA_MAX_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return NMEA_GGA_MAX_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to the automatic NMEA flags
|
||||||
|
nmeaAutomaticFlags * SFE_UBLOX_GNSS::getNMEAFlagsPtr()
|
||||||
|
{
|
||||||
|
char thisNMEA[] = "GPGGA";
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGPGGA->automaticFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(thisNMEA, "GNGGA");
|
||||||
|
if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0)
|
||||||
|
{
|
||||||
|
return &storageNMEAGNGGA->automaticFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
//We need to be able to identify an RTCM packet and then the length
|
//We need to be able to identify an RTCM packet and then the length
|
||||||
//so that we know when the RTCM message is completely received and we then start
|
//so that we know when the RTCM message is completely received and we then start
|
||||||
//listening for other sentences (like NMEA or UBX)
|
//listening for other sentences (like NMEA or UBX)
|
||||||
@@ -4309,6 +4622,17 @@ void SFE_UBLOX_GNSS::checkCallbacks(void)
|
|||||||
packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
|
packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((storageNMEAGPGGA != NULL) // If RAM has been allocated for message storage
|
||||||
|
&& (storageNMEAGPGGA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data
|
||||||
|
&& (storageNMEAGPGGA->callbackPointer != NULL) // If the pointer to the callback has been defined
|
||||||
|
&& (storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
|
||||||
|
{
|
||||||
|
// if (_printDebug == true)
|
||||||
|
// _debugSerial->println(F("checkCallbacks: calling callback for GPGGA"));
|
||||||
|
storageNMEAGPGGA->callbackPointer(*storageNMEAGPGGA->callbackCopy); // Call the callback
|
||||||
|
storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale
|
||||||
|
}
|
||||||
|
|
||||||
checkCallbacksReentrant = false;
|
checkCallbacksReentrant = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11716,6 +12040,153 @@ uint32_t SFE_UBLOX_GNSS::getProcessNMEAMask()
|
|||||||
return (_processNMEA.all);
|
return (_processNMEA.all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initiate automatic storage of NMEA GPGGA messages
|
||||||
|
|
||||||
|
// Get the most recent GPGGA message
|
||||||
|
// Return 0 if the message has not been received from the module
|
||||||
|
// Return 1 if the data is valid but has been read before
|
||||||
|
// Return 2 if the data is valid and is fresh/unread
|
||||||
|
uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPGGA(NMEA_GGA_data_t *data)
|
||||||
|
{
|
||||||
|
if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); //Check that RAM has been allocated for the message
|
||||||
|
if (storageNMEAGPGGA == NULL) //Bail if the RAM allocation failed
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID.
|
||||||
|
|
||||||
|
memcpy(data, &storageNMEAGPGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy
|
||||||
|
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid?
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read?
|
||||||
|
{
|
||||||
|
result = 2;
|
||||||
|
storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Enable a callback on the arrival of a GPGGA message
|
||||||
|
bool SFE_UBLOX_GNSS::setNMEAGPGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t))
|
||||||
|
{
|
||||||
|
if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); //Check that RAM has been allocated for the message
|
||||||
|
if (storageNMEAGPGGA == NULL) //Bail if the RAM allocation failed
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
if (storageNMEAGPGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy
|
||||||
|
{
|
||||||
|
storageNMEAGPGGA->callbackCopy = new NMEA_GGA_data_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageNMEAGPGGA->callbackCopy == NULL)
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
_debugSerial->println(F("setNMEAGPGGAcallback: RAM alloc failed!"));
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
storageNMEAGPGGA->callbackPointer = callbackPointer;
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: allocate RAM for incoming NMEA GPGGA messages and initialize it
|
||||||
|
bool SFE_UBLOX_GNSS::initStorageNMEAGPGGA()
|
||||||
|
{
|
||||||
|
storageNMEAGPGGA = new NMEA_GPGGA_t; //Allocate RAM for the main struct
|
||||||
|
if (storageNMEAGPGGA == NULL)
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
_debugSerial->println(F("initStorageNMEAGPGGA: RAM alloc failed!"));
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
storageNMEAGPGGA->workingCopy.length = 0; // Clear the data length
|
||||||
|
memset(storageNMEAGPGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
|
||||||
|
storageNMEAGPGGA->completeCopy.length = 0; // Clear the data length
|
||||||
|
memset(storageNMEAGPGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
|
||||||
|
|
||||||
|
storageNMEAGPGGA->callbackPointer = NULL; // Clear the callback pointers
|
||||||
|
storageNMEAGPGGA->callbackCopy = NULL;
|
||||||
|
|
||||||
|
storageNMEAGPGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNGGA(NMEA_GGA_data_t *data)
|
||||||
|
{
|
||||||
|
if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); //Check that RAM has been allocated for the message
|
||||||
|
if (storageNMEAGNGGA == NULL) //Bail if the RAM allocation failed
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID.
|
||||||
|
|
||||||
|
memcpy(data, &storageNMEAGNGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy
|
||||||
|
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid?
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read?
|
||||||
|
{
|
||||||
|
result = 2;
|
||||||
|
storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SFE_UBLOX_GNSS::setNMEAGNGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t))
|
||||||
|
{
|
||||||
|
if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); //Check that RAM has been allocated for the message
|
||||||
|
if (storageNMEAGNGGA == NULL) //Bail if the RAM allocation failed
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
if (storageNMEAGNGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy
|
||||||
|
{
|
||||||
|
storageNMEAGNGGA->callbackCopy = new NMEA_GGA_data_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageNMEAGNGGA->callbackCopy == NULL)
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
_debugSerial->println(F("setNMEAGNGGAcallback: RAM alloc failed!"));
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
storageNMEAGNGGA->callbackPointer = callbackPointer;
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: allocate RAM for incoming NMEA GNGGA messages and initialize it
|
||||||
|
bool SFE_UBLOX_GNSS::initStorageNMEAGNGGA()
|
||||||
|
{
|
||||||
|
storageNMEAGNGGA = new NMEA_GNGGA_t; //Allocate RAM for the main struct
|
||||||
|
if (storageNMEAGNGGA == NULL)
|
||||||
|
{
|
||||||
|
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
|
||||||
|
_debugSerial->println(F("initStorageNMEAGNGGA: RAM alloc failed!"));
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
storageNMEAGNGGA->workingCopy.length = 0; // Clear the data length
|
||||||
|
memset(storageNMEAGNGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
|
||||||
|
storageNMEAGNGGA->completeCopy.length = 0; // Clear the data length
|
||||||
|
memset(storageNMEAGNGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage
|
||||||
|
|
||||||
|
storageNMEAGNGGA->callbackPointer = NULL; // Clear the callback pointers
|
||||||
|
storageNMEAGNGGA->callbackCopy = NULL;
|
||||||
|
|
||||||
|
storageNMEAGNGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
// ***** CFG RATE Helper Functions
|
// ***** CFG RATE Helper Functions
|
||||||
|
|
||||||
//Set the rate at which the module will give us an updated navigation solution
|
//Set the rate at which the module will give us an updated navigation solution
|
||||||
|
|||||||
@@ -1323,6 +1323,13 @@ public:
|
|||||||
float getHNRpitch(uint16_t maxWait = defaultMaxWait); // Returned as degrees
|
float getHNRpitch(uint16_t maxWait = defaultMaxWait); // Returned as degrees
|
||||||
float getHNRheading(uint16_t maxWait = defaultMaxWait); // Returned as degrees
|
float getHNRheading(uint16_t maxWait = defaultMaxWait); // Returned as degrees
|
||||||
|
|
||||||
|
// Support for "auto" storage of NMEA messages
|
||||||
|
|
||||||
|
uint8_t getLatestNMEAGPGGA(NMEA_GGA_data_t *data); // Return the most recent GPGGA: 0 = no data, 1 = stale data, 2 = fresh data
|
||||||
|
bool setNMEAGPGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)); //Enable a callback on the arrival of a GPGGA message
|
||||||
|
uint8_t getLatestNMEAGNGGA(NMEA_GGA_data_t *data); // Return the most recent GNGGA: 0 = no data, 1 = stale data, 2 = fresh data
|
||||||
|
bool setNMEAGNGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)); //Enable a callback on the arrival of a GPGGA message
|
||||||
|
|
||||||
// Functions to extract signed and unsigned 8/16/32-bit data from a ubxPacket
|
// Functions to extract signed and unsigned 8/16/32-bit data from a ubxPacket
|
||||||
// From v2.0: These are public. The user can call these to extract data from custom packets
|
// From v2.0: These are public. The user can call these to extract data from custom packets
|
||||||
uint32_t extractLong(ubxPacket *msg, uint8_t spotToStart); //Combine four bytes from payload into long
|
uint32_t extractLong(ubxPacket *msg, uint8_t spotToStart); //Combine four bytes from payload into long
|
||||||
@@ -1373,6 +1380,9 @@ public:
|
|||||||
UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||||
UBX_MGA_DBD_t *packetUBXMGADBD = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
UBX_MGA_DBD_t *packetUBXMGADBD = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||||
|
|
||||||
|
NMEA_GPGGA_t *storageNMEAGPGGA = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||||
|
NMEA_GNGGA_t *storageNMEAGNGGA = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
|
||||||
|
|
||||||
uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame
|
uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1452,6 +1462,9 @@ private:
|
|||||||
bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it
|
bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it
|
||||||
bool initPacketUBXMGADBD(); // Allocate RAM for packetUBXMGADBD and initialize it
|
bool initPacketUBXMGADBD(); // Allocate RAM for packetUBXMGADBD and initialize it
|
||||||
|
|
||||||
|
bool initStorageNMEAGPGGA(); // Allocate RAM for incoming NMEA GPGGA messages and initialize it
|
||||||
|
bool initStorageNMEAGNGGA(); // Allocate RAM for incoming NMEA GNGGA messages and initialize it
|
||||||
|
|
||||||
//Variables
|
//Variables
|
||||||
TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
|
TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
|
||||||
Stream *_serialPort; //The generic connection to user's chosen Serial hardware
|
Stream *_serialPort; //The generic connection to user's chosen Serial hardware
|
||||||
@@ -1526,6 +1539,17 @@ private:
|
|||||||
bool processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA
|
bool processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA
|
||||||
bool isNMEAHeaderValid(); // Return true if the six byte NMEA header appears valid. Used to set _signsOfLife
|
bool isNMEAHeaderValid(); // Return true if the six byte NMEA header appears valid. Used to set _signsOfLife
|
||||||
|
|
||||||
|
bool isThisNMEAauto(); // Check if the NMEA message (in nmeaAddressField) is "auto" (i.e. has RAM allocated for it)
|
||||||
|
bool doesThisNMEAHaveCallback(); // Do we need to copy the data into the callback copy?
|
||||||
|
uint8_t *getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length
|
||||||
|
uint8_t *getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data
|
||||||
|
uint8_t *getNMEACompleteLengthPtr(); // Get a pointer to the complete copy length
|
||||||
|
uint8_t *getNMEACompleteNMEAPtr(); // Get a pointer to the complete copy NMEA data
|
||||||
|
uint8_t *getNMEACallbackLengthPtr(); // Get a pointer to the callback copy length
|
||||||
|
uint8_t *getNMEACallbackNMEAPtr(); // Get a pointer to the callback copy NMEA data
|
||||||
|
uint8_t getNMEAMaxLength(); // Get the maximum length of this NMEA message
|
||||||
|
nmeaAutomaticFlags *getNMEAFlagsPtr(); // Get a pointer to the flags
|
||||||
|
|
||||||
uint16_t rtcmLen = 0;
|
uint16_t rtcmLen = 0;
|
||||||
|
|
||||||
// Flag to prevent reentry into checkCallbacks
|
// Flag to prevent reentry into checkCallbacks
|
||||||
|
|||||||
@@ -2232,4 +2232,53 @@ typedef struct
|
|||||||
UBX_HNR_INS_data_t *callbackData;
|
UBX_HNR_INS_data_t *callbackData;
|
||||||
} UBX_HNR_INS_t;
|
} UBX_HNR_INS_t;
|
||||||
|
|
||||||
|
// NMEA-specific structs
|
||||||
|
|
||||||
|
//Additional flags and pointers that need to be stored with each message type
|
||||||
|
struct nmeaAutomaticFlags
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t all;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t completeCopyValid : 1; // Is the copy of the data struct used by the get function valid/fresh? 0 = invalid/stale, 1 = valid/fresh
|
||||||
|
uint8_t completeCopyRead : 1; // Has the complete copy been read? 0 = unread, 1 = read
|
||||||
|
uint8_t callbackCopyValid : 1; // Is the copy of the data struct used by the callback valid/fresh? 0 = invalid/stale, 1 = valid/fresh
|
||||||
|
} bits;
|
||||||
|
} flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The max length for NMEA messages should be 82 bytes, but GGA messages can exceed that if they include the
|
||||||
|
// extra decimal places for "High Precision".
|
||||||
|
//
|
||||||
|
// To be safe, let's allocate 90 bytes to store the message
|
||||||
|
|
||||||
|
const uint8_t NMEA_GGA_MAX_LENGTH = 90;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t length; // The number of bytes in nmea
|
||||||
|
uint8_t nmea[NMEA_GGA_MAX_LENGTH];
|
||||||
|
} NMEA_GGA_data_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
nmeaAutomaticFlags automaticFlags;
|
||||||
|
NMEA_GGA_data_t workingCopy; // Incoming data is added to the working copy
|
||||||
|
NMEA_GGA_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid
|
||||||
|
void (*callbackPointer)(NMEA_GGA_data_t);
|
||||||
|
NMEA_GGA_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy
|
||||||
|
} NMEA_GPGGA_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
nmeaAutomaticFlags automaticFlags;
|
||||||
|
NMEA_GGA_data_t workingCopy; // Incoming data is added to the working copy
|
||||||
|
NMEA_GGA_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid
|
||||||
|
void (*callbackPointer)(NMEA_GGA_data_t);
|
||||||
|
NMEA_GGA_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy
|
||||||
|
} NMEA_GNGGA_t;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user