|
| 1 | +/* |
| 2 | + Use ESP32 WiFi to get RTCM data from RTK2Go (caster) as a Client |
| 3 | + By: SparkFun Electronics / Nathan Seidle |
| 4 | + Date: November 18th, 2021 |
| 5 | + License: MIT. See license file for more information but you can |
| 6 | + basically do whatever you want with this code. |
| 7 | +
|
| 8 | + This example shows how to obtain RTCM data from a NTRIP Caster over WiFi |
| 9 | + and push it over I2C to a ZED-F9x. |
| 10 | + It's confusing, but the Arduino is acting as a 'client' to a 'caster'. In this case we will |
| 11 | + use RTK2Go.com as our caster because it is free. See the NTRIPServer example to see how |
| 12 | + to push RTCM data to the caster. |
| 13 | +
|
| 14 | + You will need to have a valid mountpoint available. To see available mountpoints go here: http://rtk2go.com:2101/ |
| 15 | +
|
| 16 | + This is a proof of concept to show how to connect to a caster via HTTP. Using WiFi for a rover |
| 17 | + is generally a bad idea because of limited WiFi range in the field. |
| 18 | +
|
| 19 | + Feel like supporting open source hardware? |
| 20 | + Buy a board from SparkFun! |
| 21 | + ZED-F9P RTK2: https://www.sparkfun.com/products/16481 |
| 22 | + RTK Surveyor: https://www.sparkfun.com/products/18443 |
| 23 | + RTK Express: https://www.sparkfun.com/products/18442 |
| 24 | +
|
| 25 | + Hardware Connections: |
| 26 | + Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus |
| 27 | + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) |
| 28 | + Open the serial monitor at 115200 baud to see the output |
| 29 | +*/ |
| 30 | +#include <WiFi.h> |
| 31 | +#include "secrets.h" |
| 32 | + |
| 33 | +#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS |
| 34 | +SFE_UBLOX_GNSS myGNSS; |
| 35 | + |
| 36 | +//The ESP32 core has a built in base64 library but not every platform does |
| 37 | +//We'll use an external lib if necessary. |
| 38 | +#if defined(ARDUINO_ARCH_ESP32) |
| 39 | +#include "base64.h" //Built-in ESP32 library |
| 40 | +#else |
| 41 | +#include <Base64.h> //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform |
| 42 | +#endif |
| 43 | + |
| 44 | +//Global variables |
| 45 | +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 46 | +long lastReceivedRTCM_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps |
| 47 | +int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster |
| 48 | +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 49 | + |
| 50 | +void setup() |
| 51 | +{ |
| 52 | + Serial.begin(115200); |
| 53 | + Serial.println("NTRIP testing"); |
| 54 | + |
| 55 | + Wire.begin(); //Start I2C |
| 56 | + |
| 57 | + if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port |
| 58 | + { |
| 59 | + Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing.")); |
| 60 | + while (1); |
| 61 | + } |
| 62 | + Serial.println(F("u-blox module connected")); |
| 63 | + |
| 64 | + myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise |
| 65 | + 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. |
| 66 | + |
| 67 | + myGNSS.setNavigationFrequency(1); //Set output in Hz. |
| 68 | + |
| 69 | + Serial.print("Connecting to local WiFi"); |
| 70 | + WiFi.begin(ssid, password); |
| 71 | + while (WiFi.status() != WL_CONNECTED) { |
| 72 | + delay(500); |
| 73 | + Serial.print("."); |
| 74 | + } |
| 75 | + Serial.println(); |
| 76 | + |
| 77 | + Serial.print("WiFi connected with IP: "); |
| 78 | + Serial.println(WiFi.localIP()); |
| 79 | + |
| 80 | + while (Serial.available()) Serial.read(); |
| 81 | +} |
| 82 | + |
| 83 | +void loop() |
| 84 | +{ |
| 85 | + if (Serial.available()) beginClient(); |
| 86 | + |
| 87 | + Serial.println(F("Press any key to start NTRIP Client.")); |
| 88 | + |
| 89 | + delay(1000); |
| 90 | +} |
| 91 | + |
| 92 | +//Connect to NTRIP Caster, receive RTCM, and push to ZED module over I2C |
| 93 | +void beginClient() |
| 94 | +{ |
| 95 | + WiFiClient ntripClient; |
| 96 | + long rtcmCount = 0; |
| 97 | + |
| 98 | + Serial.println("Subscribing to Caster. Press key to stop"); |
| 99 | + delay(10); //Wait for any serial to arrive |
| 100 | + while (Serial.available()) Serial.read(); //Flush |
| 101 | + |
| 102 | + while (Serial.available() == 0) |
| 103 | + { |
| 104 | + //Connect if we are not already |
| 105 | + if (ntripClient.connected() == false) |
| 106 | + { |
| 107 | + Serial.print("Opening socket to"); |
| 108 | + Serial.println(casterHost); |
| 109 | + |
| 110 | + if (ntripClient.connect(casterHost, casterPort) == false) //Attempt connection |
| 111 | + { |
| 112 | + Serial.println("Connection to caster failed"); |
| 113 | + } |
| 114 | + else |
| 115 | + { |
| 116 | + Serial.print("Connected to "); |
| 117 | + Serial.print(casterHost); |
| 118 | + Serial.print(": "); |
| 119 | + Serial.println(casterPort); |
| 120 | + |
| 121 | + Serial.print("Requesting NTRIP Data from mount point "); |
| 122 | + Serial.println(mountPoint); |
| 123 | + |
| 124 | + const int SERVER_BUFFER_SIZE = 512; |
| 125 | + char serverRequest[SERVER_BUFFER_SIZE]; |
| 126 | + |
| 127 | + snprintf(serverRequest, SERVER_BUFFER_SIZE, "GET /%s HTTP/1.0\r\nUser-Agent: SparkFun u-blox NTRIPClient v1.0\r\n", |
| 128 | + mountPoint); |
| 129 | + |
| 130 | + char credentials[512]; |
| 131 | + if (strlen(casterUser) == 0) |
| 132 | + { |
| 133 | + strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials)); |
| 134 | + } |
| 135 | + else |
| 136 | + { |
| 137 | + //Pass base64 encoded user:pw |
| 138 | + char userCredentials[sizeof(casterUser) + sizeof(casterUserPW) + 1]; //The ':' takes up a spot |
| 139 | + snprintf(userCredentials, sizeof(userCredentials), "%s:%s", casterUser, casterUserPW); |
| 140 | + |
| 141 | + Serial.print("Sending credentials: "); |
| 142 | + Serial.println(userCredentials); |
| 143 | + |
| 144 | +#if defined(ARDUINO_ARCH_ESP32) |
| 145 | + //Encode with ESP32 built-in library |
| 146 | + base64 b; |
| 147 | + String strEncodedCredentials = b.encode(userCredentials); |
| 148 | + char encodedCredentials[strEncodedCredentials.length() + 1]; |
| 149 | + strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array |
| 150 | + snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials); |
| 151 | +#else |
| 152 | + //Encode with nfriendly library |
| 153 | + int encodedLen = base64_enc_len(strlen(userCredentials)); |
| 154 | + char encodedCredentials[encodedLen]; //Create array large enough to house encoded data |
| 155 | + base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed |
| 156 | +#endif |
| 157 | + } |
| 158 | + strncat(serverRequest, credentials, SERVER_BUFFER_SIZE); |
| 159 | + strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE); |
| 160 | + |
| 161 | + Serial.print("serverRequest size: "); |
| 162 | + Serial.print(strlen(serverRequest)); |
| 163 | + Serial.print(" of "); |
| 164 | + Serial.print(sizeof(serverRequest)); |
| 165 | + Serial.println(" bytes available"); |
| 166 | + |
| 167 | + Serial.println("Sending server request:"); |
| 168 | + Serial.println(serverRequest); |
| 169 | + ntripClient.write(serverRequest, strlen(serverRequest)); |
| 170 | + |
| 171 | + //Wait for response |
| 172 | + unsigned long timeout = millis(); |
| 173 | + while (ntripClient.available() == 0) |
| 174 | + { |
| 175 | + if (millis() - timeout > 5000) |
| 176 | + { |
| 177 | + Serial.println("Mountpoint timed out!"); |
| 178 | + ntripClient.stop(); |
| 179 | + return; |
| 180 | + } |
| 181 | + delay(10); |
| 182 | + } |
| 183 | + |
| 184 | + //Check reply |
| 185 | + bool connectionSuccess = false; |
| 186 | + char response[512]; |
| 187 | + int responseSpot = 0; |
| 188 | + while (ntripClient.available()) |
| 189 | + { |
| 190 | + if (responseSpot == sizeof(response) - 1) break; |
| 191 | + |
| 192 | + response[responseSpot++] = ntripClient.read(); |
| 193 | + if (strstr(response, "200") > 0) //Look for 'ICY 200 OK' |
| 194 | + connectionSuccess = true; |
| 195 | + if (strstr(response, "401") > 0) //Look for '401 Unauthorized' |
| 196 | + { |
| 197 | + Serial.println("Hey - your credentials look bad! Check you caster username and password."); |
| 198 | + connectionSuccess = false; |
| 199 | + } |
| 200 | + } |
| 201 | + response[responseSpot] = '\0'; |
| 202 | + |
| 203 | + if (connectionSuccess == false) |
| 204 | + { |
| 205 | + Serial.print("Failed to connect to "); |
| 206 | + Serial.print(casterHost); |
| 207 | + Serial.print(": "); |
| 208 | + Serial.println(response); |
| 209 | + delay(5000); //Don't spam with lots of connection attempts |
| 210 | + } |
| 211 | + else |
| 212 | + { |
| 213 | + Serial.print("Connected to "); |
| 214 | + Serial.println(casterHost); |
| 215 | + lastReceivedRTCM_ms = millis(); //Reset timeout |
| 216 | + } |
| 217 | + } //End attempt to connect |
| 218 | + } //End connected == false |
| 219 | + |
| 220 | + if (ntripClient.connected() == true) |
| 221 | + { |
| 222 | + uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger |
| 223 | + rtcmCount = 0; |
| 224 | + |
| 225 | + //Print any available RTCM data |
| 226 | + while (ntripClient.available()) |
| 227 | + { |
| 228 | + //Serial.write(ntripClient.read()); //Pipe to serial port is fine but beware, it's a lot of binary data |
| 229 | + rtcmData[rtcmCount++] = ntripClient.read(); |
| 230 | + if (rtcmCount == sizeof(rtcmData)) break; |
| 231 | + } |
| 232 | + |
| 233 | + if (rtcmCount > 0) |
| 234 | + { |
| 235 | + lastReceivedRTCM_ms = millis(); |
| 236 | + |
| 237 | + //Push RTCM to GNSS module over I2C |
| 238 | + myGNSS.pushRawData(rtcmData, rtcmCount, false); |
| 239 | + Serial.print("RTCM pushed to ZED: "); |
| 240 | + Serial.println(rtcmCount); |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + //Close socket if we don't have new data for 10s |
| 245 | + if (millis() - lastReceivedRTCM_ms > maxTimeBeforeHangup_ms) |
| 246 | + { |
| 247 | + Serial.println("RTCM timeout. Disconnecting..."); |
| 248 | + if (ntripClient.connected() == true) |
| 249 | + ntripClient.stop(); |
| 250 | + return; |
| 251 | + } |
| 252 | + |
| 253 | + delay(10); |
| 254 | + } |
| 255 | + |
| 256 | + Serial.println("User pressed a key"); |
| 257 | + Serial.println("Disconnecting..."); |
| 258 | + ntripClient.stop(); |
| 259 | + |
| 260 | + while (Serial.available()) Serial.read(); //Empty buffer of any newline chars |
| 261 | +} |
0 commit comments