/*! * @file Adafruit_UBloxDDC.cpp * * @section ddc_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 ddc_dependencies Dependencies * * This library depends on: * Adafruit_BusIO * * @section ddc_author Author * * Written by Limor Fried/Ladyada for Adafruit Industries. * * @section ddc_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((uint16_t)(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; }