Merge pull request #93 from sparkfun/release_candidate

v2.1.4
This commit is contained in:
Paul
2021-12-14 11:06:43 +00:00
committed by GitHub
8 changed files with 609 additions and 90 deletions
@@ -1,7 +1,5 @@
/*
Note: compiles OK with v2.0 but is currently untested
Use ESP32 WiFi to push RTCM data to RTK2Go (caster) as a Server
Use ESP32 WiFi to push RTCM data to RTK2Go (Caster) as a Server
By: SparkFun Electronics / Nathan Seidle
Date: December 14th, 2020
License: MIT. See license file for more information but you can
@@ -33,25 +31,20 @@
#include <WiFi.h>
#include "secrets.h"
WiFiClient client;
WiFiClient ntripCaster;
#include <Wire.h> //Needed for I2C to GNSS
#include <Wire.h>
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//Basic Connection settings to RTK2Go NTRIP Caster - See secrets for mount specific credentials
//Global Variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const uint16_t casterPort = 2101;
const char * casterHost = "rtk2go.com";
const char * ntrip_server_name = "SparkFun_RTK_Surveyor";
long lastSentRTCM_ms = 0; //Time of last data pushed to socket
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
uint32_t serverBytesSent = 0; //Just a running total
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastReport_ms = 0; //Time of last report of bytes sent
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
@@ -73,7 +66,8 @@ void setup()
Serial.print("Connecting to local WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
@@ -98,7 +92,8 @@ void setup()
if (response == false)
{
Serial.println(F("Failed to disable NMEA. Freezing..."));
while (1);
while (1)
;
}
else
Serial.println(F("NMEA disabled"));
@@ -114,7 +109,8 @@ void setup()
if (response == false)
{
Serial.println(F("Failed to enable RTCM. Freezing..."));
while (1);
while (1)
;
}
else
Serial.println(F("RTCM sentences enabled"));
@@ -129,63 +125,72 @@ void setup()
if (response == false)
{
Serial.println(F("Failed to enter static position. Freezing..."));
while (1);
while (1)
;
}
else
Serial.println(F("Static position set"));
//You could instead do a survey-in but it takes much longer to start generating RTCM data. See Example4_BaseWithLCD
//Alternatively to setting a static position, you could do a survey-in
//but it takes much longer to start generating RTCM data. See Example4_BaseWithLCD
//myGNSS.enableSurveyMode(60, 5.000); //Enable Survey in, 60 seconds, 5.0m
if (myGNSS.saveConfiguration() == false) //Save the current settings to flash and BBR
Serial.println(F("Module failed to save."));
//If you were setting up a full GNSS station, you would want to save these settings.
//Because setting an incorrect static position will disable the ability to get a lock, we will skip saving during this example
//if (myGNSS.saveConfiguration() == false) //Save the current settings to flash and BBR
// Serial.println(F("Module failed to save"));
Serial.println(F("Module configuration complete"));
}
void loop()
{
if (Serial.available()) beginServing();
if (Serial.available())
beginServing();
Serial.println(F("Press any key to start serving."));
Serial.println(F("Press any key to start serving"));
delay(1000);
}
void beginServing()
{
Serial.println("Xmit to RTK2Go. Press any key to stop");
Serial.println("Begin transmitting to caster. Press any key to stop");
delay(10); //Wait for any serial to arrive
while (Serial.available()) Serial.read(); //Flush
while (Serial.available())
Serial.read(); //Flush
while (Serial.available() == 0)
{
//Connect if we are not already
if (client.connected() == false)
if (ntripCaster.connected() == false)
{
Serial.printf("Opening socket to %s\n", casterHost);
if (client.connect(casterHost, casterPort) == true) //Attempt connection
if (ntripCaster.connect(casterHost, casterPort) == true) //Attempt connection
{
Serial.printf("Connected to %s:%d\n", casterHost, casterPort);
const int SERVER_BUFFER_SIZE = 512;
char serverBuffer[SERVER_BUFFER_SIZE];
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverBuffer, SERVER_BUFFER_SIZE, "SOURCE %s /%s\r\nSource-Agent: NTRIP %s/%s\r\n\r\n",
mntpnt_pw, mntpnt, ntrip_server_name, "App Version 1.0");
snprintf(serverRequest,
SERVER_BUFFER_SIZE,
"SOURCE %s /%s\r\nSource-Agent: NTRIP SparkFun u-blox Server v1.0\r\n\r\n",
mountPointPW, mountPoint);
Serial.printf("Sending credentials:\n%s\n", serverBuffer);
client.write(serverBuffer, strlen(serverBuffer));
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripCaster.write(serverRequest, strlen(serverRequest));
//Wait for response
unsigned long timeout = millis();
while (client.available() == 0)
while (ntripCaster.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(">>> Client Timeout !");
client.stop();
Serial.println("Caster timed out!");
ntripCaster.stop();
return;
}
delay(10);
@@ -195,30 +200,34 @@ void beginServing()
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (client.available())
while (ntripCaster.available())
{
response[responseSpot++] = client.read();
response[responseSpot++] = ntripCaster.read();
if (strstr(response, "200") > 0) //Look for 'ICY 200 OK'
connectionSuccess = true;
if (responseSpot == 512 - 1) break;
if (responseSpot == 512 - 1)
break;
}
response[responseSpot] = '\0';
if (connectionSuccess == false)
{
Serial.printf("Failed to connect to RTK2Go: %s", response);
Serial.printf("Failed to connect to Caster: %s", response);
return;
}
} //End attempt to connect
else
{
Serial.println("Connection to host failed");
return;
}
} //End connected == false
if (client.connected() == true)
if (ntripCaster.connected() == true)
{
delay(10);
while (Serial.available()) Serial.read(); //Flush any endlines or carriage returns
while (Serial.available())
Serial.read(); //Flush any endlines or carriage returns
lastReport_ms = millis();
lastSentRTCM_ms = millis();
@@ -226,7 +235,8 @@ void beginServing()
//This is the main sending loop. We scan for new ublox data but processRTCM() is where the data actually gets sent out.
while (1)
{
if (Serial.available()) break;
if (Serial.available())
break;
myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.
@@ -236,7 +246,7 @@ void beginServing()
if (millis() - lastSentRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println("RTCM timeout. Disconnecting...");
client.stop();
ntripCaster.stop();
return;
}
@@ -256,10 +266,11 @@ void beginServing()
Serial.println("User pressed a key");
Serial.println("Disconnecting...");
client.stop();
ntripCaster.stop();
delay(10);
while (Serial.available()) Serial.read(); //Flush any endlines or carriage returns
while (Serial.available())
Serial.read(); //Flush any endlines or carriage returns
}
//This function gets called from the SparkFun u-blox Arduino Library.
@@ -267,9 +278,9 @@ void beginServing()
//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc.
void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming)
{
if (client.connected() == true)
if (ntripCaster.connected() == true)
{
client.write(incoming); //Send this byte to socket
ntripCaster.write(incoming); //Send this byte to socket
serverBytesSent++;
lastSentRTCM_ms = millis();
}
@@ -1,7 +1,15 @@
//Your WiFi credentials
const char* ssid = "TRex";
const char* password = "hasBigTeeth";
const char *ssid = "TRex";
const char *password = "hasBigTeeth";
//Your RTK2GO mount point credentials
const char* mntpnt_pw = "WR5wRo4H";
const char* mntpnt = "bldr_dwntwn2";
//RTK2Go works well and is free
const char casterHost[] = "rtk2go.com";
const uint16_t casterPort = 2101;
const char mountPoint[] = "bldr_dwntwn2"; //The mount point you want to push data to
const char mountPointPW[] = "WR5wRo4H";
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char mountPoint[] = "MP1979d"; //The mount point you want to push data to
//const char mountPointPW[] = "296ynq";
@@ -16,6 +16,9 @@
This is a proof of concept to show how to connect to a caster via HTTP. Using WiFi for a rover
is generally a bad idea because of limited WiFi range in the field.
For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
@@ -50,7 +53,7 @@ int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame af
void setup()
{
Serial.begin(115200);
Serial.println("NTRIP testing");
Serial.println(F("NTRIP testing"));
Wire.begin(); //Start I2C
@@ -66,15 +69,15 @@ void setup()
myGNSS.setNavigationFrequency(1); //Set output in Hz.
Serial.print("Connecting to local WiFi");
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
Serial.print(F("."));
}
Serial.println();
Serial.print("WiFi connected with IP: ");
Serial.print(F("WiFi connected with IP: "));
Serial.println(WiFi.localIP());
while (Serial.available()) Serial.read();
@@ -82,7 +85,11 @@ void setup()
void loop()
{
if (Serial.available()) beginClient();
if (Serial.available())
{
beginClient();
while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
}
Serial.println(F("Press any key to start NTRIP Client."));
@@ -95,36 +102,37 @@ void beginClient()
WiFiClient ntripClient;
long rtcmCount = 0;
Serial.println("Subscribing to Caster. Press key to stop");
Serial.println(F("Subscribing to Caster. Press key to stop"));
delay(10); //Wait for any serial to arrive
while (Serial.available()) Serial.read(); //Flush
while (Serial.available() == 0)
{
//Connect if we are not already
//Connect if we are not already. Limit to 5s between attempts.
if (ntripClient.connected() == false)
{
Serial.print("Opening socket to");
Serial.print(F("Opening socket to "));
Serial.println(casterHost);
if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
{
Serial.println("Connection to caster failed");
Serial.println(F("Connection to caster failed"));
return;
}
else
{
Serial.print("Connected to ");
Serial.print(F("Connected to "));
Serial.print(casterHost);
Serial.print(": ");
Serial.print(F(": "));
Serial.println(casterPort);
Serial.print("Requesting NTRIP Data from mount point ");
Serial.print(F("Requesting NTRIP Data from mount point "));
Serial.println(mountPoint);
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest, SERVER_BUFFER_SIZE, "GET /%s HTTP/1.0\r\nUser-Agent: SparkFun u-blox NTRIPClient v1.0\r\n",
snprintf(serverRequest, SERVER_BUFFER_SIZE, "GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
mountPoint);
char credentials[512];
@@ -138,7 +146,7 @@ void beginClient()
char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
Serial.print("Sending credentials: ");
Serial.print(F("Sending credentials: "));
Serial.println(userCredentials);
#if defined(ARDUINO_ARCH_ESP32)
@@ -158,13 +166,13 @@ void beginClient()
strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
Serial.print("serverRequest size: ");
Serial.print(F("serverRequest size: "));
Serial.print(strlen(serverRequest));
Serial.print(" of ");
Serial.print(F(" of "));
Serial.print(sizeof(serverRequest));
Serial.println(" bytes available");
Serial.println(F(" bytes available"));
Serial.println("Sending server request:");
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripClient.write(serverRequest, strlen(serverRequest));
@@ -174,7 +182,7 @@ void beginClient()
{
if (millis() - timeout > 5000)
{
Serial.println("Mountpoint timed out!");
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
@@ -194,23 +202,26 @@ void beginClient()
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println("Hey - your credentials look bad! Check you caster username and password.");
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
if (connectionSuccess == false)
{
Serial.print("Failed to connect to ");
Serial.print(F("Failed to connect to "));
Serial.print(casterHost);
Serial.print(": ");
Serial.print(F(": "));
Serial.println(response);
delay(5000); //Don't spam with lots of connection attempts
return;
}
else
{
Serial.print("Connected to ");
Serial.print(F("Connected to "));
Serial.println(casterHost);
lastReceivedRTCM_ms = millis(); //Reset timeout
}
@@ -236,7 +247,7 @@ void beginClient()
//Push RTCM to GNSS module over I2C
myGNSS.pushRawData(rtcmData, rtcmCount, false);
Serial.print("RTCM pushed to ZED: ");
Serial.print(F("RTCM pushed to ZED: "));
Serial.println(rtcmCount);
}
}
@@ -244,7 +255,7 @@ void beginClient()
//Close socket if we don't have new data for 10s
if (millis() - lastReceivedRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println("RTCM timeout. Disconnecting...");
Serial.println(F("RTCM timeout. Disconnecting..."));
if (ntripClient.connected() == true)
ntripClient.stop();
return;
@@ -253,9 +264,7 @@ void beginClient()
delay(10);
}
Serial.println("User pressed a key");
Serial.println("Disconnecting...");
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
ntripClient.stop();
while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
}
@@ -0,0 +1,403 @@
/*
Use ESP32 WiFi to get RTCM data from RTK2Go (caster) as a Client, and transmit GGA (needed for some Casters)
By: SparkFun Electronics / Nathan Seidle
Date: November 18th, 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 obtain RTCM data from a NTRIP Caster over WiFi
and push it over I2C to a ZED-F9x.
It's confusing, but the Arduino is acting as a 'client' to a 'caster'. In this case we will
use RTK2Go.com as our caster because it is free. See the NTRIPServer example to see how
to push RTCM data to the caster.
The rover's location will be broadcast to the Caster every 10s via GGA setence.
You will need to have a valid mountpoint available. To see available mountpoints go here: http://rtk2go.com:2101/
This is a proof of concept to show how to connect to a caster via HTTP.
For more information about NTRIP Clients and the differences between Rev1 and Rev2 of the protocol
please see: https://www.use-snip.com/kb/knowledge-base/ntrip-rev1-versus-rev2-formats/
"In broad protocol terms, the NTRIP client must first connect (get an HTTP “OK” reply) and only then
should it send the sentence. NTRIP protocol revision 2 (which does not have very broad industry
acceptance at this time) does allow sending the sentence in the original header."
https://www.use-snip.com/kb/knowledge-base/subtle-issues-with-using-ntrip-client-nmea-183-strings/
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
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
*/
#include <WiFi.h>
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//The ESP32 core has a built in base64 library but not every platform does
//We'll use an external lib if necessary.
#if defined(ARDUINO_ARCH_ESP32)
#include "base64.h" //Built-in ESP32 library
#else
#include <Base64.h> //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform
#endif
//Global variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastReceivedRTCM_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
bool transmitLocation = true; //By default we will transmit the units location via GGA sentence.
int timeBetweenGGAUpdate_ms = 10000; //GGA is required for Rev2 NTRIP casters. Don't transmit but once every 10 seconds
long lastTransmittedGGA_ms = 0;
//Used for GGA sentence parsing from incoming NMEA
bool ggaSentenceStarted = false;
bool ggaSentenceComplete = false;
bool ggaTransmitComplete = false; //Goes true once we transmit GGA to the caster
char ggaSentence[128] = {0};
byte ggaSentenceSpot = 0;
int ggaSentenceEndSpot = 0;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
Serial.println(F("NTRIP testing"));
Wire.begin(); //Start I2C
while (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
delay(2000);
//while (1);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both NMEA and UBX messages
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); //Be sure RTCM3 input is enabled. UBX + RTCM3 is not a valid state.
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); //Verify the GGA sentence is enabled
myGNSS.setNavigationFrequency(1); //Set output in Hz.
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.print(F("WiFi connected with IP: "));
Serial.println(WiFi.localIP());
while (Serial.available())
Serial.read();
}
void loop()
{
if (Serial.available())
{
beginClient();
while (Serial.available())
Serial.read(); //Empty buffer of any newline chars
}
Serial.println(F("Press any key to start NTRIP Client."));
delay(1000);
}
//Connect to NTRIP Caster, receive RTCM, and push to ZED module over I2C
void beginClient()
{
WiFiClient ntripClient;
long rtcmCount = 0;
Serial.println(F("Subscribing to Caster. Press key to stop"));
delay(10); //Wait for any serial to arrive
while (Serial.available())
Serial.read(); //Flush
while (Serial.available() == 0)
{
myGNSS.checkUblox();
//Connect if we are not already. Limit to 5s between attempts.
if (ntripClient.connected() == false)
{
Serial.print(F("Opening socket to "));
Serial.println(casterHost);
if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection
{
Serial.println(F("Connection to caster failed"));
return;
}
else
{
Serial.print(F("Connected to "));
Serial.print(casterHost);
Serial.print(F(": "));
Serial.println(casterPort);
Serial.print(F("Requesting NTRIP Data from mount point "));
Serial.println(mountPoint);
const int SERVER_BUFFER_SIZE = 512;
char serverRequest[SERVER_BUFFER_SIZE];
snprintf(serverRequest,
SERVER_BUFFER_SIZE,
"GET /%s HTTP/1.0\r\nUser-Agent: NTRIP SparkFun u-blox Client v1.0\r\n",
mountPoint);
char credentials[512];
if (strlen(casterUser) == 0)
{
strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials));
}
else
{
//Pass base64 encoded user:pw
char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot
snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW);
Serial.print(F("Sending credentials: "));
Serial.println(userCredentials);
#if defined(ARDUINO_ARCH_ESP32)
//Encode with ESP32 built-in library
base64 b;
String strEncodedCredentials = b.encode(userCredentials);
char encodedCredentials[strEncodedCredentials.length() + 1];
strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array
#else
//Encode with nfriendly library
int encodedLen = base64_enc_len(strlen(userCredentials));
char encodedCredentials[encodedLen]; //Create array large enough to house encoded data
base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed
#endif
snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials);
}
strncat(serverRequest, credentials, SERVER_BUFFER_SIZE);
strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE);
Serial.print(F("serverRequest size: "));
Serial.print(strlen(serverRequest));
Serial.print(F(" of "));
Serial.print(sizeof(serverRequest));
Serial.println(F(" bytes available"));
Serial.println(F("Sending server request:"));
Serial.println(serverRequest);
ntripClient.write(serverRequest, strlen(serverRequest));
//Wait for response
unsigned long timeout = millis();
while (ntripClient.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripClient.available())
{
if (responseSpot == sizeof(response) - 1)
break;
response[responseSpot++] = ntripClient.read();
if (strstr(response, "200") > 0) //Look for '200 OK'
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
if (connectionSuccess == false)
{
Serial.print(F("Failed to connect to "));
Serial.println(casterHost);
return;
}
else
{
Serial.print(F("Connected to "));
Serial.println(casterHost);
lastReceivedRTCM_ms = millis(); //Reset timeout
ggaTransmitComplete = true; //Reset to start polling for new GGA data
}
} //End attempt to connect
} //End connected == false
if (ntripClient.connected() == true)
{
uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
rtcmCount = 0;
//Print any available RTCM data
while (ntripClient.available())
{
//Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data
rtcmData[rtcmCount++] = ntripClient.read();
if (rtcmCount == sizeof(rtcmData))
break;
}
if (rtcmCount > 0)
{
lastReceivedRTCM_ms = millis();
//Push RTCM to GNSS module over I2C
myGNSS.pushRawData(rtcmData, rtcmCount, false);
Serial.print(F("RTCM pushed to ZED: "));
Serial.println(rtcmCount);
}
}
//Provide the caster with our current position as needed
if (ntripClient.connected() == true && transmitLocation == true && (millis() - lastTransmittedGGA_ms) > timeBetweenGGAUpdate_ms && ggaSentenceComplete == true && ggaTransmitComplete == false)
{
Serial.print(F("Pushing GGA to server: "));
Serial.println(ggaSentence);
lastTransmittedGGA_ms = millis();
//Push our current GGA sentence to caster
ntripClient.print(ggaSentence);
ntripClient.print("\r\n");
ggaTransmitComplete = true;
//Wait for response
unsigned long timeout = millis();
while (ntripClient.available() == 0)
{
if (millis() - timeout > 5000)
{
Serial.println(F("Caster timed out!"));
ntripClient.stop();
return;
}
delay(10);
}
//Check reply
bool connectionSuccess = false;
char response[512];
int responseSpot = 0;
while (ntripClient.available())
{
if (responseSpot == sizeof(response) - 1)
break;
response[responseSpot++] = ntripClient.read();
if (strstr(response, "200") > 0) //Look for '200 OK'
connectionSuccess = true;
if (strstr(response, "401") > 0) //Look for '401 Unauthorized'
{
Serial.println(F("Hey - your credentials look bad! Check you caster username and password."));
connectionSuccess = false;
}
}
response[responseSpot] = '\0';
Serial.print(F("Caster responded with: "));
Serial.println(response);
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceivedRTCM_ms > maxTimeBeforeHangup_ms)
{
Serial.println(F("RTCM timeout. Disconnecting..."));
if (ntripClient.connected() == true)
ntripClient.stop();
return;
}
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
ntripClient.stop();
}
//This function gets called from the SparkFun u-blox Arduino Library
//As each NMEA character comes in you can specify what to do with it
//We will look for and copy the GGA sentence
void SFE_UBLOX_GNSS::processNMEA(char incoming)
{
//Take the incoming char from the u-blox I2C port and check to see if we should record it or not
if (incoming == '$' && ggaTransmitComplete == true)
{
ggaSentenceStarted = true;
ggaSentenceSpot = 0;
ggaSentenceEndSpot = sizeof(ggaSentence);
ggaSentenceComplete = false;
}
if (ggaSentenceStarted == true)
{
ggaSentence[ggaSentenceSpot++] = incoming;
//Make sure we don't go out of bounds
if (ggaSentenceSpot == sizeof(ggaSentence))
{
//Start over
ggaSentenceStarted = false;
}
//Verify this is the GGA setence
else if (ggaSentenceSpot == 5 && incoming != 'G')
{
//Ignore this sentence, start over
ggaSentenceStarted = false;
}
else if (incoming == '*')
{
//We're near the end. Keep listening for two more bytes to complete the CRC
ggaSentenceEndSpot = ggaSentenceSpot + 2;
}
else if (ggaSentenceSpot == ggaSentenceEndSpot)
{
ggaSentence[ggaSentenceSpot] = '\0'; //Terminate this string
ggaSentenceComplete = true;
ggaTransmitComplete = false; //We are ready for transmission
//Serial.print("GGA Parsed - ");
//Serial.println(ggaSentence);
//Start over
ggaSentenceStarted = false;
}
}
}
@@ -0,0 +1,17 @@
//Your WiFi credentials
const char ssid[] = "TRex";
const char password[] = "parachutes";
//RTK2Go works well and is free
const char casterHost[] = "rtk2go.com";
const uint16_t casterPort = 2101;
const char casterUser[] = "myEmail@test.com"; //User must provide their own email address to use RTK2Go
const char casterUserPW[] = "";
const char mountPoint[] = "bldr_SparkFun1"; //The mount point you want to get data from
//Emlid Caster also works well and is free
//const char casterHost[] = "caster.emlid.com";
//const uint16_t casterPort = 2101;
//const char casterUser[] = "u99696"; //User name and pw must be obtained through their web portal
//const char casterUserPW[] = "466zez";
//const char mountPoint[] = "MP1979"; //The mount point you want to get data from
+1 -1
View File
@@ -1,5 +1,5 @@
name=SparkFun u-blox GNSS Arduino Library
version=2.1.3
version=2.1.4
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/>
+70 -5
View File
@@ -442,6 +442,7 @@ bool SFE_UBLOX_GNSS::begin(TwoWire &wirePort, uint8_t deviceAddress, uint16_t ma
{
commType = COMM_TYPE_I2C;
_i2cPort = &wirePort; //Grab which port the user wants us to use
_signsOfLife = false; //Clear the _signsOfLife flag. It will be set true if valid traffic is seen.
//We expect caller to begin their I2C port, with the speed of their choice external to the library
//But if they forget, we start the hardware here.
@@ -485,7 +486,7 @@ bool SFE_UBLOX_GNSS::begin(TwoWire &wirePort, uint8_t deviceAddress, uint16_t ma
connected = isConnected(maxWait);
}
if ((!connected ) && assumeSuccess) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
if ((!connected ) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
@@ -504,6 +505,7 @@ bool SFE_UBLOX_GNSS::begin(Stream &serialPort, uint16_t maxWait, bool assumeSucc
{
commType = COMM_TYPE_SERIAL;
_serialPort = &serialPort; //Grab which port the user wants us to use
_signsOfLife = false; //Clear the _signsOfLife flag. It will be set true if valid traffic is seen.
//New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already)
if (packetCfgPayloadSize == 0)
@@ -512,6 +514,30 @@ bool SFE_UBLOX_GNSS::begin(Stream &serialPort, uint16_t maxWait, bool assumeSucc
//New in v2.0: allocate memory for the file buffer - if required. (The user should have called setFileBufferSize already)
createFileBuffer();
//Get rid of any stale serial data already in the processor's RX buffer
while (_serialPort->available())
_serialPort->read();
//If assumeSuccess is true, the user must really want begin to succeed. So, let's empty the module's serial transmit buffer too!
//Keep discarding new serial data until we see a gap of 2ms - hopefully indicating that the module's TX buffer is empty.
if (assumeSuccess)
{
unsigned long startTime = millis();
unsigned long lastActivity = startTime;
bool keepGoing = true;
while (keepGoing && (millis() < (startTime + (unsigned long)maxWait)))
{
while (_serialPort->available()) // Discard any new data
{
_serialPort->read();
lastActivity = millis();
}
if (millis() > (lastActivity + (unsigned long)2)) // Check if we have seen no new data for at least 2ms
keepGoing = false;
}
}
// Call isConnected up to three times - tests on the NEO-M8U show the CFG RATE poll occasionally being ignored
bool connected = isConnected(maxWait);
@@ -537,7 +563,7 @@ bool SFE_UBLOX_GNSS::begin(Stream &serialPort, uint16_t maxWait, bool assumeSucc
connected = isConnected(maxWait);
}
if ((!connected ) && assumeSuccess) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
if ((!connected ) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
@@ -558,6 +584,7 @@ bool SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed,
_spiPort = &spiPort;
_csPin = csPin;
_spiSpeed = spiSpeed;
_signsOfLife = false; //Clear the _signsOfLife flag. It will be set true if valid traffic is seen.
// Initialize the chip select pin
pinMode(_csPin, OUTPUT);
@@ -617,7 +644,7 @@ bool SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed,
connected = isConnected(maxWait);
}
if ((!connected ) && assumeSuccess) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
if ((!connected ) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate.
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
@@ -1637,6 +1664,11 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
if (nmeaByteCounter == 5)
{
if (!_signsOfLife) // If _signsOfLife is not already true, set _signsOfLife to true if the NMEA header is valid
{
_signsOfLife = isNMEAHeaderValid();
}
// We've just received the end of the address field. Check if it is selected for logging
if (logThisNMEA())
{
@@ -1710,6 +1742,38 @@ bool SFE_UBLOX_GNSS::logThisNMEA()
return (false);
}
// PRIVATE: Return true if the NMEA header is valid
bool SFE_UBLOX_GNSS::isNMEAHeaderValid()
{
if (nmeaAddressField[0] != '*') return (false);
if (nmeaAddressField[1] != 'G') return (false);
if ((nmeaAddressField[3] == 'D') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'M')) return (true);
if (nmeaAddressField[3] == 'G')
{
if ((nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S')) return (true);
if ((nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A')) return (true);
if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L')) return (true);
if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S')) return (true);
if ((nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q')) return (true);
if ((nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S')) return (true);
if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A')) return (true);
if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T')) return (true);
if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V')) return (true);
}
if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'M')) return (true);
if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'M') && (nmeaAddressField[5] == 'C')) return (true);
if ((nmeaAddressField[3] == 'T') && (nmeaAddressField[4] == 'X') && (nmeaAddressField[5] == 'T')) return (true);
if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'W')) return (true);
if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'G')) return (true);
if ((nmeaAddressField[3] == 'Z') && (nmeaAddressField[4] == 'D') && (nmeaAddressField[5] == 'A')) return (true);
return (false);
}
// PRIVATE: Return true if we should pass this NMEA message to processNMEA
bool SFE_UBLOX_GNSS::processThisNMEA()
{
@@ -1887,6 +1951,7 @@ void SFE_UBLOX_GNSS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_
if ((incomingUBX->checksumA == rollingChecksumA) && (incomingUBX->checksumB == rollingChecksumB))
{
incomingUBX->valid = SFE_UBLOX_PACKET_VALIDITY_VALID; // Flag the packet as valid
_signsOfLife = true; //The checksum is valid, so set the _signsOfLife flag
// Let's check if the class and ID match the requestedClass and requestedID
// Remember - this could be a data packet or an ACK packet
@@ -3601,7 +3666,7 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::waitForACKResponse(ubxPacket *outgoingUBX, ui
packetAuto.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED;
unsigned long startTime = millis();
while (millis() - startTime < maxTime)
while (millis() < (startTime + (unsigned long)maxTime))
{
if (checkUbloxInternal(outgoingUBX, requestedClass, requestedID) == true) //See if new data is available. Process bytes as they come in.
{
@@ -3740,7 +3805,7 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::waitForACKResponse(ubxPacket *outgoingUBX, ui
} //checkUbloxInternal == true
delay(1); // Allow an RTOS to get an elbow in (#11)
} //while (millis() - startTime < maxTime)
} //while (millis() < (startTime + (unsigned long)maxTime))
// We have timed out...
// If the outgoingUBX->classAndIDmatch is VALID then we can take a gamble and return DATA_RECEIVED
@@ -599,6 +599,7 @@ public:
void setPacketCfgPayloadSize(size_t payloadSize); // Set packetCfgPayloadSize
//Begin communication with the GNSS. Advanced users can assume success if required. Useful if the port is already outputting messages at high navigation rate.
//Begin will then return true if "signs of life" have been seen: reception of _any_ valid UBX packet or _any_ valid NMEA header.
//By default use the default I2C address, and use Wire port
bool begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42, uint16_t maxWait = defaultMaxWait, bool assumeSuccess = false); //Returns true if module is detected
//serialPort needs to be perviously initialized to correct baud rate
@@ -1498,6 +1499,7 @@ private:
uint8_t nmeaAddressField[6]; // NMEA Address Field - includes the start character (*)
bool logThisNMEA(); // Return true if we should log this NMEA message
bool processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA
bool isNMEAHeaderValid(); // Return true if the six byte NMEA header appears valid. Used to set _signsOfLife
uint16_t rtcmLen = 0;
@@ -1527,6 +1529,10 @@ private:
bool _pushSingleByte = false;
uint8_t _pushThisSingleByte;
// .begin will return true if the assumeSuccess parameter is true and if _signsOfLife is true
// _signsOfLife is set to true when: a valid UBX message is seen; a valig NMEA header is seen.
bool _signsOfLife;
};
#endif