diff --git a/examples/Data_Logging/DataLoggingExample10_processLoggedUBX/DataLoggingExample10_processLoggedUBX.ino b/examples/Data_Logging/DataLoggingExample10_processLoggedUBX/DataLoggingExample10_processLoggedUBX.ino new file mode 100644 index 0000000..e1ff28a --- /dev/null +++ b/examples/Data_Logging/DataLoggingExample10_processLoggedUBX/DataLoggingExample10_processLoggedUBX.ino @@ -0,0 +1,116 @@ +/* + Demonstrate how to 'process' any UBX message - without needing the Auto methods + By: Paul Clark + SparkFun Electronics + Date: July 14th, 2025 + License: MIT. See license file for more information. + + This example shows how to configure the u-blox GNSS to output (e.g.) UBX_MON_SYS + and process those messages within your own code. This is cool because UBX_MON_SYS + does not have full "Auto" support. There are no setAutoMONSYS or setAutoMONSYScallbackPtr methods. + But we can process it anyway...! + + ** Please note: processLoggedUBX is called by the library's internal processUBXpacket method. ** + ** The library will stall while processLoggedUBX is executing. ** + ** For best results, avoid Serial prints and delays etc. within processLoggedUBX. ** + + Hardware Connections: + Plug a Qwiic cable into the GPS and a Redboard Qwiic + If you don't have a platform with a Qwiic connection use the + SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + + To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on + the u-blox module breakout. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + SparkFun GPS-RTK2 - ZED-F9P (GPS-15136) https://www.sparkfun.com/products/15136 + SparkFun GPS-RTK-SMA - ZED-F9P (GPS-16481) https://www.sparkfun.com/products/16481 + SparkFun MAX-M10S Breakout (GPS-18037) https://www.sparkfun.com/products/18037 + SparkFun ZED-F9K Breakout (GPS-18719) https://www.sparkfun.com/products/18719 + SparkFun ZED-F9R Breakout (GPS-16344) https://www.sparkfun.com/products/16344 + +*/ + +#include //Needed for I2C to GNSS + +#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS_v3 + +// Define a custom class so we can override processLoggedUBX +class my_SFE_UBLOX_GNSS : public SFE_UBLOX_GNSS +{ + public: + void processLoggedUBX(ubxPacket *incomingUBX) + { + // processLoggedUBX will stall the library while it is executing + // For best results, quickly copy the data elsewhere. Avoid Serial prints, delays etc.. + // incomingUBX->len contains the payload length (uint16_t) + // incomingUBX->payload points to the payload (uint8_t *) + // The ubxPacket struct is defined in u-blox_external_typedefs.h + + Serial.printf("UBX message received: Class 0x%02x ID 0x%02x", incomingUBX->cls, incomingUBX->id); + if ((incomingUBX->cls == UBX_CLASS_MON) && (incomingUBX->id == UBX_MON_SYS)) + { + // We can use the "extract" helper functions to read the data - useful for 16, 32 and 64-bit values + // The full list is: + // uint64_t extractLongLong(ubxPacket *msg, uint16_t spotToStart); // Combine eight bytes from payload into uint64_t + // uint64_t extractSignedLongLong(ubxPacket *msg, uint16_t spotToStart); // Combine eight bytes from payload into int64_t + // uint32_t extractLong(ubxPacket *msg, uint16_t spotToStart); // Combine four bytes from payload into long + // int32_t extractSignedLong(ubxPacket *msg, uint16_t spotToStart); // Combine four bytes from payload into signed long (avoiding any ambiguity caused by casting) + // uint16_t extractInt(ubxPacket *msg, uint16_t spotToStart); // Combine two bytes from payload into int + // int16_t extractSignedInt(ubxPacket *msg, uint16_t spotToStart); + // uint8_t extractByte(ubxPacket *msg, uint16_t spotToStart); // Get byte from payload + // int8_t extractSignedChar(ubxPacket *msg, uint16_t spotToStart); // Get signed 8-bit value from payload + // float extractFloat(ubxPacket *msg, uint16_t spotToStart); // Get 32-bit float from payload + // double extractDouble(ubxPacket *msg, uint16_t spotToStart); // Get 64-bit double from payload + + uint8_t cpuLoad = extractByte(incomingUBX, 2); + uint32_t runTime = extractLong(incomingUBX, 8); + int8_t tempValue = extractSignedChar(incomingUBX, 18); + + Serial.printf(" : UBX-MON-SYS : cpuLoad %d%% runTime %ds tempValue %dC", (int)cpuLoad, (int)runTime, (int)tempValue); + } + Serial.println(); + } +}; +my_SFE_UBLOX_GNSS myGNSS; + +void setup() +{ + delay(1000); + + Serial.begin(115200); + Serial.println("SparkFun u-blox Example"); + + Wire.begin(); // Start I2C communication + + //myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial + //myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial + + while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring...")); + } + + // Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate + // This will (re)enable the standard NMEA messages too + // This will also disable any "auto" UBX messages that were enabled and saved by other examples and reduce the load on the I2C bus + //myGNSS.factoryDefault(); delay(5000); + + myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //Set the I2C port to output UBX, NMEA and RTCM messages + + //myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR + + myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second + + myGNSS.newCfgValset(VAL_LAYER_RAM_BBR); // Use cfgValset to disable / enable individual messages + myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_UBX_MON_SYS_I2C, 1); // Enable the UBX-MON-SYS message on I2C + myGNSS.sendCfgValset(); // Send the configuration VALSET + + myGNSS.enableUBXlogging(UBX_CLASS_MON, UBX_MON_SYS, false, true); // Disable logging of MON-SYS. Enable processing +} + +void loop() +{ + myGNSS.checkUblox(); // Check for the arrival of new data. MON-SYS data will be processed by processLoggedUBX +} diff --git a/library.properties b/library.properties index 19f5f3b..9cfebcd 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS v3 -version=3.1.10 +version=3.1.11 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules

diff --git a/src/u-blox_GNSS.cpp b/src/u-blox_GNSS.cpp index e2225d1..c8915b7 100644 --- a/src/u-blox_GNSS.cpp +++ b/src/u-blox_GNSS.cpp @@ -1702,7 +1702,7 @@ void DevUBLOXGNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t req if (packetBuf.cls != UBX_CLASS_ACK) { bool logBecauseAuto = autoLookup(packetBuf.cls, packetBuf.id, &maxPayload); - bool logBecauseEnabled = logThisUBX(packetBuf.cls, packetBuf.id); + bool logBecauseEnabled = logThisUBX(packetBuf.cls, packetBuf.id) || processThisUBX(packetBuf.cls, packetBuf.id); // This is not an ACK so check for a class and ID match if ((packetBuf.cls == storedClass) && (packetBuf.id == storedID)) @@ -3260,7 +3260,7 @@ void DevUBLOXGNSS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t //{ bool logBecauseAuto = autoLookup(incomingUBX->cls, incomingUBX->id, &maximum_payload_size); - bool logBecauseEnabled = logThisUBX(incomingUBX->cls, incomingUBX->id); + bool logBecauseEnabled = logThisUBX(incomingUBX->cls, incomingUBX->id) || processThisUBX(incomingUBX->cls, incomingUBX->id); if ((!logBecauseAuto) && (logBecauseEnabled)) maximum_payload_size = SFE_UBX_MAX_LENGTH; if (maximum_payload_size == 0) @@ -3351,7 +3351,7 @@ void DevUBLOXGNSS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t // This is not an ACK and we do not have a complete class and ID match // So let's check for an "automatic" message arriving - else if ((autoLookup(incomingUBX->cls, incomingUBX->id)) || (logThisUBX(incomingUBX->cls, incomingUBX->id))) + else if ((autoLookup(incomingUBX->cls, incomingUBX->id)) || (logThisUBX(incomingUBX->cls, incomingUBX->id)) || (processThisUBX(incomingUBX->cls, incomingUBX->id))) { // This isn't the message we are looking for... // Let's say so and leave incomingUBX->classAndIDmatch _unchanged_ @@ -5117,10 +5117,14 @@ void DevUBLOXGNSS::processUBXpacket(ubxPacket *msg) // Check if this UBX message should be added to the file buffer - if it has not been added already if ((!addedToFileBuffer) && (logThisUBX(msg->cls, msg->id))) storePacket(msg); + + // Check if UBX message should be processed + if (processThisUBX(msg->cls, msg->id)) + processLoggedUBX(msg); } // UBX Logging - without needing to have or use "Auto" methods -void DevUBLOXGNSS::enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool enable) +void DevUBLOXGNSS::enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool logMe, bool processMe) { // If the list is empty if (sfe_ublox_ubx_logging_list_head == nullptr) @@ -5129,7 +5133,8 @@ void DevUBLOXGNSS::enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool enab sfe_ublox_ubx_logging_list_head = new sfe_ublox_ubx_logging_list_t; sfe_ublox_ubx_logging_list_head->UBX_CLASS = UBX_CLASS; sfe_ublox_ubx_logging_list_head->UBX_ID = UBX_ID; - sfe_ublox_ubx_logging_list_head->enable = enable; + sfe_ublox_ubx_logging_list_head->logMe = logMe; + sfe_ublox_ubx_logging_list_head->processMe = processMe; sfe_ublox_ubx_logging_list_head->next = nullptr; return; } @@ -5144,7 +5149,8 @@ void DevUBLOXGNSS::enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool enab if ((sfe_ublox_ubx_logging_list_ptr->UBX_CLASS == UBX_CLASS) // Check for a match && (sfe_ublox_ubx_logging_list_ptr->UBX_ID == UBX_ID)) { - sfe_ublox_ubx_logging_list_ptr->enable = enable; // Update enable + sfe_ublox_ubx_logging_list_ptr->logMe = logMe; // Update logMe + sfe_ublox_ubx_logging_list_ptr->logMe = processMe; // Update processMe return; } @@ -5159,12 +5165,21 @@ void DevUBLOXGNSS::enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool enab sfe_ublox_ubx_logging_list_ptr = sfe_ublox_ubx_logging_list_ptr->next; sfe_ublox_ubx_logging_list_ptr->UBX_CLASS = UBX_CLASS; sfe_ublox_ubx_logging_list_ptr->UBX_ID = UBX_ID; - sfe_ublox_ubx_logging_list_ptr->enable = enable; + sfe_ublox_ubx_logging_list_ptr->logMe = logMe; + sfe_ublox_ubx_logging_list_ptr->processMe = processMe; sfe_ublox_ubx_logging_list_ptr->next = nullptr; } // PRIVATE: Returns true if this UBX should be added to the logging buffer bool DevUBLOXGNSS::logThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID) +{ + return logOrProcessThisUBX(UBX_CLASS, UBX_ID, true); +} +bool DevUBLOXGNSS::processThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID) +{ + return logOrProcessThisUBX(UBX_CLASS, UBX_ID, false); +} +bool DevUBLOXGNSS::logOrProcessThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID, bool log) { // If the list is empty if (sfe_ublox_ubx_logging_list_head == nullptr) @@ -5178,7 +5193,10 @@ bool DevUBLOXGNSS::logThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID) if ((sfe_ublox_ubx_logging_list_ptr->UBX_CLASS == UBX_CLASS) // Check for a match && (sfe_ublox_ubx_logging_list_ptr->UBX_ID == UBX_ID)) { - return sfe_ublox_ubx_logging_list_ptr->enable; + if (log) + return (sfe_ublox_ubx_logging_list_ptr->logMe); + else + return (sfe_ublox_ubx_logging_list_ptr->processMe); } if (sfe_ublox_ubx_logging_list_ptr->next == nullptr) @@ -9062,7 +9080,7 @@ bool DevUBLOXGNSS::getESFAutoAlignment(bool *enabled, uint8_t layer, uint16_t ma } bool DevUBLOXGNSS::getESFAutoAlignment(uint8_t layer, uint16_t maxWait) // Unsafe overload { - uint8_t result; + uint8_t result = 0; getVal8(UBLOX_CFG_SFIMU_AUTO_MNTALG_ENA, &result, layer, maxWait); return (bool)result; } @@ -17859,7 +17877,7 @@ bool DevUBLOXGNSS::getMeasurementRate(uint16_t *measRate, uint8_t layer, uint16_ } uint16_t DevUBLOXGNSS::getMeasurementRate(uint8_t layer, uint16_t maxWait) // Unsafe overload... { - uint16_t measurementRate; + uint16_t measurementRate = 0; getMeasurementRate(&measurementRate, layer, maxWait); @@ -17886,7 +17904,7 @@ bool DevUBLOXGNSS::getNavigationRate(uint16_t *navRate, uint8_t layer, uint16_t } uint16_t DevUBLOXGNSS::getNavigationRate(uint8_t layer, uint16_t maxWait) // Unsafe overload... { - uint16_t navigationRate; + uint16_t navigationRate = 0; getNavigationRate(&navigationRate, layer, maxWait); diff --git a/src/u-blox_GNSS.h b/src/u-blox_GNSS.h index c198687..1487ee9 100644 --- a/src/u-blox_GNSS.h +++ b/src/u-blox_GNSS.h @@ -239,6 +239,7 @@ class DevUBLOXGNSS void processRTCM(uint8_t incoming) __attribute__((weak)); // Given rtcm byte, do something with it. User can overwrite if desired to pipe bytes to radio, internet, etc. void processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); // Given a character, file it away into the uxb packet structure void processUBXpacket(ubxPacket *msg); // Once a packet has been received and validated, identify this packet's class/id and update internal flags + virtual void processLoggedUBX(ubxPacket *incomingUBX) {} // Process any UBX message with enableUBXlogging processMe set true // Send I2C/Serial/SPI commands to the module @@ -1361,7 +1362,7 @@ void logSECSIG(bool enabled = true); #endif // UBX Logging - log any UBX message using packetAuto and avoiding having to have and use "Auto" (setAutonnn and lognnn) methods - void enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool enable = true); + void enableUBXlogging(uint8_t UBX_CLASS, uint8_t UBX_ID, bool logMe = true, bool processMe = false); // 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 @@ -1663,7 +1664,9 @@ void logSECSIG(bool enabled = true); // UBX logging sfe_ublox_ubx_logging_list_t *sfe_ublox_ubx_logging_list_head = nullptr; // Linked list of which messages to log - bool logThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID); // Returns true if this UBX should be added to the logging buffer + bool logThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID); // Returns true if this UBX should be added to the logging buffer - for logging + bool processThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID); // Returns true if this UBX should be added to the logging buffer - for processing + bool logOrProcessThisUBX(uint8_t UBX_CLASS, uint8_t UBX_ID, bool log); // Called by logThisUBX and processThisUBX // Flag to prevent reentry into checkCallbacks // Prevent badness if the user accidentally calls checkCallbacks from inside a callback diff --git a/src/u-blox_external_typedefs.h b/src/u-blox_external_typedefs.h index 773f142..d863b05 100644 --- a/src/u-blox_external_typedefs.h +++ b/src/u-blox_external_typedefs.h @@ -245,7 +245,8 @@ struct sfe_ublox_ubx_logging_list_t { uint8_t UBX_CLASS; uint8_t UBX_ID; - bool enable; + bool logMe; + bool processMe; sfe_ublox_ubx_logging_list_t *next; };