Merge pull request #49 from sparkfun/release_candidate

v2.0.8
This commit is contained in:
Paul
2021-07-01 13:14:35 +01:00
committed by GitHub
12 changed files with 637 additions and 18 deletions
+13 -1
View File
@@ -17,7 +17,7 @@
</tr>
</table>
u-blox makes some incredible GNSS receivers covering everything from low-cost, highly configurable modules such as the SAM-M8Q all the way up to the surveyor grade ZED-F9P with precision of the diameter of a dime. This library focuses on configuration and control of u-blox devices over I<sup>2</sup>C (called DDC by u-blox) and Serial. The UBX protocol is supported over both I<sup>2</sup>C and serial, and is a much easier and lighterweight interface to a GNSS module. Stop polling messages and parsing NMEA data! Simply ask for the datums you need and receive an automatic callback when they arrive.
u-blox makes some incredible GNSS receivers covering everything from low-cost, highly configurable modules such as the SAM-M8Q all the way up to the surveyor grade ZED-F9P with precision of the diameter of a dime. This library supports configuration and control of u-blox devices over I<sup>2</sup>C (called DDC by u-blox), Serial and - as of v2.0.8 (thank you @aberridg) - SPI too! The UBX protocol is a much easier and lighterweight interface to a GNSS module. Stop polling messages and parsing NMEA data! Simply ask for the datums you need and receive an automatic callback when they arrive.
This library can be installed via the Arduino Library manager. Search for **SparkFun u-blox GNSS**.
@@ -53,6 +53,18 @@ Migrating to v2.0 is easy. There are two small changes all users will need to ma
If you are using the Dead Reckoning Sensor Fusion or High Dynamic Rate messages, you will need to make more small changes to your code. Please see the [dead reckoning examples](./examples/Dead_Reckoning) for more details. There is more detail available in [Theory.md](./Theory.md#migrating-your-code-to-v20) if you need it.
## SPI Support
In v2.0.8 we added support for SPI, based on a contribution by @aberridg. Thank you Andrew!
We have tested the SPI interface on as many platforms and modules as we could pull together. It works perfectly on most but not quite all combinations.
For reasons we don't understand yet, the ZED-F9P and Teensy 3.2 don't seem to get along. But Teensy 3.2 and the ZOE-M8Q do play nicely together.
If you notice a combination that does not seem to work, please raise an [issue](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/issues) and we will investigate.
The SPI examples have their [own folder](./examples/SPI).
Please check the module datasheets for details on what clock speeds and data rates each module supports. The maximum clock speed is typically 5.5MHz and the maximum transfer rate is typically 125kBytes/s.
## Max (400kHz) I<sup>2</sup>C Support
To achieve 400kHz I<sup>2</sup>C speed please be sure to remove all pull-ups on the I<sup>2</sup>C bus. Most, if not all, u-blox modules include internal pull ups on the I<sup>2</sup>C lines (sometimes called DDC in their manuals). Cut all I<sup>2</sup>C pull up jumpers and/or remove them from peripheral boards. Otherwise, various data glitches can occur. See issues [38](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/38) and [40](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40) for more information. If possible, run the I<sup>2</sup>C bus at 100kHz.
@@ -85,7 +85,7 @@ void loop()
if (myGNSS.powerSaveMode()) // Defaults to true
Serial.println(F("Power Save Mode enabled."));
else
Serial.println(F("***!!! Power Save Mode FAILED !!!***"));
Serial.println(F("*** Power Save Mode FAILED ***"));
}
else if (incoming == '2')
{
@@ -93,14 +93,14 @@ void loop()
if (myGNSS.powerSaveMode(false))
Serial.println(F("Power Save Mode disabled."));
else
Serial.println(F("***!!! Power Save Disable FAILED !!!***"));
Serial.println(F("*** Power Save Disable FAILED ***"));
}
// Read and print the new low power mode
uint8_t lowPowerMode = myGNSS.getPowerSaveMode();
if (lowPowerMode == 255)
{
Serial.println(F("***!!! getPowerSaveMode FAILED !!!***"));
Serial.println(F("*** getPowerSaveMode FAILED ***"));
}
else
{
@@ -68,7 +68,7 @@ void setup()
if (myGNSS.setDynamicModel(DYN_MODEL_PORTABLE) == false) // Set the dynamic model to PORTABLE
{
Serial.println(F("***!!! Warning: setDynamicModel failed !!!***"));
Serial.println(F("*** Warning: setDynamicModel failed ***"));
}
else
{
@@ -79,7 +79,7 @@ void setup()
uint8_t newDynamicModel = myGNSS.getDynamicModel();
if (newDynamicModel == DYN_MODEL_UNKNOWN)
{
Serial.println(F("***!!! Warning: getDynamicModel failed !!!***"));
Serial.println(F("*** Warning: getDynamicModel failed ***"));
}
else
{
@@ -39,6 +39,9 @@ void setup()
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
//This will pipe all NMEA sentences to the serial port so we can see them
myGNSS.setNMEAOutputPort(Serial);
}
@@ -91,6 +91,12 @@ void loop()
Serial.print(myGNSS.getSecond());
Serial.print(" Time is ");
if (myGNSS.getTimeFullyResolved() == false)
{
Serial.print("not fully resolved but ");
} else {
Serial.print("fully resolved and ");
}
if (myGNSS.getTimeValid() == false)
{
Serial.print("not ");
@@ -46,6 +46,9 @@ void setup()
while (1);
}
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_ALL); // Make sure the library is passing all NMEA messages to processNMEA
myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_GGA); // Or, we can be kind to MicroNMEA and _only_ pass the GGA messages to it
@@ -0,0 +1,123 @@
/*
Reading lat and long via UBX binary commands over SPI
By: Andrew Berridge
Date: 27th June 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to query a u-blox module for its lat/long/altitude over SPI. We also
turn off the NMEA output on the SPI port. This decreases the amount of SPI traffic
dramatically.
Note: Long/lat are large numbers because they are * 10^7. To convert lat/long
to something google maps understands simply divide the numbers by 10,000,000. We
do this so that we don't have to use floating point numbers.
Leave NMEA parsing behind. Now you can simply ask the module for the datums you want!
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
NEO-M8U: https://www.sparkfun.com/products/16329
NEO-M9N: https://www.sparkfun.com/products/17285
Hardware Connections:
You need to connect the SPI pins from your microcontroller to the specific pins on your SparkFun product.
Connections will vary based on your microcontroller, but for reference please refer to this tutorial on SPI:
https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/all
Most new boards now use the terms:
CS - Chip Select
COPI - Controller Out, Peripheral In
CIPO - Controller in, Peripheral Out
SCK - Serial Clock
You can choose any pin for Chip Select, but the others are likely either defined by the library you are using
(see here for the standard Arduino one: https://www.arduino.cc/en/reference/SPI) or the microcontroller. The
ESP32 has two standard, selectable SPI ports, for example.
To enable SPI communication, you will need to solder the DSEL/SPI jumper closed on your u-blox board.
IMPORTANT: there have been reports that some u-blox devices do not respond to the UBX protocol over SPI
with the factory settings. You may find you need to connect to the device via USB and u-center and set
the incoming protocol to UBX only. Make sure you disable all other protocols as inputs if you can't
get things to work! Hopefully this is just a bug in the u-blox firmware that will be fixed soon ;-)
*/
#include <SPI.h> //Needed for SPI to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// #########################################
// Instantiate an instance of the SPI class.
// Your configuration may be different, depending on the microcontroller you are using!
#define spiPort SPI // This is the SPI port on standard Ardino boards. Comment this line if you want to use a different port.
//SPIClass spiPort (HSPI); // This is the default SPI interface on some ESP32 boards. Uncomment this line if you are using ESP32.
// #########################################
const uint8_t csPin = 10; // On ATmega328 boards, SPI Chip Select is usually pin 10. Change this to match your board.
// #########################################
long lastTime = 0; //Simple local timer. Limits amount of SPI traffic to u-blox module.
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println(F("SparkFun u-blox Example"));
spiPort.begin(); // begin the SPI port
//myGNSS.enableDebugging(); // Uncomment this line to see helpful debug messages on Serial
// Connect to the u-blox module using SPI port, csPin and speed setting
// ublox devices generally work up to 5MHz. We'll use 4MHz for this example:
if (myGNSS.begin(spiPort, csPin, 4000000) == false)
{
Serial.println(F("u-blox GNSS not detected on SPI bus. Please check wiring. Freezing."));
while (1);
}
//myGNSS.factoryDefault(); delay(5000); // Uncomment this line to reset the module back to its factory defaults
myGNSS.setPortOutput(COM_PORT_SPI, COM_TYPE_UBX); //Set the SPI port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
}
void loop()
{
//Query module only every second. Doing it more often will just cause SPI traffic.
//The module only responds when a new position is available
if (millis() - lastTime > 1000)
{
lastTime = millis(); //Update the timer
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
Serial.println();
}
}
@@ -0,0 +1,179 @@
/*
Configuring the GNSS to automatically send position reports over SPI
Based on code by: Nathan Seidle and Thorsten von Eicken
SparkFun Electronics
Date: January 3rd, 2019
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
This example shows how to configure the U-Blox GNSS the send navigation reports automatically
and retrieving the latest one via getPVT. This eliminates the blocking in getPVT while the GNSS
produces a fresh navigation solution at the expense of returning a slighly old solution.
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
NEO-M8U: https://www.sparkfun.com/products/16329
NEO-M9N: https://www.sparkfun.com/products/17285
Hardware Connections:
You need to connect the SPI pins from your microcontroller to the specific pins on your SparkFun product.
Connections will vary based on your microcontroller, but for reference please refer to this tutorial on SPI:
https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/all
Most new boards now use the terms:
CS - Chip Select
COPI - Controller Out, Peripheral In
CIPO - Controller in, Peripheral Out
SCK - Serial Clock
You can choose any pin for Chip Select, but the others are likely either defined by the library you are using
(see here for the standard Arduino one: https://www.arduino.cc/en/reference/SPI) or the microcontroller. The
ESP32 has two standard, selectable SPI ports, for example.
To enable SPI communication, you will need to solder the DSEL/SPI jumper closed on your u-blox board.
IMPORTANT: there have been reports that some u-blox devices do not respond to the UBX protocol over SPI
with the factory settings. You may find you need to connect to the device via USB and u-center and set
the incoming protocol to UBX only. Make sure you disable all other protocols as inputs if you can't
get things to work! Hopefully this is just a bug in the u-blox firmware that will be fixed soon ;-)
*/
#include <SPI.h> //Needed for SPI to GNSS
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
// #########################################
// Instantiate an instance of the SPI class.
// Your configuration may be different, depending on the microcontroller you are using!
#define spiPort SPI // This is the SPI port on standard Ardino boards. Comment this line if you want to use a different port.
//SPIClass spiPort (HSPI); // This is the default SPI interface on some ESP32 boards. Uncomment this line if you are using ESP32.
// #########################################
const uint8_t csPin = 10; // On ATmega328 boards, SPI Chip Select is usually pin 10. Change this to match your board.
// #########################################
void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");
spiPort.begin(); // begin the SPI port
//myGNSS.enableDebugging(); // Uncomment this line to see helpful debug messages on Serial
// Connect to the u-blox module using SPI port, csPin and speed setting
// ublox devices generally work up to 5MHz. We'll use 4MHz for this example:
if (myGNSS.begin(spiPort, csPin, 4000000) == false)
{
Serial.println(F("u-blox GNSS not detected on SPI bus. Please check wiring. Freezing."));
while (1);
}
//myGNSS.factoryDefault(); delay(5000); // Uncomment this line to reset the module back to its factory defaults
myGNSS.setPortOutput(COM_PORT_SPI, COM_TYPE_UBX); //Set the SPI port to output UBX only (turn off NMEA noise)
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
myGNSS.setNavigationFrequency(2); //Produce two solutions per second
myGNSS.setAutoPVT(true); //Tell the GNSS to "send" each solution
//myGNSS.saveConfiguration(); //Optional: Save _all_ the current settings to flash and BBR
}
void loop()
{
// Calling getPVT returns true if there actually is a fresh navigation solution available.
// Start the reading only when valid LLH is available
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false))
{
Serial.println();
long latitude = myGNSS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);
long longitude = myGNSS.getLongitude();
Serial.print(F(" Long: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));
long altitude = myGNSS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.print(F(" (mm)"));
byte SIV = myGNSS.getSIV();
Serial.print(F(" SIV: "));
Serial.print(SIV);
int PDOP = myGNSS.getPDOP();
Serial.print(F(" PDOP: "));
Serial.print(PDOP);
Serial.print(F(" (10^-2)"));
int nedNorthVel = myGNSS.getNedNorthVel();
Serial.print(F(" VelN: "));
Serial.print(nedNorthVel);
Serial.print(F(" (mm/s)"));
int nedEastVel = myGNSS.getNedEastVel();
Serial.print(F(" VelE: "));
Serial.print(nedEastVel);
Serial.print(F(" (mm/s)"));
int nedDownVel = myGNSS.getNedDownVel();
Serial.print(F(" VelD: "));
Serial.print(nedDownVel);
Serial.print(F(" (mm/s)"));
int verticalAccEst = myGNSS.getVerticalAccEst();
Serial.print(F(" VAccEst: "));
Serial.print(verticalAccEst);
Serial.print(F(" (mm)"));
int horizontalAccEst = myGNSS.getHorizontalAccEst();
Serial.print(F(" HAccEst: "));
Serial.print(horizontalAccEst);
Serial.print(F(" (mm)"));
int speedAccEst = myGNSS.getSpeedAccEst();
Serial.print(F(" SpeedAccEst: "));
Serial.print(speedAccEst);
Serial.print(F(" (mm/s)"));
int headAccEst = myGNSS.getHeadingAccEst();
Serial.print(F(" HeadAccEst: "));
Serial.print(headAccEst);
Serial.print(F(" (degrees * 10^-5)"));
if (myGNSS.getHeadVehValid() == true) {
int headVeh = myGNSS.getHeadVeh();
Serial.print(F(" HeadVeh: "));
Serial.print(headVeh);
Serial.print(F(" (degrees * 10^-5)"));
int magDec = myGNSS.getMagDec();
Serial.print(F(" MagDec: "));
Serial.print(magDec);
Serial.print(F(" (degrees * 10^-2)"));
int magAcc = myGNSS.getMagAcc();
Serial.print(F(" MagAcc: "));
Serial.print(magAcc);
Serial.print(F(" (degrees * 10^-2)"));
}
Serial.println();
} else {
Serial.print(".");
delay(50);
}
}
+7
View File
@@ -53,8 +53,13 @@ setPacketCfgPayloadSize KEYWORD2
begin KEYWORD2
end KEYWORD2
setI2CpollingWait KEYWORD2
setSPIpollingWait KEYWORD2
setI2CTransactionSize KEYWORD2
getI2CTransactionSize KEYWORD2
setSpiTransactionSize KEYWORD2
getSpiTransactionSize KEYWORD2
setMaxNMEAByteCount KEYWORD2
getMaxNMEAByteCount KEYWORD2
isConnected KEYWORD2
enableDebugging KEYWORD2
disableDebugging KEYWORD2
@@ -66,6 +71,7 @@ disableUBX7Fcheck KEYWORD2
checkUblox KEYWORD2
checkUbloxI2C KEYWORD2
checkUbloxSerial KEYWORD2
checkUbloxSPI KEYWORD2
process KEYWORD2
processNMEA KEYWORD2
@@ -425,6 +431,7 @@ getNanosecond KEYWORD2
getUnixEpoch KEYWORD2
getDateValid KEYWORD2
getTimeValid KEYWORD2
getTimeFullyResolved KEYWORD2
getConfirmedDate KEYWORD2
getConfirmedTime KEYWORD2
getFixType KEYWORD2
+2 -2
View File
@@ -1,9 +1,9 @@
name=SparkFun u-blox GNSS Arduino Library
version=2.0.7
version=2.0.8
author=SparkFun Electronics <techsupport@sparkfun.com>
maintainer=SparkFun Electronics <sparkfun.com>
sentence=Library for I2C and Serial Communication with u-blox GNSS modules<br/><br/>
paragraph=An Arduino Library to enable both I2C and Serial communication for both NMEA reception and binary UBX sending to u-blox modules. Useful for interfacing to the <a href="https://www.sparkfun.com/products/15136">SparkFun GPS-RTK2</a> ZED-F9P, <a href="https://www.sparkfun.com/products/15005">SparkFun GPS-RTK</a> NEO-M8P-2, the <a href="https://www.sparkfun.com/products/15210">SparkFun SAM-M8Q</a>, and the <a href="https://www.sparkfun.com/products/15193">SparkFun ZOE-M8Q</a>. Library also works with other u-blox based boards.<br/><br/>The ZED-F9P and NEO-M8P-2 modules are top-of-the-line modules for high accuracy GNSS and GPS location solutions including RTK. The ZED-F9P is unique in that it is capable of both rover and base station operations allowing the module to become a base station and produce RTCM 3.x correction data.<br/>
paragraph=An Arduino Library to enable I2C, Serial and SPI communication for both NMEA reception and binary UBX sending to u-blox modules. Useful for interfacing to the <a href="https://www.sparkfun.com/products/15136">SparkFun GPS-RTK2</a> ZED-F9P, <a href="https://www.sparkfun.com/products/15005">SparkFun GPS-RTK</a> NEO-M8P-2, the <a href="https://www.sparkfun.com/products/15210">SparkFun SAM-M8Q</a>, and the <a href="https://www.sparkfun.com/products/15193">SparkFun ZOE-M8Q</a>. Library also works with other u-blox based boards.<br/><br/>The ZED-F9P and NEO-M8P-2 modules are top-of-the-line modules for high accuracy GNSS and GPS location solutions including RTK. The ZED-F9P is unique in that it is capable of both rover and base station operations allowing the module to become a base station and produce RTCM 3.x correction data.<br/>
category=Sensors
url=https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library
architectures=*
+248 -5
View File
@@ -451,6 +451,60 @@ boolean SFE_UBLOX_GNSS::begin(Stream &serialPort)
return (connected);
}
// Initialize for SPI
boolean SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed)
{
commType = COMM_TYPE_SPI;
_spiPort = &spiPort;
_csPin = csPin;
_spiSpeed = spiSpeed;
// Initialize the chip select pin
pinMode(_csPin, OUTPUT);
digitalWrite(_csPin, HIGH);
//New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already)
if (packetCfgPayloadSize == 0)
setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
createFileBuffer();
//Create the SPI buffer
if (spiBuffer == NULL) //Memory has not yet been allocated - so use new
{
spiBuffer = new uint8_t[getSpiTransactionSize()];
}
if (spiBuffer == NULL)
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->print(F("begin (SPI): memory allocation failed for SPI Buffer!"));
return (false);
}
}
else
{
// Initialize/clear the SPI buffer - fill it with 0xFF as this is what is received from the UBLOX module if there's no data to be processed
for (uint8_t i = 0; i < getSpiTransactionSize(); i++)
{
spiBuffer[i] = 0xFF;
}
}
// Call isConnected up to three times
boolean connected = isConnected();
if (!connected)
connected = isConnected();
if (!connected)
connected = isConnected();
return (connected);
}
// Allow the user to change I2C polling wait (the minimum interval between I2C data requests - to avoid pounding the bus)
// i2cPollingWait defaults to 100ms and is adjusted automatically when setNavigationFrequency()
// or setHNRNavigationRate() are called. But if the user is using callbacks, it might be advantageous
@@ -460,6 +514,13 @@ void SFE_UBLOX_GNSS::setI2CpollingWait(uint8_t newPollingWait_ms)
i2cPollingWait = newPollingWait_ms;
}
// Allow the user to change SPI polling wait
// (the minimum interval between SPI data requests when no data is available - to avoid pounding the bus)
void SFE_UBLOX_GNSS::setSPIpollingWait(uint8_t newPollingWait_ms)
{
spiPollingWait = newPollingWait_ms;
}
//Sets the global size for I2C transactions
//Most platforms use 32 bytes (the default) but this allows users to increase the transaction
//size if the platform supports it
@@ -473,6 +534,39 @@ uint8_t SFE_UBLOX_GNSS::getI2CTransactionSize(void)
return (i2cTransactionSize);
}
//Sets the global size for the SPI buffer/transactions.
//Call this **before** begin()!
//Note: if the buffer size is too small, incoming characters may be lost if the message sent
//is larger than this buffer. If too big, you may run out of SRAM on constrained architectures!
void SFE_UBLOX_GNSS::setSpiTransactionSize(uint8_t transactionSize)
{
if (spiBuffer == NULL)
{
spiTransactionSize = transactionSize;
}
else
{
if (_printDebug == true)
{
_debugSerial->println(F("setSpiTransactionSize: you need to call setSpiTransactionSize _before_ begin!"));
}
}
}
uint8_t SFE_UBLOX_GNSS::getSpiTransactionSize(void)
{
return (spiTransactionSize);
}
//Sets the size of maxNMEAByteCount
void SFE_UBLOX_GNSS::setMaxNMEAByteCount(int8_t newMax)
{
maxNMEAByteCount = newMax;
}
int8_t SFE_UBLOX_GNSS::getMaxNMEAByteCount(void)
{
return (maxNMEAByteCount);
}
//Returns true if I2C device ack's
boolean SFE_UBLOX_GNSS::isConnected(uint16_t maxWait)
{
@@ -577,7 +671,7 @@ const char *SFE_UBLOX_GNSS::statusString(sfe_ublox_status_e stat)
return "None";
}
// Check for the arrival of new I2C/Serial data
// Check for the arrival of new I2C/Serial/SPI data
//Allow the user to disable the "7F" check (e.g.) when logging RAWX data
void SFE_UBLOX_GNSS::disableUBX7Fcheck(boolean disabled)
@@ -598,6 +692,8 @@ boolean SFE_UBLOX_GNSS::checkUbloxInternal(ubxPacket *incomingUBX, uint8_t reque
return (checkUbloxI2C(incomingUBX, requestedClass, requestedID));
else if (commType == COMM_TYPE_SERIAL)
return (checkUbloxSerial(incomingUBX, requestedClass, requestedID));
else if (commType == COMM_TYPE_SPI)
return (checkUbloxSpi(incomingUBX, requestedClass, requestedID));
return false;
}
@@ -755,6 +851,45 @@ boolean SFE_UBLOX_GNSS::checkUbloxSerial(ubxPacket *incomingUBX, uint8_t request
} //end checkUbloxSerial()
//Checks SPI for data, passing any new bytes to process()
boolean SFE_UBLOX_GNSS::checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID)
{
// Process the contents of the SPI buffer if not empty!
for (uint8_t i = 0; i < spiBufferIndex; i++) {
process(spiBuffer[i], incomingUBX, requestedClass, requestedID);
}
spiBufferIndex = 0;
_spiPort->beginTransaction(SPISettings(_spiSpeed, MSBFIRST, SPI_MODE0));
digitalWrite(_csPin, LOW);
uint8_t byteReturned = _spiPort->transfer(0xFF);
// Note to future self: I think the 0xFF check might cause problems when attempting to process (e.g.) RAWX data
// which could legitimately contain 0xFF within the data stream. But the currentSentence check will certainly help!
// If we are not receiving a sentence (currentSentence == NONE) and the byteReturned is 0xFF,
// i.e. the module has no data for us, then delay for
if ((byteReturned == 0xFF) && (currentSentence == NONE))
{
digitalWrite(_csPin, HIGH);
_spiPort->endTransaction();
delay(spiPollingWait);
return (true);
}
while ((byteReturned != 0xFF) || (currentSentence != NONE))
{
process(byteReturned, incomingUBX, requestedClass, requestedID);
byteReturned = _spiPort->transfer(0xFF);
}
digitalWrite(_csPin, HIGH);
_spiPort->endTransaction();
return (true);
} //end checkUbloxSpi()
//PRIVATE: Check if we have storage allocated for an incoming "automatic" message
boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
{
@@ -2409,7 +2544,7 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
{
packetUBXESFMEAS->data.data[i].data.all = extractLong(msg, 8 + (i * 4));
}
if (msg->len > (8 + (packetUBXESFMEAS->data.flags.bits.numMeas * 4)))
if (msg->len > (8 + (packetUBXESFMEAS->data.flags.bits.numMeas * 4))) // IGNORE COMPILER WARNING comparison between signed and unsigned integer expressions
packetUBXESFMEAS->data.calibTtag = extractLong(msg, 8 + (packetUBXESFMEAS->data.flags.bits.numMeas * 4));
//Mark all datums as fresh (not read before)
@@ -2675,6 +2810,10 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::sendCommand(ubxPacket *outgoingUBX, uint16_t
{
sendSerialCommand(outgoingUBX);
}
else if (commType == COMM_TYPE_SPI)
{
sendSpiCommand(outgoingUBX);
}
if (maxWait > 0)
{
@@ -2781,6 +2920,87 @@ void SFE_UBLOX_GNSS::sendSerialCommand(ubxPacket *outgoingUBX)
_serialPort->write(outgoingUBX->checksumB);
}
// Transfer a byte to SPI. Also capture any bytes received from the UBLOX device during sending and capture them in a small buffer so that
// they can be processed later with process
void SFE_UBLOX_GNSS::spiTransfer(uint8_t byteToTransfer)
{
uint8_t returnedByte = _spiPort->transfer(byteToTransfer);
if ((spiBufferIndex < getSpiTransactionSize()) && (returnedByte != 0xFF || currentSentence != NONE))
{
spiBuffer[spiBufferIndex] = returnedByte;
spiBufferIndex++;
}
}
// Send a command via SPI
void SFE_UBLOX_GNSS::sendSpiCommand(ubxPacket *outgoingUBX)
{
if (spiBuffer == NULL)
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->print(F("sendSpiCommand: no memory allocation for SPI Buffer!"));
}
return;
}
// Start at the beginning of the SPI buffer
spiBufferIndex = 0;
_spiPort->beginTransaction(SPISettings(_spiSpeed, MSBFIRST, SPI_MODE0));
digitalWrite(_csPin, LOW);
//Write header bytes
spiTransfer(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on.
spiTransfer(UBX_SYNCH_2); //b
spiTransfer(outgoingUBX->cls);
spiTransfer(outgoingUBX->id);
spiTransfer(outgoingUBX->len & 0xFF); //LSB
spiTransfer(outgoingUBX->len >> 8);
if (_printDebug)
{
_debugSerial->print(F("sendSpiCommand: "));
_debugSerial->print(UBX_SYNCH_1, HEX);
_debugSerial->print(F(" "));
_debugSerial->print(UBX_SYNCH_2, HEX);
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->cls, HEX);
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->id, HEX);
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->len & 0xFF, HEX);
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->len >> 8, HEX);
}
//Write payload.
for (uint16_t i = 0; i < outgoingUBX->len; i++)
{
spiTransfer(outgoingUBX->payload[i]);
if (_printDebug)
{
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->payload[i], HEX);
}
}
//Write checksum
spiTransfer(outgoingUBX->checksumA);
spiTransfer(outgoingUBX->checksumB);
digitalWrite(_csPin, HIGH);
_spiPort->endTransaction();
if (_printDebug)
{
_debugSerial->print(F(" "));
_debugSerial->print(outgoingUBX->checksumA, HEX);
_debugSerial->print(F(" "));
_debugSerial->println(outgoingUBX->checksumB, HEX);
}
}
//Pretty prints the current ubxPacket
void SFE_UBLOX_GNSS::printPacket(ubxPacket *packet, boolean alwaysPrintPayload)
{
@@ -3004,7 +3224,7 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::waitForACKResponse(ubxPacket *outgoingUBX, ui
} //checkUbloxInternal == true
delayMicroseconds(500);
delay(1); // Allow an RTOS to get an elbow in (#11)
} //while (millis() - startTime < maxTime)
// We have timed out...
@@ -3114,7 +3334,7 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::waitForNoACKResponse(ubxPacket *outgoingUBX,
}
}
delayMicroseconds(500);
delay(1); // Allow an RTOS to get an elbow in (#11)
}
if (_printDebug == true)
@@ -3404,7 +3624,7 @@ boolean SFE_UBLOX_GNSS::pushRawData(uint8_t *dataBytes, size_t numDataBytes, boo
size_t bytesWritten = _serialPort->write(dataBytes, numDataBytes);
return (bytesWritten == numDataBytes);
}
else
else if (commType == COMM_TYPE_I2C)
{
// I2C: split the data up into packets of i2cTransactionSize
size_t bytesLeftToWrite = numDataBytes;
@@ -3439,6 +3659,14 @@ boolean SFE_UBLOX_GNSS::pushRawData(uint8_t *dataBytes, size_t numDataBytes, boo
return (bytesWrittenTotal == numDataBytes);
}
else // SPI
{
if (_printDebug == true)
{
_debugSerial->println(F("pushRawData: SPI not currently supported"));
}
return (false);
}
}
// Support for data logging
@@ -9782,6 +10010,21 @@ bool SFE_UBLOX_GNSS::getTimeValid(uint16_t maxWait)
return ((bool)packetUBXNAVPVT->data.valid.bits.validTime);
}
//Check to see if the UTC time has been fully resolved
bool SFE_UBLOX_GNSS::getTimeFullyResolved(uint16_t maxWait)
{
if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); //Check that RAM has been allocated for the PVT data
if (packetUBXNAVPVT == NULL) //Bail if the RAM allocation failed
return (false);
if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fullyResolved == false)
getPVT(maxWait);
packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fullyResolved = false; //Since we are about to give this to user, mark this data as stale
packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false;
return ((bool)packetUBXNAVPVT->data.valid.bits.fullyResolved);
}
//Get the confirmed date validity
bool SFE_UBLOX_GNSS:: getConfirmedDate(uint16_t maxWait)
{
+48 -5
View File
@@ -51,6 +51,8 @@
#include <Wire.h>
#include <SPI.h>
#include "u-blox_config_keys.h"
#include "u-blox_structs.h"
@@ -492,6 +494,15 @@ enum sfe_ublox_ls_src_e
//#define MAX_PAYLOAD_SIZE 768 //Worst case: UBX_CFG_VALSET packet with 64 keyIDs each with 64 bit values
#endif
// For storing SPI bytes received during sendSpiCommand
#define SFE_UBLOX_SPI_BUFFER_SIZE 128
// Default maximum NMEA byte count
// maxNMEAByteCount was set to 82: https://en.wikipedia.org/wiki/NMEA_0183#Message_structure
// but the u-blox HP (RTK) GGA messages are 88 bytes long
// The user can adjust maxNMEAByteCount by calling setMaxNMEAByteCount
#define SFE_UBLOX_MAX_NMEA_BYTE_COUNT 88
//-=-=-=-=- UBX binary specific variables
struct ubxPacket
{
@@ -560,17 +571,29 @@ public:
boolean begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42); //Returns true if module is detected
//serialPort needs to be perviously initialized to correct baud rate
boolean begin(Stream &serialPort); //Returns true if module is detected
//SPI - supply instance of SPIClass, chip select pin and SPI speed (in Hz)
boolean begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed);
void end(void); //Stop all automatic message processing. Free all used RAM
void setI2CpollingWait(uint8_t newPollingWait_ms); // Allow the user to change the I2C polling wait if required
void setSPIpollingWait(uint8_t newPollingWait_ms); // Allow the user to change the SPI polling wait if required
//Set the max number of bytes set in a given I2C transaction
uint8_t i2cTransactionSize = 32; //Default to ATmega328 limit
//Control the size of the internal I2C transaction amount
void setI2CTransactionSize(uint8_t bufferSize);
uint8_t getI2CTransactionSize(void);
//Set the max number of bytes set in a given I2C transaction
uint8_t i2cTransactionSize = 32; //Default to ATmega328 limit
//Control the size of the spi buffer. If the buffer isn't big enough, we'll start to lose bytes
//That we receive if the buffer is full!
void setSpiTransactionSize(uint8_t bufferSize);
uint8_t getSpiTransactionSize(void);
//Control the size of maxNMEAByteCount
void setMaxNMEAByteCount(int8_t newMax);
int8_t getMaxNMEAByteCount(void);
//Returns true if device answers on _gpsI2Caddress address or via Serial
boolean isConnected(uint16_t maxWait = 1100);
@@ -612,6 +635,7 @@ public:
boolean checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for I2C polling of data, passing any new bytes to process()
boolean checkUbloxSerial(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for serial polling of data, passing any new bytes to process()
boolean checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for spi polling of data, passing any new bytes to process()
// Process the incoming data
@@ -622,12 +646,13 @@ public:
void processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Given a character, file it away into the uxb packet structure
void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags
// Send I2C/Serial commands to the module
// Send I2C/Serial/SPI commands to the module
void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages
sfe_ublox_status_e sendCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait, boolean expectACKonly = false); //Given a packet and payload, send everything including CRC bytes, return true if we got a response
sfe_ublox_status_e sendI2cCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait);
void sendSerialCommand(ubxPacket *outgoingUBX);
void sendSpiCommand(ubxPacket *outgoingUBX);
void printPacket(ubxPacket *packet, boolean alwaysPrintPayload = false); //Useful for debugging
@@ -1061,6 +1086,7 @@ public:
bool getDateValid(uint16_t maxWait = defaultMaxWait);
bool getTimeValid(uint16_t maxWait = defaultMaxWait);
bool getTimeFullyResolved(uint16_t maxWait = defaultMaxWait);
bool getConfirmedDate(uint16_t maxWait = defaultMaxWait);
bool getConfirmedTime(uint16_t maxWait = defaultMaxWait);
@@ -1235,6 +1261,9 @@ private:
//Calculate how much RAM is needed to store the payload for a given automatic message
uint16_t getMaxPayloadSize(uint8_t Class, uint8_t ID);
//Do the actual transfer to SPI
void spiTransfer(uint8_t byteToTransfer);
boolean initGeofenceParams(); // Allocate RAM for currentGeofenceParams and initialize it
boolean initModuleSWVersion(); // Allocate RAM for moduleSWVersion and initialize it
@@ -1273,6 +1302,10 @@ private:
Stream *_nmeaOutputPort = NULL; //The user can assign an output port to print NMEA sentences if they wish
Stream *_debugSerial; //The stream to send debug messages to if enabled
SPIClass *_spiPort; //The instance of SPIClass
uint8_t _csPin; //The chip select pin
uint32_t _spiSpeed; //The speed to use for SPI (Hz)
uint8_t _gpsI2Caddress = 0x42; //Default 7-bit unshifted address of the ublox 6/7/8/M8/F9 series
//This can be changed using the ublox configuration software
@@ -1292,6 +1325,10 @@ private:
uint8_t *payloadCfg = NULL;
uint8_t *payloadAuto = NULL;
uint8_t *spiBuffer = NULL; // A buffer to store any bytes being recieved back from the device while we are sending via SPI
uint8_t spiBufferIndex = 0; // Index into the SPI buffer
uint8_t spiTransactionSize = SFE_UBLOX_SPI_BUFFER_SIZE; //Default size of the SPI buffer
//Init the packet structures and init them with pointers to the payloadAck, payloadCfg, payloadBuf and payloadAuto arrays
ubxPacket packetAck = {0, 0, 0, 0, 0, payloadAck, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
ubxPacket packetBuf = {0, 0, 0, 0, 0, payloadBuf, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
@@ -1308,10 +1345,14 @@ private:
sfe_ublox_packet_buffer_e activePacketBuffer = SFE_UBLOX_PACKET_PACKETBUF;
//Limit checking of new data to every X ms
//If we are expecting an update every X Hz then we should check every half that amount of time
//If we are expecting an update every X Hz then we should check every quarter that amount of time
//Otherwise we may block ourselves from seeing new data
uint8_t i2cPollingWait = 100; //Default to 100ms. Adjusted when user calls setNavigationFrequency() or setHNRNavigationRate() or setMeasurementRate()
//The SPI polling wait is a little different. checkUbloxSpi will delay for this amount before returning if
//there is no data waiting to be read. This prevents waitForACKResponse from pounding the SPI bus too hard.
uint8_t spiPollingWait = 9; //Default to 9ms; waitForACKResponse delays for 1ms on top of this. User can adjust with setSPIPollingWait.
unsigned long lastCheck = 0;
uint16_t ubxFrameCounter; //Count all UBX frame bytes. [Fixed header(2bytes), CLS(1byte), ID(1byte), length(2bytes), payload(x bytes), checksums(2bytes)]
@@ -1319,7 +1360,9 @@ private:
uint8_t rollingChecksumB; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes
int8_t nmeaByteCounter; //Count all NMEA message bytes.
const int8_t maxNMEAByteCount = 82; // Abort NMEA message reception if nmeaByteCounter exceeds this (https://en.wikipedia.org/wiki/NMEA_0183#Message_structure)
// Abort NMEA message reception if nmeaByteCounter exceeds maxNMEAByteCount.
// The user can adjust maxNMEAByteCount by calling setMaxNMEAByteCount
int8_t maxNMEAByteCount = SFE_UBLOX_MAX_NMEA_BYTE_COUNT;
uint8_t nmeaAddressField[6]; // NMEA Address Field - includes the start character (*)
boolean logThisNMEA(); // Return true if we should log this NMEA message
boolean processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA