diff --git a/keywords.txt b/keywords.txt index 2463782..823a25b 100644 --- a/keywords.txt +++ b/keywords.txt @@ -15,6 +15,7 @@ Um980Result KEYWORD1 begin KEYWORD2 isConnected KEYWORD2 +isBlocking KEYWORD2 enableDebugging KEYWORD2 disableDebugging KEYWORD2 @@ -110,6 +111,9 @@ getVersion KEYWORD2 getCompileTime KEYWORD2 getVersionFull KEYWORD2 +enableBinaryBeforeFix KEYWORD2 +disableBinaryBeforeFix KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### diff --git a/src/SparkFun_Unicore_GNSS_Arduino_Library.cpp b/src/SparkFun_Unicore_GNSS_Arduino_Library.cpp index c2386ab..2a206a7 100644 --- a/src/SparkFun_Unicore_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_Unicore_GNSS_Arduino_Library.cpp @@ -81,14 +81,13 @@ //---------------------------------------- // parserTable index values -#define UM980_NMEA_PARSER_INDEX 0 -#define UM980_UNICORE_HASH_PARSER_INDEX 1 -#define UM980_RTCM_PARSER_INDEX 2 -#define UM980_UNICORE_BINARY_PARSER_INDEX 3 +#define UM980_NMEA_PARSER_INDEX 0 +#define UM980_UNICORE_HASH_PARSER_INDEX 1 +#define UM980_RTCM_PARSER_INDEX 2 +#define UM980_UNICORE_BINARY_PARSER_INDEX 3 // Build the table listing all of the parsers -SEMP_PARSE_ROUTINE const parserTable[] = -{ +SEMP_PARSE_ROUTINE const parserTable[] = { sempNmeaPreamble, sempUnicoreHashPreamble, sempRtcmPreamble, @@ -96,8 +95,7 @@ SEMP_PARSE_ROUTINE const parserTable[] = }; const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]); -const char * const parserNames[] = -{ +const char *const parserNames[] = { "UN980 NMEA Parser", "UM980 Unicore Hash (#) Parser", "UM980 RTCM Parser", @@ -106,7 +104,7 @@ const char * const parserNames[] = const int parserNameCount = sizeof(parserNames) / sizeof(parserNames[0]); // Account for the largest message -#define BUFFER_LENGTH 3000 +#define BUFFER_LENGTH 3000 //---------------------------------------- // Globals @@ -154,14 +152,14 @@ bool badNmeaChecksum(SEMP_PARSE_STATE *parse) // Display bad checksums if ((!badChecksum) && ptrUM980->_printBadChecksum) { - ptrUM980->debugPrintf("UM980: Message improperly includes %c in checksum", parse->buffer[0]); + ptrUM980->debugPrintf("Unicore Lib: Message improperly includes %c in checksum", parse->buffer[0]); ptrUM980->dumpBuffer(parse->buffer, parse->length); } return badChecksum; } // Translate the state value into an ASCII state name -const char * um980GetStateName(SEMP_PARSE_STATE *parse) +const char *um980GetStateName(SEMP_PARSE_STATE *parse) { const char *name; @@ -224,14 +222,12 @@ bool UM980::begin(HardwareSerial &serialPort, Print *parserDebug, Print *parserE _hwSerialPort = &serialPort; // Initialize the parser - _sempParse = sempBeginParser(parserTable, parserCount, - parserNames, parserNameCount, - 0, BUFFER_LENGTH, um980ProcessMessage, - "SFE_Unicore_GNSS_Library", parserError, - parserDebug, badNmeaChecksum); + _sempParse = + sempBeginParser(parserTable, parserCount, parserNames, parserNameCount, 0, BUFFER_LENGTH, um980ProcessMessage, + "SFE_Unicore_GNSS_Library", parserError, parserDebug, badNmeaChecksum); if (!_sempParse) { - debugPrintf("UM980: Failed to initialize the parser!"); + debugPrintf("Unicore Lib: Failed to initialize the parser!"); return false; } @@ -277,6 +273,14 @@ bool UM980::isConnected() return (false); } +// If another task outside of this library is accessing the same Serial hardware, it can +// check to see if this library currently needs exclusive read/write access for a short period. +// If isBlocking is true, external consumers should not read/write to the Serial hardware +bool UM980::isBlocking() +{ + return (unicoreLibrarySemaphoreBlock); +} + // Calling this function with nothing sets the debug port to Serial // You can also call it with other streams like Serial1, SerialUSB, etc. void UM980::enableDebugging(Print &debugPort) @@ -292,8 +296,14 @@ void UM980::disableDebugging() bool UM980::update() { bool newData = false; + + unicoreLibrarySemaphoreBlock = true; // Allow external tasks to control serial hardware + while (serialAvailable()) newData = updateOnce(); + + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware + return (newData); } @@ -331,9 +341,8 @@ bool UM980::updateOnce() endName = um980GetStateName(_sempParse); // Display the parser state transition - debugPrintf("UM980: 0x%02x (%c), crc: 0x%08x, state: %s --> %s", - incoming, ((incoming >= ' ') && (incoming < 0x7f)) ? incoming : '.', - _sempParse->crc, startName, endName); + debugPrintf("Unicore Lib: 0x%02x (%c), crc: 0x%08x, state: %s --> %s", incoming, + ((incoming >= ' ') && (incoming < 0x7f)) ? incoming : '.', _sempParse->crc, startName, endName); } return (true); } @@ -380,7 +389,8 @@ void UM980::dumpBuffer(const uint8_t *buffer, uint16_t length) // Display the ASCII values for (index = 0; index < bytes; index++) - sprintf(&line[strlen(line)], "%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); + sprintf(&line[strlen(line)], "%c", + ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); debugPrintf("%s", line); // Set the next line of data @@ -425,27 +435,24 @@ void um980ProcessMessage(SEMP_PARSE_STATE *parse, uint16_t type) ptrUM980->debugPrintf(""); switch (type) { - case UM980_NMEA_PARSER_INDEX: - ptrUM980->debugPrintf("Valid NMEA Sentence: %s, 0x%04x (%d) bytes", - sempNmeaGetSentenceName(parse), - parse->length, parse->length); - break; + case UM980_NMEA_PARSER_INDEX: + ptrUM980->debugPrintf("Unicore Lib: Valid NMEA Sentence: %s, 0x%04x (%d) bytes", + sempNmeaGetSentenceName(parse), parse->length, parse->length); + break; - case UM980_UNICORE_HASH_PARSER_INDEX: - ptrUM980->debugPrintf("Valid Unicore Hash (#) Sentence: %s, 0x%04x (%d) bytes", - sempUnicoreHashGetSentenceName(parse), - parse->length, parse->length); - break; + case UM980_UNICORE_HASH_PARSER_INDEX: + ptrUM980->debugPrintf("Unicore Lib: Valid Unicore Hash (#) Sentence: %s, 0x%04x (%d) bytes", + sempUnicoreHashGetSentenceName(parse), parse->length, parse->length); + break; - case UM980_RTCM_PARSER_INDEX: - ptrUM980->debugPrintf("Valid RTCM message: 0x%04x (%d) bytes", - parse->length, parse->length); - break; + case UM980_RTCM_PARSER_INDEX: + ptrUM980->debugPrintf("Unicore Lib: Valid RTCM message: 0x%04x (%d) bytes", parse->length, parse->length); + break; - case UM980_UNICORE_BINARY_PARSER_INDEX: - ptrUM980->debugPrintf("Valid Unicore message: 0x%04x (%d) bytes", - parse->length, parse->length); - break; + case UM980_UNICORE_BINARY_PARSER_INDEX: + ptrUM980->debugPrintf("Unicore Lib: Valid Unicore message: 0x%04x (%d) bytes", parse->length, + parse->length); + break; } } @@ -456,62 +463,75 @@ void um980ProcessMessage(SEMP_PARSE_STATE *parse, uint16_t type) // Process the message switch (type) { - case UM980_UNICORE_BINARY_PARSER_INDEX: - ptrUM980->unicoreHandler(parse->buffer, parse->length); - break; + case UM980_UNICORE_BINARY_PARSER_INDEX: + ptrUM980->unicoreHandler(parse->buffer, parse->length); + break; - case UM980_RTCM_PARSER_INDEX: - break; + case UM980_RTCM_PARSER_INDEX: + break; - case UM980_UNICORE_HASH_PARSER_INDEX: - // Does this response contain the command we are looking for? - if (strcasecmp((char *)scratchPad->unicoreHash.sentenceName, ptrUM980->commandName) == 0) // Found + case UM980_UNICORE_HASH_PARSER_INDEX: + // Does this response contain the command we are looking for? + if (strcasecmp((char *)scratchPad->unicoreHash.sentenceName, ptrUM980->commandName) == 0) // Found + { + ptrUM980->debugPrintf("Unicore Lib: Query response: %s", parse->buffer); + ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_OK; + } + break; + + case UM980_NMEA_PARSER_INDEX: + + // Is this a NMEA response or command response? + + if (strcasecmp((char *)scratchPad->nmea.sentenceName, "command") != 0 && + strcasecmp((char *)scratchPad->nmea.sentenceName, "MASK") != 0 && + strcasecmp((char *)scratchPad->nmea.sentenceName, "CONFIG") != 0) + { + // command, MASK, CONFIG not found + + if (strcasecmp((char *)scratchPad->nmea.sentenceName, "GNGGA") == 0) { - ptrUM980->debugPrintf("UM980: Query response: %s", - parse->buffer); - ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_OK; + ptrUM980->debugPrintf("um980ProcessMessage GNGGA"); } - break; - - case UM980_NMEA_PARSER_INDEX: - //$command,MODE,response: OK*5D - if (strcasecmp((char *)scratchPad->nmea.sentenceName, "command") != 0 && - strcasecmp((char *)scratchPad->nmea.sentenceName, "MASK") != 0 && - strcasecmp((char *)scratchPad->nmea.sentenceName, "CONFIG") != 0) // Found - // Unknown response, ignore this message - ptrUM980->debugPrintf("UM980: Message ignored: %s", parse->buffer); - else + // Unknown response, ignore this message + ptrUM980->debugPrintf("Unicore Lib: Message ignored: %s", parse->buffer); + } + else + { + // Does this response contain the command we are looking for? + // It may be anywhere in the response: + // $command,MODE,response: OK*5D + char *responsePointer = strcasestr((char *)parse->buffer, ptrUM980->commandName); + if (responsePointer != nullptr) // Found { - // Does this response contain the command we are looking for? - // It may be anywhere in the response: - // $command,MODE,response: OK*5D - char *responsePointer = strcasestr((char *)parse->buffer, ptrUM980->commandName); + // Display the command response + ptrUM980->debugPrintf("Unicore Lib: Known command response: %s", parse->buffer); + + // Check to see if we got a command response + responsePointer = strcasestr((char *)parse->buffer, "OK"); + if (responsePointer != nullptr) // Found + { + ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_OK; + return; + } + + responsePointer = strcasestr((char *)parse->buffer, "PARSING"); if (responsePointer != nullptr) // Found { - // Display the command response - ptrUM980->debugPrintf("UM980: Command response: %s", - parse->buffer); - - // Check to see if we got a command response - responsePointer = strcasestr((char *)parse->buffer, "OK"); - if (responsePointer != nullptr) // Found - { - ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_OK; - return; - } - - responsePointer = strcasestr((char *)parse->buffer, "PARSING"); - if (responsePointer != nullptr) // Found - { - ptrUM980->debugPrintf("UM980: Error response: %s", - parse->buffer); - ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_ERROR; - return; - } + ptrUM980->debugPrintf("Unicore Lib: Error response: %s", parse->buffer); + ptrUM980->commandResponse = UM980_RESULT_RESPONSE_COMMAND_ERROR; + return; } } - break; + else + { + // Display the command response + ptrUM980->debugPrintf("Unicore Lib: Unknown command response: %s", parse->buffer); + ptrUM980->debugPrintf("Unicore Lib: Looking for command: %s", ptrUM980->commandName); + } + } + break; } } @@ -966,13 +986,16 @@ Um980Result UM980::sendQuery(const char *command, uint16_t maxWaitMs) strncpy(commandName, command, sizeof(commandName)); commandResponse = UM980_RESULT_RESPONSE_COMMAND_WAITING; // Reset + unicoreLibrarySemaphoreBlock = true; // Prevent external tasks from harvesting serial data + // Feed the parser until we see a response to the command int wait = 0; while (1) { if (wait++ == maxWaitMs) { - debugPrintf("UM980: Response timeout"); + debugPrintf("Unicore Lib: Response timeout"); + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware return (UM980_RESULT_TIMEOUT_RESPONSE); } @@ -980,19 +1003,22 @@ Um980Result UM980::sendQuery(const char *command, uint16_t maxWaitMs) if (commandResponse == UM980_RESULT_RESPONSE_COMMAND_OK) { - debugPrintf("UM980: Response received"); + debugPrintf("Unicore Lib: Response received"); break; } if (commandResponse == UM980_RESULT_RESPONSE_COMMAND_ERROR) { - debugPrintf("UM980: Query failure"); + debugPrintf("Unicore Lib: Query failure"); + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware return (UM980_RESULT_RESPONSE_COMMAND_ERROR); } delay(1); } + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware + return (UM980_RESULT_OK); } @@ -1006,10 +1032,12 @@ Um980Result UM980::sendString(const char *command, uint16_t maxWaitMs) { clearBuffer(); - debugPrintf("UM980: Sending command %s", command); + debugPrintf("Unicore Lib: Sending command %s", command); strncpy(commandName, command, sizeof(commandName)); // Copy to class so that parsers can see it commandResponse = UM980_RESULT_RESPONSE_COMMAND_WAITING; // Reset + unicoreLibrarySemaphoreBlock = true; // Prevent external tasks from harvesting serial data + serialPrintln(command); // Feed the parser until we see a response to the command @@ -1018,7 +1046,8 @@ Um980Result UM980::sendString(const char *command, uint16_t maxWaitMs) { if (wait++ == maxWaitMs) { - debugPrintf("UM980: Command timeout"); + debugPrintf("Unicore Lib: Command timeout"); + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware return (UM980_RESULT_TIMEOUT_RESPONSE); } @@ -1026,19 +1055,22 @@ Um980Result UM980::sendString(const char *command, uint16_t maxWaitMs) if (commandResponse == UM980_RESULT_RESPONSE_COMMAND_OK) { - debugPrintf("UM980: Command success"); + debugPrintf("Unicore Lib: Command success"); break; } if (commandResponse == UM980_RESULT_RESPONSE_COMMAND_ERROR) { - debugPrintf("UM980: Command error"); + debugPrintf("Unicore Lib: Command error"); + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware return (UM980_RESULT_RESPONSE_COMMAND_ERROR); } delay(1); } + unicoreLibrarySemaphoreBlock = false; // Allow external tasks to control serial hardware + return (UM980_RESULT_OK); } @@ -1141,7 +1173,7 @@ void UM980::unicoreHandler(uint8_t *response, uint16_t length) } else if (messageID == messageIdBestnavXyz) { - // debugPrintf("BestNavXyz Handler"); + debugPrintf("BestNavXyz Handler"); CHECK_POINTER_VOID(packetBESTNAVXYZ, initBestnavXyz); // Check that RAM has been allocated lastUpdateEcef = millis(); // Update stale marker @@ -1159,7 +1191,7 @@ void UM980::unicoreHandler(uint8_t *response, uint16_t length) } else if (messageID == messageIdVersion) { - // debugPrintf("Version Handler"); + debugPrintf("Version Handler"); CHECK_POINTER_VOID(packetVERSION, initVersion); // Check that RAM has been allocated lastUpdateVersion = millis(); // Update stale marker @@ -1175,7 +1207,44 @@ void UM980::unicoreHandler(uint8_t *response, uint16_t length) } else { - debugPrintf("Unknown message id: %d\r\n", messageID); + // Is this a NMEA sentence? + if (response[0] == '$') + { + response[length] = '\0'; // Force terminator because strncasestr does not exist + + // The UM980 does not respond to binary requests when there is no GNSS reception. + // Block BestNavB, etc commands if there is no fix. + // Look for GNGGA NMEA then extract GNSS position status (spot 6). + // $GNGGA,181535.00,,,,,0,00,9999.0,,,,,,*43 + char *responsePointer = strcasestr((char *)response, "GNGGA"); + if (responsePointer != nullptr) // Found + { + char gngga[100]; + strncpy(gngga, (const char *)response, length - 1); // Make copy before strtok + + debugPrintf("Unicore Lib: GNGGA message: %s\r\n", gngga); + + char *pt; + pt = strtok(gngga, ","); + int counter = 0; + while (pt != NULL) + { + int spotValue = atoi(pt); + if (counter++ == 6) + nmeaPositionStatus = spotValue; + pt = strtok(NULL, ","); + } + } + else + { + // Unhandled NMEA message + // debugPrintf("Unicore Lib: Unhandled NMEA sentence (%d bytes): %s\r\n", length, (char *)response); + } + } + else + { + debugPrintf("Unicore Lib: Unknown message id: %d\r\n", messageID); + } } } @@ -1225,6 +1294,12 @@ bool UM980::initVersion() // Allocate RAM for packetBESTNAV and initialize it bool UM980::initBestnav(uint8_t rate) { + if ((startBinaryBeforeFix == false) && (isNmeaFixed() == false)) + { + debugPrintf("Unicore Lib: BestNav no fix"); + return (false); + } + packetBESTNAV = new UNICORE_BESTNAV_t; // Allocate RAM for the main struct if (packetBESTNAV == nullptr) { @@ -1270,6 +1345,12 @@ bool UM980::initBestnav(uint8_t rate) // Allocate RAM for packetBESTNAVXYZ and initialize it bool UM980::initBestnavXyz(uint8_t rate) { + if ((startBinaryBeforeFix == false) && (isNmeaFixed() == false)) + { + debugPrintf("Unicore Lib: BestNavXyz no fix"); + return (false); + } + packetBESTNAVXYZ = new UNICORE_BESTNAVXYZ_t; // Allocate RAM for the main struct if (packetBESTNAVXYZ == nullptr) { @@ -1315,6 +1396,12 @@ bool UM980::initBestnavXyz(uint8_t rate) // Allocate RAM for packetRECTIME and initialize it bool UM980::initRectime(uint8_t rate) { + if ((startBinaryBeforeFix == false) && (isNmeaFixed() == false)) + { + debugPrintf("Unicore Lib: RecTime no fix"); + return (false); + } + packetRECTIME = new UNICORE_RECTIME_t; // Allocate RAM for the main struct if (packetRECTIME == nullptr) { @@ -1605,3 +1692,24 @@ char *UM980::getVersionFull(uint16_t maxWaitMs) return ((char *)"Error1"); return ((char *)"Error2"); } + +// Returns true when GNGGA NMEA reports position status >= 1 +bool UM980::isNmeaFixed() +{ + if (nmeaPositionStatus >= 1) + return (true); + return (false); +} + +// By default, library will attempt to start RECTIME and BESTNAV regardless of GNSS fix +// This may lead to command timeouts as the UM980 does not appear to respond to BESTNAVB commands if 3D fix is not +// achieved. Set startBinartBeforeFix = false via disableBinaryBeforeFix() to block binary commands before a fix is +// achieved +void UM980::enableBinaryBeforeFix() +{ + startBinaryBeforeFix = true; +} +void UM980::disableBinaryBeforeFix() +{ + startBinaryBeforeFix = false; +} diff --git a/src/SparkFun_Unicore_GNSS_Arduino_Library.h b/src/SparkFun_Unicore_GNSS_Arduino_Library.h index 4971c51..36788d5 100644 --- a/src/SparkFun_Unicore_GNSS_Arduino_Library.h +++ b/src/SparkFun_Unicore_GNSS_Arduino_Library.h @@ -48,81 +48,81 @@ typedef enum UM980_RESULT_RESPONSE_COMMAND_WAITING, } Um980Result; -#define um980BinarySyncA ((uint8_t)0xAA) -#define um980BinarySyncB ((uint8_t)0x44) -#define um980BinarySyncC ((uint8_t)0xB5) -#define um980ASCIISyncEnd ((uint8_t)'\n') - -#define um980HeaderLength ((uint16_t)24) -#define offsetHeaderSyncA ((uint16_t)0) -#define offsetHeaderSyncB ((uint16_t)1) -#define offsetHeaderSyncC ((uint16_t)2) -#define offsetHeaderCpuIdle ((uint16_t)3) -#define offsetHeaderMessageId ((uint16_t)4) -#define offsetHeaderMessageLength ((uint16_t)6) -#define offsetHeaderReferenceTime ((uint16_t)8) -#define offsetHeaderTimeStatus ((uint16_t)9) -#define offsetHeaderWeekNumber ((uint16_t)10) -#define offsetHeaderSecondsOfWeek ((uint16_t)12) -#define offsetHeaderReleaseVersion ((uint16_t)20) -#define offsetHeaderLeapSecond ((uint16_t)21) -#define offsetHeaderOutputDelay ((uint16_t)22) +#define um980BinarySyncA ((uint8_t)0xAA) +#define um980BinarySyncB ((uint8_t)0x44) +#define um980BinarySyncC ((uint8_t)0xB5) +#define um980ASCIISyncEnd ((uint8_t)'\n') + +#define um980HeaderLength ((uint16_t)24) +#define offsetHeaderSyncA ((uint16_t)0) +#define offsetHeaderSyncB ((uint16_t)1) +#define offsetHeaderSyncC ((uint16_t)2) +#define offsetHeaderCpuIdle ((uint16_t)3) +#define offsetHeaderMessageId ((uint16_t)4) +#define offsetHeaderMessageLength ((uint16_t)6) +#define offsetHeaderReferenceTime ((uint16_t)8) +#define offsetHeaderTimeStatus ((uint16_t)9) +#define offsetHeaderWeekNumber ((uint16_t)10) +#define offsetHeaderSecondsOfWeek ((uint16_t)12) +#define offsetHeaderReleaseVersion ((uint16_t)20) +#define offsetHeaderLeapSecond ((uint16_t)21) +#define offsetHeaderOutputDelay ((uint16_t)22) // VERSIONB -#define messageIdVersion ((uint16_t)37) -#define offsetVersionModuleType ((uint16_t)0) +#define messageIdVersion ((uint16_t)37) +#define offsetVersionModuleType ((uint16_t)0) #define offsetVersionFirmwareVersion ((uint16_t)4) -#define offsetVersionAuth ((uint16_t)37) -#define offsetVersionPsn ((uint16_t)166) -#define offsetVersionEfuseID ((uint16_t)232) -#define offsetVersionCompTime ((uint16_t)265) +#define offsetVersionAuth ((uint16_t)37) +#define offsetVersionPsn ((uint16_t)166) +#define offsetVersionEfuseID ((uint16_t)232) +#define offsetVersionCompTime ((uint16_t)265) // BESTNAVB contains HPA, sats tracked/used, lat/long, RTK status, fix status -#define messageIdBestnav ((uint16_t)2118) -#define offsetBestnavPsolStatus ((uint16_t)0) -#define offsetBestnavPosType ((uint16_t)4) -#define offsetBestnavLat ((uint16_t)8) -#define offsetBestnavLon ((uint16_t)16) -#define offsetBestnavHgt ((uint16_t)24) -#define offsetBestnavLatDeviation ((uint16_t)40) -#define offsetBestnavLonDeviation ((uint16_t)44) -#define offsetBestnavHgtDeviation ((uint16_t)48) -#define offsetBestnavSatsTracked ((uint16_t)64) -#define offsetBestnavSatsUsed ((uint16_t)65) -#define offsetBestnavExtSolStat ((uint16_t)69) -#define offsetBestnavVelType ((uint16_t)76) -#define offsetBestnavHorSpd ((uint16_t)88) -#define offsetBestnavTrkGnd ((uint16_t)96) -#define offsetBestnavVertSpd ((uint16_t)104) -#define offsetBestnavVerspdStd ((uint16_t)112) -#define offsetBestnavHorspdStd ((uint16_t)116) +#define messageIdBestnav ((uint16_t)2118) +#define offsetBestnavPsolStatus ((uint16_t)0) +#define offsetBestnavPosType ((uint16_t)4) +#define offsetBestnavLat ((uint16_t)8) +#define offsetBestnavLon ((uint16_t)16) +#define offsetBestnavHgt ((uint16_t)24) +#define offsetBestnavLatDeviation ((uint16_t)40) +#define offsetBestnavLonDeviation ((uint16_t)44) +#define offsetBestnavHgtDeviation ((uint16_t)48) +#define offsetBestnavSatsTracked ((uint16_t)64) +#define offsetBestnavSatsUsed ((uint16_t)65) +#define offsetBestnavExtSolStat ((uint16_t)69) +#define offsetBestnavVelType ((uint16_t)76) +#define offsetBestnavHorSpd ((uint16_t)88) +#define offsetBestnavTrkGnd ((uint16_t)96) +#define offsetBestnavVertSpd ((uint16_t)104) +#define offsetBestnavVerspdStd ((uint16_t)112) +#define offsetBestnavHorspdStd ((uint16_t)116) // BESTNAVXYZB -#define messageIdBestnavXyz ((uint16_t)240) -#define offsetBestnavXyzPsolStatus ((uint16_t)0) -#define offsetBestnavXyzPosType ((uint16_t)4) -#define offsetBestnavXyzPX ((uint16_t)8) -#define offsetBestnavXyzPY ((uint16_t)16) -#define offsetBestnavXyzPZ ((uint16_t)24) +#define messageIdBestnavXyz ((uint16_t)240) +#define offsetBestnavXyzPsolStatus ((uint16_t)0) +#define offsetBestnavXyzPosType ((uint16_t)4) +#define offsetBestnavXyzPX ((uint16_t)8) +#define offsetBestnavXyzPY ((uint16_t)16) +#define offsetBestnavXyzPZ ((uint16_t)24) #define offsetBestnavXyzPXDeviation ((uint16_t)32) #define offsetBestnavXyzPYDeviation ((uint16_t)36) #define offsetBestnavXyzPZDeviation ((uint16_t)40) #define offsetBestnavXyzSatsTracked ((uint16_t)104) -#define offsetBestnavXyzSatsUsed ((uint16_t)105) -#define offsetBestnavXyzExtSolStat ((uint16_t)109) +#define offsetBestnavXyzSatsUsed ((uint16_t)105) +#define offsetBestnavXyzExtSolStat ((uint16_t)109) // RECTIMEB for time/date -#define messageIdRectime ((uint16_t)102) -#define offsetRectimeClockStatus ((uint16_t)0) -#define offsetRectimeOffset ((uint16_t)4) -#define offsetRectimeOffsetStd ((uint16_t)12) -#define offsetRectimeUtcYear ((uint16_t)28) -#define offsetRectimeUtcMonth ((uint16_t)32) -#define offsetRectimeUtcDay ((uint16_t)33) -#define offsetRectimeUtcHour ((uint16_t)34) -#define offsetRectimeUtcMinute ((uint16_t)35) +#define messageIdRectime ((uint16_t)102) +#define offsetRectimeClockStatus ((uint16_t)0) +#define offsetRectimeOffset ((uint16_t)4) +#define offsetRectimeOffsetStd ((uint16_t)12) +#define offsetRectimeUtcYear ((uint16_t)28) +#define offsetRectimeUtcMonth ((uint16_t)32) +#define offsetRectimeUtcDay ((uint16_t)33) +#define offsetRectimeUtcHour ((uint16_t)34) +#define offsetRectimeUtcMinute ((uint16_t)35) #define offsetRectimeUtcMillisecond ((uint16_t)36) -#define offsetRectimeUtcStatus ((uint16_t)40) +#define offsetRectimeUtcStatus ((uint16_t)40) // HWSTATUS has temperature info, and voltage info @@ -137,8 +137,8 @@ class UM980 unsigned long lastUpdateDateTime = 0; unsigned long lastUpdateVersion = 0; - bool staleDateTime(); - bool staleEcef(); + bool isNmeaFixed(); // Returns true when GNGGA NMEA reports position status >= 1 + void stopAutoReports(); // Delete all pointers to force reinit next time a helper function is called Um980Result getGeodetic(uint16_t maxWaitMs = 1500); @@ -149,17 +149,28 @@ class UM980 SEMP_PARSE_STATE *_sempParse; // State of the SparkFun Extensible Message Parser + bool unicoreLibrarySemaphoreBlock = false; // Gets set to true when the Unicore library needs to interact directly with the + // serial hardware + protected: HardwareSerial *_hwSerialPort = nullptr; public: - bool _printBadChecksum = false; // Display bad checksum message from the parser + bool _printBadChecksum = false; // Display bad checksum message from the parser bool _printParserTransitions = false; // Display the parser transitions - bool _printRxMessages = false; // Display the received message summary - bool _dumpRxMessages = false; // Display the received message hex dump + bool _printRxMessages = false; // Display the received message summary + bool _dumpRxMessages = false; // Display the received message hex dump + + uint8_t nmeaPositionStatus = 0; // Position psition status obtained from GNGGA NMEA + + // By default, library will attempt to start RECTIME and BESTNAV regardless of GNSS fix. + // This may lead to command timeouts as the UM980 does not appear to respond to BESTNAVB commands if 3D fix is not achieved. + // Set startBinartBeforeFix = false via disableBinaryBeforeFix() to block binary commands before a fix is achieved + bool startBinaryBeforeFix = true; bool begin(HardwareSerial &serialPort, Print *parserDebug = nullptr, Print *parserError = &Serial); bool isConnected(); + bool isBlocking(); bool update(); bool updateOnce(); @@ -172,6 +183,9 @@ class UM980 void enableParserErrors(Print *print = &Serial); void disableParserErrors(); + void enableBinaryBeforeFix(); + void disableBinaryBeforeFix(); + void enablePrintBadChecksums(); void disablePrintBadChecksums(); void enablePrintParserTransitions(); @@ -184,7 +198,7 @@ class UM980 void dumpBuffer(const uint8_t *buffer, uint16_t length); - char commandName[20] = ""; // Passes the command type into parser + char commandName[40] = ""; // Passes the command type into parser - MODE ROVER AUTOMOTIVE uint8_t commandResponse = UM980_RESULT_OK; // Gets EOM result from parser // Mode