diff --git a/examples/Basics/Example8_GetModuleInfo/Example8_GetModuleInfo.ino b/examples/Basics/Example8_GetModuleInfo/Example8_GetModuleInfo.ino index e3ac0d5..1ff4815 100644 --- a/examples/Basics/Example8_GetModuleInfo/Example8_GetModuleInfo.ino +++ b/examples/Basics/Example8_GetModuleInfo/Example8_GetModuleInfo.ino @@ -69,9 +69,41 @@ void setup() } else Serial.println(F("Error: could not read module info!")); + + // Use the helper method to read the unique chip ID as a string + // Returns "000000000000" if the read fails + Serial.print(F("Unique chip ID: 0x")); + Serial.println(myGNSS.getUniqueChipIdStr()); + + // Or we can read the ID and use the helper method to convert it to string + UBX_SEC_UNIQID_data_t chipID; + if (myGNSS.getUniqueChipId(&chipID)) + { + Serial.print(F("Unique chip ID: 0x")); + Serial.println(myGNSS.getUniqueChipIdStr(&chipID)); + } + else + Serial.println(F("Error: could not read chip ID!")); + + // Or we can read and print the unique chip ID manually + if (myGNSS.getUniqueChipId(&chipID)) + { + Serial.print(F("Unique chip ID: 0x")); + // The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2) + for (uint8_t i = 0; i < (chipID.version + 4); i++) + { + if (chipID.uniqueId[i] < 0x10) + Serial.print(F("0")); + Serial.print(chipID.uniqueId[i], HEX); + } + Serial.println(); + } + else + Serial.println(F("Error: could not read chip ID!")); + } void loop() { //Do nothing -} +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 325b3a2..5c58569 100644 --- a/keywords.txt +++ b/keywords.txt @@ -64,6 +64,8 @@ UBX_TIM_TP_data_t KEYWORD1 UBX_MON_HW_data_t KEYWORD1 +UBX_SEC_UNIQID_data_t KEYWORD1 + UBX_ESF_ALG_data_t KEYWORD1 UBX_ESF_INS_data_t KEYWORD1 UBX_ESF_MEAS_data_t KEYWORD1 @@ -243,6 +245,9 @@ setAopCfg KEYWORD2 setDynamicSPARTNKey KEYWORD2 setDynamicSPARTNKeys KEYWORD2 +getUniqueChipId KEYWORD2 +getUniqueChipIdStr KEYWORD2 + getVal8 KEYWORD2 getVal16 KEYWORD2 getVal32 KEYWORD2 diff --git a/library.properties b/library.properties index bf65b0a..461caa2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS v3 -version=3.0.13 +version=3.0.14 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules

diff --git a/src/u-blox_GNSS.cpp b/src/u-blox_GNSS.cpp index d9fcdf7..2b537ef 100644 --- a/src/u-blox_GNSS.cpp +++ b/src/u-blox_GNSS.cpp @@ -8931,6 +8931,72 @@ bool DevUBLOXGNSS::setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validF return (sendCommand(&packetCfg, 0) == SFE_UBLOX_STATUS_SUCCESS); // UBX-RXM-SPARTNKEY is silent. It does not ACK (or NACK) } +// Get the unique chip ID using UBX-SEC-UNIQID +// The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2) +bool DevUBLOXGNSS::getUniqueChipId(UBX_SEC_UNIQID_data_t *data, uint16_t maxWait) +{ + if (data == nullptr) // Check if the user forgot to include the data pointer + return (false); // Bail + + packetCfg.cls = UBX_CLASS_SEC; + packetCfg.id = UBX_SEC_UNIQID; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK + return (false); + + // Extract the data + data->version = extractByte(&packetCfg, 0); + for (uint8_t i = 0; i < 5; i++) + data->uniqueId[i] = extractByte(&packetCfg, i + 4); + + // The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2) + if ((data->version == 2) && (packetCfg.len == UBX_SEC_UNIQID_LEN_VERSION2)) + data->uniqueId[5] = extractByte(&packetCfg, 9); + else + data->uniqueId[5] = 0; + + return (true); +} +// Get the unique chip ID as text +const char *DevUBLOXGNSS::getUniqueChipIdStr(UBX_SEC_UNIQID_data_t *data, uint16_t maxWait) +{ + static char uniqueId[13] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '\0'}; + bool valid = true; + bool provided = (data != nullptr); + + if (!provided) + { + data = new UBX_SEC_UNIQID_data_t; + valid = getUniqueChipId(data, maxWait); + } + + if (valid) + { + for (uint8_t i = 0; (i < (data->version + 4)) && (i < 6); i++) + { + uint8_t nibble = data->uniqueId[i] >> 4; + if (nibble < 10) + uniqueId[(i * 2) + 0] = nibble + '0'; + else + uniqueId[(i * 2) + 0] = nibble + 'A' - 10; + nibble = data->uniqueId[i] & 0x0F; + if (nibble < 10) + uniqueId[(i * 2) + 1] = nibble + '0'; + else + uniqueId[(i * 2) + 1] = nibble + 'A' - 10; + uniqueId[(i * 2) + 2] = 0; // NULL-terminate + } + } + + if (!provided) + delete data; + + return ((const char *)uniqueId); +} + + // CONFIGURATION INTERFACE (protocol v27 and above) // Given a key, load the payload with data that can then be extracted to 8, 16, or 32 bits diff --git a/src/u-blox_GNSS.h b/src/u-blox_GNSS.h index 094ad7d..798bb51 100644 --- a/src/u-blox_GNSS.h +++ b/src/u-blox_GNSS.h @@ -459,6 +459,10 @@ class DevUBLOXGNSS bool setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validFromWno1, uint32_t validFromTow1, const uint8_t *key1, uint8_t keyLengthBytes2, uint16_t validFromWno2, uint32_t validFromTow2, const uint8_t *key2); + // Get unique chip ID - UBX-SEC-UNIQID + bool getUniqueChipId(UBX_SEC_UNIQID_data_t *data = nullptr, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Get the unique chip ID using UBX_SEC_UNIQID + const char *getUniqueChipIdStr(UBX_SEC_UNIQID_data_t *data = nullptr, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Get the unique chip ID using UBX_SEC_UNIQID + // General configuration (used only on protocol v27 and higher - ie, ZED-F9P) // VALGET diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 26b5f99..f03dac6 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -2031,6 +2031,20 @@ typedef struct UBX_TIM_TP_data_t *callbackData; } UBX_TIM_TP_t; +// SEC-specific structs + +// UBX-SEC-UNIQID (0x27 0x03): Unique chip ID +// The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2) +const uint16_t UBX_SEC_UNIQID_LEN_VERSION1 = 9; +const uint16_t UBX_SEC_UNIQID_LEN_VERSION2 = 10; + +typedef struct +{ + uint8_t version; + uint8_t reserved0[3]; + uint8_t uniqueId[6]; +} UBX_SEC_UNIQID_data_t; + // ESF-specific structs // UBX-ESF-ALG (0x10 0x14): IMU alignment information