diff --git a/examples/Callbacks/CallbackExample2_NAV_ODO/CallbackExample2_NAV_ODO.ino b/examples/Callbacks/CallbackExample2_NAV_ODO/CallbackExample2_NAV_ODO.ino index 181824b1..3c372e3e 100644 --- a/examples/Callbacks/CallbackExample2_NAV_ODO/CallbackExample2_NAV_ODO.ino +++ b/examples/Callbacks/CallbackExample2_NAV_ODO/CallbackExample2_NAV_ODO.ino @@ -2,9 +2,8 @@ Configuring the GNSS to automatically send odometer reports over I2C and display the data using a callback By: Paul Clark SparkFun Electronics - Date: December 30th, 2020 - License: MIT. See license file for more information but you can - basically do whatever you want with this code. + Date: March 20th, 2023 + License: MIT. See license file for more information. This example shows how to configure the u-blox GNSS to send odometer reports automatically and display the data via a callback. No more polling! @@ -55,6 +54,8 @@ void printODOdata(UBX_NAV_ODO_data_t *ubxDataStruct) void setup() { + delay(1000); + Serial.begin(115200); while (!Serial); //Wait for user to open terminal Serial.println("SparkFun u-blox Example"); @@ -63,10 +64,9 @@ void setup() //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 + 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. Freezing.")); - while (1); + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring.")); } myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) @@ -74,6 +74,27 @@ void setup() myGNSS.setNavigationFrequency(1); //Produce one solution per second + //By default, the odometer is disabled. We need to enable it. + //We can enable it using the default settings: + myGNSS.enableOdometer(); + + //Or we can configure it using our own settings, by performing a read-modify-write: + uint8_t flags; // Odometer/Low-speed COG filter flags + uint8_t odoCfg; // Odometer filter settings + uint8_t cogMaxSpeed; // Speed below which course-over-ground (COG) is computed with the low-speed COG filter : m/s * 0.1 + uint8_t cogMaxPosAcc; // Maximum acceptable position accuracy for computing COG with the low-speed COG filter + uint8_t velLpGain; // Velocity low-pass filter level + uint8_t cogLpGain; // COG low-pass filter level + + if (myGNSS.getOdometerConfig(&flags, &odoCfg, &cogMaxSpeed, &cogMaxPosAcc, &velLpGain, &cogLpGain)) + { + flags = UBX_CFG_ODO_USE_ODO; // Enable the odometer + odoCfg = UBX_CFG_ODO_CAR; // Use the car profile (others are RUN, CYCLE, SWIM, CUSTOM) + myGNSS.setOdometerConfig(flags, odoCfg, cogMaxSpeed, cogMaxPosAcc, velLpGain, cogLpGain); // Set the configuration + } + else + Serial.println("Could not read odometer config!"); + //myGNSS.resetOdometer(); //Uncomment this line to reset the odometer myGNSS.setAutoNAVODOcallbackPtr(&printODOdata); // Enable automatic NAV ODO messages with callback to printODOdata diff --git a/examples/NEO-M8P-2/Example4_MovingBase_Base/Example4_MovingBase_Base.ino b/examples/NEO-M8P-2/Example4_MovingBase_Base/Example4_MovingBase_Base.ino new file mode 100644 index 00000000..07d78772 --- /dev/null +++ b/examples/NEO-M8P-2/Example4_MovingBase_Base/Example4_MovingBase_Base.ino @@ -0,0 +1,74 @@ +/* + Configure the NEO-M8P-2 as a "moving base" + By: Paul Clark + SparkFun Electronics + Date: March 20th, 2023 + License: MIT. See license file for more information. + + This example configures the NEO-M8P-2 as a moving base. + It will output RTCM3 1077, 1087, 1230 and 4072.0 messages on UART1. + Connect UART1 TX to UART1 RX on a NEO-M8P rover. Also connect GND to GND. + The next example shows how to display the relative position of the rover. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + + Hardware Connections: + Plug a Qwiic cable into the GNSS 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) + Connect UART1 TX to UART1 RX on a NEO-M8P rover. Also connect GND to GND. + Open the serial monitor at 115200 baud to see the output +*/ + +#include //Needed for I2C to GNSS + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +void setup() +{ + delay(1000); + + Serial.begin(115200); + while(!Serial); //Wait for user to open terminal + Serial.println(F("u-blox NEO-M8P Moving Base Example")); + + Wire.begin(); + + 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 + //myGNSS.factoryDefault(); delay(5000); + + myGNSS.setUART1Output(COM_TYPE_RTCM3); // Configure UART1 to output RTCM3 only. Disable NMEA. + myGNSS.saveConfiguration(); //Save the current settings to flash and BBR + + bool response = true; + + response &= myGNSS.enableRTCMmessage(UBX_RTCM_1077, COM_PORT_UART1, 1); //Enable message 1077 on UART1 + response &= myGNSS.enableRTCMmessage(UBX_RTCM_1087, COM_PORT_UART1, 1); //Enable message 1087 on UART1 + response &= myGNSS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_UART1, 1); //Enable message 1230 on UART1 + response &= myGNSS.enableRTCMmessage(UBX_RTCM_4072_0, COM_PORT_UART1, 1); //Enable message 4072.0 on UART1 + + response &= myGNSS.disableSurveyMode(); //Essential - we need to set TMODE3 to Disabled to allow 4072.0 to be output + + if (response == true) + { + Serial.println(F("RTCM messages enabled")); + } + else + { + Serial.println(F("RTCM failed to enable. Are you sure you have an NEO-M8P?")); + } +} + +void loop() +{ + myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in. + + delay(250); //Don't pound too hard on the I2C bus +} diff --git a/examples/NEO-M8P-2/Example5_MovingBase_Rover/Example5_MovingBase_Rover.ino b/examples/NEO-M8P-2/Example5_MovingBase_Rover/Example5_MovingBase_Rover.ino new file mode 100644 index 00000000..5cd3f38d --- /dev/null +++ b/examples/NEO-M8P-2/Example5_MovingBase_Rover/Example5_MovingBase_Rover.ino @@ -0,0 +1,117 @@ +/* + Display the relative position of a NEO-M8P-2 rover + By: Paul Clark + SparkFun Electronics + Date: March 20th, 2023 + License: MIT. See license file for more information. + + This example shows how to query the module for RELPOS information in the NED frame. + It assumes you already have RTCM correction data being fed to the receiver on UART1. + Connect UART1 TX on the base to UART1 RX on the rover. Also connect GND to GND. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + + Hardware Connections: + Plug a Qwiic cable into the GNSS and a RedBoard Qwiic or BlackBoard + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Connect UART1 TX on the base to UART1 RX on the rover. Also connect GND to GND. + Open the serial monitor at 115200 baud to see the output +*/ + +#include //Needed for I2C to GNSS + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +void setup() +{ + delay(1000); + + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("u-blox NEO-M8P Rover Example"); + + Wire.begin(); + + 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 + //myGNSS.factoryDefault(); delay(5000); + + // By default, UART1 Protocol In is set to NMEA + UBX + RTCM3. No changes are necessary. But we can manually configure the port if required. + Serial.print(myGNSS.setPortInput(COM_PORT_UART1, COM_TYPE_UBX | COM_TYPE_RTCM3)); //Enable UBX and RTCM3 input on UART1 + myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port settings to flash and BBR +} + +void loop() +{ + // The data from getRELPOSNED (UBX-NAV-RELPOSNED) is returned in UBX_NAV_RELPOSNED_t packetUBXNAVRELPOSNED + // Please see u-blox_structs.h for the full definition of UBX_NAV_RELPOSNED_t + // You can either read the data from packetUBXNAVRELPOSNED directly + // or can use the helper functions: getRelPosN/E/D; getRelPosAccN/E/D + // Note that NEO-M8P RELPOSNED is different to ZED-F9P. It does not contain the relative length and heading. + if (myGNSS.getRELPOSNED() == true) + { + Serial.print("relPosN: "); + Serial.println(myGNSS.getRelPosN(), 4); // Use the helper functions to get the rel. pos. as m + Serial.print("relPosE: "); + Serial.println(myGNSS.getRelPosE(), 4); + Serial.print("relPosD: "); + Serial.println(myGNSS.getRelPosD(), 4); + + Serial.print("relPosHPN: "); + Serial.println((float)myGNSS.packetUBXNAVRELPOSNED->data.relPosHPN / 10, 1); //High-precision component. Convert to mm + Serial.print("relPosHPE: "); + Serial.println((float)myGNSS.packetUBXNAVRELPOSNED->data.relPosHPE / 10, 1); + Serial.print("relPosHPD: "); + Serial.println((float)myGNSS.packetUBXNAVRELPOSNED->data.relPosHPD / 10, 1); + + Serial.print("accN: "); + Serial.println(myGNSS.getRelPosAccN(), 4); // Use the helper functions to get the rel. pos. accuracy as m + Serial.print("accE: "); + Serial.println(myGNSS.getRelPosAccE(), 4); + Serial.print("accD: "); + Serial.println(myGNSS.getRelPosAccD(), 4); + + Serial.print("gnssFixOk: "); + if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.gnssFixOK == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("diffSolution: "); + if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.diffSoln == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("relPosValid: "); + if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.relPosValid == true) + Serial.println("x"); + else + Serial.println(""); + + Serial.print("carrier Solution Type: "); + if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 0) + Serial.println("None"); + else if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 1) + Serial.println("Float"); + else if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.carrSoln == 2) + Serial.println("Fixed"); + + Serial.print("isMoving: "); + if (myGNSS.packetUBXNAVRELPOSNED->data.flags.bits.isMoving == true) + Serial.println("x"); + else + Serial.println(""); + } + else + Serial.println("RELPOS request failed"); + + Serial.println(""); +} diff --git a/keywords.txt b/keywords.txt index 52cfef27..6f1b29ec 100644 --- a/keywords.txt +++ b/keywords.txt @@ -193,6 +193,9 @@ setDynamicModel KEYWORD2 getDynamicModel KEYWORD2 resetOdometer KEYWORD2 +enableOdometer KEYWORD2 +getOdometerConfig KEYWORD2 +setOdometerConfig KEYWORD2 enableGNSS KEYWORD2 isGNSSenabled KEYWORD2 @@ -831,6 +834,17 @@ VAL_CFG_SUBSEC_ANTCONF LITERAL1 VAL_CFG_SUBSEC_LOGCONF LITERAL1 VAL_CFG_SUBSEC_FTSCONF LITERAL1 +UBX_CFG_ODO_USE_ODO LITERAL1 +UBX_CFG_ODO_USE_COG LITERAL1 +UBX_CFG_ODO_OUT_LP_VEL LITERAL1 +UBX_CFG_ODO_OUT_LP_COG LITERAL1 + +UBX_CFG_ODO_RUN LITERAL1 +UBX_CFG_ODO_CYCLE LITERAL1 +UBX_CFG_ODO_SWIM LITERAL1 +UBX_CFG_ODO_CAR LITERAL1 +UBX_CFG_ODO_CUSTOM LITERAL1 + DYN_MODEL_PORTABLE LITERAL1 DYN_MODEL_STATIONARY LITERAL1 DYN_MODEL_PEDESTRIAN LITERAL1 diff --git a/library.properties b/library.properties index 987ab335..86de605e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS Arduino Library -version=2.2.21 +version=2.2.22 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules

diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 16ea35f8..460dbceb 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -8207,6 +8207,70 @@ bool SFE_UBLOX_GNSS::resetOdometer(uint16_t maxWait) return (sendCommand(&packetCfg, maxWait, true) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } +// Enable / disable the odometer +bool SFE_UBLOX_GNSS::enableOdometer(bool enable, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_ODO; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // Ask module for the current odometer configuration. Loads into payloadCfg. + if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + return (false); + + if (enable) + payloadCfg[4] = payloadCfg[4] | UBX_CFG_ODO_USE_ODO; + else + payloadCfg[4] = payloadCfg[4] & ~UBX_CFG_ODO_USE_ODO; + + return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK +} + +// Read the odometer configuration +bool SFE_UBLOX_GNSS::getOdometerConfig(uint8_t *flags, uint8_t *odoCfg, uint8_t *cogMaxSpeed, uint8_t *cogMaxPosAcc, uint8_t *velLpGain, uint8_t *cogLpGain, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_ODO; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // Ask module for the current odometer configuration. Loads into payloadCfg. + if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + return (false); + + *flags = payloadCfg[4]; + *odoCfg = payloadCfg[5]; + *cogMaxSpeed = payloadCfg[12]; + *cogMaxPosAcc = payloadCfg[13]; + *velLpGain = payloadCfg[16]; + *cogLpGain = payloadCfg[17]; + + return true; +} + +// Configure the odometer +bool SFE_UBLOX_GNSS::setOdometerConfig(uint8_t flags, uint8_t odoCfg, uint8_t cogMaxSpeed, uint8_t cogMaxPosAcc, uint8_t velLpGain, uint8_t cogLpGain, uint16_t maxWait) +{ + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_ODO; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // Ask module for the current odometer configuration. Loads into payloadCfg. + if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + return (false); + + payloadCfg[4] = flags; + payloadCfg[5] = odoCfg; + payloadCfg[12] = cogMaxSpeed; + payloadCfg[13] = cogMaxPosAcc; + payloadCfg[16] = velLpGain; + payloadCfg[17] = cogLpGain; + + return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK +} + // Enable/Disable individual GNSS systems using UBX-CFG-GNSS bool SFE_UBLOX_GNSS::enableGNSS(bool enable, sfe_ublox_gnss_ids_e id, uint16_t maxWait) { @@ -17402,6 +17466,8 @@ int32_t SFE_UBLOX_GNSS::getAltitude(uint16_t maxWait) // Get the current altitude in mm according to mean sea level // Ellipsoid model: https://www.esri.com/news/arcuser/0703/geoid1of3.html // Difference between Ellipsoid Model and Mean Sea Level: https://eos-gnss.com/elevation-for-beginners/ +// Also see: https://portal.u-blox.com/s/question/0D52p00008HKDSkCAP/what-geoid-model-is-used-and-where-is-this-calculated +// and: https://cddis.nasa.gov/926/egm96/egm96.html on 10x10 degree grid int32_t SFE_UBLOX_GNSS::getAltitudeMSL(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 18f25626..c83f002e 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -465,6 +465,22 @@ const uint8_t COM_TYPE_NMEA = (1 << 1); const uint8_t COM_TYPE_RTCM3 = (1 << 5); const uint8_t COM_TYPE_SPARTN = (1 << 6); +// Odometer configuration - flags +const uint8_t UBX_CFG_ODO_USE_ODO = (1 << 0); +const uint8_t UBX_CFG_ODO_USE_COG = (1 << 1); +const uint8_t UBX_CFG_ODO_OUT_LP_VEL = (1 << 2); +const uint8_t UBX_CFG_ODO_OUT_LP_COG = (1 << 3); + +// Odometer configuration - odoCfg +enum odoCfg_e +{ + UBX_CFG_ODO_RUN = 0, + UBX_CFG_ODO_CYCLE, + UBX_CFG_ODO_SWIM, + UBX_CFG_ODO_CAR, + UBX_CFG_ODO_CUSTOM, +}; + // Configuration Sub-Section mask definitions for saveConfigSelective (UBX-CFG-CFG) const uint32_t VAL_CFG_SUBSEC_IOPORT = 0x00000001; // ioPort - communications port settings (causes IO system reset!) const uint32_t VAL_CFG_SUBSEC_MSGCONF = 0x00000002; // msgConf - message configuration @@ -909,8 +925,11 @@ class SFE_UBLOX_GNSS bool setDynamicModel(dynModel newDynamicModel = DYN_MODEL_PORTABLE, uint16_t maxWait = defaultMaxWait); uint8_t getDynamicModel(uint16_t maxWait = defaultMaxWait); // Get the dynamic model - returns 255 if the sendCommand fails - // Reset the odometer + // Reset / enable / configure the odometer bool resetOdometer(uint16_t maxWait = defaultMaxWait); // Reset the odometer + bool enableOdometer(bool enable = true, uint16_t maxWait = defaultMaxWait); // Enable / disable the odometer + bool getOdometerConfig(uint8_t *flags, uint8_t *odoCfg, uint8_t *cogMaxSpeed, uint8_t *cogMaxPosAcc, uint8_t *velLpGain, uint8_t *cogLpGain, uint16_t maxWait = defaultMaxWait); // Read the odometer configuration + bool setOdometerConfig(uint8_t flags, uint8_t odoCfg, uint8_t cogMaxSpeed, uint8_t cogMaxPosAcc, uint8_t velLpGain, uint8_t cogLpGain, uint16_t maxWait = defaultMaxWait); // Configure the odometer // Enable/Disable individual GNSS systems using UBX-CFG-GNSS // Note: you must leave at least one major GNSS enabled! If in doubt, enable GPS before disabling the others