init
This commit is contained in:
commit
f149235d28
5 changed files with 946 additions and 0 deletions
451
Adafruit_UBX.cpp
Normal file
451
Adafruit_UBX.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
98
Adafruit_UBX.h
Normal file
98
Adafruit_UBX.h
Normal file
|
|
@ -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 <Arduino.h>
|
||||
#include <Stream.h>
|
||||
#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
|
||||
222
Adafruit_UBloxDDC.cpp
Normal file
222
Adafruit_UBloxDDC.cpp
Normal file
|
|
@ -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:
|
||||
* <a href="https://github.com/adafruit/Adafruit_BusIO">Adafruit_BusIO</a>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
68
Adafruit_UBloxDDC.h
Normal file
68
Adafruit_UBloxDDC.h
Normal file
|
|
@ -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 <Arduino.h>
|
||||
#include <Stream.h>
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
/*!
|
||||
* @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
|
||||
107
Adafruit_uBlox_typedef.h
Normal file
107
Adafruit_uBlox_typedef.h
Normal file
|
|
@ -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 <Arduino.h>
|
||||
|
||||
// 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
|
||||
Loading…
Reference in a new issue