From 5b51956a67c96ee9aaf84347096ace0f034531f6 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Mon, 23 Dec 2019 16:23:31 -0700 Subject: [PATCH 1/8] Packet ACK is now checked during sendCommand. --- src/SparkFun_Ublox_Arduino_Library.cpp | 57 ++++++++++++++++---------- src/SparkFun_Ublox_Arduino_Library.h | 52 +++++++++++------------ 2 files changed, 61 insertions(+), 48 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 3290236..1e669f4 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -237,7 +237,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (bytesAvailable == 0) { - debugPrintln((char *)"Zero bytes available"); + debugPrintln((char *)"OK: Zero bytes Zero bytes available"); lastCheck = millis(); //Put off checking to avoid I2C bus traffic return (false); } @@ -368,9 +368,7 @@ void SFE_UBLOX_GPS::process(uint8_t incoming) else if (ubxFrameCounter == 2) //Class { packetAck.counter = 0; - packetAck.valid = false; packetCfg.counter = 0; - packetCfg.valid = false; //We can now identify the type of response if (incoming == UBX_CLASS_ACK) @@ -509,14 +507,20 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) //Validate this sentence if (incomingUBX->checksumA == rollingChecksumA && incomingUBX->checksumB == rollingChecksumB) { + incomingUBX->valid = true; + if (_printDebug == true) { - _debugSerial->print(F("Size: ")); + _debugSerial->print(F("Incoming: Size: ")); _debugSerial->print(incomingUBX->len); _debugSerial->print(F(" Received: ")); printPacket(incomingUBX); + if (packetCfg.valid == true) + debugPrintln((char *)"packetCfg now valid"); + if (packetAck.valid == true) + debugPrintln((char *)"packetAck now valid"); } - incomingUBX->valid = true; + processUBXpacket(incomingUBX); //We've got a valid packet, now do something with it } else @@ -584,7 +588,7 @@ void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) } else if (msg->id == UBX_ACK_NACK && msg->payload[0] == packetCfg.cls && msg->payload[1] == packetCfg.id) { - //The ack we just received matched the CLS/ID of last packetCfg sent (or received) + //The ack we just received matched the CLS/ID of last packetCfg sent debugPrintln((char *)"UBX ACK: Not-Acknowledged"); commandAck = UBX_ACK_NACK; } @@ -700,9 +704,10 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (_printDebug == true) { - _debugSerial->print(F("Sending: ")); + _debugSerial->print(F("\nSending: ")); printPacket(&outgoingUBX); } + if (commType == COMM_TYPE_I2C) { if (!sendI2cCommand(outgoingUBX, maxWait)) @@ -897,6 +902,10 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested { if (checkUblox() == true) //See if new data is available. Process bytes as they come in. { + //If we are quering a register the module will send response packet to query as well as a ACK/NACK packet + //So first we check that an ACK came through, then the response + //If we are sending a new register value, the module will simply ACK/NACK our register value. + if (packetAck.valid == true) { //If the packet we just sent was a CFG packet then we'll get an ACK @@ -908,7 +917,7 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (true); // Received an ACK + return (true); //Received an ACK } else if (commandAck == UBX_ACK_NACK) { @@ -918,14 +927,19 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (false); // Received a NACK + return (false); //Received a NACK } } if (packetCfg.valid == true) { - //Did we receive a config packet that matches the cls/id we requested? - if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) + if (commandAck == UBX_ACK_NACK) + { + debugPrintln((char *)"Config valid but command NACK'd"); + return (false); //Received a NACK. Is this command not known? + } + + if (packetCfg.valid == true) { if (_printDebug == true) { @@ -937,11 +951,11 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested } else { - if (_printDebug == true) - { - _debugSerial->print(F("Packet didn't match CLS/ID")); - printPacket(&packetCfg); - } + //We have an ACK but no valid config packet. We must have + //gotten an ACK from sending a new value + + debugPrintln((char *)"New config ACK'd"); + return (true); } } } @@ -1491,12 +1505,11 @@ boolean SFE_UBLOX_GPS::setPortOutput(uint8_t portID, uint8_t outStreamSettings, if (getPortSettings(portID, maxWait) == false) return (false); //Something went wrong. Bail. - // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) - // This is only required because we are doing two sendCommands in quick succession using the same class and ID - waitForResponse(UBX_CLASS_CFG, UBX_CFG_PRT, 100); // But we'll only wait for 100msec max - - //Yes, this is the depreciated way to do it but it's still supported on v27 so it - //covers both ZED-F9P (v27) and SAM-M8Q (v18) + if (commandAck != UBX_ACK_ACK) + { + debugPrintln((char *)"setPortOutput failed to ACK"); + return (false); + } packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 23adb05..01c023a 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -291,30 +291,30 @@ class SFE_UBLOX_GPS boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until and ack is received - // getPVT will only return data once in each navigation cycle. By default, that is once per second. - // Therefore we should set getPVTmaxWait to slightly longer than that. - // If you change the navigation frequency to (e.g.) 4Hz using setNavigationFrequency(4) - // then you should use a shorter maxWait for getPVT. 300msec would be about right: getPVT(300) - // The same is true for getHPPOSLLH. - #define getPVTmaxWait 1100 // Default maxWait for getPVT and all functions which call it - #define getHPPOSLLHmaxWait 1100 // Default maxWait for getHPPOSLLH and all functions which call it - - boolean assumeAutoPVT(boolean enabled, boolean implicitUpdate = true); //In case no config access to the GPS is possible and PVT is send cyclically already - boolean setAutoPVT(boolean enabled, uint16_t maxWait = 250); //Enable/disable automatic PVT reports at the navigation frequency - boolean getPVT(uint16_t maxWait = getPVTmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. +// getPVT will only return data once in each navigation cycle. By default, that is once per second. +// Therefore we should set getPVTmaxWait to slightly longer than that. +// If you change the navigation frequency to (e.g.) 4Hz using setNavigationFrequency(4) +// then you should use a shorter maxWait for getPVT. 300msec would be about right: getPVT(300) +// The same is true for getHPPOSLLH. +#define getPVTmaxWait 1100 // Default maxWait for getPVT and all functions which call it +#define getHPPOSLLHmaxWait 1100 // Default maxWait for getHPPOSLLH and all functions which call it + + boolean assumeAutoPVT(boolean enabled, boolean implicitUpdate = true); //In case no config access to the GPS is possible and PVT is send cyclically already + boolean setAutoPVT(boolean enabled, uint16_t maxWait = 250); //Enable/disable automatic PVT reports at the navigation frequency + boolean getPVT(uint16_t maxWait = getPVTmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. boolean setAutoPVT(boolean enabled, boolean implicitUpdate, uint16_t maxWait = 250); //Enable/disable automatic PVT 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 - boolean getHPPOSLLH(uint16_t maxWait = getHPPOSLLHmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. - - int32_t getLatitude(uint16_t maxWait = getPVTmaxWait); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. - int32_t getLongitude(uint16_t maxWait = getPVTmaxWait); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. - int32_t getAltitude(uint16_t maxWait = getPVTmaxWait); //Returns the current altitude in mm above ellipsoid - int32_t getAltitudeMSL(uint16_t maxWait = getPVTmaxWait); //Returns the current altitude in mm above mean sea level - uint8_t getSIV(uint16_t maxWait = getPVTmaxWait); //Returns number of sats used in fix - uint8_t getFixType(uint16_t maxWait = getPVTmaxWait); //Returns the type of fix: 0=no, 3=3D, 4=GNSS+Deadreckoning + boolean getHPPOSLLH(uint16_t maxWait = getHPPOSLLHmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. + + int32_t getLatitude(uint16_t maxWait = getPVTmaxWait); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. + int32_t getLongitude(uint16_t maxWait = getPVTmaxWait); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. + int32_t getAltitude(uint16_t maxWait = getPVTmaxWait); //Returns the current altitude in mm above ellipsoid + int32_t getAltitudeMSL(uint16_t maxWait = getPVTmaxWait); //Returns the current altitude in mm above mean sea level + uint8_t getSIV(uint16_t maxWait = getPVTmaxWait); //Returns number of sats used in fix + uint8_t getFixType(uint16_t maxWait = getPVTmaxWait); //Returns the type of fix: 0=no, 3=3D, 4=GNSS+Deadreckoning uint8_t getCarrierSolutionType(uint16_t maxWait = getPVTmaxWait); //Returns RTK solution: 0=no, 1=float solution, 2=fixed solution - int32_t getGroundSpeed(uint16_t maxWait = getPVTmaxWait); //Returns speed in mm/s - int32_t getHeading(uint16_t maxWait = getPVTmaxWait); //Returns heading in degrees * 10^-7 - uint16_t getPDOP(uint16_t maxWait = getPVTmaxWait); //Returns positional dillution of precision * 10^-2 + int32_t getGroundSpeed(uint16_t maxWait = getPVTmaxWait); //Returns speed in mm/s + int32_t getHeading(uint16_t maxWait = getPVTmaxWait); //Returns heading in degrees * 10^-7 + uint16_t getPDOP(uint16_t maxWait = getPVTmaxWait); //Returns positional dillution of precision * 10^-2 uint16_t getYear(uint16_t maxWait = getPVTmaxWait); uint8_t getMonth(uint16_t maxWait = getPVTmaxWait); uint8_t getDay(uint16_t maxWait = getPVTmaxWait); @@ -393,10 +393,10 @@ class SFE_UBLOX_GPS //Support for geofences boolean addGeofence(int32_t latitude, int32_t longitude, uint32_t radius, byte confidence = 0, byte pinPolarity = 0, byte pin = 0, uint16_t maxWait = 1100); // Add a new geofence - boolean clearGeofences(uint16_t maxWait = 1100); //Clears all geofences - boolean getGeofenceState(geofenceState ¤tGeofenceState, uint16_t maxWait = 1100); //Returns the combined geofence state - boolean clearAntPIO(uint16_t maxWait = 1100); //Clears the antenna control pin settings to release the PIOs - geofenceParams currentGeofenceParams; // Global to store the geofence parameters + boolean clearGeofences(uint16_t maxWait = 1100); //Clears all geofences + boolean getGeofenceState(geofenceState ¤tGeofenceState, uint16_t maxWait = 1100); //Returns the combined geofence state + boolean clearAntPIO(uint16_t maxWait = 1100); //Clears the antenna control pin settings to release the PIOs + geofenceParams currentGeofenceParams; // Global to store the geofence parameters boolean powerSaveMode(bool power_save = true, uint16_t maxWait = 1100); From 3ae42b5f775471d3ff404705e0fd7a9b2b53ad27 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Mon, 23 Dec 2019 17:01:35 -0700 Subject: [PATCH 2/8] Adding response for cfg packet only in waitForResponse. Many more debug statements added. This may break Uno/Flash compatibility. --- src/SparkFun_Ublox_Arduino_Library.cpp | 36 ++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 1e669f4..1c2b256 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -711,7 +711,10 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (commType == COMM_TYPE_I2C) { if (!sendI2cCommand(outgoingUBX, maxWait)) + { + debugPrintln((char *)"Send I2C Command failed"); return false; + } } else if (commType == COMM_TYPE_SERIAL) { @@ -720,12 +723,15 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (maxWait > 0) { + debugPrintln((char *)"Waiting for response from sendCommand"); + //Give waitForResponse the cls/id to check for return waitForResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response } return true; } +//Returns false if sensor fails to respond to I2C traffic boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) { //Point at 0xFF data register @@ -958,12 +964,31 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested return (true); } } + else if (packetCfg.valid == true) + { + //Packets that are not CFG packets such as getPVT() + Serial.println("Non CFG packet"); + //Did we receive a config packet that matches the cls/id we requested? + if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) + { + debugPrintln((char *)"CLS/ID match!"); + return (true); //We have new data to act upon + } + else + { + if (_printDebug == true) + { + _debugSerial->print(F("Packet didn't match CLS/ID")); + printPacket(&packetCfg); + } + } + } } delayMicroseconds(500); } - debugPrintln((char *)"waitForResponse timeout"); + debugPrintln((char *)"waitForResponse timeout: No valid ack packet received"); return (false); } @@ -2063,6 +2088,7 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) { if (autoPVT && autoPVTImplicitUpdate) { + Serial.println("Check ulbox"); //The GPS is automatically reporting, we just check whether we got unread data checkUblox(); return moduleQueried.all; @@ -2070,10 +2096,12 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) else if (autoPVT && !autoPVTImplicitUpdate) { //Someone else has to call checkUblox for us... + Serial.println("Immediateexi"); return (false); } else { + Serial.println("Poll PVT"); //The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_PVT; @@ -2082,7 +2110,6 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) //The data is parsed as part of processing the response return sendCommand(packetCfg, maxWait); - return (false); //If command send fails then bail } } @@ -2245,8 +2272,13 @@ uint8_t SFE_UBLOX_GPS::getSIV(uint16_t maxWait) //0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=GNSS, 5=Time fix uint8_t SFE_UBLOX_GPS::getFixType(uint16_t maxWait) { + Serial.println("Get fix type"); if (moduleQueried.fixType == false) + { + Serial.println("GetPVT"); + getPVT(maxWait); + } moduleQueried.fixType = false; //Since we are about to give this to user, mark this data as stale moduleQueried.all = false; From d716f9c5de22d32b71a0f1f5a5e18e5354fe43b8 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 12:04:12 -0700 Subject: [PATCH 3/8] Change to ACK and NoAck waitForResponses --- src/SparkFun_Ublox_Arduino_Library.cpp | 382 ++++++++++++++++++------- src/SparkFun_Ublox_Arduino_Library.h | 4 +- 2 files changed, 282 insertions(+), 104 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 1c2b256..5571131 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -228,7 +228,8 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() uint8_t lsb = _i2cPort->read(); if (lsb == 0xFF) { - debugPrintln((char *)"No bytes available"); + //I believe this is a Ublox bug. Device should never present an 0xFF. + debugPrintln((char *)"checkUbloxI2C: Ublox bug, no bytes available"); lastCheck = millis(); //Put off checking to avoid I2C bus traffic return (false); } @@ -237,15 +238,14 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (bytesAvailable == 0) { - debugPrintln((char *)"OK: Zero bytes Zero bytes available"); + debugPrintln((char *)"checkUbloxI2C: OK, zero bytes available"); lastCheck = millis(); //Put off checking to avoid I2C bus traffic return (false); } - //Check for bit error + //Check for undocumented bit error. We found this doing logic scans. //This error is rare but if we incorrectly interpret the first bit of the two 'data available' bytes as 1 - //then we have far too many bytes to check - //Correct back down to + //then we have far too many bytes to check. May be related to I2C setup time violations: https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40 if (bytesAvailable & ((uint16_t)1 << 15)) { //Clear the MSbit @@ -253,7 +253,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (_printDebug == true) { - _debugSerial->print(F("Bytes available error:")); + _debugSerial->print(F("checkUbloxI2C: Bytes available error:")); _debugSerial->println(bytesAvailable); } } @@ -262,8 +262,9 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() { if (_printDebug == true) { - _debugSerial->print(F("Bytes available:")); + _debugSerial->print(F("checkUbloxI2C: Large packet of ")); _debugSerial->println(bytesAvailable); + _debugSerial->print(F("bytes received")); } } @@ -367,14 +368,19 @@ void SFE_UBLOX_GPS::process(uint8_t incoming) currentSentence = NONE; //Something went wrong. Reset. else if (ubxFrameCounter == 2) //Class { - packetAck.counter = 0; - packetCfg.counter = 0; - //We can now identify the type of response if (incoming == UBX_CLASS_ACK) + { + packetAck.counter = 0; + packetAck.valid = false; ubxFrameClass = CLASS_ACK; + } else + { + packetCfg.counter = 0; + packetCfg.valid = false; ubxFrameClass = CLASS_NOT_AN_ACK; + } } ubxFrameCounter++; @@ -515,6 +521,7 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) _debugSerial->print(incomingUBX->len); _debugSerial->print(F(" Received: ")); printPacket(incomingUBX); + if (packetCfg.valid == true) debugPrintln((char *)"packetCfg now valid"); if (packetAck.valid == true) @@ -527,8 +534,6 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) { if (_printDebug == true) { - debugPrintln((char *)"Checksum failed. Response too big?"); - //Drive an external pin to allow for easier logic analyzation if (checksumFailurePin >= 0) { @@ -537,11 +542,7 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) digitalWrite((uint8_t)checksumFailurePin, HIGH); } - _debugSerial->print(F("Size: ")); - _debugSerial->print(incomingUBX->len); - _debugSerial->print(F(" Received: ")); - printPacket(incomingUBX); - + debugPrint((char *)"Checksum failed:"); _debugSerial->print(F(" checksumA: ")); _debugSerial->print(incomingUBX->checksumA); _debugSerial->print(F(" checksumB: ")); @@ -552,6 +553,12 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) _debugSerial->print(F(" rollingChecksumB: ")); _debugSerial->print(rollingChecksumB); _debugSerial->println(); + + _debugSerial->print(F("Failed : ")); + _debugSerial->print(F("Size: ")); + _debugSerial->print(incomingUBX->len); + _debugSerial->print(F(" Received: ")); + printPacket(incomingUBX); } } } @@ -699,7 +706,6 @@ void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) //Given a packet and payload, send everything including CRC bytes via I2C port boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) { - commandAck = UBX_ACK_NONE; //We're about to send a command. Begin waiting for ack. calcChecksum(&outgoingUBX); //Sets checksum A and B bytes of the packet if (_printDebug == true) @@ -723,10 +729,17 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (maxWait > 0) { - debugPrintln((char *)"Waiting for response from sendCommand"); - - //Give waitForResponse the cls/id to check for - return waitForResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + //Depending on what we just sent, either we need to look for an ACK or not + if (outgoingUBX.cls == UBX_CLASS_CFG) + { + debugPrintln((char *)"sendCommand: Waiting for ACK response"); + return waitForACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + } + else + { + debugPrintln((char *)"sendCommand: Waiting for No ACK response"); + return waitForNoACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + } } return true; } @@ -874,10 +887,22 @@ void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) if (_printDebug == true) { _debugSerial->print(F("CLS:")); - _debugSerial->print(packet->cls, HEX); + if (packet->cls == UBX_CLASS_NAV) //1 + _debugSerial->print("NAV"); + else if (packet->cls == UBX_CLASS_ACK) //5 + _debugSerial->print("ACK"); + else if (packet->cls == UBX_CLASS_CFG) //6 + _debugSerial->print("CFG"); + else if (packet->cls == UBX_CLASS_MON) //0x0A + _debugSerial->print("MON"); + else + _debugSerial->print(packet->cls, HEX); _debugSerial->print(F(" ID:")); - _debugSerial->print(packet->id, HEX); + if (packet->cls == UBX_CLASS_NAV && packet->id == UBX_NAV_PVT) + _debugSerial->print("PVT"); + else + _debugSerial->print(packet->id, HEX); _debugSerial->print(F(" Len: 0x")); _debugSerial->print(packet->len, HEX); @@ -896,8 +921,115 @@ void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) //=-=-=-=-=-=-=-= Specific commands =-=-=-=-=-=-=-==-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -//Poll the module until and ack is received -boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +// //Poll the module until and ack is received +// boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +// { +// commandAck = UBX_ACK_NONE; //Reset flag +// packetCfg.valid = false; //This will go true when we receive a response to the packet we sent +// packetAck.valid = false; + +// unsigned long startTime = millis(); +// while (millis() - startTime < maxTime) +// { +// if (checkUblox() == true) //See if new data is available. Process bytes as they come in. +// { +// //If we are quering a register the module will send response packet to query as well as a ACK/NACK packet +// //So first we check that an ACK came through, then the response +// //If we are sending a new register value, the module will simply ACK/NACK our register value. + +// if (packetAck.valid == true) +// { +// //If the packet we just sent was a CFG packet then we'll get an ACK +// if (commandAck == UBX_ACK_ACK) +// { +// if (_printDebug == true) +// { +// _debugSerial->print(F("ACK received after ")); +// _debugSerial->print(millis() - startTime); +// _debugSerial->println(F(" msec")); +// } +// return (true); //Received an ACK +// } +// else if (commandAck == UBX_ACK_NACK) +// { +// if (_printDebug == true) +// { +// _debugSerial->print(F("NACK received after ")); +// _debugSerial->print(millis() - startTime); +// _debugSerial->println(F(" msec")); +// } +// return (false); //Received a NACK +// } +// } + +// if (packetCfg.valid == true) +// { +// if (commandAck == UBX_ACK_NACK) +// { +// debugPrintln((char *)"Config valid but command NACK'd"); +// return (false); //Received a NACK. Is this command not known? +// } + +// if (packetCfg.valid == true) +// { +// if (_printDebug == true) +// { +// _debugSerial->print(F("CLS/ID match after ")); +// _debugSerial->print(millis() - startTime); +// _debugSerial->println(F(" msec")); +// } +// return (true); //If the packet we just sent was a NAV packet then we'll just get data back +// } +// else +// { +// //We have an ACK but no valid config packet. We must have +// //gotten an ACK from sending a new value + +// debugPrintln((char *)"New config ACK'd"); +// return (true); +// } +// } +// else if (packetCfg.valid == true) +// { +// //Packets that are not CFG packets such as getPVT() +// Serial.println("Non CFG packet"); +// //Did we receive a config packet that matches the cls/id we requested? +// if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) +// { +// debugPrintln((char *)"CLS/ID match!"); +// return (true); //We have new data to act upon +// } +// else +// { +// if (_printDebug == true) +// { +// _debugSerial->print(F("Packet didn't match CLS/ID")); +// printPacket(&packetCfg); +// } +// } +// } +// } + +// delayMicroseconds(500); +// } + +// debugPrintln((char *)"waitForResponse timeout: No valid ack packet received"); + +// return (false); +// } + +//When messages from the class CFG are sent to the receiver, the receiver will send an "acknowledge"(UBX - ACK - ACK) or a +//"not acknowledge"(UBX-ACK-NAK) message back to the sender, depending on whether or not the message was processed correctly. +//Some messages from other classes also use the same acknowledgement mechanism. + +//If the packetCfg len is 1, then we are querying the device for data +//If the packetCfg len is >1, then we are sending a new setting + +//Returns true if we got the following: +//* If packetCfg len is 1 and we got and ACK and a valid packetCfg (module is responding with register content) +//* If packetCfg len is >1 and we got an ACK (no valid packetCfg needed, module absorbs new register data) +//Returns false if we timed out, got a NACK (command unknown), or had a CLS/ID mismatch +boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) { commandAck = UBX_ACK_NONE; //Reset flag packetCfg.valid = false; //This will go true when we receive a response to the packet we sent @@ -908,79 +1040,116 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested { if (checkUblox() == true) //See if new data is available. Process bytes as they come in. { - //If we are quering a register the module will send response packet to query as well as a ACK/NACK packet - //So first we check that an ACK came through, then the response - //If we are sending a new register value, the module will simply ACK/NACK our register value. - - if (packetAck.valid == true) + //First we verify the ACK. commandAck will only go UBX_ACK_ACK if CLS/ID matches + if (commandAck == UBX_ACK_ACK) { - //If the packet we just sent was a CFG packet then we'll get an ACK - if (commandAck == UBX_ACK_ACK) + if (_printDebug == true) { - if (_printDebug == true) - { - _debugSerial->print(F("ACK received after ")); - _debugSerial->print(millis() - startTime); - _debugSerial->println(F(" msec")); - } - return (true); //Received an ACK + _debugSerial->print(F("waitForACKResponse: ACK received after ")); + _debugSerial->print(millis() - startTime); + _debugSerial->println(F(" msec")); } - else if (commandAck == UBX_ACK_NACK) + + //Are we expecting data back or just an ACK? + if (packetCfg.len == 1) { - if (_printDebug == true) + //We are expecting a data response so now we verify the response packet was valid + if (packetCfg.valid == true) { - _debugSerial->print(F("NACK received after ")); - _debugSerial->print(millis() - startTime); - _debugSerial->println(F(" msec")); + if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) + { + if (_printDebug == true) + { + _debugSerial->print(F("waitForACKResponse: CLS/ID match after ")); + _debugSerial->print(millis() - startTime); + _debugSerial->println(F(" msec")); + } + return (true); //Received a data and a correct ACK! + } + else + { + //Reset packet and continue checking incoming data for matching cls/id + debugPrintln((char *)"waitForACKResponse: CLS/ID mismatch, continue to wait..."); + packetCfg.valid = false; //This will go true when we receive a response to the packet we sent + } + } + else + { + //We were expecting data but didn't get a valid config packet + debugPrintln((char *)"waitForACKResponse: Invalid config packet"); + return (false); //We got an ACK, we're never going to get valid config data } - return (false); //Received a NACK + } + else + { + //We have sent new data. We expect an ACK but no return config packet. + debugPrintln((char *)"waitForACKResponse: New data successfully sent"); + return (true); //New data successfully sent } } - - if (packetCfg.valid == true) + else if (commandAck == UBX_ACK_NACK) { - if (commandAck == UBX_ACK_NACK) + if (_printDebug == true) { - debugPrintln((char *)"Config valid but command NACK'd"); - return (false); //Received a NACK. Is this command not known? + _debugSerial->print(F("waitForACKResponse: NACK received after ")); + _debugSerial->print(millis() - startTime); + _debugSerial->println(F(" msec")); } + return (false); //Received a NACK + } + } //checkUblox == true - if (packetCfg.valid == true) + delayMicroseconds(500); + } //while (millis() - startTime < maxTime) + + //TODO add check here if config went valid but we never got the following ack + //Through debug warning, This command might not get an ACK + if (packetCfg.valid == true) + { + debugPrintln((char *)"waitForACKResponse: Config was valid but ACK not received"); + } + + if (_printDebug == true) + { + _debugSerial->print(F("waitForACKResponse: timeout after ")); + _debugSerial->print(millis() - startTime); + _debugSerial->println(F(" msec. No ack packet received.")); + } + + return (false); +} + +//For non-CFG queries no ACK is sent so we use this function +//Returns true if we got a config packet full of response data that has CLS/ID match to our query packet +//Returns false if we timed out +boolean SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +{ + packetCfg.valid = false; //This will go true when we receive a response to the packet we sent + packetAck.valid = false; + + unsigned long startTime = millis(); + while (millis() - startTime < maxTime) + { + if (checkUblox() == true) //See if new data is available. Process bytes as they come in. + { + if (packetCfg.valid == true) + { + //Did we receive a config packet that matches the cls/id we requested? + if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) { if (_printDebug == true) { - _debugSerial->print(F("CLS/ID match after ")); + _debugSerial->print(F("waitForNoACKResponse: CLS/ID match after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (true); //If the packet we just sent was a NAV packet then we'll just get data back - } - else - { - //We have an ACK but no valid config packet. We must have - //gotten an ACK from sending a new value - - debugPrintln((char *)"New config ACK'd"); - return (true); - } - } - else if (packetCfg.valid == true) - { - //Packets that are not CFG packets such as getPVT() - Serial.println("Non CFG packet"); - //Did we receive a config packet that matches the cls/id we requested? - if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) - { - debugPrintln((char *)"CLS/ID match!"); return (true); //We have new data to act upon } else { - if (_printDebug == true) - { - _debugSerial->print(F("Packet didn't match CLS/ID")); - printPacket(&packetCfg); - } + //Reset packet and continue checking incoming data for matching cls/id + debugPrintln((char *)"waitForNoACKResponse: CLS/ID mismatch, continue to wait..."); + packetCfg.valid = false; //This will go true when we receive a response to the packet we sent } } } @@ -988,7 +1157,12 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested delayMicroseconds(500); } - debugPrintln((char *)"waitForResponse timeout: No valid ack packet received"); + if (_printDebug == true) + { + _debugSerial->print(F("waitForNoACKResponse: timeout after ")); + _debugSerial->print(millis() - startTime); + _debugSerial->println(F(" msec. No packet received.")); + } return (false); } @@ -1559,7 +1733,7 @@ boolean SFE_UBLOX_GPS::setPortInput(uint8_t portID, uint8_t inStreamSettings, ui // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) // This is only required because we are doing two sendCommands in quick succession using the same class and ID - waitForResponse(UBX_CLASS_CFG, UBX_CFG_PRT, 100); // But we'll only wait for 100msec max + //waitForResponse(UBX_CLASS_CFG, UBX_CFG_PRT, 100); // But we'll only wait for 100msec max packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; @@ -1938,7 +2112,7 @@ boolean SFE_UBLOX_GPS::powerSaveMode(bool power_save, uint16_t maxWait) // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) // This is only required because we are doing two sendCommands in quick succession using the same class and ID - waitForResponse(UBX_CLASS_CFG, UBX_CFG_RXM, 100); // But we'll only wait for 100msec max + //waitForResponse(UBX_CLASS_CFG, UBX_CFG_RXM, 100); // But we'll only wait for 100msec max if (power_save) { @@ -1973,7 +2147,7 @@ boolean SFE_UBLOX_GPS::setDynamicModel(dynModel newDynamicModel, uint16_t maxWai // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) // This is only required because we are doing two sendCommands in quick succession using the same class and ID - waitForResponse(UBX_CLASS_CFG, UBX_CFG_NAV5, 100); // But we'll only wait for 100msec max + //waitForResponse(UBX_CLASS_CFG, UBX_CFG_NAV5, 100); // But we'll only wait for 100msec max payloadCfg[0] = 0x01; // mask: set only the dyn bit (0) payloadCfg[1] = 0x00; // mask @@ -2088,20 +2262,21 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) { if (autoPVT && autoPVTImplicitUpdate) { - Serial.println("Check ulbox"); //The GPS is automatically reporting, we just check whether we got unread data + debugPrintln((char *)"getPVT: Autoreporting"); checkUblox(); return moduleQueried.all; } else if (autoPVT && !autoPVTImplicitUpdate) { //Someone else has to call checkUblox for us... - Serial.println("Immediateexi"); + debugPrintln((char *)"getPVT: Exit immediately"); return (false); } else { - Serial.println("Poll PVT"); + debugPrintln((char *)"getPVT: Polling"); + //The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_PVT; @@ -2272,11 +2447,8 @@ uint8_t SFE_UBLOX_GPS::getSIV(uint16_t maxWait) //0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=GNSS, 5=Time fix uint8_t SFE_UBLOX_GPS::getFixType(uint16_t maxWait) { - Serial.println("Get fix type"); if (moduleQueried.fixType == false) { - Serial.println("GetPVT"); - getPVT(maxWait); } moduleQueried.fixType = false; //Since we are about to give this to user, mark this data as stale @@ -2363,23 +2535,19 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) if (sendCommand(packetCfg, maxWait) == false) return (false); //If command send fails then bail - // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) - // This is only required because we are doing multiple sendCommands in quick succession using the same class and ID - waitForResponse(UBX_CLASS_MON, UBX_MON_VER, 100); // But we'll only wait for 100msec max - //Payload should now contain ~220 characters (depends on module type) - if (_printDebug == true) - { - _debugSerial->print(F("MON VER Payload:")); - for (int location = 0; location < packetCfg.len; location++) - { - if (location % 30 == 0) - _debugSerial->println(); - _debugSerial->write(payloadCfg[location]); - } - _debugSerial->println(); - } + // if (_printDebug == true) + // { + // _debugSerial->print(F("MON VER Payload:")); + // for (int location = 0; location < packetCfg.len; location++) + // { + // if (location % 30 == 0) + // _debugSerial->println(); + // _debugSerial->write(payloadCfg[location]); + // } + // _debugSerial->println(); + // } //We will step through the payload looking at each extension field of 30 bytes for (uint8_t extensionNumber = 0; extensionNumber < 10; extensionNumber++) @@ -2390,7 +2558,15 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) versionHigh = (payloadCfg[(30 * extensionNumber) + 8] - '0') * 10 + (payloadCfg[(30 * extensionNumber) + 9] - '0'); //Convert '18' to 18 versionLow = (payloadCfg[(30 * extensionNumber) + 11] - '0') * 10 + (payloadCfg[(30 * extensionNumber) + 12] - '0'); //Convert '00' to 00 moduleQueried.versionNumber = true; //Mark this data as new - return (true); //Success! + + if (_printDebug == true) + { + _debugSerial->print(F("Protocol version: ")); + _debugSerial->print(versionHigh); + _debugSerial->print(F(".")); + _debugSerial->println(versionLow); + } + return (true); //Success! } } diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 01c023a..3495b9c 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -289,7 +289,9 @@ class SFE_UBLOX_GPS boolean saveConfiguration(uint16_t maxWait = 250); //Save current configuration to flash and BBR (battery backed RAM) boolean factoryDefault(uint16_t maxWait = 250); //Reset module to factory defaults - boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until and ack is received + //boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until an ACK is received + boolean waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet and an ACK is received + boolean waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet is received // getPVT will only return data once in each navigation cycle. By default, that is once per second. // Therefore we should set getPVTmaxWait to slightly longer than that. From 21eb4dd221423177dcce297146035666401cf2d2 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 13:28:18 -0700 Subject: [PATCH 4/8] Change waitForACKResponse to return status rather than bool. Add flushPVT(). --- keywords.txt | 1 + src/SparkFun_Ublox_Arduino_Library.cpp | 271 +++++++++++++------------ src/SparkFun_Ublox_Arduino_Library.h | 39 +++- 3 files changed, 171 insertions(+), 140 deletions(-) diff --git a/keywords.txt b/keywords.txt index 3d8a1f5..ddd571b 100644 --- a/keywords.txt +++ b/keywords.txt @@ -102,6 +102,7 @@ debugPrintln KEYWORD2 factoryReset KEYWORD2 setAutoPVT KEYWORD2 assumeAutoPVT KEYWORD2 +flushPVT KEYWORD2 getYear KEYWORD2 getMonth KEYWORD2 diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 5571131..d6ffef8 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -103,6 +103,56 @@ void SFE_UBLOX_GPS::debugPrintln(char *message) } } +const char *SFE_UBLOX_GPS::statusString(sfe_ublox_status_e stat) +{ + switch (stat) + { + case SFE_UBLOX_STATUS_SUCCESS: + return "Success"; + break; + case SFE_UBLOX_STATUS_FAIL: + return "General Failure"; + break; + case SFE_UBLOX_STATUS_CRC_FAIL: + return "CRC Fail"; + break; + case SFE_UBLOX_STATUS_TIMEOUT: + return "Timeout"; + break; + case SFE_UBLOX_STATUS_COMMAND_UNKNOWN: + return "Command Unknown"; + break; + case SFE_UBLOX_STATUS_OUT_OF_RANGE: + return "Out of range"; + break; + case SFE_UBLOX_STATUS_INVALID_ARG: + return "Invalid Arg"; + break; + case SFE_UBLOX_STATUS_INVALID_OPERATION: + return "Invalid operation"; + break; + case SFE_UBLOX_STATUS_MEM_ERR: + return "Memory Error"; + break; + case SFE_UBLOX_STATUS_HW_ERR: + return "Hardware Error"; + break; + case SFE_UBLOX_STATUS_DATA_SENT: + return "Data Sent"; + break; + case SFE_UBLOX_STATUS_DATA_RECEIVED: + return "Data Received"; + break; + case SFE_UBLOX_STATUS_I2C_COMM_FAILURE: + return "I2C Comm Failure"; + break; + default: + return "Unknown Status"; + break; + } + return "None"; +} + void SFE_UBLOX_GPS::factoryReset() { // Copy default settings to permanent @@ -704,8 +754,10 @@ void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) } //Given a packet and payload, send everything including CRC bytes via I2C port -boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) +sfe_ublox_status_e SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) { + sfe_ublox_status_e retVal = SFE_UBLOX_STATUS_SUCCESS; + calcChecksum(&outgoingUBX); //Sets checksum A and B bytes of the packet if (_printDebug == true) @@ -716,10 +768,11 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (commType == COMM_TYPE_I2C) { - if (!sendI2cCommand(outgoingUBX, maxWait)) + retVal = sendI2cCommand(outgoingUBX, maxWait); + if (retVal != SFE_UBLOX_STATUS_SUCCESS) { debugPrintln((char *)"Send I2C Command failed"); - return false; + return retVal; } } else if (commType == COMM_TYPE_SERIAL) @@ -733,25 +786,25 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (outgoingUBX.cls == UBX_CLASS_CFG) { debugPrintln((char *)"sendCommand: Waiting for ACK response"); - return waitForACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + retVal = waitForACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response } else { debugPrintln((char *)"sendCommand: Waiting for No ACK response"); - return waitForNoACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + retVal = waitForNoACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response } } - return true; + return retVal; } //Returns false if sensor fails to respond to I2C traffic -boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) +sfe_ublox_status_e SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) { //Point at 0xFF data register _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes _i2cPort->write(0xFF); - if (_i2cPort->endTransmission() != 0) //Don't release bus - return (false); //Sensor did not ACK + if (_i2cPort->endTransmission() != 0) //Don't release bus + return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK //Write header bytes _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes @@ -759,10 +812,10 @@ boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) _i2cPort->write(UBX_SYNCH_2); //b _i2cPort->write(outgoingUBX.cls); _i2cPort->write(outgoingUBX.id); - _i2cPort->write(outgoingUBX.len & 0xFF); //LSB - _i2cPort->write(outgoingUBX.len >> 8); //MSB - if (_i2cPort->endTransmission(false) != 0) //Do not release bus - return (false); //Sensor did not ACK + _i2cPort->write(outgoingUBX.len & 0xFF); //LSB + _i2cPort->write(outgoingUBX.len >> 8); //MSB + if (_i2cPort->endTransmission(false) != 0) //Do not release bus + return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK //Write payload. Limit the sends into 32 byte chunks //This code based on ublox: https://forum.u-blox.com/index.php/20528/how-to-use-i2c-to-get-the-nmea-frames @@ -783,8 +836,8 @@ boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) for (uint16_t x = 0; x < len; x++) _i2cPort->write(outgoingUBX.payload[startSpot + x]); //Write a portion of the payload to the bus - if (_i2cPort->endTransmission(false) != 0) //Don't release bus - return (false); //Sensor did not ACK + if (_i2cPort->endTransmission(false) != 0) //Don't release bus + return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK //*outgoingUBX.payload += len; //Move the pointer forward startSpot += len; //Move the pointer forward @@ -800,8 +853,8 @@ boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) //All done transmitting bytes. Release bus. if (_i2cPort->endTransmission() != 0) - return (false); //Sensor did not ACK - return (true); + return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK + return (SFE_UBLOX_STATUS_SUCCESS); } //Given a packet and payload, send everything including CRC bytesA via Serial port @@ -921,103 +974,6 @@ void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) //=-=-=-=-=-=-=-= Specific commands =-=-=-=-=-=-=-==-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// //Poll the module until and ack is received -// boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) -// { -// commandAck = UBX_ACK_NONE; //Reset flag -// packetCfg.valid = false; //This will go true when we receive a response to the packet we sent -// packetAck.valid = false; - -// unsigned long startTime = millis(); -// while (millis() - startTime < maxTime) -// { -// if (checkUblox() == true) //See if new data is available. Process bytes as they come in. -// { -// //If we are quering a register the module will send response packet to query as well as a ACK/NACK packet -// //So first we check that an ACK came through, then the response -// //If we are sending a new register value, the module will simply ACK/NACK our register value. - -// if (packetAck.valid == true) -// { -// //If the packet we just sent was a CFG packet then we'll get an ACK -// if (commandAck == UBX_ACK_ACK) -// { -// if (_printDebug == true) -// { -// _debugSerial->print(F("ACK received after ")); -// _debugSerial->print(millis() - startTime); -// _debugSerial->println(F(" msec")); -// } -// return (true); //Received an ACK -// } -// else if (commandAck == UBX_ACK_NACK) -// { -// if (_printDebug == true) -// { -// _debugSerial->print(F("NACK received after ")); -// _debugSerial->print(millis() - startTime); -// _debugSerial->println(F(" msec")); -// } -// return (false); //Received a NACK -// } -// } - -// if (packetCfg.valid == true) -// { -// if (commandAck == UBX_ACK_NACK) -// { -// debugPrintln((char *)"Config valid but command NACK'd"); -// return (false); //Received a NACK. Is this command not known? -// } - -// if (packetCfg.valid == true) -// { -// if (_printDebug == true) -// { -// _debugSerial->print(F("CLS/ID match after ")); -// _debugSerial->print(millis() - startTime); -// _debugSerial->println(F(" msec")); -// } -// return (true); //If the packet we just sent was a NAV packet then we'll just get data back -// } -// else -// { -// //We have an ACK but no valid config packet. We must have -// //gotten an ACK from sending a new value - -// debugPrintln((char *)"New config ACK'd"); -// return (true); -// } -// } -// else if (packetCfg.valid == true) -// { -// //Packets that are not CFG packets such as getPVT() -// Serial.println("Non CFG packet"); -// //Did we receive a config packet that matches the cls/id we requested? -// if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) -// { -// debugPrintln((char *)"CLS/ID match!"); -// return (true); //We have new data to act upon -// } -// else -// { -// if (_printDebug == true) -// { -// _debugSerial->print(F("Packet didn't match CLS/ID")); -// printPacket(&packetCfg); -// } -// } -// } -// } - -// delayMicroseconds(500); -// } - -// debugPrintln((char *)"waitForResponse timeout: No valid ack packet received"); - -// return (false); -// } - //When messages from the class CFG are sent to the receiver, the receiver will send an "acknowledge"(UBX - ACK - ACK) or a //"not acknowledge"(UBX-ACK-NAK) message back to the sender, depending on whether or not the message was processed correctly. //Some messages from other classes also use the same acknowledgement mechanism. @@ -1029,7 +985,7 @@ void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) //* If packetCfg len is 1 and we got and ACK and a valid packetCfg (module is responding with register content) //* If packetCfg len is >1 and we got an ACK (no valid packetCfg needed, module absorbs new register data) //Returns false if we timed out, got a NACK (command unknown), or had a CLS/ID mismatch -boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +sfe_ublox_status_e SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) { commandAck = UBX_ACK_NONE; //Reset flag packetCfg.valid = false; //This will go true when we receive a response to the packet we sent @@ -1064,7 +1020,7 @@ boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t reques _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (true); //Received a data and a correct ACK! + return (SFE_UBLOX_STATUS_DATA_RECEIVED); //Received a data and a correct ACK! } else { @@ -1077,14 +1033,14 @@ boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t reques { //We were expecting data but didn't get a valid config packet debugPrintln((char *)"waitForACKResponse: Invalid config packet"); - return (false); //We got an ACK, we're never going to get valid config data + return (SFE_UBLOX_STATUS_FAIL); //We got an ACK, we're never going to get valid config data } } else { //We have sent new data. We expect an ACK but no return config packet. debugPrintln((char *)"waitForACKResponse: New data successfully sent"); - return (true); //New data successfully sent + return (SFE_UBLOX_STATUS_DATA_SENT); //New data successfully sent } } else if (commandAck == UBX_ACK_NACK) @@ -1095,7 +1051,7 @@ boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t reques _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (false); //Received a NACK + return (SFE_UBLOX_STATUS_COMMAND_UNKNOWN); //Received a NACK } } //checkUblox == true @@ -1116,26 +1072,29 @@ boolean SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uint8_t reques _debugSerial->println(F(" msec. No ack packet received.")); } - return (false); + return (SFE_UBLOX_STATUS_TIMEOUT); } //For non-CFG queries no ACK is sent so we use this function //Returns true if we got a config packet full of response data that has CLS/ID match to our query packet //Returns false if we timed out -boolean SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) +sfe_ublox_status_e SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) { packetCfg.valid = false; //This will go true when we receive a response to the packet we sent packetAck.valid = false; + packetCfg.cls = 255; + packetCfg.id = 255; unsigned long startTime = millis(); while (millis() - startTime < maxTime) { if (checkUblox() == true) //See if new data is available. Process bytes as they come in. { - if (packetCfg.valid == true) + //Did we receive a config packet that matches the cls/id we requested? + if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) { - //Did we receive a config packet that matches the cls/id we requested? - if (packetCfg.cls == requestedClass && packetCfg.id == requestedID) + //This packet might be good or it might be CRC corrupt + if (packetCfg.valid == true) { if (_printDebug == true) { @@ -1143,15 +1102,31 @@ boolean SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, uint8_t requ _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } - return (true); //We have new data to act upon + return (SFE_UBLOX_STATUS_DATA_RECEIVED); //We have new data to act upon } else { - //Reset packet and continue checking incoming data for matching cls/id - debugPrintln((char *)"waitForNoACKResponse: CLS/ID mismatch, continue to wait..."); - packetCfg.valid = false; //This will go true when we receive a response to the packet we sent + debugPrintln((char *)"waitForNoACKResponse: CLS/ID match but failed CRC"); + return (SFE_UBLOX_STATUS_CRC_FAIL); //We got the right packet but it was corrupt } } + else if (packetCfg.cls < 255 && packetCfg.id < 255) + { + //Reset packet and continue checking incoming data for matching cls/id + if (_printDebug == true) + { + debugPrint((char *)"waitForNoACKResponse: CLS/ID mismatch: "); + _debugSerial->print(F("CLS: ")); + _debugSerial->print(packetCfg.cls, HEX); + _debugSerial->print(F(" ID: ")); + _debugSerial->print(packetCfg.id, HEX); + _debugSerial->println(); + } + + packetCfg.valid = false; //This will go true when we receive a response to the packet we sent + packetCfg.cls = 255; + packetCfg.id = 255; + } } delayMicroseconds(500); @@ -1164,7 +1139,7 @@ boolean SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, uint8_t requ _debugSerial->println(F(" msec. No packet received.")); } - return (false); + return (SFE_UBLOX_STATUS_TIMEOUT); } //Save current configuration to flash and BBR (battery backed RAM) @@ -2284,7 +2259,17 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) //packetCfg.startingSpot = 20; //Begin listening at spot 20 so we can record up to 20+MAX_PAYLOAD_SIZE = 84 bytes Note:now hard-coded in processUBX //The data is parsed as part of processing the response - return sendCommand(packetCfg, maxWait); + sfe_ublox_status_e retVal = sendCommand(packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (_printDebug == true) + { + _debugSerial->print(F("getPVT retVal: ")); + _debugSerial->println(statusString(retVal)); + } + return (false); } } @@ -2573,6 +2558,32 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) return (false); //We failed } +//Mark all the PVT data as read/stale. This is handy to get data alignment after CRC failure +void SFE_UBLOX_GPS::flushPVT() +{ + //Mark all datums as stale (read before) + moduleQueried.gpsiTOW = false; + moduleQueried.gpsYear = false; + moduleQueried.gpsMonth = false; + moduleQueried.gpsDay = false; + moduleQueried.gpsHour = false; + moduleQueried.gpsMinute = false; + moduleQueried.gpsSecond = false; + moduleQueried.gpsNanosecond = false; + + moduleQueried.all = false; + moduleQueried.longitude = false; + moduleQueried.latitude = false; + moduleQueried.altitude = false; + moduleQueried.altitudeMSL = false; + moduleQueried.SIV = false; + moduleQueried.fixType = false; + moduleQueried.carrierSolution = false; + moduleQueried.groundSpeed = false; + moduleQueried.headingOfMotion = false; + moduleQueried.pDOP = false; +} + //Relative Positioning Information in NED frame //Returns true if commands was successful boolean SFE_UBLOX_GPS::getRELPOSNED(uint16_t maxWait) diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 3495b9c..b574cab 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -80,6 +80,24 @@ //Leave set to -1 if not needed const int checksumFailurePin = -1; +// Global Status Returns +typedef enum +{ + SFE_UBLOX_STATUS_SUCCESS, + SFE_UBLOX_STATUS_FAIL, + SFE_UBLOX_STATUS_CRC_FAIL, + SFE_UBLOX_STATUS_TIMEOUT, + SFE_UBLOX_STATUS_COMMAND_UNKNOWN, + SFE_UBLOX_STATUS_OUT_OF_RANGE, + SFE_UBLOX_STATUS_INVALID_ARG, + SFE_UBLOX_STATUS_INVALID_OPERATION, + SFE_UBLOX_STATUS_MEM_ERR, + SFE_UBLOX_STATUS_HW_ERR, + SFE_UBLOX_STATUS_DATA_SENT, + SFE_UBLOX_STATUS_DATA_RECEIVED, + SFE_UBLOX_STATUS_I2C_COMM_FAILURE, +} sfe_ublox_status_e; + //Registers const uint8_t UBX_SYNCH_1 = 0xB5; const uint8_t UBX_SYNCH_2 = 0x62; @@ -270,9 +288,9 @@ class SFE_UBLOX_GPS void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags void processNMEA(char incoming) __attribute__((weak)); //Given a NMEA character, do something with it. User can overwrite if desired to use something like tinyGPS or MicroNMEA libraries - void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages - boolean sendCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); //Given a packet and payload, send everything including CRC bytes, return true if we got a response - boolean sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); + void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages + sfe_ublox_status_e sendCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); //Given a packet and payload, send everything including CRC bytes, return true if we got a response + sfe_ublox_status_e sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); void sendSerialCommand(ubxPacket outgoingUBX); void printPacket(ubxPacket *packet); //Useful for debugging @@ -289,9 +307,8 @@ class SFE_UBLOX_GPS boolean saveConfiguration(uint16_t maxWait = 250); //Save current configuration to flash and BBR (battery backed RAM) boolean factoryDefault(uint16_t maxWait = 250); //Reset module to factory defaults - //boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until an ACK is received - boolean waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet and an ACK is received - boolean waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet is received + sfe_ublox_status_e waitForACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet and an ACK is received + sfe_ublox_status_e waitForNoACKResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until a config packet is received // getPVT will only return data once in each navigation cycle. By default, that is once per second. // Therefore we should set getPVTmaxWait to slightly longer than that. @@ -306,6 +323,7 @@ class SFE_UBLOX_GPS boolean getPVT(uint16_t maxWait = getPVTmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. boolean setAutoPVT(boolean enabled, boolean implicitUpdate, uint16_t maxWait = 250); //Enable/disable automatic PVT 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 boolean getHPPOSLLH(uint16_t maxWait = getHPPOSLLHmaxWait); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. If autoPVT is disabled, performs an explicit poll and waits, if enabled does not block. Retruns true if new PVT is available. + void flushPVT(); //Mark all the PVT data as read/stale. This is handy to get data alignment after CRC failure int32_t getLatitude(uint16_t maxWait = getPVTmaxWait); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. int32_t getLongitude(uint16_t maxWait = getPVTmaxWait); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. @@ -388,10 +406,11 @@ class SFE_UBLOX_GPS boolean getRELPOSNED(uint16_t maxWait = 1100); //Get Relative Positioning Information of the NED frame - void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages - void disableDebugging(void); //Turn off debug statements - void debugPrint(char *message); //Safely print debug statements - void debugPrintln(char *message); //Safely print debug statements + void enableDebugging(Stream &debugPort = Serial); //Given a port to print to, enable debug messages + void disableDebugging(void); //Turn off debug statements + void debugPrint(char *message); //Safely print debug statements + void debugPrintln(char *message); //Safely print debug statements + const char *statusString(sfe_ublox_status_e stat); //Pretty print the return value //Support for geofences boolean addGeofence(int32_t latitude, int32_t longitude, uint32_t radius, byte confidence = 0, byte pinPolarity = 0, byte pin = 0, uint16_t maxWait = 1100); // Add a new geofence From f4cb57e6552dec45024f21c8f7ac6c738908f4fc Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 13:28:50 -0700 Subject: [PATCH 5/8] Change high speed examples to use getPVT() polling. --- .../Example2_AutoPVT_ExplicitUpdate.ino | 23 +++++++++++-------- .../Example16_Nanosecond_MaxOutput.ino | 15 ++++++------ .../Example16_PartialSecond_MaxOutput.ino | 22 ++++++++++-------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/examples/Example13_PVT/Example2_AutoPVT_ExplicitUpdate/Example2_AutoPVT_ExplicitUpdate.ino b/examples/Example13_PVT/Example2_AutoPVT_ExplicitUpdate/Example2_AutoPVT_ExplicitUpdate.ino index 3919959..5b5fb32 100644 --- a/examples/Example13_PVT/Example2_AutoPVT_ExplicitUpdate/Example2_AutoPVT_ExplicitUpdate.ino +++ b/examples/Example13_PVT/Example2_AutoPVT_ExplicitUpdate/Example2_AutoPVT_ExplicitUpdate.ino @@ -35,7 +35,8 @@ SFE_UBLOX_GPS myGPS; void setup() { Serial.begin(115200); - while (!Serial); //Wait for user to open terminal + while (!Serial) + ; //Wait for user to open terminal Serial.println("SparkFun Ublox Example"); Wire.begin(); @@ -43,17 +44,18 @@ void setup() if (myGPS.begin() == false) //Connect to the Ublox module using Wire port { Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); - while (1); + while (1) + ; } myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) - myGPS.setNavigationFrequency(2); //Produce two solutions per second - myGPS.setAutoPVT(true, false); //Tell the GPS to "send" each solution and the lib not to update stale data implicitly - myGPS.saveConfiguration(); //Save the current settings to flash and BBR + myGPS.setNavigationFrequency(2); //Produce two solutions per second + myGPS.setAutoPVT(true, false); //Tell the GPS to "send" each solution and the lib not to update stale data implicitly + myGPS.saveConfiguration(); //Save the current settings to flash and BBR } /* - Calling getPVT would return false now (compare to Example 13 where it would return true), so we just use the data provided + Calling getPVT would return false now (compare to previous example where it would return true), so we just use the data provided If you are using a threaded OS eg. FreeRTOS on an ESP32, the explicit mode of autoPVT allows you to use the data provided on both cores and inside multiple threads The data update in background creates an inconsistent state, but that should not cause issues for most applications as they usually won't change the GPS location significantly within a 2Hz - 5Hz update rate. Also you could oversample (10Hz - 20Hz) the data to smooth out such issues... @@ -62,12 +64,14 @@ void loop() { static uint16_t counter = 0; - if (counter % 10 == 0) { + if (counter % 10 == 0) + { // update your AHRS filter here for a ~100Hz update rate // GPS data will be quasi static but data from your IMU will be changing } // debug output each half second - if (counter % 500 == 0) { + if (counter % 500 == 0) + { Serial.println(); long latitude = myGPS.getLatitude(); Serial.print(F("Lat: ")); @@ -90,7 +94,8 @@ void loop() Serial.println(); } // call checkUblox all 50ms to capture the gps data - if (counter % 50 == 0) { + if (counter % 50 == 0) + { myGPS.checkUblox(); } delay(1); diff --git a/examples/Example16_Nanosecond_MaxOutput/Example16_Nanosecond_MaxOutput.ino b/examples/Example16_Nanosecond_MaxOutput/Example16_Nanosecond_MaxOutput.ino index 9ec5eaa..3862c6d 100644 --- a/examples/Example16_Nanosecond_MaxOutput/Example16_Nanosecond_MaxOutput.ino +++ b/examples/Example16_Nanosecond_MaxOutput/Example16_Nanosecond_MaxOutput.ino @@ -33,14 +33,14 @@ long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox m void setup() { - Serial.begin(500000); //Increase serial speed to maximize + Serial.begin(500000); //Increase serial speed to maximize while (!Serial) ; //Wait for user to open terminal Serial.println("SparkFun Ublox Example"); Wire.begin(); Wire.setClock(400000); - + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port { Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); @@ -53,18 +53,16 @@ void setup() //myGPS.enableDebugging(); //Enable debug messages over Serial (default) - myGPS.setNavigationFrequency(10); //Set output to 10 times a second + myGPS.setNavigationFrequency(10); //Set output to 10 times a second byte rate = myGPS.getNavigationFrequency(); //Get the update rate of this module Serial.print("Current update rate:"); Serial.println(rate); - } void loop() { - //Query module only every second. Doing it more often will just cause I2C traffic. - //The module only responds when a new position is available - if (millis() - lastTime > 10) + // Calling getPVT returns true if there actually is a fresh navigation solution available. + if (myGPS.getPVT()) { lastTime = millis(); //Update the timer @@ -86,6 +84,7 @@ void loop() Serial.print(F(" SIV: ")); Serial.print(SIV); + Serial.print(" "); Serial.print(myGPS.getYear()); Serial.print("-"); Serial.print(myGPS.getMonth()); @@ -100,6 +99,8 @@ void loop() Serial.print("."); Serial.print(myGPS.getNanosecond()); + myGPS.flushPVT(); + Serial.println(); } } diff --git a/examples/Example16_PartialSecond_MaxOutput/Example16_PartialSecond_MaxOutput.ino b/examples/Example16_PartialSecond_MaxOutput/Example16_PartialSecond_MaxOutput.ino index 49a2031..45c226f 100644 --- a/examples/Example16_PartialSecond_MaxOutput/Example16_PartialSecond_MaxOutput.ino +++ b/examples/Example16_PartialSecond_MaxOutput/Example16_PartialSecond_MaxOutput.ino @@ -32,14 +32,14 @@ long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox m void setup() { - Serial.begin(500000); //Increase serial speed to maximize + Serial.begin(500000); //Increase serial speed to maximize while (!Serial) ; //Wait for user to open terminal Serial.println("SparkFun Ublox Example"); Wire.begin(); Wire.setClock(400000); - + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port { Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); @@ -51,12 +51,12 @@ void setup() //myGPS.enableDebugging(); //Enable debug messages over Serial (default) - myGPS.setNavigationFrequency(10); //Set output to 10 times a second + myGPS.setNavigationFrequency(10); //Set output to 10 times a second byte rate = myGPS.getNavigationFrequency(); //Get the update rate of this module Serial.print("Current update rate:"); Serial.println(rate); - - myGPS.saveConfiguration(); //Save the current settings to flash and BBR + + myGPS.saveConfiguration(); //Save the current settings to flash and BBR pinMode(2, OUTPUT); //For debug capture digitalWrite(2, HIGH); @@ -64,8 +64,8 @@ void setup() void loop() { - //Query module very often to get max update rate - if (millis() - lastTime > 10) + // Calling getPVT returns true if there actually is a fresh navigation solution available. + if (myGPS.getPVT()) { lastTime = millis(); //Update the timer @@ -102,11 +102,13 @@ void loop() Serial.print("."); //Pretty print leading zeros int mseconds = myGPS.getMillisecond(); - if(mseconds < 100) Serial.print("0"); - if(mseconds < 10) Serial.print("0"); + if (mseconds < 100) + Serial.print("0"); + if (mseconds < 10) + Serial.print("0"); Serial.print(mseconds); - Serial.print(" nanoSeconds: "); + Serial.print(" nanoSeconds: "); Serial.print(myGPS.getNanosecond()); Serial.println(); From eea836917c54da75e6ce9211c8a40a2e072eebd7 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 13:52:59 -0700 Subject: [PATCH 6/8] Fix typo --- src/SparkFun_Ublox_Arduino_Library.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index d6ffef8..0aec0e9 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -313,8 +313,8 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (_printDebug == true) { _debugSerial->print(F("checkUbloxI2C: Large packet of ")); - _debugSerial->println(bytesAvailable); - _debugSerial->print(F("bytes received")); + _debugSerial->print(bytesAvailable); + _debugSerial->println(F(" bytes received")); } } From 61e2e2e7367370e02a074209bda6097df8e0204a Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 14:00:18 -0700 Subject: [PATCH 7/8] Flash guarding the print statements This allows this lib to run on Unos. --- src/SparkFun_Ublox_Arduino_Library.cpp | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 0aec0e9..5c4d3aa 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -279,7 +279,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (lsb == 0xFF) { //I believe this is a Ublox bug. Device should never present an 0xFF. - debugPrintln((char *)"checkUbloxI2C: Ublox bug, no bytes available"); + debugPrintln((char *)F("checkUbloxI2C: Ublox bug, no bytes available")); lastCheck = millis(); //Put off checking to avoid I2C bus traffic return (false); } @@ -288,7 +288,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (bytesAvailable == 0) { - debugPrintln((char *)"checkUbloxI2C: OK, zero bytes available"); + debugPrintln((char *)F("checkUbloxI2C: OK, zero bytes available")); lastCheck = millis(); //Put off checking to avoid I2C bus traffic return (false); } @@ -345,7 +345,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() { if (incoming == 0x7F) { - debugPrintln((char *)"Module not ready with data"); + debugPrintln((char *)F("Module not ready with data")); delay(5); //In logic analyzation, the module starting responding after 1.48ms goto TRY_AGAIN; } @@ -573,9 +573,9 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) printPacket(incomingUBX); if (packetCfg.valid == true) - debugPrintln((char *)"packetCfg now valid"); + debugPrintln((char *)F("packetCfg now valid")); if (packetAck.valid == true) - debugPrintln((char *)"packetAck now valid"); + debugPrintln((char *)F("packetAck now valid")); } processUBXpacket(incomingUBX); //We've got a valid packet, now do something with it @@ -592,7 +592,7 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) digitalWrite((uint8_t)checksumFailurePin, HIGH); } - debugPrint((char *)"Checksum failed:"); + debugPrint((char *)F("Checksum failed:")); _debugSerial->print(F(" checksumA: ")); _debugSerial->print(incomingUBX->checksumA); _debugSerial->print(F(" checksumB: ")); @@ -640,13 +640,13 @@ void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) if (msg->id == UBX_ACK_ACK && msg->payload[0] == packetCfg.cls && msg->payload[1] == packetCfg.id) { //The ack we just received matched the CLS/ID of last packetCfg sent (or received) - debugPrintln((char *)"UBX ACK: Command sent/ack'd successfully"); + debugPrintln((char *)F("UBX ACK: Command sent/ack'd successfully")); commandAck = UBX_ACK_ACK; } else if (msg->id == UBX_ACK_NACK && msg->payload[0] == packetCfg.cls && msg->payload[1] == packetCfg.id) { //The ack we just received matched the CLS/ID of last packetCfg sent - debugPrintln((char *)"UBX ACK: Not-Acknowledged"); + debugPrintln((char *)F("UBX ACK: Not-Acknowledged")); commandAck = UBX_ACK_NACK; } break; @@ -771,7 +771,7 @@ sfe_ublox_status_e SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t ma retVal = sendI2cCommand(outgoingUBX, maxWait); if (retVal != SFE_UBLOX_STATUS_SUCCESS) { - debugPrintln((char *)"Send I2C Command failed"); + debugPrintln((char *)F("Send I2C Command failed")); return retVal; } } @@ -785,12 +785,12 @@ sfe_ublox_status_e SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t ma //Depending on what we just sent, either we need to look for an ACK or not if (outgoingUBX.cls == UBX_CLASS_CFG) { - debugPrintln((char *)"sendCommand: Waiting for ACK response"); + debugPrintln((char *)F("sendCommand: Waiting for ACK response")); retVal = waitForACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response } else { - debugPrintln((char *)"sendCommand: Waiting for No ACK response"); + debugPrintln((char *)F("sendCommand: Waiting for No ACK response")); retVal = waitForNoACKResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response } } @@ -1025,21 +1025,21 @@ sfe_ublox_status_e SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uin else { //Reset packet and continue checking incoming data for matching cls/id - debugPrintln((char *)"waitForACKResponse: CLS/ID mismatch, continue to wait..."); + debugPrintln((char *)F("waitForACKResponse: CLS/ID mismatch, continue to wait...")); packetCfg.valid = false; //This will go true when we receive a response to the packet we sent } } else { //We were expecting data but didn't get a valid config packet - debugPrintln((char *)"waitForACKResponse: Invalid config packet"); + debugPrintln((char *)F("waitForACKResponse: Invalid config packet")); return (SFE_UBLOX_STATUS_FAIL); //We got an ACK, we're never going to get valid config data } } else { //We have sent new data. We expect an ACK but no return config packet. - debugPrintln((char *)"waitForACKResponse: New data successfully sent"); + debugPrintln((char *)F("waitForACKResponse: New data successfully sent")); return (SFE_UBLOX_STATUS_DATA_SENT); //New data successfully sent } } @@ -1062,7 +1062,7 @@ sfe_ublox_status_e SFE_UBLOX_GPS::waitForACKResponse(uint8_t requestedClass, uin //Through debug warning, This command might not get an ACK if (packetCfg.valid == true) { - debugPrintln((char *)"waitForACKResponse: Config was valid but ACK not received"); + debugPrintln((char *)F("waitForACKResponse: Config was valid but ACK not received")); } if (_printDebug == true) @@ -1106,7 +1106,7 @@ sfe_ublox_status_e SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, u } else { - debugPrintln((char *)"waitForNoACKResponse: CLS/ID match but failed CRC"); + debugPrintln((char *)F("waitForNoACKResponse: CLS/ID match but failed CRC")); return (SFE_UBLOX_STATUS_CRC_FAIL); //We got the right packet but it was corrupt } } @@ -1115,7 +1115,7 @@ sfe_ublox_status_e SFE_UBLOX_GPS::waitForNoACKResponse(uint8_t requestedClass, u //Reset packet and continue checking incoming data for matching cls/id if (_printDebug == true) { - debugPrint((char *)"waitForNoACKResponse: CLS/ID mismatch: "); + debugPrint((char *)F("waitForNoACKResponse: CLS/ID mismatch: ")); _debugSerial->print(F("CLS: ")); _debugSerial->print(packetCfg.cls, HEX); _debugSerial->print(F(" ID: ")); @@ -1681,7 +1681,7 @@ boolean SFE_UBLOX_GPS::setPortOutput(uint8_t portID, uint8_t outStreamSettings, if (commandAck != UBX_ACK_ACK) { - debugPrintln((char *)"setPortOutput failed to ACK"); + debugPrintln((char *)F("setPortOutput failed to ACK")); return (false); } @@ -2072,7 +2072,7 @@ boolean SFE_UBLOX_GPS::powerSaveMode(bool power_save, uint16_t maxWait) */ if (protVer >= 27) { - debugPrintln((char *)"powerSaveMode (UBX-CFG-RXM) is not supported by this protocol version"); + debugPrintln((char *)F("powerSaveMode (UBX-CFG-RXM) is not supported by this protocol version")); return (false); } @@ -2238,19 +2238,19 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) if (autoPVT && autoPVTImplicitUpdate) { //The GPS is automatically reporting, we just check whether we got unread data - debugPrintln((char *)"getPVT: Autoreporting"); + debugPrintln((char *)F("getPVT: Autoreporting")); checkUblox(); return moduleQueried.all; } else if (autoPVT && !autoPVTImplicitUpdate) { //Someone else has to call checkUblox for us... - debugPrintln((char *)"getPVT: Exit immediately"); + debugPrintln((char *)F("getPVT: Exit immediately")); return (false); } else { - debugPrintln((char *)"getPVT: Polling"); + debugPrintln((char *)F("getPVT: Polling")); //The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; From 9e9ea848d1ffec24398628b580023b390779d770 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Fri, 27 Dec 2019 14:14:50 -0700 Subject: [PATCH 8/8] Remove old waitForResponses --- src/SparkFun_Ublox_Arduino_Library.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 5c4d3aa..121f7d8 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -1706,10 +1706,6 @@ boolean SFE_UBLOX_GPS::setPortInput(uint8_t portID, uint8_t inStreamSettings, ui if (getPortSettings(portID, maxWait) == false) return (false); //Something went wrong. Bail. - // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) - // This is only required because we are doing two sendCommands in quick succession using the same class and ID - //waitForResponse(UBX_CLASS_CFG, UBX_CFG_PRT, 100); // But we'll only wait for 100msec max - packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; @@ -2085,10 +2081,6 @@ boolean SFE_UBLOX_GPS::powerSaveMode(bool power_save, uint16_t maxWait) if (sendCommand(packetCfg, maxWait) == false) //Ask module for the current power management settings. Loads into payloadCfg. return (false); - // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) - // This is only required because we are doing two sendCommands in quick succession using the same class and ID - //waitForResponse(UBX_CLASS_CFG, UBX_CFG_RXM, 100); // But we'll only wait for 100msec max - if (power_save) { payloadCfg[1] = 1; // Power Save Mode @@ -2120,10 +2112,6 @@ boolean SFE_UBLOX_GPS::setDynamicModel(dynModel newDynamicModel, uint16_t maxWai if (sendCommand(packetCfg, maxWait) == false) //Ask module for the current navigation model settings. Loads into payloadCfg. return (false); - // Let's make sure we wait for the ACK too (sendCommand will have returned as soon as the module sent its response) - // This is only required because we are doing two sendCommands in quick succession using the same class and ID - //waitForResponse(UBX_CLASS_CFG, UBX_CFG_NAV5, 100); // But we'll only wait for 100msec max - payloadCfg[0] = 0x01; // mask: set only the dyn bit (0) payloadCfg[1] = 0x00; // mask payloadCfg[2] = newDynamicModel; // dynModel