diff --git a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino new file mode 100644 index 0000000..5606dfe --- /dev/null +++ b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_AssistNow_Online.ino @@ -0,0 +1,188 @@ + +#include "secrets.h" // Update secrets.h with your AssistNow token string + +// u-blox AssistNow https servers +const char assistNowServer[] = "online-live1.services.u-blox.com"; +//const char assistNowServer[] = "online-live2.services.u-blox.com"; // Alternate server + +const char getQuery[] = "GetOnlineData.ashx?"; +const char tokenPrefix[] = "token="; +const char tokenSuffix[] = ";"; +const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal +const char getDataType[] = "datatype=eph,alm,aux;"; // Data type can be: eph,alm,aux,pos + +volatile bool httpResultSeen = false; // Flag to indicate that the HTTP URC was received + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// processHTTPcommandResult is provided to the SARA-R5 library via a +// callback setter -- setHTTPCommandCallback. (See the end of setup()) +void processHTTPcommandResult(int profile, int command, int result) +{ + Serial.println(); + Serial.print(F("HTTP Command Result: profile: ")); + Serial.print(profile); + Serial.print(F(" command: ")); + Serial.print(command); + Serial.print(F(" result: ")); + Serial.print(result); + if (result == 0) + Serial.print(F(" (fail)")); + if (result == 1) + Serial.print(F(" (success)")); + Serial.println(); + + // Get and print the most recent HTTP protocol error + int error_class; + int error_code; + mySARA.getHTTPprotocolError(0, &error_class, &error_code); + Serial.print(F("Most recent HTTP protocol error: class: ")); + Serial.print(error_class); + Serial.print(F(" code: ")); + Serial.print(error_code); + if (error_code == 0) + Serial.print(F(" (no error)")); + Serial.println(); + + httpResultSeen = true; // Set the flag +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool getAssistNowOnlineData(String theFilename) +{ + // Use HTTP GET to receive the AssistNow_Online data. Store it in the SARA-R5's internal file system. + + String theServer = assistNowServer; // Convert the AssistNow server to String + + const int REQUEST_BUFFER_SIZE = 256; + char theRequest[REQUEST_BUFFER_SIZE]; + + // Assemble the request + // Note the slash at the beginning + snprintf(theRequest, REQUEST_BUFFER_SIZE, "/%s%s%s%s%s%s", + getQuery, + tokenPrefix, + myAssistNowToken, + tokenSuffix, + getGNSS, + getDataType); + + String theRequestStr = theRequest; // Convert to String + + Serial.print(F("getAssistNowOnlineData: HTTP GET is https://")); + Serial.print(theServer); + Serial.println(theRequestStr); + + Serial.print(F("getAssistNowOnlineData: the AssistNow data will be stored in: ")); + Serial.println(theFilename); + + // Reset HTTP profile 0 + mySARA.resetHTTPprofile(0); + + // Set the server name + mySARA.setHTTPserverName(0, theServer); + + // Use HTTPS + mySARA.setHTTPsecure(0, false); // Setting this to true causes the GET to fail. Maybe due to the default CMNG profile? + + // Set a callback to process the HTTP command result + mySARA.setHTTPCommandCallback(&processHTTPcommandResult); + + httpResultSeen = false; // Clear the flag + + // HTTP GET + mySARA.sendHTTPGET(0, theRequestStr, theFilename); + + // Wait for 20 seconds while calling mySARA.bufferedPoll() to see the HTTP result. + Serial.print(F("getAssistNowOnlineData: Waiting up to 20 seconds for the HTTP Result")); + int i = 0; + while ((i < 20000) && (httpResultSeen == false)) + { + mySARA.bufferedPoll(); // Keep processing data from the SARA so we can catch the HTTP command result + i++; + delay(1); + if (i % 1000 == 0) + Serial.print(F(".")); + } + Serial.println(); + + if (httpResultSeen == false) + { + Serial.print(F("getAssistNowOnlineData: HTTP GET failed!")); + return false; + } + + int fileSize; + if (mySARA.getFileSize(theFilename, &fileSize) != SARA_R5_SUCCESS) + { + Serial.print(F("getAssistNowOnlineData: No file written?!")); + return false; + } + + Serial.print(F("File size is: ")); + Serial.println(fileSize); + + return true; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +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(); +} diff --git a/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino new file mode 100644 index 0000000..abc7e2d --- /dev/null +++ b/examples/SARA-R5_Example12_AssistNowOnline/SARA-R5_Example12_AssistNowOnline.ino @@ -0,0 +1,250 @@ +/* + + SARA-R5 Example + =============== + + u-blox AssistNow Online + + Written by: Paul Clark + Date: January 8th 2021 + + This example uses the SARA's mobile data connection to: + * Request AssistNow Online data from the u-blox server + * Provide assistance 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. + + You will need to have a token to be able to access Thingstream. See the AssistNow README for more details: + https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/examples/AssistNow + + Update secrets.h with your AssistNow token string + + Note: this code does not use the AssistNow or CellLocate features built-in to the SARA-R510M8S. + Those features are great but the assistance data remains 'hidden' and cannot be read and passed to an external GNSS. + This code downloads the assistance data to the SARA-R5's internal file system where it can be accessed, + used and re-used with an external GNSS. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + + Licence: MIT + Please see LICENSE.md for full details + +*/ + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#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; + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +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(); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //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 + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Start I2C. Connect to the GNSS. + + Wire.begin(); //Start I2C + + //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + + 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); //Turn off NMEA noise + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Request the AssistNow data from the server. Data is stored in the SARA's file system. + + String theFilename = "assistnow_online.ubx"; // The file that will contain the AssistNow Online data + + if (getAssistNowOnlineData(theFilename) == false) // See SARA-R5_AssistNow_Online.ino + { + Serial.println(F("getAssistNowOnlineData failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Read the AssistNow data from file and push it to the module + + // Read the data from file + String theAssistData = ""; + if (mySARA.getFileContents(theFilename, &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) + + // Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data + myGNSS.setAckAiding(1); + + // Speed things up by setting setI2CpollingWait to 1ms + myGNSS.setI2CpollingWait(1); + + // Push all the AssistNow data. + // + // pushAssistNowData is clever and will only push valid UBX-format data. + // It will ignore the HTTP header at the start of the AssistNow file. + // + // 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); + + // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus + myGNSS.setI2CpollingWait(125); + + // Delete the file after use. This is optional as the SARA will automatically overwrite the file. + // And you might want to reuse it? AssistNow Online data is valid for 2-4 hours. + //if (mySARA.deleteFile(theFilename) != SARA_R5_SUCCESS) + //{ + // Serial.println(F("Warning: deleteFile failed!")); + //} +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void loop() +{ + // Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D + + long latitude = myGNSS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGNSS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGNSS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGNSS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + byte fixType = myGNSS.getFixType(); + Serial.print(F(" Fix: ")); + if(fixType == 0) Serial.print(F("No fix")); + 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 == 4) Serial.print(F("GNSS + Dead reckoning")); + else if(fixType == 5) Serial.print(F("Time only")); + + Serial.println(); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example12_AssistNowOnline/secrets.h b/examples/SARA-R5_Example12_AssistNowOnline/secrets.h new file mode 100644 index 0000000..ff875b2 --- /dev/null +++ b/examples/SARA-R5_Example12_AssistNowOnline/secrets.h @@ -0,0 +1,2 @@ +//Your AssistNow token +const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ"; diff --git a/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_Example13_SetClockWithNTP.ino b/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_Example13_SetClockWithNTP.ino new file mode 100644 index 0000000..69ee33b --- /dev/null +++ b/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_Example13_SetClockWithNTP.ino @@ -0,0 +1,167 @@ +/* + + SARA-R5 Example + =============== + + Set Clock With NTP + + Written by: Paul Clark + Date: January 9th 2022 + + This example demonstrates how to set the SARA-R5's internal Real Time Clock using NTP. + When the SARA-R5 registers on a network, it will set its clock automatically if: + Automatic time zone is enabled + Your network supports NITZ (Network Identity and Time Zone) + But for things like AssistNow Offline, it is convenient to have the SARA's RTC set to UTC. + Then the clock can be used to select the AssistNow data for now/today without time zone headaches. + This example shows how to: + Disable the automatic time zone (using autoTimeZoneForBegin before .begin) + Set the SARA's clock to UTC using NTP (see SARA-R5_NTP.ino) + + The PDP profile is read from NVM. Please make sure you have run examples 4 & 7 previously to set up the profile. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + + Licence: MIT + Please see LICENSE.md for full details + +*/ + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#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 + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +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(); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //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); + + // Disable the automatic time zone so we can use UTC. We need to do this _before_ .begin + mySARA.autoTimeZoneForBegin(false); + + // 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 + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Get the time from an NTP server and use it to set the clock. See SARA-R5_NTP.ino + uint8_t y, mo, d, h, min, s; + bool success = getNTPTime(&y, &mo, &d, &h, &min, &s); + if (!success) + { + Serial.println(F("getNTPTime failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //Set the SARA's RTC. Set the time zone to zero so the clock uses UTC + if (mySARA.setClock(y, mo, d, h, min, s, 0) != SARA_R5_SUCCESS) + { + Serial.println(F("setClock failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Read and print the clock as a String + Serial.print(F("The UTC time is: ")); + String theTime = mySARA.clock(); + Serial.println(theTime); +} + +void loop() +{ + // Nothing to do here +} diff --git a/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_NTP.ino b/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_NTP.ino new file mode 100644 index 0000000..e58ecc6 --- /dev/null +++ b/examples/SARA-R5_Example13_SetClockWithNTP/SARA-R5_NTP.ino @@ -0,0 +1,176 @@ + +#include // Note: this is the standard c time library, not Time.h + +// Get the time from NTP +// This code is based heavily on: +// https://docs.arduino.cc/tutorials/mkr-nb-1500/mkr-nb-library-examples#mkr-nb-gprs-udp-ntp-client +// Many thanks to: Michael Margolis, Tom Igoe, Arturo Guadalupi, et al + +// NTP Server +const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "africa.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "asia.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "europe.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "north-america.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "oceania.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "south-america.pool.ntp.org"; // The Network Time Protocol Server + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool getNTPTime(uint8_t *y, uint8_t *mo, uint8_t *d, uint8_t *h, uint8_t *min, uint8_t *s) +{ + int serverPort = 123; //NTP requests are to port 123 + + // Set up the packetBuffer for the NTP request + const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message + byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + + // Initialize values needed to form NTP request + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Allocate a UDP socket to talk to the NTP server + + int socketNum = mySARA.socketOpen(SARA_R5_UDP); + if (socketNum == -1) + { + Serial.println(F("getNTPTime: socketOpen failed!")); + return (false); + } + + Serial.print(F("getNTPTime: using socket ")); + Serial.println(socketNum); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Send the request - NTP uses UDP + + if (mySARA.socketWriteUDP(socketNum, ntpServer, serverPort, (const char *)&packetBuffer, NTP_PACKET_SIZE) != SARA_R5_SUCCESS) // Send the request + { + Serial.println(F("getNTPTime: socketWrite failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Wait up to 10 seconds for the response + unsigned long requestTime = millis(); + + while (millis() < (requestTime + 10000)) + { + // We could use the Socket Read Callback to get the data, but, just for giggles, + // and to prove it works, let's poll the arrival of the data manually... + int avail = 0; + if (mySARA.socketReadAvailableUDP(socketNum, &avail) != SARA_R5_SUCCESS) + { + Serial.println(F("getNTPTime: socketReadAvailable failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + if (avail >= NTP_PACKET_SIZE) // Is enough data available? + { + if (avail > NTP_PACKET_SIZE) // Too much data? + { + Serial.print(F("getNTPTime: too much data received! Length: ")); + Serial.print(avail); + Serial.print(F(". Reading ")); + Serial.print(NTP_PACKET_SIZE); + Serial.println(F(" bytes...")); + } + + if (mySARA.socketReadUDP(socketNum, NTP_PACKET_SIZE, (char *)&packetBuffer) != SARA_R5_SUCCESS) + { + Serial.println(F("getNTPTime: socketRead failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + // Extract the time from the reply + + Serial.print(F("getNTPTime: received ")); + Serial.print(avail); + Serial.println(F(" bytes. Extracting the time...")); + + //the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, esxtract the two words: + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + Serial.print(F("getNTPTime: seconds since Jan 1 1900 = ")); + Serial.println(secsSince1900); + + // now convert NTP time into everyday time: + Serial.print(F("getNTPTime: Unix time = ")); + + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + + // print Unix time: + Serial.println(epoch); + + // Instead of calculating the year, month, day, etc. manually, let's use time_t and tm to do it for us! + time_t dateTime = epoch; + tm *theTime = gmtime(&dateTime); + + // Load the time into y, mo, d, h, min, s + *y = theTime->tm_year - 100; // tm_year is years since 1900. Convert to years since 2000. + *mo = theTime->tm_mon + 1; //tm_mon starts at zero. Add 1 for January. + *d = theTime->tm_mday; + *h = theTime->tm_hour; + *min = theTime->tm_min; + *s = theTime->tm_sec; + + // Finish off by printing the time + Serial.print(F("getNTPTime: YY/MM/DD HH:MM:SS : ")); + if (*y < 10) Serial.print(F("0")); + Serial.print(*y); + Serial.print(F("/")); + if (*mo < 10) Serial.print(F("0")); + Serial.print(*mo); + Serial.print(F("/")); + if (*d < 10) Serial.print(F("0")); + Serial.print(*d); + Serial.print(F(" ")); + if (*h < 10) Serial.print(F("0")); + Serial.print(*h); + Serial.print(F(":")); + if (*min < 10) Serial.print(F("0")); + Serial.print(*min); + Serial.print(F(":")); + if (*s < 10) Serial.print(F("0")); + Serial.println(*s); + + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (true); // We are done! + } + + delay(100); // Wait before trying again + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + Serial.println(F("getNTPTime: no NTP data received!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); +} diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino new file mode 100644 index 0000000..48a8aa6 --- /dev/null +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_AssistNow_Online.ino @@ -0,0 +1,195 @@ + +#include "secrets.h" // Update secrets.h with your AssistNow token string + +// u-blox AssistNow https servers +const char assistNowOfflineServer[] = "offline-live1.services.u-blox.com"; +//const char assistNowOfflineServer[] = "offline-live2.services.u-blox.com"; // Alternate server + +const char getQuery[] = "GetOfflineData.ashx?"; +const char tokenPrefix[] = "token="; +const char tokenSuffix[] = ";"; +const char getGNSS[] = "gnss=gps,glo;"; // GNSS can be: gps,qzss,glo,bds,gal +const char getFormat[] = "format=mga;"; // Data format. Leave set to mga for M8 onwards. Can be aid. +const char getPeriod[] = "period=1;"; // Optional. The number of weeks into the future that the data will be valid. Can be 1-5. Default = 4. +const char getMgaResolution[] = "resolution=1;"; // Optional. Data resolution: 1 = every day; 2 = every other day; 3 = every 3rd day. +//Note: always use resolution=1. findMGAANOForDate does not yet support finding the 'closest' date. It needs an exact match. + +volatile bool httpResultSeen = false; // Flag to indicate that the HTTP URC was received + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// processHTTPcommandResult is provided to the SARA-R5 library via a +// callback setter -- setHTTPCommandCallback. (See the end of setup()) +void processHTTPcommandResult(int profile, int command, int result) +{ + Serial.println(); + Serial.print(F("HTTP Command Result: profile: ")); + Serial.print(profile); + Serial.print(F(" command: ")); + Serial.print(command); + Serial.print(F(" result: ")); + Serial.print(result); + if (result == 0) + Serial.print(F(" (fail)")); + if (result == 1) + Serial.print(F(" (success)")); + Serial.println(); + + // Get and print the most recent HTTP protocol error + int error_class; + int error_code; + mySARA.getHTTPprotocolError(0, &error_class, &error_code); + Serial.print(F("Most recent HTTP protocol error: class: ")); + Serial.print(error_class); + Serial.print(F(" code: ")); + Serial.print(error_code); + if (error_code == 0) + Serial.print(F(" (no error)")); + Serial.println(); + + httpResultSeen = true; // Set the flag +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool getAssistNowOfflineData(String theFilename) +{ + // Use HTTP GET to receive the AssistNow_Offline data. Store it in the SARA-R5's internal file system. + + String theServer = assistNowOfflineServer; // Convert the AssistNow server to String + + const int REQUEST_BUFFER_SIZE = 256; + char theRequest[REQUEST_BUFFER_SIZE]; + + // Assemble the request + // Note the slash at the beginning + snprintf(theRequest, REQUEST_BUFFER_SIZE, "/%s%s%s%s%s%s%s%s", + getQuery, + tokenPrefix, + myAssistNowToken, + tokenSuffix, + getGNSS, + getFormat, + getPeriod, + getMgaResolution + ); + + + String theRequestStr = theRequest; // Convert to String + + Serial.print(F("getAssistNowOfflineData: HTTP GET is https://")); + Serial.print(theServer); + Serial.println(theRequestStr); + + Serial.print(F("getAssistNowOfflineData: the AssistNow data will be stored in: ")); + Serial.println(theFilename); + + // Reset HTTP profile 0 + mySARA.resetHTTPprofile(0); + + // Set the server name + mySARA.setHTTPserverName(0, theServer); + + // Use HTTPS + mySARA.setHTTPsecure(0, false); // Setting this to true causes the GET to fail. Maybe due to the default CMNG profile? + + // Set a callback to process the HTTP command result + mySARA.setHTTPCommandCallback(&processHTTPcommandResult); + + httpResultSeen = false; // Clear the flag + + // HTTP GET + mySARA.sendHTTPGET(0, theRequestStr, theFilename); + + // Wait for 20 seconds while calling mySARA.bufferedPoll() to see the HTTP result. + Serial.print(F("getAssistNowOfflineData: Waiting up to 20 seconds for the HTTP Result")); + int i = 0; + while ((i < 20000) && (httpResultSeen == false)) + { + mySARA.bufferedPoll(); // Keep processing data from the SARA so we can catch the HTTP command result + i++; + delay(1); + if (i % 1000 == 0) + Serial.print(F(".")); + } + Serial.println(); + + if (httpResultSeen == false) + { + Serial.print(F("getAssistNowOfflineData: HTTP GET failed!")); + 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; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +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(); +} diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino new file mode 100644 index 0000000..4eb66af --- /dev/null +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_Example14_AssistNowOffline.ino @@ -0,0 +1,365 @@ +/* + + SARA-R5 Example + =============== + + u-blox AssistNow Offline + + Written by: Paul Clark + Date: January 8th 2021 + + This example uses the SARA's mobile data connection to: + * Request AssistNow Offline data from the u-blox server + * Provide assistance 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. + + This example uses UTC time from the SARA-R5's Real Time Clock to select the AssistNow Offline data for today. + + You will need to have a token to be able to access Thingstream. See the AssistNow README for more details: + https://github.com/sparkfun/SparkFun_u-blox_GNSS_Arduino_Library/tree/main/examples/AssistNow + + Update secrets.h with your AssistNow token string + + Note: this code does not use the AssistNow or CellLocate features built-in to the SARA-R510M8S. + Those features are great but the assistance data remains 'hidden' and cannot be read and passed to an external GNSS. + This code downloads the assistance data to the SARA-R5's internal file system where it can be accessed, + used and re-used with an external GNSS. + + Note: AssistNow Offline is not supported by the ZED-F9P! "The ZED-F9P supports AssistNow Online only." + + Feel like supporting open source hardware? + Buy a board from SparkFun! + + Licence: MIT + Please see LICENSE.md for full details + +*/ + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#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; + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +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(); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //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); + + // Disable the automatic time zone so we can use UTC. We need to do this _before_ .begin + mySARA.autoTimeZoneForBegin(false); + + // 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 + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Get the time from an NTP server and use it to set the clock. See SARA-R5_NTP.ino + uint8_t y, mo, d, h, min, s; + bool success = getNTPTime(&y, &mo, &d, &h, &min, &s); + if (!success) + { + Serial.println(F("getNTPTime failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //Set the SARA's RTC. Set the time zone to zero so the clock uses UTC + if (mySARA.setClock(y, mo, d, h, min, s, 0) != SARA_R5_SUCCESS) + { + Serial.println(F("setClock failed! Freezing...")); + while (1) + ; // Do nothing more + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Read and print the clock as a String + Serial.print(F("The UTC time is: ")); + String theTime = mySARA.clock(); + Serial.println(theTime); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // 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); //Turn off NMEA noise + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Request the AssistNow data from the server. Data is stored in the SARA's file system. + + String theFilename = "assistnow_offline.ubx"; // The file that will contain the AssistNow Offline data + +///* Comment from here ===> + if (getAssistNowOfflineData(theFilename) == false) // See SARA-R5_AssistNow_Offline.ino + { + Serial.println(F("getAssistNowOfflineData failed! Freezing...")); + while (1) + ; // Do nothing more + } +//*/ // <=== to here if you want to re-use the existing AssistNow Offline data + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Read the AssistNow data from file + + // Read the data from file + String theAssistData = ""; + if (mySARA.getFileContents(theFilename, &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) + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Read UTC time from the SARA-R5's RTC + + uint8_t year, month, day, hours, minutes, seconds; + int8_t timeZone; // Should be zero for UTC + if (mySARA.clock(&year, &month, &day, &hours, &minutes, &seconds, &timeZone) != SARA_R5_SUCCESS) + { + Serial.println(F("clock failed! Freezing...")); + while (1) + ; // Do nothing more + } + + if (year < 22) + { + Serial.print(F("WARNING: the SARA-R5's clock time is: ")); + Serial.print(mySARA.clock()); + Serial.println(F(". Did you forget to set the clock to UTC?")); + } + + if (timeZone != 0) + { + Serial.print(F("WARNING: the SARA-R5's time zone is: ")); + if (timeZone >= 0) Serial.println(F("+")); + Serial.print(timeZone); + Serial.println(F(". Did you forget to set the clock to UTC?")); + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // 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(); + + if (theAssistData.length() > 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()) + { + Serial.print(F("Found the data for today starting at location ")); + Serial.println(todayStart); + } + else + { + Serial.println("Could not find the data for today. This will not work well. The GNSS needs help to start up quickly."); + } + + // 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()) + { + Serial.print(F("Found the data for tomorrow starting at location ")); + Serial.println(tomorrowStart); + } + else + { + Serial.println("Could not find the data for tomorrow. (Today's data may be the last?)"); + } + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Push the RTC time to the module + + mySARA.clock(&year, &month, &day, &hours, &minutes, &seconds, &timeZone); // Refresh the time + + // setUTCTimeAssistance uses a default time accuracy of 2 seconds which should be OK here. + // Have a look at the library source code for more details. + myGNSS.setUTCTimeAssistance(year + 2000, month, day, hours, minutes, seconds); // Push it to the GNSS module + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Push the AssistNow data for today to the module - without the time + + if ((tomorrowStart - todayStart) > 0) + { + // Tell the module to return UBX_MGA_ACK_DATA0 messages when we push the AssistNow data + myGNSS.setAckAiding(1); + + // Speed things up by setting setI2CpollingWait to 1ms + myGNSS.setI2CpollingWait(1); + + // Push the AssistNow data for today - without the time + // + // 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); + + // Set setI2CpollingWait to 125ms to avoid pounding the I2C bus + myGNSS.setI2CpollingWait(125); + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Delete the file after use. This is optional as the SARA will automatically overwrite the file. + // And you might want to reuse it? AssistNow Offline data is valid for up to 35 days. + + //if (mySARA.deleteFile(theFilename) != SARA_R5_SUCCESS) + //{ + // Serial.println(F("Warning: deleteFile failed!")); + //} +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void loop() +{ + // Print the UBX-NAV-PVT data so we can see how quickly the fixType goes to 3D + + long latitude = myGNSS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGNSS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGNSS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGNSS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + byte fixType = myGNSS.getFixType(); + Serial.print(F(" Fix: ")); + if(fixType == 0) Serial.print(F("No fix")); + 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 == 4) Serial.print(F("GNSS + Dead reckoning")); + else if(fixType == 5) Serial.print(F("Time only")); + + Serial.println(); +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_NTP.ino b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_NTP.ino new file mode 100644 index 0000000..e58ecc6 --- /dev/null +++ b/examples/SARA-R5_Example14_AssistNowOffline/SARA-R5_NTP.ino @@ -0,0 +1,176 @@ + +#include // Note: this is the standard c time library, not Time.h + +// Get the time from NTP +// This code is based heavily on: +// https://docs.arduino.cc/tutorials/mkr-nb-1500/mkr-nb-library-examples#mkr-nb-gprs-udp-ntp-client +// Many thanks to: Michael Margolis, Tom Igoe, Arturo Guadalupi, et al + +// NTP Server +const char* ntpServer = "pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "africa.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "asia.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "europe.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "north-america.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "oceania.pool.ntp.org"; // The Network Time Protocol Server +//const char* ntpServer = "south-america.pool.ntp.org"; // The Network Time Protocol Server + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool getNTPTime(uint8_t *y, uint8_t *mo, uint8_t *d, uint8_t *h, uint8_t *min, uint8_t *s) +{ + int serverPort = 123; //NTP requests are to port 123 + + // Set up the packetBuffer for the NTP request + const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message + byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + + // Initialize values needed to form NTP request + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + //Allocate a UDP socket to talk to the NTP server + + int socketNum = mySARA.socketOpen(SARA_R5_UDP); + if (socketNum == -1) + { + Serial.println(F("getNTPTime: socketOpen failed!")); + return (false); + } + + Serial.print(F("getNTPTime: using socket ")); + Serial.println(socketNum); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Send the request - NTP uses UDP + + if (mySARA.socketWriteUDP(socketNum, ntpServer, serverPort, (const char *)&packetBuffer, NTP_PACKET_SIZE) != SARA_R5_SUCCESS) // Send the request + { + Serial.println(F("getNTPTime: socketWrite failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + // Wait up to 10 seconds for the response + unsigned long requestTime = millis(); + + while (millis() < (requestTime + 10000)) + { + // We could use the Socket Read Callback to get the data, but, just for giggles, + // and to prove it works, let's poll the arrival of the data manually... + int avail = 0; + if (mySARA.socketReadAvailableUDP(socketNum, &avail) != SARA_R5_SUCCESS) + { + Serial.println(F("getNTPTime: socketReadAvailable failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + if (avail >= NTP_PACKET_SIZE) // Is enough data available? + { + if (avail > NTP_PACKET_SIZE) // Too much data? + { + Serial.print(F("getNTPTime: too much data received! Length: ")); + Serial.print(avail); + Serial.print(F(". Reading ")); + Serial.print(NTP_PACKET_SIZE); + Serial.println(F(" bytes...")); + } + + if (mySARA.socketReadUDP(socketNum, NTP_PACKET_SIZE, (char *)&packetBuffer) != SARA_R5_SUCCESS) + { + Serial.println(F("getNTPTime: socketRead failed!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); + } + + // Extract the time from the reply + + Serial.print(F("getNTPTime: received ")); + Serial.print(avail); + Serial.println(F(" bytes. Extracting the time...")); + + //the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, esxtract the two words: + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + Serial.print(F("getNTPTime: seconds since Jan 1 1900 = ")); + Serial.println(secsSince1900); + + // now convert NTP time into everyday time: + Serial.print(F("getNTPTime: Unix time = ")); + + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + + // print Unix time: + Serial.println(epoch); + + // Instead of calculating the year, month, day, etc. manually, let's use time_t and tm to do it for us! + time_t dateTime = epoch; + tm *theTime = gmtime(&dateTime); + + // Load the time into y, mo, d, h, min, s + *y = theTime->tm_year - 100; // tm_year is years since 1900. Convert to years since 2000. + *mo = theTime->tm_mon + 1; //tm_mon starts at zero. Add 1 for January. + *d = theTime->tm_mday; + *h = theTime->tm_hour; + *min = theTime->tm_min; + *s = theTime->tm_sec; + + // Finish off by printing the time + Serial.print(F("getNTPTime: YY/MM/DD HH:MM:SS : ")); + if (*y < 10) Serial.print(F("0")); + Serial.print(*y); + Serial.print(F("/")); + if (*mo < 10) Serial.print(F("0")); + Serial.print(*mo); + Serial.print(F("/")); + if (*d < 10) Serial.print(F("0")); + Serial.print(*d); + Serial.print(F(" ")); + if (*h < 10) Serial.print(F("0")); + Serial.print(*h); + Serial.print(F(":")); + if (*min < 10) Serial.print(F("0")); + Serial.print(*min); + Serial.print(F(":")); + if (*s < 10) Serial.print(F("0")); + Serial.println(*s); + + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (true); // We are done! + } + + delay(100); // Wait before trying again + } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + Serial.println(F("getNTPTime: no NTP data received!")); + mySARA.socketClose(socketNum); // Be nice. Close the socket + return (false); +} diff --git a/examples/SARA-R5_Example14_AssistNowOffline/secrets.h b/examples/SARA-R5_Example14_AssistNowOffline/secrets.h new file mode 100644 index 0000000..ff875b2 --- /dev/null +++ b/examples/SARA-R5_Example14_AssistNowOffline/secrets.h @@ -0,0 +1,2 @@ +//Your AssistNow token +const char myAssistNowToken[] = "58XXXXXXXXXXXXXXXXXXYQ"; diff --git a/keywords.txt b/keywords.txt index a3d07c6..a3ad65c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -72,6 +72,8 @@ getSubscriberNo KEYWORD2 getCapabilities KEYWORD2 reset KEYWORD2 clock KEYWORD2 +setClock KEYWORD2 +autoTimeZoneForBegin KEYWORD2 autoTimeZone KEYWORD2 setUtimeMode KEYWORD2 getUtimeMode KEYWORD2 @@ -163,6 +165,8 @@ gpsGetRmc KEYWORD2 gpsRequest KEYWORD2 gpsAidingServerConf KEYWORD2 getFileContents KEYWORD2 +getFileSize KEYWORD2 +deleteFile KEYWORD2 functionality KEYWORD2 sendCustomCommandWithResponse KEYWORD2 diff --git a/library.properties b/library.properties index 4d6f0f8..fe9a712 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox SARA-R5 Arduino Library -version=1.0.5 +version=1.0.6 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 9687997..baf09b5 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -40,6 +40,7 @@ SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitDepth) _lastLocalIP = {0, 0, 0, 0}; 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; memset(_saraRXBuffer, 0, _RXBuffSize); memset(_pruneBuffer, 0, _RXBuffSize); @@ -962,6 +963,68 @@ SARA_R5_error_t SARA_R5::clock(uint8_t *y, uint8_t *mo, uint8_t *d, return err; } +SARA_R5_error_t SARA_R5::setClock(uint8_t y, uint8_t mo, uint8_t d, + uint8_t h, uint8_t min, uint8_t s, int8_t tz) +{ + //Convert y,mo,d,h,min,s,tz into a String + //Some platforms don't support sprintf correctly (for %02d or %+02d) so we need to build the String manually + //Format is "yy/MM/dd,hh:mm:ss+TZ" + //TZ can be +/- and is in increments of 15 minutes (not hours) + + String theTime = ""; + + theTime.concat(y / 10); + theTime.concat(y % 10); + theTime.concat('/'); + theTime.concat(mo / 10); + theTime.concat(mo % 10); + theTime.concat('/'); + theTime.concat(d / 10); + theTime.concat(d % 10); + theTime.concat(','); + theTime.concat(h / 10); + theTime.concat(h % 10); + theTime.concat(':'); + theTime.concat(min / 10); + theTime.concat(min % 10); + theTime.concat(':'); + theTime.concat(s / 10); + theTime.concat(s % 10); + if (tz < 0) + { + theTime.concat('-'); + tz = 0 - tz; + } + else + theTime.concat('+'); + theTime.concat(tz / 10); + theTime.concat(tz % 10); + + return (setClock(theTime)); +} + +SARA_R5_error_t SARA_R5::setClock(String theTime) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_CLOCK) + theTime.length() + 8); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\"", SARA_R5_COMMAND_CLOCK, theTime.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +void SARA_R5::autoTimeZoneForBegin(bool tz) +{ + _autoTimeZoneForBegin = tz; +} + SARA_R5_error_t SARA_R5::setUtimeMode(SARA_R5_utime_mode_t mode, SARA_R5_utime_sensor_t sensor) { SARA_R5_error_t err; @@ -2696,9 +2759,9 @@ SARA_R5_error_t SARA_R5::socketReadAvailableUDP(int socket, int *length) if (err == SARA_R5_ERROR_SUCCESS) { - char *searchPtr = strstr(response, "+UPSND: "); + char *searchPtr = strstr(response, "+USORF: "); if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+UPSND: %d,%d", + scanNum = sscanf(searchPtr, "+USORF: %d,%d", &socketStore, &readLength); if (scanNum != 2) { @@ -3963,73 +4026,199 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) SARA_R5_error_t err; char *command; char *response; - char *contentsPtr; - command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_READ_FILE) + 8 + filename.length()); + // 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(1072); // Hopefully this should be way more than enough?! (1024 + 48 extra) + 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; } - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT, 1072); + // 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 - contentsPtr = strchr(response, '\"'); // Find the third quote - contentsPtr = strchr(contentsPtr + 1, '\"'); - contentsPtr = strchr(contentsPtr + 1, '\"'); - - if (contentsPtr == NULL) + int scanned = 0; + int readFileSize = 0; + char *searchPtr = strstr(response, "+URDFILE: "); + if (searchPtr != NULL) { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } + searchPtr = strchr(searchPtr, '\"'); // Find the first quote + searchPtr = strchr(++searchPtr, '\"'); // Find the second quote - bool keepGoing = true; - int bytesRead = 0; + int scanned = sscanf(searchPtr, "\",%d,", &readFileSize); // Get the file size (again) + if (scanned == 1) + { + searchPtr = strchr(++searchPtr, '\"'); // Find the third quote - if (_printDebug == true) - _debugPort->print(F("getFileContents: file contents are \"")); + if (searchPtr == NULL) + { + if (_printDebug == true) + { + _debugPort->println(F("getFileContents: third quote not found!")); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } - while (keepGoing) - { - char c = *(++contentsPtr); // Increment contentsPtr then copy file char into c - if ((c == '\0') || (c == '\"') || (bytesRead == 1024)) - { - keepGoing = false; + int bytesRead = 0; + + while (bytesRead < readFileSize) + { + searchPtr++; // Increment searchPtr then copy file char into contents + contents->concat(*(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 { - bytesRead++; - contents->concat(c); // Append c to contents if (_printDebug == true) - _debugPort->print(c); + { + _debugPort->print(F("getFileContents: sscanf failed! scanned is ")); + _debugPort->println(scanned); + } + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; } } - if (_printDebug == true) + else { - _debugPort->println(F("\"")); - _debugPort->print(F("getFileContents: total bytes read: ")); - _debugPort->println(bytesRead); + if (_printDebug == true) + _debugPort->println(F("getFileContents: strstr failed!")); + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; } free(command); free(response); + return err; +} - return SARA_R5_ERROR_SUCCESS; +SARA_R5_error_t SARA_R5::getFileSize(String filename, int *size) +{ + SARA_R5_error_t err; + char *command; + char *response; + + command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_LIST_FILES) + filename.length() + 8); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=2,\"%s\"", SARA_R5_FILE_SYSTEM_LIST_FILES, filename.c_str()); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileSize: Fail: Error: ")); + _debugPort->print(err); + _debugPort->print(F(" Response: {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + free(command); + free(response); + return err; + } + + char *responseStart = strstr(response, "+ULSTFILE: "); + if (responseStart == NULL) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileSize: Failure: {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + int fileSize; + sscanf(responseStart, "+ULSTFILE: %d", &fileSize); + *size = fileSize; + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::deleteFile(String filename) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_DELETE_FILE) + filename.length() + 8); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\"", SARA_R5_FILE_SYSTEM_DELETE_FILE, filename.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("deleteFile: Fail: Error: ")); + _debugPort->println(err); + } + } + + free(command); + return err; } SARA_R5_error_t SARA_R5::modulePowerOff(void) @@ -4132,7 +4321,7 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, //setGpioMode(GPIO2, GNSS_SUPPLY_ENABLE); setGpioMode(GPIO6, TIME_PULSE_OUTPUT); setSMSMessageFormat(SARA_R5_MESSAGE_FORMAT_TEXT); - autoTimeZone(true); + autoTimeZone(_autoTimeZoneForBegin); for (int i = 0; i < SARA_R5_NUM_SOCKETS; i++) { socketClose(i, 100); diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index e9e8776..a4beaae 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -167,7 +167,10 @@ const char SARA_R5_GNSS_CONFIGURE_SENSOR[] = "+ULOCGNSS"; // Configure GNSS s const char SARA_R5_GNSS_CONFIGURE_LOCATION[] = "+ULOCCELL"; // Configure cellular location sensor (CellLocate®) const char SARA_R5_AIDING_SERVER_CONFIGURATION[] = "+UGSRV"; // Configure aiding server (CellLocate®) // ### File System -const char SARA_R5_FILE_SYSTEM_READ_FILE[] = "+URDFILE"; // Read a file +// TO DO: Add support for file tags. Default tag to USER +const char SARA_R5_FILE_SYSTEM_READ_FILE[] = "+URDFILE"; // Read a file +const char SARA_R5_FILE_SYSTEM_LIST_FILES[] = "+ULSTFILE"; // List of files, size of file, etc. +const char SARA_R5_FILE_SYSTEM_DELETE_FILE[] = "+UDELFILE"; // Delete a file // ### Response const char SARA_R5_RESPONSE_OK[] = "OK\r\n"; const char SARA_R5_RESPONSE_ERROR[] = "ERROR\r\n"; @@ -545,6 +548,10 @@ class SARA_R5 : public Print // TODO: Return a clock struct SARA_R5_error_t clock(uint8_t *y, uint8_t *mo, uint8_t *d, uint8_t *h, uint8_t *min, uint8_t *s, int8_t *tz); // TZ can be +/- and is in increments of 15 minutes. -28 == 7 hours behind UTC/GMT + SARA_R5_error_t setClock(String theTime); + SARA_R5_error_t setClock(uint8_t y, uint8_t mo, uint8_t d, + uint8_t h, uint8_t min, uint8_t s, int8_t tz); // TZ can be +/- and is in increments of 15 minutes. -28 == 7 hours behind UTC/GMT + void autoTimeZoneForBegin(bool enable = true); // Call autoTimeZoneForBegin(false) _before_ .begin if you want to disable the automatic time zone SARA_R5_error_t autoTimeZone(bool enable); // Enable/disable automatic time zone adjustment SARA_R5_error_t setUtimeMode(SARA_R5_utime_mode_t mode = SARA_R5_UTIME_MODE_PPS, SARA_R5_utime_sensor_t sensor = SARA_R5_UTIME_SENSOR_GNSS_LTE); // Time mode, source etc. (+UTIME) SARA_R5_error_t getUtimeMode(SARA_R5_utime_mode_t *mode, SARA_R5_utime_sensor_t *sensor); @@ -663,9 +670,9 @@ class SARA_R5 : public Print SARA_R5_gpio_mode_t getGpioMode(SARA_R5_gpio_t gpio); // IP Transport Layer - int socketOpen(SARA_R5_socket_protocol_t protocol, unsigned int localPort = 0); // Open a socket. Returns the socket number. Not required for UDP sockets. + int socketOpen(SARA_R5_socket_protocol_t protocol, unsigned int localPort = 0); // Open a socket. Returns the socket number. SARA_R5_error_t socketClose(int socket, unsigned long timeout = SARA_R5_2_MIN_TIMEOUT); // Close the socket - SARA_R5_error_t socketConnect(int socket, const char *address, unsigned int port); // TCP - connect to a remote IP Address using the specified port + SARA_R5_error_t socketConnect(int socket, const char *address, unsigned int port); // TCP - connect to a remote IP Address using the specified port. Not required for UDP sockets. SARA_R5_error_t socketConnect(int socket, IPAddress address, unsigned int port); // Write data to the specified socket. Works with binary data - but you must specify the data length when using the const char * version // Works with both TCP and UDP sockets - but socketWriteUDP is preferred for UDP and doesn't require socketOpen to be called first @@ -785,7 +792,10 @@ class SARA_R5 : public Print unsigned int gnssTypes = 65, unsigned int mode = 0, unsigned int dataType = 15); // 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 getFileSize(String filename, int *size); + SARA_R5_error_t deleteFile(String filename); // Functionality SARA_R5_error_t functionality(SARA_R5_functionality_t function = FULL_FUNCTIONALITY); @@ -812,6 +822,7 @@ class SARA_R5 : public Print IPAddress _lastLocalIP; uint8_t _maxInitDepth; uint8_t _currentInitDepth = 0; + bool _autoTimeZoneForBegin = true; #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.