Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Adding packetBuf. Changing sendCommand. Requested class and ID passing. #97

Merged
merged 28 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
51de78e
Experimental code - passes requested class and id down to processUBX
PaulZC Apr 17, 2020
2b30dd0
Initial commit - passing the ubxPacket down to processUBX
PaulZC Apr 18, 2020
852dfe1
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
d2503cf
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
95a729a
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
e514b38
Merge branch 'Experimental_ACKResponse' into Experimental__custom_pac…
PaulZC Apr 18, 2020
f101690
Merge branch 'Experimental_custom_packetCfg' into Experimental__custo…
PaulZC Apr 18, 2020
94c32a0
Merge branch 'master' into Experimental_ACKResponse
PaulZC Apr 18, 2020
35c6623
Merge branch 'master' into Experimental__custom_packetCfg
PaulZC Apr 18, 2020
f2ac34f
Merge branch 'master' into Experimental_ACKResponse
PaulZC Apr 18, 2020
98d9edf
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
46cd041
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
3a69451
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 18, 2020
c738f7a
Merge branch 'Experimental_ACKResponse' into Experimental__custom_pac…
PaulZC Apr 18, 2020
12b48f9
Added Example20_SendCustomCommand
PaulZC Apr 18, 2020
927b9cb
Added NOTACKNOWLEDGED
PaulZC Apr 19, 2020
3154c95
Removed typos
PaulZC Apr 19, 2020
8e037a5
Added the bug fix for setNavigationFrequency
PaulZC Apr 19, 2020
b22e812
Added an extra debug print in setNavigationFrequency
PaulZC Apr 19, 2020
e0c3230
Updated so packet pointers are used everywhere from sendCommand down
PaulZC Apr 19, 2020
e724638
Updated the example
PaulZC Apr 19, 2020
3e2aa74
Updated Theory.md
PaulZC Apr 19, 2020
09e52fa
Merge branch 'Experimental__custom_packetCfg' into Experimental_packe…
PaulZC Apr 20, 2020
504efe7
Added packetBuf etc to the header
PaulZC Apr 20, 2020
956b41f
Added most of the packetBuf functionality
PaulZC Apr 20, 2020
66ee2b3
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 20, 2020
d420820
Update Example20_SendCustomCommand.ino
PaulZC Apr 20, 2020
8ce607c
Update SparkFun_Ublox_Arduino_Library.cpp
PaulZC Apr 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ Need a library for the Ublox and Particle? Checkout the [Particle library](https
Repository Contents
-------------------

* **/examples** - Example sketches for the library (.ino). Run these from the Arduino IDE.
* **/examples** - Example sketches for the library (.ino). Run these from the Arduino IDE.
* **/src** - Source files for the library (.cpp, .h).
* **keywords.txt** - Keywords from this library that will be highlighted in the Arduino IDE.
* **library.properties** - General library properties for the Arduino package manager.
* **keywords.txt** - Keywords from this library that will be highlighted in the Arduino IDE.
* **library.properties** - General library properties for the Arduino package manager.

Documentation
--------------
Expand Down Expand Up @@ -85,9 +85,9 @@ As an example, assume that the GPS is set to produce 5 navigation
solutions per second and that the sketch only calls getPVT once a second, then the GPS will queue 5
packets in its internal buffer (about 500 bytes) and the library will read those when getPVT is
called, update its internal copy of the nav data 5 times, and return `true` to the sketch. The
skecth calls `getLatitude`, etc. and retrieve the data of the most recent of those 5 packets.
sketch calls `getLatitude`, etc. and retrieve the data of the most recent of those 5 packets.

Products That Use This Library
Products That Use This Library
---------------------------------

* [GPS-15136](https://www.sparkfun.com/products/15136) - SparkFun GPS-RTK2 ZED-F9P
Expand All @@ -102,7 +102,7 @@ Products That Use This Library
License Information
-------------------

This product is _**open source**_!
This product is _**open source**_!

Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round!

Expand All @@ -111,4 +111,3 @@ Please use, reuse, and modify these files as you see fit. Please maintain attrib
Distributed as-is; no warranty is given.

- Your friends at SparkFun.

11 changes: 8 additions & 3 deletions Theory.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
How I2C (aka DDC) communication works with a uBlox module
===========================================================

When the user calls one of the methods the library will poll the Ublox module for new data.
When the user calls one of the methods the library will poll the Ublox module for new data.

* Wait for a minimum of 25 ms between polls (configured dynamically when update rate is set)
* Write 0xFD to module
Expand All @@ -19,9 +19,14 @@ A method will call **sendCommand()**. This will begin waiting for a response wit

Once **waitForACKResponse()** or **waitForNoACKResponse()** is called the library will start checking the ublox module for new bytes. These bytes may be part of a NMEA sentence, an RTCM sentence, or a UBX packet. The library will file each byte into the appropriate container. Once a given sentence or packet is complete, the appropriate processUBX(), processNMEA() will be called. These functions deal with specific processing for each type.

Note: When interfacing to a ublox module over I2C the **checkUbloxI2C()** will read all bytes currently sitting in the I2C buffer. This may pick up multiple UBX packets. For example, an ACK for a VALSET may be mixed in with an auto-PVT response. We cannot tell **checkUbloxI2C()** to stop once a give ACK is found because we run the risk of leaving bytes in the I2C buffer and loosing them. We don't have this issue with **checkUbloxSerial()**.
Note: When interfacing to a ublox module over I2C **checkUbloxI2C()** will read all bytes currently sitting in the I2C buffer. This may pick up multiple UBX packets. For example, an ACK for a VALSET may be mixed in with an auto-PVT response. We cannot tell **checkUbloxI2C()** to stop once a given ACK is found because we run the risk of leaving bytes in the I2C buffer and losing them. We don't have this issue with **checkUbloxSerial()**.

**processUBX()** will check the CRC of the UBX packet. If validated, the packet will be marked as valid. Once a packet is marked as valid then **processUBXpacket()** is called to extract the contents. This is most commonly used to get the position, velocity, and time (PVT) out of the packet but is also used to check the nature of an ACK packet.

Once a packet has been processed, **waitForACKResponse()/waitForNoACKResponse()** makes the appropriate decision what to do with it. If a packet satisfies the CLS/ID and characteristics of what **waitForACKResponse()/waitForNoACKResponse()** is waiting for, then it returns back to sendCommand. If the packet didn't match or was invalid then **waitForACKResponse()/waitForNoACKResponse()** will continue to wait until the correct packet is received or we time out. **sendCommand()** then returns with true/false depending on the success of **waitForACKResponse()/waitForNoACKResponse()**.
Once a packet has been processed, **waitForACKResponse()/waitForNoACKResponse()** makes the appropriate decision what to do with it. If a packet satisfies the CLS/ID and characteristics of what **waitForACKResponse()/waitForNoACKResponse()** is waiting for, then it returns back to sendCommand. If the packet didn't match or was invalid then **waitForACKResponse()/waitForNoACKResponse()** will continue to wait until the correct packet is received or we time out. **sendCommand()** then returns with a value from the **sfe_ublox_status_e** enum depending on the success of **waitForACKResponse()/waitForNoACKResponse()**.

If we are getting / polling data from the module, **sendCommand()** will return **SFE_UBLOX_STATUS_DATA_RECEIVED** if the get was successful.

If we are setting / writing data to the module, **sendCommand()** will return **SFE_UBLOX_STATUS_DATA_SENT** if the set was successful.

There are circumstances where the library can get the data it is expecting from the module, but it is overwritten (e.g. by an auto-PVT packet) before **sendCommand()** is able to return. In this case, **sendCommand()** will return the error **SFE_UBLOX_STATUS_DATA_OVERWRITTEN**. We should simply call the library function again, but we will need to reset the packet contents first as they will indeed have been overwritten as the error implies.
161 changes: 161 additions & 0 deletions examples/Example20_SendCustomCommand/Example20_SendCustomCommand.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
Send Custom Command
By: Paul Clark (PaulZC)
Date: April 20th, 2020

License: MIT. See license file for more information but you can
basically do whatever you want with this code.

This example shows how you can create and send a custom UBX packet
using the SparkFun u-blox library.

Previously it was possible to create and send a custom packet
through the library but it would always appear to timeout as
some of the internal functions referred to the internal private
struct packetCfg.
The most recent version of the library allows sendCommand to
use a custom packet as if it were packetCfg and so:
- sendCommand will return a sfe_ublox_status_e enum as if
it had been called from within the library
- the custom packet will be updated with data returned by the module
(previously this was not possible from outside the library)

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
SAM-M8Q: https://www.sparkfun.com/products/15106

Hardware Connections:
Plug a Qwiic cable into the GPS and a BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/

#define NAV_RATE 20 // The new navigation rate in Hz (measurements per second)

#include <Wire.h> //Needed for I2C to GPS

#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS
SFE_UBLOX_GPS myGPS;

void setup()
{
Serial.begin(115200); // You may need to increase this for high navigation rates!
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun Ublox Example");

Wire.begin();

//myGPS.enableDebugging(); // Uncomment this line to enable debug messages

if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}

myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)

// Let's configure the module's navigation rate as if we were using setNavigationFrequency

// Let's create our custom packet
uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes
// The next line creates and initialises the packet information which wraps around the payload
ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};

// The structure of ubxPacket is:
// uint8_t cls : The message Class
// uint8_t id : The message ID
// uint16_t len : Length of the payload. Does not include cls, id, or checksum bytes
// uint16_t counter : Keeps track of number of overall bytes received. Some responses are larger than 255 bytes.
// uint16_t startingSpot : The counter value needed to go past before we begin recording into payload array
// uint8_t *payload : The payload
// uint8_t checksumA : Given to us by the module. Checked against the rolling calculated A/B checksums.
// uint8_t checksumB
// sfe_ublox_packet_validity_e valid : Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
// sfe_ublox_packet_validity_e classAndIDmatch : Goes from NOT_DEFINED to VALID or NOT_VALID when the Class and ID match the requestedClass and requestedID

// sendCommand will return:
// SFE_UBLOX_STATUS_DATA_RECEIVED if the data we requested was read / polled successfully
// SFE_UBLOX_STATUS_DATA_SENT if the data we sent was writted successfully (ACK'd)
// Other values indicate errors. Please see the sfe_ublox_status_e enum for further details.

// Referring to the u-blox M8 Receiver Description and Protocol Specification we see that
// the navigation rate is configured using the UBX-CFG-RATE message. So let's load our
// custom packet with the correct information so we can read (poll / get) the current settings.

customCfg.cls = UBX_CLASS_CFG; // This is the message Class
customCfg.id = UBX_CFG_RATE; // This is the message ID
customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings
customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing)

// We also need to tell sendCommand how long it should wait for a reply
uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need a lot longer e.g. 1100)

// Now let's read the current navigation rate. The results will be loaded into customCfg.
if (myGPS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
{
Serial.println(F("sendCommand (poll / get) failed! Freezing..."));
while (1)
;
}

// Referring to the message definition for UBX-CFG-RATE we see that the measurement rate
// is stored in payload bytes 0 and 1 as a uint16_t in LSB-first (little endian) format

uint16_t rate = (customPayload[1] << 8) | customPayload[0]; // Extract the current rate (ms)
float f_rate = 1000.0 / ((float)rate); // Convert the navigation rate to Hz (measurements per second)

// Print the current measurement rate
Serial.print(F("The current measurement rate is: "));
Serial.println(f_rate, 1);

// Let's change it
rate = 1000 / NAV_RATE; // Load the new value into rate
customPayload[0] = rate & 0xFF; // Store it in the payload
customPayload[1] = rate >> 8;

// Print the new measurement rate
Serial.print(F("The new measurement rate will be: "));
Serial.println(NAV_RATE);

// We don't need to update customCfg.len as it will have been set to 6
// when sendCommand read the data

// Now we write the custom packet back again to change the setting
if (myGPS.sendCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // This time we are only expecting an ACK
{
Serial.println(F("sendCommand (set) failed! Freezing."));
while (1)
;
}
else
{
Serial.println(F("Navigation rate updated. Here we go..."));
}

myGPS.setAutoPVT(true); // Enable AutoPVT. The module will generate measurements automatically without being polled.

//myGPS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); //Uncomment this line to save only the NAV settings to flash and BBR
}

void loop()
{
//Query the module as fast as possible
int32_t latitude = myGPS.getLatitude();
Serial.print(F("Lat: "));
Serial.print(latitude);

int32_t longitude = myGPS.getLongitude();
Serial.print(F(" Lon: "));
Serial.print(longitude);
Serial.print(F(" (degrees * 10^-7)"));

int32_t altitude = myGPS.getAltitude();
Serial.print(F(" Alt: "));
Serial.print(altitude);
Serial.println(F(" (mm)"));
}
Loading