From 6357b31d28d18c137321db1d6e7084ac1660e1c7 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Wed, 8 Dec 2021 13:56:42 -0700 Subject: [PATCH 1/8] Moving prints to flash. --- .../Example15_NTRIPClient.ino | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/examples/ZED-F9P/Example15_NTRIPClient/Example15_NTRIPClient.ino b/examples/ZED-F9P/Example15_NTRIPClient/Example15_NTRIPClient.ino index 7e9fb72..f739b9e 100644 --- a/examples/ZED-F9P/Example15_NTRIPClient/Example15_NTRIPClient.ino +++ b/examples/ZED-F9P/Example15_NTRIPClient/Example15_NTRIPClient.ino @@ -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 -} +} \ No newline at end of file From 4b582c886014370a45d4f5fb526ede11ebb5c279 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Thu, 9 Dec 2021 17:50:14 +0000 Subject: [PATCH 2/8] Add _signsOfLife and isNMEAHeaderValid --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 47 ++++++++++++++++++-- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 5 +++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 34419a0..3170beb 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -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) @@ -537,7 +539,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 +560,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 +620,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 +1640,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 +1718,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 +1927,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 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 6f5bc92..2f23313 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1498,6 +1498,7 @@ class SFE_UBLOX_GNSS 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 +1528,10 @@ class SFE_UBLOX_GNSS 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 From af363909243e4f782513156abec85048d7f30e5f Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 10 Dec 2021 10:36:05 +0000 Subject: [PATCH 3/8] For Serial, if assumeSuccess is true, also try to clear the buffers For Serial, if the Serial port is congested, .begin can fail when the NAV RATE messages are delayed. This is usually a sign that the baud rate is too low. Clearing the serial buffer first seems to help. --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 28 ++++++++++++++++++-- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 3170beb..0cba6fb 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -514,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); @@ -3642,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. { @@ -3781,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 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 2f23313..4b3194b 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -599,6 +599,7 @@ class SFE_UBLOX_GNSS 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 From 2c47c098db1f6cc28f91fd8a7cf18c63718b87cc Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Sun, 12 Dec 2021 13:58:32 -0700 Subject: [PATCH 4/8] Add example 16: NTRIP Client with GGA support --- .../Example16_NTRIPClient_WithGGA.ino | 400 ++++++++++++++++++ .../Example16_NTRIPClient_WithGGA/secrets.h | 17 + 2 files changed, 417 insertions(+) create mode 100644 examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino create mode 100644 examples/ZED-F9P/Example16_NTRIPClient_WithGGA/secrets.h diff --git a/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino new file mode 100644 index 0000000..689527f --- /dev/null +++ b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino @@ -0,0 +1,400 @@ +/* + 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 +#include "secrets.h" + +#include //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 //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 + snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials); +#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 + } + 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; + } + } +} diff --git a/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/secrets.h b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/secrets.h new file mode 100644 index 0000000..3a6becf --- /dev/null +++ b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/secrets.h @@ -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 From 460836c5c1f9a70479a127d4516c8bda12d60f54 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Sun, 12 Dec 2021 21:36:25 -0700 Subject: [PATCH 5/8] Refactor example 14. Add Emlid as target. --- .../Example14_NTRIPServer.ino | 103 ++++++++++-------- .../ZED-F9P/Example14_NTRIPServer/secrets.h | 18 ++- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino b/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino index ca01530..86a93a5 100644 --- a/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino +++ b/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino @@ -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,26 +31,21 @@ #include #include "secrets.h" -WiFiClient client; +WiFiClient ntripCaster; -#include //Needed for I2C to GNSS +#include #include //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 +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 //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -long lastReport_ms = 0; //Time of last report of bytes sent - void setup() { Serial.begin(115200); // You may need to increase this for high navigation rates! @@ -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,70 @@ 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.")); + 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]; + const int SERVER_BUFFER_SIZE = 512; + 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 +198,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 +233,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 +244,7 @@ void beginServing() if (millis() - lastSentRTCM_ms > maxTimeBeforeHangup_ms) { Serial.println("RTCM timeout. Disconnecting..."); - client.stop(); + ntripCaster.stop(); return; } @@ -256,10 +264,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,10 +276,10 @@ 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(); } -} +} \ No newline at end of file diff --git a/examples/ZED-F9P/Example14_NTRIPServer/secrets.h b/examples/ZED-F9P/Example14_NTRIPServer/secrets.h index 5dcd50e..8cd0920 100644 --- a/examples/ZED-F9P/Example14_NTRIPServer/secrets.h +++ b/examples/ZED-F9P/Example14_NTRIPServer/secrets.h @@ -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"; From 1b3f033792e5061d70004db8c1fc6665e5c9dffa Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Sun, 12 Dec 2021 21:44:26 -0700 Subject: [PATCH 6/8] Disable save in ex 14 so other examples work. --- .../ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino b/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino index 86a93a5..aebdd31 100644 --- a/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino +++ b/examples/ZED-F9P/Example14_NTRIPServer/Example14_NTRIPServer.ino @@ -135,8 +135,10 @@ void setup() //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")); } From 356a43ded63b9bd2b0edefca4673ad7517b847a4 Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Sun, 12 Dec 2021 21:46:36 -0700 Subject: [PATCH 7/8] Move snprintf to work with any architecture. --- .../Example16_NTRIPClient_WithGGA.ino | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino index 689527f..b2506eb 100644 --- a/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino +++ b/examples/ZED-F9P/Example16_NTRIPClient_WithGGA/Example16_NTRIPClient_WithGGA.ino @@ -15,16 +15,16 @@ 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. + 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 + "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 @@ -52,10 +52,10 @@ SFE_UBLOX_GNSS myGNSS; //Global variables //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -long lastReceivedRTCM_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps +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. +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; @@ -76,7 +76,7 @@ void setup() Wire.begin(); //Start I2C - while(myGNSS.begin() == false) //Connect to the Ublox module using Wire port + 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); @@ -84,7 +84,7 @@ void setup() } 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.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 @@ -93,7 +93,8 @@ void setup() Serial.print(F("Connecting to local WiFi")); WiFi.begin(ssid, password); - while (WiFi.status() != WL_CONNECTED) { + while (WiFi.status() != WL_CONNECTED) + { delay(500); Serial.print(F(".")); } @@ -102,7 +103,8 @@ void setup() Serial.print(F("WiFi connected with IP: ")); Serial.println(WiFi.localIP()); - while (Serial.available()) Serial.read(); + while (Serial.available()) + Serial.read(); } void loop() @@ -110,7 +112,8 @@ void loop() if (Serial.available()) { beginClient(); - while (Serial.available()) Serial.read(); //Empty buffer of any newline chars + while (Serial.available()) + Serial.read(); //Empty buffer of any newline chars } Serial.println(F("Press any key to start NTRIP Client.")); @@ -126,12 +129,13 @@ void beginClient() 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()) + Serial.read(); //Flush while (Serial.available() == 0) { myGNSS.checkUblox(); - + //Connect if we are not already. Limit to 5s between attempts. if (ntripClient.connected() == false) { @@ -181,13 +185,14 @@ void beginClient() String strEncodedCredentials = b.encode(userCredentials); char encodedCredentials[strEncodedCredentials.length() + 1]; strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array - snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials); #else //Encode with nfriendly library int encodedLen = base64_enc_len(strlen(userCredentials)); - char encodedCredentials[encodedLen]; //Create array large enough to house encoded data + 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); @@ -221,7 +226,8 @@ void beginClient() int responseSpot = 0; while (ntripClient.available()) { - if (responseSpot == sizeof(response) - 1) break; + if (responseSpot == sizeof(response) - 1) + break; response[responseSpot++] = ntripClient.read(); if (strstr(response, "200") > 0) //Look for '200 OK' @@ -248,10 +254,10 @@ void beginClient() Serial.print(F("Connected to ")); Serial.println(casterHost); lastReceivedRTCM_ms = millis(); //Reset timeout - ggaTransmitComplete = true; //Reset to start polling for new GGA data + ggaTransmitComplete = true; //Reset to start polling for new GGA data } } //End attempt to connect - } //End connected == false + } //End connected == false if (ntripClient.connected() == true) { @@ -263,7 +269,8 @@ void beginClient() { //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 == sizeof(rtcmData)) + break; } if (rtcmCount > 0) @@ -278,12 +285,7 @@ void beginClient() } //Provide the caster with our current position as needed - if (ntripClient.connected() == true - && transmitLocation == true - && (millis() - lastTransmittedGGA_ms) > timeBetweenGGAUpdate_ms - && ggaSentenceComplete == true - && ggaTransmitComplete == false - ) + 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); @@ -315,7 +317,8 @@ void beginClient() int responseSpot = 0; while (ntripClient.available()) { - if (responseSpot == sizeof(response) - 1) break; + if (responseSpot == sizeof(response) - 1) + break; response[responseSpot++] = ntripClient.read(); if (strstr(response, "200") > 0) //Look for '200 OK' @@ -397,4 +400,4 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming) ggaSentenceStarted = false; } } -} +} \ No newline at end of file From 61b49ce53bb05aefe89cec439861b242108bd499 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 14 Dec 2021 10:45:51 +0000 Subject: [PATCH 8/8] v2.1.4 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index b06dd6b..ce120ad 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS Arduino Library -version=2.1.3 +version=2.1.4 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C and Serial Communication with u-blox GNSS modules