Add support for automatic NMEA

This commit is contained in:
PaulZC
2022-01-12 14:02:46 +00:00
parent bdfb274549
commit 185c6c200a
4 changed files with 669 additions and 0 deletions
@@ -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
+49
View File
@@ -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