From 4580dce13a0f83b2a13471183c334d3cccbdf963 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Thu, 15 Aug 2024 07:27:43 +0100 Subject: [PATCH 1/4] Example34 - poll UBX-MON-VER to kickstart I2C --- .../Example34_I2C_Serial_Passthrough.ino | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/Basics/Example34_I2C_Serial_Passthrough/Example34_I2C_Serial_Passthrough.ino b/examples/Basics/Example34_I2C_Serial_Passthrough/Example34_I2C_Serial_Passthrough.ino index cfc6a5a..68b82dd 100644 --- a/examples/Basics/Example34_I2C_Serial_Passthrough/Example34_I2C_Serial_Passthrough.ino +++ b/examples/Basics/Example34_I2C_Serial_Passthrough/Example34_I2C_Serial_Passthrough.ino @@ -50,11 +50,11 @@ uint8_t uartI2cRingBuffer[ringBufferSize]; uint16_t uartI2cBufferHead = 0; uint16_t uartI2cBufferTail = 0; -const unsigned long uartI2cSendInterval_ms = 50; +const unsigned long uartI2cSendInterval_ms = 5; unsigned long uartI2cLastSend_ms = 0; const uint16_t uartI2cSendLimit = 16; -const unsigned long i2cUartSendInterval_ms = 50; +const unsigned long i2cUartSendInterval_ms = 5; unsigned long i2cUartLastSend_ms = 0; const uint16_t i2cUartSendLimit = 16; @@ -66,12 +66,19 @@ const uint16_t i2cReadLimit = 16; void setup() { - delay(1000); // Wait for ESP32 to start up + delay(2000); // Wait for ESP32 and GNSS to start up mySerial.begin(115200); // Baud rate for u-center myWire.begin(); // Start I2C myWire.setClock(400000); // 400kHz + + // Give I2C a kickstart - if needed. Request UBX-MON-VER + const uint8_t pollUbxMonVer[] = { 0xB5, 0x62, 0x0A, 0x04, 0x00, 0x00, 0x0E, 0x34 }; + for (int i = 0; i < (sizeof(pollUbxMonVer) / sizeof(uint8_t)); i++) { + addToUartI2cBuffer(pollUbxMonVer[i]); + } + } // /setup void loop() @@ -147,6 +154,7 @@ void loop() i2cUartLastSend_ms = millis(); } } + } // /loop // Read how many bytes are available in the GNSS I2C buffer. From 7cb42b19e323e9ea4cbb3e8e52ad56a24bce1ee7 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 4 Nov 2024 13:18:36 +0000 Subject: [PATCH 2/4] v3.1.8 - add auto support for UBX-MON-COMMS --- .../CallbackExample8_MON_COMMS.ino | 177 ++++++++++++ keywords.txt | 9 + library.properties | 2 +- src/u-blox_Class_and_ID.h | 6 + src/u-blox_GNSS.cpp | 263 +++++++++++++++++- src/u-blox_GNSS.h | 19 +- src/u-blox_structs.h | 57 ++++ 7 files changed, 529 insertions(+), 4 deletions(-) create mode 100644 examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino diff --git a/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino new file mode 100644 index 0000000..8b0ea05 --- /dev/null +++ b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino @@ -0,0 +1,177 @@ +/* + Configuring the GNSS to automatically send MON COMMS reports over I2C and display them using a callback + By: Paul Clark + SparkFun Electronics + Date: Novenber 4th, 2024 + License: MIT. See license file for more information. + + This example shows how to configure the u-blox GNSS to send MON COMMS reports automatically + and access the data via a callback. No more polling! + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + + Hardware Connections: + Plug a Qwiic cable into the GPS and a BlackBoard + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Open the serial monitor at 115200 baud to see the output +*/ + +#include //Needed for I2C to GPS + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 +SFE_UBLOX_GNSS myGNSS; + +// Callback: newMONCOMMS will be called when new MON COMMS data arrives +// See u-blox_structs.h for the full definition of UBX_MON_COMMS_data_t +// _____ You can use any name you like for the callback. Use the same name when you call setAutoMONCOMMScallback +// / _____ This _must_ be UBX_MON_COMMS_data_t +// | / _____ You can use any name you like for the struct +// | | / +// | | | +void newMONCOMMS(UBX_MON_COMMS_data_t *ubxDataStruct) +{ + Serial.println(); + + Serial.print(F("New MON COMMS data received. It contains data for ")); + Serial.print(ubxDataStruct->header.nPorts); + if (ubxDataStruct->header.nPorts == 1) + Serial.println(F(" port.")); + else + Serial.println(F(" ports.")); + + // Mimic the data shown in u-center + for (uint8_t port = 0; port < ubxDataStruct->header.nPorts; port++) // For each port + { + bool known = true; + switch (ubxDataStruct->port[port].portId) // Check the port ID is valid + { + case COM_PORT_ID_I2C: + case COM_PORT_ID_UART1: + case COM_PORT_ID_UART2: + case COM_PORT_ID_USB: + case COM_PORT_ID_SPI: + break; + default: + known = false; + break; + } + if (!known) + break; // Skip if port ID is not known + + switch (ubxDataStruct->port[port].portId) // Print the port ID + { + case COM_PORT_ID_I2C: + Serial.print(F("I2C ")); + break; + case COM_PORT_ID_UART1: + Serial.print(F("UART1 ")); + break; + case COM_PORT_ID_UART2: + Serial.print(F("UART2 ")); + break; + case COM_PORT_ID_USB: + Serial.print(F("USB ")); + break; + case COM_PORT_ID_SPI: + Serial.print(F("SPI ")); + break; + default: + Serial.print(F("UNKNOWN ")); + //Serial.printf("0x%04X ", ubxDataStruct->port[port].portId); + break; + } + + Serial.print(": txBytes "); + String txBytes = String(ubxDataStruct->port[port].txBytes); + Serial.print(txBytes); + for (int i = 0; i < 10 - txBytes.length(); i++) + Serial.print(" "); + + Serial.print(" : rxBytes "); + String rxBytes = String(ubxDataStruct->port[port].rxBytes); + Serial.print(rxBytes); + for (int i = 0; i < 10 - rxBytes.length(); i++) + Serial.print(" "); + + for (int i = 0; i < 4; i++) + { + if (ubxDataStruct->header.protIds[i] < 0xFF) + { + switch (ubxDataStruct->header.protIds[i]) + { + case 0: + Serial.print(F(" : UBX ")); + break; + case 1: + Serial.print(F(" : NMEA ")); + break; + case 2: + Serial.print(F(" : RTCM2 ")); + break; + case 5: + Serial.print(F(" : RTCM3 ")); + break; + case 6: + Serial.print(F(" : SPARTN ")); + break; + default: + Serial.print(F(" : UNKNOWN ")); + break; + } + String msgs = String(ubxDataStruct->port[port].msgs[i]); + Serial.print(msgs); + for (int i = 0; i < 5 - msgs.length(); i++) + Serial.print(" "); + } + } + + Serial.print(" : skipped "); + Serial.print(ubxDataStruct->port[port].skipped); + + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun u-blox Example"); + + Wire.begin(); + + //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + + if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); //Set the I2C port to output NMEA and UBX + myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR + + myGNSS.setNavigationFrequency(1); //Produce one solution per second + + // Just to prove we can, poll the MON COMMS data manually + Serial.println("Polling MON COMMS data:"); + UBX_MON_COMMS_data_t portInfo; + if (myGNSS.getCommsPortInfo(&portInfo)) + newMONCOMMS(&portInfo); + else + Serial.println("getCommsPortInfo failed!"); + + // Now enable automatic (periodic) MON COMMS messages with callback to newMONCOMMS + myGNSS.setAutoMONCOMMScallbackPtr(&newMONCOMMS); +} + +void loop() +{ + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. + + Serial.print("."); + delay(50); +} diff --git a/keywords.txt b/keywords.txt index 6d665a3..7edc7c2 100644 --- a/keywords.txt +++ b/keywords.txt @@ -233,6 +233,7 @@ setESFAutoAlignment KEYWORD2 getRFinformation KEYWORD2 +getCommsPortInfo KEYWORD2 getHWstatus KEYWORD2 getAntennaStatus KEYWORD2 getHW2status KEYWORD2 @@ -538,6 +539,14 @@ assumeAutoMONHW KEYWORD2 flushMONHW KEYWORD2 logMONHW KEYWORD2 +getMONCOMMS KEYWORD2 +setAutoMONCOMMS KEYWORD2 +setAutoMONCOMMSrate KEYWORD2 +setAutoMONCOMMScallbackPtr KEYWORD2 +assumeAutoMONCOMMS KEYWORD2 +flushMONCOMMS KEYWORD2 +logMONCOMMS KEYWORD2 + getEsfAlignment KEYWORD2 getESFALG KEYWORD2 setAutoESFALG KEYWORD2 diff --git a/library.properties b/library.properties index b3168cf..5ee6b5b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS v3 -version=3.1.7 +version=3.1.8 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_Class_and_ID.h b/src/u-blox_Class_and_ID.h index a505d46..f0b78a8 100644 --- a/src/u-blox_Class_and_ID.h +++ b/src/u-blox_Class_and_ID.h @@ -344,6 +344,12 @@ const uint8_t COM_PORT_UART2 = 2; const uint8_t COM_PORT_USB = 3; const uint8_t COM_PORT_SPI = 4; +const uint16_t COM_PORT_ID_I2C = 0x0000; // Port IDs used by MON-COMMS +const uint16_t COM_PORT_ID_UART1 = 0x0100; +const uint16_t COM_PORT_ID_UART2 = 0x0201; +const uint16_t COM_PORT_ID_USB = 0x0300; +const uint16_t COM_PORT_ID_SPI = 0x0400; + const uint8_t COM_TYPE_UBX = (1 << 0); const uint8_t COM_TYPE_NMEA = (1 << 1); const uint8_t COM_TYPE_RTCM3 = (1 << 5); diff --git a/src/u-blox_GNSS.cpp b/src/u-blox_GNSS.cpp index 5c83c0f..7cb2379 100644 --- a/src/u-blox_GNSS.cpp +++ b/src/u-blox_GNSS.cpp @@ -434,6 +434,16 @@ void DevUBLOXGNSS::end(void) packetUBXTIMTP = nullptr; } + if (packetUBXMONCOMMS != nullptr) + { + if (packetUBXMONCOMMS->callbackData != nullptr) + { + delete packetUBXMONCOMMS->callbackData; + } + delete packetUBXMONCOMMS; + packetUBXMONCOMMS = nullptr; + } + if (packetUBXMONHW != nullptr) { if (packetUBXMONHW->callbackData != nullptr) @@ -1483,7 +1493,13 @@ bool DevUBLOXGNSS::autoLookup(uint8_t Class, uint8_t ID, uint16_t *maxSize) } break; case UBX_CLASS_MON: - if (ID == UBX_MON_HW) + if (ID == UBX_MON_COMMS) + { + if (maxSize != nullptr) + *maxSize = UBX_MON_COMMS_MAX_LEN; + return (packetUBXMONCOMMS != nullptr); + } + else if (ID == UBX_MON_HW) { if (maxSize != nullptr) *maxSize = UBX_MON_HW_LEN; @@ -4403,7 +4419,53 @@ void DevUBLOXGNSS::processUBXpacket(ubxPacket *msg) } break; case UBX_CLASS_MON: - if (msg->id == UBX_MON_HW && msg->len == UBX_MON_HW_LEN) + if (msg->id == UBX_MON_COMMS && msg->len <= UBX_MON_COMMS_MAX_LEN) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXMONCOMMS != nullptr) + { + packetUBXMONCOMMS->data.header.version = extractByte(msg, 0); + packetUBXMONCOMMS->data.header.nPorts = extractByte(msg, 1); + packetUBXMONCOMMS->data.header.txErrors.all = extractByte(msg, 2); + for (uint16_t i = 0; i < 4; i++) + packetUBXMONCOMMS->data.header.protIds[i] = extractByte(msg, 4 + i); + + for (uint16_t i = 0; (i < UBX_MON_COMMS_MAX_PORTS) && (i < packetUBXMONCOMMS->data.header.nPorts) && ((i * 40) < (msg->len - 8)); i++) + { + packetUBXMONCOMMS->data.port[i].portId = extractInt(msg, 8 + (i * 40) + 0); + packetUBXMONCOMMS->data.port[i].txPending = extractInt(msg, 8 + (i * 40) + 2); + packetUBXMONCOMMS->data.port[i].txBytes = extractLong(msg, 8 + (i * 40) + 4); + packetUBXMONCOMMS->data.port[i].txUsage = extractByte(msg, 8 + (i * 40) + 8); + packetUBXMONCOMMS->data.port[i].txPeakUsage = extractByte(msg, 8 + (i * 40) + 9); + packetUBXMONCOMMS->data.port[i].rxPending = extractInt(msg, 8 + (i * 40) + 10); + packetUBXMONCOMMS->data.port[i].rxBytes = extractLong(msg, 8 + (i * 40) + 12); + packetUBXMONCOMMS->data.port[i].rxUsage = extractByte(msg, 8 + (i * 40) + 16); + packetUBXMONCOMMS->data.port[i].rxPeakUsage = extractByte(msg, 8 + (i * 40) + 17); + packetUBXMONCOMMS->data.port[i].overrunErrs = extractInt(msg, 8 + (i * 40) + 18); + for (uint16_t j = 0; j < 4; j++) + packetUBXMONCOMMS->data.port[i].msgs[j] = extractInt(msg, 8 + (i * 40) + 20 + (j * 2)); + packetUBXMONCOMMS->data.port[i].skipped = extractLong(msg, 8 + (i * 40) + 36); + } + + // Mark all datums as fresh (not read before) + packetUBXMONCOMMS->moduleQueried = true; + + // Check if we need to copy the data for the callback + if ((packetUBXMONCOMMS->callbackData != nullptr) // If RAM has been allocated for the copy of the data + && (packetUBXMONCOMMS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXMONCOMMS->callbackData->header.version, &packetUBXMONCOMMS->data.header.version, sizeof(UBX_MON_COMMS_data_t)); + packetUBXMONCOMMS->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXMONCOMMS->automaticFlags.flags.bits.addToFileBuffer) + { + addedToFileBuffer = storePacket(msg); + } + } + } + else if (msg->id == UBX_MON_HW && msg->len == UBX_MON_HW_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXMONHW != nullptr) @@ -5977,6 +6039,17 @@ void DevUBLOXGNSS::checkCallbacks(void) packetUBXTIMTP->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + if (packetUBXMONCOMMS != nullptr) // If RAM has been allocated for message storage + if (packetUBXMONCOMMS->callbackData != nullptr) // If RAM has been allocated for the copy of the data + if (packetUBXMONCOMMS->automaticFlags.flags.bits.callbackCopyValid == true) // If the copy of the data is valid + { + if (packetUBXMONCOMMS->callbackPointerPtr != nullptr) // If the pointer to the callback has been defined + { + packetUBXMONCOMMS->callbackPointerPtr(packetUBXMONCOMMS->callbackData); // Call the callback + } + packetUBXMONCOMMS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + if (packetUBXMONHW != nullptr) // If RAM has been allocated for message storage if (packetUBXMONHW->callbackData != nullptr) // If RAM has been allocated for the copy of the data if (packetUBXMONHW->automaticFlags.flags.bits.callbackCopyValid == true) // If the copy of the data is valid @@ -14583,6 +14656,172 @@ void DevUBLOXGNSS::logTIMTP(bool enabled) packetUBXTIMTP->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** MON COMMS automatic support + +bool DevUBLOXGNSS::getMONCOMMS(uint16_t maxWait) +{ + if (packetUBXMONCOMMS == nullptr) + initPacketUBXMONCOMMS(); // Check that RAM has been allocated for the COMMS data + if (packetUBXMONCOMMS == nullptr) // Bail if the RAM allocation failed + return (false); + + if (packetUBXMONCOMMS->automaticFlags.flags.bits.automatic && packetUBXMONCOMMS->automaticFlags.flags.bits.implicitUpdate) + { + // The GPS is automatically reporting, we just check whether we got unread data + checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Don't overwrite the requested Class and ID + return packetUBXMONCOMMS->moduleQueried; + } + else if (packetUBXMONCOMMS->automaticFlags.flags.bits.automatic && !packetUBXMONCOMMS->automaticFlags.flags.bits.implicitUpdate) + { + // Someone else has to call checkUblox for us... + return (false); + } + else + { + // The GPS is not automatically reporting navigation position so we have to poll explicitly + packetCfg.cls = UBX_CLASS_MON; + packetCfg.id = UBX_MON_COMMS; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + return (true); + } + + return (false); + } +} + +// Enable or disable automatic message generation by the GNSS. This changes the way getMONCOMMS works. +bool DevUBLOXGNSS::setAutoMONCOMMS(bool enable, uint8_t layer, uint16_t maxWait) +{ + return setAutoMONCOMMSrate(enable ? 1 : 0, true, layer, maxWait); +} + +// Enable or disable automatic message generation by the GNSS. This changes the way getMONCOMMS works. +bool DevUBLOXGNSS::setAutoMONCOMMS(bool enable, bool implicitUpdate, uint8_t layer, uint16_t maxWait) +{ + return setAutoMONCOMMSrate(enable ? 1 : 0, implicitUpdate, layer, maxWait); +} + +// Enable or disable automatic message generation by the GNSS. This changes the way getMONCOMMS works. +bool DevUBLOXGNSS::setAutoMONCOMMSrate(uint8_t rate, bool implicitUpdate, uint8_t layer, uint16_t maxWait) +{ + if (packetUBXMONCOMMS == nullptr) + initPacketUBXMONCOMMS(); // Check that RAM has been allocated for the data + if (packetUBXMONCOMMS == nullptr) // Only attempt this if RAM allocation was successful + return false; + + if (rate > 127) + rate = 127; + + uint32_t key = UBLOX_CFG_MSGOUT_UBX_MON_COMMS_I2C; + if (_commType == COMM_TYPE_SPI) + key = UBLOX_CFG_MSGOUT_UBX_MON_COMMS_SPI; + else if (_commType == COMM_TYPE_SERIAL) + { + if (!_UART2) + key = UBLOX_CFG_MSGOUT_UBX_MON_COMMS_UART1; + else + key = UBLOX_CFG_MSGOUT_UBX_MON_COMMS_UART2; + } + + bool ok = setVal8(key, rate, layer, maxWait); + if (ok) + { + packetUBXMONCOMMS->automaticFlags.flags.bits.automatic = (rate > 0); + packetUBXMONCOMMS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + packetUBXMONCOMMS->moduleQueried = false; + return ok; +} + +// Enable automatic message generation by the GNSS. +bool DevUBLOXGNSS::setAutoMONCOMMScallbackPtr(void (*callbackPointerPtr)(UBX_MON_COMMS_data_t *), uint8_t layer, uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoMONCOMMS(true, false, layer, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXMONCOMMS->callbackData == nullptr) // Check if RAM has been allocated for the callback copy + { + packetUBXMONCOMMS->callbackData = new UBX_MON_COMMS_data_t; // Allocate RAM for the main struct + } + + if (packetUBXMONCOMMS->callbackData == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("setAutoMONCOMMScallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXMONCOMMS->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// In case no config access to the GNSS is possible and TIM TP is send cyclically already +// set config to suitable parameters +bool DevUBLOXGNSS::assumeAutoMONCOMMS(bool enabled, bool implicitUpdate) +{ + if (packetUBXMONCOMMS == nullptr) + initPacketUBXMONCOMMS(); // Check that RAM has been allocated for the data + if (packetUBXMONCOMMS == nullptr) // Only attempt this if RAM allocation was successful + return false; + + bool changes = packetUBXMONCOMMS->automaticFlags.flags.bits.automatic != enabled || packetUBXMONCOMMS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; + if (changes) + { + packetUBXMONCOMMS->automaticFlags.flags.bits.automatic = enabled; + packetUBXMONCOMMS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + return changes; +} + +// PRIVATE: Allocate RAM for packetUBXMONCOMMS and initialize it +bool DevUBLOXGNSS::initPacketUBXMONCOMMS() +{ + packetUBXMONCOMMS = new UBX_MON_COMMS_t; // Allocate RAM for the main struct + if (packetUBXMONCOMMS == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("initPacketUBXMONCOMMS: RAM alloc failed!")); +#endif + return (false); + } + packetUBXMONCOMMS->automaticFlags.flags.all = 0; + packetUBXMONCOMMS->callbackPointerPtr = nullptr; + packetUBXMONCOMMS->callbackData = nullptr; + packetUBXMONCOMMS->moduleQueried = false; + return (true); +} + +// Mark all the data as read/stale +void DevUBLOXGNSS::flushMONCOMMS() +{ + if (packetUBXMONCOMMS == nullptr) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXMONCOMMS->moduleQueried = false; // Mark all datums as stale (read before) +} + +// Log this data in file buffer +void DevUBLOXGNSS::logMONCOMMS(bool enabled) +{ + if (packetUBXMONCOMMS == nullptr) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXMONCOMMS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; +} + // ***** MON HW automatic support bool DevUBLOXGNSS::getMONHW(uint16_t maxWait) @@ -18487,6 +18726,24 @@ uint32_t DevUBLOXGNSS::getTIMTPAsEpoch(uint32_t µsecond, uint16_t maxWait) return tow; } +// ***** MON COMMS Helper Functions + +// Get the communication port information using UBX_MON_COMMS +bool DevUBLOXGNSS::getCommsPortInfo(UBX_MON_COMMS_data_t *data, uint16_t maxWait) +{ + if (data == nullptr) // Check if the user forgot to include the data pointer + return (false); // Bail + + if (!getMONCOMMS(maxWait)) + return (false); + + memcpy(data, &packetUBXMONCOMMS->data, sizeof(UBX_MON_COMMS_data_t)); + + packetUBXMONCOMMS->moduleQueried = false; // Mark the data as stale + + return (true); +} + // ***** MON HW Helper Functions // Get the hardware status (including jamming) using UBX_MON_HW @@ -18500,6 +18757,8 @@ bool DevUBLOXGNSS::getHWstatus(UBX_MON_HW_data_t *data, uint16_t maxWait) memcpy(data, &packetUBXMONHW->data, sizeof(UBX_MON_HW_data_t)); + packetUBXMONHW->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale + return (true); } diff --git a/src/u-blox_GNSS.h b/src/u-blox_GNSS.h index 83a9ead..6d1fea5 100644 --- a/src/u-blox_GNSS.h +++ b/src/u-blox_GNSS.h @@ -995,6 +995,17 @@ class DevUBLOXGNSS void flushTIMTP(); // Mark all the data as read/stale void logTIMTP(bool enabled = true); // Log data to file buffer + // Receiver status (MON) + + bool getMONCOMMS(uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // MON COMMS + bool setAutoMONCOMMS(bool enabled, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic MON COMMS reports at the navigation frequency + bool setAutoMONCOMMS(bool enabled, bool implicitUpdate, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic MON COMMS 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 + bool setAutoMONCOMMSrate(uint8_t rate, bool implicitUpdate = true, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Set the rate for automatic MON COMMS reports + bool setAutoMONCOMMScallbackPtr(void (*callbackPointerPtr)(UBX_MON_COMMS_data_t *), uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable automatic MON COMMS reports at the navigation frequency. Data is accessed from the callback. + bool assumeAutoMONCOMMS(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and MON COMMS is send cyclically already + void flushMONCOMMS(); // Mark all the data as read/stale + void logMONCOMMS(bool enabled = true); // Log data to file buffer + bool getMONHW(uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // MON HW bool setAutoMONHW(bool enabled, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic MON HW reports at the navigation frequency bool setAutoMONHW(bool enabled, bool implicitUpdate, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic MON HW 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 @@ -1240,6 +1251,10 @@ class DevUBLOXGNSS uint16_t getTIMTPweek(uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Returns the UBX-TIM-TP time pulse week according to time base uint32_t getTIMTPAsEpoch(uint32_t µsecond, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Convert TIM TP to Unix Epoch - CAUTION! Assumes the time base is UTC and the week number is GPS + // Helper function for MON COMMS + + bool getCommsPortInfo(UBX_MON_COMMS_data_t *data, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Get the communication port information using UBX_MON_COMMS + // Helper function for hardware status (including jamming) // For safety, call getAntennaStatus inside an if(getMONHW()) @@ -1386,7 +1401,8 @@ class DevUBLOXGNSS UBX_TIM_TM2_t *packetUBXTIMTM2 = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_TIM_TP_t *packetUBXTIMTP = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary - UBX_MON_HW_t *packetUBXMONHW = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_MON_COMMS_t *packetUBXMONCOMMS = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_MON_HW_t *packetUBXMONHW = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary #ifndef SFE_UBLOX_DISABLE_ESF UBX_ESF_ALG_t *packetUBXESFALG = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1499,6 +1515,7 @@ class DevUBLOXGNSS bool initPacketUBXRXMMEASX(); // Allocate RAM for packetUBXRXMMEASX and initialize it bool initPacketUBXTIMTM2(); // Allocate RAM for packetUBXTIMTM2 and initialize it bool initPacketUBXTIMTP(); // Allocate RAM for packetUBXTIMTP and initialize it + bool initPacketUBXMONCOMMS(); // Allocate RAM for packetUBXMONCOMMS and initialize it bool initPacketUBXMONHW(); // Allocate RAM for packetUBXMONHW and initialize it bool initPacketUBXESFALG(); // Allocate RAM for packetUBXESFALG and initialize it bool initPacketUBXESFSTATUS(); // Allocate RAM for packetUBXESFSTATUS and initialize it diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 6666d8f..5516313 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1887,6 +1887,63 @@ typedef struct // MON-specific structs +// UBX-MON-COMMS (0x0A 0x36): Communication port information +// Note: length is variable +const uint8_t UBX_MON_COMMS_MAX_PORTS = 5; // I2C, UART1, UART2, USB, SPI +const uint16_t UBX_MON_COMMS_MAX_LEN = 8 + (40 * UBX_MON_COMMS_MAX_PORTS); + +typedef struct +{ + uint8_t version; // Message version (0x00 for this version) + uint8_t nPorts; // Number of ports included + union + { + uint8_t all; + struct + { + uint8_t mem : 1; // Memory Allocation error + uint8_t alloc : 1; // Allocation error (TX buffer full) + } bits; + } txErrors; // TX error bitmask + uint8_t reserved0; + uint8_t protIds[4]; // The identifiers of the protocols reported in the msgs array. + // 0: UBX, 1: NMEA, 2: RTCM2, 5: RTCM3, 6: SPARTN, 0xFF: No protocol reported. + +} UBX_MON_COMMS_header_t; + +typedef struct +{ + uint16_t portId; // Unique identifier for the port + uint16_t txPending; // Number of bytes pending in transmitter buffer + uint32_t txBytes; // Number of bytes ever sent + uint8_t txUsage; // % Maximum usage transmitter buffer during the last sysmon period + uint8_t txPeakUsage; // % Maximum usage transmitter buffer + uint16_t rxPending; // Number of bytes in receiver buffer + uint32_t rxBytes; // Number of bytes ever received + uint8_t rxUsage; // % Maximum usage receiver buffer during the last sysmon period + uint8_t rxPeakUsage; // % Maximum usage receiver buffer + uint16_t overrunErrs; // Number of 100 ms timeslots with overrun errors + uint16_t msgs[4]; // Number of successfully parsed messages for each protocol + uint8_t reserved1[8]; + uint32_t skipped; // Number of skipped bytes +} UBX_MON_COMMS_port_t; + +typedef struct +{ + UBX_MON_COMMS_header_t header; + UBX_MON_COMMS_port_t port[UBX_MON_COMMS_MAX_PORTS]; +} UBX_MON_COMMS_data_t; + +typedef struct +{ + ubxAutomaticFlags automaticFlags; + UBX_MON_COMMS_data_t data; + bool moduleQueried; + void (*callbackPointerPtr)(UBX_MON_COMMS_data_t *); + UBX_MON_COMMS_data_t *callbackData; +} UBX_MON_COMMS_t; + + // UBX-MON-HW (0x0A 0x09): Hardware status const uint16_t UBX_MON_HW_LEN = 60; From 9c238469bd08e61f37c44ea60e806b08ccd38886 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 4 Nov 2024 13:31:12 +0000 Subject: [PATCH 3/4] Update CallbackExample8_MON_COMMS.ino --- .../CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino index 8b0ea05..92cbf7a 100644 --- a/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino +++ b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino @@ -159,7 +159,7 @@ void setup() Serial.println("Polling MON COMMS data:"); UBX_MON_COMMS_data_t portInfo; if (myGNSS.getCommsPortInfo(&portInfo)) - newMONCOMMS(&portInfo); + newMONCOMMS(&portInfo); // Call the callback manually to print the data else Serial.println("getCommsPortInfo failed!"); From d28022a777375908ab0d88ffb3b0655de4a84f8a Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 4 Nov 2024 18:11:09 +0000 Subject: [PATCH 4/4] Increase UBX_MON_COMMS_MAX_PORTS to 7 --- .../CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino | 1 + src/u-blox_Class_and_ID.h | 2 ++ src/u-blox_structs.h | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino index 92cbf7a..b4fba12 100644 --- a/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino +++ b/examples/Callbacks/CallbackExample8_MON_COMMS/CallbackExample8_MON_COMMS.ino @@ -143,6 +143,7 @@ void setup() Wire.begin(); //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + //myGNSS.enableDebugging(Serial, true); // Uncomment this line to enable only the major debug messages on Serial if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port { diff --git a/src/u-blox_Class_and_ID.h b/src/u-blox_Class_and_ID.h index f0b78a8..37fd31e 100644 --- a/src/u-blox_Class_and_ID.h +++ b/src/u-blox_Class_and_ID.h @@ -344,6 +344,8 @@ const uint8_t COM_PORT_UART2 = 2; const uint8_t COM_PORT_USB = 3; const uint8_t COM_PORT_SPI = 4; +// Port IDs used by MON-COMMS +// UBX-18010802 - R15 also documents 0x0101 and 0x0200 - both are "Reserved" const uint16_t COM_PORT_ID_I2C = 0x0000; // Port IDs used by MON-COMMS const uint16_t COM_PORT_ID_UART1 = 0x0100; const uint16_t COM_PORT_ID_UART2 = 0x0201; diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 5516313..c786a89 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1889,7 +1889,7 @@ typedef struct // UBX-MON-COMMS (0x0A 0x36): Communication port information // Note: length is variable -const uint8_t UBX_MON_COMMS_MAX_PORTS = 5; // I2C, UART1, UART2, USB, SPI +const uint16_t UBX_MON_COMMS_MAX_PORTS = 7; // I2C, UART1, UART2, USB, SPI, 0x0101, 0x0200 const uint16_t UBX_MON_COMMS_MAX_LEN = 8 + (40 * UBX_MON_COMMS_MAX_PORTS); typedef struct