From f149235d2801ca059b7761ef0a375e645cfe773d Mon Sep 17 00:00:00 2001 From: ladyada Date: Fri, 11 Jul 2025 14:35:25 -0400 Subject: [PATCH] init --- Adafruit_UBX.cpp | 451 +++++++++++++++++++++++++++++++++++++++ Adafruit_UBX.h | 98 +++++++++ Adafruit_UBloxDDC.cpp | 222 +++++++++++++++++++ Adafruit_UBloxDDC.h | 68 ++++++ Adafruit_uBlox_typedef.h | 107 ++++++++++ 5 files changed, 946 insertions(+) create mode 100644 Adafruit_UBX.cpp create mode 100644 Adafruit_UBX.h create mode 100644 Adafruit_UBloxDDC.cpp create mode 100644 Adafruit_UBloxDDC.h create mode 100644 Adafruit_uBlox_typedef.h diff --git a/Adafruit_UBX.cpp b/Adafruit_UBX.cpp new file mode 100644 index 0000000..c0b7b6b --- /dev/null +++ b/Adafruit_UBX.cpp @@ -0,0 +1,451 @@ +/*! + * @file Adafruit_UBX.cpp + * + * @mainpage Arduino library for UBX protocol from u-blox GPS/RTK modules + * + * @section intro_sec Introduction + * + * This is a library for parsing UBX protocol messages from u-blox GPS/RTK modules. + * It works with any Stream-based interface including UART and DDC (I2C). + * + * Designed specifically to work with u-blox GPS/RTK modules + * like NEO-M8P, ZED-F9P, etc. + * + * @section author Author + * + * Written by Your Name for Project Name. + * + * @section license License + * + * MIT license, all text above must be included in any redistribution + */ + +#include "Adafruit_UBX.h" + + + +/*! + * @brief Constructor + * @param stream Reference to a Stream object (Serial, Adafruit_UBloxDDC, etc.) + */ +Adafruit_UBX::Adafruit_UBX(Stream &stream) { + _stream = &stream; + onUBXMessage = NULL; +} + +/*! + * @brief Initializes the UBX parser + * @return Always returns true (initialization is trivial) + */ +bool Adafruit_UBX::begin() { + resetParser(); + return true; +} + + + + +/*! + * @brief Configure the GPS module to output only UBX protocol (disables NMEA) + * @param portID Port identifier (UBX_PORT_DDC, UBX_PORT_UART1, etc.) + * @param checkAck Whether to wait for acknowledgment + * @param timeout_ms Maximum time to wait for acknowledgment in milliseconds + * @return UBXSendStatus indicating success, failure, or timeout + */ +UBXSendStatus Adafruit_UBX::setUBXOnly(UBXPortId portID, bool checkAck, uint16_t timeout_ms) { + UBX_CFG_PRT_t cfgPrt; + + // Zero out the structure + memset(&cfgPrt, 0, sizeof(cfgPrt)); + + // Set the port ID + cfgPrt.fields.portID = portID; + + // Configure the port appropriately + switch (portID) { + case UBX_PORT_DDC: // I2C/DDC port + // Set the I2C address to 0x42 (the default) + // For DDC, the mode field contains the I2C address in bits 7:1 + cfgPrt.fields.mode = 0x42 << 1; // 0x84 + + // Set protocol masks to UBX only + cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX; + cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX; + break; + + case UBX_PORT_UART1: // Fall through + case UBX_PORT_UART2: // UART ports + // Keep current baud rate (baudRate = 0 keeps current setting) + // Set 8N1 mode for binary protocol + cfgPrt.fields.mode = UBX_UART_MODE_8N1; + // Set protocol masks to UBX only + cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX; + cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX; + break; + + case UBX_PORT_USB: // USB port + // Set protocol masks to UBX only + cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX; + cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX; + break; + + case UBX_PORT_SPI: // SPI port + // Set protocol masks to UBX only + cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX; + break; + + default: + if (verbose_debug > 0) { + Serial.println(F("UBX: Invalid port ID")); + } + return UBX_SEND_FAIL; // Invalid port ID + } + + // Send the message and wait for acknowledgment if requested + if (checkAck) { + return sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw, sizeof(cfgPrt), timeout_ms); + } else { + if (sendMessage(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw, sizeof(cfgPrt))) { + return UBX_SEND_SUCCESS; + } else { + return UBX_SEND_FAIL; + } + } +} + + + + +/*! + * @brief Sets the callback function for UBX messages + * @param callback Function pointer to call when a complete UBX message is received + */ +void Adafruit_UBX::setMessageCallback(UBXMessageCallback callback) { + onUBXMessage = callback; +} + +/*! + * @brief Reset the parser state machine + */ +void Adafruit_UBX::resetParser() { + _parserState = WAIT_SYNC_1; + _payloadCounter = 0; + _payloadLength = 0; +} + +/*! + * @brief Calculate UBX checksum according to protocol + * @param buffer Pointer to the data buffer + * @param len Length of data to checksum + * @param checksumA Reference to store the first checksum byte + * @param checksumB Reference to store the second checksum byte + */ +void Adafruit_UBX::calculateChecksum(uint8_t *buffer, uint16_t len, uint8_t &checksumA, uint8_t &checksumB) { + checksumA = 0; + checksumB = 0; + + for (uint16_t i = 0; i < len; i++) { + checksumA += buffer[i]; + checksumB += checksumA; + } +} + +/*! + * @brief Check for new UBX messages and parse them + * @return True if a complete message was parsed + */ +bool Adafruit_UBX::checkMessages() { + bool messageReceived = false; + + // Process all available bytes + while (_stream->available()) { + uint8_t incomingByte = _stream->read(); + + // State machine for UBX protocol parsing + switch (_parserState) { + case WAIT_SYNC_1: + if (incomingByte == UBX_SYNC_CHAR_1) { + _parserState = WAIT_SYNC_2; + _buffer[0] = incomingByte; // Store for checksum calculation + } + break; + + case WAIT_SYNC_2: + if (incomingByte == UBX_SYNC_CHAR_2) { + _parserState = GET_CLASS; + _buffer[1] = incomingByte; // Store for checksum calculation + } else { + resetParser(); // Invalid sync char, reset + } + break; + + case GET_CLASS: + _msgClass = incomingByte; + _buffer[2] = incomingByte; // Store for checksum calculation + _parserState = GET_ID; + break; + + case GET_ID: + _msgId = incomingByte; + _buffer[3] = incomingByte; // Store for checksum calculation + _parserState = GET_LENGTH_1; + break; + + case GET_LENGTH_1: + _payloadLength = incomingByte; + _buffer[4] = incomingByte; // Store for checksum calculation + _parserState = GET_LENGTH_2; + break; + + case GET_LENGTH_2: + _payloadLength |= (incomingByte << 8); + _buffer[5] = incomingByte; // Store for checksum calculation + + if (_payloadLength > MAX_PAYLOAD_SIZE) { + resetParser(); // Payload too large, reset + } else { + _payloadCounter = 0; + _parserState = GET_PAYLOAD; + } + break; + + case GET_PAYLOAD: + if (_payloadCounter < _payloadLength) { + _buffer[6 + _payloadCounter] = incomingByte; + _payloadCounter++; + + if (_payloadCounter == _payloadLength) { + _parserState = GET_CHECKSUM_A; + } + } + break; + + case GET_CHECKSUM_A: + // Calculate expected checksum + calculateChecksum(_buffer + 2, _payloadLength + 4, _checksumA, _checksumB); + + if (incomingByte == _checksumA) { + _parserState = GET_CHECKSUM_B; // Checksum A matches + } else { + resetParser(); // Invalid checksum, reset + } + break; + + case GET_CHECKSUM_B: + if (incomingByte == _checksumB) { + // We have a valid message! + if (onUBXMessage != NULL) { + onUBXMessage(_msgClass, _msgId, _payloadLength, _buffer + 6); // Call the callback with the message + } + messageReceived = true; + + _lastMsgClass = _msgClass; + _lastMsgId = _msgId; + _lastPayloadLength = _payloadLength; + + // Store a small copy of the payload if it's within size limits + if (_payloadLength <= sizeof(_lastPayload)) { + memcpy(_lastPayload, _buffer + 6, _payloadLength); + } + + if (verbose_debug > 0) { + Serial.print("UBX RX: "); + + // Print header (sync chars, class, id, length) + Serial.print("HDR[B5 62 "); + if (_msgClass < 0x10) Serial.print("0"); + Serial.print(_msgClass, HEX); + Serial.print(" "); + if (_msgId < 0x10) Serial.print("0"); + Serial.print(_msgId, HEX); + Serial.print(" "); + + uint8_t lenLSB = _payloadLength & 0xFF; + uint8_t lenMSB = (_payloadLength >> 8) & 0xFF; + if (lenLSB < 0x10) Serial.print("0"); + Serial.print(lenLSB, HEX); + Serial.print(" "); + if (lenMSB < 0x10) Serial.print("0"); + Serial.print(lenMSB, HEX); + Serial.print("] "); + + // Print payload if verbose debug is enabled + if (verbose_debug > 1 && _payloadLength > 0) { + Serial.print("PL["); + for (uint16_t i = 0; i < _payloadLength; i++) { + if (_buffer[6 + i] < 0x10) Serial.print("0"); + Serial.print(_buffer[6 + i], HEX); + Serial.print(" "); + } + Serial.print("] "); + } + + // Print checksum + Serial.print("CS["); + if (_checksumA < 0x10) Serial.print("0"); + Serial.print(_checksumA, HEX); + Serial.print(" "); + if (_checksumB < 0x10) Serial.print("0"); + Serial.print(_checksumB, HEX); + Serial.println("]"); + } + } + + resetParser(); // Reset for next message + break; + } + } + + return messageReceived; +} + + +/*! + * @brief Send a UBX message and wait for acknowledgment + * @param msgClass Message class + * @param msgId Message ID + * @param payload Pointer to the payload data + * @param length Length of payload + * @param timeout_ms Maximum time to wait for acknowledgment + * @return UBXSendStatus indicating success, failure, or timeout + */ +UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId, uint8_t *payload, uint16_t length, uint16_t timeout_ms = 500) { + // First send the message + if (!sendMessage(msgClass, msgId, payload, length)) { + if (verbose_debug > 0) { + Serial.println(F("UBX ACK: SEND FAIL")); + } + return UBX_SEND_FAIL; + } + + uint32_t startTime = millis(); + + // Check for messages until timeout + while ((millis() - startTime) < timeout_ms) { + // Process incoming bytes + if (checkMessages()) { + // If we have a message handler, it will be called from checkMessages + // We need to check our last received message + if (_lastMsgClass == UBX_CLASS_ACK) { + if (_lastMsgId == UBX_ACK_ACK && _lastPayloadLength >= 2) { + // ACK-ACK message + if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) { + if (verbose_debug > 0) { + Serial.print(F("UBX ACK: SUCCESS for message class 0x")); + if (msgClass < 0x10) Serial.print("0"); + Serial.print(msgClass, HEX); + Serial.print(" ID 0x"); + if (msgId < 0x10) Serial.print("0"); + Serial.println(msgId, HEX); + } + return UBX_SEND_SUCCESS; + } + } else if (_lastMsgId == UBX_ACK_NAK && _lastPayloadLength >= 2) { + // ACK-NAK message + if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) { + if (verbose_debug > 0) { + Serial.print(F("UBX ACK: NAK for message class 0x")); + if (msgClass < 0x10) Serial.print("0"); + Serial.print(msgClass, HEX); + Serial.print(" ID 0x"); + if (msgId < 0x10) Serial.print("0"); + Serial.println(msgId, HEX); + } + return UBX_SEND_NAK; + } + } + } + } + + // Short delay + delay(1); + } + + if (verbose_debug > 0) { + Serial.print(F("UBX ACK: TIMEOUT waiting for ACK/NAK for message class 0x")); + if (msgClass < 0x10) Serial.print("0"); + Serial.print(msgClass, HEX); + Serial.print(" ID 0x"); + if (msgId < 0x10) Serial.print("0"); + Serial.println(msgId, HEX); + } + + return UBX_SEND_TIMEOUT; +} + + +/*! + * @brief Send a UBX message to the GPS module + * @param msgClass Message class + * @param msgId Message ID + * @param payload Pointer to the payload data (can be NULL for zero-length payload) + * @param length Length of payload + * @return True if message was sent successfully + */ +bool Adafruit_UBX::sendMessage(uint8_t msgClass, uint8_t msgId, uint8_t *payload, uint16_t length) { + // Buffer for message (2 sync chars + class + id + 2 length bytes + payload + 2 checksum bytes) + uint8_t msgBuffer[length + 8]; + + // Sync characters + msgBuffer[0] = UBX_SYNC_CHAR_1; + msgBuffer[1] = UBX_SYNC_CHAR_2; + + // Message class and ID + msgBuffer[2] = msgClass; + msgBuffer[3] = msgId; + + // Length (little endian) + msgBuffer[4] = length & 0xFF; + msgBuffer[5] = (length >> 8) & 0xFF; + + // Payload + if (payload != NULL && length > 0) { + memcpy(&msgBuffer[6], payload, length); + } + + // Calculate checksum + uint8_t checksumA, checksumB; + calculateChecksum(&msgBuffer[2], length + 4, checksumA, checksumB); + + msgBuffer[6 + length] = checksumA; + msgBuffer[7 + length] = checksumB; + + // Debug output + if (verbose_debug > 0) { + Serial.print("UBX TX: "); + + // Print header (sync chars, class, id, length) + Serial.print("HDR["); + for (int i = 0; i < 6; i++) { + if (msgBuffer[i] < 0x10) Serial.print("0"); + Serial.print(msgBuffer[i], HEX); + Serial.print(" "); + } + Serial.print("] "); + + // Print payload if verbose debug is enabled + if (verbose_debug > 1 && length > 0) { + Serial.print("PL["); + for (uint16_t i = 0; i < length; i++) { + if (msgBuffer[6 + i] < 0x10) Serial.print("0"); + Serial.print(msgBuffer[6 + i], HEX); + Serial.print(" "); + } + Serial.print("] "); + } + + // Print checksum + Serial.print("CS["); + if (checksumA < 0x10) Serial.print("0"); + Serial.print(checksumA, HEX); + Serial.print(" "); + if (checksumB < 0x10) Serial.print("0"); + Serial.print(checksumB, HEX); + Serial.println("]"); + } + + // Send the message + size_t written = _stream->write(msgBuffer, length + 8); + + return (written == length + 8); +} diff --git a/Adafruit_UBX.h b/Adafruit_UBX.h new file mode 100644 index 0000000..6691e12 --- /dev/null +++ b/Adafruit_UBX.h @@ -0,0 +1,98 @@ +/*! + * @file Adafruit_UBX.h + * + * Arduino library for parsing UBX protocol from u-blox GPS/RTK modules. + * + * This library can use any Stream object as input (UART, DDC, or other). + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Your Name for Project Name. + * + * MIT license, all text here must be included in any redistribution. + */ + +#ifndef ADAFRUIT_UBX_H +#define ADAFRUIT_UBX_H + +#include +#include +#include "Adafruit_uBlox_typedef.h" + +// UBX protocol constants +#define UBX_SYNC_CHAR_1 0xB5 // First UBX protocol sync char (µ) +#define UBX_SYNC_CHAR_2 0x62 // Second UBX protocol sync char (b) +// UBX ACK Message IDs +#define UBX_ACK_NAK 0x00 // Message Not Acknowledged +#define UBX_ACK_ACK 0x01 // Message Acknowledged + +// Callback function type for UBX messages - defined at global scope so other classes can use it +typedef void (*UBXMessageCallback)(uint8_t msgClass, uint8_t msgId, uint16_t payloadLen, uint8_t *payload); + + +/*! + * @brief Class for parsing UBX protocol messages from u-blox GPS/RTK modules + */ +class Adafruit_UBX { + public: + // Constructor + Adafruit_UBX(Stream &stream); + uint8_t verbose_debug = 0; // 0=off, 1=basic, 2=verbose + + // Basic methods + bool begin(); + + bool checkMessages(); // Message parsing + bool sendMessage(uint8_t msgClass, uint8_t msgId, uint8_t *payload, uint16_t length); // Send a UBX message + UBXSendStatus sendMessageWithAck(uint8_t msgClass, uint8_t msgId, uint8_t *payload, uint16_t length, uint16_t timeout_ms = 500); + + // Configure port to use UBX protocol only (disable NMEA) + UBXSendStatus setUBXOnly(UBXPortId portID, bool checkAck = true, uint16_t timeout_ms = 500); + + void setMessageCallback(UBXMessageCallback callback); // Set callback function + UBXMessageCallback onUBXMessage; // Callback for message received + + private: + Stream *_stream; // Stream interface for reading data + + // Buffer for reading messages + static const uint16_t MAX_PAYLOAD_SIZE = 64; // Maximum UBX payload size + uint8_t _buffer[MAX_PAYLOAD_SIZE + 8]; // Buffer for message (header, payload, checksum) + + // Parser state machine + enum ParserState { + WAIT_SYNC_1, // Waiting for first sync char (0xB5) + WAIT_SYNC_2, // Waiting for second sync char (0x62) + GET_CLASS, // Reading message class + GET_ID, // Reading message ID + GET_LENGTH_1, // Reading length LSB + GET_LENGTH_2, // Reading length MSB + GET_PAYLOAD, // Reading payload + GET_CHECKSUM_A, // Reading checksum A + GET_CHECKSUM_B // Reading checksum B + }; + + ParserState _parserState = WAIT_SYNC_1; // Current state of the parser + uint8_t _msgClass; // Message class of current message + uint8_t _msgId; // Message ID of current message + uint16_t _payloadLength; // Length of current message payload + uint16_t _payloadCounter; // Counter for payload bytes received + uint8_t _checksumA; // Running checksum A + uint8_t _checksumB; // Running checksum B + + // Calculate checksum for a block of data + void calculateChecksum(uint8_t *buffer, uint16_t len, uint8_t &checksumA, uint8_t &checksumB); + + // Reset parser state + void resetParser(); + + // Add to private section of Adafruit_UBX.h + uint8_t _lastMsgClass; // Class of last message + uint8_t _lastMsgId; // ID of last message + uint16_t _lastPayloadLength; // Length of last message payload + uint8_t _lastPayload[8]; // Buffer for small payloads (like ACK messages) +}; + +#endif // ADAFRUIT_UBX_H diff --git a/Adafruit_UBloxDDC.cpp b/Adafruit_UBloxDDC.cpp new file mode 100644 index 0000000..b4c8e95 --- /dev/null +++ b/Adafruit_UBloxDDC.cpp @@ -0,0 +1,222 @@ +/*! + * @file Adafruit_UBloxDDC.cpp + * + * @mainpage Arduino library for u-blox GPS/RTK modules over DDC (I2C) + * + * @section intro_sec Introduction + * + * This is a library for the u-blox GPS/RTK modules using I2C interface (DDC) + * + * Designed specifically to work with u-blox GPS/RTK modules + * like NEO-M8P, ZED-F9P, etc. + * + * @section dependencies Dependencies + * + * This library depends on: + * Adafruit_BusIO + * + * @section author Author + * + * Written by Your Name for Project Name. + * + * @section license License + * + * MIT license, all text above must be included in any redistribution + */ + +#include "Adafruit_UBloxDDC.h" + +/*! + * @brief Constructor + * @param address + * i2c address (default 0x42) + * @param wire + * TwoWire instance (default &Wire) + */ +Adafruit_UBloxDDC::Adafruit_UBloxDDC(uint8_t address, TwoWire *wire) { + _i2cDevice = new Adafruit_I2CDevice(address, wire); +} + +/*! + * @brief Destructor - frees allocated resources + */ +Adafruit_UBloxDDC::~Adafruit_UBloxDDC() { + if (_i2cDevice) { + delete _i2cDevice; + } +} + +/*! + * @brief Initializes the GPS module and I2C interface + * @return True if GPS module responds, false on any failure + */ +bool Adafruit_UBloxDDC::begin() { + return _i2cDevice->begin(); +} + +/*! + * @brief Gets the number of bytes available for reading + * @return Number of bytes available, or 0 if no data or error + */ +int Adafruit_UBloxDDC::available() { + uint8_t buffer[2]; + + // Create a register for reading bytes available + Adafruit_BusIO_Register bytesAvailableReg = Adafruit_BusIO_Register(_i2cDevice, REG_BYTES_AVAILABLE_MSB, 2); + + if (!bytesAvailableReg.read(buffer, 2)) { + return 0; + } + + uint16_t bytesAvailable = (uint16_t)buffer[0] << 8; + bytesAvailable |= buffer[1]; + + return bytesAvailable; +} + +/*! + * @brief Reads a single byte from the data stream + * @return -1 if no data available or error, otherwise the byte read (0-255) + */ +int Adafruit_UBloxDDC::read() { + // If we have a peeked byte, return it + if (_hasPeeked) { + _hasPeeked = false; + return _lastByte; + } + + uint8_t value; + + // Create a register for the data stream + Adafruit_BusIO_Register dataStreamReg = Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1); + + if (!dataStreamReg.read(&value, 1)) { + return -1; + } + + return value; +} + +/*! + * @brief Peek at the next available byte without removing it from the stream + * @return -1 if no data available or error, otherwise the byte (0-255) + */ +int Adafruit_UBloxDDC::peek() { + // If we've already peeked, return the last byte + if (_hasPeeked) { + return _lastByte; + } + + // Otherwise, read a byte and store it + _lastByte = read(); + if (_lastByte != -1) { + _hasPeeked = true; + } + + return _lastByte; +} + +/*! + * @brief Write a single byte (required by Stream but not suitable for I2C) + * @param val Byte to write + * @return Always returns 0 as single-byte writes aren't supported on I2C + */ +size_t Adafruit_UBloxDDC::write(uint8_t val) { + // Single-byte writes aren't suitable for I2C/DDC + // This shouldn't be called if properly using the multi-byte version + return 0; +} + +/*! + * @brief Write multiple bytes at once (required for I2C/DDC) + * @param buffer Pointer to data buffer + * @param size Number of bytes to write + * @return Number of bytes written + */ +size_t Adafruit_UBloxDDC::write(const uint8_t *buffer, size_t size) { + // For I2C/DDC, we need at least 2 bytes for a write + if (size < 2) { + // Single-byte writes aren't supported + return 0; + } + + // Use Adafruit_BusIO to handle the I2C transaction + if (_i2cDevice->write(buffer, size)) { + return size; + } + return 0; +} + +/*! + * @brief Read multiple bytes from the data stream + * @param buffer Pointer to buffer to store data + * @param length Maximum number of bytes to read + * @return Number of bytes actually read, which may be less than requested + */ +uint16_t Adafruit_UBloxDDC::readBytes(uint8_t* buffer, uint16_t length) { + if (buffer == nullptr || length == 0) { + return 0; + } + + uint16_t bytesRead = 0; + uint16_t bytesAvailable = available(); + + // Don't try to read more bytes than are available + length = min(length, bytesAvailable); + + // Handle any peeked byte first + if (_hasPeeked && length > 0) { + buffer[0] = _lastByte; + _hasPeeked = false; + bytesRead = 1; + } + + if (bytesRead >= length) { + return bytesRead; + } + + // Create a register for the data stream + Adafruit_BusIO_Register dataStreamReg = Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1); + + while (bytesRead < length) { + // Calculate chunk size (I2C has a limit on bytes per transfer) + uint16_t chunkSize = min(length - bytesRead, (uint16_t)32); + + if (!dataStreamReg.read(&buffer[bytesRead], chunkSize)) { + break; + } + + bytesRead += chunkSize; + } + + return bytesRead; +} + +/*! + * @brief Read a complete message from the device + * @param buffer Pointer to buffer to store message data + * @param maxLength Maximum length of buffer + * @return Number of bytes read into the buffer + */ +uint16_t Adafruit_UBloxDDC::readMessage(uint8_t* buffer, uint16_t maxLength) { + uint16_t bytesAvailable = available(); + + if (bytesAvailable == 0) { + return 0; + } + + // Limit to buffer size + uint16_t bytesToRead = min(bytesAvailable, maxLength); + return readBytes(buffer, bytesToRead); +} + +/*! + * @brief Read a message into the internal buffer and return a pointer to it + * @param messageLength Pointer to variable to store message length + * @return Pointer to internal buffer containing the message + */ +uint8_t* Adafruit_UBloxDDC::readMessage(uint16_t* messageLength) { + *messageLength = readMessage(_buffer, MAX_BUFFER_SIZE); + return _buffer; +} + diff --git a/Adafruit_UBloxDDC.h b/Adafruit_UBloxDDC.h new file mode 100644 index 0000000..b5138db --- /dev/null +++ b/Adafruit_UBloxDDC.h @@ -0,0 +1,68 @@ +/*! + * @file Adafruit_UBloxDDC.h + * + * Arduino library for interfacing with u-blox GPS/RTK modules over I2C (DDC). + * + * This library implements the Stream interface, allowing it to be used with + * existing GPS parsing libraries like TinyGPS++. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Your Name for Project Name. + * + * MIT license, all text here must be included in any redistribution. + */ + +#ifndef ADAFRUIT_UBLOXDDC_H +#define ADAFRUIT_UBLOXDDC_H + +#include +#include +#include +#include + +/*! + * @brief Arduino library for interfacing with u-blox GPS/RTK modules over I2C + */ +class Adafruit_UBloxDDC : public Stream { + private: + Adafruit_I2CDevice *_i2cDevice; ///< Underlying I2C device + + // Register addresses + static const uint8_t REG_DATA_STREAM = 0xFF; ///< Register for reading data stream + static const uint8_t REG_BYTES_AVAILABLE_MSB = 0xFD; ///< MSB of bytes available + static const uint8_t REG_BYTES_AVAILABLE_LSB = 0xFE; ///< LSB of bytes available + + // Buffer for reading messages + static const uint16_t MAX_BUFFER_SIZE = 128; ///< Maximum buffer size for messages + uint8_t _buffer[MAX_BUFFER_SIZE]; ///< Internal buffer for messages + + // Last byte read for peek() implementation + int _lastByte = -1; ///< Last byte read by peek() + bool _hasPeeked = false; ///< Indicates if we have a peeked byte waiting + + public: + // Constructor & destructor + Adafruit_UBloxDDC(uint8_t address = 0x42, TwoWire *wire = &Wire); + ~Adafruit_UBloxDDC(); + + // Basic methods + bool begin(); + + // Stream interface implementation + virtual int available() override; + virtual int read() override; + virtual int peek() override; + // Stream interface implementation + virtual size_t write(uint8_t) override; + virtual size_t write(const uint8_t *buffer, size_t size) override; + + // Additional methods + uint16_t readBytes(uint8_t* buffer, uint16_t length); + uint16_t readMessage(uint8_t* buffer, uint16_t maxLength); + uint8_t* readMessage(uint16_t* messageLength); +}; + +#endif // ADAFRUIT_UBLOXDDC_H diff --git a/Adafruit_uBlox_typedef.h b/Adafruit_uBlox_typedef.h new file mode 100644 index 0000000..f8e44b2 --- /dev/null +++ b/Adafruit_uBlox_typedef.h @@ -0,0 +1,107 @@ +/*! + * @file Adafruit_uBlox_typedef.h + * + * Type definitions for u-blox GPS/RTK module messages + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Your Name for Project Name. + * + * MIT license, all text here must be included in any redistribution. + */ + +#ifndef ADAFRUIT_UBLOX_TYPEDEF_H +#define ADAFRUIT_UBLOX_TYPEDEF_H + +#include + +// UBX Message Classes +typedef enum { + UBX_CLASS_NAV = 0x01, // Navigation Results + UBX_CLASS_RXM = 0x02, // Receiver Manager Messages + UBX_CLASS_INF = 0x04, // Information Messages + UBX_CLASS_ACK = 0x05, // Acknowledgements + UBX_CLASS_CFG = 0x06, // Configuration + UBX_CLASS_UPD = 0x09, // Firmware Update + UBX_CLASS_MON = 0x0A, // Monitoring + UBX_CLASS_AID = 0x0B, // AssistNow Aiding + UBX_CLASS_TIM = 0x0D, // Timing + UBX_CLASS_ESF = 0x10, // External Sensor Fusion + UBX_CLASS_MGA = 0x13, // Multiple GNSS Assistance + UBX_CLASS_LOG = 0x21, // Logging + UBX_CLASS_SEC = 0x27, // Security + UBX_CLASS_HNR = 0x28, // High Rate Navigation + UBX_CLASS_NMEA = 0xF0 // NMEA Standard Messages +} UBXMessageClass; + +// UBX CFG Message IDs +typedef enum { + UBX_CFG_PRT = 0x00, // Port Configuration + UBX_CFG_MSG = 0x01, // Message Configuration + UBX_CFG_RST = 0x04, // Reset Receiver + UBX_CFG_RATE = 0x08, // Navigation/Measurement Rate Settings + UBX_CFG_CFG = 0x09, // Clear, Save, and Load Configurations + UBX_CFG_NAVX5 = 0x23, // Navigation Engine Settings + UBX_CFG_GNSS = 0x3E, // GNSS Configuration + UBX_CFG_PMS = 0x86 // Power Mode Setup +} UBXCfgMessageId; + +// Return values for functions that wait for acknowledgment +typedef enum { + UBX_SEND_SUCCESS = 0, // Message was acknowledged (ACK) + UBX_SEND_NAK, // Message was not acknowledged (NAK) + UBX_SEND_FAIL, // Failed to send the message + UBX_SEND_TIMEOUT // Timed out waiting for ACK/NAK +} UBXSendStatus; + +// Port ID enum for different interfaces +typedef enum { + UBX_PORT_DDC = 0, // I2C / DDC port + UBX_PORT_UART1 = 1, // UART1 port + UBX_PORT_UART2 = 2, // UART2 port + UBX_PORT_USB = 3, // USB port + UBX_PORT_SPI = 4 // SPI port +} UBXPortId; + +// UART mode flags (Charlen, Parity & Stop bit settings) +typedef enum { + UBX_UART_MODE_8N1 = 0x000, // 8-bit, no parity, 1 stop bit + UBX_UART_MODE_8E1 = 0x100, // 8-bit, even parity, 1 stop bit + UBX_UART_MODE_8O1 = 0x200, // 8-bit, odd parity, 1 stop bit + UBX_UART_MODE_7N1 = 0x400, // 7-bit, no parity, 1 stop bit + UBX_UART_MODE_7E1 = 0x500, // 7-bit, even parity, 1 stop bit + UBX_UART_MODE_7O1 = 0x600, // 7-bit, odd parity, 1 stop bit + UBX_UART_MODE_7N2 = 0x800, // 7-bit, no parity, 2 stop bits + UBX_UART_MODE_7E2 = 0x900, // 7-bit, even parity, 2 stop bits + UBX_UART_MODE_7O2 = 0xA00, // 7-bit, odd parity, 2 stop bits + UBX_UART_MODE_8N2 = 0xC00, // 8-bit, no parity, 2 stop bits + UBX_UART_MODE_8E2 = 0xD00, // 8-bit, even parity, 2 stop bits + UBX_UART_MODE_8O2 = 0xE00 // 8-bit, odd parity, 2 stop bits +} UBXUARTMode; + +// Protocol flags for inProtoMask and outProtoMask +#define UBX_PROTOCOL_UBX 0x0001 // UBX protocol +#define UBX_PROTOCOL_NMEA 0x0002 // NMEA protocol +#define UBX_PROTOCOL_RTCM 0x0004 // RTCM2 protocol (only for inProtoMask) +#define UBX_PROTOCOL_RTCM3 0x0020 // RTCM3 protocol + +// CFG-PRT (Port Configuration) Message +// Total size: 20 bytes +typedef union { + struct { + uint8_t portID; // Port identifier (0=DDC/I2C, 1=UART1, 2=UART2, 3=USB, 4=SPI) + uint8_t reserved1; // Reserved + uint16_t txReady; // TX ready PIN configuration + uint32_t mode; // UART mode (bit field) or Reserved for non-UART ports + uint32_t baudRate; // Baudrate in bits/second (UART only) + uint16_t inProtoMask; // Input protocol mask + uint16_t outProtoMask; // Output protocol mask + uint16_t flags; // Flags bit field + uint16_t reserved2; // Reserved + } fields; + uint8_t raw[20]; +} UBX_CFG_PRT_t; + +#endif // ADAFRUIT_UBLOX_TYPEDEF_H