From c96f4411fabe439d4b49cf69177df35f968411d3 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Thu, 13 Jan 2022 11:03:52 +0000 Subject: [PATCH 01/21] Add reentry protection for poll and bufferedPoll --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 22 +++++++++++++++++-- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index baf09b5..78cf3bf 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -41,6 +41,8 @@ SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitDepth) for (int i = 0; i < SARA_R5_NUM_SOCKETS; i++) _lastSocketProtocol[i] = 0; // Set to zero initially. Will be set to TCP/UDP by socketOpen etc. _autoTimeZoneForBegin = true; + _bufferedPollReentrant = false; + _pollReentrant = false; memset(_saraRXBuffer, 0, _RXBuffSize); memset(_pruneBuffer, 0, _RXBuffSize); @@ -92,6 +94,11 @@ void SARA_R5::enableDebugging(Stream &debugPort) // It also has a built-in timeout - which ::poll does not bool SARA_R5::bufferedPoll(void) { + if (_bufferedPollReentrant == true) // Check for reentry (i.e. bufferedPoll has been called from inside a callback) + return false; + + _bufferedPollReentrant = true; + int avail = 0; char c = 0; bool handled = false; @@ -190,8 +197,11 @@ bool SARA_R5::bufferedPoll(void) } free(event); + + _bufferedPollReentrant = false; + return handled; -} +} // /bufferedPoll // Parse incoming URC's - the associated parse functions pass the data to the user via the callbacks (if defined) bool SARA_R5::processURCEvent(const char *event) @@ -480,6 +490,11 @@ bool SARA_R5::processURCEvent(const char *event) // ::bufferedPoll is the new improved version. It processes any data in the backlog and includes a timeout. bool SARA_R5::poll(void) { + if (_pollReentrant == true) // Check for reentry (i.e. poll has been called from inside a callback) + return false; + + _pollReentrant = true; + int avail = 0; char c = 0; bool handled = false; @@ -512,6 +527,9 @@ bool SARA_R5::poll(void) { } } + + _pollReentrant = false; + return handled; } @@ -4086,7 +4104,7 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) searchPtr = strchr(searchPtr, '\"'); // Find the first quote searchPtr = strchr(++searchPtr, '\"'); // Find the second quote - int scanned = sscanf(searchPtr, "\",%d,", &readFileSize); // Get the file size (again) + scanned = sscanf(searchPtr, "\",%d,", &readFileSize); // Get the file size (again) if (scanned == 1) { searchPtr = strchr(++searchPtr, '\"'); // Find the third quote diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index a4beaae..559491a 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -823,6 +823,8 @@ class SARA_R5 : public Print uint8_t _maxInitDepth; uint8_t _currentInitDepth = 0; bool _autoTimeZoneForBegin = true; + bool _bufferedPollReentrant = false; // Prevent reentry of bufferedPoll - just in case it gets called from a callback + bool _pollReentrant = false; // Prevent reentry of poll - just in case it gets called from a callback #define _RXBuffSize 2056 const unsigned long _rxWindowMillis = 2; // 1ms is not quite long enough for a single char at 9600 baud. millis roll over much less often than micros. From 4a060622013aca0f632e7e6cf7ce09ed8cf95904 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 14 Jan 2022 15:04:40 +0000 Subject: [PATCH 02/21] Add SARA-R5_Example15_GNSS_NTRIP_Caster --- .../GNSS_Callbacks.ino | 88 ++++++ .../SARA-R5_Callbacks.ino | 43 +++ .../SARA-R5_Example15_GNSS_NTRIP_Caster.ino | 290 ++++++++++++++++++ .../SARA-R5_NTRIP_Client.ino | 285 +++++++++++++++++ .../Utils.ino | 78 +++++ .../secrets.h | 27 ++ 6 files changed, 811 insertions(+) create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino create mode 100644 examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino new file mode 100644 index 0000000..4a48612 --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino @@ -0,0 +1,88 @@ +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// Callback: pushGPGGA will be called when new GPGGA NMEA data arrives +// See u-blox_structs.h for the full definition of NMEA_GGA_data_t +// _____ You can use any name you like for the callback. Use the same name when you call setNMEAGPGGAcallback +// / _____ This _must_ be NMEA_GGA_data_t +// | / _____ You can use any name you like for the struct +// | | / +// | | | +void pushGPGGA(NMEA_GGA_data_t nmeaData) +{ + if (connectionOpen) + { + Serial.print(F("Pushing GGA to server: ")); + Serial.print((const char *)nmeaData.nmea); // .nmea is printable (NULL-terminated) and already has \r\n on the end + + //Push our current GGA sentence to caster + mySARA.socketWrite(socketNum, (const char *)nmeaData.nmea); + } +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// Callback: printPVTdata will be called when new NAV PVT data arrives +// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t +// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallback +// / _____ This _must_ be UBX_NAV_PVT_data_t +// | / _____ You can use any name you like for the struct +// | | / +// | | | +void printPVTdata(UBX_NAV_PVT_data_t ubxDataStruct) +{ + long latitude = ubxDataStruct.lat; // Print the latitude + Serial.print(F("Lat: ")); + Serial.print(latitude / 10000000L); + Serial.print(F(".")); + Serial.print(abs(latitude % 10000000L)); + + long longitude = ubxDataStruct.lon; // Print the longitude + Serial.print(F(" Long: ")); + Serial.print(longitude / 10000000L); + Serial.print(F(".")); + Serial.print(abs(longitude % 10000000L)); + + long altitude = ubxDataStruct.hMSL; // Print the height above mean sea level + Serial.print(F(" Height: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + uint8_t fixType = ubxDataStruct.fixType; // Print the fix type + Serial.print(F(" Fix: ")); + Serial.print(fixType); + if (fixType == 0) + Serial.print(F(" (None)")); + else if (fixType == 1) + Serial.print(F(" (Dead Reckoning)")); + else if (fixType == 2) + Serial.print(F(" (2D)")); + else if (fixType == 3) + Serial.print(F(" (3D)")); + else if (fixType == 3) + Serial.print(F(" (GNSS + Dead Reckoning)")); + else if (fixType == 5) + Serial.print(F(" (Time Only)")); + else + Serial.print(F(" (UNKNOWN)")); + + uint8_t carrSoln = ubxDataStruct.flags.bits.carrSoln; // Print the carrier solution + Serial.print(F(" Carrier Solution: ")); + Serial.print(carrSoln); + if (carrSoln == 0) + Serial.print(F(" (None)")); + else if (carrSoln == 1) + Serial.print(F(" (Floating)")); + else if (carrSoln == 2) + Serial.print(F(" (Fixed)")); + else + Serial.print(F(" (UNKNOWN)")); + + uint32_t hAcc = ubxDataStruct.hAcc; // Print the horizontal accuracy estimate + Serial.print(F(" Horizontal Accuracy Estimate: ")); + Serial.print(hAcc); + Serial.print(F(" (mm)")); + + Serial.println(); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino new file mode 100644 index 0000000..74c2719 --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino @@ -0,0 +1,43 @@ +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +// processSocketClose is provided to the SARA-R5 library via a +// callback setter -- setSocketCloseCallback. (See setup()) +// +// Note: the SARA-R5 only sends a +UUSOCL URC when the socket is closed by the remote +void processSocketClose(int socket) +{ + Serial.println(); + Serial.print(F("processSocketClose: Socket ")); + Serial.print(socket); + Serial.println(F(" closed!")); + + if (socket == socketNum) + { + socketNum = -1; + connectionOpen = false; + } +} + +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +// processPSDAction is provided to the SARA-R5 library via a +// callback setter -- setPSDActionCallback. (See setup()) +void processPSDAction(int result, IPAddress ip) +{ + Serial.println(); + Serial.print(F("processPSDAction: result: ")); + Serial.print(String(result)); + if (result == 0) + Serial.print(F(" (success)")); + Serial.print(F(" IP Address: \"")); + Serial.print(String(ip[0])); + Serial.print(F(".")); + Serial.print(String(ip[1])); + Serial.print(F(".")); + Serial.print(String(ip[2])); + Serial.print(F(".")); + Serial.print(String(ip[3])); + Serial.println(F("\"")); +} + +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino new file mode 100644 index 0000000..2139484 --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino @@ -0,0 +1,290 @@ +/* + + SARA-R5 Example + =============== + + u-blox GNSS NTRIP Caster Client + + Written by: Paul Clark + Date: January 14th 2021 + + This example uses the SARA's mobile data connection to: + * Request RTK RTCM data from a NTRIP Caster service + * Push the RTCM data to an external u-blox GNSS module over I2C (not to the one built-in to the SARA-R510M8S) + + The PDP profile is read from NVM. Please make sure you have run examples 4 & 7 previously to set up the profile. + + Update secrets.h with your NTRIP Caster username and password + + ************************************************************************************************** + * Important Note: * + * * + * This example pulls kBytes of correction data from the NTRIP Caster. * + * Depending on your location and service provider, the data rate may exceed the allowable * + * rates for LTE-M or NB-IoT. * + * Worst case, your service provider may throttle or block the connection - now or in the future. * + * We are looking for a long-term solution to this - almost certainly using LTE Cat 1 instead. * + ************************************************************************************************** + + Feel like supporting open source hardware? + Buy a board from SparkFun! + + Licence: MIT + Please see LICENSE.md for full details + +*/ + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// 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 + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_SARA-R5_Arduino_Library + +// Uncomment the next line to connect to the SARA-R5 using hardware Serial1 +#define saraSerial Serial1 + +// Uncomment the next line to create a SoftwareSerial object to pass to the SARA-R5 library instead +//SoftwareSerial saraSerial(8, 9); + +// Create a SARA_R5 object to use throughout the sketch +// Usually we would tell the library which GPIO pin to use to control the SARA power (see below), +// but we can start the SARA without a power pin. It just means we need to manually +// turn the power on if required! ;-D +SARA_R5 mySARA; + +// Create a SARA_R5 object to use throughout the sketch +// We need to tell the library what GPIO pin is connected to the SARA power pin. +// If you're using the MicroMod Asset Tracker and the MicroMod Artemis Processor Board, +// the pin name is G2 which is connected to pin AD34. +// Change the pin number if required. +//SARA_R5 mySARA(34); + +// Create a SARA_R5 object to use throughout the sketch +// If you are using the LTE GNSS Breakout, and have access to the SARA's RESET_N pin, you can pass that to the library too +// allowing it to do an emergency shutdown if required. +// Change the pin numbers if required. +//SARA_R5 mySARA(34, 35); // PWR_ON, RESET_N + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// Globals + +volatile int socketNum = -1; // The TCP socket number. -1 indicates invalid/closed socket +volatile bool connectionOpen = false; // Flag to indicate if the connection to the NTRIP Caster is open +volatile unsigned long lastReceivedRTCM_ms; // Record when data last arrived - so we can time out if required + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void setup() +{ + String currentOperator = ""; + + Serial.begin(115200); // Start the serial console + + // Wait for user to press key to begin + Serial.println(F("SARA-R5 Example")); + Serial.println(F("Wait for the SARA NI LED to light up - then press any key to begin")); + + while (!Serial.available()) // Wait for the user to press a key (send any serial character) + ; + while (Serial.available()) // Empty the serial RX buffer + Serial.read(); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Start communication with the SARA-R5. Load and activate the Packet Switched Data profile. + + //mySARA.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + + // For the MicroMod Asset Tracker, we need to invert the power pin so it pulls high instead of low + // Comment the next line if required + mySARA.invertPowerPin(true); + + // Initialize the SARA + if (mySARA.begin(saraSerial, 115200) ) + { + Serial.println(F("SARA-R5 connected!")); + } + else + { + Serial.println(F("Unable to communicate with the SARA.")); + Serial.println(F("Manually power-on (hold the SARA On button for 3 seconds) on and try again.")); + while (1) ; // Loop forever on fail + } + Serial.println(); + + // First check to see if we're connected to an operator: + if (mySARA.getOperator(¤tOperator) == SARA_R5_SUCCESS) + { + Serial.print(F("Connected to: ")); + Serial.println(currentOperator); + } + else + { + Serial.print(F("The SARA is not yet connected to an operator. Please use the previous examples to connect. Or wait and retry. Freezing...")); + while (1) + ; // Do nothing more + } + + // Deactivate the PSD profile - in case one is already active + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_DEACTIVATE) != SARA_R5_SUCCESS) + { + Serial.println(F("Warning: performPDPaction (deactivate profile) failed. Probably because no profile was active.")); + } + + // Load the PSD profile from NVM - these were saved by a previous example + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_LOAD) != SARA_R5_SUCCESS) + { + Serial.println(F("performPDPaction (load from NVM) failed! Freezing...")); + while (1) + ; // Do nothing more + } + + // Set a callback to process the results of the PSD Action - OPTIONAL + mySARA.setPSDActionCallback(&processPSDAction); + + // Activate the profile + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_ACTIVATE) != SARA_R5_SUCCESS) + { + Serial.println(F("performPDPaction (activate profile) failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Print the dynamic IP Address (for profile 0) + IPAddress myAddress; + mySARA.getNetworkAssignedIPAddress(0, &myAddress); + Serial.print(F("\r\nMy IP Address is: ")); + Serial.println(myAddress.toString()); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Set a callback to process the socket close + // + // Note: the SARA-R5 only sends a +UUSOCL URC when the socket os closed by the remote + mySARA.setSocketCloseCallback(&processSocketClose); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Start I2C. Connect to the GNSS. + + Wire.begin(); //Start I2C + + // Uncomment the next line to enable the 'major' GNSS debug messages on Serial so you can see what AssistNow data is being sent + //myGNSS.enableDebugging(Serial, true); + + if (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.")); + 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.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible + + myGNSS.setNavigationFrequency(1); //Set output in Hz. + + // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA + myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP); + + myGNSS.setNMEAGPGGAcallback(&pushGPGGA); // Set up the callback for GPGGA + + myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 10); // Tell the module to output GGA every 10 seconds + + myGNSS.setAutoPVTcallback(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed + + //myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM + +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void loop() +{ + myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it. + myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed. + + mySARA.bufferedPoll(); // Process the SARA-R5 URC's. This pushes the incoming RTCM data to the GNSS. + + enum states // Use a 'state machine' to open and close the connection + { + open_connection, + process_connection_and_wait_for_keypress, + close_connection, + waiting_for_keypress + }; + static states state = open_connection; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + switch (state) + { + case open_connection: + Serial.println(F("Connecting to the NTRIP caster...")); + if (beginClient((int *)&socketNum, (bool *)&connectionOpen)) // Try to open the connection to the caster + { + Serial.println(F("Connected to the NTRIP caster! Press any key to disconnect...")); + state = process_connection_and_wait_for_keypress; // Move on + } + else + { + Serial.print(F("Could not connect to the caster. Trying again in 5 seconds.")); + for (int i = 0; i < 5; i++) + { + delay(1000); + Serial.print(F(".")); + } + Serial.println(); + } + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case process_connection_and_wait_for_keypress: + // If the connection has dropped or timed out, or if the user has pressed a key + if ((processConnection((int)socketNum, (bool)connectionOpen) == false) || (keyPressed())) + { + state = close_connection; // Move on + } + delay(50); // Don't pound the SARA too hard + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case close_connection: + Serial.println(F("Closing the connection to the NTRIP caster...")); + closeConnection((int *)&socketNum, (bool *)&connectionOpen); + Serial.println(F("Press any key to reconnect...")); + state = waiting_for_keypress; // Move on + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case waiting_for_keypress: + // Wait for the user to press a key + if (keyPressed()) + state = open_connection; // Move on + break; + } +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino new file mode 100644 index 0000000..c62a28b --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino @@ -0,0 +1,285 @@ + +#include "secrets.h" // Update secrets.h with your AssistNow token string + +const unsigned long maxTimeBeforeHangup_ms = 20000UL; //If we fail to get a complete RTCM frame after 20s, then disconnect from caster +const int maxSocketRead = 1000; // Limit socket reads to this length + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Connect to NTRIP Caster. Return true is connection is successful. +bool beginClient(int *theSocket, bool *connectionIsOpen) +{ + // Sanity check - return now if the socket is already open (should be impossible, but still...) + if (*theSocket >= 0) + { + Serial.print(F("beginClient: Socket is already open")); + if (*connectionIsOpen) + { + Serial.print(F(" and the connection is open!")); + } + else + { + Serial.print(F("!")); + } + return (false); + } + + Serial.println(F("beginClient: Opening TCP socket")); + + *theSocket = mySARA.socketOpen(SARA_R5_TCP); + if (*theSocket == -1) + { + Serial.println(F("beginClient: socketOpen failed!")); + return (false); + } + + Serial.print(F("beginClient: Using socket ")); + Serial.println(*theSocket); + + Serial.print(F("beginClient: Connecting to ")); + Serial.print(casterHost); + Serial.print(F(" on port ")); + Serial.println(casterPort); + + if (mySARA.socketConnect(*theSocket, casterHost, casterPort) != SARA_R5_SUCCESS) + { + Serial.println(F("beginClient: socketConnect failed!")); + } + else + { + Serial.print(F("beginClient: Connected to ")); + Serial.print(casterHost); + Serial.print(F(" : ")); + Serial.println(casterPort); + + Serial.print(F("beginClient: Requesting NTRIP Data from mount point ")); + Serial.println(mountPoint); + + // Set up the server request (GET) + 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); + + // Set up the credentials + 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("beginClient: 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); + } + + // Add the encoded credentials to the server request + strncat(serverRequest, credentials, SERVER_BUFFER_SIZE); + strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE); + + Serial.print(F("beginClient: serverRequest size: ")); + Serial.print(strlen(serverRequest)); + Serial.print(F(" of ")); + Serial.print(sizeof(serverRequest)); + Serial.println(F(" bytes available")); + + // Send the server request + Serial.println(F("beginClient: Sending server request: ")); + Serial.println(serverRequest); + + mySARA.socketWrite(*theSocket, (const char *)serverRequest); + + //Wait up to 5 seconds for response. Poll the number of available bytes. Don't use the callback yet. + unsigned long startTime = millis(); + int availableLength = 0; + while (availableLength == 0) + { + mySARA.socketReadAvailable(*theSocket, &availableLength); + if (millis() > (startTime + 5000)) + { + Serial.println(F("beginClient: Caster timed out!")); + return (false); + } + delay(100); + } + + Serial.print(F("beginClient: server replied with ")); + Serial.print(availableLength); + Serial.println(F(" bytes")); + + //Check reply + int connectionResult = 0; + char response[512 * 4]; + memset(response, 0, 512 * 4); + size_t responseSpot = 0; + while ((availableLength > 0) && (connectionResult == 0)) // Read bytes from the caster and store them + { + if ((responseSpot + availableLength) >= (sizeof(response) - 1)) // Exit the loop if we get too much data + break; + + mySARA.socketRead(*theSocket, availableLength, &response[responseSpot]); + responseSpot += availableLength; + + //Serial.print(F("beginClient: response is: ")); + //Serial.println(response); + + if (connectionResult == 0) // Only print success/fail once + { + if (strstr(response, "200") != NULL) //Look for '200 OK' + { + Serial.println(F("beginClient: 200 seen!")); + connectionResult = 200; + } + if (strstr(response, "401") != NULL) //Look for '401 Unauthorized' + { + Serial.println(F("beginClient: Hey - your credentials look bad! Check your caster username and password.")); + connectionResult = 401; + } + } + + mySARA.socketReadAvailable(*theSocket, &availableLength); // Update availableLength + + Serial.print(F("beginClient: socket now has ")); + Serial.print(availableLength); + Serial.println(F(" bytes available")); + } + response[responseSpot] = '\0'; // NULL-terminate the response + + //Serial.print(F("beginClient: Caster responded with: ")); Serial.println(response); // Uncomment this line to see the full response + + if (connectionResult != 200) + { + Serial.print(F("beginClient: Failed to connect to ")); + Serial.println(casterHost); + return (false); + } + else + { + Serial.print(F("beginClient: Connected to: ")); + Serial.println(casterHost); + lastReceivedRTCM_ms = millis(); //Reset timeout + } + + } //End attempt to connect + + *connectionIsOpen = true; + return (true); +} // /beginClient + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Read and push the RTCM data to the GNSS +//Return false if: the connection has dropped, or if we receive no data for maxTimeBeforeHangup_ms +bool processConnection(int theSocket, bool connectionIsOpen) +{ + if (!connectionIsOpen) + { + Serial.print(F("processConnection: the connection is closed")); + if (theSocket < 0) + { + Serial.print(F(" and the socket is closed too!")); + } + else + { + Serial.print(F("!")); + } + return (false); + } + + if (theSocket < 0) + { + Serial.print(F("processConnection: the socket is closed!")); + return (false); + } + + int availableLength = 0; + mySARA.socketReadAvailable(theSocket, &availableLength); + + if (availableLength > 0) + { + Serial.print(F("processConnection: Socket ")); + Serial.print(theSocket); + Serial.print(F(" has ")); + Serial.print(availableLength); + Serial.println(F(" bytes available")); + + if (availableLength > maxSocketRead) + { + Serial.println(F("processConnection: only reading 1024 bytes")); + availableLength = 1000; + } + + uint8_t *socketData = new uint8_t[availableLength]; + + if (socketData == NULL) + { + Serial.print(F("processConnection: new (malloc) failed!")); + return (false); + } + + if (mySARA.socketRead(theSocket, availableLength, (char *)socketData) != SARA_R5_SUCCESS) + { + Serial.print(F("processConnection: socketRead failed!")); + free(socketData); + return (false); + } + + Serial.println(F("processConnection: Pushing it to the GNSS...")); + myGNSS.pushRawData(socketData, availableLength); + + delete[] socketData; + + lastReceivedRTCM_ms = millis(); // Update lastReceivedRTCM_ms + } + + //Timeout if we don't have new data for maxTimeBeforeHangup_ms + if ((millis() - lastReceivedRTCM_ms) > maxTimeBeforeHangup_ms) + { + Serial.println(F("processConnection: RTCM timeout!")); + return (false); // Connection has timed out - return false + } + + return (true); +} // /processConnection + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void closeConnection(int *theSocket, bool *connectionIsOpen) +{ + // Check the socket is actually open, otherwise we'll get an error when we try to close it + if (*theSocket >= 0) + { + mySARA.socketClose(*theSocket); + Serial.println(F("closeConnection: Connection closed!")); + } + else + { + Serial.println(F("closeConnection: Connection was already closed!")); + } + + *theSocket = -1; // Clear the socket number to indicate it is closed + *connectionIsOpen = false; // Flag that the connection is closed +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino new file mode 100644 index 0000000..1f33eba --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino @@ -0,0 +1,78 @@ +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void prettyPrintString(String theString) // Pretty-print a String in HEX and ASCII format +{ + int theLength = theString.length(); + + Serial.println(); + Serial.print(F("String length is ")); + Serial.print(theLength); + Serial.print(F(" (0x")); + Serial.print(theLength, HEX); + Serial.println(F(")")); + Serial.println(); + + for (int i = 0; i < theLength; i += 16) + { + if (i < 10000) Serial.print(F("0")); + if (i < 1000) Serial.print(F("0")); + if (i < 100) Serial.print(F("0")); + if (i < 10) Serial.print(F("0")); + Serial.print(i); + + Serial.print(F(" 0x")); + + if (i < 0x1000) Serial.print(F("0")); + if (i < 0x100) Serial.print(F("0")); + if (i < 0x10) Serial.print(F("0")); + Serial.print(i, HEX); + + Serial.print(F(" ")); + + int j; + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if (theString[i + j] < 0x10) Serial.print(F("0")); + Serial.print(theString[i + j], HEX); + Serial.print(F(" ")); + } + + if (((i + j) == theLength) && (j < 16)) + { + for (int k = 0; k < (16 - (theLength % 16)); k++) + { + Serial.print(F(" ")); + } + } + + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if ((theString[i + j] >= 0x20) && (theString[i + j] <= 0x7E)) + Serial.write(theString[i + j]); + else + Serial.print(F(".")); + } + + Serial.println(); + } + + Serial.println(); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Return true if a key has been pressed +bool keyPressed() +{ + if (Serial.available()) // Check for a new key press + { + delay(100); // Wait for any more keystrokes to arrive + while (Serial.available()) // Empty the serial buffer + Serial.read(); + return (true); + } + + return (false); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h new file mode 100644 index 0000000..9de9b71 --- /dev/null +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h @@ -0,0 +1,27 @@ +//Your WiFi credentials +const char ssid[] = "yourSSID"; +const char password[] = "yourPassword"; + +//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 + +// Skylark (Swift Navigation) is awesome - but requires a subscription: +// https://www.swiftnav.com/skylark +// https://account.swiftnav.com/sign-up +// Use the promo-code ONEMONTHFREE for a free one month access to Skylark on one device +const char casterHost[] = "na.skylark.swiftnav.com"; // na = North Americs L1+L2; eu = Europe L1+L2 +const uint16_t casterPort = 2101; +const char casterUser[] = "NTRIPusername+accountSubdomain"; // This is generated when you add a device to your Skylark account +const char casterUserPW[] = "devicePassword"; +const char mountPoint[] = "CRS"; // The mount point you want to get data from. Select CRS (Cloud Reference Station) for the ZED-F9x From f2cae0c70190cdd74d7b2a919962c17dfae747f2 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 15 Jan 2022 10:30:32 +0000 Subject: [PATCH 03/21] Update socketRead to respect _saraR5maxSocketRead. Update Example15 --- .../GNSS_Callbacks.ino | 0 .../SARA-R5_Callbacks.ino | 26 ++- ...le15_GNSS_NTRIP_Caster_With_Callbacks.ino} | 15 +- .../SARA-R5_NTRIP_Client.ino | 73 +----- .../Utils.ino | 0 .../secrets.h | 0 ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 218 ++++++++++++++---- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 27 ++- 8 files changed, 232 insertions(+), 127 deletions(-) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks}/GNSS_Callbacks.ino (100%) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks}/SARA-R5_Callbacks.ino (65%) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino} (95%) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks}/SARA-R5_NTRIP_Client.ino (80%) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks}/Utils.ino (100%) rename examples/{SARA-R5_Example15_GNSS_NTRIP_Caster => SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks}/secrets.h (100%) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/GNSS_Callbacks.ino similarity index 100% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/GNSS_Callbacks.ino rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/GNSS_Callbacks.ino diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino similarity index 65% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino index 74c2719..bc4a1f5 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Callbacks.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino @@ -1,12 +1,35 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// processSocketData is provided to the SARA-R5 library via a +// callback setter -- setSocketReadCallback. (See setup()) +void processSocketData(int socket, String theData) +{ + Serial.print(F("processSocketData: Data received on socket ")); + Serial.print(socket); + Serial.print(F(". Length is ")); + Serial.print(theData.length()); + + if (connectionOpen) + { + Serial.println(F(". Pushing it to the GNSS...")); + myGNSS.pushRawData((uint8_t *)theData.c_str(), theData.length()); + + lastReceivedRTCM_ms = millis(); // Update lastReceivedRTCM_ms + } + else + { + Serial.println(); + } +} + +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // processSocketClose is provided to the SARA-R5 library via a // callback setter -- setSocketCloseCallback. (See setup()) // // Note: the SARA-R5 only sends a +UUSOCL URC when the socket is closed by the remote void processSocketClose(int socket) { - Serial.println(); Serial.print(F("processSocketClose: Socket ")); Serial.print(socket); Serial.println(F(" closed!")); @@ -24,7 +47,6 @@ void processSocketClose(int socket) // callback setter -- setPSDActionCallback. (See setup()) void processPSDAction(int result, IPAddress ip) { - Serial.println(); Serial.print(F("processPSDAction: result: ")); Serial.print(String(result)); if (result == 0) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino similarity index 95% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino index 2139484..2265e29 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_Example15_GNSS_NTRIP_Caster.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino @@ -175,6 +175,12 @@ void setup() //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + // Set a callback to process the socket data + // This will push the RTCM data to the GNSS + mySARA.setSocketReadCallback(&processSocketData); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + // Set a callback to process the socket close // // Note: the SARA-R5 only sends a +UUSOCL URC when the socket os closed by the remote @@ -228,7 +234,7 @@ void loop() enum states // Use a 'state machine' to open and close the connection { open_connection, - process_connection_and_wait_for_keypress, + check_connection_and_wait_for_keypress, close_connection, waiting_for_keypress }; @@ -243,7 +249,7 @@ void loop() if (beginClient((int *)&socketNum, (bool *)&connectionOpen)) // Try to open the connection to the caster { Serial.println(F("Connected to the NTRIP caster! Press any key to disconnect...")); - state = process_connection_and_wait_for_keypress; // Move on + state = check_connection_and_wait_for_keypress; // Move on } else { @@ -259,13 +265,12 @@ void loop() //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - case process_connection_and_wait_for_keypress: + case check_connection_and_wait_for_keypress: // If the connection has dropped or timed out, or if the user has pressed a key - if ((processConnection((int)socketNum, (bool)connectionOpen) == false) || (keyPressed())) + if ((checkConnection((int)socketNum, (bool)connectionOpen) == false) || (keyPressed())) { state = close_connection; // Move on } - delay(50); // Don't pound the SARA too hard break; //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino similarity index 80% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino index c62a28b..6f53ab8 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/SARA-R5_NTRIP_Client.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino @@ -2,7 +2,6 @@ #include "secrets.h" // Update secrets.h with your AssistNow token string const unsigned long maxTimeBeforeHangup_ms = 20000UL; //If we fail to get a complete RTCM frame after 20s, then disconnect from caster -const int maxSocketRead = 1000; // Limit socket reads to this length //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -130,8 +129,8 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //Check reply int connectionResult = 0; - char response[512 * 4]; - memset(response, 0, 512 * 4); + char response[512]; + memset(response, 0, 512); size_t responseSpot = 0; while ((availableLength > 0) && (connectionResult == 0)) // Read bytes from the caster and store them { @@ -189,74 +188,24 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -//Read and push the RTCM data to the GNSS +//Check the connection //Return false if: the connection has dropped, or if we receive no data for maxTimeBeforeHangup_ms -bool processConnection(int theSocket, bool connectionIsOpen) +bool checkConnection(int theSocket, bool connectionIsOpen) { - if (!connectionIsOpen) + if ((theSocket >= 0) && connectionIsOpen) // Check that the connection is still open { - Serial.print(F("processConnection: the connection is closed")); - if (theSocket < 0) - { - Serial.print(F(" and the socket is closed too!")); - } - else - { - Serial.print(F("!")); - } - return (false); - } - - if (theSocket < 0) - { - Serial.print(F("processConnection: the socket is closed!")); - return (false); + ; // Nothing to do here. The RTCM is pushed to the GNSS by the callabck. } - - int availableLength = 0; - mySARA.socketReadAvailable(theSocket, &availableLength); - - if (availableLength > 0) + else { - Serial.print(F("processConnection: Socket ")); - Serial.print(theSocket); - Serial.print(F(" has ")); - Serial.print(availableLength); - Serial.println(F(" bytes available")); - - if (availableLength > maxSocketRead) - { - Serial.println(F("processConnection: only reading 1024 bytes")); - availableLength = 1000; - } - - uint8_t *socketData = new uint8_t[availableLength]; - - if (socketData == NULL) - { - Serial.print(F("processConnection: new (malloc) failed!")); - return (false); - } - - if (mySARA.socketRead(theSocket, availableLength, (char *)socketData) != SARA_R5_SUCCESS) - { - Serial.print(F("processConnection: socketRead failed!")); - free(socketData); - return (false); - } - - Serial.println(F("processConnection: Pushing it to the GNSS...")); - myGNSS.pushRawData(socketData, availableLength); - - delete[] socketData; - - lastReceivedRTCM_ms = millis(); // Update lastReceivedRTCM_ms - } + Serial.println(F("checkConnection: Connection dropped!")); + return (false); // Connection has dropped - return false + } //Timeout if we don't have new data for maxTimeBeforeHangup_ms if ((millis() - lastReceivedRTCM_ms) > maxTimeBeforeHangup_ms) { - Serial.println(F("processConnection: RTCM timeout!")); + Serial.println(F("checkConnection: RTCM timeout!")); return (false); // Connection has timed out - return false } diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/Utils.ino similarity index 100% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/Utils.ino rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/Utils.ino diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/secrets.h similarity index 100% rename from examples/SARA-R5_Example15_GNSS_NTRIP_Caster/secrets.h rename to examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/secrets.h diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 78cf3bf..74f3d2d 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -43,6 +43,7 @@ SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitDepth) _autoTimeZoneForBegin = true; _bufferedPollReentrant = false; _pollReentrant = false; + _currentInitDepth = 0; memset(_saraRXBuffer, 0, _RXBuffSize); memset(_pruneBuffer, 0, _RXBuffSize); @@ -2490,16 +2491,23 @@ SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, String address, int port, St return socketWriteUDP(socket, address.c_str(), port, str.c_str(), str.length()); } -SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest) +SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest, int *bytesRead) { char *command; char *response; char *strBegin; - int readIndex = 0; + int readIndexTotal = 0; + int readIndexThisRead = 0; SARA_R5_error_t err; int scanNum = 0; int readLength = 0; int socketStore = 0; + int bytesLeftToRead = length; + int bytesToRead; + + // Set *bytesRead to zero + if (bytesRead != NULL) + *bytesRead = 0; // Check if length is zero if (length == 0) @@ -2509,12 +2517,14 @@ SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest) return SARA_R5_ERROR_UNEXPECTED_PARAM; } + // Allocate memory for the command command = sara_r5_calloc_char(strlen(SARA_R5_READ_SOCKET) + 32); if (command == NULL) return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d", SARA_R5_READ_SOCKET, socket, length); - int responseLength = length + strlen(SARA_R5_READ_SOCKET) + minimumResponseAllocation; + // Allocate memory for the response + // We only need enough to read _saraR5maxSocketRead bytes - not the whole thing + int responseLength = _saraR5maxSocketRead + strlen(SARA_R5_READ_SOCKET) + minimumResponseAllocation; response = sara_r5_calloc_char(responseLength); if (response == NULL) { @@ -2522,15 +2532,37 @@ SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest) return SARA_R5_ERROR_OUT_OF_MEMORY; } - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT, responseLength); + // If there are more than _saraR5maxSocketRead (1024) bytes to be read, + // we need to do multiple reads to get all the data - if (err == SARA_R5_ERROR_SUCCESS) + while (bytesLeftToRead > 0) { - // Extract the data - and check the quote is present + if (bytesLeftToRead > _saraR5maxSocketRead) // Limit a single read to _saraR5maxSocketRead + bytesToRead = _saraR5maxSocketRead; + else + bytesToRead = bytesLeftToRead; + + sprintf(command, "%s=%d,%d", SARA_R5_READ_SOCKET, socket, bytesToRead); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT, responseLength); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("socketRead: sendCommandWithResponse err ")); + _debugPort->println(err); + } + free(command); + free(response); + return err; + } + + // Extract the data char *searchPtr = strstr(response, "+USORD: "); if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USORD: %d,%d,\"", + scanNum = sscanf(searchPtr, "+USORD: %d,%d", &socketStore, &readLength); if (scanNum != 2) { @@ -2544,21 +2576,32 @@ SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest) return SARA_R5_ERROR_UNEXPECTED_RESPONSE; } - // Check that readLength == length - // TO DO: handle this properly. The user needs to know how many bytes were read. - if (readLength != length) + // Check that readLength == bytesToRead + if (readLength != bytesToRead) { if (_printDebug == true) { - _debugPort->print(F("socketRead: length mismatch! length=")); - _debugPort->print(length); + _debugPort->print(F("socketRead: length mismatch! bytesToRead=")); + _debugPort->print(bytesToRead); _debugPort->print(F(" readLength=")); _debugPort->println(readLength); } } + // Check that readLength > 0 + if (readLength == 0) + { + if (_printDebug == true) + { + _debugPort->println(F("socketRead: zero length!")); + } + free(command); + free(response); + return SARA_R5_ERROR_ZERO_READ_LENGTH; + } + // Find the first double-quote: - strBegin = strchr(response, '\"'); + strBegin = strchr(searchPtr, '\"'); if (strBegin == NULL) { @@ -2568,20 +2611,38 @@ SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest) } // Now copy the data into readDest - while (readIndex < readLength) + readIndexThisRead = 1; // Start after the quote + while (readIndexThisRead < (readLength + 1)) { - readDest[readIndex] = strBegin[1 + readIndex]; - readIndex++; + readDest[readIndexTotal] = strBegin[readIndexThisRead]; + readIndexTotal++; + readIndexThisRead++; } if (_printDebug == true) _debugPort->println(F("socketRead: success")); - } + + // Update *bytesRead + if (bytesRead != NULL) + *bytesRead = readIndexTotal; + + // How many bytes are left to read? + // This would have been bytesLeftToRead -= bytesToRead + // Except the SARA can potentially return less data than requested... + // So we need to subtract readLength instead. + bytesLeftToRead -= readLength; + if (_printDebug == true) + { + if (bytesLeftToRead > 0) + _debugPort->print(F("socketRead: multiple read. bytesLeftToRead: ")); + _debugPort->println(bytesLeftToRead); + } + } // /while (bytesLeftToRead > 0) free(command); free(response); - return err; + return SARA_R5_ERROR_SUCCESS; } SARA_R5_error_t SARA_R5::socketReadAvailable(int socket, int *length) @@ -2635,18 +2696,26 @@ SARA_R5_error_t SARA_R5::socketReadAvailable(int socket, int *length) return err; } -SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, IPAddress *remoteIPAddress, int *remotePort) +SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, + IPAddress *remoteIPAddress, int *remotePort, int *bytesRead) { char *command; char *response; char *strBegin; - int readIndex = 0; + int readIndexTotal = 0; + int readIndexThisRead = 0; SARA_R5_error_t err; int scanNum = 0; int remoteIPstore[4] = { 0, 0, 0, 0 }; int portStore = 0; int readLength = 0; int socketStore = 0; + int bytesLeftToRead = length; + int bytesToRead; + + // Set *bytesRead to zero + if (bytesRead != NULL) + *bytesRead = 0; // Check if length is zero if (length == 0) @@ -2656,12 +2725,14 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, I return SARA_R5_ERROR_UNEXPECTED_PARAM; } + // Allocate memory for the command command = sara_r5_calloc_char(strlen(SARA_R5_READ_UDP_SOCKET) + 32); if (command == NULL) return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d", SARA_R5_READ_UDP_SOCKET, socket, length); - int responseLength = length + strlen(SARA_R5_READ_UDP_SOCKET) + minimumResponseAllocation; + // Allocate memory for the response + // We only need enough to read _saraR5maxSocketRead bytes - not the whole thing + int responseLength = _saraR5maxSocketRead + strlen(SARA_R5_READ_UDP_SOCKET) + minimumResponseAllocation; response = sara_r5_calloc_char(responseLength); if (response == NULL) { @@ -2669,15 +2740,37 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, I return SARA_R5_ERROR_OUT_OF_MEMORY; } - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT, responseLength); + // If there are more than _saraR5maxSocketRead (1024) bytes to be read, + // we need to do multiple reads to get all the data - if (err == SARA_R5_ERROR_SUCCESS) + while (bytesLeftToRead > 0) { - // Extract the data - and check the third quote is present + if (bytesLeftToRead > _saraR5maxSocketRead) // Limit a single read to _saraR5maxSocketRead + bytesToRead = _saraR5maxSocketRead; + else + bytesToRead = bytesLeftToRead; + + sprintf(command, "%s=%d,%d", SARA_R5_READ_UDP_SOCKET, socket, bytesToRead); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT, responseLength); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("socketReadUDP: sendCommandWithResponse err ")); + _debugPort->println(err); + } + free(command); + free(response); + return err; + } + + // Extract the data char *searchPtr = strstr(response, "+USORF: "); if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USORF: %d,\"%d.%d.%d.%d\",%d,%d,\"", + scanNum = sscanf(searchPtr, "+USORF: %d,\"%d.%d.%d.%d\",%d,%d", &socketStore, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], &portStore, &readLength); if (scanNum != 7) @@ -2692,21 +2785,32 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, I return SARA_R5_ERROR_UNEXPECTED_RESPONSE; } - // Check that readLength == length - // TO DO: handle this properly. The user needs to know how many bytes were read. - if (readLength != length) + // Check that readLength == bytesToRead + if (readLength != bytesToRead) { if (_printDebug == true) { - _debugPort->print(F("socketReadUDP: length mismatch! length=")); - _debugPort->print(length); + _debugPort->print(F("socketReadUDP: length mismatch! bytesToRead=")); + _debugPort->print(bytesToRead); _debugPort->print(F(" readLength=")); _debugPort->println(readLength); } } + // Check that readLength > 0 + if (readLength == 0) + { + if (_printDebug == true) + { + _debugPort->println(F("socketRead: zero length!")); + } + free(command); + free(response); + return SARA_R5_ERROR_ZERO_READ_LENGTH; + } + // Find the third double-quote - strBegin = strchr(response, '\"'); + strBegin = strchr(searchPtr, '\"'); strBegin = strchr(strBegin + 1, '\"'); strBegin = strchr(strBegin + 1, '\"'); @@ -2718,10 +2822,12 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, I } // Now copy the data into readDest - while (readIndex < readLength) + readIndexThisRead = 1; // Start after the quote + while (readIndexThisRead < (readLength + 1)) { - readDest[readIndex] = strBegin[1 + readIndex]; - readIndex++; + readDest[readIndexTotal] = strBegin[readIndexThisRead]; + readIndexTotal++; + readIndexThisRead++; } // If remoteIPaddress is not NULL, copy the remote IP address @@ -2743,12 +2849,28 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, I if (_printDebug == true) _debugPort->println(F("socketReadUDP: success")); - } + + // Update *bytesRead + if (bytesRead != NULL) + *bytesRead = readIndexTotal; + + // How many bytes are left to read? + // This would have been bytesLeftToRead -= bytesToRead + // Except the SARA can potentially return less data than requested... + // So we need to subtract readLength instead. + bytesLeftToRead -= readLength; + if (_printDebug == true) + { + if (bytesLeftToRead > 0) + _debugPort->print(F("socketReadUDP: multiple read. bytesLeftToRead: ")); + _debugPort->println(bytesLeftToRead); + } + } // /while (bytesLeftToRead > 0) free(command); free(response); - return err; + return SARA_R5_ERROR_SUCCESS; } SARA_R5_error_t SARA_R5::socketReadAvailableUDP(int socket, int *length) @@ -4312,7 +4434,7 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, if (_printDebug == true) _debugPort->println(F("init: Power cycling module.")); powerOff(); - delay(1000); + delay(SARA_R5_POWER_OFF_PULSE_PERIOD); powerOn(); delay(2000); if (at() != SARA_R5_ERROR_SUCCESS) @@ -4780,7 +4902,8 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) if (readDest == NULL) return SARA_R5_ERROR_OUT_OF_MEMORY; - err = socketRead(socket, length, readDest); + int bytesRead; + err = socketRead(socket, length, readDest, &bytesRead); if (err != SARA_R5_ERROR_SUCCESS) { free(readDest); @@ -4790,7 +4913,7 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) if (_socketReadCallback != NULL) { String dataAsString = ""; // Create an empty string - for (int i = 0; i < length; i++) // Copy the data from readDest into the String in a binary-compatible way + for (int i = 0; i < bytesRead; i++) // Copy the data from readDest into the String in a binary-compatible way dataAsString.concat(readDest[i]); _socketReadCallback(socket, dataAsString); } @@ -4799,7 +4922,7 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) { IPAddress dummyAddress = { 0, 0, 0, 0 }; int dummyPort = 0; - _socketReadCallbackPlus(socket, (const char *)readDest, length, dummyAddress, dummyPort); + _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, dummyAddress, dummyPort); } free(readDest); @@ -4828,7 +4951,8 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndicationUDP(int socket, int length) return SARA_R5_ERROR_OUT_OF_MEMORY; } - err = socketReadUDP(socket, length, readDest, &remoteAddress, &remotePort); + int bytesRead; + err = socketReadUDP(socket, length, readDest, &remoteAddress, &remotePort, &bytesRead); if (err != SARA_R5_ERROR_SUCCESS) { free(readDest); @@ -4838,14 +4962,14 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndicationUDP(int socket, int length) if (_socketReadCallback != NULL) { String dataAsString = ""; // Create an empty string - for (int i = 0; i < length; i++) // Copy the data from readDest into the String in a binary-compatible way + for (int i = 0; i < bytesRead; i++) // Copy the data from readDest into the String in a binary-compatible way dataAsString.concat(readDest[i]); _socketReadCallback(socket, dataAsString); } if (_socketReadCallbackPlus != NULL) { - _socketReadCallbackPlus(socket, (const char *)readDest, length, remoteAddress, remotePort); + _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, remoteAddress, remotePort); } free(readDest); diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index 559491a..6ea1d27 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -240,15 +240,16 @@ typedef enum typedef enum { - SARA_R5_ERROR_INVALID = -1, // -1 - SARA_R5_ERROR_SUCCESS = 0, // 0 - SARA_R5_ERROR_OUT_OF_MEMORY, // 1 - SARA_R5_ERROR_TIMEOUT, // 2 - SARA_R5_ERROR_UNEXPECTED_PARAM, // 3 - SARA_R5_ERROR_UNEXPECTED_RESPONSE, // 4 - SARA_R5_ERROR_NO_RESPONSE, // 5 - SARA_R5_ERROR_DEREGISTERED, // 6 - SARA_R5_ERROR_ERROR // 7 + SARA_R5_ERROR_INVALID = -1, // -1 + SARA_R5_ERROR_SUCCESS = 0, // 0 + SARA_R5_ERROR_OUT_OF_MEMORY, // 1 + SARA_R5_ERROR_TIMEOUT, // 2 + SARA_R5_ERROR_UNEXPECTED_PARAM, // 3 + SARA_R5_ERROR_UNEXPECTED_RESPONSE, // 4 + SARA_R5_ERROR_NO_RESPONSE, // 5 + SARA_R5_ERROR_DEREGISTERED, // 6 + SARA_R5_ERROR_ZERO_READ_LENGTH, // 7 + SARA_R5_ERROR_ERROR // 8 } SARA_R5_error_t; #define SARA_R5_SUCCESS SARA_R5_ERROR_SUCCESS @@ -687,14 +688,16 @@ class SARA_R5 : public Print // Read data from the specified socket // Call socketReadAvailable first to determine how much data is available - or use the callbacks (triggered by URC's) // Works for both TCP and UDP - but socketReadUDP is preferred for UDP as it records the remote IP Address and port - SARA_R5_error_t socketRead(int socket, int length, char *readDest); + // bytesRead - if provided - will be updated with the number of bytes actually read. This could be less than length! + SARA_R5_error_t socketRead(int socket, int length, char *readDest, int *bytesRead = NULL); // Return the number of bytes available (waiting to be read) on the chosen socket // Uses +USORD. Valid for both TCP and UDP sockets - but socketReadAvailableUDP is preferred for UDP SARA_R5_error_t socketReadAvailable(int socket, int *length); // Read data from the specified UDP port // Call socketReadAvailableUDP first to determine how much data is available - or use the callbacks (triggered by URC's) // The remote IP Address and port are returned via *remoteIPAddress and *remotePort (if not NULL) - SARA_R5_error_t socketReadUDP(int socket, int length, char *readDest, IPAddress *remoteIPAddress = NULL, int *remotePort = NULL); + // bytesRead - if provided - will be updated with the number of bytes actually read. This could be less than length! + SARA_R5_error_t socketReadUDP(int socket, int length, char *readDest, IPAddress *remoteIPAddress = NULL, int *remotePort = NULL, int *bytesRead = NULL); // Return the number of bytes available (waiting to be read) on the chosen UDP socket SARA_R5_error_t socketReadAvailableUDP(int socket, int *length); // Start listening for a connection on the specified port. The connection is reported via the socket listen callback @@ -872,6 +875,8 @@ class SARA_R5 : public Print // Send a command -- prepend AT if at is true void sendCommand(const char *command, bool at); + const int _saraR5maxSocketRead = 1024; // The limit on bytes that can be read in a single read + SARA_R5_error_t parseSocketReadIndication(int socket, int length); SARA_R5_error_t parseSocketReadIndicationUDP(int socket, int length); SARA_R5_error_t parseSocketListenIndication(int listeningSocket, IPAddress localIP, unsigned int listeningPort, int socket, IPAddress remoteIP, unsigned int port); From 39ed676d70c4035aa4737e2b09f3658f94db26a8 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 15 Jan 2022 16:30:49 +0000 Subject: [PATCH 04/21] Update SparkFun_u-blox_SARA-R5_Arduino_Library.cpp --- src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 74f3d2d..5284f62 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -2634,8 +2634,10 @@ SARA_R5_error_t SARA_R5::socketRead(int socket, int length, char *readDest, int if (_printDebug == true) { if (bytesLeftToRead > 0) + { _debugPort->print(F("socketRead: multiple read. bytesLeftToRead: ")); _debugPort->println(bytesLeftToRead); + } } } // /while (bytesLeftToRead > 0) @@ -2862,8 +2864,10 @@ SARA_R5_error_t SARA_R5::socketReadUDP(int socket, int length, char *readDest, if (_printDebug == true) { if (bytesLeftToRead > 0) + { _debugPort->print(F("socketReadUDP: multiple read. bytesLeftToRead: ")); _debugPort->println(bytesLeftToRead); + } } } // /while (bytesLeftToRead > 0) From b823e852c1dbe0619c0f5397a389e985cdfe1cc7 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 15 Jan 2022 16:32:10 +0000 Subject: [PATCH 05/21] Add missing closeConnection's --- ...SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino | 8 +++++++- .../SARA-R5_NTRIP_Client.ino | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino index 2265e29..4b218bc 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino @@ -171,7 +171,13 @@ void setup() IPAddress myAddress; mySARA.getNetworkAssignedIPAddress(0, &myAddress); Serial.print(F("\r\nMy IP Address is: ")); - Serial.println(myAddress.toString()); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino index 6f53ab8..5f28e34 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino @@ -118,6 +118,7 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) if (millis() > (startTime + 5000)) { Serial.println(F("beginClient: Caster timed out!")); + closeConnection(theSocket, connectionIsOpen); return (false); } delay(100); @@ -171,6 +172,7 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) { Serial.print(F("beginClient: Failed to connect to ")); Serial.println(casterHost); + closeConnection(theSocket, connectionIsOpen); return (false); } else From ee9b6c773b072b3399f5b8e2ffcc400b0737ef51 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 15 Jan 2022 17:35:26 +0000 Subject: [PATCH 06/21] Add base64 for Mbed platforms (Artemis & NRF52840) --- .../SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino | 2 +- .../SARA-R5_NTRIP_Client.ino | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino index 4b218bc..d99f383 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino @@ -39,7 +39,7 @@ // 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) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_APOLLO3) || defined(ARDUINO_ARDUINO_NANO33BLE) #include "base64.h" //Built-in ESP32 library #else #include //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino index 5f28e34..dc658ed 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino @@ -83,6 +83,10 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) String strEncodedCredentials = b.encode(userCredentials); char encodedCredentials[strEncodedCredentials.length() + 1]; strEncodedCredentials.toCharArray(encodedCredentials, sizeof(encodedCredentials)); //Convert String to char array +#elif defined(ARDUINO_ARCH_APOLLO3) || defined(ARDUINO_ARDUINO_NANO33BLE) + char encodedCredentials[sizeof(userCredentials) * 8]; + size_t olen; + mbedtls_base64_encode((unsigned char *)encodedCredentials, sizeof(userCredentials) * 8, &olen, (const unsigned char *)userCredentials, strlen(userCredentials)); #else //Encode with nfriendly library int encodedLen = base64_enc_len(strlen(userCredentials)); From 8591d75618f519bd244e3e869deb341c87369e74 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 09:34:44 +0000 Subject: [PATCH 07/21] Change debug print to write --- src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 5284f62..6da035c 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -4758,9 +4758,10 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( bool printedSomething = false; if (_printDebug == true) + { _debugPort->print(F("sendCommandWithResponse: Command: ")); - if (_printDebug == true) _debugPort->println(String(command)); + } sendCommand(command, at); //Sending command needs to dump data to backlog buffer as well. unsigned long timeIn = millis(); @@ -4773,9 +4774,11 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( if (_printDebug == true) { if (printedSomething == false) + { _debugPort->print(F("sendCommandWithResponse: Response: ")); - _debugPort->print(c); - printedSomething = true; + printedSomething = true; + } + _debugPort->write(c); } if (responseDest != NULL) { From 26e06a95441e5caae5611abc7b6a6fe1f6e6ea8a Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 10:11:42 +0000 Subject: [PATCH 08/21] Disable response debug prints - they were causing serial buffer overruns --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 6da035c..017fde4 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -4685,7 +4685,7 @@ SARA_R5_error_t SARA_R5::waitForResponse(const char *expectedResponse, const cha // { // if (printedSomething == false) // _debugPort->print(F("waitForResponse: ")); - // _debugPort->print(c); + // _debugPort->write(c); // printedSomething = true; // } if (c == expectedResponse[responseIndex]) @@ -4755,7 +4755,7 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( int index = 0; int destIndex = 0; unsigned int charsRead = 0; - bool printedSomething = false; + //bool printedSomething = false; if (_printDebug == true) { @@ -4771,15 +4771,15 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL { char c = readChar(); - if (_printDebug == true) - { - if (printedSomething == false) - { - _debugPort->print(F("sendCommandWithResponse: Response: ")); - printedSomething = true; - } - _debugPort->write(c); - } + // if (_printDebug == true) + // { + // if (printedSomething == false) + // { + // _debugPort->print(F("sendCommandWithResponse: Response: ")); + // printedSomething = true; + // } + // _debugPort->write(c); + // } if (responseDest != NULL) { if (destIndex < destSize) // Only add this char to response if there is room for it @@ -4789,11 +4789,11 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( { if (_printDebug == true) { - if (printedSomething) - _debugPort->println(); + // if (printedSomething) + // _debugPort->println(); _debugPort->print(F("sendCommandWithResponse: Panic! responseDest is full!")); - if (printedSomething) - _debugPort->print(F("sendCommandWithResponse: Ignored response: ")); + // if (printedSomething) + // _debugPort->print(F("sendCommandWithResponse: Ignored response: ")); } } } @@ -4825,9 +4825,9 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( } } - if (_printDebug == true) - if (printedSomething) - _debugPort->println(); + // if (_printDebug == true) + // if (printedSomething) + // _debugPort->println(); pruneBacklog(); // Prune any incoming non-actionable URC's and responses/errors from the backlog From 1503630daf6f0934e09e12c3f2c0a60e2412e5fa Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 10:14:04 +0000 Subject: [PATCH 09/21] Avoid compiler warnings --- .../SARA-R5_NTRIP_Client.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino index dc658ed..067583d 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino @@ -98,8 +98,8 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) } // Add the encoded credentials to the server request - strncat(serverRequest, credentials, SERVER_BUFFER_SIZE); - strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE); + strncat(serverRequest, credentials, SERVER_BUFFER_SIZE - 1); + strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE - 1); Serial.print(F("beginClient: serverRequest size: ")); Serial.print(strlen(serverRequest)); @@ -134,8 +134,8 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //Check reply int connectionResult = 0; - char response[512]; - memset(response, 0, 512); + char response[512 * 4]; + memset(response, 0, 512 * 4); size_t responseSpot = 0; while ((availableLength > 0) && (connectionResult == 0)) // Read bytes from the caster and store them { From 2fd66ae5db5eec9f99f1b4b44ea8c2e237603935 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 13:15:43 +0000 Subject: [PATCH 10/21] Add getFileContents(String filename, char *contents) --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 131 +++++++++++++++++- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 3 +- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 017fde4..3723a01 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -4165,6 +4165,7 @@ SARA_R5_error_t SARA_R5::gpsAidingServerConf(const char *primaryServer, const ch return err; } +// OK for text files. But will fail with binary files (containing \0) on some platforms. SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) { SARA_R5_error_t err; @@ -4251,6 +4252,9 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) while (bytesRead < readFileSize) { searchPtr++; // Increment searchPtr then copy file char into contents + // Important Note: some implementations of concat, like the one on ESP32, are binary-compatible. + // But some, like SAMD, are not. They use strlen or strcpy internally - which don't like \0's. + // The only true binary-compatible solution is to use getFileContents(String filename, char *contents)... contents->concat(*(searchPtr)); // Append file char to contents bytesRead++; } @@ -4283,6 +4287,124 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) return err; } +// OK for binary files. Make sure contents can hold the entire file. Get the size first with getFileSize. +SARA_R5_error_t SARA_R5::getFileContents(String filename, char *contents) +{ + SARA_R5_error_t err; + char *command; + char *response; + + // Start by getting the file size so we know in advance how much data to expect + int fileSize = 0; + err = getFileSize(filename, &fileSize); + if (err != SARA_R5_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: getFileSize returned err ")); + _debugPort->println(err); + } + return err; + } + + command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_READ_FILE) + filename.length() + 8); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\"", SARA_R5_FILE_SYSTEM_READ_FILE, filename.c_str()); + + response = sara_r5_calloc_char(fileSize + minimumResponseAllocation); + if (response == NULL) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: response alloc failed: ")); + _debugPort->println(fileSize + minimumResponseAllocation); + } + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // A large file will completely fill the backlog buffer - but it will be pruned afterwards + // Note to self: if the file contents contain "OK\r\n" sendCommandWithResponse will return true too early... + // To try and avoid this, look for \"\r\nOK\r\n + const char fileReadTerm[] = "\"\r\nOK\r\n"; + err = sendCommandWithResponse(command, fileReadTerm, + response, (5 * SARA_R5_STANDARD_RESPONSE_TIMEOUT), + (fileSize + minimumResponseAllocation)); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: sendCommandWithResponse returned err ")); + _debugPort->println(err); + } + free(command); + free(response); + return err; + } + + // Response format: \r\n+URDFILE: "filename",36,"these bytes are the data of the file"\r\n\r\nOK\r\n + int scanned = 0; + int readFileSize = 0; + char *searchPtr = strstr(response, "+URDFILE: "); + if (searchPtr != NULL) + { + searchPtr = strchr(searchPtr, '\"'); // Find the first quote + searchPtr = strchr(++searchPtr, '\"'); // Find the second quote + + scanned = sscanf(searchPtr, "\",%d,", &readFileSize); // Get the file size (again) + if (scanned == 1) + { + searchPtr = strchr(++searchPtr, '\"'); // Find the third quote + + if (searchPtr == NULL) + { + if (_printDebug == true) + { + _debugPort->println(F("getFileContents: third quote not found!")); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + int bytesRead = 0; + + while (bytesRead < readFileSize) + { + searchPtr++; // Increment searchPtr then copy file char into contents + contents[bytesRead] = *searchPtr; // Append file char to contents + bytesRead++; + } + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: total bytes read: ")); + _debugPort->println(bytesRead); + } + err = SARA_R5_ERROR_SUCCESS; + } + else + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: sscanf failed! scanned is ")); + _debugPort->println(scanned); + } + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + } + else + { + if (_printDebug == true) + _debugPort->println(F("getFileContents: strstr failed!")); + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} SARA_R5_error_t SARA_R5::getFileSize(String filename, int *size) { SARA_R5_error_t err; @@ -4920,7 +5042,11 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) if (_socketReadCallback != NULL) { String dataAsString = ""; // Create an empty string - for (int i = 0; i < bytesRead; i++) // Copy the data from readDest into the String in a binary-compatible way + // Copy the data from readDest into the String in a binary-compatible way + // Important Note: some implementations of concat, like the one on ESP32, are binary-compatible. + // But some, like SAMD, are not. They use strlen or strcpy internally - which don't like \0's. + // The only true binary-compatible solution is to use socketReadCallbackPlus... + for (int i = 0; i < bytesRead; i++) dataAsString.concat(readDest[i]); _socketReadCallback(socket, dataAsString); } @@ -4969,6 +5095,9 @@ SARA_R5_error_t SARA_R5::parseSocketReadIndicationUDP(int socket, int length) if (_socketReadCallback != NULL) { String dataAsString = ""; // Create an empty string + // Important Note: some implementations of concat, like the one on ESP32, are binary-compatible. + // But some, like SAMD, are not. They use strlen or strcpy internally - which don't like \0's. + // The only true binary-compatible solution is to use socketReadCallbackPlus... for (int i = 0; i < bytesRead; i++) // Copy the data from readDest into the String in a binary-compatible way dataAsString.concat(readDest[i]); _socketReadCallback(socket, dataAsString); diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index 6ea1d27..2bfeed9 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -796,7 +796,8 @@ class SARA_R5 : public Print // File system // TO DO: add full support for file tags. Default tag to USER - SARA_R5_error_t getFileContents(String filename, String *contents); + SARA_R5_error_t getFileContents(String filename, String *contents); // OK for text files. But will fail with binary files (containing \0) on some platforms. + SARA_R5_error_t getFileContents(String filename, char *contents); // OK for binary files. Make sure contents can hold the entire file. Get the size first with getFileSize. SARA_R5_error_t getFileSize(String filename, int *size); SARA_R5_error_t deleteFile(String filename); From 9d69e1e2f89a04176a627799e8a4dbaaa9138ae4 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 14:27:03 +0000 Subject: [PATCH 11/21] Update SparkFun_u-blox_SARA-R5_Arduino_Library.cpp --- src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 3723a01..79526d5 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -4405,6 +4405,7 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, char *contents) free(response); return err; } + SARA_R5_error_t SARA_R5::getFileSize(String filename, int *size) { SARA_R5_error_t err; From 2f9d63749e2688fec87a605e3c1a0c5908e40a1a Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 14:46:59 +0000 Subject: [PATCH 12/21] Update examples 12 and 14 to use the updated getFileContents --- .../SARA-R5_AssistNow_Online.ino | 59 ++++++++++++++++ .../SARA-R5_Example12_AssistNowOnline.ino | 19 +++-- ...line.ino => SARA-R5_AssistNow_Offline.ino} | 69 ++++++++++++++++--- .../SARA-R5_Example14_AssistNowOffline.ino | 31 ++++++--- 4 files changed, 154 insertions(+), 24 deletions(-) rename examples/SARA-R5_Example14_AssistNowOffline/{SARA-R5_AssistNow_Online.ino => SARA-R5_AssistNow_Offline.ino} (79%) diff --git a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino index 5606dfe..af7a2f5 100644 --- a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino +++ b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino @@ -186,3 +186,62 @@ void prettyPrintString(String theString) // Pretty-print a String in HEX and ASC Serial.println(); } + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void prettyPrintChars(char *theData, int theLength) // Pretty-print char data in HEX and ASCII format +{ + Serial.println(); + Serial.print(F("String length is ")); + Serial.print(theLength); + Serial.print(F(" (0x")); + Serial.print(theLength, HEX); + Serial.println(F(")")); + Serial.println(); + + for (int i = 0; i < theLength; i += 16) + { + if (i < 10000) Serial.print(F("0")); + if (i < 1000) Serial.print(F("0")); + if (i < 100) Serial.print(F("0")); + if (i < 10) Serial.print(F("0")); + Serial.print(i); + + Serial.print(F(" 0x")); + + if (i < 0x1000) Serial.print(F("0")); + if (i < 0x100) Serial.print(F("0")); + if (i < 0x10) Serial.print(F("0")); + Serial.print(i, HEX); + + Serial.print(F(" ")); + + int j; + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if (theData[i + j] < 0x10) Serial.print(F("0")); + Serial.print(theData[i + j], HEX); + Serial.print(F(" ")); + } + + if (((i + j) == theLength) && (j < 16)) + { + for (int k = 0; k < (16 - (theLength % 16)); k++) + { + Serial.print(F(" ")); + } + } + + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if ((theData[i + j] >= 0x20) && (theData[i + j] <= 0x7E)) + Serial.write(theData[i + j]); + else + Serial.print(F(".")); + } + + Serial.println(); + } + + Serial.println(); +} diff --git a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino index abc7e2d..06db4ba 100644 --- a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino +++ b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino @@ -173,16 +173,27 @@ void setup() // Read the AssistNow data from file and push it to the module + int fileSize; + if (mySARA.getFileSize(theFilename, &fileSize) != SARA_R5_SUCCESS) + { + Serial.print(F("getFileSize failed! Freezing...")); + while (1) + ; // Do nothing more + } + + Serial.print(F("AssistNow file size is: ")); + Serial.println(fileSize); + // Read the data from file - String theAssistData = ""; - if (mySARA.getFileContents(theFilename, &theAssistData) != SARA_R5_SUCCESS) + char theAssistData[fileSize]; + if (mySARA.getFileContents(theFilename, (char *)theAssistData) != SARA_R5_SUCCESS) { Serial.println(F("getFileContents failed! Freezing...")); while (1) ; // Do nothing more } - //prettyPrintString(theAssistData); // Uncomment this line to see the whole file contents (including the HTTP header) + //prettyPrintChars(theAssistData, fileSize); // Uncomment this line to see the whole file contents (including the HTTP header) // Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data myGNSS.setAckAiding(1); @@ -198,7 +209,7 @@ void setup() // We have called setAckAiding(1) to instruct the module to return MGA-ACK messages. // So, set the pushAssistNowData mgaAck parameter to SFE_UBLOX_MGA_ASSIST_ACK_YES. // Wait for up to 100ms for each ACK to arrive! 100ms is a bit excessive... 7ms is nearer the mark. - myGNSS.pushAssistNowData(theAssistData, theAssistData.length(), SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); + myGNSS.pushAssistNowData((const uint8_t *)theAssistData, fileSize, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus myGNSS.setI2CpollingWait(125); diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Offline.ino similarity index 79% rename from examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino rename to examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Offline.ino index 48a8aa6..d240ec0 100644 --- a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Offline.ino @@ -120,16 +120,6 @@ bool getAssistNowOfflineData(String theFilename) return false; } - int fileSize; - if (mySARA.getFileSize(theFilename, &fileSize) != SARA_R5_SUCCESS) - { - Serial.print(F("getAssistNowOfflineData: No file written?!")); - return false; - } - - Serial.print(F("File size is: ")); - Serial.println(fileSize); - return true; } @@ -193,3 +183,62 @@ void prettyPrintString(String theString) // Pretty-print a String in HEX and ASC Serial.println(); } + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void prettyPrintChars(char *theData, int theLength) // Pretty-print char data in HEX and ASCII format +{ + Serial.println(); + Serial.print(F("String length is ")); + Serial.print(theLength); + Serial.print(F(" (0x")); + Serial.print(theLength, HEX); + Serial.println(F(")")); + Serial.println(); + + for (int i = 0; i < theLength; i += 16) + { + if (i < 10000) Serial.print(F("0")); + if (i < 1000) Serial.print(F("0")); + if (i < 100) Serial.print(F("0")); + if (i < 10) Serial.print(F("0")); + Serial.print(i); + + Serial.print(F(" 0x")); + + if (i < 0x1000) Serial.print(F("0")); + if (i < 0x100) Serial.print(F("0")); + if (i < 0x10) Serial.print(F("0")); + Serial.print(i, HEX); + + Serial.print(F(" ")); + + int j; + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if (theData[i + j] < 0x10) Serial.print(F("0")); + Serial.print(theData[i + j], HEX); + Serial.print(F(" ")); + } + + if (((i + j) == theLength) && (j < 16)) + { + for (int k = 0; k < (16 - (theLength % 16)); k++) + { + Serial.print(F(" ")); + } + } + + for (j = 0; ((i + j) < theLength) && (j < 16); j++) + { + if ((theData[i + j] >= 0x20) && (theData[i + j] <= 0x7E)) + Serial.write(theData[i + j]); + else + Serial.print(F(".")); + } + + Serial.println(); + } + + Serial.println(); +} diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino index 4eb66af..a1a45ec 100644 --- a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino @@ -210,16 +210,27 @@ void setup() // Read the AssistNow data from file + int fileSize; + if (mySARA.getFileSize(theFilename, &fileSize) != SARA_R5_SUCCESS) + { + Serial.print(F("getFileSize failed! Freezing...")); + while (1) + ; // Do nothing more + } + + Serial.print(F("File size is: ")); + Serial.println(fileSize); + // Read the data from file - String theAssistData = ""; - if (mySARA.getFileContents(theFilename, &theAssistData) != SARA_R5_SUCCESS) + char theAssistData[fileSize]; + if (mySARA.getFileContents(theFilename, (char *)theAssistData) != SARA_R5_SUCCESS) { Serial.println(F("getFileContents failed! Freezing...")); while (1) ; // Do nothing more } - //prettyPrintString(theAssistData); // Uncomment this line to see the whole file contents (including the HTTP header) + //prettyPrintChars(theAssistData, fileSize); // Uncomment this line to see the whole file contents (including the HTTP header) //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -254,13 +265,13 @@ void setup() // Find where the AssistNow data for today starts and ends size_t todayStart = 0; // Default to sending all the data - size_t tomorrowStart = (size_t)theAssistData.length(); + size_t tomorrowStart = (size_t)fileSize; - if (theAssistData.length() > 0) + if (fileSize > 0) { // Find the start of today's data - todayStart = myGNSS.findMGAANOForDate(theAssistData, (size_t)theAssistData.length(), year + 2000, month, day); - if (todayStart < (size_t)theAssistData.length()) + todayStart = myGNSS.findMGAANOForDate((const uint8_t *)theAssistData, (size_t)fileSize, year + 2000, month, day); + if (todayStart < (size_t)fileSize) { Serial.print(F("Found the data for today starting at location ")); Serial.println(todayStart); @@ -271,8 +282,8 @@ void setup() } // Find the start of tomorrow's data - tomorrowStart = myGNSS.findMGAANOForDate(theAssistData, (size_t)theAssistData.length(), year + 2000, month, day, 1); - if (tomorrowStart < (size_t)theAssistData.length()) + tomorrowStart = myGNSS.findMGAANOForDate((const uint8_t *)theAssistData, (size_t)fileSize, year + 2000, month, day, 1); + if (tomorrowStart < (size_t)fileSize) { Serial.print(F("Found the data for tomorrow starting at location ")); Serial.println(tomorrowStart); @@ -309,7 +320,7 @@ void setup() // // pushAssistNowData is clever and will only push valid UBX-format data. // It will ignore the HTTP header at the start of the AssistNow file. - myGNSS.pushAssistNowData(todayStart, true, theAssistData, tomorrowStart - todayStart, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); + myGNSS.pushAssistNowData(todayStart, true, (const uint8_t *)theAssistData, tomorrowStart - todayStart, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus myGNSS.setI2CpollingWait(125); From 21cda947a3261dd482415981955344a5aa5cd50f Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 14:54:40 +0000 Subject: [PATCH 13/21] Update example 15 to use setSocketReadCallbackPlus for binary compatibility --- .../SARA-R5_Callbacks.ino | 8 ++++---- ...SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino index bc4a1f5..366f4b3 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Callbacks.ino @@ -1,18 +1,18 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // processSocketData is provided to the SARA-R5 library via a -// callback setter -- setSocketReadCallback. (See setup()) -void processSocketData(int socket, String theData) +// callback setter -- setSocketReadCallbackPlus. (See setup()) +void processSocketData(int socket, const char *theData, int dataLength, IPAddress remoteAddress, int remotePort) { Serial.print(F("processSocketData: Data received on socket ")); Serial.print(socket); Serial.print(F(". Length is ")); - Serial.print(theData.length()); + Serial.print(dataLength); if (connectionOpen) { Serial.println(F(". Pushing it to the GNSS...")); - myGNSS.pushRawData((uint8_t *)theData.c_str(), theData.length()); + myGNSS.pushRawData((uint8_t *)theData, (size_t)dataLength); lastReceivedRTCM_ms = millis(); // Update lastReceivedRTCM_ms } diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino index d99f383..09d461b 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks.ino @@ -183,7 +183,7 @@ void setup() // Set a callback to process the socket data // This will push the RTCM data to the GNSS - mySARA.setSocketReadCallback(&processSocketData); + mySARA.setSocketReadCallbackPlus(&processSocketData); //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= From a03c336edb292b2f8863167a05b2e629ce1df383 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 17:54:36 +0000 Subject: [PATCH 14/21] Remove String.toString(). Use setSocketReadCallbackPlus in the binary TCP example --- .../SARA-R5_Example10_SocketPingPong.ino | 26 +++++++-- ...-R5_Example10_SocketPingPong_BinaryTCP.ino | 58 +++++++++++-------- ...5_Example10_SocketPingPong_MultipleTCP.ino | 24 +++++++- ...5_Example10_SocketPingPong_MultipleUDP.ino | 46 ++++++++++----- 4 files changed, 108 insertions(+), 46 deletions(-) diff --git a/examples/SARA-R5_Example10_SocketPingPong/SARA-R5_Example10_SocketPingPong.ino b/examples/SARA-R5_Example10_SocketPingPong/SARA-R5_Example10_SocketPingPong.ino index ec4ffd7..12f369c 100644 --- a/examples/SARA-R5_Example10_SocketPingPong/SARA-R5_Example10_SocketPingPong.ino +++ b/examples/SARA-R5_Example10_SocketPingPong/SARA-R5_Example10_SocketPingPong.ino @@ -294,7 +294,13 @@ void setup() IPAddress myAddress; mySARA.getNetworkAssignedIPAddress(0, &myAddress); Serial.print(F("\r\nMy IP Address is: ")); - Serial.println(myAddress.toString()); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -392,8 +398,14 @@ void setup() } } - Serial.print(F("Remote address is ")); - Serial.println(theAddress.toString()); + Serial.print(F("Remote address is: ")); + Serial.print(theAddress[0]); + Serial.print(F(".")); + Serial.print(theAddress[1]); + Serial.print(F(".")); + Serial.print(theAddress[2]); + Serial.print(F(".")); + Serial.println(theAddress[3]); // Open the socket socketNum = mySARA.socketOpen(SARA_R5_TCP); @@ -525,7 +537,13 @@ void printSocketParameters(int socket) IPAddress remoteAddress; int remotePort; mySARA.querySocketRemoteIPAddress(socket, &remoteAddress, &remotePort); - Serial.println(remoteAddress.toString()); + Serial.print(remoteAddress[0]); + Serial.print(F(".")); + Serial.print(remoteAddress[1]); + Serial.print(F(".")); + Serial.print(remoteAddress[2]); + Serial.print(F(".")); + Serial.println(remoteAddress[3]); Serial.print(F("Socket status (TCP sockets only): ")); SARA_R5_tcp_socket_status_t socketStatus; diff --git a/examples/SARA-R5_Example10_SocketPingPong_BinaryTCP/SARA-R5_Example10_SocketPingPong_BinaryTCP.ino b/examples/SARA-R5_Example10_SocketPingPong_BinaryTCP/SARA-R5_Example10_SocketPingPong_BinaryTCP.ino index 9f15b25..c528f04 100644 --- a/examples/SARA-R5_Example10_SocketPingPong_BinaryTCP/SARA-R5_Example10_SocketPingPong_BinaryTCP.ino +++ b/examples/SARA-R5_Example10_SocketPingPong_BinaryTCP/SARA-R5_Example10_SocketPingPong_BinaryTCP.ino @@ -143,14 +143,14 @@ void processSocketListen(int listeningSocket, IPAddress localIP, unsigned int li // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // processSocketData is provided to the SARA-R5 library via a -// callback setter -- setSocketReadCallback. (See setup()) -void processSocketData(int socket, String theData) +// callback setter -- setSocketReadCallbackPlus. (See setup()) +void processSocketData(int socket, const char *theData, int dataLength, IPAddress remoteAddress, int remotePort) { Serial.println(); Serial.print(F("Data received on socket ")); Serial.print(socket); Serial.print(F(" :")); - for (int i = 0; i < theData.length(); i++) + for (int i = 0; i < dataLength; i++) { Serial.print(F(" 0x")); if (theData[i] < 16) @@ -168,18 +168,10 @@ void processSocketData(int socket, String theData) if ((theData[0] == 0x04) && (theData[1] == 0x05) && (theData[2] == 0x06) && (theData[3] == 0x07)) // Look for the "Pong" { - // Use the const char * version - //const char ping[] = { 0x00, 0x01, 0x02, 0x03 }; - //mySARA.socketWrite(socket, ping, 4); // Send the "Ping" - - // Or use the String version. Both are OK for binary data. - String ping = ""; - ping.concat('\0'); // Construct the ping in a binary-friendly way - ping.concat('\1'); - ping.concat('\2'); - ping.concat('\3'); - mySARA.socketWrite(socket, ping); // Send the "Ping" - + // Use the const char * version for binary data + const char ping[] = { 0x00, 0x01, 0x02, 0x03 }; + mySARA.socketWrite(socket, ping, 4); // Send the "Ping" + pingCount++; } @@ -215,13 +207,13 @@ void processPSDAction(int result, IPAddress ip) if (result == 0) Serial.print(F(" (success)")); Serial.print(F(" IP Address: \"")); - Serial.print(String(ip[0])); + Serial.print(ip[0]); Serial.print(F(".")); - Serial.print(String(ip[1])); + Serial.print(ip[1]); Serial.print(F(".")); - Serial.print(String(ip[2])); + Serial.print(ip[2]); Serial.print(F(".")); - Serial.print(String(ip[3])); + Serial.print(ip[3]); Serial.println(F("\"")); } @@ -311,7 +303,13 @@ void setup() IPAddress myAddress; mySARA.getNetworkAssignedIPAddress(0, &myAddress); Serial.print(F("\r\nMy IP Address is: ")); - Serial.println(myAddress.toString()); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -321,7 +319,7 @@ void setup() // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Set a callback to process the socket data - mySARA.setSocketReadCallback(&processSocketData); + mySARA.setSocketReadCallbackPlus(&processSocketData); // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -409,8 +407,14 @@ void setup() } } - Serial.print(F("Remote address is ")); - Serial.println(theAddress.toString()); + Serial.print(F("Remote address is: ")); + Serial.print(theAddress[0]); + Serial.print(F(".")); + Serial.print(theAddress[1]); + Serial.print(F(".")); + Serial.print(theAddress[2]); + Serial.print(F(".")); + Serial.println(theAddress[3]); // Open the socket socketNum = mySARA.socketOpen(SARA_R5_TCP); @@ -541,7 +545,13 @@ void printSocketParameters(int socket) IPAddress remoteAddress; int remotePort; mySARA.querySocketRemoteIPAddress(socket, &remoteAddress, &remotePort); - Serial.println(remoteAddress.toString()); + Serial.print(remoteAddress[0]); + Serial.print(F(".")); + Serial.print(remoteAddress[1]); + Serial.print(F(".")); + Serial.print(remoteAddress[2]); + Serial.print(F(".")); + Serial.println(remoteAddress[3]); Serial.print(F("Socket status (TCP sockets only): ")); SARA_R5_tcp_socket_status_t socketStatus; diff --git a/examples/SARA-R5_Example10_SocketPingPong_MultipleTCP/SARA-R5_Example10_SocketPingPong_MultipleTCP.ino b/examples/SARA-R5_Example10_SocketPingPong_MultipleTCP/SARA-R5_Example10_SocketPingPong_MultipleTCP.ino index db49fa0..2ed1bbe 100644 --- a/examples/SARA-R5_Example10_SocketPingPong_MultipleTCP/SARA-R5_Example10_SocketPingPong_MultipleTCP.ino +++ b/examples/SARA-R5_Example10_SocketPingPong_MultipleTCP/SARA-R5_Example10_SocketPingPong_MultipleTCP.ino @@ -320,7 +320,13 @@ void setup() IPAddress myAddress; mySARA.getNetworkAssignedIPAddress(0, &myAddress); Serial.print(F("\r\nMy IP Address is: ")); - Serial.println(myAddress.toString()); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -419,7 +425,13 @@ void setup() } Serial.print(F("Remote address is ")); - Serial.println(theAddress.toString()); + Serial.print(theAddress[0]); + Serial.print(F(".")); + Serial.print(theAddress[1]); + Serial.print(F(".")); + Serial.print(theAddress[2]); + Serial.print(F(".")); + Serial.println(theAddress[3]); // Open the sockets for (int i = 0; i < numConnections; i++) @@ -569,7 +581,13 @@ void printSocketParameters(int socket) IPAddress remoteAddress; int remotePort; mySARA.querySocketRemoteIPAddress(socket, &remoteAddress, &remotePort); - Serial.println(remoteAddress.toString()); + Serial.print(remoteAddress[0]); + Serial.print(F(".")); + Serial.print(remoteAddress[1]); + Serial.print(F(".")); + Serial.print(remoteAddress[2]); + Serial.print(F(".")); + Serial.println(remoteAddress[3]); Serial.print(F("Socket status (TCP sockets only): ")); SARA_R5_tcp_socket_status_t socketStatus; diff --git a/examples/SARA-R5_Example10_SocketPingPong_MultipleUDP/SARA-R5_Example10_SocketPingPong_MultipleUDP.ino b/examples/SARA-R5_Example10_SocketPingPong_MultipleUDP/SARA-R5_Example10_SocketPingPong_MultipleUDP.ino index d85541a..26965b0 100644 --- a/examples/SARA-R5_Example10_SocketPingPong_MultipleUDP/SARA-R5_Example10_SocketPingPong_MultipleUDP.ino +++ b/examples/SARA-R5_Example10_SocketPingPong_MultipleUDP/SARA-R5_Example10_SocketPingPong_MultipleUDP.ino @@ -121,7 +121,13 @@ void processSocketData(int socket, const char *theData, int length, IPAddress re Serial.print(F("Data received on socket ")); Serial.print(socket); Serial.print(F(" from IP ")); - Serial.print(remoteAddress.toString()); + Serial.print(remoteAddress[0]); + Serial.print(F(".")); + Serial.print(remoteAddress[1]); + Serial.print(F(".")); + Serial.print(remoteAddress[2]); + Serial.print(F(".")); + Serial.print(remoteAddress[3]); Serial.print(F(" using port ")); Serial.print(remotePort); Serial.print(F(" :")); @@ -148,18 +154,10 @@ void processSocketData(int socket, const char *theData, int length, IPAddress re { if (pingCount[connection] < pingPongLimit) { - // Use the const char * version - //const char ping[] = { 0x00, 0x01, 0x02, 0x03 }; - //mySARA.socketWriteUDP(socket, remoteAddress, remotePort, ping, 4); // Send the "Ping" + // Use the const char * version for binary data + const char ping[] = { 0x00, 0x01, 0x02, 0x03 }; + mySARA.socketWriteUDP(socket, remoteAddress, remotePort, ping, 4); // Send the "Ping" - // Or use the String version. Both are OK for binary data. - String ping = ""; - ping.concat('\0'); // Construct the ping in a binary-friendly way - ping.concat('\1'); - ping.concat('\2'); - ping.concat('\3'); - mySARA.socketWriteUDP(socket, remoteAddress.toString(), remotePort, ping); // Send the "Ping" - pingCount[connection]++; } } @@ -196,7 +194,13 @@ void processPSDAction(int result, IPAddress ip) if (result == 0) Serial.print(F(" (success)")); Serial.print(F(" IP Address: \"")); - Serial.print(ip.toString()); + Serial.print(ip[0]); + Serial.print(F(".")); + Serial.print(ip[1]); + Serial.print(F(".")); + Serial.print(ip[2]); + Serial.print(F(".")); + Serial.print(ip[3]); Serial.println(F("\"")); } @@ -292,7 +296,13 @@ void setup() IPAddress myAddress; mySARA.getNetworkAssignedIPAddress(0, &myAddress); Serial.print(F("\r\nMy IP Address is: ")); - Serial.println(myAddress.toString()); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -344,7 +354,13 @@ void setup() } Serial.print(F("Remote address is ")); - Serial.println(theAddress.toString()); + Serial.print(theAddress[0]); + Serial.print(F(".")); + Serial.print(theAddress[1]); + Serial.print(F(".")); + Serial.print(theAddress[2]); + Serial.print(F(".")); + Serial.println(theAddress[3]); // Open the sockets for (int i = 0; i < numConnections; i++) From 004eb4a5b91bbf0ab01cba02264038e7cc1d3ab7 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 21:51:40 +0000 Subject: [PATCH 15/21] Allocate memory for _saraRXBuffer, _pruneBuffer & _saraResponseBacklog in begin --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 58 +++++++++++++++++-- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 6 +- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 79526d5..8f3be3b 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -44,16 +44,39 @@ SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitDepth) _bufferedPollReentrant = false; _pollReentrant = false; _currentInitDepth = 0; - - memset(_saraRXBuffer, 0, _RXBuffSize); - memset(_pruneBuffer, 0, _RXBuffSize); - memset(_saraResponseBacklog, 0, _RXBuffSize); _saraResponseBacklogLength = 0; } #ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED bool SARA_R5::begin(SoftwareSerial &softSerial, unsigned long baud) { + _saraRXBuffer = new char[_RXBuffSize]; + if (_saraRXBuffer == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); + return false; + } + memset(_saraRXBuffer, 0, _RXBuffSize); + + _pruneBuffer = new char[_RXBuffSize]; + if (_pruneBuffer == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); + return false; + } + memset(_pruneBuffer, 0, _RXBuffSize); + + _saraResponseBacklog = new char[_RXBuffSize]; + if (_saraResponseBacklog == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraResponseBacklog!")); + return false; + } + memset(_saraResponseBacklog, 0, _RXBuffSize); + SARA_R5_error_t err; _softSerial = &softSerial; @@ -69,6 +92,33 @@ bool SARA_R5::begin(SoftwareSerial &softSerial, unsigned long baud) bool SARA_R5::begin(HardwareSerial &hardSerial, unsigned long baud) { + _saraRXBuffer = new char[_RXBuffSize]; + if (_saraRXBuffer == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); + return false; + } + memset(_saraRXBuffer, 0, _RXBuffSize); + + _pruneBuffer = new char[_RXBuffSize]; + if (_pruneBuffer == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); + return false; + } + memset(_pruneBuffer, 0, _RXBuffSize); + + _saraResponseBacklog = new char[_RXBuffSize]; + if (_saraResponseBacklog == NULL) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraResponseBacklog!")); + return false; + } + memset(_saraResponseBacklog, 0, _RXBuffSize); + SARA_R5_error_t err; _hardSerial = &hardSerial; diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index 2bfeed9..66b4d0a 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -832,9 +832,9 @@ class SARA_R5 : public Print #define _RXBuffSize 2056 const unsigned long _rxWindowMillis = 2; // 1ms is not quite long enough for a single char at 9600 baud. millis roll over much less often than micros. - char _saraRXBuffer[_RXBuffSize]; - char _pruneBuffer[_RXBuffSize]; - char _saraResponseBacklog[_RXBuffSize]; + char *_saraRXBuffer; // Allocated in SARA_R5::begin + char *_pruneBuffer; + char *_saraResponseBacklog; int _saraResponseBacklogLength = 0; // The backlog could contain binary data so we can't use strlen to find its length void (*_socketListenCallback)(int, IPAddress, unsigned int, int, IPAddress, unsigned int); From e6fc72bdb214d2b812ee1a1b9a44a8144029e469 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 22:04:21 +0000 Subject: [PATCH 16/21] Decrease socket write @ timeout to 5 seconds --- src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 8f3be3b..819c8af 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -2426,7 +2426,7 @@ SARA_R5_error_t SARA_R5::socketWrite(int socket, const char *str, int len) sprintf(command, "%s=%d,%d", SARA_R5_WRITE_SOCKET, socket, dataLen); err = sendCommandWithResponse(command, "@", response, - SARA_R5_2_MIN_TIMEOUT); + SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); if (err == SARA_R5_ERROR_SUCCESS) { @@ -2498,7 +2498,7 @@ SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, const char *address, int por sprintf(command, "%s=%d,\"%s\",%d,%d", SARA_R5_WRITE_UDP_SOCKET, socket, address, port, dataLen); - err = sendCommandWithResponse(command, "@", response, SARA_R5_IP_CONNECT_TIMEOUT); + err = sendCommandWithResponse(command, "@", response, SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); if (err == SARA_R5_ERROR_SUCCESS) { From a2134bc38c821b7a6cc61eea6a78a8e529f54e17 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 17 Jan 2022 22:38:01 +0000 Subject: [PATCH 17/21] Store the NTRIP credentials etc. in allocated memory - to avoid stack problems on Mbed platforms --- .../SARA-R5_NTRIP_Client.ino | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino index 067583d..70becfc 100644 --- a/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino +++ b/examples/SARA-R5_Example15_GNSS_NTRIP_Caster_With_Callbacks/SARA-R5_NTRIP_Client.ino @@ -14,11 +14,11 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) Serial.print(F("beginClient: Socket is already open")); if (*connectionIsOpen) { - Serial.print(F(" and the connection is open!")); + Serial.println(F(" and the connection is open!")); } else { - Serial.print(F("!")); + Serial.println(F("!")); } return (false); } @@ -56,23 +56,28 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) // Set up the server request (GET) const int SERVER_BUFFER_SIZE = 512; - char serverRequest[SERVER_BUFFER_SIZE]; + char *serverRequest = new char[SERVER_BUFFER_SIZE]; + memset(serverRequest, 0, 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); // Set up the credentials - char credentials[512]; + const int CREDENTIALS_BUFFER_SIZE = 512; + char *credentials = new char[CREDENTIALS_BUFFER_SIZE]; + memset(credentials, 0, CREDENTIALS_BUFFER_SIZE); if (strlen(casterUser) == 0) { - strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", sizeof(credentials)); + strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", CREDENTIALS_BUFFER_SIZE); } 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); + int userCredentialsSize = sizeof(casterUser) + sizeof(casterUserPW) + 1; //The ':' takes up a spot + char *userCredentials = new char[userCredentialsSize]; + memset(userCredentials, 0, userCredentialsSize); + snprintf(userCredentials, userCredentialsSize, "%s:%s", casterUser, casterUserPW); Serial.print(F("beginClient: Sending credentials: ")); Serial.println(userCredentials); @@ -81,20 +86,27 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //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 + int encodedCredentialsSize = strEncodedCredentials.length() + 1; + char *encodedCredentials = new char[encodedCredentialsSize]; + memset(encodedCredentials, 0, encodedCredentialsSize); + strEncodedCredentials.toCharArray(encodedCredentials, encodedCredentialsSize); //Convert String to char array #elif defined(ARDUINO_ARCH_APOLLO3) || defined(ARDUINO_ARDUINO_NANO33BLE) - char encodedCredentials[sizeof(userCredentials) * 8]; + int encodedCredentialsSize = userCredentialsSize * 8; + char *encodedCredentials = new char[encodedCredentialsSize]; + memset(encodedCredentials, 0, encodedCredentialsSize); size_t olen; - mbedtls_base64_encode((unsigned char *)encodedCredentials, sizeof(userCredentials) * 8, &olen, (const unsigned char *)userCredentials, strlen(userCredentials)); + mbedtls_base64_encode((unsigned char *)encodedCredentials, encodedCredentialsSize, &olen, (const unsigned char *)userCredentials, strlen(userCredentials)); #else //Encode with nfriendly library - int encodedLen = base64_enc_len(strlen(userCredentials)); - char encodedCredentials[encodedLen]; //Create array large enough to house encoded data + int encodedLen = base64_enc_len(userCredentialsSize); + char *encodedCredentials = new char[encodedLen]; //Create array large enough to house encoded data + memset(encodedCredentials, 0, encodedLen); base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed #endif - snprintf(credentials, sizeof(credentials), "Authorization: Basic %s\r\n", encodedCredentials); + snprintf(credentials, CREDENTIALS_BUFFER_SIZE, "Authorization: Basic %s\r\n", encodedCredentials); + delete[] userCredentials; + delete[] encodedCredentials; } // Add the encoded credentials to the server request @@ -104,7 +116,7 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) Serial.print(F("beginClient: serverRequest size: ")); Serial.print(strlen(serverRequest)); Serial.print(F(" of ")); - Serial.print(sizeof(serverRequest)); + Serial.print(SERVER_BUFFER_SIZE); Serial.println(F(" bytes available")); // Send the server request @@ -123,6 +135,8 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) { Serial.println(F("beginClient: Caster timed out!")); closeConnection(theSocket, connectionIsOpen); + delete[] serverRequest; + delete[] credentials; return (false); } delay(100); @@ -134,12 +148,12 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //Check reply int connectionResult = 0; - char response[512 * 4]; + char *response = new char[512 * 4]; memset(response, 0, 512 * 4); size_t responseSpot = 0; while ((availableLength > 0) && (connectionResult == 0)) // Read bytes from the caster and store them { - if ((responseSpot + availableLength) >= (sizeof(response) - 1)) // Exit the loop if we get too much data + if ((responseSpot + availableLength) >= ((512 * 4) - 1)) // Exit the loop if we get too much data break; mySARA.socketRead(*theSocket, availableLength, &response[responseSpot]); @@ -172,6 +186,11 @@ bool beginClient(int *theSocket, bool *connectionIsOpen) //Serial.print(F("beginClient: Caster responded with: ")); Serial.println(response); // Uncomment this line to see the full response + // Free the memory allocated for serverRequest, credentials and response + delete[] serverRequest; + delete[] credentials; + delete[] response; + if (connectionResult != 200) { Serial.print(F("beginClient: Failed to connect to ")); From 138bb85669ae828d42e732c398673462e73ab4de Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 18 Jan 2022 08:07:40 +0000 Subject: [PATCH 18/21] Allocate memory for the AssistNow data --- .../SARA-R5_Example12_AssistNowOnline.ino | 9 ++++++--- .../SARA-R5_Example14_AssistNowOffline.ino | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino index 06db4ba..26f986e 100644 --- a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino +++ b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino @@ -185,12 +185,12 @@ void setup() Serial.println(fileSize); // Read the data from file - char theAssistData[fileSize]; - if (mySARA.getFileContents(theFilename, (char *)theAssistData) != SARA_R5_SUCCESS) + char *theAssistData = new char[fileSize]; + if (mySARA.getFileContents(theFilename, theAssistData) != SARA_R5_SUCCESS) { Serial.println(F("getFileContents failed! Freezing...")); while (1) - ; // Do nothing more + ; // Do nothing more } //prettyPrintChars(theAssistData, fileSize); // Uncomment this line to see the whole file contents (including the HTTP header) @@ -211,6 +211,9 @@ void setup() // Wait for up to 100ms for each ACK to arrive! 100ms is a bit excessive... 7ms is nearer the mark. myGNSS.pushAssistNowData((const uint8_t *)theAssistData, fileSize, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); + // Delete the memory allocated to store the AssistNow data + delete[] theAssistData; + // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus myGNSS.setI2CpollingWait(125); diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino index a1a45ec..22243f2 100644 --- a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino @@ -222,8 +222,8 @@ void setup() Serial.println(fileSize); // Read the data from file - char theAssistData[fileSize]; - if (mySARA.getFileContents(theFilename, (char *)theAssistData) != SARA_R5_SUCCESS) + char *theAssistData = new char[fileSize]; + if (mySARA.getFileContents(theFilename, theAssistData) != SARA_R5_SUCCESS) { Serial.println(F("getFileContents failed! Freezing...")); while (1) @@ -322,7 +322,10 @@ void setup() // It will ignore the HTTP header at the start of the AssistNow file. myGNSS.pushAssistNowData(todayStart, true, (const uint8_t *)theAssistData, tomorrowStart - todayStart, SFE_UBLOX_MGA_ASSIST_ACK_YES, 100); - // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus + // Delete the memory allocated to store the AssistNow data + delete[] theAssistData; + + // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus myGNSS.setI2CpollingWait(125); } From f21d98221547802393d1f2a0be8a4010c44e17a1 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 18 Jan 2022 08:57:05 +0000 Subject: [PATCH 19/21] v1.1.0 --- README.md | 8 +++++++- library.properties | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5ecce49..79ad3af 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,13 @@ SparkFun u-blox SARA-R5 Arduino Library -An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker and SparkFun LTE GNSS Breakout - SARA-R5. +An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the [SparkFun MicroMod Asset Tracker](https://www.sparkfun.com/products/17272) and the [SparkFun LTE GNSS Breakout - SARA-R5](https://www.sparkfun.com/products/18031). + +v1.1 has had a thorough update and includes new features and examples. This library now supports up to 7 simultaneous TCP or UDP sockets. There are new examples to show how to play ping pong with both TCP and UDP sockets. + +v1.1 also supports binary data transfers correctly. There are new examples showing how you can integrate this library with the [SparkFun u-blox GNSS Arduino Library](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library), to use the SARA-R5 to: download AssistNow Online and Offline data and push it to the GNSS; open a connection to a NTRIP Caster (such as RTK2go, Skylark or Emlid Caster) and push RTK correction data to the GNSS. + +You can install this library using the Arduino IDE Library Manager: search for _**SparkFun u-blox SARA-R5**_ ## Repository Contents diff --git a/library.properties b/library.properties index fe9a712..ba05891 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=SparkFun u-blox SARA-R5 Arduino Library -version=1.0.6 +version=1.1.0 author=SparkFun Electronics maintainer=SparkFun Electronics -sentence=Library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud -paragraph=An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker. +sentence=Library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud

+paragraph=An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker and the SparkFun LTE GNSS Breakout - SARA-R5.

v1.1 has had a thorough update and includes new features and examples. This library now supports up to 7 simultaneous TCP or UDP sockets. There are new examples to show how to play ping pong with both TCP and UDP sockets.

v1.1 also supports binary data transfers correctly. There are new examples showing how you can integrate this library with the SparkFun u-blox GNSS Arduino Library, to use the SARA-R5 to: download AssistNow Online and Offline data and push it to the GNSS; open a connection to a NTRIP Caster (such as RTK2go, Skylark or Emlid Caster) and push RTK correction data to the GNSS.
category=Communication url=https://github.com/sparkfun/SparkFun_u-blox_SARA-R5_Arduino_Library architectures=* From 58986bdb6301ed1986a3eebe04867bdb7574f7e5 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 18 Jan 2022 10:33:02 +0000 Subject: [PATCH 20/21] Add Example 16 - works better on Mbed OS --- README.md | 2 +- ...5_Example16_GNSS_NTRIP_Caster__Polling.ino | 284 ++++++++++++++ .../SARA-R5_NTRIP_Client.ino | 366 ++++++++++++++++++ .../secrets.h | 27 ++ 4 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling.ino create mode 100644 examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_NTRIP_Client.ino create mode 100644 examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/secrets.h diff --git a/README.md b/README.md index 79ad3af..8253e26 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure clo v1.1 has had a thorough update and includes new features and examples. This library now supports up to 7 simultaneous TCP or UDP sockets. There are new examples to show how to play ping pong with both TCP and UDP sockets. -v1.1 also supports binary data transfers correctly. There are new examples showing how you can integrate this library with the [SparkFun u-blox GNSS Arduino Library](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library), to use the SARA-R5 to: download AssistNow Online and Offline data and push it to the GNSS; open a connection to a NTRIP Caster (such as RTK2go, Skylark or Emlid Caster) and push RTK correction data to the GNSS. +v1.1 also supports binary data transfers correctly. There are new examples showing how you can integrate this library with the [SparkFun u-blox GNSS Arduino Library](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library) and use the SARA-R5 to: download AssistNow Online and Offline data and push it to the GNSS; open a connection to a NTRIP Caster (such as RTK2go, Skylark or Emlid Caster) and push RTK correction data to the GNSS. You can install this library using the Arduino IDE Library Manager: search for _**SparkFun u-blox SARA-R5**_ diff --git a/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling.ino b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling.ino new file mode 100644 index 0000000..fb9c60a --- /dev/null +++ b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling.ino @@ -0,0 +1,284 @@ +/* + + SARA-R5 Example + =============== + + u-blox GNSS NTRIP Caster Client - Polling + + This version uses polling to check for the arrival of new RTK correction and NMEA GPGGA data. + It performs better on Mbed OS platforms: SparkFun Artemis; Arduino Nano + + Written by: Paul Clark + Date: January 18th 2021 + + This example uses the SARA's mobile data connection to: + * Request RTK RTCM data from a NTRIP Caster service + * Push the RTCM data to an external u-blox GNSS module over I2C (not to the one built-in to the SARA-R510M8S) + * NMEA GPGGA data is pushed to the Caster every 10 seconds + + The PDP profile is read from NVM. Please make sure you have run examples 4 & 7 previously to set up the profile. + + Update secrets.h with your NTRIP Caster username and password + + ************************************************************************************************** + * Important Note: * + * * + * This example pulls kBytes of correction data from the NTRIP Caster. * + * Depending on your location and service provider, the data rate may exceed the allowable * + * rates for LTE-M or NB-IoT. * + * Worst case, your service provider may throttle or block the connection - now or in the future. * + * We are looking for a long-term solution to this - almost certainly using LTE Cat 1 instead. * + ************************************************************************************************** + + Feel like supporting open source hardware? + Buy a board from SparkFun! + + Licence: MIT + Please see LICENSE.md for full details + +*/ + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// 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) || defined(ARDUINO_ARCH_APOLLO3) || defined(ARDUINO_ARDUINO_NANO33BLE) +#include "base64.h" //Built-in ESP32 library +#else +#include //nfriendly library from https://github.com/adamvr/arduino-base64, will work with any platform +#endif + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_SARA-R5_Arduino_Library + +// Uncomment the next line to connect to the SARA-R5 using hardware Serial1 +#define saraSerial Serial1 + +// Uncomment the next line to create a SoftwareSerial object to pass to the SARA-R5 library instead +//SoftwareSerial saraSerial(8, 9); + +// Create a SARA_R5 object to use throughout the sketch +// Usually we would tell the library which GPIO pin to use to control the SARA power (see below), +// but we can start the SARA without a power pin. It just means we need to manually +// turn the power on if required! ;-D +SARA_R5 mySARA; + +// Create a SARA_R5 object to use throughout the sketch +// We need to tell the library what GPIO pin is connected to the SARA power pin. +// If you're using the MicroMod Asset Tracker and the MicroMod Artemis Processor Board, +// the pin name is G2 which is connected to pin AD34. +// Change the pin number if required. +//SARA_R5 mySARA(34); + +// Create a SARA_R5 object to use throughout the sketch +// If you are using the LTE GNSS Breakout, and have access to the SARA's RESET_N pin, you can pass that to the library too +// allowing it to do an emergency shutdown if required. +// Change the pin numbers if required. +//SARA_R5 mySARA(34, 35); // PWR_ON, RESET_N + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// Globals + +volatile int socketNum = -1; // The TCP socket number. -1 indicates invalid/closed socket +volatile bool connectionOpen = false; // Flag to indicate if the connection to the NTRIP Caster is open +volatile unsigned long lastReceivedRTCM_ms; // Record when data last arrived - so we can time out if required + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void setup() +{ + String currentOperator = ""; + + Serial.begin(115200); // Start the serial console + + // Wait for user to press key to begin + Serial.println(F("SARA-R5 Example")); + Serial.println(F("Wait for the SARA NI LED to light up - then press any key to begin")); + + while (!Serial.available()) // Wait for the user to press a key (send any serial character) + ; + while (Serial.available()) // Empty the serial RX buffer + Serial.read(); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Start communication with the SARA-R5. Load and activate the Packet Switched Data profile. + + //mySARA.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + + // For the MicroMod Asset Tracker, we need to invert the power pin so it pulls high instead of low + // Comment the next line if required + mySARA.invertPowerPin(true); + + // Initialize the SARA + if (mySARA.begin(saraSerial, 115200) ) + { + Serial.println(F("SARA-R5 connected!")); + } + else + { + Serial.println(F("Unable to communicate with the SARA.")); + Serial.println(F("Manually power-on (hold the SARA On button for 3 seconds) on and try again.")); + while (1) ; // Loop forever on fail + } + Serial.println(); + + // First check to see if we're connected to an operator: + if (mySARA.getOperator(¤tOperator) == SARA_R5_SUCCESS) + { + Serial.print(F("Connected to: ")); + Serial.println(currentOperator); + } + else + { + Serial.print(F("The SARA is not yet connected to an operator. Please use the previous examples to connect. Or wait and retry. Freezing...")); + while (1) + ; // Do nothing more + } + + // Deactivate the PSD profile - in case one is already active + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_DEACTIVATE) != SARA_R5_SUCCESS) + { + Serial.println(F("Warning: performPDPaction (deactivate profile) failed. Probably because no profile was active.")); + } + + // Load the PSD profile from NVM - these were saved by a previous example + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_LOAD) != SARA_R5_SUCCESS) + { + Serial.println(F("performPDPaction (load from NVM) failed! Freezing...")); + while (1) + ; // Do nothing more + } + + // Activate the profile + if (mySARA.performPDPaction(0, SARA_R5_PSD_ACTION_ACTIVATE) != SARA_R5_SUCCESS) + { + Serial.println(F("performPDPaction (activate profile) failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Print the dynamic IP Address (for profile 0) + IPAddress myAddress; + mySARA.getNetworkAssignedIPAddress(0, &myAddress); + Serial.print(F("\r\nMy IP Address is: ")); + Serial.print(myAddress[0]); + Serial.print(F(".")); + Serial.print(myAddress[1]); + Serial.print(F(".")); + Serial.print(myAddress[2]); + Serial.print(F(".")); + Serial.println(myAddress[3]); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Start I2C. Connect to the GNSS. + + Wire.begin(); //Start I2C + + // Uncomment the next line to enable the 'major' GNSS debug messages on Serial so you can see what AssistNow data is being sent + //myGNSS.enableDebugging(Serial, true); + + if (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.")); + 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.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible + + myGNSS.setNavigationFrequency(1); //Set the navigation rate to 1Hz + + myGNSS.setAutoPVT(true); // Enable automatic PVT reports at the navigation frequency + + // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA + myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP); + + myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 10); // Tell the module to output GGA every 10 seconds + + //myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM + +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void loop() +{ + enum states // Use a 'state machine' to open and close the connection + { + open_connection, + check_connection_and_wait_for_keypress, + close_connection, + waiting_for_keypress + }; + static states state = open_connection; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + switch (state) + { + case open_connection: + Serial.println(F("Connecting to the NTRIP caster...")); + if (beginClient((int *)&socketNum, (bool *)&connectionOpen)) // Try to open the connection to the caster + { + Serial.println(F("Connected to the NTRIP caster! Press any key to disconnect...")); + state = check_connection_and_wait_for_keypress; // Move on + } + else + { + Serial.print(F("Could not connect to the caster. Trying again in 5 seconds.")); + for (int i = 0; i < 5; i++) + { + delay(1000); + Serial.print(F(".")); + } + Serial.println(); + } + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case check_connection_and_wait_for_keypress: + // If the connection has dropped or timed out, or if the user has pressed a key + if ((checkConnection((int)socketNum, (bool)connectionOpen) == false) || (keyPressed())) + { + state = close_connection; // Move on + } + delay(50); + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case close_connection: + Serial.println(F("Closing the connection to the NTRIP caster...")); + closeConnection((int *)&socketNum, (bool *)&connectionOpen); + Serial.println(F("Press any key to reconnect...")); + state = waiting_for_keypress; // Move on + break; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + case waiting_for_keypress: + // Wait for the user to press a key + checkConnection((int)socketNum, (bool)connectionOpen); // 'Check' the connection - to print the latest PVT data + if (keyPressed()) + state = open_connection; // Move on + break; + } +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_NTRIP_Client.ino b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_NTRIP_Client.ino new file mode 100644 index 0000000..0c1b797 --- /dev/null +++ b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/SARA-R5_NTRIP_Client.ino @@ -0,0 +1,366 @@ + +#include "secrets.h" // Update secrets.h with your AssistNow token string + +const unsigned long maxTimeBeforeHangup_ms = 20000UL; //If we fail to get a complete RTCM frame after 20s, then disconnect from caster + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Connect to NTRIP Caster. Return true is connection is successful. +bool beginClient(int *theSocket, bool *connectionIsOpen) +{ + // Sanity check - return now if the socket is already open (should be impossible, but still...) + if (*theSocket >= 0) + { + Serial.print(F("beginClient: Socket is already open")); + if (*connectionIsOpen) + { + Serial.println(F(" and the connection is open!")); + } + else + { + Serial.println(F("!")); + } + return (false); + } + + Serial.println(F("beginClient: Opening TCP socket")); + + *theSocket = mySARA.socketOpen(SARA_R5_TCP); + if (*theSocket == -1) + { + Serial.println(F("beginClient: socketOpen failed!")); + return (false); + } + + Serial.print(F("beginClient: Using socket ")); + Serial.println(*theSocket); + + Serial.print(F("beginClient: Connecting to ")); + Serial.print(casterHost); + Serial.print(F(" on port ")); + Serial.println(casterPort); + + if (mySARA.socketConnect(*theSocket, casterHost, casterPort) != SARA_R5_SUCCESS) + { + Serial.println(F("beginClient: socketConnect failed!")); + } + else + { + Serial.print(F("beginClient: Connected to ")); + Serial.print(casterHost); + Serial.print(F(" : ")); + Serial.println(casterPort); + + Serial.print(F("beginClient: Requesting NTRIP Data from mount point ")); + Serial.println(mountPoint); + + // Set up the server request (GET) + const int SERVER_BUFFER_SIZE = 512; + char *serverRequest = new char[SERVER_BUFFER_SIZE]; + memset(serverRequest, 0, 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); + + // Set up the credentials + const int CREDENTIALS_BUFFER_SIZE = 512; + char *credentials = new char[CREDENTIALS_BUFFER_SIZE]; + memset(credentials, 0, CREDENTIALS_BUFFER_SIZE); + if (strlen(casterUser) == 0) + { + strncpy(credentials, "Accept: */*\r\nConnection: close\r\n", CREDENTIALS_BUFFER_SIZE); + } + else + { + //Pass base64 encoded user:pw + int userCredentialsSize = sizeof(casterUser) + sizeof(casterUserPW) + 1; //The ':' takes up a spot + char *userCredentials = new char[userCredentialsSize]; + memset(userCredentials, 0, userCredentialsSize); + snprintf(userCredentials, userCredentialsSize, "%s:%s", casterUser, casterUserPW); + + Serial.print(F("beginClient: Sending credentials: ")); + Serial.println(userCredentials); + +#if defined(ARDUINO_ARCH_ESP32) + //Encode with ESP32 built-in library + base64 b; + String strEncodedCredentials = b.encode(userCredentials); + int encodedCredentialsSize = strEncodedCredentials.length() + 1; + char *encodedCredentials = new char[encodedCredentialsSize]; + memset(encodedCredentials, 0, encodedCredentialsSize); + strEncodedCredentials.toCharArray(encodedCredentials, encodedCredentialsSize); //Convert String to char array +#elif defined(ARDUINO_ARCH_APOLLO3) || defined(ARDUINO_ARDUINO_NANO33BLE) + int encodedCredentialsSize = userCredentialsSize * 8; + char *encodedCredentials = new char[encodedCredentialsSize]; + memset(encodedCredentials, 0, encodedCredentialsSize); + size_t olen; + mbedtls_base64_encode((unsigned char *)encodedCredentials, encodedCredentialsSize, &olen, (const unsigned char *)userCredentials, strlen(userCredentials)); +#else + //Encode with nfriendly library + int encodedLen = base64_enc_len(userCredentialsSize); + char *encodedCredentials = new char[encodedLen]; //Create array large enough to house encoded data + memset(encodedCredentials, 0, encodedLen); + base64_encode(encodedCredentials, userCredentials, strlen(userCredentials)); //Note: Input array is consumed +#endif + + snprintf(credentials, CREDENTIALS_BUFFER_SIZE, "Authorization: Basic %s\r\n", encodedCredentials); + delete[] userCredentials; + delete[] encodedCredentials; + } + + // Add the encoded credentials to the server request + strncat(serverRequest, credentials, SERVER_BUFFER_SIZE - 1); + strncat(serverRequest, "\r\n", SERVER_BUFFER_SIZE - 1); + + Serial.print(F("beginClient: serverRequest size: ")); + Serial.print(strlen(serverRequest)); + Serial.print(F(" of ")); + Serial.print(SERVER_BUFFER_SIZE); + Serial.println(F(" bytes available")); + + // Send the server request + Serial.println(F("beginClient: Sending server request: ")); + Serial.println(serverRequest); + + mySARA.socketWrite(*theSocket, (const char *)serverRequest); + + //Wait up to 5 seconds for response. Poll the number of available bytes. Don't use the callback yet. + unsigned long startTime = millis(); + int availableLength = 0; + while (availableLength == 0) + { + mySARA.socketReadAvailable(*theSocket, &availableLength); + if (millis() > (startTime + 5000)) + { + Serial.println(F("beginClient: Caster timed out!")); + closeConnection(theSocket, connectionIsOpen); + delete[] serverRequest; + delete[] credentials; + return (false); + } + delay(100); + } + + Serial.print(F("beginClient: server replied with ")); + Serial.print(availableLength); + Serial.println(F(" bytes")); + + //Check reply + int connectionResult = 0; + char *response = new char[512 * 4]; + memset(response, 0, 512 * 4); + size_t responseSpot = 0; + while ((availableLength > 0) && (connectionResult == 0)) // Read bytes from the caster and store them + { + if ((responseSpot + availableLength) >= ((512 * 4) - 1)) // Exit the loop if we get too much data + break; + + mySARA.socketRead(*theSocket, availableLength, &response[responseSpot]); + responseSpot += availableLength; + + //Serial.print(F("beginClient: response is: ")); + //Serial.println(response); + + if (connectionResult == 0) // Only print success/fail once + { + if (strstr(response, "200") != NULL) //Look for '200 OK' + { + Serial.println(F("beginClient: 200 seen!")); + connectionResult = 200; + } + if (strstr(response, "401") != NULL) //Look for '401 Unauthorized' + { + Serial.println(F("beginClient: Hey - your credentials look bad! Check your caster username and password.")); + connectionResult = 401; + } + } + + mySARA.socketReadAvailable(*theSocket, &availableLength); // Update availableLength + + Serial.print(F("beginClient: socket now has ")); + Serial.print(availableLength); + Serial.println(F(" bytes available")); + } + response[responseSpot] = '\0'; // NULL-terminate the response + + //Serial.print(F("beginClient: Caster responded with: ")); Serial.println(response); // Uncomment this line to see the full response + + // Free the memory allocated for serverRequest, credentials and response + delete[] serverRequest; + delete[] credentials; + delete[] response; + + if (connectionResult != 200) + { + Serial.print(F("beginClient: Failed to connect to ")); + Serial.println(casterHost); + closeConnection(theSocket, connectionIsOpen); + return (false); + } + else + { + Serial.print(F("beginClient: Connected to: ")); + Serial.println(casterHost); + lastReceivedRTCM_ms = millis(); //Reset timeout + } + + } //End attempt to connect + + *connectionIsOpen = true; + return (true); +} // /beginClient + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Check the connection: print PVT data; check for and push GPGGA; check for and push RTCM data +//Return false if: the connection has dropped, or if we receive no data for maxTimeBeforeHangup_ms +bool checkConnection(int theSocket, bool connectionIsOpen) +{ + // See if new PVT data is available + if (myGNSS.getPVT()) // getPVT will return true if fresh data is available + { + long latitude = myGNSS.getLatitude(); // Print the latitude + Serial.print(F("checkConnection: Lat: ")); + Serial.print(latitude / 10000000L); + Serial.print(F(".")); + Serial.print(abs(latitude % 10000000L)); + + long longitude = myGNSS.getLongitude(); // Print the longitude + Serial.print(F(" Long: ")); + Serial.print(longitude / 10000000L); + Serial.print(F(".")); + Serial.print(abs(longitude % 10000000L)); + + long altitude = myGNSS.getAltitudeMSL(); // Print the height above mean sea level + Serial.print(F(" Height: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + uint8_t fixType = myGNSS.getFixType(); // Print the fix type + Serial.print(F(" Fix: ")); + Serial.print(fixType); + if (fixType == 0) + Serial.print(F(" (None)")); + else if (fixType == 1) + Serial.print(F(" (Dead Reckoning)")); + else if (fixType == 2) + Serial.print(F(" (2D)")); + else if (fixType == 3) + Serial.print(F(" (3D)")); + else if (fixType == 3) + Serial.print(F(" (GNSS + Dead Reckoning)")); + else if (fixType == 5) + Serial.print(F(" (Time Only)")); + else + Serial.print(F(" (UNKNOWN)")); + + uint8_t carrSoln = myGNSS.getCarrierSolutionType(); // Print the carrier solution + Serial.print(F(" Carrier Solution: ")); + Serial.print(carrSoln); + if (carrSoln == 0) + Serial.print(F(" (None)")); + else if (carrSoln == 1) + Serial.print(F(" (Floating)")); + else if (carrSoln == 2) + Serial.print(F(" (Fixed)")); + else + Serial.print(F(" (UNKNOWN)")); + + uint32_t hAcc = myGNSS.getHorizontalAccEst(); // Print the horizontal accuracy estimate + Serial.print(F(" Horizontal Accuracy Estimate: ")); + Serial.print(hAcc); + Serial.print(F(" (mm)")); + + Serial.println(); + } + + if ((theSocket >= 0) && connectionIsOpen) // Check that the connection is still open + { + + // Check if new NMEA GPGGA data is available + NMEA_GGA_data_t *gpgga = new NMEA_GGA_data_t; // Allocate storage for the GPGGA data + if (myGNSS.getLatestNMEAGPGGA(gpgga) == 2) // Is new /fresh GPGGA data available? + { + //Push our current GGA sentence to caster + Serial.print(F("checkConnection: pushing NMEA GPGGA to Caster: ")); + Serial.print((const char *)gpgga->nmea); // .nmea is printable and has a \r\n on the end + mySARA.socketWrite(theSocket, (const char *)gpgga->nmea); + } + delete gpgga; // Delete (free) the allocated memory + + // Check if new RTCM data is available + int length = 0; + if (mySARA.socketReadAvailable(theSocket, &length) == SARA_R5_SUCCESS) + { + if (length > 0) + { + // Push RTCM data to the GNSS + char *rtcm = new char[length]; // Allocate storage for the RTCM data + int bytesRead = 0; + if (mySARA.socketRead(theSocket, length, rtcm, &bytesRead) == SARA_R5_SUCCESS) // Get the data. bytesRead could be less than length + { + Serial.print(F("checkConnection: RTCM data received. Length is ")); + Serial.print(length); + Serial.println(F(". Pushing it to the GNSS")); + + myGNSS.pushRawData((uint8_t *)rtcm, (size_t)bytesRead); + + lastReceivedRTCM_ms = millis(); // Update lastReceivedRTCM_ms + } + delete rtcm; // Delete (free) the allocated memory + } + } + } + else + { + //Serial.println(F("checkConnection: Connection dropped!")); + return (false); // Connection has dropped - return false + } + + //Timeout if we don't have new data for maxTimeBeforeHangup_ms + if ((millis() - lastReceivedRTCM_ms) > maxTimeBeforeHangup_ms) + { + Serial.println(F("checkConnection: RTCM timeout!")); + return (false); // Connection has timed out - return false + } + + return (true); +} // /processConnection + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void closeConnection(int *theSocket, bool *connectionIsOpen) +{ + // Check the socket is actually open, otherwise we'll get an error when we try to close it + if (*theSocket >= 0) + { + mySARA.socketClose(*theSocket); + Serial.println(F("closeConnection: Connection closed!")); + } + else + { + Serial.println(F("closeConnection: Connection was already closed!")); + } + + *theSocket = -1; // Clear the socket number to indicate it is closed + *connectionIsOpen = false; // Flag that the connection is closed +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//Return true if a key has been pressed +bool keyPressed() +{ + if (Serial.available()) // Check for a new key press + { + delay(100); // Wait for any more keystrokes to arrive + while (Serial.available()) // Empty the serial buffer + Serial.read(); + return (true); + } + + return (false); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/secrets.h b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/secrets.h new file mode 100644 index 0000000..9de9b71 --- /dev/null +++ b/examples/SARA-R5_Example16_GNSS_NTRIP_Caster__Polling/secrets.h @@ -0,0 +1,27 @@ +//Your WiFi credentials +const char ssid[] = "yourSSID"; +const char password[] = "yourPassword"; + +//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 + +// Skylark (Swift Navigation) is awesome - but requires a subscription: +// https://www.swiftnav.com/skylark +// https://account.swiftnav.com/sign-up +// Use the promo-code ONEMONTHFREE for a free one month access to Skylark on one device +const char casterHost[] = "na.skylark.swiftnav.com"; // na = North Americs L1+L2; eu = Europe L1+L2 +const uint16_t casterPort = 2101; +const char casterUser[] = "NTRIPusername+accountSubdomain"; // This is generated when you add a device to your Skylark account +const char casterUserPW[] = "devicePassword"; +const char mountPoint[] = "CRS"; // The mount point you want to get data from. Select CRS (Cloud Reference Station) for the ZED-F9x From 4cc47e0c197ef174e9e7bb5568ca1f683032db25 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 18 Jan 2022 10:37:48 +0000 Subject: [PATCH 21/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8253e26..56bfec7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ SparkFun u-blox SARA-R5 Arduino Library An Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the [SparkFun MicroMod Asset Tracker](https://www.sparkfun.com/products/17272) and the [SparkFun LTE GNSS Breakout - SARA-R5](https://www.sparkfun.com/products/18031). -v1.1 has had a thorough update and includes new features and examples. This library now supports up to 7 simultaneous TCP or UDP sockets. There are new examples to show how to play ping pong with both TCP and UDP sockets. +v1.1 has had a thorough update and includes new features and examples. This library now supports up to 7 simultaneous TCP or UDP sockets. There are new examples to show how to play ping pong with multiple TCP and UDP sockets. v1.1 also supports binary data transfers correctly. There are new examples showing how you can integrate this library with the [SparkFun u-blox GNSS Arduino Library](https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library) and use the SARA-R5 to: download AssistNow Online and Offline data and push it to the GNSS; open a connection to a NTRIP Caster (such as RTK2go, Skylark or Emlid Caster) and push RTK correction data to the GNSS.