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

Commit 12b48f9

Browse files
committed
Added Example20_SendCustomCommand
1 parent c738f7a commit 12b48f9

File tree

4 files changed

+226
-2
lines changed

4 files changed

+226
-2
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
sendCustomCommand
3+
By: Paul Clark (PaulZC)
4+
Date: April 18th, 2020
5+
6+
License: MIT. See license file for more information but you can
7+
basically do whatever you want with this code.
8+
9+
This example shows how you can create and send a custom UBX packet
10+
using the SparkFun u-blox library.
11+
12+
Previously it was possible to create and send a custom packet
13+
through the library but it would always appear to timeout as
14+
some of the internal functions referred to the internal private
15+
struct packetCfg.
16+
The most recent version of the library allows sendCustomCommand to
17+
use a custom packet as if it were packetCfg and so:
18+
- sendCustomCommand will return a sfe_ublox_status_e enum as if
19+
sendCommand had been called from within the library
20+
- the custom packet will be updated with data returned by the module
21+
(previously this was not possible)
22+
23+
Feel like supporting open source hardware?
24+
Buy a board from SparkFun!
25+
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
26+
NEO-M8P RTK: https://www.sparkfun.com/products/15005
27+
SAM-M8Q: https://www.sparkfun.com/products/15106
28+
29+
Hardware Connections:
30+
Plug a Qwiic cable into the GPS and a BlackBoard
31+
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
32+
Open the serial monitor at 115200 baud to see the output
33+
*/
34+
35+
#include <Wire.h> //Needed for I2C to GPS
36+
37+
#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS
38+
SFE_UBLOX_GPS myGPS;
39+
40+
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module.
41+
42+
void setup()
43+
{
44+
Serial.begin(115200);
45+
while (!Serial)
46+
; //Wait for user to open terminal
47+
Serial.println("SparkFun Ublox Example");
48+
49+
Wire.begin();
50+
51+
//myGPS.enableDebugging(); // Uncomment this line to enable debug messages
52+
53+
if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
54+
{
55+
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
56+
while (1)
57+
;
58+
}
59+
60+
myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
61+
62+
// Let's configure the module's dynamic platform model as if we were using setDynamicModel
63+
// Possible values are:
64+
// 0 (PORTABLE), 2 (STATIONARY), 3 (PEDESTRIAN), 4 (AUTOMOTIVE), 5 (SEA),
65+
// 6 (AIRBORNE1g), 7 (AIRBORNE2g), 8 (AIRBORNE4g), 9 (WRIST), 10 (BIKE)
66+
67+
// Let's create our custom packet
68+
uint8_t customPayload[MAX_PAYLOAD_SIZE]; // This array holds the payload data bytes
69+
// The next line creates and initialises the packet information which wraps around the payload
70+
ubxPacket customCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
71+
72+
// The structure of ubxPacket is:
73+
// uint8_t cls : The message Class
74+
// uint8_t id : The message ID
75+
// uint16_t len : Length of the payload. Does not include cls, id, or checksum bytes
76+
// uint16_t counter : Keeps track of number of overall bytes received. Some responses are larger than 255 bytes.
77+
// uint16_t startingSpot : The counter value needed to go past before we begin recording into payload array
78+
// uint8_t *payload : The payload
79+
// uint8_t checksumA : Given to us by the module. Checked against the rolling calculated A/B checksums.
80+
// uint8_t checksumB
81+
// sfe_ublox_packet_validity_e valid : Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
82+
// 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
83+
84+
// sendCustomCommand will return:
85+
// SFE_UBLOX_STATUS_DATA_RECEIVED if the data we requested was read / polled successfully
86+
// SFE_UBLOX_STATUS_DATA_SENT if the data we sent was writted successfully (ACK'd)
87+
// Other values indicate errors. Please see the sfe_ublox_status_e enum for further details.
88+
89+
// Referring to the u-blox M8 Receiver Description and Protocol Specification we see that
90+
// the dynamic model is configured using the UBX-CFG-NAV5 message. So let's load our
91+
// custom packet with the correct information so we can read (poll) the current settings.
92+
93+
customCfg.cls = UBX_CLASS_CFG; // This is the message Class
94+
customCfg.id = UBX_CFG_NAV5; // This is the message ID
95+
customCfg.len = 0; // Setting the len (length) to zero let's us poll the current settings
96+
customCfg.startingSpot = 0; // Always set the startingSpot to zero (unless you really know what you are doing)
97+
98+
// We also need to tell sendCustomCommand how long it should wait for a reply
99+
uint16_t maxWait = 250; // Wait for up to 250ms (Serial may need longer)
100+
101+
// Now let's read the current navigation model settings. The results will be loaded into customCfg.
102+
if (myGPS.sendCustomCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK
103+
{
104+
Serial.println(F("sendCustomCommand (poll) failed! Freezing."));
105+
while (1)
106+
;
107+
}
108+
109+
// Referring to the message definition for UBX-CFG-NAV5 we see that we need to change
110+
// byte 2 to update the dynamic platform model.
111+
112+
// Print the current dynamic model
113+
Serial.print(F("The current dynamic model is: "));
114+
Serial.print(customPayload[2]);
115+
116+
// Let's change it
117+
if (customPayload[2] != 0x04) // If it is current not 4, change it to 4
118+
{
119+
Serial.println(F(". Changing it to 4."));
120+
customPayload[2] = 0x04;
121+
}
122+
else // If it is already 4, change it to 2
123+
{
124+
Serial.println(F(". Changing it to 2."));
125+
customPayload[2] = 0x02;
126+
}
127+
128+
// We don't need to update customCfg.len as it will have been set to 36 (0x24)
129+
// when sendCustomCommand read the data
130+
131+
// Now we write the custom packet back again to change the setting
132+
if (myGPS.sendCustomCommand(&customCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // This time we are only expecting an ACK
133+
{
134+
Serial.println(F("sendCustomCommand (set) failed! Freezing."));
135+
while (1)
136+
;
137+
}
138+
else
139+
{
140+
Serial.println(F("Dynamic platform model updated."));
141+
}
142+
143+
//myGPS.saveConfigSelective(VAL_CFG_SUBSEC_NAVCONF); //Uncomment this line to save only the NAV settings to flash and BBR
144+
}
145+
146+
void loop()
147+
{
148+
//Query module only every second. Doing it more often will just cause I2C traffic.
149+
//The module only responds when a new position is available
150+
if (millis() - lastTime > 1000)
151+
{
152+
lastTime = millis(); //Update the timer
153+
154+
long latitude = myGPS.getLatitude();
155+
Serial.print(F("Lat: "));
156+
Serial.print(latitude);
157+
158+
long longitude = myGPS.getLongitude();
159+
Serial.print(F(" Long: "));
160+
Serial.print(longitude);
161+
Serial.print(F(" (degrees * 10^-7)"));
162+
163+
long altitude = myGPS.getAltitude();
164+
Serial.print(F(" Alt: "));
165+
Serial.print(altitude);
166+
Serial.print(F(" (mm)"));
167+
168+
Serial.println();
169+
}
170+
}

keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ processNMEA KEYWORD2
2727

2828
calcChecksum KEYWORD2
2929
sendCommand KEYWORD2
30+
sendCustomCommand KEYWORD2
3031
printPacket KEYWORD2
3132
setI2CAddress KEYWORD2
3233
setSerialRate KEYWORD2

src/SparkFun_Ublox_Arduino_Library.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,6 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t
712712
// This is potentially risky as we are saying that we saw the requested Class and ID
713713
// but that the packet checksum failed. Potentially it could be the class or ID bytes
714714
// that caused the checksum error!
715-
716715
if ((incomingUBX->cls == requestedClass) && (incomingUBX->id == requestedID))
717716
{
718717
incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_VALID; // If we have a match, set the classAndIDmatch flag to not valid
@@ -950,6 +949,59 @@ sfe_ublox_status_e SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t ma
950949
return retVal;
951950
}
952951

952+
//Given a custom packet and payload, send everything including CRC bytes via I2C port
953+
sfe_ublox_status_e SFE_UBLOX_GPS::sendCustomCommand(ubxPacket *outgoingUBX, uint16_t maxWait)
954+
{
955+
sfe_ublox_status_e retVal = SFE_UBLOX_STATUS_SUCCESS;
956+
957+
calcChecksum(outgoingUBX); //Sets checksum A and B bytes of the packet
958+
959+
if (_printDebug == true)
960+
{
961+
_debugSerial->print(F("\nSending: "));
962+
printPacket(outgoingUBX);
963+
}
964+
965+
if (commType == COMM_TYPE_I2C)
966+
{
967+
retVal = sendI2cCommand(*outgoingUBX, maxWait);
968+
if (retVal != SFE_UBLOX_STATUS_SUCCESS)
969+
{
970+
if (_printDebug == true)
971+
{
972+
_debugSerial->println(F("Send I2C Command failed"));
973+
}
974+
return retVal;
975+
}
976+
}
977+
else if (commType == COMM_TYPE_SERIAL)
978+
{
979+
sendSerialCommand(*outgoingUBX);
980+
}
981+
982+
if (maxWait > 0)
983+
{
984+
//Depending on what we just sent, either we need to look for an ACK or not
985+
if (outgoingUBX->cls == UBX_CLASS_CFG)
986+
{
987+
if (_printDebug == true)
988+
{
989+
_debugSerial->println(F("sendCommand: Waiting for ACK response"));
990+
}
991+
retVal = waitForACKResponse(outgoingUBX, outgoingUBX->cls, outgoingUBX->id, maxWait); //Wait for Ack response
992+
}
993+
else
994+
{
995+
if (_printDebug == true)
996+
{
997+
_debugSerial->println(F("sendCommand: Waiting for No ACK response"));
998+
}
999+
retVal = waitForNoACKResponse(outgoingUBX, outgoingUBX->cls, outgoingUBX->id, maxWait); //Wait for Ack response
1000+
}
1001+
}
1002+
return retVal;
1003+
}
1004+
9531005
//Returns false if sensor fails to respond to I2C traffic
9541006
sfe_ublox_status_e SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait)
9551007
{

src/SparkFun_Ublox_Arduino_Library.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ typedef struct
409409
uint8_t checksumA; //Given to us from module. Checked against the rolling calculated A/B checksums.
410410
uint8_t checksumB;
411411
sfe_ublox_packet_validity_e valid; //Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
412-
sfe_ublox_packet_validity_e classAndIDmatch; // Goes from flase to true when the Class and ID match the requestedClass and requestedID
412+
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
413413
} ubxPacket;
414414

415415
// Struct to hold the results returned by getGeofenceState (returned by UBX-NAV-GEOFENCE)
@@ -463,6 +463,7 @@ class SFE_UBLOX_GPS
463463

464464
void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages
465465
sfe_ublox_status_e sendCommand(ubxPacket outgoingUBX, uint16_t maxWait = defaultMaxWait); //Given a packet and payload, send everything including CRC bytes, return true if we got a response
466+
sfe_ublox_status_e sendCustomCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait); //This allows custom packets to be used
466467
sfe_ublox_status_e sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250);
467468
void sendSerialCommand(ubxPacket outgoingUBX);
468469

0 commit comments

Comments
 (0)