Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b90085793 | ||
|
|
57c7b6cb03 | ||
|
|
8081d05d6f | ||
|
|
2ffb9de69b | ||
|
|
0ca6e789eb | ||
|
|
c885a346fc | ||
|
|
431d1a98b7 | ||
|
|
e369e88efe | ||
|
|
739f2be25a | ||
|
|
569ff1495e | ||
|
|
5eb550766a | ||
|
|
8f6b59b218 |
16 changed files with 1099 additions and 438 deletions
46
.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||||
|
improve the speed of resolution please review the following guidelines and
|
||||||
|
common troubleshooting steps below before creating the issue:
|
||||||
|
|
||||||
|
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||||
|
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||||
|
something isn't working as expected. In many cases the problem is a common issue
|
||||||
|
that you will more quickly receive help from the forum community. GitHub issues
|
||||||
|
are meant for known defects in the code. If you don't know if there is a defect
|
||||||
|
in the code then start with troubleshooting on the forum first.
|
||||||
|
|
||||||
|
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||||
|
check all of the steps and commands to run have been followed. Consult the
|
||||||
|
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||||
|
|
||||||
|
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||||
|
|
||||||
|
- For uploading sketches or communicating with the board make sure you're using
|
||||||
|
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||||
|
very hard to tell the difference between a data and charge cable! Try using the
|
||||||
|
cable with other devices or swapping to another cable to confirm it is not
|
||||||
|
the problem.
|
||||||
|
|
||||||
|
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||||
|
your board and plug in an external power supply. In many cases just
|
||||||
|
plugging a board into your computer is not enough to power it and other
|
||||||
|
peripherals.
|
||||||
|
|
||||||
|
- **Double check all soldering joints and connections.** Flakey connections
|
||||||
|
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||||
|
|
||||||
|
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||||
|
guarantee a clone board will have the same functionality and work as expected
|
||||||
|
with this code and don't support them.
|
||||||
|
|
||||||
|
If you're sure this issue is a defect in the code and checked the steps above
|
||||||
|
please fill in the following fields to provide enough troubleshooting information.
|
||||||
|
You may delete the guideline and text above to just leave the following details:
|
||||||
|
|
||||||
|
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||||
|
|
||||||
|
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||||
|
VERSION HERE**
|
||||||
|
|
||||||
|
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||||
|
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
||||||
26
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||||
|
Before you open the request please review the following guidelines and tips to
|
||||||
|
help it be more easily integrated:
|
||||||
|
|
||||||
|
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||||
|
of the code were modified.** This will help us understand any risks of integrating
|
||||||
|
the code.
|
||||||
|
|
||||||
|
- **Describe any known limitations with your change.** For example if the change
|
||||||
|
doesn't apply to a supported platform of the library please mention it.
|
||||||
|
|
||||||
|
- **Please run any tests or examples that can exercise your modified code.** We
|
||||||
|
strive to not break users of the code and running tests/examples helps with this
|
||||||
|
process.
|
||||||
|
|
||||||
|
Thank you again for contributing! We will try to test and integrate the change
|
||||||
|
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||||
|
can't immediately respond to every request. There is no need to bump or check in
|
||||||
|
on a pull request (it will clutter the discussion of the request).
|
||||||
|
|
||||||
|
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||||
|
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||||
|
priorities of the pull request. Don't fret, the open source community thrives on
|
||||||
|
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||||
|
|
||||||
|
After reviewing the guidelines above you can delete this text from the pull request.
|
||||||
32
.github/workflows/githubci.yml
vendored
Normal file
32
.github/workflows/githubci.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Arduino Library CI
|
||||||
|
|
||||||
|
on: [pull_request, push, repository_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: adafruit/ci-arduino
|
||||||
|
path: ci
|
||||||
|
|
||||||
|
- name: Install the prerequisites
|
||||||
|
run: bash ci/actions_install.sh
|
||||||
|
|
||||||
|
- name: Check for correct code formatting with clang-format
|
||||||
|
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
|
||||||
|
|
||||||
|
- name: Check for correct documentation with doxygen
|
||||||
|
env:
|
||||||
|
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||||
|
PRETTYNAME : "Adafruit uBlox Library"
|
||||||
|
run: bash ci/doxy_gen_and_deploy.sh
|
||||||
|
|
||||||
|
- name: Test the code on supported platforms
|
||||||
|
run: python3 ci/build_platform.py main_platforms
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Our handy .gitignore for automation ease
|
||||||
|
Doxyfile*
|
||||||
|
doxygen_sqlite3.db
|
||||||
|
html
|
||||||
486
Adafruit_UBX.cpp
486
Adafruit_UBX.cpp
|
|
@ -1,38 +1,46 @@
|
||||||
/*!
|
/*!
|
||||||
* @file Adafruit_UBX.cpp
|
* @file Adafruit_UBX.cpp
|
||||||
*
|
*
|
||||||
* @mainpage Arduino library for UBX protocol from u-blox GPS/RTK modules
|
* @mainpage Arduino library for UBX protocol from u-blox GPS/RTK modules
|
||||||
*
|
*
|
||||||
* @section intro_sec Introduction
|
* @section intro_sec Introduction
|
||||||
*
|
*
|
||||||
* This is a library for parsing UBX protocol messages from u-blox GPS/RTK modules.
|
* This is a library for parsing UBX protocol messages from u-blox GPS/RTK
|
||||||
* It works with any Stream-based interface including UART and DDC (I2C).
|
* modules. It works with any Stream-based interface including UART and DDC
|
||||||
*
|
* (I2C).
|
||||||
* Designed specifically to work with u-blox GPS/RTK modules
|
*
|
||||||
|
* Designed specifically to work with u-blox GPS/RTK modules
|
||||||
* like NEO-M8P, ZED-F9P, etc.
|
* like NEO-M8P, ZED-F9P, etc.
|
||||||
*
|
*
|
||||||
* @section author Author
|
* @section author Author
|
||||||
*
|
*
|
||||||
* Written by Your Name for Project Name.
|
* Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
*
|
*
|
||||||
* @section license License
|
* @section license License
|
||||||
*
|
*
|
||||||
* MIT license, all text above must be included in any redistribution
|
* MIT license, all text above must be included in any redistribution
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Adafruit_UBX.h"
|
#include "Adafruit_UBX.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Constructor
|
* @brief Constructor
|
||||||
* @param stream Reference to a Stream object (Serial, Adafruit_UBloxDDC, etc.)
|
* @param stream Reference to a Stream object (Serial, Adafruit_UBloxDDC,
|
||||||
|
* etc.)
|
||||||
*/
|
*/
|
||||||
Adafruit_UBX::Adafruit_UBX(Stream &stream) {
|
Adafruit_UBX::Adafruit_UBX(Stream &stream) {
|
||||||
_stream = &stream;
|
_stream = &stream;
|
||||||
onUBXMessage = NULL;
|
onUBXMessage = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
Adafruit_UBX::~Adafruit_UBX() {
|
||||||
|
if (onUBXMessage)
|
||||||
|
onUBXMessage = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Initializes the UBX parser
|
* @brief Initializes the UBX parser
|
||||||
* @return Always returns true (initialization is trivial)
|
* @return Always returns true (initialization is trivial)
|
||||||
|
|
@ -42,9 +50,6 @@ bool Adafruit_UBX::begin() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Configure the GPS module to output only UBX protocol (disables NMEA)
|
* @brief Configure the GPS module to output only UBX protocol (disables NMEA)
|
||||||
* @param portID Port identifier (UBX_PORT_DDC, UBX_PORT_UART1, etc.)
|
* @param portID Port identifier (UBX_PORT_DDC, UBX_PORT_UART1, etc.)
|
||||||
|
|
@ -52,58 +57,60 @@ bool Adafruit_UBX::begin() {
|
||||||
* @param timeout_ms Maximum time to wait for acknowledgment in milliseconds
|
* @param timeout_ms Maximum time to wait for acknowledgment in milliseconds
|
||||||
* @return UBXSendStatus indicating success, failure, or timeout
|
* @return UBXSendStatus indicating success, failure, or timeout
|
||||||
*/
|
*/
|
||||||
UBXSendStatus Adafruit_UBX::setUBXOnly(UBXPortId portID, bool checkAck, uint16_t timeout_ms) {
|
UBXSendStatus Adafruit_UBX::setUBXOnly(UBXPortId portID, bool checkAck,
|
||||||
|
uint16_t timeout_ms) {
|
||||||
UBX_CFG_PRT_t cfgPrt;
|
UBX_CFG_PRT_t cfgPrt;
|
||||||
|
|
||||||
// Zero out the structure
|
// Zero out the structure
|
||||||
memset(&cfgPrt, 0, sizeof(cfgPrt));
|
memset(&cfgPrt, 0, sizeof(cfgPrt));
|
||||||
|
|
||||||
// Set the port ID
|
// Set the port ID
|
||||||
cfgPrt.fields.portID = portID;
|
cfgPrt.fields.portID = portID;
|
||||||
|
|
||||||
// Configure the port appropriately
|
// Configure the port appropriately
|
||||||
switch (portID) {
|
switch (portID) {
|
||||||
case UBX_PORT_DDC: // I2C/DDC port
|
case UBX_PORT_DDC: // I2C/DDC port
|
||||||
// Set the I2C address to 0x42 (the default)
|
// Set the I2C address to 0x42 (the default)
|
||||||
// For DDC, the mode field contains the I2C address in bits 7:1
|
// For DDC, the mode field contains the I2C address in bits 7:1
|
||||||
cfgPrt.fields.mode = 0x42 << 1; // 0x84
|
cfgPrt.fields.mode = 0x42 << 1; // 0x84
|
||||||
|
|
||||||
// Set protocol masks to UBX only
|
// Set protocol masks to UBX only
|
||||||
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
||||||
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UBX_PORT_UART1: // Fall through
|
case UBX_PORT_UART1: // Fall through
|
||||||
case UBX_PORT_UART2: // UART ports
|
case UBX_PORT_UART2: // UART ports
|
||||||
// Keep current baud rate (baudRate = 0 keeps current setting)
|
// Keep current baud rate (baudRate = 0 keeps current setting)
|
||||||
// Set 8N1 mode for binary protocol
|
// Set 8N1 mode for binary protocol
|
||||||
cfgPrt.fields.mode = UBX_UART_MODE_8N1;
|
cfgPrt.fields.mode = UBX_UART_MODE_8N1;
|
||||||
// Set protocol masks to UBX only
|
// Set protocol masks to UBX only
|
||||||
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
||||||
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UBX_PORT_USB: // USB port
|
case UBX_PORT_USB: // USB port
|
||||||
// Set protocol masks to UBX only
|
// Set protocol masks to UBX only
|
||||||
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.inProtoMask = UBX_PROTOCOL_UBX;
|
||||||
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UBX_PORT_SPI: // SPI port
|
case UBX_PORT_SPI: // SPI port
|
||||||
// Set protocol masks to UBX only
|
// Set protocol masks to UBX only
|
||||||
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
cfgPrt.fields.outProtoMask = UBX_PROTOCOL_UBX;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
Serial.println(F("UBX: Invalid port ID"));
|
Serial.println(F("UBX: Invalid port ID"));
|
||||||
}
|
}
|
||||||
return UBX_SEND_FAIL; // Invalid port ID
|
return UBX_SEND_FAIL; // Invalid port ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the message and wait for acknowledgment if requested
|
// Send the message and wait for acknowledgment if requested
|
||||||
if (checkAck) {
|
if (checkAck) {
|
||||||
return sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw, sizeof(cfgPrt), timeout_ms);
|
return sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw,
|
||||||
|
sizeof(cfgPrt), timeout_ms);
|
||||||
} else {
|
} else {
|
||||||
if (sendMessage(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw, sizeof(cfgPrt))) {
|
if (sendMessage(UBX_CLASS_CFG, UBX_CFG_PRT, cfgPrt.raw, sizeof(cfgPrt))) {
|
||||||
return UBX_SEND_SUCCESS;
|
return UBX_SEND_SUCCESS;
|
||||||
|
|
@ -113,12 +120,10 @@ UBXSendStatus Adafruit_UBX::setUBXOnly(UBXPortId portID, bool checkAck, uint16_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Sets the callback function for UBX messages
|
* @brief Sets the callback function for UBX messages
|
||||||
* @param callback Function pointer to call when a complete UBX message is received
|
* @param callback Function pointer to call when a complete UBX message is
|
||||||
|
* received
|
||||||
*/
|
*/
|
||||||
void Adafruit_UBX::setMessageCallback(UBXMessageCallback callback) {
|
void Adafruit_UBX::setMessageCallback(UBXMessageCallback callback) {
|
||||||
onUBXMessage = callback;
|
onUBXMessage = callback;
|
||||||
|
|
@ -140,10 +145,11 @@ void Adafruit_UBX::resetParser() {
|
||||||
* @param checksumA Reference to store the first checksum byte
|
* @param checksumA Reference to store the first checksum byte
|
||||||
* @param checksumB Reference to store the second 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) {
|
void Adafruit_UBX::calculateChecksum(uint8_t *buffer, uint16_t len,
|
||||||
|
uint8_t &checksumA, uint8_t &checksumB) {
|
||||||
checksumA = 0;
|
checksumA = 0;
|
||||||
checksumB = 0;
|
checksumB = 0;
|
||||||
|
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
for (uint16_t i = 0; i < len; i++) {
|
||||||
checksumA += buffer[i];
|
checksumA += buffer[i];
|
||||||
checksumB += checksumA;
|
checksumB += checksumA;
|
||||||
|
|
@ -156,150 +162,158 @@ void Adafruit_UBX::calculateChecksum(uint8_t *buffer, uint16_t len, uint8_t &che
|
||||||
*/
|
*/
|
||||||
bool Adafruit_UBX::checkMessages() {
|
bool Adafruit_UBX::checkMessages() {
|
||||||
bool messageReceived = false;
|
bool messageReceived = false;
|
||||||
|
|
||||||
// Process all available bytes
|
// Process all available bytes
|
||||||
while (_stream->available()) {
|
while (_stream->available()) {
|
||||||
uint8_t incomingByte = _stream->read();
|
uint8_t incomingByte = _stream->read();
|
||||||
|
|
||||||
// State machine for UBX protocol parsing
|
// State machine for UBX protocol parsing
|
||||||
switch (_parserState) {
|
switch (_parserState) {
|
||||||
case WAIT_SYNC_1:
|
case WAIT_SYNC_1:
|
||||||
if (incomingByte == UBX_SYNC_CHAR_1) {
|
if (incomingByte == UBX_SYNC_CHAR_1) {
|
||||||
_parserState = WAIT_SYNC_2;
|
_parserState = WAIT_SYNC_2;
|
||||||
_buffer[0] = incomingByte; // Store for checksum calculation
|
_buffer[0] = incomingByte; // Store for checksum calculation
|
||||||
}
|
}
|
||||||
break;
|
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;
|
case WAIT_SYNC_2:
|
||||||
_lastMsgId = _msgId;
|
if (incomingByte == UBX_SYNC_CHAR_2) {
|
||||||
_lastPayloadLength = _payloadLength;
|
_parserState = GET_CLASS;
|
||||||
|
_buffer[1] = incomingByte; // Store for checksum calculation
|
||||||
// Store a small copy of the payload if it's within size limits
|
} else {
|
||||||
if (_payloadLength <= sizeof(_lastPayload)) {
|
resetParser(); // Invalid sync char, reset
|
||||||
memcpy(_lastPayload, _buffer + 6, _payloadLength);
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (verbose_debug > 0) {
|
case GET_CLASS:
|
||||||
Serial.print("UBX RX: ");
|
_msgClass = incomingByte;
|
||||||
|
_buffer[2] = incomingByte; // Store for checksum calculation
|
||||||
// Print header (sync chars, class, id, length)
|
_parserState = GET_ID;
|
||||||
Serial.print("HDR[B5 62 ");
|
break;
|
||||||
if (_msgClass < 0x10) Serial.print("0");
|
|
||||||
Serial.print(_msgClass, HEX);
|
case GET_ID:
|
||||||
Serial.print(" ");
|
_msgId = incomingByte;
|
||||||
if (_msgId < 0x10) Serial.print("0");
|
_buffer[3] = incomingByte; // Store for checksum calculation
|
||||||
Serial.print(_msgId, HEX);
|
_parserState = GET_LENGTH_1;
|
||||||
Serial.print(" ");
|
break;
|
||||||
|
|
||||||
uint8_t lenLSB = _payloadLength & 0xFF;
|
case GET_LENGTH_1:
|
||||||
uint8_t lenMSB = (_payloadLength >> 8) & 0xFF;
|
_payloadLength = incomingByte;
|
||||||
if (lenLSB < 0x10) Serial.print("0");
|
_buffer[4] = incomingByte; // Store for checksum calculation
|
||||||
Serial.print(lenLSB, HEX);
|
_parserState = GET_LENGTH_2;
|
||||||
Serial.print(" ");
|
break;
|
||||||
if (lenMSB < 0x10) Serial.print("0");
|
|
||||||
Serial.print(lenMSB, HEX);
|
case GET_LENGTH_2:
|
||||||
Serial.print("] ");
|
_payloadLength |= (incomingByte << 8);
|
||||||
|
_buffer[5] = incomingByte; // Store for checksum calculation
|
||||||
// Print payload if verbose debug is enabled
|
|
||||||
if (verbose_debug > 1 && _payloadLength > 0) {
|
if (_payloadLength > MAX_PAYLOAD_SIZE) {
|
||||||
Serial.print("PL[");
|
resetParser(); // Payload too large, reset
|
||||||
for (uint16_t i = 0; i < _payloadLength; i++) {
|
} else {
|
||||||
if (_buffer[6 + i] < 0x10) Serial.print("0");
|
_payloadCounter = 0;
|
||||||
Serial.print(_buffer[6 + i], HEX);
|
_parserState = GET_PAYLOAD;
|
||||||
Serial.print(" ");
|
}
|
||||||
}
|
break;
|
||||||
Serial.print("] ");
|
|
||||||
|
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("]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
resetParser(); // Reset for next message
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageReceived;
|
return messageReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Send a UBX message and wait for acknowledgment
|
* @brief Send a UBX message and wait for acknowledgment
|
||||||
* @param msgClass Message class
|
* @param msgClass Message class
|
||||||
|
|
@ -309,7 +323,10 @@ bool Adafruit_UBX::checkMessages() {
|
||||||
* @param timeout_ms Maximum time to wait for acknowledgment
|
* @param timeout_ms Maximum time to wait for acknowledgment
|
||||||
* @return UBXSendStatus indicating success, failure, or timeout
|
* @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) {
|
UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId,
|
||||||
|
uint8_t *payload,
|
||||||
|
uint16_t length,
|
||||||
|
uint16_t timeout_ms) {
|
||||||
// First send the message
|
// First send the message
|
||||||
if (!sendMessage(msgClass, msgId, payload, length)) {
|
if (!sendMessage(msgClass, msgId, payload, length)) {
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
|
|
@ -317,9 +334,9 @@ UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId,
|
||||||
}
|
}
|
||||||
return UBX_SEND_FAIL;
|
return UBX_SEND_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t startTime = millis();
|
uint32_t startTime = millis();
|
||||||
|
|
||||||
// Check for messages until timeout
|
// Check for messages until timeout
|
||||||
while ((millis() - startTime) < timeout_ms) {
|
while ((millis() - startTime) < timeout_ms) {
|
||||||
// Process incoming bytes
|
// Process incoming bytes
|
||||||
|
|
@ -332,10 +349,12 @@ UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId,
|
||||||
if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) {
|
if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) {
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
Serial.print(F("UBX ACK: SUCCESS for message class 0x"));
|
Serial.print(F("UBX ACK: SUCCESS for message class 0x"));
|
||||||
if (msgClass < 0x10) Serial.print("0");
|
if (msgClass < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(msgClass, HEX);
|
Serial.print(msgClass, HEX);
|
||||||
Serial.print(" ID 0x");
|
Serial.print(" ID 0x");
|
||||||
if (msgId < 0x10) Serial.print("0");
|
if (msgId < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.println(msgId, HEX);
|
Serial.println(msgId, HEX);
|
||||||
}
|
}
|
||||||
return UBX_SEND_SUCCESS;
|
return UBX_SEND_SUCCESS;
|
||||||
|
|
@ -345,10 +364,12 @@ UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId,
|
||||||
if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) {
|
if (_lastPayload[0] == msgClass && _lastPayload[1] == msgId) {
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
Serial.print(F("UBX ACK: NAK for message class 0x"));
|
Serial.print(F("UBX ACK: NAK for message class 0x"));
|
||||||
if (msgClass < 0x10) Serial.print("0");
|
if (msgClass < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(msgClass, HEX);
|
Serial.print(msgClass, HEX);
|
||||||
Serial.print(" ID 0x");
|
Serial.print(" ID 0x");
|
||||||
if (msgId < 0x10) Serial.print("0");
|
if (msgId < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.println(msgId, HEX);
|
Serial.println(msgId, HEX);
|
||||||
}
|
}
|
||||||
return UBX_SEND_NAK;
|
return UBX_SEND_NAK;
|
||||||
|
|
@ -356,96 +377,105 @@ UBXSendStatus Adafruit_UBX::sendMessageWithAck(uint8_t msgClass, uint8_t msgId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short delay
|
// Short delay
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
Serial.print(F("UBX ACK: TIMEOUT waiting for ACK/NAK for message class 0x"));
|
Serial.print(
|
||||||
if (msgClass < 0x10) Serial.print("0");
|
F("UBX ACK: TIMEOUT waiting for ACK/NAK for message class 0x"));
|
||||||
|
if (msgClass < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(msgClass, HEX);
|
Serial.print(msgClass, HEX);
|
||||||
Serial.print(" ID 0x");
|
Serial.print(" ID 0x");
|
||||||
if (msgId < 0x10) Serial.print("0");
|
if (msgId < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.println(msgId, HEX);
|
Serial.println(msgId, HEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UBX_SEND_TIMEOUT;
|
return UBX_SEND_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Send a UBX message to the GPS module
|
* @brief Send a UBX message to the GPS module
|
||||||
* @param msgClass Message class
|
* @param msgClass Message class
|
||||||
* @param msgId Message ID
|
* @param msgId Message ID
|
||||||
* @param payload Pointer to the payload data (can be NULL for zero-length payload)
|
* @param payload Pointer to the payload data (can be NULL for zero-length
|
||||||
|
* payload)
|
||||||
* @param length Length of payload
|
* @param length Length of payload
|
||||||
* @return True if message was sent successfully
|
* @return True if message was sent successfully
|
||||||
*/
|
*/
|
||||||
bool Adafruit_UBX::sendMessage(uint8_t msgClass, uint8_t msgId, uint8_t *payload, uint16_t length) {
|
bool Adafruit_UBX::sendMessage(uint8_t msgClass, uint8_t msgId,
|
||||||
// Buffer for message (2 sync chars + class + id + 2 length bytes + payload + 2 checksum bytes)
|
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];
|
uint8_t msgBuffer[length + 8];
|
||||||
|
|
||||||
// Sync characters
|
// Sync characters
|
||||||
msgBuffer[0] = UBX_SYNC_CHAR_1;
|
msgBuffer[0] = UBX_SYNC_CHAR_1;
|
||||||
msgBuffer[1] = UBX_SYNC_CHAR_2;
|
msgBuffer[1] = UBX_SYNC_CHAR_2;
|
||||||
|
|
||||||
// Message class and ID
|
// Message class and ID
|
||||||
msgBuffer[2] = msgClass;
|
msgBuffer[2] = msgClass;
|
||||||
msgBuffer[3] = msgId;
|
msgBuffer[3] = msgId;
|
||||||
|
|
||||||
// Length (little endian)
|
// Length (little endian)
|
||||||
msgBuffer[4] = length & 0xFF;
|
msgBuffer[4] = length & 0xFF;
|
||||||
msgBuffer[5] = (length >> 8) & 0xFF;
|
msgBuffer[5] = (length >> 8) & 0xFF;
|
||||||
|
|
||||||
// Payload
|
// Payload
|
||||||
if (payload != NULL && length > 0) {
|
if (payload != NULL && length > 0) {
|
||||||
memcpy(&msgBuffer[6], payload, length);
|
memcpy(&msgBuffer[6], payload, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate checksum
|
// Calculate checksum
|
||||||
uint8_t checksumA, checksumB;
|
uint8_t checksumA, checksumB;
|
||||||
calculateChecksum(&msgBuffer[2], length + 4, checksumA, checksumB);
|
calculateChecksum(&msgBuffer[2], length + 4, checksumA, checksumB);
|
||||||
|
|
||||||
msgBuffer[6 + length] = checksumA;
|
msgBuffer[6 + length] = checksumA;
|
||||||
msgBuffer[7 + length] = checksumB;
|
msgBuffer[7 + length] = checksumB;
|
||||||
|
|
||||||
// Debug output
|
// Debug output
|
||||||
if (verbose_debug > 0) {
|
if (verbose_debug > 0) {
|
||||||
Serial.print("UBX TX: ");
|
Serial.print("UBX TX: ");
|
||||||
|
|
||||||
// Print header (sync chars, class, id, length)
|
// Print header (sync chars, class, id, length)
|
||||||
Serial.print("HDR[");
|
Serial.print("HDR[");
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (msgBuffer[i] < 0x10) Serial.print("0");
|
if (msgBuffer[i] < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(msgBuffer[i], HEX);
|
Serial.print(msgBuffer[i], HEX);
|
||||||
Serial.print(" ");
|
Serial.print(" ");
|
||||||
}
|
}
|
||||||
Serial.print("] ");
|
Serial.print("] ");
|
||||||
|
|
||||||
// Print payload if verbose debug is enabled
|
// Print payload if verbose debug is enabled
|
||||||
if (verbose_debug > 1 && length > 0) {
|
if (verbose_debug > 1 && length > 0) {
|
||||||
Serial.print("PL[");
|
Serial.print("PL[");
|
||||||
for (uint16_t i = 0; i < length; i++) {
|
for (uint16_t i = 0; i < length; i++) {
|
||||||
if (msgBuffer[6 + i] < 0x10) Serial.print("0");
|
if (msgBuffer[6 + i] < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(msgBuffer[6 + i], HEX);
|
Serial.print(msgBuffer[6 + i], HEX);
|
||||||
Serial.print(" ");
|
Serial.print(" ");
|
||||||
}
|
}
|
||||||
Serial.print("] ");
|
Serial.print("] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print checksum
|
// Print checksum
|
||||||
Serial.print("CS[");
|
Serial.print("CS[");
|
||||||
if (checksumA < 0x10) Serial.print("0");
|
if (checksumA < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(checksumA, HEX);
|
Serial.print(checksumA, HEX);
|
||||||
Serial.print(" ");
|
Serial.print(" ");
|
||||||
if (checksumB < 0x10) Serial.print("0");
|
if (checksumB < 0x10)
|
||||||
|
Serial.print("0");
|
||||||
Serial.print(checksumB, HEX);
|
Serial.print(checksumB, HEX);
|
||||||
Serial.println("]");
|
Serial.println("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
size_t written = _stream->write(msgBuffer, length + 8);
|
size_t written = _stream->write(msgBuffer, length + 8);
|
||||||
|
|
||||||
return (written == length + 8);
|
return (written == length + 8);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
142
Adafruit_UBX.h
142
Adafruit_UBX.h
|
|
@ -1,98 +1,110 @@
|
||||||
/*!
|
/*!
|
||||||
* @file Adafruit_UBX.h
|
* @file Adafruit_UBX.h
|
||||||
*
|
*
|
||||||
* Arduino library for parsing UBX protocol from u-blox GPS/RTK modules.
|
* 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).
|
* This library can use any Stream object as input (UART, DDC, or other).
|
||||||
*
|
*
|
||||||
* Adafruit invests time and resources providing this open source code,
|
* Adafruit invests time and resources providing this open source code,
|
||||||
* please support Adafruit and open-source hardware by purchasing
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
* products from Adafruit!
|
* products from Adafruit!
|
||||||
*
|
*
|
||||||
* Written by Your Name for Project Name.
|
* Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
*
|
*
|
||||||
* MIT license, all text here must be included in any redistribution.
|
* MIT license, all text here must be included in any redistribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ADAFRUIT_UBX_H
|
#ifndef ADAFRUIT_UBX_H
|
||||||
#define ADAFRUIT_UBX_H
|
#define ADAFRUIT_UBX_H
|
||||||
|
|
||||||
|
#include "Adafruit_uBlox_Ubx_Messages.h"
|
||||||
|
#include "Adafruit_uBlox_typedef.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
#include "Adafruit_uBlox_typedef.h"
|
|
||||||
|
|
||||||
// UBX protocol constants
|
// UBX protocol constants
|
||||||
#define UBX_SYNC_CHAR_1 0xB5 // First UBX protocol sync char (µ)
|
#define UBX_SYNC_CHAR_1 0xB5 ///< First UBX protocol sync char (<28>)
|
||||||
#define UBX_SYNC_CHAR_2 0x62 // Second UBX protocol sync char (b)
|
#define UBX_SYNC_CHAR_2 0x62 ///< Second UBX protocol sync char (b)
|
||||||
// UBX ACK Message IDs
|
// UBX ACK Message IDs
|
||||||
#define UBX_ACK_NAK 0x00 // Message Not Acknowledged
|
#define UBX_ACK_NAK 0x00 ///< Message Not Acknowledged
|
||||||
#define UBX_ACK_ACK 0x01 // Message 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 Callback function type for UBX messages - defined at global scope so
|
||||||
|
* other classes can use it
|
||||||
|
* @param msgClass Message class
|
||||||
|
* @param msgId Message ID
|
||||||
|
* @param payloadLen Length of payload data
|
||||||
|
* @param payload Pointer to payload data
|
||||||
|
*/
|
||||||
|
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
|
* @brief Class for parsing UBX protocol messages from u-blox GPS/RTK modules
|
||||||
*/
|
*/
|
||||||
class Adafruit_UBX {
|
class Adafruit_UBX {
|
||||||
public:
|
public:
|
||||||
// Constructor
|
Adafruit_UBX(Stream &stream);
|
||||||
Adafruit_UBX(Stream &stream);
|
~Adafruit_UBX();
|
||||||
uint8_t verbose_debug = 0; // 0=off, 1=basic, 2=verbose
|
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);
|
||||||
|
|
||||||
// Basic methods
|
// Configure port to use UBX protocol only (disable NMEA)
|
||||||
bool begin();
|
UBXSendStatus setUBXOnly(UBXPortId portID, bool checkAck = true,
|
||||||
|
uint16_t timeout_ms = 500);
|
||||||
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)
|
void setMessageCallback(UBXMessageCallback callback); // Set callback function
|
||||||
UBXSendStatus setUBXOnly(UBXPortId portID, bool checkAck = true, uint16_t timeout_ms = 500);
|
UBXMessageCallback onUBXMessage; ///< Callback for message received
|
||||||
|
|
||||||
void setMessageCallback(UBXMessageCallback callback); // Set callback function
|
private:
|
||||||
UBXMessageCallback onUBXMessage; // Callback for message received
|
Stream *_stream; // Stream interface for reading data
|
||||||
|
|
||||||
private:
|
// Buffer for reading messages
|
||||||
Stream *_stream; // Stream interface for reading data
|
static const uint16_t MAX_PAYLOAD_SIZE = 64; // Maximum UBX payload size
|
||||||
|
uint8_t _buffer[MAX_PAYLOAD_SIZE +
|
||||||
// Buffer for reading messages
|
8]; // Buffer for message (header, payload, checksum)
|
||||||
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
|
// Parser state machine
|
||||||
uint8_t _lastMsgClass; // Class of last message
|
enum ParserState {
|
||||||
uint8_t _lastMsgId; // ID of last message
|
WAIT_SYNC_1, // Waiting for first sync char (0xB5)
|
||||||
uint16_t _lastPayloadLength; // Length of last message payload
|
WAIT_SYNC_2, // Waiting for second sync char (0x62)
|
||||||
uint8_t _lastPayload[8]; // Buffer for small payloads (like ACK messages)
|
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
|
#endif // ADAFRUIT_UBX_H
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
/*!
|
/*!
|
||||||
* @file Adafruit_UBloxDDC.cpp
|
* @file Adafruit_UBloxDDC.cpp
|
||||||
*
|
*
|
||||||
* @mainpage Arduino library for u-blox GPS/RTK modules over DDC (I2C)
|
* @section ddc_intro_sec Introduction
|
||||||
*
|
*
|
||||||
* @section intro_sec Introduction
|
|
||||||
*
|
|
||||||
* This is a library for the u-blox GPS/RTK modules using I2C interface (DDC)
|
* 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
|
* Designed specifically to work with u-blox GPS/RTK modules
|
||||||
* like NEO-M8P, ZED-F9P, etc.
|
* like NEO-M8P, ZED-F9P, etc.
|
||||||
*
|
*
|
||||||
* @section dependencies Dependencies
|
* @section ddc_dependencies Dependencies
|
||||||
*
|
*
|
||||||
* This library depends on:
|
* This library depends on:
|
||||||
* <a href="https://github.com/adafruit/Adafruit_BusIO">Adafruit_BusIO</a>
|
* <a href="https://github.com/adafruit/Adafruit_BusIO">Adafruit_BusIO</a>
|
||||||
*
|
*
|
||||||
* @section author Author
|
* @section ddc_author Author
|
||||||
*
|
*
|
||||||
* Written by Your Name for Project Name.
|
* Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
*
|
*
|
||||||
* @section license License
|
* @section ddc_license License
|
||||||
*
|
*
|
||||||
* MIT license, all text above must be included in any redistribution
|
* MIT license, all text above must be included in any redistribution
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -50,9 +48,7 @@ Adafruit_UBloxDDC::~Adafruit_UBloxDDC() {
|
||||||
* @brief Initializes the GPS module and I2C interface
|
* @brief Initializes the GPS module and I2C interface
|
||||||
* @return True if GPS module responds, false on any failure
|
* @return True if GPS module responds, false on any failure
|
||||||
*/
|
*/
|
||||||
bool Adafruit_UBloxDDC::begin() {
|
bool Adafruit_UBloxDDC::begin() { return _i2cDevice->begin(); }
|
||||||
return _i2cDevice->begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Gets the number of bytes available for reading
|
* @brief Gets the number of bytes available for reading
|
||||||
|
|
@ -60,17 +56,18 @@ bool Adafruit_UBloxDDC::begin() {
|
||||||
*/
|
*/
|
||||||
int Adafruit_UBloxDDC::available() {
|
int Adafruit_UBloxDDC::available() {
|
||||||
uint8_t buffer[2];
|
uint8_t buffer[2];
|
||||||
|
|
||||||
// Create a register for reading bytes available
|
// Create a register for reading bytes available
|
||||||
Adafruit_BusIO_Register bytesAvailableReg = Adafruit_BusIO_Register(_i2cDevice, REG_BYTES_AVAILABLE_MSB, 2);
|
Adafruit_BusIO_Register bytesAvailableReg =
|
||||||
|
Adafruit_BusIO_Register(_i2cDevice, REG_BYTES_AVAILABLE_MSB, 2);
|
||||||
|
|
||||||
if (!bytesAvailableReg.read(buffer, 2)) {
|
if (!bytesAvailableReg.read(buffer, 2)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t bytesAvailable = (uint16_t)buffer[0] << 8;
|
uint16_t bytesAvailable = (uint16_t)buffer[0] << 8;
|
||||||
bytesAvailable |= buffer[1];
|
bytesAvailable |= buffer[1];
|
||||||
|
|
||||||
return bytesAvailable;
|
return bytesAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,16 +81,17 @@ int Adafruit_UBloxDDC::read() {
|
||||||
_hasPeeked = false;
|
_hasPeeked = false;
|
||||||
return _lastByte;
|
return _lastByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
|
|
||||||
// Create a register for the data stream
|
// Create a register for the data stream
|
||||||
Adafruit_BusIO_Register dataStreamReg = Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1);
|
Adafruit_BusIO_Register dataStreamReg =
|
||||||
|
Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1);
|
||||||
|
|
||||||
if (!dataStreamReg.read(&value, 1)) {
|
if (!dataStreamReg.read(&value, 1)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,13 +104,13 @@ int Adafruit_UBloxDDC::peek() {
|
||||||
if (_hasPeeked) {
|
if (_hasPeeked) {
|
||||||
return _lastByte;
|
return _lastByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, read a byte and store it
|
// Otherwise, read a byte and store it
|
||||||
_lastByte = read();
|
_lastByte = read();
|
||||||
if (_lastByte != -1) {
|
if (_lastByte != -1) {
|
||||||
_hasPeeked = true;
|
_hasPeeked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _lastByte;
|
return _lastByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +137,7 @@ size_t Adafruit_UBloxDDC::write(const uint8_t *buffer, size_t size) {
|
||||||
// Single-byte writes aren't supported
|
// Single-byte writes aren't supported
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Adafruit_BusIO to handle the I2C transaction
|
// Use Adafruit_BusIO to handle the I2C transaction
|
||||||
if (_i2cDevice->write(buffer, size)) {
|
if (_i2cDevice->write(buffer, size)) {
|
||||||
return size;
|
return size;
|
||||||
|
|
@ -153,42 +151,43 @@ size_t Adafruit_UBloxDDC::write(const uint8_t *buffer, size_t size) {
|
||||||
* @param length Maximum number of bytes to read
|
* @param length Maximum number of bytes to read
|
||||||
* @return Number of bytes actually read, which may be less than requested
|
* @return Number of bytes actually read, which may be less than requested
|
||||||
*/
|
*/
|
||||||
uint16_t Adafruit_UBloxDDC::readBytes(uint8_t* buffer, uint16_t length) {
|
uint16_t Adafruit_UBloxDDC::readBytes(uint8_t *buffer, uint16_t length) {
|
||||||
if (buffer == nullptr || length == 0) {
|
if (buffer == nullptr || length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t bytesRead = 0;
|
uint16_t bytesRead = 0;
|
||||||
uint16_t bytesAvailable = available();
|
uint16_t bytesAvailable = available();
|
||||||
|
|
||||||
// Don't try to read more bytes than are available
|
// Don't try to read more bytes than are available
|
||||||
length = min(length, bytesAvailable);
|
length = min(length, bytesAvailable);
|
||||||
|
|
||||||
// Handle any peeked byte first
|
// Handle any peeked byte first
|
||||||
if (_hasPeeked && length > 0) {
|
if (_hasPeeked && length > 0) {
|
||||||
buffer[0] = _lastByte;
|
buffer[0] = _lastByte;
|
||||||
_hasPeeked = false;
|
_hasPeeked = false;
|
||||||
bytesRead = 1;
|
bytesRead = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesRead >= length) {
|
if (bytesRead >= length) {
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a register for the data stream
|
// Create a register for the data stream
|
||||||
Adafruit_BusIO_Register dataStreamReg = Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1);
|
Adafruit_BusIO_Register dataStreamReg =
|
||||||
|
Adafruit_BusIO_Register(_i2cDevice, REG_DATA_STREAM, 1);
|
||||||
|
|
||||||
while (bytesRead < length) {
|
while (bytesRead < length) {
|
||||||
// Calculate chunk size (I2C has a limit on bytes per transfer)
|
// Calculate chunk size (I2C has a limit on bytes per transfer)
|
||||||
uint16_t chunkSize = min(length - bytesRead, (uint16_t)32);
|
uint16_t chunkSize = min((uint16_t)(length - bytesRead), (uint16_t)32);
|
||||||
|
|
||||||
if (!dataStreamReg.read(&buffer[bytesRead], chunkSize)) {
|
if (!dataStreamReg.read(&buffer[bytesRead], chunkSize)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead += chunkSize;
|
bytesRead += chunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,13 +197,13 @@ uint16_t Adafruit_UBloxDDC::readBytes(uint8_t* buffer, uint16_t length) {
|
||||||
* @param maxLength Maximum length of buffer
|
* @param maxLength Maximum length of buffer
|
||||||
* @return Number of bytes read into the buffer
|
* @return Number of bytes read into the buffer
|
||||||
*/
|
*/
|
||||||
uint16_t Adafruit_UBloxDDC::readMessage(uint8_t* buffer, uint16_t maxLength) {
|
uint16_t Adafruit_UBloxDDC::readMessage(uint8_t *buffer, uint16_t maxLength) {
|
||||||
uint16_t bytesAvailable = available();
|
uint16_t bytesAvailable = available();
|
||||||
|
|
||||||
if (bytesAvailable == 0) {
|
if (bytesAvailable == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit to buffer size
|
// Limit to buffer size
|
||||||
uint16_t bytesToRead = min(bytesAvailable, maxLength);
|
uint16_t bytesToRead = min(bytesAvailable, maxLength);
|
||||||
return readBytes(buffer, bytesToRead);
|
return readBytes(buffer, bytesToRead);
|
||||||
|
|
@ -215,8 +214,7 @@ uint16_t Adafruit_UBloxDDC::readMessage(uint8_t* buffer, uint16_t maxLength) {
|
||||||
* @param messageLength Pointer to variable to store message length
|
* @param messageLength Pointer to variable to store message length
|
||||||
* @return Pointer to internal buffer containing the message
|
* @return Pointer to internal buffer containing the message
|
||||||
*/
|
*/
|
||||||
uint8_t* Adafruit_UBloxDDC::readMessage(uint16_t* messageLength) {
|
uint8_t *Adafruit_UBloxDDC::readMessage(uint16_t *messageLength) {
|
||||||
*messageLength = readMessage(_buffer, MAX_BUFFER_SIZE);
|
*messageLength = readMessage(_buffer, MAX_BUFFER_SIZE);
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,72 @@
|
||||||
/*!
|
/*!
|
||||||
* @file Adafruit_UBloxDDC.h
|
* @file Adafruit_UBloxDDC.h
|
||||||
*
|
*
|
||||||
* Arduino library for interfacing with u-blox GPS/RTK modules over I2C (DDC).
|
* 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
|
* This library implements the Stream interface, allowing it to be used with
|
||||||
* existing GPS parsing libraries like TinyGPS++.
|
* existing GPS parsing libraries like TinyGPS++.
|
||||||
*
|
*
|
||||||
* Adafruit invests time and resources providing this open source code,
|
* Adafruit invests time and resources providing this open source code,
|
||||||
* please support Adafruit and open-source hardware by purchasing
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
* products from Adafruit!
|
* products from Adafruit!
|
||||||
*
|
*
|
||||||
* Written by Your Name for Project Name.
|
* Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
*
|
*
|
||||||
* MIT license, all text here must be included in any redistribution.
|
* MIT license, all text here must be included in any redistribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ADAFRUIT_UBLOXDDC_H
|
#ifndef ADAFRUIT_UBLOXDDC_H
|
||||||
#define ADAFRUIT_UBLOXDDC_H
|
#define ADAFRUIT_UBLOXDDC_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Stream.h>
|
|
||||||
#include <Adafruit_BusIO_Register.h>
|
#include <Adafruit_BusIO_Register.h>
|
||||||
#include <Adafruit_I2CDevice.h>
|
#include <Adafruit_I2CDevice.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Stream.h>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Arduino library for interfacing with u-blox GPS/RTK modules over I2C
|
* @brief Arduino library for interfacing with u-blox GPS/RTK modules over I2C
|
||||||
*/
|
*/
|
||||||
class Adafruit_UBloxDDC : public Stream {
|
class Adafruit_UBloxDDC : public Stream {
|
||||||
private:
|
private:
|
||||||
Adafruit_I2CDevice *_i2cDevice; ///< Underlying I2C device
|
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:
|
// Register addresses
|
||||||
// Constructor & destructor
|
static const uint8_t REG_DATA_STREAM =
|
||||||
Adafruit_UBloxDDC(uint8_t address = 0x42, TwoWire *wire = &Wire);
|
0xFF; ///< Register for reading data stream
|
||||||
~Adafruit_UBloxDDC();
|
static const uint8_t REG_BYTES_AVAILABLE_MSB =
|
||||||
|
0xFD; ///< MSB of bytes available
|
||||||
// Basic methods
|
static const uint8_t REG_BYTES_AVAILABLE_LSB =
|
||||||
bool begin();
|
0xFE; ///< LSB of bytes available
|
||||||
|
|
||||||
// Stream interface implementation
|
// Buffer for reading messages
|
||||||
virtual int available() override;
|
static const uint16_t MAX_BUFFER_SIZE =
|
||||||
virtual int read() override;
|
128; ///< Maximum buffer size for messages
|
||||||
virtual int peek() override;
|
uint8_t _buffer[MAX_BUFFER_SIZE]; ///< Internal buffer for messages
|
||||||
// Stream interface implementation
|
|
||||||
virtual size_t write(uint8_t) override;
|
// Last byte read for peek() implementation
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size) override;
|
int _lastByte = -1; ///< Last byte read by peek()
|
||||||
|
bool _hasPeeked = false; ///< Indicates if we have a peeked byte waiting
|
||||||
// Additional methods
|
|
||||||
uint16_t readBytes(uint8_t* buffer, uint16_t length);
|
public:
|
||||||
uint16_t readMessage(uint8_t* buffer, uint16_t maxLength);
|
// Constructor & destructor
|
||||||
uint8_t* readMessage(uint16_t* messageLength);
|
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
|
#endif // ADAFRUIT_UBLOXDDC_H
|
||||||
|
|
|
||||||
58
Adafruit_uBlox_Ubx_Messages.h
Normal file
58
Adafruit_uBlox_Ubx_Messages.h
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*!
|
||||||
|
* @file Adafruit_uBlox_Ubx_Messages.h
|
||||||
|
*
|
||||||
|
* Pre-built UBX message payloads for common tasks
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Brent Rubell for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* MIT license, all text here must be included in any redistribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_UBLOX_UBX_MESSAGES
|
||||||
|
#define ADAFRUIT_UBLOX_UBX_MESSAGES
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// NMEA Message Configuration Commands (CFG-MSG)
|
||||||
|
// Format: {msgClass, msgID, rate_I2C, rate_UART1, rate_UART2, rate_USB,
|
||||||
|
// rate_SPI, reserved}
|
||||||
|
|
||||||
|
// Enable specific NMEA message sentences to output on the DDC interface
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GGA_ENABLE[] = {0xF0, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GLL_ENABLE[] = {0xF0, 0x01, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GSA_ENABLE[] = {0xF0, 0x02, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GSV_ENABLE[] = {0xF0, 0x03, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_RMC_ENABLE[] = {0xF0, 0x04, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_VTG_ENABLE[] = {0xF0, 0x05, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
// Disable specific NMEA message sentences from outputting on the DDC interface
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GGA_DISABLE[] = {0xF0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GLL_DISABLE[] = {0xF0, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GSA_DISABLE[] = {0xF0, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_GSV_DISABLE[] = {0xF0, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_RMC_DISABLE[] = {0xF0, 0x04, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
static uint8_t UBX_CFG_MSG_NMEA_VTG_DISABLE[] = {0xF0, 0x05, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
// Navigation Rate Configuration Commands (CFG-RATE)
|
||||||
|
// Format: {measRate_ms (2 bytes), navRate, timeRef}
|
||||||
|
static uint8_t UBX_CFG_RATE_1HZ[] = {0xE8, 0x03, 0x01, 0x00, 0x01, 0x00};
|
||||||
|
static uint8_t UBX_CFG_RATE_2HZ[] = {0xF4, 0x01, 0x01, 0x00, 0x01, 0x00};
|
||||||
|
static uint8_t UBX_CFG_RATE_5HZ[] = {0xC8, 0x00, 0x01, 0x00, 0x01, 0x00};
|
||||||
|
static uint8_t UBX_CFG_RATE_10HZ[] = {0x64, 0x00, 0x01, 0x00, 0x01, 0x00};
|
||||||
|
|
||||||
|
#endif // ADAFRUIT_UBLOX_UBX_MESSAGES
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
/*!
|
/*!
|
||||||
* @file Adafruit_uBlox_typedef.h
|
* @file Adafruit_uBlox_typedef.h
|
||||||
*
|
*
|
||||||
* Type definitions for u-blox GPS/RTK module messages
|
* Type definitions for u-blox GPS/RTK module messages
|
||||||
*
|
*
|
||||||
* Adafruit invests time and resources providing this open source code,
|
* Adafruit invests time and resources providing this open source code,
|
||||||
* please support Adafruit and open-source hardware by purchasing
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
* products from Adafruit!
|
* products from Adafruit!
|
||||||
*
|
*
|
||||||
* Written by Your Name for Project Name.
|
* Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
*
|
*
|
||||||
* MIT license, all text here must be included in any redistribution.
|
* MIT license, all text here must be included in any redistribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -17,46 +17,46 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
// UBX Message Classes
|
/** UBX protocol message class identifiers. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UBX_CLASS_NAV = 0x01, // Navigation Results
|
UBX_CLASS_NAV = 0x01, // Navigation Results
|
||||||
UBX_CLASS_RXM = 0x02, // Receiver Manager Messages
|
UBX_CLASS_RXM = 0x02, // Receiver Manager Messages
|
||||||
UBX_CLASS_INF = 0x04, // Information Messages
|
UBX_CLASS_INF = 0x04, // Information Messages
|
||||||
UBX_CLASS_ACK = 0x05, // Acknowledgements
|
UBX_CLASS_ACK = 0x05, // Acknowledgements
|
||||||
UBX_CLASS_CFG = 0x06, // Configuration
|
UBX_CLASS_CFG = 0x06, // Configuration
|
||||||
UBX_CLASS_UPD = 0x09, // Firmware Update
|
UBX_CLASS_UPD = 0x09, // Firmware Update
|
||||||
UBX_CLASS_MON = 0x0A, // Monitoring
|
UBX_CLASS_MON = 0x0A, // Monitoring
|
||||||
UBX_CLASS_AID = 0x0B, // AssistNow Aiding
|
UBX_CLASS_AID = 0x0B, // AssistNow Aiding
|
||||||
UBX_CLASS_TIM = 0x0D, // Timing
|
UBX_CLASS_TIM = 0x0D, // Timing
|
||||||
UBX_CLASS_ESF = 0x10, // External Sensor Fusion
|
UBX_CLASS_ESF = 0x10, // External Sensor Fusion
|
||||||
UBX_CLASS_MGA = 0x13, // Multiple GNSS Assistance
|
UBX_CLASS_MGA = 0x13, // Multiple GNSS Assistance
|
||||||
UBX_CLASS_LOG = 0x21, // Logging
|
UBX_CLASS_LOG = 0x21, // Logging
|
||||||
UBX_CLASS_SEC = 0x27, // Security
|
UBX_CLASS_SEC = 0x27, // Security
|
||||||
UBX_CLASS_HNR = 0x28, // High Rate Navigation
|
UBX_CLASS_HNR = 0x28, // High Rate Navigation
|
||||||
UBX_CLASS_NMEA = 0xF0 // NMEA Standard Messages
|
UBX_CLASS_NMEA = 0xF0 // NMEA Standard Messages
|
||||||
} UBXMessageClass;
|
} UBXMessageClass;
|
||||||
|
|
||||||
// UBX CFG Message IDs
|
/** UBX CFG Message IDs. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UBX_CFG_PRT = 0x00, // Port Configuration
|
UBX_CFG_PRT = 0x00, // Port Configuration
|
||||||
UBX_CFG_MSG = 0x01, // Message Configuration
|
UBX_CFG_MSG = 0x01, // Message Configuration
|
||||||
UBX_CFG_RST = 0x04, // Reset Receiver
|
UBX_CFG_RST = 0x04, // Reset Receiver
|
||||||
UBX_CFG_RATE = 0x08, // Navigation/Measurement Rate Settings
|
UBX_CFG_RATE = 0x08, // Navigation/Measurement Rate Settings
|
||||||
UBX_CFG_CFG = 0x09, // Clear, Save, and Load Configurations
|
UBX_CFG_CFG = 0x09, // Clear, Save, and Load Configurations
|
||||||
UBX_CFG_NAVX5 = 0x23, // Navigation Engine Settings
|
UBX_CFG_NAVX5 = 0x23, // Navigation Engine Settings
|
||||||
UBX_CFG_GNSS = 0x3E, // GNSS Configuration
|
UBX_CFG_GNSS = 0x3E, // GNSS Configuration
|
||||||
UBX_CFG_PMS = 0x86 // Power Mode Setup
|
UBX_CFG_PMS = 0x86 // Power Mode Setup
|
||||||
} UBXCfgMessageId;
|
} UBXCfgMessageId;
|
||||||
|
|
||||||
// Return values for functions that wait for acknowledgment
|
/** Return values for functions that wait for acknowledgment. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UBX_SEND_SUCCESS = 0, // Message was acknowledged (ACK)
|
UBX_SEND_SUCCESS = 0, // Message was acknowledged (ACK)
|
||||||
UBX_SEND_NAK, // Message was not acknowledged (NAK)
|
UBX_SEND_NAK, // Message was not acknowledged (NAK)
|
||||||
UBX_SEND_FAIL, // Failed to send the message
|
UBX_SEND_FAIL, // Failed to send the message
|
||||||
UBX_SEND_TIMEOUT // Timed out waiting for ACK/NAK
|
UBX_SEND_TIMEOUT // Timed out waiting for ACK/NAK
|
||||||
} UBXSendStatus;
|
} UBXSendStatus;
|
||||||
|
|
||||||
// Port ID enum for different interfaces
|
/** Port ID enum for different interfaces. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UBX_PORT_DDC = 0, // I2C / DDC port
|
UBX_PORT_DDC = 0, // I2C / DDC port
|
||||||
UBX_PORT_UART1 = 1, // UART1 port
|
UBX_PORT_UART1 = 1, // UART1 port
|
||||||
|
|
@ -65,7 +65,7 @@ typedef enum {
|
||||||
UBX_PORT_SPI = 4 // SPI port
|
UBX_PORT_SPI = 4 // SPI port
|
||||||
} UBXPortId;
|
} UBXPortId;
|
||||||
|
|
||||||
// UART mode flags (Charlen, Parity & Stop bit settings)
|
/** UART mode flags (Charlen, Parity & Stop bit settings). */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UBX_UART_MODE_8N1 = 0x000, // 8-bit, no parity, 1 stop bit
|
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_8E1 = 0x100, // 8-bit, even parity, 1 stop bit
|
||||||
|
|
@ -82,26 +82,27 @@ typedef enum {
|
||||||
} UBXUARTMode;
|
} UBXUARTMode;
|
||||||
|
|
||||||
// Protocol flags for inProtoMask and outProtoMask
|
// Protocol flags for inProtoMask and outProtoMask
|
||||||
#define UBX_PROTOCOL_UBX 0x0001 // UBX protocol
|
#define UBX_PROTOCOL_UBX 0x0001 ///< UBX protocol
|
||||||
#define UBX_PROTOCOL_NMEA 0x0002 // NMEA protocol
|
#define UBX_PROTOCOL_NMEA 0x0002 ///< NMEA protocol
|
||||||
#define UBX_PROTOCOL_RTCM 0x0004 // RTCM2 protocol (only for inProtoMask)
|
#define UBX_PROTOCOL_RTCM 0x0004 ///< RTCM2 protocol (only for inProtoMask)
|
||||||
#define UBX_PROTOCOL_RTCM3 0x0020 // RTCM3 protocol
|
#define UBX_PROTOCOL_RTCM3 0x0020 ///< RTCM3 protocol
|
||||||
|
|
||||||
// CFG-PRT (Port Configuration) Message
|
/** UBX CFG-PRT (Port Configuration) message structure. 20 bytes total.
|
||||||
// Total size: 20 bytes
|
*/
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint8_t portID; // Port identifier (0=DDC/I2C, 1=UART1, 2=UART2, 3=USB, 4=SPI)
|
uint8_t
|
||||||
uint8_t reserved1; // Reserved
|
portID; ///< Port identifier (0=DDC/I2C, 1=UART1, 2=UART2, 3=USB, 4=SPI)
|
||||||
uint16_t txReady; // TX ready PIN configuration
|
uint8_t reserved1; ///< Reserved
|
||||||
uint32_t mode; // UART mode (bit field) or Reserved for non-UART ports
|
uint16_t txReady; ///< TX ready PIN configuration
|
||||||
uint32_t baudRate; // Baudrate in bits/second (UART only)
|
uint32_t mode; ///< UART mode (bit field) or Reserved for non-UART ports
|
||||||
uint16_t inProtoMask; // Input protocol mask
|
uint32_t baudRate; ///< Baudrate in bits/second (UART only)
|
||||||
uint16_t outProtoMask; // Output protocol mask
|
uint16_t inProtoMask; ///< Input protocol mask
|
||||||
uint16_t flags; // Flags bit field
|
uint16_t outProtoMask; ///< Output protocol mask
|
||||||
uint16_t reserved2; // Reserved
|
uint16_t flags; ///< Flags bit field
|
||||||
} fields;
|
uint16_t reserved2; ///< Reserved
|
||||||
uint8_t raw[20];
|
} fields; ///< Fields for CFG-PRT message
|
||||||
|
uint8_t raw[20]; ///< Raw byte array for CFG-PRT message
|
||||||
} UBX_CFG_PRT_t;
|
} UBX_CFG_PRT_t;
|
||||||
|
|
||||||
#endif // ADAFRUIT_UBLOX_TYPEDEF_H
|
#endif // ADAFRUIT_UBLOX_TYPEDEF_H
|
||||||
|
|
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Adafruit Industries
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
13
README.md
Normal file
13
README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Adafruit uBlox Library [](https://github.com/adafruit/Adafruit_uBlox/actions)
|
||||||
|
|
||||||
|
This is a driver library to abstract away the details of communicating with u-blox GPS and RTK modules. It provides a simple interface for sending and receiving data over a generic Stream interface.
|
||||||
|
|
||||||
|
This library provides two main classes:
|
||||||
|
- `Adafruit_UBX`: Interfaces with u-blox GPS/RTK modules using any stream object as an input (UART, DDC, or other) and parses UBX protocol messages.
|
||||||
|
- `Adafruit_UBloxDDC`: Interface for communicating with u-blox modules over DDC (I2C).
|
||||||
|
|
||||||
|
For parsing NMEA sentences, we provide an example for using this library with the [Adafruit_GPS library](https://github.com/adafruit/Adafruit_GPS).
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||||
|
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
63
examples/ublox_ddc/ublox_ddc.ino
Normal file
63
examples/ublox_ddc/ublox_ddc.ino
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*!
|
||||||
|
* @file ublox_ddc.ino
|
||||||
|
*
|
||||||
|
* Example sketch demonstrating the use of the Adafruit_UBloxDDC library
|
||||||
|
* with u-blox GPS/RTK modules over I2C (DDC) interface.
|
||||||
|
*
|
||||||
|
* This example simply streams all raw bytes from the GPS module to the
|
||||||
|
* Serial port so you can see the NMEA sentences in their original format.
|
||||||
|
*
|
||||||
|
* Written by Ladyada for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* MIT license, all text above must be included in any redistribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Adafruit_UBloxDDC.h>
|
||||||
|
|
||||||
|
// Create Adafruit_UBloxDDC object with default I2C address (0x42)
|
||||||
|
Adafruit_UBloxDDC gps;
|
||||||
|
|
||||||
|
// Buffer for reading chunks of data
|
||||||
|
const size_t BUFFER_SIZE = 64;
|
||||||
|
uint8_t buffer[BUFFER_SIZE];
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Initialize serial port for debugging
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
; // Wait for serial port to connect
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Adafruit UBlox DDC Raw NMEA Stream Example");
|
||||||
|
|
||||||
|
// Initialize GPS module
|
||||||
|
if (gps.begin()) {
|
||||||
|
Serial.println("GPS module found!");
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to connect to GPS module!");
|
||||||
|
while (1); // Don't proceed if we couldn't connect to the module
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Streaming raw data from GPS module...");
|
||||||
|
Serial.println("------------------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Check how many bytes are available
|
||||||
|
int bytesAvailable = gps.available();
|
||||||
|
|
||||||
|
if (bytesAvailable > 0) {
|
||||||
|
// Read up to BUFFER_SIZE bytes at a time
|
||||||
|
size_t bytesToRead = min(bytesAvailable, (int)BUFFER_SIZE);
|
||||||
|
size_t bytesRead = gps.readBytes(buffer, bytesToRead);
|
||||||
|
|
||||||
|
// Stream the bytes directly to Serial
|
||||||
|
// This will show raw NMEA sentences
|
||||||
|
for (size_t i = 0; i < bytesRead; i++) {
|
||||||
|
Serial.write(buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short delay to prevent overwhelming the serial monitor
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
192
examples/ublox_ddc_parse_nmea/ublox_ddc_parse_nmea.ino
Normal file
192
examples/ublox_ddc_parse_nmea/ublox_ddc_parse_nmea.ino
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*!
|
||||||
|
* @file ublox_ddc_parse_nmea.ino
|
||||||
|
*
|
||||||
|
* Example sketch demonstrating parsing NMEA sentences from a u-blox GPS/RTK module
|
||||||
|
* using the Adafruit_GPS and Adafruit_UBX libraries.
|
||||||
|
*
|
||||||
|
* Written by Brent Rubell for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* MIT license, all text above must be included in any redistribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Adafruit_GPS.h>
|
||||||
|
#include <Adafruit_UBX.h>
|
||||||
|
#include <Adafruit_UBloxDDC.h>
|
||||||
|
|
||||||
|
// Adafruit_UBloxDDC object with default I2C address (0x42)
|
||||||
|
Adafruit_UBloxDDC gps;
|
||||||
|
// Create Adafruit_UBX parser using the DDC object as input stream
|
||||||
|
Adafruit_UBX ubx(gps);
|
||||||
|
// Create Adafruit_GPS object for use as a NMEA parser only
|
||||||
|
Adafruit_GPS NMEA;
|
||||||
|
|
||||||
|
// Buffer to store NMEA sentences from the module (may be partial sentences)
|
||||||
|
const size_t SZ_NMEA_BUFFER = 128;
|
||||||
|
uint8_t buffer[SZ_NMEA_BUFFER];
|
||||||
|
uint32_t timer = millis();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Sets the NMEA output to only RMC and GGA sentences and waits for an
|
||||||
|
* acknowledgment. All other common NMEA sentences are disabled to increase
|
||||||
|
* loop() performance by decreasing the amount of messages output by the UBlox
|
||||||
|
* module.
|
||||||
|
* @return Status indicating success, failure, or timeout
|
||||||
|
*/
|
||||||
|
UBXSendStatus SetNMEAOutputRMCGGAOnly() {
|
||||||
|
UBXSendStatus status;
|
||||||
|
// Disable all common NMEA messages
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_GLL_DISABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_GLL_DISABLE));
|
||||||
|
if (status != UBX_SEND_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_GSA_DISABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_GSA_DISABLE));
|
||||||
|
if (status != UBX_SEND_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_GSV_DISABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_GSV_DISABLE));
|
||||||
|
if (status != UBX_SEND_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_VTG_DISABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_VTG_DISABLE));
|
||||||
|
if (status != UBX_SEND_SUCCESS)
|
||||||
|
return status;
|
||||||
|
// Enable only GGA and RMC messages
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_GGA_ENABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_GGA_ENABLE));
|
||||||
|
if (status != UBX_SEND_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = ubx.sendMessageWithAck(UBX_CLASS_CFG, UBX_CFG_MSG,
|
||||||
|
UBX_CFG_MSG_NMEA_RMC_ENABLE,
|
||||||
|
sizeof(UBX_CFG_MSG_NMEA_RMC_ENABLE));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Initialize serial port for debugging
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
; // Wait for serial port to connect
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize GPS module
|
||||||
|
if (!gps.begin()) {
|
||||||
|
Serial.println("Failed to initialize GPS module!");
|
||||||
|
while (1); // Halt if initialization fails
|
||||||
|
}
|
||||||
|
Serial.println("GPS module initialized successfully!");
|
||||||
|
|
||||||
|
// Initialize the u-blox parser
|
||||||
|
if (!ubx.begin()) {
|
||||||
|
Serial.println("Failed to initialize u-blox parser!");
|
||||||
|
while (1); // Halt if initialization fails
|
||||||
|
}
|
||||||
|
Serial.println("u-blox parser initialized successfully!");
|
||||||
|
ubx.verbose_debug = 3; // Set debug level to verbose
|
||||||
|
|
||||||
|
// Set NMEA output on DDC to RMC and GGA sentences only
|
||||||
|
UBXSendStatus status = SetNMEAOutputRMCGGAOnly();
|
||||||
|
if (status != UBX_SEND_SUCCESS) {
|
||||||
|
Serial.print("Failed to configure NMEA output [status code: ");
|
||||||
|
Serial.print(status);
|
||||||
|
Serial.println("]");
|
||||||
|
while (1); // Halt if setting NMEA output fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
static char nmeaSentenceBuf[SZ_NMEA_BUFFER];
|
||||||
|
static size_t nmeaSentenceIdx = 0;
|
||||||
|
|
||||||
|
// Check how many bytes are available
|
||||||
|
int bytesAvailable = gps.available();
|
||||||
|
|
||||||
|
if (bytesAvailable > 0) {
|
||||||
|
// Read up to SZ_NMEA_BUFFER bytes at a time
|
||||||
|
size_t bytesToRead = min(bytesAvailable, (int)SZ_NMEA_BUFFER);
|
||||||
|
size_t bytesRead = gps.readBytes(buffer, bytesToRead);
|
||||||
|
|
||||||
|
// Build NMEA sentences and parse when complete
|
||||||
|
for (size_t i = 0; i < bytesRead; i++) {
|
||||||
|
char c = buffer[i];
|
||||||
|
|
||||||
|
// Add to buffer if space available
|
||||||
|
if (nmeaSentenceIdx < SZ_NMEA_BUFFER - 1) {
|
||||||
|
nmeaSentenceBuf[nmeaSentenceIdx++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for end of NMEA sentence
|
||||||
|
if (c == '\n') {
|
||||||
|
nmeaSentenceBuf[nmeaSentenceIdx] = '\0';
|
||||||
|
|
||||||
|
// Parse the NMEA sentence
|
||||||
|
if (NMEA.parse(nmeaSentenceBuf)) {
|
||||||
|
// Successfully parsed sentence!
|
||||||
|
Serial.println(nmeaSentenceBuf);
|
||||||
|
// approximately every 2 seconds or so, print out the current stats
|
||||||
|
if (millis() - timer > 2000) {
|
||||||
|
timer = millis(); // reset the timer
|
||||||
|
Serial.print("\nTime: ");
|
||||||
|
if (NMEA.hour < 10) {
|
||||||
|
Serial.print('0');
|
||||||
|
}
|
||||||
|
Serial.print(NMEA.hour, DEC);
|
||||||
|
Serial.print(':');
|
||||||
|
if (NMEA.minute < 10) {
|
||||||
|
Serial.print('0');
|
||||||
|
}
|
||||||
|
Serial.print(NMEA.minute, DEC);
|
||||||
|
Serial.print(':');
|
||||||
|
if (NMEA.seconds < 10) {
|
||||||
|
Serial.print('0');
|
||||||
|
}
|
||||||
|
Serial.print(NMEA.seconds, DEC);
|
||||||
|
Serial.print('.');
|
||||||
|
if (NMEA.milliseconds < 10) {
|
||||||
|
Serial.print("00");
|
||||||
|
} else if (NMEA.milliseconds > 9 && NMEA.milliseconds < 100) {
|
||||||
|
Serial.print("0");
|
||||||
|
}
|
||||||
|
Serial.println(NMEA.milliseconds);
|
||||||
|
Serial.print("Date: ");
|
||||||
|
Serial.print(NMEA.day, DEC);
|
||||||
|
Serial.print('/');
|
||||||
|
Serial.print(NMEA.month, DEC);
|
||||||
|
Serial.print("/20");
|
||||||
|
Serial.println(NMEA.year, DEC);
|
||||||
|
Serial.print("Fix: ");
|
||||||
|
Serial.print((int)NMEA.fix);
|
||||||
|
Serial.print(" quality: ");
|
||||||
|
Serial.println((int)NMEA.fixquality);
|
||||||
|
if (NMEA.fix) {
|
||||||
|
Serial.print("Location: ");
|
||||||
|
Serial.print(NMEA.latitude, 4);
|
||||||
|
Serial.print(NMEA.lat);
|
||||||
|
Serial.print(", ");
|
||||||
|
Serial.print(NMEA.longitude, 4);
|
||||||
|
Serial.println(NMEA.lon);
|
||||||
|
Serial.print("Speed (knots): ");
|
||||||
|
Serial.println(NMEA.speed);
|
||||||
|
Serial.print("Angle: ");
|
||||||
|
Serial.println(NMEA.angle);
|
||||||
|
Serial.print("Altitude: ");
|
||||||
|
Serial.println(NMEA.altitude);
|
||||||
|
Serial.print("Satellites: ");
|
||||||
|
Serial.println((int)NMEA.satellites);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to parse sentence.");
|
||||||
|
}
|
||||||
|
nmeaSentenceIdx = 0; // Reset index for next sentence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Short delay to prevent overwhelming the module
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
151
examples/ublox_ubxtest/ublox_ubxtest.ino
Normal file
151
examples/ublox_ubxtest/ublox_ubxtest.ino
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*!
|
||||||
|
* @file ublox_ubxtest.ino
|
||||||
|
*
|
||||||
|
* Test sketch to verify switching to UBX protocol mode
|
||||||
|
* This example uses I2C (DDC) to communicate with a u-blox module.
|
||||||
|
*
|
||||||
|
* Written by Ladyada for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* MIT license, all text above must be included in any redistribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Adafruit_UBloxDDC.h"
|
||||||
|
#include "Adafruit_UBX.h"
|
||||||
|
#include "Adafruit_uBlox_typedef.h"
|
||||||
|
|
||||||
|
// Create Adafruit_UBloxDDC object with default I2C address (0x42)
|
||||||
|
Adafruit_UBloxDDC ddc;
|
||||||
|
|
||||||
|
// Create Adafruit_UBX parser using the DDC object as input stream
|
||||||
|
Adafruit_UBX ubx(ddc);
|
||||||
|
|
||||||
|
// Variables to track message statistics
|
||||||
|
uint32_t messageCount = 0;
|
||||||
|
uint32_t lastMsgTime = 0;
|
||||||
|
bool ubxModeConfirmed = false;
|
||||||
|
|
||||||
|
// Callback function for UBX messages
|
||||||
|
void ubxMessageCallback(uint8_t msgClass, uint8_t msgId, uint16_t payloadLen, uint8_t *payload) {
|
||||||
|
messageCount++;
|
||||||
|
lastMsgTime = millis();
|
||||||
|
|
||||||
|
// First successful UBX message confirms we're in UBX mode
|
||||||
|
if (!ubxModeConfirmed) {
|
||||||
|
ubxModeConfirmed = true;
|
||||||
|
Serial.println("SUCCESS: UBX mode confirmed! Receiving UBX messages.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print message details
|
||||||
|
Serial.print("UBX Message: Class 0x");
|
||||||
|
if (msgClass < 0x10) Serial.print("0");
|
||||||
|
Serial.print(msgClass, HEX);
|
||||||
|
|
||||||
|
Serial.print(", ID 0x");
|
||||||
|
if (msgId < 0x10) Serial.print("0");
|
||||||
|
Serial.print(msgId, HEX);
|
||||||
|
|
||||||
|
Serial.print(", Length: ");
|
||||||
|
Serial.print(payloadLen);
|
||||||
|
Serial.println(" bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Initialize serial port for debugging
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
; // Wait for serial port to connect
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("\n\n------------- UBX Mode Test -------------");
|
||||||
|
Serial.println("This test will try to switch a u-blox module to UBX protocol");
|
||||||
|
|
||||||
|
// Initialize GPS module
|
||||||
|
Serial.print("Connecting to u-blox module via I2C...");
|
||||||
|
if (ddc.begin()) {
|
||||||
|
Serial.println(" SUCCESS!");
|
||||||
|
} else {
|
||||||
|
Serial.println(" FAILED!");
|
||||||
|
Serial.println("Check connections and try again.");
|
||||||
|
while (1); // Don't proceed if we couldn't connect to the module
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show current data format (should be NMEA)
|
||||||
|
Serial.println("\nCurrent data format (should be NMEA sentences):");
|
||||||
|
Serial.println("------------------------------------------------");
|
||||||
|
|
||||||
|
// Show some raw data
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
while (millis() - startTime < 3000) {
|
||||||
|
if (ddc.available()) {
|
||||||
|
int c = ddc.read();
|
||||||
|
if (c != -1) {
|
||||||
|
Serial.write(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("\n------------------------------------------------");
|
||||||
|
|
||||||
|
// Initialize UBX parser and set debug mode
|
||||||
|
ubx.begin();
|
||||||
|
ubx.verbose_debug = 2; // Enable basic debug output
|
||||||
|
|
||||||
|
// Set the callback function for UBX messages
|
||||||
|
ubx.setMessageCallback(ubxMessageCallback);
|
||||||
|
|
||||||
|
// Now switch to UBX mode
|
||||||
|
Serial.println("\nSwitching to UBX-only mode on DDC port...");
|
||||||
|
|
||||||
|
UBXSendStatus status = ubx.setUBXOnly(UBX_PORT_DDC, true, 1000);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case UBX_SEND_SUCCESS:
|
||||||
|
Serial.println("SUCCESS: UBX configuration command acknowledged!");
|
||||||
|
break;
|
||||||
|
case UBX_SEND_NAK:
|
||||||
|
Serial.println("ERROR: UBX configuration command was rejected by the module.");
|
||||||
|
break;
|
||||||
|
case UBX_SEND_FAIL:
|
||||||
|
Serial.println("ERROR: Failed to send UBX configuration command.");
|
||||||
|
break;
|
||||||
|
case UBX_SEND_TIMEOUT:
|
||||||
|
Serial.println("ERROR: Timeout waiting for acknowledgment.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != UBX_SEND_SUCCESS) {
|
||||||
|
Serial.println("Continuing anyway to see if we receive any UBX messages...");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("\nWaiting for UBX messages (timeout: 5 seconds)...");
|
||||||
|
lastMsgTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Check for UBX messages
|
||||||
|
ubx.checkMessages();
|
||||||
|
|
||||||
|
// Display statistics every second
|
||||||
|
static uint32_t lastStatsTime = 0;
|
||||||
|
if (millis() - lastStatsTime >= 1000) {
|
||||||
|
lastStatsTime = millis();
|
||||||
|
Serial.print("Status: Messages received: ");
|
||||||
|
Serial.print(messageCount);
|
||||||
|
|
||||||
|
// Check for timeout
|
||||||
|
if (!ubxModeConfirmed && (millis() - lastMsgTime > 5000)) {
|
||||||
|
Serial.println("\n\nTIMEOUT: No UBX messages received within 5 seconds.");
|
||||||
|
Serial.println("Possible issues:");
|
||||||
|
Serial.println("1. Module doesn't support UBX protocol on this port");
|
||||||
|
Serial.println("2. Configuration command wasn't received properly");
|
||||||
|
Serial.println("3. Module requires different configuration");
|
||||||
|
Serial.println("\nTry using verbose_debug = 2 for more details.");
|
||||||
|
while(1); // Stop execution
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short delay to prevent overwhelming the processor
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
10
library.properties
Normal file
10
library.properties
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
name=Adafruit uBlox
|
||||||
|
version=1.0.0
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=Library for interfacing with u-blox GPS/RTK modules.
|
||||||
|
paragraph=Library for interfacing with u-blox GPS/RTK modules.
|
||||||
|
category=Communication
|
||||||
|
url=https://github.com/adafruit/Adafruit_uBlox/
|
||||||
|
architectures=*
|
||||||
|
depends=Adafruit BusIO, Adafruit GPS Library
|
||||||
Loading…
Reference in a new issue