From 75ea5d4db87f18c312979230f3c9dbfb52bedc0b Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Sun, 8 May 2022 18:53:14 +0200 Subject: [PATCH 01/23] better parsing of MQTT Read --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 6326 +++++++++++++++++ src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 1048 +++ 2 files changed, 7374 insertions(+) create mode 100644 src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp create mode 100644 src/SparkFun_u-blox_SARA-R5_Arduino_Library.h diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp new file mode 100644 index 0000000..795097d --- /dev/null +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -0,0 +1,6326 @@ +/* + Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker + By: Paul Clark + October 19th 2020 + + Based extensively on the: + Arduino Library for the SparkFun LTE CAT M1/NB-IoT Shield - SARA-R4 + Written by Jim Lindblom @ SparkFun Electronics, September 5, 2018 + + This Arduino library provides mechanisms to initialize and use + the SARA-R5 module over either a SoftwareSerial or hardware serial port. + + Please see LICENSE.md for the license information + +*/ + +#include + +SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitTries) +{ +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + _softSerial = NULL; +#endif + _hardSerial = NULL; + _baud = 0; + _resetPin = resetPin; + _powerPin = powerPin; + _invertPowerPin = false; + _maxInitTries = maxInitTries; + _socketListenCallback = NULL; + _socketReadCallback = NULL; + _socketReadCallbackPlus = NULL; + _socketCloseCallback = NULL; + _gpsRequestCallback = NULL; + _simStateReportCallback = NULL; + _psdActionRequestCallback = NULL; + _pingRequestCallback = NULL; + _httpCommandRequestCallback = NULL; + _mqttCommandRequestCallback = NULL; + _registrationCallback = NULL; + _epsRegistrationCallback = NULL; + _debugAtPort = NULL; + _debugPort = NULL; + _printDebug = false; + _lastRemoteIP = {0, 0, 0, 0}; + _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; + _bufferedPollReentrant = false; + _pollReentrant = false; + _saraResponseBacklogLength = 0; + _saraRXBuffer = NULL; + _pruneBuffer = NULL; + _saraResponseBacklog = NULL; +} + +SARA_R5::~SARA_R5(void) { + if (NULL != _saraRXBuffer) { + delete[] _saraRXBuffer; + _saraRXBuffer = NULL; + } + if (NULL != _pruneBuffer) { + delete[] _pruneBuffer; + _pruneBuffer = NULL; + } + if (NULL != _saraResponseBacklog) { + delete[] _saraResponseBacklog; + _saraResponseBacklog = NULL; + } +} + +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED +bool SARA_R5::begin(SoftwareSerial &softSerial, unsigned long baud) +{ + if (NULL == _saraRXBuffer) + { + _saraRXBuffer = new char[_RXBuffSize]; + if (NULL == _saraRXBuffer) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); + return false; + } + } + memset(_saraRXBuffer, 0, _RXBuffSize); + + if (NULL == _pruneBuffer) + { + _pruneBuffer = new char[_RXBuffSize]; + if (NULL == _pruneBuffer) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); + return false; + } + } + memset(_pruneBuffer, 0, _RXBuffSize); + + if (NULL == _saraResponseBacklog) + { + _saraResponseBacklog = new char[_RXBuffSize]; + if (NULL == _saraResponseBacklog) + { + 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; + + err = init(baud); + if (err == SARA_R5_ERROR_SUCCESS) + { + return true; + } + return false; +} +#endif + +bool SARA_R5::begin(HardwareSerial &hardSerial, unsigned long baud, bool doBegin) +{ + if (NULL == _saraRXBuffer) + { + _saraRXBuffer = new char[_RXBuffSize]; + if (NULL == _saraRXBuffer) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); + return false; + } + } + memset(_saraRXBuffer, 0, _RXBuffSize); + + if (NULL == _pruneBuffer) + { + _pruneBuffer = new char[_RXBuffSize]; + if (NULL == _pruneBuffer) + { + if (_printDebug == true) + _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); + return false; + } + } + memset(_pruneBuffer, 0, _RXBuffSize); + + if (NULL == _saraResponseBacklog) + { + _saraResponseBacklog = new char[_RXBuffSize]; + if (NULL == _saraResponseBacklog) + { + 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; + if (doBegin) { + _hardSerial->begin(baud); + } + err = init(baud); + if (err == SARA_R5_ERROR_SUCCESS) + { + return true; + } + return false; +} + +//Calling this function with nothing sets the debug port to Serial +//You can also call it with other streams like Serial1, SerialUSB, etc. +void SARA_R5::enableDebugging(Print &debugPort) +{ + _debugPort = &debugPort; + _printDebug = true; +} + +//Calling this function with nothing sets the debug port to Serial +//You can also call it with other streams like Serial1, SerialUSB, etc. +void SARA_R5::enableAtDebugging(Print &debugPort) +{ + _debugAtPort = &debugPort; + _printAtDebug = true; +} + +// This function was originally written by Matthew Menze for the LTE Shield (SARA-R4) library +// See: https://github.com/sparkfun/SparkFun_LTE_Shield_Arduino_Library/pull/8 +// It does the same job as ::poll but also processed any 'old' data stored in the backlog first +// 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; + unsigned long timeIn = millis(); + char *event; + int backlogLen = _saraResponseBacklogLength; + + memset(_saraRXBuffer, 0, _RXBuffSize); // Clear _saraRXBuffer + + // Does the backlog contain any data? If it does, copy it into _saraRXBuffer and then clear the backlog + if (_saraResponseBacklogLength > 0) + { + //The backlog also logs reads from other tasks like transmitting. + if (_printDebug == true) + { + _debugPort->print(F("bufferedPoll: backlog found! backlogLen is ")); + _debugPort->println(_saraResponseBacklogLength); + } + memcpy(_saraRXBuffer + avail, _saraResponseBacklog, _saraResponseBacklogLength); + avail += _saraResponseBacklogLength; + memset(_saraResponseBacklog, 0, _RXBuffSize); // Clear the backlog making sure it is NULL-terminated + _saraResponseBacklogLength = 0; + } + + if ((hwAvailable() > 0) || (backlogLen > 0)) // If either new data is available, or backlog had data. + { + //Check for incoming serial data. Copy it into the backlog + + // Important note: + // On ESP32, Serial.available only provides an update every ~120 bytes during the reception of long messages: + // https://gitter.im/espressif/arduino-esp32?at=5e25d6370a1cf54144909c85 + // Be aware that if a long message is being received, the code below will timeout after _rxWindowMillis = 2 millis. + // At 115200 baud, hwAvailable takes ~120 * 10 / 115200 = 10.4 millis before it indicates that data is being received. + + while (((millis() - timeIn) < _rxWindowMillis) && (avail < _RXBuffSize)) + { + if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL + { + c = readChar(); + // bufferedPoll is only interested in the URCs. + // The URCs are all readable. + // strtok does not like NULL characters. + // So we need to make sure no NULL characters are added to _saraRXBuffer + if (c == '\0') + c = '0'; // Convert any NULLs to ASCII Zeros + _saraRXBuffer[avail++] = c; + timeIn = millis(); + } else { + yield(); + } + } + + // _saraRXBuffer now contains the backlog (if any) and the new serial data (if any) + + // A health warning about strtok: + // strtok will convert any delimiters it finds ("\r\n" in our case) into NULL characters. + // Also, be very careful that you do not use strtok within an strtok while loop. + // The next call of strtok(NULL, ...) in the outer loop will use the pointer saved from the inner loop! + // In our case, strtok is also used in pruneBacklog, which is called by waitForRespone or sendCommandWithResponse, + // which is called by the parse functions called by processURCEvent... + // The solution is to use strtok_r - the reentrant version of strtok + + char *preservedEvent; + event = strtok_r(_saraRXBuffer, "\r\n", &preservedEvent); // Look for an 'event' (_saraRXBuffer contains something ending in \r\n) + + if (event != NULL) + if (_printDebug == true) + _debugPort->println(F("bufferedPoll: event(s) found! ===>")); + + while (event != NULL) // Keep going until all events have been processed + { + if (_printDebug == true) + { + _debugPort->print(F("bufferedPoll: start of event: ")); + _debugPort->println(event); + } + + //Process the event + bool latestHandled = processURCEvent((const char *)event); + if (latestHandled) { + if ((true == _printAtDebug) && (NULL != event)) { + _debugAtPort->print(event); + } + handled = true; // handled will be true if latestHandled has ever been true + } + if ((_saraResponseBacklogLength > 0) && ((avail + _saraResponseBacklogLength) < _RXBuffSize)) // Has any new data been added to the backlog? + { + if (_printDebug == true) + { + _debugPort->println(F("bufferedPoll: new backlog added!")); + } + memcpy(_saraRXBuffer + avail, _saraResponseBacklog, _saraResponseBacklogLength); + avail += _saraResponseBacklogLength; + memset(_saraResponseBacklog, 0, _RXBuffSize); //Clear out the backlog buffer again. + _saraResponseBacklogLength = 0; + } + + //Walk through any remaining events + event = strtok_r(NULL, "\r\n", &preservedEvent); + + if (_printDebug == true) + _debugPort->println(F("bufferedPoll: end of event")); //Just to denote end of processing event. + + if (event == NULL) + if (_printDebug == true) + _debugPort->println(F("bufferedPoll: <=== end of event(s)!")); + } + } + + 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) +{ + { // URC: +UUSORD (Read Socket Data) + int socket, length; + int ret = sscanf(event, "+UUSORD: %d,%d", &socket, &length); + if (ret == 2) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: read socket data")); + // From the SARA_R5 AT Commands Manual: + // "For the UDP socket type the URC +UUSORD: , notifies that a UDP packet has been received, + // either when buffer is empty or after a UDP packet has been read and one or more packets are stored in the + // buffer." + // So we need to check if this is a TCP socket or a UDP socket: + // If UDP, we call parseSocketReadIndicationUDP. + // Otherwise, we call parseSocketReadIndication. + if (_lastSocketProtocol[socket] == SARA_R5_UDP) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: received +UUSORD but socket is UDP. Calling parseSocketReadIndicationUDP")); + parseSocketReadIndicationUDP(socket, length); + } + else + parseSocketReadIndication(socket, length); + return true; + } + } + { // URC: +UUSORF (Receive From command (UDP only)) + int socket, length; + int ret = sscanf(event, "+UUSORF: %d,%d", &socket, &length); + if (ret == 2) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: UDP receive")); + parseSocketReadIndicationUDP(socket, length); + return true; + } + } + { // URC: +UUSOLI (Set Listening Socket) + int socket = 0; + int listenSocket = 0; + unsigned int port = 0; + unsigned int listenPort = 0; + IPAddress remoteIP = {0,0,0,0}; + IPAddress localIP = {0,0,0,0}; + int remoteIPstore[4] = {0,0,0,0}; + int localIPstore[4] = {0,0,0,0}; + + int ret = sscanf(event, + "+UUSOLI: %d,\"%d.%d.%d.%d\",%u,%d,\"%d.%d.%d.%d\",%u", + &socket, + &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], + &port, &listenSocket, + &localIPstore[0], &localIPstore[1], &localIPstore[2], &localIPstore[3], + &listenPort); + for (int i = 0; i <= 3; i++) + { + if (ret >= 5) + remoteIP[i] = (uint8_t)remoteIPstore[i]; + if (ret >= 11) + localIP[i] = (uint8_t)localIPstore[i]; + } + if (ret >= 5) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: socket listen")); + parseSocketListenIndication(listenSocket, localIP, listenPort, socket, remoteIP, port); + return true; + } + } + { // URC: +UUSOCL (Close Socket) + int socket; + int ret = sscanf(event, "+UUSOCL: %d", &socket); + if (ret == 1) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: socket close")); + if ((socket >= 0) && (socket <= 6)) + { + if (_socketCloseCallback != NULL) + { + _socketCloseCallback(socket); + } + } + return true; + } + } + { // URC: +UULOC (Localization information - CellLocate and hybrid positioning) + ClockData clck; + PositionData gps; + SpeedData spd; + unsigned long uncertainty; + int scanNum; + int latH, lonH, alt; + unsigned int speedU, cogU; + char latL[10], lonL[10]; + int dateStore[5]; + + // Maybe we should also scan for +UUGIND and extract the activated gnss system? + + // This assumes the ULOC response type is "0" or "1" - as selected by gpsRequest detailed + scanNum = sscanf(event, + "+UULOC: %d/%d/%d,%d:%d:%d.%d,%d.%[^,],%d.%[^,],%d,%lu,%u,%u,%*s", + &dateStore[0], &dateStore[1], &clck.date.year, + &dateStore[2], &dateStore[3], &dateStore[4], &clck.time.ms, + &latH, latL, &lonH, lonL, &alt, &uncertainty, + &speedU, &cogU); + clck.date.day = dateStore[0]; + clck.date.month = dateStore[1]; + clck.time.hour = dateStore[2]; + clck.time.minute = dateStore[3]; + clck.time.second = dateStore[4]; + + if (scanNum >= 13) + { + // Found a Location string! + if (_printDebug == true) + { + _debugPort->println(F("processReadEvent: location")); + } + + if (latH >= 0) + gps.lat = (float)latH + ((float)atol(latL) / pow(10, strlen(latL))); + else + gps.lat = (float)latH - ((float)atol(latL) / pow(10, strlen(latL))); + if (lonH >= 0) + gps.lon = (float)lonH + ((float)atol(lonL) / pow(10, strlen(lonL))); + else + gps.lon = (float)lonH - ((float)atol(lonL) / pow(10, strlen(lonL))); + gps.alt = (float)alt; + if (scanNum >= 15) // If detailed response, get speed data + { + spd.speed = (float)speedU; + spd.cog = (float)cogU; + } + + // if (_printDebug == true) + // { + // _debugPort->print(F("processReadEvent: location: lat: ")); + // _debugPort->print(gps.lat, 7); + // _debugPort->print(F(" lon: ")); + // _debugPort->print(gps.lon, 7); + // _debugPort->print(F(" alt: ")); + // _debugPort->print(gps.alt, 2); + // _debugPort->print(F(" speed: ")); + // _debugPort->print(spd.speed, 2); + // _debugPort->print(F(" cog: ")); + // _debugPort->println(spd.cog, 2); + // } + + if (_gpsRequestCallback != NULL) + { + _gpsRequestCallback(clck, gps, spd, uncertainty); + } + + return true; + } + } + { // URC: +UUSIMSTAT (SIM Status) + SARA_R5_sim_states_t state; + int scanNum; + int stateStore; + + scanNum = sscanf(event, "+UUSIMSTAT:%d", &stateStore); // Note: no space after the colon! + + if (scanNum == 1) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: SIM status")); + + state = (SARA_R5_sim_states_t)stateStore; + + if (_simStateReportCallback != NULL) + { + _simStateReportCallback(state); + } + + return true; + } + } + { // URC: +UUPSDA (Packet Switched Data Action) + int result; + IPAddress remoteIP = {0, 0, 0, 0}; + int scanNum; + int remoteIPstore[4]; + + scanNum = sscanf(event, "+UUPSDA: %d,\"%d.%d.%d.%d\"", + &result, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3]); + + if (scanNum == 5) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: packet switched data action")); + + for (int i = 0; i <= 3; i++) + { + remoteIP[i] = (uint8_t)remoteIPstore[i]; + } + + if (_psdActionRequestCallback != NULL) + { + _psdActionRequestCallback(result, remoteIP); + } + + return true; + } + } + { // URC: +UUHTTPCR (HTTP Command Result) + int profile, command, result; + int scanNum; + + scanNum = sscanf(event, "+UUHTTPCR: %d,%d,%d", &profile, &command, &result); + + if (scanNum == 3) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: HTTP command result")); + + if ((profile >= 0) && (profile < SARA_R5_NUM_HTTP_PROFILES)) + { + if (_httpCommandRequestCallback != NULL) + { + _httpCommandRequestCallback(profile, command, result); + } + } + + return true; + } + } + { // URC: +UUMQTTC (HTTP Command Result) + int command, result; + int scanNum; + int qos = -1; + String topic; + scanNum = sscanf(event, "+UUMQTTC: %d,%d", &command, &result); + if ((scanNum == 2) && (command == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) { + char topicC[100] = ""; + scanNum = sscanf(event, "+UUMQTTC: %*d,%*d,%d,\"%[^\"]\"", &qos, topicC); + topic = topicC; + } + if ((scanNum == 2) || (scanNum == 4)) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: MQTT command result")); + + if (_mqttCommandRequestCallback != NULL) + { + _mqttCommandRequestCallback(command, result); + } + + return true; + } + } + { // URC: +UUPING (Ping Result) + int retry = 0; + int p_size = 0; + int ttl = 0; + String remote_host = ""; + IPAddress remoteIP = {0, 0, 0, 0}; + long rtt = 0; + int scanNum; + const char *searchPtr = event; + + // Try to extract the UUPING retries and payload size + scanNum = sscanf(searchPtr, "+UUPING: %d,%d,", &retry, &p_size); + + if (scanNum == 2) + { + if (_printDebug == true) + { + _debugPort->println(F("processReadEvent: ping")); + } + + searchPtr = strchr(++searchPtr, '\"'); // Search to the first quote + + // Extract the remote host name, stop at the next quote + while ((*(++searchPtr) != '\"') && (*searchPtr != '\0')) + { + remote_host.concat(*(searchPtr)); + } + + if (*searchPtr != '\0') // Make sure we found a quote + { + int remoteIPstore[4]; + scanNum = sscanf(searchPtr, "\",\"%d.%d.%d.%d\",%d,%ld", + &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], &ttl, &rtt); + for (int i = 0; i <= 3; i++) + { + remoteIP[i] = (uint8_t)remoteIPstore[i]; + } + + if (scanNum == 6) // Make sure we extracted enough data + { + if (_pingRequestCallback != NULL) + { + _pingRequestCallback(retry, p_size, remote_host, remoteIP, ttl, rtt); + } + } + } + return true; + } + } + { // URC: +A + int status = 0; + unsigned int lac = 0, ci = 0, Act = 0; + int scanNum = sscanf(event, "+CREG: %d,\"%4x\",\"%4x\",%d", &status, &lac, &ci, &Act); + if (scanNum == 4) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: CREG")); + + if (_registrationCallback != NULL) + { + _registrationCallback((SARA_R5_registration_status_t)status, lac, ci, Act); + } + + return true; + } + } + { // URC: +CEREG + int status = 0; + unsigned int tac = 0, ci = 0, Act = 0; + int scanNum = sscanf(event, "+CEREG: %d,\"%4x\",\"%4x\",%d", &status, &tac, &ci, &Act); + if (scanNum == 4) + { + if (_printDebug == true) + _debugPort->println(F("processReadEvent: CEREG")); + + if (_epsRegistrationCallback != NULL) + { + _epsRegistrationCallback((SARA_R5_registration_status_t)status, tac, ci, Act); + } + + return true; + } + } + + return false; +} + +// This is the original poll function. +// It is 'blocking' - it does not return when serial data is available until it receives a `\n`. +// ::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; + + memset(_saraRXBuffer, 0, _RXBuffSize); // Clear _saraRXBuffer + + if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL + { + while (c != '\n') // Copy characters into _saraRXBuffer. Stop at the first new line + { + if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL + { + c = readChar(); + _saraRXBuffer[avail++] = c; + } else { + yield(); + } + } + + // Now search for all supported URC's + handled = processURCEvent(_saraRXBuffer); + if (handled && (true == _printAtDebug)) { + _debugAtPort->write(_saraRXBuffer, avail); + } + if ((handled == false) && (strlen(_saraRXBuffer) > 2)) + { + if (_printDebug == true) + { + _debugPort->print(F("poll: ")); + _debugPort->println(_saraRXBuffer); + } + } + else + { + } + } + + _pollReentrant = false; + + return handled; +} + +void SARA_R5::setSocketListenCallback(void (*socketListenCallback)(int, IPAddress, unsigned int, int, IPAddress, unsigned int)) +{ + _socketListenCallback = socketListenCallback; +} + +void SARA_R5::setSocketReadCallback(void (*socketReadCallback)(int, String)) +{ + _socketReadCallback = socketReadCallback; +} + +void SARA_R5::setSocketReadCallbackPlus(void (*socketReadCallbackPlus)(int, const char *, int, IPAddress, int)) // socket, data, length, remoteAddress, remotePort +{ + _socketReadCallbackPlus = socketReadCallbackPlus; +} + +void SARA_R5::setSocketCloseCallback(void (*socketCloseCallback)(int)) +{ + _socketCloseCallback = socketCloseCallback; +} + +void SARA_R5::setGpsReadCallback(void (*gpsRequestCallback)(ClockData time, + PositionData gps, SpeedData spd, unsigned long uncertainty)) +{ + _gpsRequestCallback = gpsRequestCallback; +} + +void SARA_R5::setSIMstateReportCallback(void (*simStateReportCallback)(SARA_R5_sim_states_t state)) +{ + _simStateReportCallback = simStateReportCallback; +} + +void SARA_R5::setPSDActionCallback(void (*psdActionRequestCallback)(int result, IPAddress ip)) +{ + _psdActionRequestCallback = psdActionRequestCallback; +} + +void SARA_R5::setPingCallback(void (*pingRequestCallback)(int retry, int p_size, String remote_hostname, IPAddress ip, int ttl, long rtt)) +{ + _pingRequestCallback = pingRequestCallback; +} + +void SARA_R5::setHTTPCommandCallback(void (*httpCommandRequestCallback)(int profile, int command, int result)) +{ + _httpCommandRequestCallback = httpCommandRequestCallback; +} + +void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)) +{ + _mqttCommandRequestCallback = mqttCommandRequestCallback; +} + +SARA_R5_error_t SARA_R5::setRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, unsigned int lac, unsigned int ci, int Act)) +{ + _registrationCallback = registrationCallback; + + char *command = sara_r5_calloc_char(strlen(SARA_R5_REGISTRATION_STATUS) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_REGISTRATION_STATUS, 2/*enable URC with location*/); + SARA_R5_error_t err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setEpsRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, unsigned int tac, unsigned int ci, int Act)) +{ + _epsRegistrationCallback = registrationCallback; + + char *command = sara_r5_calloc_char(strlen(SARA_R5_EPSREGISTRATION_STATUS) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_EPSREGISTRATION_STATUS, 2/*enable URC with location*/); + SARA_R5_error_t err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +size_t SARA_R5::write(uint8_t c) +{ + return hwWrite(c); +} + +size_t SARA_R5::write(const char *str) +{ + return hwPrint(str); +} + +size_t SARA_R5::write(const char *buffer, size_t size) +{ + return hwWriteData(buffer, size); +} + +SARA_R5_error_t SARA_R5::at(void) +{ + SARA_R5_error_t err; + + err = sendCommandWithResponse(NULL, SARA_R5_RESPONSE_OK, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + return err; +} + +SARA_R5_error_t SARA_R5::enableEcho(bool enable) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_ECHO) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s%d", SARA_R5_COMMAND_ECHO, enable ? 1 : 0); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +String SARA_R5::getManufacturerID(void) +{ + char *response; + char idResponse[16] = {0x00}; // E.g. u-blox + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_MANU_ID, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) + { + memset(idResponse, 0, 16); + } + } + free(response); + return String(idResponse); +} + +String SARA_R5::getModelID(void) +{ + char *response; + char idResponse[16] = {0x00}; // E.g. SARA-R510M8Q + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_MODEL_ID, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) + { + memset(idResponse, 0, 16); + } + } + free(response); + return String(idResponse); +} + +String SARA_R5::getFirmwareVersion(void) +{ + char *response; + char idResponse[16] = {0x00}; // E.g. 11.40 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_FW_VER_ID, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) + { + memset(idResponse, 0, 16); + } + } + free(response); + return String(idResponse); +} + +String SARA_R5::getSerialNo(void) +{ + char *response; + char idResponse[16] = {0x00}; // E.g. 357520070120767 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_SERIAL_NO, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) + { + memset(idResponse, 0, 16); + } + } + free(response); + return String(idResponse); +} + +String SARA_R5::getIMEI(void) +{ + char *response; + char imeiResponse[16] = {0x00}; // E.g. 004999010640000 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_IMEI, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", imeiResponse) != 1) + { + memset(imeiResponse, 0, 16); + } + } + free(response); + return String(imeiResponse); +} + +String SARA_R5::getIMSI(void) +{ + char *response; + char imsiResponse[16] = {0x00}; // E.g. 222107701772423 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_IMSI, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n%s\r\n", imsiResponse) != 1) + { + memset(imsiResponse, 0, 16); + } + } + free(response); + return String(imsiResponse); +} + +String SARA_R5::getCCID(void) +{ + char *response; + char ccidResponse[21] = {0x00}; // E.g. +CCID: 8939107900010087330 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_CCID, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n+CCID: %s", ccidResponse) != 1) + { + memset(ccidResponse, 0, 21); + } + } + free(response); + return String(ccidResponse); +} + +String SARA_R5::getSubscriberNo(void) +{ + char *response; + char idResponse[128] = {0x00}; // E.g. +CNUM: "ABCD . AAA","123456789012",129 + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_CNUM, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_10_SEC_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n+CNUM: %s", idResponse) != 1) + { + memset(idResponse, 0, 128); + } + } + free(response); + return String(idResponse); +} + +String SARA_R5::getCapabilities(void) +{ + char *response; + char idResponse[128] = {0x00}; // E.g. +GCAP: +FCLASS, +CGSM + SARA_R5_error_t err; + + response = sara_r5_calloc_char(minimumResponseAllocation); + + err = sendCommandWithResponse(SARA_R5_COMMAND_REQ_CAP, + SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (sscanf(response, "\r\n+GCAP: %s", idResponse) != 1) + { + memset(idResponse, 0, 128); + } + } + free(response); + return String(idResponse); +} + +SARA_R5_error_t SARA_R5::reset(void) +{ + SARA_R5_error_t err; + + err = functionality(SILENT_RESET_WITH_SIM); + if (err == SARA_R5_ERROR_SUCCESS) + { + // Reset will set the baud rate back to 115200 + //beginSerial(9600); + err = SARA_R5_ERROR_INVALID; + while (err != SARA_R5_ERROR_SUCCESS) + { + beginSerial(SARA_R5_DEFAULT_BAUD_RATE); + setBaud(_baud); + beginSerial(_baud); + err = at(); + } + return init(_baud); + } + return err; +} + +String SARA_R5::clock(void) +{ + SARA_R5_error_t err; + char *command; + char *response; + char *clockBegin; + char *clockEnd; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_CLOCK) + 2); + if (command == NULL) + return ""; + sprintf(command, "%s?", SARA_R5_COMMAND_CLOCK); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return ""; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return ""; + } + + // Response format: \r\n+CCLK: "YY/MM/DD,HH:MM:SS-TZ"\r\n\r\nOK\r\n + clockBegin = strchr(response, '\"'); // Find first quote + if (clockBegin == NULL) + { + free(command); + free(response); + return ""; + } + clockBegin += 1; // Increment pointer to begin at first number + clockEnd = strchr(clockBegin, '\"'); // Find last quote + if (clockEnd == NULL) + { + free(command); + free(response); + return ""; + } + *(clockEnd) = '\0'; // Set last quote to null char -- end string + + String clock = String(clockBegin); // Extract the clock as a String _before_ freeing response + + free(command); + free(response); + + return (clock); +} + +SARA_R5_error_t SARA_R5::clock(uint8_t *y, uint8_t *mo, uint8_t *d, + uint8_t *h, uint8_t *min, uint8_t *s, int8_t *tz) +{ + SARA_R5_error_t err; + char *command; + char *response; + char tzPlusMinus; + int scanNum = 0; + + int iy, imo, id, ih, imin, is, itz; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_CLOCK) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_COMMAND_CLOCK); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + // Response format (if TZ is negative): \r\n+CCLK: "YY/MM/DD,HH:MM:SS-TZ"\r\n\r\nOK\r\n + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+CCLK: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+CCLK: \"%d/%d/%d,%d:%d:%d%c%d\"\r\n", + &iy, &imo, &id, &ih, &imin, &is, &tzPlusMinus, &itz); + if (scanNum == 8) + { + *y = iy; + *mo = imo; + *d = id; + *h = ih; + *min = imin; + *s = is; + if (tzPlusMinus == '-') + *tz = 0 - itz; + else + *tz = itz; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + 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_OR_ERROR, + 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; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_TIME) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (mode == SARA_R5_UTIME_MODE_STOP) // stop UTIME does not require a sensor + sprintf(command, "%s=%d", SARA_R5_GNSS_REQUEST_TIME, mode); + else + sprintf(command, "%s=%d,%d", SARA_R5_GNSS_REQUEST_TIME, mode, sensor); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_10_SEC_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getUtimeMode(SARA_R5_utime_mode_t *mode, SARA_R5_utime_sensor_t *sensor) +{ + SARA_R5_error_t err; + char *command; + char *response; + + SARA_R5_utime_mode_t m; + SARA_R5_utime_sensor_t s; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_TIME) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_GNSS_REQUEST_TIME); + + 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_OR_ERROR, + response, SARA_R5_10_SEC_TIMEOUT); + + // Response format: \r\n+UTIME: [,]\r\n\r\nOK\r\n + if (err == SARA_R5_ERROR_SUCCESS) + { + int mStore, sStore, scanned = 0; + char *searchPtr = strstr(response, "+UTIME: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+UTIME: %d,%d\r\n", &mStore, &sStore); + m = (SARA_R5_utime_mode_t)mStore; + s = (SARA_R5_utime_sensor_t)sStore; + if (scanned == 2) + { + *mode = m; + *sensor = s; + } + else if (scanned == 1) + { + *mode = m; + *sensor = SARA_R5_UTIME_SENSOR_NONE; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::setUtimeIndication(SARA_R5_utime_urc_configuration_t config) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_INDICATION) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_GNSS_TIME_INDICATION, config); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getUtimeIndication(SARA_R5_utime_urc_configuration_t *config) +{ + SARA_R5_error_t err; + char *command; + char *response; + + SARA_R5_utime_urc_configuration_t c; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_INDICATION) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_GNSS_TIME_INDICATION); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + // Response format: \r\n+UTIMEIND: \r\n\r\nOK\r\n + if (err == SARA_R5_ERROR_SUCCESS) + { + int cStore, scanned = 0; + char *searchPtr = strstr(response, "+UTIMEIND: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+UTIMEIND: %d\r\n", &cStore); + c = (SARA_R5_utime_urc_configuration_t)cStore; + if (scanned == 1) + { + *config = c; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::setUtimeConfiguration(int32_t offsetNanoseconds, int32_t offsetSeconds) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_CONFIGURATION) + 48); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) + sprintf(command, "%s=%d,%d", SARA_R5_GNSS_TIME_CONFIGURATION, offsetNanoseconds, offsetSeconds); +#else + sprintf(command, "%s=%ld,%ld", SARA_R5_GNSS_TIME_CONFIGURATION, offsetNanoseconds, offsetSeconds); +#endif + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getUtimeConfiguration(int32_t *offsetNanoseconds, int32_t *offsetSeconds) +{ + SARA_R5_error_t err; + char *command; + char *response; + + int32_t ons; + int32_t os; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_CONFIGURATION) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_GNSS_TIME_CONFIGURATION); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + // Response format: \r\n+UTIMECFG: ,\r\n\r\nOK\r\n + if (err == SARA_R5_ERROR_SUCCESS) + { + int scanned = 0; + char *searchPtr = strstr(response, "+UTIMECFG: "); + if (searchPtr != NULL) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) + scanned = sscanf(searchPtr, "+UTIMECFG: %d,%d\r\n", &ons, &os); +#else + scanned = sscanf(searchPtr, "+UTIMECFG: %ld,%ld\r\n", &ons, &os); +#endif + if (scanned == 2) + { + *offsetNanoseconds = ons; + *offsetSeconds = os; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::autoTimeZone(bool enable) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_AUTO_TZ) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_COMMAND_AUTO_TZ, enable ? 1 : 0); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +int8_t SARA_R5::rssi(void) +{ + char *command; + char *response; + SARA_R5_error_t err; + int rssi; + + command = sara_r5_calloc_char(strlen(SARA_R5_SIGNAL_QUALITY) + 1); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s", SARA_R5_SIGNAL_QUALITY); + + 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_OR_ERROR, response, 10000, + minimumResponseAllocation, AT_COMMAND); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return -1; + } + + int scanned = 0; + char *searchPtr = strstr(response, "+CSQ: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+CSQ: %d,%*d", &rssi); + if (scanned != 1) + { + rssi = -1; + } + + free(command); + free(response); + return rssi; +} + +SARA_R5_registration_status_t SARA_R5::registration(bool eps) +{ + char *command; + char *response; + SARA_R5_error_t err; + int status; + const char* tag = eps ? SARA_R5_EPSREGISTRATION_STATUS : SARA_R5_REGISTRATION_STATUS; + command = sara_r5_calloc_char(strlen(tag) + 3); + if (command == NULL) + return SARA_R5_REGISTRATION_INVALID; + sprintf(command, "%s?", tag); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_REGISTRATION_INVALID; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT, + minimumResponseAllocation, AT_COMMAND); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return SARA_R5_REGISTRATION_INVALID; + } + + int scanned = 0; + const char *startTag = eps ? "+CEREG: " : "+CREG: "; + char *searchPtr = strstr(response, startTag); + if (searchPtr != NULL) { + const char *format = eps ? "+CEREG: %*d,%d" : "+CREG: %*d,%d"; + scanned = sscanf(searchPtr, format, &status); + } + if (scanned != 1) + status = SARA_R5_REGISTRATION_INVALID; + + free(command); + free(response); + return (SARA_R5_registration_status_t)status; +} + +bool SARA_R5::setNetworkProfile(mobile_network_operator_t mno, bool autoReset, bool urcNotification) +{ + mobile_network_operator_t currentMno; + + // Check currently set MNO profile + if (getMNOprofile(¤tMno) != SARA_R5_ERROR_SUCCESS) + { + return false; + } + + if (currentMno == mno) + { + return true; + } + + // Disable transmit and receive so we can change operator + if (functionality(MINIMUM_FUNCTIONALITY) != SARA_R5_ERROR_SUCCESS) + { + return false; + } + + if (setMNOprofile(mno, autoReset, urcNotification) != SARA_R5_ERROR_SUCCESS) + { + return false; + } + + if (reset() != SARA_R5_ERROR_SUCCESS) + { + return false; + } + + return true; +} + +mobile_network_operator_t SARA_R5::getNetworkProfile(void) +{ + mobile_network_operator_t mno; + SARA_R5_error_t err; + + err = getMNOprofile(&mno); + if (err != SARA_R5_ERROR_SUCCESS) + { + return MNO_INVALID; + } + return mno; +} + +SARA_R5_error_t SARA_R5::setAPN(String apn, uint8_t cid, SARA_R5_pdp_type pdpType) +{ + SARA_R5_error_t err; + char *command; + char pdpStr[8]; + + memset(pdpStr, 0, 8); + + if (cid >= 8) + return SARA_R5_ERROR_UNEXPECTED_PARAM; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_DEF) + strlen(apn.c_str()) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + switch (pdpType) + { + case PDP_TYPE_INVALID: + free(command); + return SARA_R5_ERROR_UNEXPECTED_PARAM; + break; + case PDP_TYPE_IP: + memcpy(pdpStr, "IP", 2); + break; + case PDP_TYPE_NONIP: + memcpy(pdpStr, "NONIP", 5); + break; + case PDP_TYPE_IPV4V6: + memcpy(pdpStr, "IPV4V6", 6); + break; + case PDP_TYPE_IPV6: + memcpy(pdpStr, "IPV6", 4); + break; + default: + free(command); + return SARA_R5_ERROR_UNEXPECTED_PARAM; + break; + } + if (apn == NULL) + { + if (_printDebug == true) + _debugPort->println(F("setAPN: NULL")); + sprintf(command, "%s=%d,\"%s\",\"\"", SARA_R5_MESSAGE_PDP_DEF, + cid, pdpStr); + } + else + { + if (_printDebug == true) + { + _debugPort->print(F("setAPN: ")); + _debugPort->println(apn); + } + sprintf(command, "%s=%d,\"%s\",\"%s\"", SARA_R5_MESSAGE_PDP_DEF, + cid, pdpStr, apn.c_str()); + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + + return err; +} + +// Return the Access Point Name and IP address for the chosen context identifier +SARA_R5_error_t SARA_R5::getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp_type* pdpType) +{ + SARA_R5_error_t err; + char *command; + char *response; + + if (cid > SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_DEF) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_MESSAGE_PDP_DEF); + + response = sara_r5_calloc_char(1024); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT, 1024); + + if (err == SARA_R5_ERROR_SUCCESS) + { + // Example: + // +CGDCONT: 0,"IP","payandgo.o2.co.uk","0.0.0.0",0,0,0,0,0,0,0,0,0,0 + // +CGDCONT: 1,"IP","payandgo.o2.co.uk.mnc010.mcc234.gprs","10.160.182.234",0,0,0,2,0,0,0,0,0,0 + int rcid = -1; + char *searchPtr = response; + + bool keepGoing = true; + while (keepGoing == true) + { + int scanned = 0; + // Find the first/next occurrence of +CGDCONT: + searchPtr = strstr(searchPtr, "+CGDCONT: "); + if (searchPtr != NULL) + { + char strPdpType[10]; + char strApn[128]; + int ipOct[4]; + + searchPtr += strlen("+CGDCONT: "); // Point to the cid + scanned = sscanf(searchPtr, "%d,\"%[^\"]\",\"%[^\"]\",\"%d.%d.%d.%d", + &rcid, strPdpType, strApn, + &ipOct[0], &ipOct[1], &ipOct[2], &ipOct[3]); + if ((scanned == 7) && (rcid == cid)) { + if (apn) *apn = strApn; + for (int o = 0; ip && (o < 4); o++) + { + (*ip)[o] = (uint8_t)ipOct[o]; + } + if (pdpType) { + *pdpType = (0 == strcmp(strPdpType, "IPV4V6")) ? PDP_TYPE_IPV4V6 : + (0 == strcmp(strPdpType, "IPV6")) ? PDP_TYPE_IPV6 : + (0 == strcmp(strPdpType, "IP")) ? PDP_TYPE_IP : + PDP_TYPE_INVALID; + } + keepGoing = false; + } + } + else // We don't have a match so let's clear the APN and IP address + { + if (apn) *apn = ""; + if (pdpType) *pdpType = PDP_TYPE_INVALID; + if (ip) *ip = {0, 0, 0, 0}; + keepGoing = false; + } + } + } + else + { + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::getSimStatus(String* code) +{ + SARA_R5_error_t err; + char *command; + char *response; + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_SIMPIN) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_COMMAND_SIMPIN); + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + int scanned = 0; + char c[16]; + char *searchPtr = strstr(response, "+CPIN: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+CPIN: %s\r\n", c); + if (scanned == 1) + { + if(code) + *code = c; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::setSimPin(String pin) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_SIMPIN) + 4 + pin.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\"", SARA_R5_COMMAND_SIMPIN, pin.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setSIMstateReportingMode(int mode) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_SIM_STATE) + 4); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_SIM_STATE, mode); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getSIMstateReportingMode(int *mode) +{ + SARA_R5_error_t err; + char *command; + char *response; + + int m; + + command = sara_r5_calloc_char(strlen(SARA_R5_SIM_STATE) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_SIM_STATE); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + int scanned = 0; + char *searchPtr = strstr(response, "+USIMSTAT: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+USIMSTAT: %d\r\n", &m); + if (scanned == 1) + { + *mode = m; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} + +const char *PPP_L2P[5] = { + "", + "PPP", + "M-HEX", + "M-RAW_IP", + "M-OPT-PPP", +}; + +SARA_R5_error_t SARA_R5::enterPPP(uint8_t cid, char dialing_type_char, + unsigned long dialNumber, SARA_R5::SARA_R5_l2p_t l2p) +{ + SARA_R5_error_t err; + char *command; + + if ((dialing_type_char != 0) && (dialing_type_char != 'T') && + (dialing_type_char != 'P')) + { + return SARA_R5_ERROR_UNEXPECTED_PARAM; + } + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_ENTER_PPP) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (dialing_type_char != 0) + { + sprintf(command, "%s%c*%lu**%s*%u#", SARA_R5_MESSAGE_ENTER_PPP, dialing_type_char, + dialNumber, PPP_L2P[l2p], (unsigned int)cid); + } + else + { + sprintf(command, "%s*%lu**%s*%u#", SARA_R5_MESSAGE_ENTER_PPP, + dialNumber, PPP_L2P[l2p], (unsigned int)cid); + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_CONNECT, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +uint8_t SARA_R5::getOperators(struct operator_stats *opRet, int maxOps) +{ + SARA_R5_error_t err; + char *command; + char *response; + uint8_t opsSeen = 0; + + command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=?", SARA_R5_OPERATOR_SELECTION); + + int responseSize = (maxOps + 1) * 48; + response = sara_r5_calloc_char(responseSize); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // AT+COPS maximum response time is 3 minutes (180000 ms) + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, + SARA_R5_3_MIN_TIMEOUT, responseSize); + + // Sample responses: + // +COPS: (3,"Verizon Wireless","VzW","311480",8),,(0,1,2,3,4),(0,1,2) + // +COPS: (1,"313 100","313 100","313100",8),(2,"AT&T","AT&T","310410",8),(3,"311 480","311 480","311480",8),,(0,1,2,3,4),(0,1,2) + + if (_printDebug == true) + { + _debugPort->print(F("getOperators: Response: {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *opBegin; + char *opEnd; + int op = 0; + int stat; + char longOp[26]; + char shortOp[11]; + int act; + unsigned long numOp; + + opBegin = response; + + for (; op < maxOps; op++) + { + opBegin = strchr(opBegin, '('); + if (opBegin == NULL) + break; + opEnd = strchr(opBegin, ')'); + if (opEnd == NULL) + break; + + int sscanRead = sscanf(opBegin, "(%d,\"%[^\"]\",\"%[^\"]\",\"%lu\",%d)%*s", + &stat, longOp, shortOp, &numOp, &act); + if (sscanRead == 5) + { + opRet[op].stat = stat; + opRet[op].longOp = (String)(longOp); + opRet[op].shortOp = (String)(shortOp); + opRet[op].numOp = numOp; + opRet[op].act = act; + opsSeen += 1; + } + // TODO: Search for other possible patterns here + else + { + break; // Break out if pattern doesn't match. + } + opBegin = opEnd + 1; // Move opBegin to beginning of next value + } + } + + free(command); + free(response); + + return opsSeen; +} + +SARA_R5_error_t SARA_R5::registerOperator(struct operator_stats oper) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 24); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=1,2,\"%lu\"", SARA_R5_OPERATOR_SELECTION, oper.numOp); + + // AT+COPS maximum response time is 3 minutes (180000 ms) + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_3_MIN_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::automaticOperatorSelection() +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 6); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=0,0", SARA_R5_OPERATOR_SELECTION); + + // AT+COPS maximum response time is 3 minutes (180000 ms) + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_3_MIN_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getOperator(String *oper) +{ + SARA_R5_error_t err; + char *command; + char *response; + char *searchPtr; + char mode; + + command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_OPERATOR_SELECTION); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // AT+COPS maximum response time is 3 minutes (180000 ms) + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, + SARA_R5_3_MIN_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + searchPtr = strstr(response, "+COPS: "); + if (searchPtr != NULL) + { + searchPtr += strlen("+COPS: "); // Move searchPtr to first char + mode = *searchPtr; // Read first char -- should be mode + if (mode == '2') // Check for de-register + { + err = SARA_R5_ERROR_DEREGISTERED; + } + // Otherwise if it's default, manual, set-only, or automatic + else if ((mode == '0') || (mode == '1') || (mode == '3') || (mode == '4')) + { + *oper = ""; + searchPtr = strchr(searchPtr, '\"'); // Move to first quote + if (searchPtr == NULL) + { + err = SARA_R5_ERROR_DEREGISTERED; + } + else + { + while ((*(++searchPtr) != '\"') && (*searchPtr != '\0')) + { + oper->concat(*(searchPtr)); + } + } + if (_printDebug == true) + { + _debugPort->print(F("getOperator: ")); + _debugPort->println(*oper); + } + } + } + } + + free(response); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::deregisterOperator(void) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 4); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=2", SARA_R5_OPERATOR_SELECTION); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_3_MIN_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setSMSMessageFormat(SARA_R5_message_format_t textMode) +{ + char *command; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_FORMAT) + 4); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_MESSAGE_FORMAT, + (textMode == SARA_R5_MESSAGE_FORMAT_TEXT) ? 1 : 0); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::sendSMS(String number, String message) +{ + char *command; + char *messageCStr; + char *numberCStr; + SARA_R5_error_t err; + + numberCStr = sara_r5_calloc_char(number.length() + 2); + if (numberCStr == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + number.toCharArray(numberCStr, number.length() + 1); + + command = sara_r5_calloc_char(strlen(SARA_R5_SEND_TEXT) + strlen(numberCStr) + 8); + if (command != NULL) + { + sprintf(command, "%s=\"%s\"", SARA_R5_SEND_TEXT, numberCStr); + + err = sendCommandWithResponse(command, ">", NULL, + SARA_R5_3_MIN_TIMEOUT); + free(command); + free(numberCStr); + if (err != SARA_R5_ERROR_SUCCESS) + return err; + + messageCStr = sara_r5_calloc_char(message.length() + 1); + if (messageCStr == NULL) + { + hwWrite(ASCII_CTRL_Z); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + message.toCharArray(messageCStr, message.length() + 1); + messageCStr[message.length()] = ASCII_CTRL_Z; + + err = sendCommandWithResponse(messageCStr, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_3_MIN_TIMEOUT, minimumResponseAllocation, NOT_AT_COMMAND); + + free(messageCStr); + } + else + { + free(numberCStr); + err = SARA_R5_ERROR_OUT_OF_MEMORY; + } + + return err; +} + +SARA_R5_error_t SARA_R5::getPreferredMessageStorage(int *used, int *total, String memory) +{ + SARA_R5_error_t err; + char *command; + char *response; + int u; + int t; + + command = sara_r5_calloc_char(strlen(SARA_R5_PREF_MESSAGE_STORE) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\"", SARA_R5_PREF_MESSAGE_STORE, memory.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_OR_ERROR, response, + SARA_R5_3_MIN_TIMEOUT); + + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return err; + } + + int scanned = 0; + char *searchPtr = strstr(response, "+CPMS: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+CPMS: %d,%d", &u, &t); + if (scanned == 2) + { + if (_printDebug == true) + { + _debugPort->print(F("getPreferredMessageStorage: memory1 (read and delete): ")); + _debugPort->print(memory); + _debugPort->print(F(" used: ")); + _debugPort->print(u); + _debugPort->print(F(" total: ")); + _debugPort->println(t); + } + *used = u; + *total = t; + } + else + { + err = SARA_R5_ERROR_INVALID; + } + + free(response); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::readSMSmessage(int location, String *unread, String *from, String *dateTime, String *message) +{ + SARA_R5_error_t err; + char *command; + char *response; + + command = sara_r5_calloc_char(strlen(SARA_R5_READ_TEXT_MESSAGE) + 5); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_READ_TEXT_MESSAGE, location); + + response = sara_r5_calloc_char(1024); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, + SARA_R5_10_SEC_TIMEOUT, 1024); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = response; + + // Find the first occurrence of +CGDCONT: + searchPtr = strstr(searchPtr, "+CMGR: "); + if (searchPtr != NULL) + { + searchPtr += strlen("+CMGR: "); // Point to the originator address + int pointer = 0; + while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 12)) + { + unread->concat(*(searchPtr)); + pointer++; + } + if ((*searchPtr == '\0') || (pointer == 12)) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + // Search to the next quote + searchPtr = strchr(++searchPtr, '\"'); + pointer = 0; + while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 24)) + { + from->concat(*(searchPtr)); + pointer++; + } + if ((*searchPtr == '\0') || (pointer == 24)) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + // Skip two commas + searchPtr = strchr(++searchPtr, ','); + searchPtr = strchr(++searchPtr, ','); + // Search to the next quote + searchPtr = strchr(++searchPtr, '\"'); + pointer = 0; + while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 24)) + { + dateTime->concat(*(searchPtr)); + pointer++; + } + if ((*searchPtr == '\0') || (pointer == 24)) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + // Search to the next new line + searchPtr = strchr(++searchPtr, '\n'); + pointer = 0; + while ((*(++searchPtr) != '\r') && (*searchPtr != '\n') && (*searchPtr != '\0') && (pointer < 512)) + { + message->concat(*(searchPtr)); + pointer++; + } + if ((*searchPtr == '\0') || (pointer == 512)) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + } + else + { + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + } + else + { + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::deleteSMSmessage(int location, int deleteFlag) +{ + char *command; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_DELETE_MESSAGE) + 12); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (deleteFlag == 0) + sprintf(command, "%s=%d", SARA_R5_DELETE_MESSAGE, location); + else + sprintf(command, "%s=%d,%d", SARA_R5_DELETE_MESSAGE, location, deleteFlag); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_55_SECS_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setBaud(unsigned long baud) +{ + SARA_R5_error_t err; + char *command; + int b = 0; + + // Error check -- ensure supported baud + for (; b < NUM_SUPPORTED_BAUD; b++) + { + if (SARA_R5_SUPPORTED_BAUD[b] == baud) + { + break; + } + } + if (b >= NUM_SUPPORTED_BAUD) + { + return SARA_R5_ERROR_UNEXPECTED_PARAM; + } + + // Construct command + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_BAUD) + 7 + 12); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%lu", SARA_R5_COMMAND_BAUD, baud); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_SET_BAUD_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5_error_t SARA_R5::setFlowControl(SARA_R5_flow_control_t value) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_FLOW_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s%d", SARA_R5_FLOW_CONTROL, value); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5_error_t SARA_R5::setGpioMode(SARA_R5_gpio_t gpio, + SARA_R5_gpio_mode_t mode, int value) +{ + SARA_R5_error_t err; + char *command; + + // Example command: AT+UGPIOC=16,2 + // Example command: AT+UGPIOC=23,0,1 + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_GPIO) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (mode == GPIO_OUTPUT) + sprintf(command, "%s=%d,%d,%d", SARA_R5_COMMAND_GPIO, gpio, mode, value); + else + sprintf(command, "%s=%d,%d", SARA_R5_COMMAND_GPIO, gpio, mode); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_10_SEC_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5::SARA_R5_gpio_mode_t SARA_R5::getGpioMode(SARA_R5_gpio_t gpio) +{ + SARA_R5_error_t err; + char *command; + char *response; + char gpioChar[4]; + char *gpioStart; + int gpioMode; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_GPIO) + 2); + if (command == NULL) + return GPIO_MODE_INVALID; + sprintf(command, "%s?", SARA_R5_COMMAND_GPIO); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return GPIO_MODE_INVALID; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return GPIO_MODE_INVALID; + } + + sprintf(gpioChar, "%d", gpio); // Convert GPIO to char array + gpioStart = strstr(response, gpioChar); // Find first occurence of GPIO in response + + free(command); + free(response); + + if (gpioStart == NULL) + return GPIO_MODE_INVALID; // If not found return invalid + sscanf(gpioStart, "%*d,%d\r\n", &gpioMode); + + return (SARA_R5_gpio_mode_t)gpioMode; +} + +int SARA_R5::socketOpen(SARA_R5_socket_protocol_t protocol, unsigned int localPort) +{ + SARA_R5_error_t err; + char *command; + char *response; + int sockId = -1; + char *responseStart; + + command = sara_r5_calloc_char(strlen(SARA_R5_CREATE_SOCKET) + 10); + if (command == NULL) + return -1; + if (localPort == 0) + sprintf(command, "%s=%d", SARA_R5_CREATE_SOCKET, (int)protocol); + else + sprintf(command, "%s=%d,%d", SARA_R5_CREATE_SOCKET, (int)protocol, localPort); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + if (_printDebug == true) + _debugPort->println(F("socketOpen: Fail: NULL response")); + free(command); + return -1; + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("socketOpen: Fail: Error: ")); + _debugPort->print(err); + _debugPort->print(F(" Response: {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + free(command); + free(response); + return -1; + } + + responseStart = strstr(response, "+USOCR"); + if (responseStart == NULL) + { + if (_printDebug == true) + { + _debugPort->print(F("socketOpen: Failure: {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + free(command); + free(response); + return -1; + } + + sscanf(responseStart, "+USOCR: %d", &sockId); + _lastSocketProtocol[sockId] = (int)protocol; + + free(command); + free(response); + + return sockId; +} + +SARA_R5_error_t SARA_R5::socketClose(int socket, unsigned long timeout) +{ + SARA_R5_error_t err; + char *command; + char *response; + + command = sara_r5_calloc_char(strlen(SARA_R5_CLOSE_SOCKET) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + sprintf(command, "%s=%d", SARA_R5_CLOSE_SOCKET, socket); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, timeout); + + if ((err != SARA_R5_ERROR_SUCCESS) && (_printDebug == true)) + { + _debugPort->print(F("socketClose: Error: ")); + _debugPort->println(socketGetLastError()); + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::socketConnect(int socket, const char *address, + unsigned int port) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_CONNECT_SOCKET) + strlen(address) + 11); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,\"%s\",%d", SARA_R5_CONNECT_SOCKET, socket, address, port); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_IP_CONNECT_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5_error_t SARA_R5::socketConnect(int socket, IPAddress address, + unsigned int port) +{ + char *charAddress = sara_r5_calloc_char(16); + if (charAddress == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + memset(charAddress, 0, 16); + sprintf(charAddress, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); + + return (socketConnect(socket, (const char *)charAddress, port)); +} + +SARA_R5_error_t SARA_R5::socketWrite(int socket, const char *str, int len) +{ + char *command; + char *response; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_WRITE_SOCKET) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + int dataLen = len == -1 ? strlen(str) : len; + sprintf(command, "%s=%d,%d", SARA_R5_WRITE_SOCKET, socket, dataLen); + + err = sendCommandWithResponse(command, "@", response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); + + if (err == SARA_R5_ERROR_SUCCESS) + { + unsigned long writeDelay = millis(); + while (millis() < (writeDelay + 50)) + delay(1); //u-blox specification says to wait 50ms after receiving "@" to write data. + + if (len == -1) + { + if (_printDebug == true) + { + _debugPort->print(F("socketWrite: writing: ")); + _debugPort->println(str); + } + hwPrint(str); + } + else + { + if (_printDebug == true) + { + _debugPort->print(F("socketWrite: writing ")); + _debugPort->print(len); + _debugPort->println(F(" bytes")); + } + hwWriteData(str, len); + } + + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_SOCKET_WRITE_TIMEOUT); + } + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("socketWrite: Error: ")); + _debugPort->print(err); + _debugPort->print(F(" => {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::socketWrite(int socket, String str) +{ + return socketWrite(socket, str.c_str(), str.length()); +} + +SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, const char *address, int port, const char *str, int len) +{ + char *command; + char *response; + SARA_R5_error_t err; + int dataLen = len == -1 ? strlen(str) : len; + + command = sara_r5_calloc_char(64); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s=%d,\"%s\",%d,%d", SARA_R5_WRITE_UDP_SOCKET, + socket, address, port, dataLen); + err = sendCommandWithResponse(command, "@", response, SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); + + if (err == SARA_R5_ERROR_SUCCESS) + { + if (len == -1) //If binary data we need to send a length. + { + hwPrint(str); + } + else + { + hwWriteData(str, len); + } + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_SOCKET_WRITE_TIMEOUT); + } + else + { + if (_printDebug == true) + _debugPort->print(F("socketWriteUDP: Error: ")); + if (_printDebug == true) + _debugPort->println(socketGetLastError()); + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, IPAddress address, int port, const char *str, int len) +{ + char *charAddress = sara_r5_calloc_char(16); + if (charAddress == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + memset(charAddress, 0, 16); + sprintf(charAddress, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); + + return (socketWriteUDP(socket, (const char *)charAddress, port, str, len)); +} + +SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, String address, int port, String str) +{ + 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, int *bytesRead) +{ + char *command; + char *response; + char *strBegin; + 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) + { + if (_printDebug == true) + _debugPort->print(F("socketRead: length is 0! Call socketReadAvailable?")); + 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; + + // 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) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // If there are more than _saraR5maxSocketRead (1024) bytes to be read, + // we need to do multiple reads to get all the data + + while (bytesLeftToRead > 0) + { + 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_OR_ERROR, 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", + &socketStore, &readLength); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("socketRead: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Check that readLength == bytesToRead + if (readLength != bytesToRead) + { + if (_printDebug == true) + { + _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(searchPtr, '\"'); + + if (strBegin == NULL) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Now copy the data into readDest + readIndexThisRead = 1; // Start after the quote + while (readIndexThisRead < (readLength + 1)) + { + 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 SARA_R5_ERROR_SUCCESS; +} + +SARA_R5_error_t SARA_R5::socketReadAvailable(int socket, int *length) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int readLength = 0; + int socketStore = 0; + + 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,0", SARA_R5_READ_SOCKET, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USORD: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USORD: %d,%d", + &socketStore, &readLength); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("socketReadAvailable: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *length = readLength; + } + + free(command); + free(response); + + return err; +} + +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 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) + { + if (_printDebug == true) + _debugPort->print(F("socketReadUDP: length is 0! Call socketReadAvailableUDP?")); + 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; + + // 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) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // If there are more than _saraR5maxSocketRead (1024) bytes to be read, + // we need to do multiple reads to get all the data + + while (bytesLeftToRead > 0) + { + 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_OR_ERROR, 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", + &socketStore, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], + &portStore, &readLength); + if (scanNum != 7) + { + if (_printDebug == true) + { + _debugPort->print(F("socketReadUDP: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Check that readLength == bytesToRead + if (readLength != bytesToRead) + { + if (_printDebug == true) + { + _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(searchPtr, '\"'); + strBegin = strchr(strBegin + 1, '\"'); + strBegin = strchr(strBegin + 1, '\"'); + + if (strBegin == NULL) + { + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Now copy the data into readDest + readIndexThisRead = 1; // Start after the quote + while (readIndexThisRead < (readLength + 1)) + { + readDest[readIndexTotal] = strBegin[readIndexThisRead]; + readIndexTotal++; + readIndexThisRead++; + } + + // If remoteIPaddress is not NULL, copy the remote IP address + if (remoteIPAddress != NULL) + { + IPAddress tempAddress; + for (int i = 0; i <= 3; i++) + { + tempAddress[i] = (uint8_t)remoteIPstore[i]; + } + *remoteIPAddress = tempAddress; + } + + // If remotePort is not NULL, copy the remote port + if (remotePort != NULL) + { + *remotePort = portStore; + } + + 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 SARA_R5_ERROR_SUCCESS; +} + +SARA_R5_error_t SARA_R5::socketReadAvailableUDP(int socket, int *length) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int readLength = 0; + int socketStore = 0; + + 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,0", SARA_R5_READ_UDP_SOCKET, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USORF: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USORF: %d,%d", + &socketStore, &readLength); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("socketReadAvailableUDP: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *length = readLength; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::socketListen(int socket, unsigned int port) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_LISTEN_SOCKET) + 9); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d", SARA_R5_LISTEN_SOCKET, socket, port); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::socketDirectLinkMode(int socket) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_DIRECT_LINK) + 8); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_SOCKET_DIRECT_LINK, socket); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_CONNECT, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::socketDirectLinkTimeTrigger(int socket, unsigned long timerTrigger) +{ + // valid range is 0 (trigger disabled), 100-120000 + if (!((timerTrigger == 0) || ((timerTrigger >= 100) && (timerTrigger <= 120000)))) + return SARA_R5_ERROR_ERROR; + + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=5,%d,%ld", SARA_R5_UD_CONFIGURATION, socket, timerTrigger); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::socketDirectLinkDataLengthTrigger(int socket, int dataLengthTrigger) +{ + // valid range is 0, 3-1472 for UDP + if (!((dataLengthTrigger == 0) || ((dataLengthTrigger >= 3) && (dataLengthTrigger <= 1472)))) + return SARA_R5_ERROR_ERROR; + + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=6,%d,%d", SARA_R5_UD_CONFIGURATION, socket, dataLengthTrigger); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::socketDirectLinkCharacterTrigger(int socket, int characterTrigger) +{ + // The allowed range is -1, 0-255, the factory-programmed value is -1; -1 means trigger disabled. + if (!((characterTrigger >= -1) && (characterTrigger <= 255))) + return SARA_R5_ERROR_ERROR; + + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=7,%d,%d", SARA_R5_UD_CONFIGURATION, socket, characterTrigger); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::socketDirectLinkCongestionTimer(int socket, unsigned long congestionTimer) +{ + // valid range is 0, 1000-72000 + if (!((congestionTimer == 0) || ((congestionTimer >= 1000) && (congestionTimer <= 72000)))) + return SARA_R5_ERROR_ERROR; + + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=8,%d,%ld", SARA_R5_UD_CONFIGURATION, socket, congestionTimer); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::querySocketType(int socket, SARA_R5_socket_protocol_t *protocol) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,0", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,0,%d", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketType: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *protocol = (SARA_R5_socket_protocol_t)paramVal; + _lastSocketProtocol[socketStore] = paramVal; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketLastError(int socket, int *error) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,1", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,1,%d", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketLastError: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *error = paramVal; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketTotalBytesSent(int socket, uint32_t *total) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + long unsigned int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,2", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,2,%lu", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketTotalBytesSent: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *total = (uint32_t)paramVal; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketTotalBytesReceived(int socket, uint32_t *total) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + long unsigned int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,3", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,3,%lu", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketTotalBytesReceived: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *total = (uint32_t)paramVal; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketRemoteIPAddress(int socket, IPAddress *address, int *port) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + int paramVals[5]; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,4", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,4,\"%d.%d.%d.%d\",%d", + &socketStore, + ¶mVals[0], ¶mVals[1], ¶mVals[2], ¶mVals[3], + ¶mVals[4]); + if (scanNum != 6) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketRemoteIPAddress: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + IPAddress tempAddress = { (uint8_t)paramVals[0], (uint8_t)paramVals[1], + (uint8_t)paramVals[2], (uint8_t)paramVals[3] }; + *address = tempAddress; + *port = paramVals[4]; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketStatusTCP(int socket, SARA_R5_tcp_socket_status_t *status) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,10", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,10,%d", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketStatusTCP: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *status = (SARA_R5_tcp_socket_status_t)paramVal; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::querySocketOutUnackData(int socket, uint32_t *total) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int socketStore = 0; + long unsigned int paramVal; + + command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,11", SARA_R5_SOCKET_CONTROL, socket); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOCTL: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+USOCTL: %d,11,%lu", + &socketStore, ¶mVal); + if (scanNum != 2) + { + if (_printDebug == true) + { + _debugPort->print(F("querySocketOutUnackData: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + *total = (uint32_t)paramVal; + } + + free(command); + free(response); + + return err; +} + +//Issues command to get last socket error, then prints to serial. Also updates rx/backlog buffers. +int SARA_R5::socketGetLastError() +{ + SARA_R5_error_t err; + char *command; + char *response; + int errorCode = -1; + + command = sara_r5_calloc_char(64); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s", SARA_R5_GET_ERROR); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+USOER: "); + if (searchPtr != NULL) + sscanf(searchPtr, "+USOER: %d", &errorCode); + } + + free(command); + free(response); + + return errorCode; +} + +IPAddress SARA_R5::lastRemoteIP(void) +{ + return _lastRemoteIP; +} + +SARA_R5_error_t SARA_R5::resetHTTPprofile(int profile) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_HTTP_PROFILE, profile); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPserverIPaddress(int profile, IPAddress address) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 64); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%d.%d.%d.%d\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_IP, + address[0], address[1], address[2], address[3]); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPserverName(int profile, String server) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + server.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_NAME, + server.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPusername(int profile, String username) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + username.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_USERNAME, + username.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPpassword(int profile, String password) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + password.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_PASSWORD, + password.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPauthentication(int profile, bool authenticate) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_AUTHENTICATION, + authenticate); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPserverPort(int profile, int port) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_PORT, + port); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPcustomHeader(int profile, String header) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + header.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_ADD_CUSTOM_HEADERS, + header.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setHTTPsecure(int profile, bool secure, int secprofile) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (secprofile == -1) + sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SECURE, + secure); + else sprintf(command, "%s=%d,%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SECURE, + secure, secprofile); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::ping(String remote_host, int retry, int p_size, + unsigned long timeout, int ttl) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_PING_COMMAND) + 48 + + remote_host.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=\"%s\",%d,%d,%ld,%d", SARA_R5_PING_COMMAND, + remote_host.c_str(), retry, p_size, timeout, ttl); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::sendHTTPGET(int profile, String path, String responseFilename) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + + path.length() + responseFilename.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\",\"%s\"", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_GET, + path.c_str(), responseFilename.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::sendHTTPPOSTdata(int profile, String path, String responseFilename, + String data, SARA_R5_http_content_types_t httpContentType) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + + path.length() + responseFilename.length() + data.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\",\"%s\",\"%s\",%d", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_POST_DATA, + path.c_str(), responseFilename.c_str(), data.c_str(), httpContentType); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::sendHTTPPOSTfile(int profile, String path, String responseFilename, + String requestFile, SARA_R5_http_content_types_t httpContentType) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_HTTP_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + + path.length() + responseFilename.length() + requestFile.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\",\"%s\",\"%s\",%d", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_POST_FILE, + path.c_str(), responseFilename.c_str(), requestFile.c_str(), httpContentType); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getHTTPprotocolError(int profile, int *error_class, int *error_code) +{ + SARA_R5_error_t err; + char *command; + char *response; + + int rprofile, eclass, ecode; + + command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROTOCOL_ERROR) + 4); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_HTTP_PROTOCOL_ERROR, profile); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + int scanned = 0; + char *searchPtr = strstr(response, "+UHTTPER: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+UHTTPER: %d,%d,%d\r\n", + &rprofile, &eclass, &ecode); + if (scanned == 3) + { + *error_class = eclass; + *error_code = ecode; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::nvMQTT(SARA_R5_mqtt_nv_parameter_t parameter) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_NVM) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_MQTT_NVM, parameter); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setMQTTclientId(String clientId) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + clientId.length() + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,\"%s\"", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_CLIENT_ID, clientId.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setMQTTserver(String serverName, int port) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + serverName.length() + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,\"%s\",%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SERVERNAME, serverName.c_str(), port); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setMQTTsecure(bool secure, int secprofile) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (secprofile == -1) sprintf(command, "%s=%d,%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SECURE, secure); + else sprintf(command, "%s=%d,%d,%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SECURE, secure, secprofile); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::connectMQTT(void) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_LOGIN); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::disconnectMQTT(void) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_LOGOUT); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::subscribeMQTTtopic(int max_Qos, String topic) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 16 + topic.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_SUBSCRIBE, max_Qos, topic.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::unsubscribeMQTTtopic(String topic) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 16 + topic.length()); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_UNSUBSCRIBE, topic.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int total_length, topic_length, data_length; + + // Set *bytesRead to zero + if (bytesRead != NULL) + *bytesRead = 0; + + // Allocate memory for the command + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + // Allocate memory for the response + int responseLength = readLength + minimumResponseAllocation; + response = sara_r5_calloc_char(responseLength); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + // 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\n\r\nOK\r\n there is a extra \r\n beetween " and the the standard \r\nOK\r\n + const char mqttReadTerm[] = "\r\n\r\nOK\r\n"; + sprintf(command, "%s=%d,%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_READ, 1); + err = sendCommandWithResponse(command, mqttReadTerm, response, + (5 * SARA_R5_STANDARD_RESPONSE_TIMEOUT), responseLength); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("readMQTT: sendCommandWithResponse err ")); + _debugPort->println(err); + } + free(command); + free(response); + return err; + } + + // Extract the data + char *searchPtr = strstr(response, "+UMQTTC: 6"); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+UMQTTC: 6,%d,%d,%d,\"%*[^\"]\",%d,\"", pQos, &total_length, &topic_length, &data_length); + if (scanNum != 4) + { + if (_printDebug == true) + { + _debugPort->print(F("readMQTT: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + err = SARA_R5_ERROR_SUCCESS; + searchPtr = strstr(searchPtr, "\""); + if (searchPtr!= NULL) { + if (pTopic) { + searchPtr[topic_length + 1] = '\0'; // zero terminate + *pTopic = searchPtr + 1; + searchPtr[topic_length + 1] = '\"'; // restore + } + searchPtr = strstr(searchPtr + topic_length + 2, "\""); + if (readDest && (searchPtr != NULL) && (searchPtr[data_length + 1] == '"')) { + if (data_length > readLength) { + data_length = readLength; + if (_printDebug == true) { + _debugPort->print(F("readMQTT: error: trucate message")); + } + err = SARA_R5_ERROR_OUT_OF_MEMORY; + } + memcpy(readDest, searchPtr+1, data_length); + *bytesRead = data_length; + } else { + if (_printDebug == true) { + _debugPort->print(F("readMQTT: error: message end ")); + } + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + } + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::getMQTTprotocolError(int *error_code, int *error_code2) +{ + SARA_R5_error_t err; + char *response; + + int code, code2; + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + err = sendCommandWithResponse(SARA_R5_MQTT_PROTOCOL_ERROR, SARA_R5_RESPONSE_OK_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + int scanned = 0; + char *searchPtr = strstr(response, "+UMQTTER: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+UMQTTER:%d,%d\r\n", + &code, &code2); + if (scanned == 2) + { + *error_code = code; + *error_code2 = code2; + } + else + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::resetSecurityProfile(int secprofile) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + 6); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + sprintf(command, "%s=%d", SARA_R5_SEC_PROFILE, secprofile); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::configSecurityProfile(int secprofile, SARA_R5_sec_profile_parameter_t parameter, int value) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,%d", SARA_R5_SEC_PROFILE, secprofile,parameter,value); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::configSecurityProfileString(int secprofile, SARA_R5_sec_profile_parameter_t parameter, String value) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + value.length() + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_SEC_PROFILE, secprofile,parameter,value.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setSecurityManager(SARA_R5_sec_manager_opcode_t opcode, SARA_R5_sec_manager_parameter_t parameter, String name, String data) +{ + char *command; + char *response; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_SEC_MANAGER) + name.length() + 20); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + int dataLen = data.length(); + sprintf(command, "%s=%d,%d,\"%s\",%d", SARA_R5_SEC_MANAGER, opcode, parameter, name.c_str(), dataLen); + + err = sendCommandWithResponse(command, ">", response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("dataDownload: writing ")); + _debugPort->print(dataLen); + _debugPort->println(F(" bytes")); + } + hwWriteData(data.c_str(), dataLen); + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT*3); + } + + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("dataDownload: Error: ")); + _debugPort->print(err); + _debugPort->print(F(" => {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, int value) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_PSD_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 24); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,%d", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, + value); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, SARA_R5_pdp_protocol_type_t value) +{ + return (setPDPconfiguration(profile, parameter, (int)value)); +} + +SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, String value) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_PSD_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 64); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, + value.c_str()); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, IPAddress value) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_PSD_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 64); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d,\"%d.%d.%d.%d\"", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, + value[0], value[1], value[2], value[3]); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::performPDPaction(int profile, SARA_R5_pdp_actions_t action) +{ + SARA_R5_error_t err; + char *command; + + if (profile >= SARA_R5_NUM_PSD_PROFILES) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_ACTION) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d", SARA_R5_MESSAGE_PDP_ACTION, profile, action); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::activatePDPcontext(bool status, int cid) +{ + SARA_R5_error_t err; + char *command; + + if (cid >= SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS) + return SARA_R5_ERROR_ERROR; + + command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE) + 32); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (cid == -1) + sprintf(command, "%s=%d", SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE, status); + else + sprintf(command, "%s=%d,%d", SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE, status, cid); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getNetworkAssignedIPAddress(int profile, IPAddress *address) +{ + char *command; + char *response; + SARA_R5_error_t err; + int scanNum = 0; + int profileStore = 0; + int paramTag = 0; // 0: IP address: dynamic IP address assigned during PDP context activation + int paramVals[4]; + + command = sara_r5_calloc_char(strlen(SARA_R5_NETWORK_ASSIGNED_DATA) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d,%d", SARA_R5_NETWORK_ASSIGNED_DATA, profile, paramTag); + + 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_OR_ERROR, response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + char *searchPtr = strstr(response, "+UPSND: "); + if (searchPtr != NULL) + scanNum = sscanf(searchPtr, "+UPSND: %d,%d,\"%d.%d.%d.%d\"", + &profileStore, ¶mTag, + ¶mVals[0], ¶mVals[1], ¶mVals[2], ¶mVals[3]); + if (scanNum != 6) + { + if (_printDebug == true) + { + _debugPort->print(F("getNetworkAssignedIPAddress: error: scanNum is ")); + _debugPort->println(scanNum); + } + free(command); + free(response); + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + IPAddress tempAddress = { (uint8_t)paramVals[0], (uint8_t)paramVals[1], + (uint8_t)paramVals[2], (uint8_t)paramVals[3] }; + *address = tempAddress; + } + + free(command); + free(response); + + return err; +} + +bool SARA_R5::isGPSon(void) +{ + SARA_R5_error_t err; + char *command; + char *response; + bool on = false; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_POWER) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_GNSS_POWER); + + 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_OR_ERROR, response, + SARA_R5_10_SEC_TIMEOUT); + + if (err == SARA_R5_ERROR_SUCCESS) + { + // Example response: "+UGPS: 0" for off "+UGPS: 1,0,1" for on + // Search for a ':' followed by a '1' or ' 1' + char *pch1 = strchr(response, ':'); + if (pch1 != NULL) + { + char *pch2 = strchr(response, '1'); + if ((pch2 != NULL) && ((pch2 == pch1 + 1) || (pch2 == pch1 + 2))) + on = true; + } + } + + free(command); + free(response); + + return on; +} + +SARA_R5_error_t SARA_R5::gpsPower(bool enable, gnss_system_t gnss_sys, gnss_aiding_mode_t gnss_aiding) +{ + SARA_R5_error_t err; + char *command; + bool gpsState; + + // Don't turn GPS on/off if it's already on/off + gpsState = isGPSon(); + if ((enable && gpsState) || (!enable && !gpsState)) + { + return SARA_R5_ERROR_SUCCESS; + } + + // GPS power management + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_POWER) + 32); // gnss_sys could be up to three digits + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (enable) + { + sprintf(command, "%s=1,%d,%d", SARA_R5_GNSS_POWER, gnss_aiding, gnss_sys); + } + else + { + sprintf(command, "%s=0", SARA_R5_GNSS_POWER); + } + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, 10000); + + free(command); + return err; +} + +/* +SARA_R5_error_t SARA_R5::gpsEnableClock(bool enable) +{ + // AT+UGZDA=<0,1> + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetClock(struct ClockData *clock) +{ + // AT+UGZDA? + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsEnableFix(bool enable) +{ + // AT+UGGGA=<0,1> + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetFix(struct PositionData *pos) +{ + // AT+UGGGA? + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsEnablePos(bool enable) +{ + // AT+UGGLL=<0,1> + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetPos(struct PositionData *pos) +{ + // AT+UGGLL? + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsEnableSat(bool enable) +{ + // AT+UGGSV=<0,1> + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetSat(uint8_t *sats) +{ + // AT+UGGSV? + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} +*/ + +SARA_R5_error_t SARA_R5::gpsEnableRmc(bool enable) +{ + // AT+UGRMC=<0,1> + SARA_R5_error_t err; + char *command; + + // ** Don't call gpsPower here. It causes problems for +UTIME and the PPS signal ** + // ** Call isGPSon and gpsPower externally if required ** + // if (!isGPSon()) + // { + // err = gpsPower(true); + // if (err != SARA_R5_ERROR_SUCCESS) + // { + // return err; + // } + // } + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_GPRMC) + 3); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_GNSS_GPRMC, enable ? 1 : 0); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_10_SEC_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetRmc(struct PositionData *pos, struct SpeedData *spd, + struct ClockData *clk, bool *valid) +{ + SARA_R5_error_t err; + char *command; + char *response; + char *rmcBegin; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_GPRMC) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_GNSS_GPRMC); + + 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_OR_ERROR, response, SARA_R5_10_SEC_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + // Fast-forward response string to $GPRMC starter + rmcBegin = strstr(response, "$GPRMC"); + if (rmcBegin == NULL) + { + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + else + { + *valid = parseGPRMCString(rmcBegin, pos, clk, spd); + } + } + + free(command); + free(response); + return err; +} + +/* +SARA_R5_error_t SARA_R5::gpsEnableSpeed(bool enable) +{ + // AT+UGVTG=<0,1> + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} + +SARA_R5_error_t SARA_R5::gpsGetSpeed(struct SpeedData *speed) +{ + // AT+UGVTG? + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + return err; +} +*/ + +SARA_R5_error_t SARA_R5::gpsRequest(unsigned int timeout, uint32_t accuracy, + bool detailed, unsigned int sensor) +{ + // AT+ULOC=2,,,, + SARA_R5_error_t err; + char *command; + + // This function will only work if the GPS module is initially turned off. + if (isGPSon()) + { + gpsPower(false); + } + + if (timeout > 999) + timeout = 999; + if (accuracy > 999999) + accuracy = 999999; + + command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_LOCATION) + 24); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) + sprintf(command, "%s=2,%d,%d,%d,%d", SARA_R5_GNSS_REQUEST_LOCATION, + sensor, detailed ? 1 : 0, timeout, accuracy); +#else + sprintf(command, "%s=2,%d,%d,%d,%ld", SARA_R5_GNSS_REQUEST_LOCATION, + sensor, detailed ? 1 : 0, timeout, accuracy); +#endif + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_10_SEC_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::gpsAidingServerConf(const char *primaryServer, const char *secondaryServer, const char *authToken, + unsigned int days, unsigned int period, unsigned int resolution, + unsigned int gnssTypes, unsigned int mode, unsigned int dataType) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_AIDING_SERVER_CONFIGURATION) + 256); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + sprintf(command, "%s=\"%s\",\"%s\",\"%s\",%d,%d,%d,%d,%d,%d", SARA_R5_AIDING_SERVER_CONFIGURATION, + primaryServer, secondaryServer, authToken, + days, period, resolution, gnssTypes, mode, dataType); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + + +// OK for text files. But will fail with binary files (containing \0) on some platforms. +SARA_R5_error_t SARA_R5::appendFileContents(String filename, const char *str, int len) +{ + char *command; + char *response; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_DOWNLOAD_FILE) + filename.length() + 10); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == NULL) + { + free(command); + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + int dataLen = len == -1 ? strlen(str) : len; + sprintf(command, "%s=\"%s\",%d", SARA_R5_FILE_SYSTEM_DOWNLOAD_FILE, filename.c_str(), dataLen); + + err = sendCommandWithResponse(command, ">", response, + SARA_R5_STANDARD_RESPONSE_TIMEOUT*2); + + unsigned long writeDelay = millis(); + while (millis() < (writeDelay + 50)) + delay(1); //uBlox specification says to wait 50ms after receiving "@" to write data. + + if (err == SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("fileDownload: writing ")); + _debugPort->print(dataLen); + _debugPort->println(F(" bytes")); + } + hwWriteData(str, dataLen); + + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT*5); + } + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("fileDownload: Error: ")); + _debugPort->print(err); + _debugPort->print(F(" => {")); + _debugPort->print(response); + _debugPort->println(F("}")); + } + } + + free(command); + free(response); + return err; +} + +SARA_R5_error_t SARA_R5::appendFileContents(String filename, String str) +{ + return appendFileContents(filename, str.c_str(), str.length()); +} + + +// 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; + 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 + 2*minimumResponseAllocation); + if (response == NULL) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: response alloc failed: ")); + _debugPort->println(fileSize + 2*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, (10 * SARA_R5_STANDARD_RESPONSE_TIMEOUT), + (fileSize + 2*minimumResponseAllocation)); + + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + { + _debugPort->print(F("getFileContents: sendCommandWithResponse returned err ")); + _debugPort->print(">>>");_debugPort->print(response);_debugPort->print("<<<"); + _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 + // 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++; + } + 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; +} + +// 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; + 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_OR_ERROR, 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_OR_ERROR, 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) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_POWER_OFF) + 6); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + sprintf(command, "%s", SARA_R5_COMMAND_POWER_OFF); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, + SARA_R5_POWER_OFF_TIMEOUT); + + free(command); + return err; +} + +void SARA_R5::modulePowerOn(void) +{ + if (_powerPin >= 0) + { + powerOn(); + } + else + { + if (_printDebug == true) + _debugPort->println(F("modulePowerOn: not supported. _powerPin not defined.")); + } +} + +///////////// +// Private // +///////////// + +SARA_R5_error_t SARA_R5::init(unsigned long baud, + SARA_R5::SARA_R5_init_type_t initType) +{ + int retries = _maxInitTries; + SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; + + beginSerial(baud); + + do + { + if (_printDebug == true) + _debugPort->println(F("init: Begin module init.")); + + if (initType == SARA_R5_INIT_AUTOBAUD) + { + if (_printDebug == true) + _debugPort->println(F("init: Attempting autobaud connection to module.")); + + err = autobaud(baud); + + if (err != SARA_R5_ERROR_SUCCESS) { + initType = SARA_R5_INIT_RESET; + } + } + else if (initType == SARA_R5_INIT_RESET) + { + if (_printDebug == true) + _debugPort->println(F("init: Power cycling module.")); + + powerOff(); + delay(SARA_R5_POWER_OFF_PULSE_PERIOD); + powerOn(); + beginSerial(baud); + delay(2000); + + err = at(); + if (err != SARA_R5_ERROR_SUCCESS) + { + initType = SARA_R5_INIT_AUTOBAUD; + } + } + if (err == SARA_R5_ERROR_SUCCESS) + { + err = enableEcho(false); // = disableEcho + if (err != SARA_R5_ERROR_SUCCESS) + { + if (_printDebug == true) + _debugPort->println(F("init: Module failed echo test.")); + initType = SARA_R5_INIT_AUTOBAUD; + } + } + } + while ((retries --) && (err != SARA_R5_ERROR_SUCCESS)); + + // we tried but seems failed + if (err != SARA_R5_ERROR_SUCCESS) { + if (_printDebug == true) + _debugPort->println(F("init: Module failed to init. Exiting.")); + return (SARA_R5_ERROR_NO_RESPONSE); + } + + if (_printDebug == true) + _debugPort->println(F("init: Module responded successfully.")); + + _baud = baud; + setGpioMode(GPIO1, NETWORK_STATUS); + //setGpioMode(GPIO2, GNSS_SUPPLY_ENABLE); + setGpioMode(GPIO6, TIME_PULSE_OUTPUT); + setSMSMessageFormat(SARA_R5_MESSAGE_FORMAT_TEXT); + autoTimeZone(_autoTimeZoneForBegin); + for (int i = 0; i < SARA_R5_NUM_SOCKETS; i++) + { + socketClose(i, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + } + + return SARA_R5_ERROR_SUCCESS; +} + +void SARA_R5::invertPowerPin(bool invert) +{ + _invertPowerPin = invert; +} + +// Do a graceful power off. Hold the PWR_ON pin low for SARA_R5_POWER_OFF_PULSE_PERIOD +// Note: +CPWROFF () is preferred to this. +void SARA_R5::powerOff(void) +{ + if (_powerPin >= 0) + { + if (_invertPowerPin) // Set the pin state before making it an output + digitalWrite(_powerPin, HIGH); + else + digitalWrite(_powerPin, LOW); + pinMode(_powerPin, OUTPUT); + if (_invertPowerPin) // Set the pin state + digitalWrite(_powerPin, HIGH); + else + digitalWrite(_powerPin, LOW); + delay(SARA_R5_POWER_OFF_PULSE_PERIOD); + pinMode(_powerPin, INPUT); // Return to high-impedance, rely on (e.g.) SARA module internal pull-up + if (_printDebug == true) + _debugPort->println(F("powerOff: complete")); + } +} + +void SARA_R5::powerOn(void) +{ + if (_powerPin >= 0) + { + if (_invertPowerPin) // Set the pin state before making it an output + digitalWrite(_powerPin, HIGH); + else + digitalWrite(_powerPin, LOW); + pinMode(_powerPin, OUTPUT); + if (_invertPowerPin) // Set the pin state + digitalWrite(_powerPin, HIGH); + else + digitalWrite(_powerPin, LOW); + delay(SARA_R5_POWER_ON_PULSE_PERIOD); + pinMode(_powerPin, INPUT); // Return to high-impedance, rely on (e.g.) SARA module internal pull-up + //delay(2000); // Do this in init. Wait before sending AT commands to module. 100 is too short. + if (_printDebug == true) + _debugPort->println(F("powerOn: complete")); + } +} + +//This does an abrupt emergency hardware shutdown of the SARA-R5 series modules. +//It only works if you have access to both the RESET_N and PWR_ON pins. +//You cannot use this function on the SparkFun Asset Tracker and RESET_N is tied to the MicroMod processor !RESET!... +void SARA_R5::hwReset(void) +{ + if ((_resetPin >= 0) && (_powerPin >= 0)) + { + digitalWrite(_resetPin, HIGH); // Start by making sure the RESET_N pin is high + pinMode(_resetPin, OUTPUT); + digitalWrite(_resetPin, HIGH); + + if (_invertPowerPin) // Now pull PWR_ON low - invert as necessary (on the Asset Tracker) + { + digitalWrite(_powerPin, HIGH); // Inverted - Asset Tracker + pinMode(_powerPin, OUTPUT); + digitalWrite(_powerPin, HIGH); + } + else + { + digitalWrite(_powerPin, LOW); // Not inverted + pinMode(_powerPin, OUTPUT); + digitalWrite(_powerPin, LOW); + } + + delay(SARA_R5_RESET_PULSE_PERIOD); // Wait 23 seconds... (Yes, really!) + + digitalWrite(_resetPin, LOW); // Now pull RESET_N low + + delay(100); // Wait a little... (The data sheet doesn't say how long for) + + if (_invertPowerPin) // Now pull PWR_ON high - invert as necessary (on the Asset Tracker) + { + digitalWrite(_powerPin, LOW); // Inverted - Asset Tracker + } + else + { + digitalWrite(_powerPin, HIGH); // Not inverted + } + + delay(1500); // Wait 1.5 seconds + + digitalWrite(_resetPin, HIGH); // Now pull RESET_N high again + + pinMode(_resetPin, INPUT); // Return to high-impedance, rely on SARA module internal pull-up + pinMode(_powerPin, INPUT); // Return to high-impedance, rely on SARA module internal pull-up + } +} + +SARA_R5_error_t SARA_R5::functionality(SARA_R5_functionality_t function) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_FUNC) + 16); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s=%d", SARA_R5_COMMAND_FUNC, function); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_3_MIN_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5_error_t SARA_R5::setMNOprofile(mobile_network_operator_t mno, bool autoReset, bool urcNotification) +{ + SARA_R5_error_t err; + char *command; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_MNO) + 9); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + if (mno == MNO_SIM_ICCID) // Only add autoReset and urcNotification if mno is MNO_SIM_ICCID + sprintf(command, "%s=%d,%d,%d", SARA_R5_COMMAND_MNO, (uint8_t)mno, (uint8_t)autoReset, (uint8_t)urcNotification); + else + sprintf(command, "%s=%d", SARA_R5_COMMAND_MNO, (uint8_t)mno); + + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, + NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + + return err; +} + +SARA_R5_error_t SARA_R5::getMNOprofile(mobile_network_operator_t *mno) +{ + SARA_R5_error_t err; + char *command; + char *response; + mobile_network_operator_t o; + int d; + int r; + int u; + int oStore; + + command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_MNO) + 2); + if (command == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + sprintf(command, "%s?", SARA_R5_COMMAND_MNO); + + 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_OR_ERROR, + response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(command); + free(response); + return err; + } + + int scanned = 0; + char *searchPtr = strstr(response, "+UMNOPROF: "); + if (searchPtr != NULL) + scanned = sscanf(searchPtr, "+UMNOPROF: %d,%d,%d,%d", &oStore, &d, &r, &u); + o = (mobile_network_operator_t)oStore; + + if (scanned >= 1) + { + if (_printDebug == true) + { + _debugPort->print(F("getMNOprofile: MNO is: ")); + _debugPort->println(o); + } + *mno = o; + } + else + { + err = SARA_R5_ERROR_INVALID; + } + + free(command); + free(response); + + return err; +} + +SARA_R5_error_t SARA_R5::waitForResponse(const char *expectedResponse, const char *expectedError, uint16_t timeout) +{ + unsigned long timeIn; + bool found = false; + bool error = false; + int responseIndex = 0, errorIndex = 0; + // bool printedSomething = false; + + timeIn = millis(); + + int responseLen = (int)strlen(expectedResponse); + int errorLen = (int)strlen(expectedError); + + while ((!found) && ((timeIn + timeout) > millis())) + { + 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("waitForResponse: ")); + // _debugPort->write(c); + // printedSomething = true; + // } + if ((responseIndex < responseLen) && (c == expectedResponse[responseIndex])) + { + if (++responseIndex == responseLen) + { + found = true; + } + } + else + { + responseIndex = ((responseIndex < responseLen) && (c == expectedResponse[0])) ? 1 : 0; + } + if ((errorIndex < errorLen) && (c == expectedError[errorIndex])) + { + if (++errorIndex == errorLen) + { + error = true; + found = true; + } + } + else + { + errorIndex = ((errorIndex < errorLen) && (c == expectedError[0])) ? 1 : 0; + } + //_saraResponseBacklog is a global array that holds the backlog of any events + //that came in while waiting for response. To be processed later within bufferedPoll(). + //Note: the expectedResponse or expectedError will also be added to the backlog. + //The backlog is only used by bufferedPoll to process the URCs - which are all readable. + //bufferedPoll uses strtok - which does not like NULL characters. + //So let's make sure no NULLs end up in the backlog! + if (_saraResponseBacklogLength < _RXBuffSize) // Don't overflow the buffer + { + if (c == '\0') + _saraResponseBacklog[_saraResponseBacklogLength++] = '0'; // Change NULLs to ASCII Zeros + else + _saraResponseBacklog[_saraResponseBacklogLength++] = c; + } + } else { + yield(); + } + } + + // if (_printDebug == true) + // if (printedSomething) + // _debugPort->println(); + + pruneBacklog(); // Prune any incoming non-actionable URC's and responses/errors from the backlog + + if (found == true) + { + if (true == _printAtDebug) { + _debugAtPort->print((error == true) ? expectedError : expectedResponse); + } + + return (error == true) ? SARA_R5_ERROR_ERROR : SARA_R5_ERROR_SUCCESS; + } + + return SARA_R5_ERROR_NO_RESPONSE; +} + +SARA_R5_error_t SARA_R5::sendCommandWithResponse( + const char *command, const char *expectedResponse, char *responseDest, + unsigned long commandTimeout, int destSize, bool at) +{ + bool found = false; + bool error = false; + int responseIndex = 0; + int errorIndex = 0; + int destIndex = 0; + unsigned int charsRead = 0; + int responseLen = 0; + int errorLen = 0; + const char* expectedError= NULL; + //bool printedSomething = false; + + if (_printDebug == true) + { + _debugPort->print(F("sendCommandWithResponse: Command: ")); + _debugPort->println(String(command)); + } + + sendCommand(command, at); //Sending command needs to dump data to backlog buffer as well. + unsigned long timeIn = millis(); + if (SARA_R5_RESPONSE_OK_OR_ERROR == expectedResponse) { + expectedResponse = SARA_R5_RESPONSE_OK; + expectedError = SARA_R5_RESPONSE_ERROR; + responseLen = sizeof(SARA_R5_RESPONSE_OK)-1; + errorLen = sizeof(SARA_R5_RESPONSE_ERROR)-1; + } else { + responseLen = (int)strlen(expectedResponse); + } + + while ((!found) && ((timeIn + commandTimeout) > millis())) + { + 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 (responseDest != NULL) + { + if (destIndex < destSize) // Only add this char to response if there is room for it + responseDest[destIndex] = c; + destIndex++; + if (destIndex == destSize) + { + if (_printDebug == true) + { + // if (printedSomething) + // _debugPort->println(); + _debugPort->print(F("sendCommandWithResponse: Panic! responseDest is full!")); + // if (printedSomething) + // _debugPort->print(F("sendCommandWithResponse: Ignored response: ")); + } + } + } + charsRead++; + if ((errorIndex < errorLen) && (c == expectedError[errorIndex])) + { + if (++errorIndex == errorLen) + { + error = true; + found = true; + } + } + else + { + errorIndex = ((errorIndex < errorLen) && (c == expectedError[0])) ? 1 : 0; + } + if ((responseIndex < responseLen) && (c == expectedResponse[responseIndex])) + { + if (++responseIndex == responseLen) + { + found = true; + } + } + else + { + responseIndex = ((responseIndex < responseLen) && (c == expectedResponse[0])) ? 1 : 0; + } + //_saraResponseBacklog is a global array that holds the backlog of any events + //that came in while waiting for response. To be processed later within bufferedPoll(). + //Note: the expectedResponse or expectedError will also be added to the backlog + //The backlog is only used by bufferedPoll to process the URCs - which are all readable. + //bufferedPoll uses strtok - which does not like NULL characters. + //So let's make sure no NULLs end up in the backlog! + if (_saraResponseBacklogLength < _RXBuffSize) // Don't overflow the buffer + { + if (c == '\0') + _saraResponseBacklog[_saraResponseBacklogLength++] = '0'; // Change NULLs to ASCII Zeros + else + _saraResponseBacklog[_saraResponseBacklogLength++] = c; + } + } else { + yield(); + } + } + + // if (_printDebug == true) + // if (printedSomething) + // _debugPort->println(); + + pruneBacklog(); // Prune any incoming non-actionable URC's and responses/errors from the backlog + + if (found) + { + if ((true == _printAtDebug) && ((NULL != responseDest) || (NULL != expectedResponse))) { + _debugAtPort->print((NULL != responseDest) ? responseDest : expectedResponse); + } + return error ? SARA_R5_ERROR_ERROR : SARA_R5_ERROR_SUCCESS; + } + else if (charsRead == 0) + { + return SARA_R5_ERROR_NO_RESPONSE; + } + else + { + if ((true == _printAtDebug) && (NULL != responseDest)) { + _debugAtPort->print(responseDest); + } + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } +} + +// Send a custom command with an expected (potentially partial) response, store entire response +SARA_R5_error_t SARA_R5::sendCustomCommandWithResponse(const char *command, const char *expectedResponse, + char *responseDest, unsigned long commandTimeout, bool at) +{ + // Assume the user has allocated enough storage for any response. Set destSize to 32766. + return sendCommandWithResponse(command, expectedResponse, responseDest, commandTimeout, 32766, at); +} + +void SARA_R5::sendCommand(const char *command, bool at) +{ + //Check for incoming serial data. Copy it into the backlog + + // Important note: + // On ESP32, Serial.available only provides an update every ~120 bytes during the reception of long messages: + // https://gitter.im/espressif/arduino-esp32?at=5e25d6370a1cf54144909c85 + // Be aware that if a long message is being received, the code below will timeout after _rxWindowMillis = 2 millis. + // At 115200 baud, hwAvailable takes ~120 * 10 / 115200 = 10.4 millis before it indicates that data is being received. + + unsigned long timeIn = millis(); + if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL + { + while (((millis() - timeIn) < _rxWindowMillis) && (_saraResponseBacklogLength < _RXBuffSize)) //May need to escape on newline? + { + if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL + { + //_saraResponseBacklog is a global array that holds the backlog of any events + //that came in while waiting for response. To be processed later within bufferedPoll(). + //Note: the expectedResponse or expectedError will also be added to the backlog + //The backlog is only used by bufferedPoll to process the URCs - which are all readable. + //bufferedPoll uses strtok - which does not like NULL characters. + //So let's make sure no NULLs end up in the backlog! + char c = readChar(); + if (c == '\0') // Make sure no NULL characters end up in the backlog! Change them to ASCII Zeros + c = '0'; + _saraResponseBacklog[_saraResponseBacklogLength++] = c; + timeIn = millis(); + } else { + yield(); + } + } + } + + //Now send the command + if (at) + { + hwPrint(SARA_R5_COMMAND_AT); + hwPrint(command); + hwPrint("\r\n"); + } + else + { + hwPrint(command); + } +} + +SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) +{ + SARA_R5_error_t err; + char *readDest; + + if ((socket < 0) || (length < 0)) + { + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Return now if both callbacks pointers are NULL - otherwise the data will be read and lost! + if ((_socketReadCallback == NULL) && (_socketReadCallbackPlus == NULL)) + return SARA_R5_ERROR_INVALID; + + readDest = sara_r5_calloc_char(length + 1); + if (readDest == NULL) + return SARA_R5_ERROR_OUT_OF_MEMORY; + + int bytesRead; + err = socketRead(socket, length, readDest, &bytesRead); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(readDest); + return err; + } + + if (_socketReadCallback != NULL) + { + String dataAsString = ""; // Create an empty string + // 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); + } + + if (_socketReadCallbackPlus != NULL) + { + IPAddress dummyAddress = { 0, 0, 0, 0 }; + int dummyPort = 0; + _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, dummyAddress, dummyPort); + } + + free(readDest); + return SARA_R5_ERROR_SUCCESS; +} + +SARA_R5_error_t SARA_R5::parseSocketReadIndicationUDP(int socket, int length) +{ + SARA_R5_error_t err; + char *readDest; + IPAddress remoteAddress = { 0, 0, 0, 0 }; + int remotePort = 0; + + if ((socket < 0) || (length < 0)) + { + return SARA_R5_ERROR_UNEXPECTED_RESPONSE; + } + + // Return now if both callbacks pointers are NULL - otherwise the data will be read and lost! + if ((_socketReadCallback == NULL) && (_socketReadCallbackPlus == NULL)) + return SARA_R5_ERROR_INVALID; + + readDest = sara_r5_calloc_char(length + 1); + if (readDest == NULL) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + int bytesRead; + err = socketReadUDP(socket, length, readDest, &remoteAddress, &remotePort, &bytesRead); + if (err != SARA_R5_ERROR_SUCCESS) + { + free(readDest); + return err; + } + + 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); + } + + if (_socketReadCallbackPlus != NULL) + { + _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, remoteAddress, remotePort); + } + + free(readDest); + return SARA_R5_ERROR_SUCCESS; +} + +SARA_R5_error_t SARA_R5::parseSocketListenIndication(int listeningSocket, IPAddress localIP, unsigned int listeningPort, int socket, IPAddress remoteIP, unsigned int port) +{ + _lastLocalIP = localIP; + _lastRemoteIP = remoteIP; + + if (_socketListenCallback != NULL) + { + _socketListenCallback(listeningSocket, localIP, listeningPort, socket, remoteIP, port); + } + + return SARA_R5_ERROR_SUCCESS; +} + +SARA_R5_error_t SARA_R5::parseSocketCloseIndication(String *closeIndication) +{ + int search; + int socket; + + search = closeIndication->indexOf("UUSOCL: ") + strlen("UUSOCL: "); + + // Socket will be first integer, should be single-digit number between 0-6: + socket = closeIndication->substring(search, search + 1).toInt(); + + if (_socketCloseCallback != NULL) + { + _socketCloseCallback(socket); + } + + return SARA_R5_ERROR_SUCCESS; +} + +size_t SARA_R5::hwPrint(const char *s) +{ + if ((true == _printAtDebug) && (NULL != s)) { + _debugAtPort->print(s); + } + if (_hardSerial != NULL) + { + return _hardSerial->print(s); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + return _softSerial->print(s); + } +#endif + + return (size_t)0; +} + +size_t SARA_R5::hwWriteData(const char *buff, int len) +{ + if ((true == _printAtDebug) && (NULL != buff) && (0 < len) ) { + _debugAtPort->write(buff,len); + } + if (_hardSerial != NULL) + { + return _hardSerial->write((const uint8_t *)buff, len); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + return _softSerial->write((const uint8_t *)buff, len); + } +#endif + return (size_t)0; +} + +size_t SARA_R5::hwWrite(const char c) +{ + if (true == _printAtDebug) { + _debugAtPort->write(c); + } + if (_hardSerial != NULL) + { + return _hardSerial->write(c); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + return _softSerial->write(c); + } +#endif + + return (size_t)0; +} + +int SARA_R5::readAvailable(char *inString) +{ + int len = 0; + + if (_hardSerial != NULL) + { + while (_hardSerial->available()) + { + char c = (char)_hardSerial->read(); + if (inString != NULL) + { + inString[len++] = c; + } + } + if (inString != NULL) + { + inString[len] = 0; + } + //if (_printDebug == true) + // _debugPort->println(inString); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + while (_softSerial->available()) + { + char c = (char)_softSerial->read(); + if (inString != NULL) + { + inString[len++] = c; + } + } + if (inString != NULL) + { + inString[len] = 0; + } + } +#endif + + return len; +} + +char SARA_R5::readChar(void) +{ + char ret = 0; + + if (_hardSerial != NULL) + { + ret = (char)_hardSerial->read(); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + ret = (char)_softSerial->read(); + } +#endif + + return ret; +} + +int SARA_R5::hwAvailable(void) +{ + if (_hardSerial != NULL) + { + return _hardSerial->available(); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + return _softSerial->available(); + } +#endif + + return -1; +} + +void SARA_R5::beginSerial(unsigned long baud) +{ + delay(100); + if (_hardSerial != NULL) + { + _hardSerial->updateBaudRate(baud); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + _softSerial->end(); + _softSerial->begin(baud); + } +#endif + delay(100); +} + +void SARA_R5::setTimeout(unsigned long timeout) +{ + if (_hardSerial != NULL) + { + _hardSerial->setTimeout(timeout); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + _softSerial->setTimeout(timeout); + } +#endif +} + +bool SARA_R5::find(char *target) +{ + bool found = false; + if (_hardSerial != NULL) + { + found = _hardSerial->find(target); + } +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + else if (_softSerial != NULL) + { + found = _softSerial->find(target); + } +#endif + return found; +} + +SARA_R5_error_t SARA_R5::autobaud(unsigned long desiredBaud) +{ + SARA_R5_error_t err = SARA_R5_ERROR_INVALID; + int b = 0; + + while ((err != SARA_R5_ERROR_SUCCESS) && (b < NUM_SUPPORTED_BAUD)) + { + beginSerial(SARA_R5_SUPPORTED_BAUD[b++]); + setBaud(desiredBaud); + beginSerial(desiredBaud); + err = at(); + } + if (err == SARA_R5_ERROR_SUCCESS) + { + beginSerial(desiredBaud); + } + return err; +} + +char *SARA_R5::sara_r5_calloc_char(size_t num) +{ + return (char *)calloc(num, sizeof(char)); +} + +//This prunes the backlog of non-actionable events. If new actionable events are added, you must modify the if statement. +void SARA_R5::pruneBacklog() +{ + char *event; + + // if (_printDebug == true) + // { + // if (_saraResponseBacklogLength > 0) //Handy for debugging new parsing. + // { + // _debugPort->println(F("pruneBacklog: before pruning, backlog was:")); + // _debugPort->println(_saraResponseBacklog); + // _debugPort->println(F("pruneBacklog: end of backlog")); + // } + // else + // { + // _debugPort->println(F("pruneBacklog: backlog was empty")); + // } + // } + + memset(_pruneBuffer, 0, _RXBuffSize); // Clear the _pruneBuffer + + _saraResponseBacklogLength = 0; // Zero the backlog length + + char *preservedEvent; + event = strtok_r(_saraResponseBacklog, "\r\n", &preservedEvent); // Look for an 'event' - something ending in \r\n + + while (event != NULL) //If event is actionable, add it to pruneBuffer. + { + // These are the events we want to keep so they can be processed by poll / bufferedPoll + if ((strstr(event, "+UUSORD:") != NULL) + || (strstr(event, "+UUSORF:") != NULL) + || (strstr(event, "+UUSOLI:") != NULL) + || (strstr(event, "+UUSOCL:") != NULL) + || (strstr(event, "+UULOC:") != NULL) + || (strstr(event, "+UUSIMSTAT:") != NULL) + || (strstr(event, "+UUPSDA:") != NULL) + || (strstr(event, "+UUPING:") != NULL) + || (strstr(event, "+UUMQTTC:") != NULL) + || (strstr(event, "+UUCREG:") != NULL) + || (strstr(event, "+UUCEREG:") != NULL) + || (strstr(event, "+UUHTTPCR:") != NULL)) + { + strcat(_pruneBuffer, event); // The URCs are all readable text so using strcat is OK + strcat(_pruneBuffer, "\r\n"); // strtok blows away delimiter, but we want that for later. + _saraResponseBacklogLength += strlen(event) + 2; // Add the length of this event to _saraResponseBacklogLength + } + + event = strtok_r(NULL, "\r\n", &preservedEvent); // Walk though any remaining events + } + + memset(_saraResponseBacklog, 0, _RXBuffSize); //Clear out backlog buffer. + memcpy(_saraResponseBacklog, _pruneBuffer, _saraResponseBacklogLength); //Copy the pruned buffer back into the backlog + + // if (_printDebug == true) + // { + // if (_saraResponseBacklogLength > 0) //Handy for debugging new parsing. + // { + // _debugPort->println(F("pruneBacklog: after pruning, backlog is now:")); + // _debugPort->println(_saraResponseBacklog); + // _debugPort->println(F("pruneBacklog: end of backlog")); + // } + // else + // { + // _debugPort->println(F("pruneBacklog: backlog is now empty")); + // } + // } + + free(event); +} + +// GPS Helper Functions: + +// Read a source string until a delimiter is hit, store the result in destination +char *SARA_R5::readDataUntil(char *destination, unsigned int destSize, + char *source, char delimiter) +{ + + char *strEnd; + size_t len; + + strEnd = strchr(source, delimiter); + + if (strEnd != NULL) + { + len = strEnd - source; + memset(destination, 0, destSize); + memcpy(destination, source, len); + } + + return strEnd; +} + +bool SARA_R5::parseGPRMCString(char *rmcString, PositionData *pos, + ClockData *clk, SpeedData *spd) +{ + char *ptr, *search; + unsigned long tTemp; + char tempData[TEMP_NMEA_DATA_SIZE]; + + // if (_printDebug == true) + // { + // _debugPort->println(F("parseGPRMCString: rmcString: ")); + // _debugPort->println(rmcString); + // } + + // Fast-forward test to first value: + ptr = strchr(rmcString, ','); + ptr++; // Move ptr past first comma + + // If the next character is another comma, there's no time data + // Find time: + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + // Next comma should be present and not the next position + if ((search != NULL) && (search != ptr)) + { + pos->utc = atof(tempData); // Extract hhmmss.ss as float + tTemp = pos->utc; // Convert to unsigned long (discard the digits beyond the decimal point) + clk->time.ms = ((unsigned int)(pos->utc * 100)) % 100; // Extract the milliseconds + clk->time.hour = tTemp / 10000; + tTemp -= ((unsigned long)clk->time.hour * 10000); + clk->time.minute = tTemp / 100; + tTemp -= ((unsigned long)clk->time.minute * 100); + clk->time.second = tTemp; + } + else + { + pos->utc = 0.0; + clk->time.hour = 0; + clk->time.minute = 0; + clk->time.second = 0; + } + ptr = search + 1; // Move pointer to next value + + // Find status character: + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + // Should be a single character: V = Data invalid, A = Data valid + if ((search != NULL) && (search == ptr + 1)) + { + pos->status = *ptr; // Assign char at ptr to status + } + else + { + pos->status = 'X'; // Made up very bad status + } + ptr = search + 1; + + // Find latitude: + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + pos->lat = atof(tempData); // Extract ddmm.mmmmm as float + unsigned long lat_deg = pos->lat / 100; // Extract the degrees + pos->lat -= (float)lat_deg * 100.0; // Subtract the degrees leaving only the minutes + pos->lat /= 60.0; // Convert minutes into degrees + pos->lat += (float)lat_deg; // Finally add the degrees back on again + } + else + { + pos->lat = 0.0; + } + ptr = search + 1; + + // Find latitude hemishpere + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search == ptr + 1)) + { + if (*ptr == 'S') // Is the latitude South + pos->lat *= -1.0; // Make lat negative + } + ptr = search + 1; + + // Find longitude: + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + pos->lon = atof(tempData); // Extract dddmm.mmmmm as float + unsigned long lon_deg = pos->lon / 100; // Extract the degrees + pos->lon -= (float)lon_deg * 100.0; // Subtract the degrees leaving only the minutes + pos->lon /= 60.0; // Convert minutes into degrees + pos->lon += (float)lon_deg; // Finally add the degrees back on again + } + else + { + pos->lon = 0.0; + } + ptr = search + 1; + + // Find longitude hemishpere + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search == ptr + 1)) + { + if (*ptr == 'W') // Is the longitude West + pos->lon *= -1.0; // Make lon negative + } + ptr = search + 1; + + // Find speed + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + spd->speed = atof(tempData); // Extract speed over ground in knots + spd->speed *= 0.514444; // Convert to m/s + } + else + { + spd->speed = 0.0; + } + ptr = search + 1; + + // Find course over ground + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + spd->cog = atof(tempData); + } + else + { + spd->cog = 0.0; + } + ptr = search + 1; + + // Find date + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + tTemp = atol(tempData); + clk->date.day = tTemp / 10000; + tTemp -= ((unsigned long)clk->date.day * 10000); + clk->date.month = tTemp / 100; + tTemp -= ((unsigned long)clk->date.month * 100); + clk->date.year = tTemp; + } + else + { + clk->date.day = 0; + clk->date.month = 0; + clk->date.year = 0; + } + ptr = search + 1; + + // Find magnetic variation in degrees: + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search != ptr)) + { + spd->magVar = atof(tempData); + } + else + { + spd->magVar = 0.0; + } + ptr = search + 1; + + // Find magnetic variation direction + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); + if ((search != NULL) && (search == ptr + 1)) + { + if (*ptr == 'W') // Is the magnetic variation West + spd->magVar *= -1.0; // Make magnetic variation negative + } + ptr = search + 1; + + // Find position system mode + // Possible values for posMode: N = No fix, E = Estimated/Dead reckoning fix, A = Autonomous GNSS fix, + // D = Differential GNSS fix, F = RTK float, R = RTK fixed + search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, '*'); + if ((search != NULL) && (search = ptr + 1)) + { + pos->mode = *ptr; + } + else + { + pos->mode = 'X'; + } + ptr = search + 1; + + if (pos->status == 'A') + { + return true; + } + return false; +} diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h new file mode 100644 index 0000000..482a025 --- /dev/null +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -0,0 +1,1048 @@ +/* + Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker + By: Paul Clark + October 19th 2020 + + Based extensively on the: + Arduino Library for the SparkFun LTE CAT M1/NB-IoT Shield - SARA-R4 + Written by Jim Lindblom @ SparkFun Electronics, September 5, 2018 + + This Arduino library provides mechanisms to initialize and use + the SARA-R5 module over either a SoftwareSerial or hardware serial port. + + Please see LICENSE.md for the license information + +*/ + +#ifndef SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H +#define SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H + +#if (ARDUINO >= 100) +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#ifdef ARDUINO_ARCH_AVR // Arduino AVR boards (Uno, Pro Micro, etc.) +#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial +#endif + +#ifdef ARDUINO_ARCH_SAMD // Arduino SAMD boards (SAMD21, etc.) +#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial +#endif + +#ifdef ARDUINO_ARCH_APOLLO3 // Arduino Apollo boards (Artemis module, RedBoard Artemis, etc) +#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial (no longer supported with v2 of Apollo3) +// Note: paulvha has provided software serial support for v2 of the Apollo3 / Artemis core. +// Further details are available at: +// https://github.com/paulvha/apollo3/tree/master/SoftwareSerial +#endif + +#ifdef ARDUINO_ARCH_STM32 // STM32 based boards (Disco, Nucleo, etc) +#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial +#endif + +#ifdef ARDUINO_ARCH_ESP32 // ESP32 based boards +// Check to see if ESP Software Serial has been included +// Note: you need to #include at the very start of your script, +// _before_ the #include , for this to work. +// See SARA-R5_Example2_Identification_ESPSoftwareSerial for more details. +#if __has_include( ) +#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial +#else +#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial +#endif +#endif + +#ifdef ARDUINO_ARCH_ESP8266 // ESP8266 based boards +// Check to see if ESP Software Serial has been included +// Note: you need to #include at the very start of your script, +// _before_ the #include , for this to work. +// See SARA-R5_Example2_Identification_ESPSoftwareSerial for more details. +#if __has_include( ) +#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial +#else +#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial +#endif +#endif + +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED +#include // SoftwareSerial.h is guarded. It is OK to include it twice. +#endif + +#include + +#define SARA_R5_POWER_PIN -1 // Default to no pin +#define SARA_R5_RESET_PIN -1 + +// Timing +#define SARA_R5_STANDARD_RESPONSE_TIMEOUT 1000 +#define SARA_R5_10_SEC_TIMEOUT 10000 +#define SARA_R5_55_SECS_TIMEOUT 55000 +#define SARA_R5_2_MIN_TIMEOUT 120000 +#define SARA_R5_3_MIN_TIMEOUT 180000 +#define SARA_R5_SET_BAUD_TIMEOUT 500 +#define SARA_R5_POWER_OFF_PULSE_PERIOD 3200 // Hold PWR_ON low for this long to power the module off +#define SARA_R5_POWER_ON_PULSE_PERIOD 100 // Hold PWR_ON low for this long to power the module on (SARA-R510M8S) +#define SARA_R5_RESET_PULSE_PERIOD 23000 // Used to perform an abrupt emergency hardware shutdown. 23 seconds... (Yes, really!) +#define SARA_R5_POWER_OFF_TIMEOUT 40000 // Datasheet says 40 seconds... +#define SARA_R5_IP_CONNECT_TIMEOUT 130000 +#define SARA_R5_POLL_DELAY 1 +#define SARA_R5_SOCKET_WRITE_TIMEOUT 10000 + +// ## Suported AT Commands +// ### General +const char SARA_R5_COMMAND_AT[] = "AT"; // AT "Test" +const char SARA_R5_COMMAND_ECHO[] = "E"; // Local Echo +const char SARA_R5_COMMAND_MANU_ID[] = "+CGMI"; // Manufacturer identification +const char SARA_R5_COMMAND_MODEL_ID[] = "+CGMM"; // Model identification +const char SARA_R5_COMMAND_FW_VER_ID[] = "+CGMR"; // Firmware version identification +const char SARA_R5_COMMAND_SERIAL_NO[] = "+CGSN"; // Product serial number +const char SARA_R5_COMMAND_IMEI[] = "+CSN"; // IMEI identification +const char SARA_R5_COMMAND_IMSI[] = "+CIMI"; // IMSI identification +const char SARA_R5_COMMAND_CCID[] = "+CCID"; // SIM CCID +const char SARA_R5_COMMAND_REQ_CAP[] = "+GCAP"; // Request capabilities list +// ### Control and status +const char SARA_R5_COMMAND_POWER_OFF[] = "+CPWROFF"; // Module switch off +const char SARA_R5_COMMAND_FUNC[] = "+CFUN"; // Functionality (reset, etc.) +const char SARA_R5_COMMAND_CLOCK[] = "+CCLK"; // Real-time clock +const char SARA_R5_COMMAND_AUTO_TZ[] = "+CTZU"; // Automatic time zone update +const char SARA_R5_COMMAND_TZ_REPORT[] = "+CTZR"; // Time zone reporting +// ### Network service +const char SARA_R5_COMMAND_CNUM[] = "+CNUM"; // Subscriber number +const char SARA_R5_SIGNAL_QUALITY[] = "+CSQ"; +const char SARA_R5_OPERATOR_SELECTION[] = "+COPS"; +const char SARA_R5_REGISTRATION_STATUS[] = "+CREG"; +const char SARA_R5_EPSREGISTRATION_STATUS[] = "+CEREG"; +const char SARA_R5_READ_OPERATOR_NAMES[] = "+COPN"; +const char SARA_R5_COMMAND_MNO[] = "+UMNOPROF"; // MNO (mobile network operator) Profile +// ### SIM +const char SARA_R5_SIM_STATE[] = "+USIMSTAT"; +const char SARA_R5_COMMAND_SIMPIN[] = "+CPIN"; // SIM PIN +// ### SMS +const char SARA_R5_MESSAGE_FORMAT[] = "+CMGF"; // Set SMS message format +const char SARA_R5_SEND_TEXT[] = "+CMGS"; // Send SMS message +const char SARA_R5_NEW_MESSAGE_IND[] = "+CNMI"; // New [SMS] message indication +const char SARA_R5_PREF_MESSAGE_STORE[] = "+CPMS"; // Preferred message storage +const char SARA_R5_READ_TEXT_MESSAGE[] = "+CMGR"; // Read message +const char SARA_R5_DELETE_MESSAGE[] = "+CMGD"; // Delete message +// V24 control and V25ter (UART interface) +const char SARA_R5_FLOW_CONTROL[] = "&K"; // Flow control +const char SARA_R5_COMMAND_BAUD[] = "+IPR"; // Baud rate +// ### Packet switched data services +const char SARA_R5_MESSAGE_PDP_DEF[] = "+CGDCONT"; // Packet switched Data Profile context definition +const char SARA_R5_MESSAGE_PDP_CONFIG[] = "+UPSD"; // Packet switched Data Profile configuration +const char SARA_R5_MESSAGE_PDP_ACTION[] = "+UPSDA"; // Perform the action for the specified PSD profile +const char SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE[] = "+CGACT"; // Activates or deactivates the specified PDP context +const char SARA_R5_MESSAGE_ENTER_PPP[] = "D"; +const char SARA_R5_NETWORK_ASSIGNED_DATA[] = "+UPSND"; // Packet switched network-assigned data +// ### GPIO +const char SARA_R5_COMMAND_GPIO[] = "+UGPIOC"; // GPIO Configuration +// ### IP +const char SARA_R5_CREATE_SOCKET[] = "+USOCR"; // Create a new socket +const char SARA_R5_CLOSE_SOCKET[] = "+USOCL"; // Close a socket +const char SARA_R5_CONNECT_SOCKET[] = "+USOCO"; // Connect to server on socket +const char SARA_R5_WRITE_SOCKET[] = "+USOWR"; // Write data to a socket +const char SARA_R5_WRITE_UDP_SOCKET[] = "+USOST"; // Write data to a UDP socket +const char SARA_R5_READ_SOCKET[] = "+USORD"; // Read from a socket +const char SARA_R5_READ_UDP_SOCKET[] = "+USORF"; // Read UDP data from a socket +const char SARA_R5_LISTEN_SOCKET[] = "+USOLI"; // Listen for connection on socket +const char SARA_R5_GET_ERROR[] = "+USOER"; // Get last socket error. +const char SARA_R5_SOCKET_DIRECT_LINK[] = "+USODL"; // Set socket in Direct Link mode +const char SARA_R5_SOCKET_CONTROL[] = "+USOCTL"; // Query the socket parameters +const char SARA_R5_UD_CONFIGURATION[] = "+UDCONF"; // User Datagram Configuration +// ### Ping +const char SARA_R5_PING_COMMAND[] = "+UPING"; // Ping +// ### HTTP +const char SARA_R5_HTTP_PROFILE[] = "+UHTTP"; // Configure the HTTP profile. Up to 4 different profiles can be defined +const char SARA_R5_HTTP_COMMAND[] = "+UHTTPC"; // Trigger the specified HTTP command +const char SARA_R5_HTTP_PROTOCOL_ERROR[] = "+UHTTPER"; // Retrieves the error class and code of the latest HTTP operation on the specified HTTP profile. + +const char SARA_R5_MQTT_NVM[] = "+UMQTTNV"; +const char SARA_R5_MQTT_PROFILE[] = "+UMQTT"; +const char SARA_R5_MQTT_COMMAND[] = "+UMQTTC"; +const char SARA_R5_MQTT_PROTOCOL_ERROR[] = "+UMQTTER"; + +// ### GNSS +const char SARA_R5_GNSS_POWER[] = "+UGPS"; // GNSS power management configuration +const char SARA_R5_GNSS_ASSISTED_IND[] = "+UGIND"; // Assisted GNSS unsolicited indication +const char SARA_R5_GNSS_REQUEST_LOCATION[] = "+ULOC"; // Ask for localization information +const char SARA_R5_GNSS_GPRMC[] = "+UGRMC"; // Ask for localization information +const char SARA_R5_GNSS_REQUEST_TIME[] = "+UTIME"; // Ask for time information from cellular modem (CellTime) +const char SARA_R5_GNSS_TIME_INDICATION[] = "+UTIMEIND"; // Time information request status unsolicited indication +const char SARA_R5_GNSS_TIME_CONFIGURATION[] = "+UTIMECFG"; // Sets time configuration +const char SARA_R5_GNSS_CONFIGURE_SENSOR[] = "+ULOCGNSS"; // Configure GNSS sensor +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 +// 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_DOWNLOAD_FILE[] = "+UDWNFILE"; // Download a file into the module +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 +// ### File System +// TO DO: Add support for file tags. Default tag to USER +const char SARA_R5_SEC_PROFILE[] = "+USECPRF"; +const char SARA_R5_SEC_MANAGER[] = "+USECMNG"; + +// ### Response +const char SARA_R5_RESPONSE_OK[] = "\nOK\r\n"; +const char SARA_R5_RESPONSE_ERROR[] = "\nERROR\r\n"; +const char SARA_R5_RESPONSE_CONNECT[] = "\r\nCONNECT\r\n"; +#define SARA_R5_RESPONSE_OK_OR_ERROR NULL + +// CTRL+Z and ESC ASCII codes for SMS message sends +const char ASCII_CTRL_Z = 0x1A; +const char ASCII_ESC = 0x1B; + +// NMEA data size - used by parseGPRMCString +#define TEMP_NMEA_DATA_SIZE 16 + +#define NOT_AT_COMMAND false +#define AT_COMMAND true + +// The minimum memory allocation for responses from sendCommandWithResponse +// This needs to be large enough to hold the response you're expecting plus and URC's that may arrive during the timeout +#define minimumResponseAllocation 128 + +#define SARA_R5_NUM_SOCKETS 6 + +#define NUM_SUPPORTED_BAUD 9 +const unsigned long SARA_R5_SUPPORTED_BAUD[NUM_SUPPORTED_BAUD] = + { + 115200, + 9600, + 19200, + 38400, + 57600, + 230400, + 460800, + 921600, + 3000000}; +#define SARA_R5_DEFAULT_BAUD_RATE 115200 + +// Flow control definitions for AT&K +// Note: SW (XON/XOFF) flow control is not supported on the SARA_R5 +typedef enum +{ + SARA_R5_DISABLE_FLOW_CONTROL = 0, + SARA_R5_ENABLE_FLOW_CONTROL = 3 +} SARA_R5_flow_control_t; + +// The standard Europe profile should be used as the basis for all other MNOs in Europe outside of Vodafone +// and Deutsche Telekom. However, there may be changes that need to be applied to the module for proper +// operation with any given European MNO such as attach type, RAT preference, band selection, etc. Please +// consult with the preferred network provider. +typedef enum +{ + MNO_INVALID = -1, + MNO_SW_DEFAULT = 0, // Undefined / regulatory + MNO_SIM_ICCID = 1, + MNO_ATT = 2, // AT&T + MNO_VERIZON = 3, + MNO_TELSTRA = 4, + MNO_TMO = 5, // T-Mobile US + MNO_CT = 6, // China Telecom + MNO_SPRINT = 8, + MNO_VODAFONE = 19, + MNO_NTT_DOCOMO = 20, + MNO_TELUS = 21, + MNO_SOFTBANK = 28, + MNO_DT = 31, // Deutsche Telekom + MNO_US_CELLULAR = 32, + MNO_SKT = 39, + MNO_GLOBAL = 90, + MNO_STD_EUROPE = 100, + MNO_STD_EU_NOEPCO = 101 +} mobile_network_operator_t; + +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_ZERO_READ_LENGTH, // 7 + SARA_R5_ERROR_ERROR // 8 +} SARA_R5_error_t; +#define SARA_R5_SUCCESS SARA_R5_ERROR_SUCCESS + +typedef enum +{ + SARA_R5_REGISTRATION_INVALID = -1, + SARA_R5_REGISTRATION_NOT_REGISTERED = 0, + SARA_R5_REGISTRATION_HOME = 1, + SARA_R5_REGISTRATION_SEARCHING = 2, + SARA_R5_REGISTRATION_DENIED = 3, + SARA_R5_REGISTRATION_UNKNOWN = 4, + SARA_R5_REGISTRATION_ROAMING = 5, + SARA_R5_REGISTRATION_HOME_SMS_ONLY = 6, + SARA_R5_REGISTRATION_ROAMING_SMS_ONLY = 7, + SARA_R5_REGISTRATION_EMERGENCY_SERV_ONLY = 8, + SARA_R5_REGISTRATION_HOME_CSFB_NOT_PREFERRED = 9, + SARA_R5_REGISTRATION_ROAMING_CSFB_NOT_PREFERRED = 10 +} SARA_R5_registration_status_t; + +struct DateData +{ + uint8_t day; + uint8_t month; + unsigned int year; +}; + +struct TimeData +{ + uint8_t hour; + uint8_t minute; + uint8_t second; + unsigned int ms; + uint8_t tzh; + uint8_t tzm; +}; + +struct ClockData +{ + struct DateData date; + struct TimeData time; +}; + +struct PositionData +{ + float utc; + float lat; // Degrees: +/- 90 + float lon; // Degrees: +/- 180 + float alt; + char mode; + char status; +}; + +struct SpeedData +{ + float speed; // m/s + float cog; // Degrees + float magVar; // Degrees +}; + +struct operator_stats +{ + uint8_t stat; + String shortOp; + String longOp; + unsigned long numOp; + uint8_t act; +}; + +typedef enum +{ + SARA_R5_TCP = 6, + SARA_R5_UDP = 17 +} SARA_R5_socket_protocol_t; + +typedef enum +{ + SARA_R5_TCP_SOCKET_STATUS_INACTIVE, + SARA_R5_TCP_SOCKET_STATUS_LISTEN, + SARA_R5_TCP_SOCKET_STATUS_SYN_SENT, + SARA_R5_TCP_SOCKET_STATUS_SYN_RCVD, + SARA_R5_TCP_SOCKET_STATUS_ESTABLISHED, + SARA_R5_TCP_SOCKET_STATUS_FIN_WAIT_1, + SARA_R5_TCP_SOCKET_STATUS_FIN_WAIT_2, + SARA_R5_TCP_SOCKET_STATUS_CLOSE_WAIT, + SARA_R5_TCP_SOCKET_STATUS_CLOSING, + SARA_R5_TCP_SOCKET_STATUS_LAST_ACK, + SARA_R5_TCP_SOCKET_STATUS_TIME_WAIT +} SARA_R5_tcp_socket_status_t; + +typedef enum +{ + SARA_R5_MESSAGE_FORMAT_PDU = 0, + SARA_R5_MESSAGE_FORMAT_TEXT = 1 +} SARA_R5_message_format_t; + +typedef enum +{ + SARA_R5_UTIME_MODE_STOP = 0, + SARA_R5_UTIME_MODE_PPS, + SARA_R5_UTIME_MODE_ONE_SHOT, + SARA_R5_UTIME_MODE_EXT_INT +} SARA_R5_utime_mode_t; + +typedef enum +{ + SARA_R5_UTIME_SENSOR_NONE = 0, + SARA_R5_UTIME_SENSOR_GNSS_LTE = 1, + SARA_R5_UTIME_SENSOR_LTE +} SARA_R5_utime_sensor_t; + +typedef enum +{ + SARA_R5_UTIME_URC_CONFIGURATION_DISABLED = 0, + SARA_R5_UTIME_URC_CONFIGURATION_ENABLED +} SARA_R5_utime_urc_configuration_t; + +typedef enum +{ + SARA_R5_SIM_NOT_PRESENT = 0, + SARA_R5_SIM_PIN_NEEDED, + SARA_R5_SIM_PIN_BLOCKED, + SARA_R5_SIM_PUK_BLOCKED, + SARA_R5_SIM_NOT_OPERATIONAL, + SARA_R5_SIM_RESTRICTED, + SARA_R5_SIM_OPERATIONAL + //SARA_R5_SIM_PHONEBOOK_READY, // Not reported by SARA-R5 + //SARA_R5_SIM_USIM_PHONEBOOK_READY, // Not reported by SARA-R5 + //SARA_R5_SIM_TOOLKIT_REFRESH_SUCCESSFUL, // Not reported by SARA-R5 + //SARA_R5_SIM_TOOLKIT_REFRESH_UNSUCCESSFUL, // Not reported by SARA-R5 + //SARA_R5_SIM_PPP_CONNECTION_ACTIVE, // Not reported by SARA-R5 + //SARA_R5_SIM_VOICE_CALL_ACTIVE, // Not reported by SARA-R5 + //SARA_R5_SIM_CSD_CALL_ACTIVE // Not reported by SARA-R5 +} SARA_R5_sim_states_t; + +#define SARA_R5_NUM_PSD_PROFILES 6 // Number of supported PSD profiles +#define SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS 11 // Number of supported PDP context identifiers +#define SARA_R5_NUM_HTTP_PROFILES 4 // Number of supported HTTP profiles + +typedef enum +{ + SARA_R5_HTTP_OP_CODE_SERVER_IP = 0, + SARA_R5_HTTP_OP_CODE_SERVER_NAME, + SARA_R5_HTTP_OP_CODE_USERNAME, + SARA_R5_HTTP_OP_CODE_PASSWORD, + SARA_R5_HTTP_OP_CODE_AUTHENTICATION, + SARA_R5_HTTP_OP_CODE_SERVER_PORT, + SARA_R5_HTTP_OP_CODE_SECURE, + SARA_R5_HTTP_OP_CODE_REQUEST_TIMEOUT, + SARA_R5_HTTP_OP_CODE_ADD_CUSTOM_HEADERS = 9 +} SARA_R5_http_op_codes_t; + +typedef enum +{ + SARA_R5_HTTP_COMMAND_HEAD = 0, + SARA_R5_HTTP_COMMAND_GET, + SARA_R5_HTTP_COMMAND_DELETE, + SARA_R5_HTTP_COMMAND_PUT, + SARA_R5_HTTP_COMMAND_POST_FILE, + SARA_R5_HTTP_COMMAND_POST_DATA, + SARA_R5_HTTP_COMMAND_GET_FOTA = 100 +} SARA_R5_http_commands_t; + +typedef enum +{ + SARA_R5_HTTP_CONTENT_APPLICATION_X_WWW = 0, + SARA_R5_HTTP_CONTENT_TEXT_PLAIN, + SARA_R5_HTTP_CONTENT_APPLICATION_OCTET, + SARA_R5_HTTP_CONTENT_MULTIPART_FORM, + SARA_R5_HTTP_CONTENT_APPLICATION_JSON, + SARA_R5_HTTP_CONTENT_APPLICATION_XML, + SARA_R5_HTTP_CONTENT_USER_DEFINED +} SARA_R5_http_content_types_t; + +typedef enum +{ + SARA_R5_MQTT_NV_RESTORE = 0, + SARA_R5_MQTT_NV_SET, + SARA_R5_MQTT_NV_STORE, +} SARA_R5_mqtt_nv_parameter_t; + +typedef enum +{ + SARA_R5_MQTT_PROFILE_CLIENT_ID = 0, + SARA_R5_MQTT_PROFILE_SERVERNAME = 2, + SARA_R5_MQTT_PROFILE_IPADDRESS, + SARA_R5_MQTT_PROFILE_USERNAMEPWD, + SARA_R5_MQTT_PROFILE_QOS = 6, + SARA_R5_MQTT_PROFILE_RETAIN, + SARA_R5_MQTT_PROFILE_TOPIC, + SARA_R5_MQTT_PROFILE_MESSAGE, + SARA_R5_MQTT_PROFILE_INACTIVITYTIMEOUT, + SARA_R5_MQTT_PROFILE_SECURE, +} SARA_R5_mqtt_profile_opcode_t; + +typedef enum +{ + SARA_R5_MQTT_COMMAND_LOGOUT = 0, + SARA_R5_MQTT_COMMAND_LOGIN, + SARA_R5_MQTT_COMMAND_PUBLISH, + SARA_R5_MQTT_COMMAND_PUBLISHFILE, + SARA_R5_MQTT_COMMAND_SUBSCRIBE, + SARA_R5_MQTT_COMMAND_UNSUBSCRIBE, + SARA_R5_MQTT_COMMAND_READ, + SARA_R5_MQTT_COMMAND_PING, + SARA_R5_MQTT_COMMAND_PUBLISHBINARY, +} SARA_R5_mqtt_command_opcode_t; + +typedef enum +{ + SARA_R5_PSD_CONFIG_PARAM_PROTOCOL = 0, + SARA_R5_PSD_CONFIG_PARAM_APN, + //SARA_R5_PSD_CONFIG_PARAM_USERNAME, // Not allowed on SARA-R5 + //SARA_R5_PSD_CONFIG_PARAM_PASSWORD, // Not allowed on SARA-R5 + SARA_R5_PSD_CONFIG_PARAM_DNS1 = 4, + SARA_R5_PSD_CONFIG_PARAM_DNS2, + //SARA_R5_PSD_CONFIG_PARAM_AUTHENTICATION, // Not allowed on SARA-R5 + //SARA_R5_PSD_CONFIG_PARAM_IP_ADDRESS, // Not allowed on SARA-R5 + //SARA_R5_PSD_CONFIG_PARAM_DATA_COMPRESSION, // Not allowed on SARA-R5 + //SARA_R5_PSD_CONFIG_PARAM_HEADER_COMPRESSION, // Not allowed on SARA-R5 + SARA_R5_PSD_CONFIG_PARAM_MAP_TO_CID = 100 +} SARA_R5_pdp_configuration_parameter_t; + +typedef enum +{ + SARA_R5_PSD_PROTOCOL_IPV4 = 0, + SARA_R5_PSD_PROTOCOL_IPV6, + SARA_R5_PSD_PROTOCOL_IPV4V6_V4_PREF, + SARA_R5_PSD_PROTOCOL_IPV4V6_V6_PREF +} SARA_R5_pdp_protocol_type_t; + +typedef enum +{ + SARA_R5_PSD_ACTION_RESET = 0, + SARA_R5_PSD_ACTION_STORE, + SARA_R5_PSD_ACTION_LOAD, + SARA_R5_PSD_ACTION_ACTIVATE, + SARA_R5_PSD_ACTION_DEACTIVATE +} SARA_R5_pdp_actions_t; + +typedef enum +{ + SARA_R5_SEC_PROFILE_PARAM_CERT_VAL_LEVEL = 0, + SARA_R5_SEC_PROFILE_PARAM_TLS_VER, + SARA_R5_SEC_PROFILE_PARAM_CYPHER_SUITE, + SARA_R5_SEC_PROFILE_PARAM_ROOT_CA, + SARA_R5_SEC_PROFILE_PARAM_HOSTNAME, + SARA_R5_SEC_PROFILE_PARAM_CLIENT_CERT, + SARA_R5_SEC_PROFILE_PARAM_CLIENT_KEY, + SARA_R5_SEC_PROFILE_PARAM_CLIENT_KEY_PWD, + SARA_R5_SEC_PROFILE_PARAM_PSK, + SARA_R5_SEC_PROFILE_PARAM_PSK_IDENT, + SARA_R5_SEC_PROFILE_PARAM_SNI, +} SARA_R5_sec_profile_parameter_t; + +typedef enum +{ + SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_NO = 0, + SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_YESNOURL, + SARA_R5_SEC_PROFILE_CERVTAL_OPCODE_YESURL, + SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_YESURLDATE, +} SARA_R5_sec_profile_certval_op_code_t; + +typedef enum +{ + SARA_R5_SEC_PROFILE_TLS_OPCODE_ANYVER = 0, + SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_0, + SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_1, + SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_2, + SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_3, +} SARA_R5_sec_profile_tls_op_code_t; + +typedef enum +{ + SARA_R5_SEC_PROFILE_SUITE_OPCODE_PROPOSEDDEFAULT = 0, +} SARA_R5_sec_profile_suite_op_code_t; + +typedef enum +{ + SARA_R5_SEC_MANAGER_OPCODE_IMPORT = 0, +} SARA_R5_sec_manager_opcode_t; + +typedef enum +{ + SARA_R5_SEC_MANAGER_ROOTCA = 0, + SARA_R5_SEC_MANAGER_CLIENT_CERT, + SARA_R5_SEC_MANAGER_CLIENT_KEY, + SARA_R5_SEC_MANAGER_SERVER_CERT +} SARA_R5_sec_manager_parameter_t; + +typedef enum +{ + MINIMUM_FUNCTIONALITY = 0, // (disable both transmit and receive RF circuits by deactivating both CS and PS services) + FULL_FUNCTIONALITY = 1, + AIRPLANE_MODE = 4, + SIM_TOOLKIT_ENABLE_DEDICATED = 6, + SIM_TOOLKIT_DISABLE_DEDICATED = 7, + SIM_TOOLKIT_ENABLE_RAW = 9, + FAST_SAFE_POWER_OFF = 10, + //SILENT_RESET_WITHOUT_SIM = 15, // Not supported on SARA-R5 + SILENT_RESET_WITH_SIM = 16 + //MINIMUM_FUNCTIONALITY = 19, // Not supported on SARA-R5 + //DEEP_LOW_POWER_STATE = 127 // Not supported on SARA-R5 +} SARA_R5_functionality_t; + +class SARA_R5 : public Print +{ +public: + // Constructor + // The library will use the powerPin and resetPin (if provided) to power the module off/on and perform an emergency reset + // maxInitTries sets the maximum number of initialization attempts. .init is called by .begin. + SARA_R5(int powerPin = SARA_R5_POWER_PIN, int resetPin = SARA_R5_RESET_PIN, uint8_t maxInitTries = 9); + + ~SARA_R5(); + // Begin -- initialize module and ensure it's connected +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + bool begin(SoftwareSerial &softSerial, unsigned long baud = 9600); +#endif + bool begin(HardwareSerial &hardSerial, unsigned long baud = 9600, bool doBegin = true); + + // Debug prints + void enableDebugging(Print &debugPort = Serial); //Turn on debug printing. If user doesn't specify then Serial will be used. + void enableAtDebugging(Print &debugPort = Serial); //Turn on AT debug printing. If user doesn't specify then Serial will be used. + + // Invert the polarity of the power pin - if required + // Normally the SARA's power pin is pulled low and released to toggle the power + // But the Asset Tracker needs this to be pulled high and released instead + void invertPowerPin(bool invert = false); + + SARA_R5_error_t modulePowerOff(void); // Graceful disconnect and shutdown using +CPWROFF. + void modulePowerOn(void); // Requires access to the PWR_ON pin + + // Loop polling and polling setup - process URC's etc. from the module + + // This function was originally written by Matthew Menze for the LTE Shield (SARA-R4) library + // See: https://github.com/sparkfun/SparkFun_LTE_Shield_Arduino_Library/pull/8 + // It does the same job as ::poll but also processes any 'old' data stored in the backlog first + // It also has a built-in timeout - which ::poll does not + // Use this - it is way better than ::poll. Thank you Matthew! + bool bufferedPoll(void); + + // This is the original poll function. + // It is 'blocking' - it does not return when serial data is available until it receives a `\n`. + // ::bufferedPoll is the new improved version. It processes any data in the backlog and includes a timeout. + // Retained for backward-compatibility and just in case you do want to (temporarily) ignore any data in the backlog + bool poll(void); + + // Callbacks (called during polling) + void setSocketListenCallback(void (*socketListenCallback)(int, IPAddress, unsigned int, int, IPAddress, unsigned int)); // listen Socket, local IP Address, listen Port, socket, remote IP Address, port + // This is the original read socket callback - called when a +UUSORD or +UUSORF URC is received + // It works - and handles binary data correctly - but the remote IP Address and Port are lost for UDP connections + // setSocketReadCallbackPlus is preferred! + void setSocketReadCallback(void (*socketReadCallback)(int, String)); // socket, read data + void setSocketReadCallbackPlus(void (*socketReadCallbackPlus)(int, const char *, int, IPAddress, int)); // socket, read data, length, remoteAddress, remotePort + void setSocketCloseCallback(void (*socketCloseCallback)(int)); // socket + void setGpsReadCallback(void (*gpsRequestCallback)(ClockData time, + PositionData gps, SpeedData spd, unsigned long uncertainty)); + void setSIMstateReportCallback(void (*simStateRequestCallback)(SARA_R5_sim_states_t state)); + void setPSDActionCallback(void (*psdActionRequestCallback)(int result, IPAddress ip)); + void setPingCallback(void (*pingRequestCallback)(int retry, int p_size, String remote_hostname, IPAddress ip, int ttl, long rtt)); + void setHTTPCommandCallback(void (*httpCommandRequestCallback)(int profile, int command, int result)); + void setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)); + SARA_R5_error_t setRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, + unsigned int lac, unsigned int ci, int Act)); + SARA_R5_error_t setEpsRegistrationCallback(void (*epsRegistrationCallback)(SARA_R5_registration_status_t status, + unsigned int tac, unsigned int ci, int Act)); + + // Direct write/print to cell serial port + virtual size_t write(uint8_t c); + virtual size_t write(const char *str); + virtual size_t write(const char *buffer, size_t size); + + // General AT Commands + SARA_R5_error_t at(void); + SARA_R5_error_t enableEcho(bool enable = true); + String getManufacturerID(void); + String getModelID(void); + String getFirmwareVersion(void); + String getSerialNo(void); + String getIMEI(void); + String getIMSI(void); + String getCCID(void); + String getSubscriberNo(void); + String getCapabilities(void); + + // Control and status AT commands + SARA_R5_error_t reset(void); + String clock(void); + // 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); + SARA_R5_error_t setUtimeIndication(SARA_R5_utime_urc_configuration_t config = SARA_R5_UTIME_URC_CONFIGURATION_ENABLED); // +UTIMEIND + SARA_R5_error_t getUtimeIndication(SARA_R5_utime_urc_configuration_t *config); + SARA_R5_error_t setUtimeConfiguration(int32_t offsetNanoseconds = 0, int32_t offsetSeconds = 0); // +UTIMECFG + SARA_R5_error_t getUtimeConfiguration(int32_t *offsetNanoseconds, int32_t *offsetSeconds); + + // Network service AT commands + int8_t rssi(void); // Receive signal strength + SARA_R5_registration_status_t registration(bool eps = true); + bool setNetworkProfile(mobile_network_operator_t mno, bool autoReset = false, bool urcNotification = false); + mobile_network_operator_t getNetworkProfile(void); + typedef enum + { + PDP_TYPE_INVALID = -1, + PDP_TYPE_IP = 0, + PDP_TYPE_NONIP = 1, + PDP_TYPE_IPV4V6 = 2, + PDP_TYPE_IPV6 = 3 + } SARA_R5_pdp_type; + SARA_R5_error_t setAPN(String apn, uint8_t cid = 1, SARA_R5_pdp_type pdpType = PDP_TYPE_IP); // Set the Access Point Name + SARA_R5_error_t getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp_type* pdpType = NULL); // Return the apn and IP address for the chosen context identifier + + SARA_R5_error_t getSimStatus(String* code); + SARA_R5_error_t setSimPin(String pin); + + // SIM + // Status report Mode: + // Bit States reported + // 0 Reports the (U)SIM initialization status ('s from 0 to 6 may be reported) + // 1 Reports the (U)SIM phonebook initialization status ('s from 7 to 8 may be reported) + // 2 Reports the (U)SIM toolkit REFRESH proactive command execution result ('s from 9 to 13 may be reported) + // Note: For the SARA-R5: =7, 8, 9, 10, 11, 12 and 13 are not reported. + SARA_R5_error_t setSIMstateReportingMode(int mode); + SARA_R5_error_t getSIMstateReportingMode(int *mode); + + typedef enum + { + L2P_DEFAULT, + L2P_PPP, + L2P_M_HEX, + L2P_M_RAW_IP, + L2P_M_OPT_PPP + } SARA_R5_l2p_t; + SARA_R5_error_t enterPPP(uint8_t cid = 1, char dialing_type_char = 0, + unsigned long dialNumber = 99, SARA_R5_l2p_t l2p = L2P_DEFAULT); + + uint8_t getOperators(struct operator_stats *op, int maxOps = 3); + SARA_R5_error_t registerOperator(struct operator_stats oper); + SARA_R5_error_t automaticOperatorSelection(); + SARA_R5_error_t getOperator(String *oper); + SARA_R5_error_t deregisterOperator(void); + + // SMS -- Short Messages Service + SARA_R5_error_t setSMSMessageFormat(SARA_R5_message_format_t textMode = SARA_R5_MESSAGE_FORMAT_TEXT); + SARA_R5_error_t sendSMS(String number, String message); + SARA_R5_error_t getPreferredMessageStorage(int *used, int *total, String memory = "ME"); + SARA_R5_error_t readSMSmessage(int location, String *unread, String *from, String *dateTime, String *message); + SARA_R5_error_t deleteSMSmessage(int location, int deleteFlag = 0); // Default to deleting the single message at the specified location + SARA_R5_error_t deleteReadSMSmessages(void) { return (deleteSMSmessage( 1, 1 )); }; // Delete all the read messages from preferred storage + SARA_R5_error_t deleteReadSentSMSmessages(void) { return (deleteSMSmessage( 1, 2 )); }; // Delete the read and sent messages from preferred storage + SARA_R5_error_t deleteReadSentUnsentSMSmessages(void) { return (deleteSMSmessage( 1, 3 )); }; // Delete the read, sent and unsent messages from preferred storage + SARA_R5_error_t deleteAllSMSmessages(void) { return (deleteSMSmessage( 1, 4 )); }; // Delete the read, sent, unsent and unread messages from preferred storage + + // V24 Control and V25ter (UART interface) AT commands + SARA_R5_error_t setBaud(unsigned long baud); + SARA_R5_error_t setFlowControl(SARA_R5_flow_control_t value = SARA_R5_ENABLE_FLOW_CONTROL); + + // GPIO + // GPIO pin map + typedef enum + { + GPIO1 = 16, + GPIO2 = 23, + GPIO3 = 24, + GPIO4 = 25, + GPIO5 = 42, + GPIO6 = 19 + } SARA_R5_gpio_t; + // GPIO pin modes + typedef enum + { + GPIO_MODE_INVALID = -1, + GPIO_OUTPUT = 0, + GPIO_INPUT, + NETWORK_STATUS, + GNSS_SUPPLY_ENABLE, + GNSS_DATA_READY, + GNSS_RTC_SHARING, + JAMMING_DETECTION, + SIM_CARD_DETECTION, + HEADSET_DETECTION, + GSM_TX_BURST_INDICATION, + MODULE_STATUS_INDICATION, + MODULE_OPERATING_MODE_INDICATION, + I2S_DIGITAL_AUDIO_INTERFACE, + SPI_SERIAL_INTERFACE, + MASTER_CLOCK_GENRATION, + UART_INTERFACE, + WIFI_ENABLE, + RING_INDICATION = 18, + LAST_GASP_ENABLE, + EXTERNAL_GNSS_ANTENNA, + TIME_PULSE_GNSS, + TIME_PULSE_OUTPUT, + TIMESTAMP, + FAST_POWER_OFF, + LWM2M_PULSE, + HARDWARE_FLOW_CONTROL, + ANTENNA_TUNING, + EXT_GNSS_TIME_PULSE, + EXT_GNSS_TIMESTAMP, + DTR_MODE, + KHZ_32768_OUT = 32, + PAD_DISABLED = 255 + } SARA_R5_gpio_mode_t; + SARA_R5_error_t setGpioMode(SARA_R5_gpio_t gpio, SARA_R5_gpio_mode_t mode, int value = 0); + 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. + 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. 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 + SARA_R5_error_t socketWrite(int socket, const char *str, int len = -1); + SARA_R5_error_t socketWrite(int socket, String str); // OK for binary data + // Write UDP data to the specified IP Address and port. + // Works with binary data - but you must specify the data length when using the const char * versions + // If you let len default to -1, strlen is used to calculate the data length - and will be incorrect for binary data + SARA_R5_error_t socketWriteUDP(int socket, const char *address, int port, const char *str, int len = -1); + SARA_R5_error_t socketWriteUDP(int socket, IPAddress address, int port, const char *str, int len = -1); + SARA_R5_error_t socketWriteUDP(int socket, String address, int port, String str); + // 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 + // 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) + // 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 + SARA_R5_error_t socketListen(int socket, unsigned int port); + // Place the socket into direct link mode - making it easy to transfer binary data. Wait two seconds and then send +++ to exit the link. + SARA_R5_error_t socketDirectLinkMode(int socket); + // Configure when direct link data is sent + SARA_R5_error_t socketDirectLinkTimeTrigger(int socket, unsigned long timerTrigger); + SARA_R5_error_t socketDirectLinkDataLengthTrigger(int socket, int dataLengthTrigger); + SARA_R5_error_t socketDirectLinkCharacterTrigger(int socket, int characterTrigger); + SARA_R5_error_t socketDirectLinkCongestionTimer(int socket, unsigned long congestionTimer); + // Use +USOCTL (Socket control) to query the socket parameters + SARA_R5_error_t querySocketType(int socket, SARA_R5_socket_protocol_t *protocol); + SARA_R5_error_t querySocketLastError(int socket, int *error); + SARA_R5_error_t querySocketTotalBytesSent(int socket, uint32_t *total); + SARA_R5_error_t querySocketTotalBytesReceived(int socket, uint32_t *total); + SARA_R5_error_t querySocketRemoteIPAddress(int socket, IPAddress *address, int *port); + SARA_R5_error_t querySocketStatusTCP(int socket, SARA_R5_tcp_socket_status_t *status); + SARA_R5_error_t querySocketOutUnackData(int socket, uint32_t *total); + // Return the most recent socket error + int socketGetLastError(); + // Return the remote IP Address from the most recent socket listen indication (socket connection) + // Use the socket listen callback to get the full address and port information + IPAddress lastRemoteIP(void); + + // Ping + SARA_R5_error_t ping(String remote_host, int retry = 4, int p_size = 32, unsigned long timeout = 5000, int ttl = 32); + + // HTTP + SARA_R5_error_t resetHTTPprofile(int profile); // Reset the HTTP profile. Note: The configured HTTP profile parameters are not saved in the non volatile memory. + SARA_R5_error_t setHTTPserverIPaddress(int profile, IPAddress address); // Default: empty string + SARA_R5_error_t setHTTPserverName(int profile, String server); // Default: empty string + SARA_R5_error_t setHTTPusername(int profile, String username); // Default: empty string + SARA_R5_error_t setHTTPpassword(int profile, String password); // Default: empty string + SARA_R5_error_t setHTTPauthentication(int profile, bool authenticate); // Default: no authentication + SARA_R5_error_t setHTTPserverPort(int profile, int port); // Default: 80 + SARA_R5_error_t setHTTPcustomHeader(int profile, String header); // Default: format 0:Content-Type:application/json" + SARA_R5_error_t setHTTPsecure(int profile, bool secure, int secprofile = -1); // Default: disabled (HTTP on port 80). Set to true for HTTPS on port 443 + // TO DO: Add custom request headers + SARA_R5_error_t getHTTPprotocolError(int profile, int *error_class, int *error_code); // Read the most recent HTTP protocol error for this profile + SARA_R5_error_t sendHTTPGET(int profile, String path, String responseFilename); + SARA_R5_error_t sendHTTPPOSTdata(int profile, String path, String responseFilename, String data, SARA_R5_http_content_types_t httpContentType); + SARA_R5_error_t sendHTTPPOSTfile(int profile, String path, String responseFilename, String requestFile, SARA_R5_http_content_types_t httpContentType); + + SARA_R5_error_t nvMQTT(SARA_R5_mqtt_nv_parameter_t parameter); + SARA_R5_error_t setMQTTclientId(String clientId); + SARA_R5_error_t setMQTTserver(String serverName, int port); + SARA_R5_error_t setMQTTsecure(bool secure, int secprofile = -1); + SARA_R5_error_t connectMQTT(void); + SARA_R5_error_t disconnectMQTT(void); + SARA_R5_error_t subscribeMQTTtopic(int max_Qos, String topic); + SARA_R5_error_t unsubscribeMQTTtopic(String topic); + SARA_R5_error_t readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead); + SARA_R5_error_t getMQTTprotocolError(int *error_code, int *error_code2); + + // Configure security profiles + SARA_R5_error_t resetSecurityProfile(int secprofile); + SARA_R5_error_t configSecurityProfileString(int secprofile, SARA_R5_sec_profile_parameter_t parameter, String value); + SARA_R5_error_t configSecurityProfile(int secprofile, SARA_R5_sec_profile_parameter_t parameter, int value); + SARA_R5_error_t setSecurityManager(SARA_R5_sec_manager_opcode_t opcode, SARA_R5_sec_manager_parameter_t parameter, String name, String data); + + // Packet Switched Data + // Configure the PDP using +UPSD. See SARA_R5_pdp_configuration_parameter_t for the list of parameters: protocol, APN, username, DNS, etc. + SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, int value); // Set parameters in the chosen PSD profile + SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, SARA_R5_pdp_protocol_type_t value); // Set parameters in the chosen PSD profile + SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, String value); // Set parameters in the chosen PSD profile + SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, IPAddress value); // Set parameters in the chosen PSD profile + SARA_R5_error_t performPDPaction(int profile, SARA_R5_pdp_actions_t action); // Performs the requested action for the specified PSD profile: reset, store, load, activate, deactivate + SARA_R5_error_t activatePDPcontext(bool status, int cid = -1); // Activates or deactivates the specified PDP context. Default to all (cid = -1) + SARA_R5_error_t getNetworkAssignedIPAddress(int profile, IPAddress *address); // Get the dynamic IP address assigned during PDP context activation + + // GPS + typedef enum + { + GNSS_SYSTEM_GPS = 1, + GNSS_SYSTEM_SBAS = 2, + GNSS_SYSTEM_GALILEO = 4, + GNSS_SYSTEM_BEIDOU = 8, + GNSS_SYSTEM_IMES = 16, + GNSS_SYSTEM_QZSS = 32, + GNSS_SYSTEM_GLONASS = 64 + } gnss_system_t; + typedef enum + { + GNSS_AIDING_MODE_NONE = 0, + GNSS_AIDING_MODE_AUTOMATIC = 1, + GNSS_AIDING_MODE_ASSISTNOW_OFFLINE = 2, + GNSS_AIDING_MODE_ASSISTNOW_ONLINE = 4, + GNSS_AIDING_MODE_ASSISTNOW_AUTONOMOUS = 8 + } gnss_aiding_mode_t; + bool isGPSon(void); + SARA_R5_error_t gpsPower(bool enable = true, + gnss_system_t gnss_sys = GNSS_SYSTEM_GPS, + gnss_aiding_mode_t gnss_aiding = GNSS_AIDING_MODE_AUTOMATIC); + //SARA_R5_error_t gpsEnableClock(bool enable = true); + //SARA_R5_error_t gpsGetClock(struct ClockData *clock); + //SARA_R5_error_t gpsEnableFix(bool enable = true); + //SARA_R5_error_t gpsGetFix(float *lat, float *lon, unsigned int *alt, uint8_t *quality, uint8_t *sat); + //SARA_R5_error_t gpsGetFix(struct PositionData *pos); + //SARA_R5_error_t gpsEnablePos(bool enable = true); + //SARA_R5_error_t gpsGetPos(struct PositionData *pos); + //SARA_R5_error_t gpsEnableSat(bool enable = true); + //SARA_R5_error_t gpsGetSat(uint8_t *sats); + SARA_R5_error_t gpsEnableRmc(bool enable = true); // Enable GPRMC messages + SARA_R5_error_t gpsGetRmc(struct PositionData *pos, struct SpeedData *speed, struct ClockData *clk, bool *valid); //Parse a GPRMC message + //SARA_R5_error_t gpsEnableSpeed(bool enable = true); + //SARA_R5_error_t gpsGetSpeed(struct SpeedData *speed); + + SARA_R5_error_t gpsRequest(unsigned int timeout, uint32_t accuracy, bool detailed = true, unsigned int sensor = 3); + + //CellLocate + SARA_R5_error_t gpsAidingServerConf(const char *primaryServer, const char *secondaryServer, const char *authToken, + unsigned int days = 14, unsigned int period = 4, unsigned int resolution = 1, + 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); // 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. + // Append data to a file, delete file first to not appends the data. + SARA_R5_error_t appendFileContents(String filename, String str); + SARA_R5_error_t appendFileContents(String filename, const char *str, int len); + 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); + + // Send a custom command with an expected (potentially partial) response, store entire response + SARA_R5_error_t sendCustomCommandWithResponse(const char *command, const char *expectedResponse, + char *responseDest, unsigned long commandTimeout = SARA_R5_STANDARD_RESPONSE_TIMEOUT, bool at = true); + +private: + HardwareSerial *_hardSerial; +#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED + SoftwareSerial *_softSerial; +#endif + + Print *_debugPort; //The stream to send debug messages to if enabled. Usually Serial. + bool _printDebug = false; //Flag to print debugging variables + Print *_debugAtPort; //The stream to send debug messages to if enabled. Usually Serial. + bool _printAtDebug = false; //Flag to print debugging variables + + int _powerPin; + int _resetPin; + bool _invertPowerPin = false; + + unsigned long _baud; + IPAddress _lastRemoteIP; + IPAddress _lastLocalIP; + uint8_t _maxInitTries; + 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. See notes in .cpp re. ESP32! + 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); + void (*_socketReadCallback)(int, String); + void (*_socketReadCallbackPlus)(int, const char *, int, IPAddress, int); // socket, data, length, remoteAddress, remotePort + void (*_socketCloseCallback)(int); + void (*_gpsRequestCallback)(ClockData, PositionData, SpeedData, unsigned long); + void (*_simStateReportCallback)(SARA_R5_sim_states_t); + void (*_psdActionRequestCallback)(int, IPAddress); + void (*_pingRequestCallback)(int, int, String, IPAddress, int, long); + void (*_httpCommandRequestCallback)(int, int, int); + void (*_mqttCommandRequestCallback)(int, int); + void (*_registrationCallback)(SARA_R5_registration_status_t status, unsigned int lac, unsigned int ci, int Act); + void (*_epsRegistrationCallback)(SARA_R5_registration_status_t status, unsigned int tac, unsigned int ci, int Act); + + + int _lastSocketProtocol[SARA_R5_NUM_SOCKETS]; // Record the protocol for each socket to avoid having to call querySocketType in parseSocketReadIndication + + typedef enum + { + SARA_R5_INIT_STANDARD, + SARA_R5_INIT_AUTOBAUD, + SARA_R5_INIT_RESET + } SARA_R5_init_type_t; + + SARA_R5_error_t init(unsigned long baud, SARA_R5_init_type_t initType = SARA_R5_INIT_STANDARD); + + void powerOn(void); // Brief pulse on PWR_ON to turn module back on + void powerOff(void); // Long pulse on PWR_ON to do a graceful shutdown. Note modulePowerOff (+CPWROFF) is preferred. + + void hwReset(void); + + SARA_R5_error_t setMNOprofile(mobile_network_operator_t mno, bool autoReset = false, bool urcNotification = false); + SARA_R5_error_t getMNOprofile(mobile_network_operator_t *mno); + + // Wait for an expected response (don't send a command) + SARA_R5_error_t waitForResponse(const char *expectedResponse, const char *expectedError, uint16_t timeout); + + // Send command with an expected (potentially partial) response, store entire response + SARA_R5_error_t sendCommandWithResponse(const char *command, const char *expectedResponse, + char *responseDest, unsigned long commandTimeout, int destSize = minimumResponseAllocation, bool at = true); + + // 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); + SARA_R5_error_t parseSocketCloseIndication(String *closeIndication); + + // UART Functions + size_t hwPrint(const char *s); + size_t hwWriteData(const char *buff, int len); + size_t hwWrite(const char c); + int readAvailable(char *inString); + char readChar(void); + int hwAvailable(void); + void beginSerial(unsigned long baud); + void setTimeout(unsigned long timeout); + bool find(char *target); + + SARA_R5_error_t autobaud(unsigned long desiredBaud); + + char *sara_r5_calloc_char(size_t num); + + bool processURCEvent(const char *event); + void pruneBacklog(void); + + // GPS Helper functions + char *readDataUntil(char *destination, unsigned int destSize, char *source, char delimiter); + bool parseGPRMCString(char *rmcString, PositionData *pos, ClockData *clk, SpeedData *spd); +}; + +#endif //SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H From 98e69586aede4f6d7bb1a5ae8fae5c9671531f46 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Sun, 8 May 2022 18:54:00 +0200 Subject: [PATCH 02/23] Delete SparkFun_u-blox_SARA-R5_Arduino_Library.cpp --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 6326 ----------------- 1 file changed, 6326 deletions(-) delete mode 100644 src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp deleted file mode 100644 index 795097d..0000000 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ /dev/null @@ -1,6326 +0,0 @@ -/* - Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker - By: Paul Clark - October 19th 2020 - - Based extensively on the: - Arduino Library for the SparkFun LTE CAT M1/NB-IoT Shield - SARA-R4 - Written by Jim Lindblom @ SparkFun Electronics, September 5, 2018 - - This Arduino library provides mechanisms to initialize and use - the SARA-R5 module over either a SoftwareSerial or hardware serial port. - - Please see LICENSE.md for the license information - -*/ - -#include - -SARA_R5::SARA_R5(int powerPin, int resetPin, uint8_t maxInitTries) -{ -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - _softSerial = NULL; -#endif - _hardSerial = NULL; - _baud = 0; - _resetPin = resetPin; - _powerPin = powerPin; - _invertPowerPin = false; - _maxInitTries = maxInitTries; - _socketListenCallback = NULL; - _socketReadCallback = NULL; - _socketReadCallbackPlus = NULL; - _socketCloseCallback = NULL; - _gpsRequestCallback = NULL; - _simStateReportCallback = NULL; - _psdActionRequestCallback = NULL; - _pingRequestCallback = NULL; - _httpCommandRequestCallback = NULL; - _mqttCommandRequestCallback = NULL; - _registrationCallback = NULL; - _epsRegistrationCallback = NULL; - _debugAtPort = NULL; - _debugPort = NULL; - _printDebug = false; - _lastRemoteIP = {0, 0, 0, 0}; - _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; - _bufferedPollReentrant = false; - _pollReentrant = false; - _saraResponseBacklogLength = 0; - _saraRXBuffer = NULL; - _pruneBuffer = NULL; - _saraResponseBacklog = NULL; -} - -SARA_R5::~SARA_R5(void) { - if (NULL != _saraRXBuffer) { - delete[] _saraRXBuffer; - _saraRXBuffer = NULL; - } - if (NULL != _pruneBuffer) { - delete[] _pruneBuffer; - _pruneBuffer = NULL; - } - if (NULL != _saraResponseBacklog) { - delete[] _saraResponseBacklog; - _saraResponseBacklog = NULL; - } -} - -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED -bool SARA_R5::begin(SoftwareSerial &softSerial, unsigned long baud) -{ - if (NULL == _saraRXBuffer) - { - _saraRXBuffer = new char[_RXBuffSize]; - if (NULL == _saraRXBuffer) - { - if (_printDebug == true) - _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); - return false; - } - } - memset(_saraRXBuffer, 0, _RXBuffSize); - - if (NULL == _pruneBuffer) - { - _pruneBuffer = new char[_RXBuffSize]; - if (NULL == _pruneBuffer) - { - if (_printDebug == true) - _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); - return false; - } - } - memset(_pruneBuffer, 0, _RXBuffSize); - - if (NULL == _saraResponseBacklog) - { - _saraResponseBacklog = new char[_RXBuffSize]; - if (NULL == _saraResponseBacklog) - { - 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; - - err = init(baud); - if (err == SARA_R5_ERROR_SUCCESS) - { - return true; - } - return false; -} -#endif - -bool SARA_R5::begin(HardwareSerial &hardSerial, unsigned long baud, bool doBegin) -{ - if (NULL == _saraRXBuffer) - { - _saraRXBuffer = new char[_RXBuffSize]; - if (NULL == _saraRXBuffer) - { - if (_printDebug == true) - _debugPort->println(F("begin: not enough memory for _saraRXBuffer!")); - return false; - } - } - memset(_saraRXBuffer, 0, _RXBuffSize); - - if (NULL == _pruneBuffer) - { - _pruneBuffer = new char[_RXBuffSize]; - if (NULL == _pruneBuffer) - { - if (_printDebug == true) - _debugPort->println(F("begin: not enough memory for _pruneBuffer!")); - return false; - } - } - memset(_pruneBuffer, 0, _RXBuffSize); - - if (NULL == _saraResponseBacklog) - { - _saraResponseBacklog = new char[_RXBuffSize]; - if (NULL == _saraResponseBacklog) - { - 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; - if (doBegin) { - _hardSerial->begin(baud); - } - err = init(baud); - if (err == SARA_R5_ERROR_SUCCESS) - { - return true; - } - return false; -} - -//Calling this function with nothing sets the debug port to Serial -//You can also call it with other streams like Serial1, SerialUSB, etc. -void SARA_R5::enableDebugging(Print &debugPort) -{ - _debugPort = &debugPort; - _printDebug = true; -} - -//Calling this function with nothing sets the debug port to Serial -//You can also call it with other streams like Serial1, SerialUSB, etc. -void SARA_R5::enableAtDebugging(Print &debugPort) -{ - _debugAtPort = &debugPort; - _printAtDebug = true; -} - -// This function was originally written by Matthew Menze for the LTE Shield (SARA-R4) library -// See: https://github.com/sparkfun/SparkFun_LTE_Shield_Arduino_Library/pull/8 -// It does the same job as ::poll but also processed any 'old' data stored in the backlog first -// 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; - unsigned long timeIn = millis(); - char *event; - int backlogLen = _saraResponseBacklogLength; - - memset(_saraRXBuffer, 0, _RXBuffSize); // Clear _saraRXBuffer - - // Does the backlog contain any data? If it does, copy it into _saraRXBuffer and then clear the backlog - if (_saraResponseBacklogLength > 0) - { - //The backlog also logs reads from other tasks like transmitting. - if (_printDebug == true) - { - _debugPort->print(F("bufferedPoll: backlog found! backlogLen is ")); - _debugPort->println(_saraResponseBacklogLength); - } - memcpy(_saraRXBuffer + avail, _saraResponseBacklog, _saraResponseBacklogLength); - avail += _saraResponseBacklogLength; - memset(_saraResponseBacklog, 0, _RXBuffSize); // Clear the backlog making sure it is NULL-terminated - _saraResponseBacklogLength = 0; - } - - if ((hwAvailable() > 0) || (backlogLen > 0)) // If either new data is available, or backlog had data. - { - //Check for incoming serial data. Copy it into the backlog - - // Important note: - // On ESP32, Serial.available only provides an update every ~120 bytes during the reception of long messages: - // https://gitter.im/espressif/arduino-esp32?at=5e25d6370a1cf54144909c85 - // Be aware that if a long message is being received, the code below will timeout after _rxWindowMillis = 2 millis. - // At 115200 baud, hwAvailable takes ~120 * 10 / 115200 = 10.4 millis before it indicates that data is being received. - - while (((millis() - timeIn) < _rxWindowMillis) && (avail < _RXBuffSize)) - { - if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL - { - c = readChar(); - // bufferedPoll is only interested in the URCs. - // The URCs are all readable. - // strtok does not like NULL characters. - // So we need to make sure no NULL characters are added to _saraRXBuffer - if (c == '\0') - c = '0'; // Convert any NULLs to ASCII Zeros - _saraRXBuffer[avail++] = c; - timeIn = millis(); - } else { - yield(); - } - } - - // _saraRXBuffer now contains the backlog (if any) and the new serial data (if any) - - // A health warning about strtok: - // strtok will convert any delimiters it finds ("\r\n" in our case) into NULL characters. - // Also, be very careful that you do not use strtok within an strtok while loop. - // The next call of strtok(NULL, ...) in the outer loop will use the pointer saved from the inner loop! - // In our case, strtok is also used in pruneBacklog, which is called by waitForRespone or sendCommandWithResponse, - // which is called by the parse functions called by processURCEvent... - // The solution is to use strtok_r - the reentrant version of strtok - - char *preservedEvent; - event = strtok_r(_saraRXBuffer, "\r\n", &preservedEvent); // Look for an 'event' (_saraRXBuffer contains something ending in \r\n) - - if (event != NULL) - if (_printDebug == true) - _debugPort->println(F("bufferedPoll: event(s) found! ===>")); - - while (event != NULL) // Keep going until all events have been processed - { - if (_printDebug == true) - { - _debugPort->print(F("bufferedPoll: start of event: ")); - _debugPort->println(event); - } - - //Process the event - bool latestHandled = processURCEvent((const char *)event); - if (latestHandled) { - if ((true == _printAtDebug) && (NULL != event)) { - _debugAtPort->print(event); - } - handled = true; // handled will be true if latestHandled has ever been true - } - if ((_saraResponseBacklogLength > 0) && ((avail + _saraResponseBacklogLength) < _RXBuffSize)) // Has any new data been added to the backlog? - { - if (_printDebug == true) - { - _debugPort->println(F("bufferedPoll: new backlog added!")); - } - memcpy(_saraRXBuffer + avail, _saraResponseBacklog, _saraResponseBacklogLength); - avail += _saraResponseBacklogLength; - memset(_saraResponseBacklog, 0, _RXBuffSize); //Clear out the backlog buffer again. - _saraResponseBacklogLength = 0; - } - - //Walk through any remaining events - event = strtok_r(NULL, "\r\n", &preservedEvent); - - if (_printDebug == true) - _debugPort->println(F("bufferedPoll: end of event")); //Just to denote end of processing event. - - if (event == NULL) - if (_printDebug == true) - _debugPort->println(F("bufferedPoll: <=== end of event(s)!")); - } - } - - 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) -{ - { // URC: +UUSORD (Read Socket Data) - int socket, length; - int ret = sscanf(event, "+UUSORD: %d,%d", &socket, &length); - if (ret == 2) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: read socket data")); - // From the SARA_R5 AT Commands Manual: - // "For the UDP socket type the URC +UUSORD: , notifies that a UDP packet has been received, - // either when buffer is empty or after a UDP packet has been read and one or more packets are stored in the - // buffer." - // So we need to check if this is a TCP socket or a UDP socket: - // If UDP, we call parseSocketReadIndicationUDP. - // Otherwise, we call parseSocketReadIndication. - if (_lastSocketProtocol[socket] == SARA_R5_UDP) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: received +UUSORD but socket is UDP. Calling parseSocketReadIndicationUDP")); - parseSocketReadIndicationUDP(socket, length); - } - else - parseSocketReadIndication(socket, length); - return true; - } - } - { // URC: +UUSORF (Receive From command (UDP only)) - int socket, length; - int ret = sscanf(event, "+UUSORF: %d,%d", &socket, &length); - if (ret == 2) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: UDP receive")); - parseSocketReadIndicationUDP(socket, length); - return true; - } - } - { // URC: +UUSOLI (Set Listening Socket) - int socket = 0; - int listenSocket = 0; - unsigned int port = 0; - unsigned int listenPort = 0; - IPAddress remoteIP = {0,0,0,0}; - IPAddress localIP = {0,0,0,0}; - int remoteIPstore[4] = {0,0,0,0}; - int localIPstore[4] = {0,0,0,0}; - - int ret = sscanf(event, - "+UUSOLI: %d,\"%d.%d.%d.%d\",%u,%d,\"%d.%d.%d.%d\",%u", - &socket, - &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], - &port, &listenSocket, - &localIPstore[0], &localIPstore[1], &localIPstore[2], &localIPstore[3], - &listenPort); - for (int i = 0; i <= 3; i++) - { - if (ret >= 5) - remoteIP[i] = (uint8_t)remoteIPstore[i]; - if (ret >= 11) - localIP[i] = (uint8_t)localIPstore[i]; - } - if (ret >= 5) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: socket listen")); - parseSocketListenIndication(listenSocket, localIP, listenPort, socket, remoteIP, port); - return true; - } - } - { // URC: +UUSOCL (Close Socket) - int socket; - int ret = sscanf(event, "+UUSOCL: %d", &socket); - if (ret == 1) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: socket close")); - if ((socket >= 0) && (socket <= 6)) - { - if (_socketCloseCallback != NULL) - { - _socketCloseCallback(socket); - } - } - return true; - } - } - { // URC: +UULOC (Localization information - CellLocate and hybrid positioning) - ClockData clck; - PositionData gps; - SpeedData spd; - unsigned long uncertainty; - int scanNum; - int latH, lonH, alt; - unsigned int speedU, cogU; - char latL[10], lonL[10]; - int dateStore[5]; - - // Maybe we should also scan for +UUGIND and extract the activated gnss system? - - // This assumes the ULOC response type is "0" or "1" - as selected by gpsRequest detailed - scanNum = sscanf(event, - "+UULOC: %d/%d/%d,%d:%d:%d.%d,%d.%[^,],%d.%[^,],%d,%lu,%u,%u,%*s", - &dateStore[0], &dateStore[1], &clck.date.year, - &dateStore[2], &dateStore[3], &dateStore[4], &clck.time.ms, - &latH, latL, &lonH, lonL, &alt, &uncertainty, - &speedU, &cogU); - clck.date.day = dateStore[0]; - clck.date.month = dateStore[1]; - clck.time.hour = dateStore[2]; - clck.time.minute = dateStore[3]; - clck.time.second = dateStore[4]; - - if (scanNum >= 13) - { - // Found a Location string! - if (_printDebug == true) - { - _debugPort->println(F("processReadEvent: location")); - } - - if (latH >= 0) - gps.lat = (float)latH + ((float)atol(latL) / pow(10, strlen(latL))); - else - gps.lat = (float)latH - ((float)atol(latL) / pow(10, strlen(latL))); - if (lonH >= 0) - gps.lon = (float)lonH + ((float)atol(lonL) / pow(10, strlen(lonL))); - else - gps.lon = (float)lonH - ((float)atol(lonL) / pow(10, strlen(lonL))); - gps.alt = (float)alt; - if (scanNum >= 15) // If detailed response, get speed data - { - spd.speed = (float)speedU; - spd.cog = (float)cogU; - } - - // if (_printDebug == true) - // { - // _debugPort->print(F("processReadEvent: location: lat: ")); - // _debugPort->print(gps.lat, 7); - // _debugPort->print(F(" lon: ")); - // _debugPort->print(gps.lon, 7); - // _debugPort->print(F(" alt: ")); - // _debugPort->print(gps.alt, 2); - // _debugPort->print(F(" speed: ")); - // _debugPort->print(spd.speed, 2); - // _debugPort->print(F(" cog: ")); - // _debugPort->println(spd.cog, 2); - // } - - if (_gpsRequestCallback != NULL) - { - _gpsRequestCallback(clck, gps, spd, uncertainty); - } - - return true; - } - } - { // URC: +UUSIMSTAT (SIM Status) - SARA_R5_sim_states_t state; - int scanNum; - int stateStore; - - scanNum = sscanf(event, "+UUSIMSTAT:%d", &stateStore); // Note: no space after the colon! - - if (scanNum == 1) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: SIM status")); - - state = (SARA_R5_sim_states_t)stateStore; - - if (_simStateReportCallback != NULL) - { - _simStateReportCallback(state); - } - - return true; - } - } - { // URC: +UUPSDA (Packet Switched Data Action) - int result; - IPAddress remoteIP = {0, 0, 0, 0}; - int scanNum; - int remoteIPstore[4]; - - scanNum = sscanf(event, "+UUPSDA: %d,\"%d.%d.%d.%d\"", - &result, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3]); - - if (scanNum == 5) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: packet switched data action")); - - for (int i = 0; i <= 3; i++) - { - remoteIP[i] = (uint8_t)remoteIPstore[i]; - } - - if (_psdActionRequestCallback != NULL) - { - _psdActionRequestCallback(result, remoteIP); - } - - return true; - } - } - { // URC: +UUHTTPCR (HTTP Command Result) - int profile, command, result; - int scanNum; - - scanNum = sscanf(event, "+UUHTTPCR: %d,%d,%d", &profile, &command, &result); - - if (scanNum == 3) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: HTTP command result")); - - if ((profile >= 0) && (profile < SARA_R5_NUM_HTTP_PROFILES)) - { - if (_httpCommandRequestCallback != NULL) - { - _httpCommandRequestCallback(profile, command, result); - } - } - - return true; - } - } - { // URC: +UUMQTTC (HTTP Command Result) - int command, result; - int scanNum; - int qos = -1; - String topic; - scanNum = sscanf(event, "+UUMQTTC: %d,%d", &command, &result); - if ((scanNum == 2) && (command == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) { - char topicC[100] = ""; - scanNum = sscanf(event, "+UUMQTTC: %*d,%*d,%d,\"%[^\"]\"", &qos, topicC); - topic = topicC; - } - if ((scanNum == 2) || (scanNum == 4)) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: MQTT command result")); - - if (_mqttCommandRequestCallback != NULL) - { - _mqttCommandRequestCallback(command, result); - } - - return true; - } - } - { // URC: +UUPING (Ping Result) - int retry = 0; - int p_size = 0; - int ttl = 0; - String remote_host = ""; - IPAddress remoteIP = {0, 0, 0, 0}; - long rtt = 0; - int scanNum; - const char *searchPtr = event; - - // Try to extract the UUPING retries and payload size - scanNum = sscanf(searchPtr, "+UUPING: %d,%d,", &retry, &p_size); - - if (scanNum == 2) - { - if (_printDebug == true) - { - _debugPort->println(F("processReadEvent: ping")); - } - - searchPtr = strchr(++searchPtr, '\"'); // Search to the first quote - - // Extract the remote host name, stop at the next quote - while ((*(++searchPtr) != '\"') && (*searchPtr != '\0')) - { - remote_host.concat(*(searchPtr)); - } - - if (*searchPtr != '\0') // Make sure we found a quote - { - int remoteIPstore[4]; - scanNum = sscanf(searchPtr, "\",\"%d.%d.%d.%d\",%d,%ld", - &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], &ttl, &rtt); - for (int i = 0; i <= 3; i++) - { - remoteIP[i] = (uint8_t)remoteIPstore[i]; - } - - if (scanNum == 6) // Make sure we extracted enough data - { - if (_pingRequestCallback != NULL) - { - _pingRequestCallback(retry, p_size, remote_host, remoteIP, ttl, rtt); - } - } - } - return true; - } - } - { // URC: +A - int status = 0; - unsigned int lac = 0, ci = 0, Act = 0; - int scanNum = sscanf(event, "+CREG: %d,\"%4x\",\"%4x\",%d", &status, &lac, &ci, &Act); - if (scanNum == 4) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: CREG")); - - if (_registrationCallback != NULL) - { - _registrationCallback((SARA_R5_registration_status_t)status, lac, ci, Act); - } - - return true; - } - } - { // URC: +CEREG - int status = 0; - unsigned int tac = 0, ci = 0, Act = 0; - int scanNum = sscanf(event, "+CEREG: %d,\"%4x\",\"%4x\",%d", &status, &tac, &ci, &Act); - if (scanNum == 4) - { - if (_printDebug == true) - _debugPort->println(F("processReadEvent: CEREG")); - - if (_epsRegistrationCallback != NULL) - { - _epsRegistrationCallback((SARA_R5_registration_status_t)status, tac, ci, Act); - } - - return true; - } - } - - return false; -} - -// This is the original poll function. -// It is 'blocking' - it does not return when serial data is available until it receives a `\n`. -// ::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; - - memset(_saraRXBuffer, 0, _RXBuffSize); // Clear _saraRXBuffer - - if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL - { - while (c != '\n') // Copy characters into _saraRXBuffer. Stop at the first new line - { - if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL - { - c = readChar(); - _saraRXBuffer[avail++] = c; - } else { - yield(); - } - } - - // Now search for all supported URC's - handled = processURCEvent(_saraRXBuffer); - if (handled && (true == _printAtDebug)) { - _debugAtPort->write(_saraRXBuffer, avail); - } - if ((handled == false) && (strlen(_saraRXBuffer) > 2)) - { - if (_printDebug == true) - { - _debugPort->print(F("poll: ")); - _debugPort->println(_saraRXBuffer); - } - } - else - { - } - } - - _pollReentrant = false; - - return handled; -} - -void SARA_R5::setSocketListenCallback(void (*socketListenCallback)(int, IPAddress, unsigned int, int, IPAddress, unsigned int)) -{ - _socketListenCallback = socketListenCallback; -} - -void SARA_R5::setSocketReadCallback(void (*socketReadCallback)(int, String)) -{ - _socketReadCallback = socketReadCallback; -} - -void SARA_R5::setSocketReadCallbackPlus(void (*socketReadCallbackPlus)(int, const char *, int, IPAddress, int)) // socket, data, length, remoteAddress, remotePort -{ - _socketReadCallbackPlus = socketReadCallbackPlus; -} - -void SARA_R5::setSocketCloseCallback(void (*socketCloseCallback)(int)) -{ - _socketCloseCallback = socketCloseCallback; -} - -void SARA_R5::setGpsReadCallback(void (*gpsRequestCallback)(ClockData time, - PositionData gps, SpeedData spd, unsigned long uncertainty)) -{ - _gpsRequestCallback = gpsRequestCallback; -} - -void SARA_R5::setSIMstateReportCallback(void (*simStateReportCallback)(SARA_R5_sim_states_t state)) -{ - _simStateReportCallback = simStateReportCallback; -} - -void SARA_R5::setPSDActionCallback(void (*psdActionRequestCallback)(int result, IPAddress ip)) -{ - _psdActionRequestCallback = psdActionRequestCallback; -} - -void SARA_R5::setPingCallback(void (*pingRequestCallback)(int retry, int p_size, String remote_hostname, IPAddress ip, int ttl, long rtt)) -{ - _pingRequestCallback = pingRequestCallback; -} - -void SARA_R5::setHTTPCommandCallback(void (*httpCommandRequestCallback)(int profile, int command, int result)) -{ - _httpCommandRequestCallback = httpCommandRequestCallback; -} - -void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)) -{ - _mqttCommandRequestCallback = mqttCommandRequestCallback; -} - -SARA_R5_error_t SARA_R5::setRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, unsigned int lac, unsigned int ci, int Act)) -{ - _registrationCallback = registrationCallback; - - char *command = sara_r5_calloc_char(strlen(SARA_R5_REGISTRATION_STATUS) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_REGISTRATION_STATUS, 2/*enable URC with location*/); - SARA_R5_error_t err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setEpsRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, unsigned int tac, unsigned int ci, int Act)) -{ - _epsRegistrationCallback = registrationCallback; - - char *command = sara_r5_calloc_char(strlen(SARA_R5_EPSREGISTRATION_STATUS) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_EPSREGISTRATION_STATUS, 2/*enable URC with location*/); - SARA_R5_error_t err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -size_t SARA_R5::write(uint8_t c) -{ - return hwWrite(c); -} - -size_t SARA_R5::write(const char *str) -{ - return hwPrint(str); -} - -size_t SARA_R5::write(const char *buffer, size_t size) -{ - return hwWriteData(buffer, size); -} - -SARA_R5_error_t SARA_R5::at(void) -{ - SARA_R5_error_t err; - - err = sendCommandWithResponse(NULL, SARA_R5_RESPONSE_OK, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - return err; -} - -SARA_R5_error_t SARA_R5::enableEcho(bool enable) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_ECHO) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s%d", SARA_R5_COMMAND_ECHO, enable ? 1 : 0); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -String SARA_R5::getManufacturerID(void) -{ - char *response; - char idResponse[16] = {0x00}; // E.g. u-blox - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_MANU_ID, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) - { - memset(idResponse, 0, 16); - } - } - free(response); - return String(idResponse); -} - -String SARA_R5::getModelID(void) -{ - char *response; - char idResponse[16] = {0x00}; // E.g. SARA-R510M8Q - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_MODEL_ID, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) - { - memset(idResponse, 0, 16); - } - } - free(response); - return String(idResponse); -} - -String SARA_R5::getFirmwareVersion(void) -{ - char *response; - char idResponse[16] = {0x00}; // E.g. 11.40 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_FW_VER_ID, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) - { - memset(idResponse, 0, 16); - } - } - free(response); - return String(idResponse); -} - -String SARA_R5::getSerialNo(void) -{ - char *response; - char idResponse[16] = {0x00}; // E.g. 357520070120767 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_SERIAL_NO, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", idResponse) != 1) - { - memset(idResponse, 0, 16); - } - } - free(response); - return String(idResponse); -} - -String SARA_R5::getIMEI(void) -{ - char *response; - char imeiResponse[16] = {0x00}; // E.g. 004999010640000 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_IMEI, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", imeiResponse) != 1) - { - memset(imeiResponse, 0, 16); - } - } - free(response); - return String(imeiResponse); -} - -String SARA_R5::getIMSI(void) -{ - char *response; - char imsiResponse[16] = {0x00}; // E.g. 222107701772423 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_IMSI, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n%s\r\n", imsiResponse) != 1) - { - memset(imsiResponse, 0, 16); - } - } - free(response); - return String(imsiResponse); -} - -String SARA_R5::getCCID(void) -{ - char *response; - char ccidResponse[21] = {0x00}; // E.g. +CCID: 8939107900010087330 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_CCID, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n+CCID: %s", ccidResponse) != 1) - { - memset(ccidResponse, 0, 21); - } - } - free(response); - return String(ccidResponse); -} - -String SARA_R5::getSubscriberNo(void) -{ - char *response; - char idResponse[128] = {0x00}; // E.g. +CNUM: "ABCD . AAA","123456789012",129 - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_CNUM, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_10_SEC_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n+CNUM: %s", idResponse) != 1) - { - memset(idResponse, 0, 128); - } - } - free(response); - return String(idResponse); -} - -String SARA_R5::getCapabilities(void) -{ - char *response; - char idResponse[128] = {0x00}; // E.g. +GCAP: +FCLASS, +CGSM - SARA_R5_error_t err; - - response = sara_r5_calloc_char(minimumResponseAllocation); - - err = sendCommandWithResponse(SARA_R5_COMMAND_REQ_CAP, - SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (sscanf(response, "\r\n+GCAP: %s", idResponse) != 1) - { - memset(idResponse, 0, 128); - } - } - free(response); - return String(idResponse); -} - -SARA_R5_error_t SARA_R5::reset(void) -{ - SARA_R5_error_t err; - - err = functionality(SILENT_RESET_WITH_SIM); - if (err == SARA_R5_ERROR_SUCCESS) - { - // Reset will set the baud rate back to 115200 - //beginSerial(9600); - err = SARA_R5_ERROR_INVALID; - while (err != SARA_R5_ERROR_SUCCESS) - { - beginSerial(SARA_R5_DEFAULT_BAUD_RATE); - setBaud(_baud); - beginSerial(_baud); - err = at(); - } - return init(_baud); - } - return err; -} - -String SARA_R5::clock(void) -{ - SARA_R5_error_t err; - char *command; - char *response; - char *clockBegin; - char *clockEnd; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_CLOCK) + 2); - if (command == NULL) - return ""; - sprintf(command, "%s?", SARA_R5_COMMAND_CLOCK); - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return ""; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return ""; - } - - // Response format: \r\n+CCLK: "YY/MM/DD,HH:MM:SS-TZ"\r\n\r\nOK\r\n - clockBegin = strchr(response, '\"'); // Find first quote - if (clockBegin == NULL) - { - free(command); - free(response); - return ""; - } - clockBegin += 1; // Increment pointer to begin at first number - clockEnd = strchr(clockBegin, '\"'); // Find last quote - if (clockEnd == NULL) - { - free(command); - free(response); - return ""; - } - *(clockEnd) = '\0'; // Set last quote to null char -- end string - - String clock = String(clockBegin); // Extract the clock as a String _before_ freeing response - - free(command); - free(response); - - return (clock); -} - -SARA_R5_error_t SARA_R5::clock(uint8_t *y, uint8_t *mo, uint8_t *d, - uint8_t *h, uint8_t *min, uint8_t *s, int8_t *tz) -{ - SARA_R5_error_t err; - char *command; - char *response; - char tzPlusMinus; - int scanNum = 0; - - int iy, imo, id, ih, imin, is, itz; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_CLOCK) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_COMMAND_CLOCK); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - // Response format (if TZ is negative): \r\n+CCLK: "YY/MM/DD,HH:MM:SS-TZ"\r\n\r\nOK\r\n - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+CCLK: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+CCLK: \"%d/%d/%d,%d:%d:%d%c%d\"\r\n", - &iy, &imo, &id, &ih, &imin, &is, &tzPlusMinus, &itz); - if (scanNum == 8) - { - *y = iy; - *mo = imo; - *d = id; - *h = ih; - *min = imin; - *s = is; - if (tzPlusMinus == '-') - *tz = 0 - itz; - else - *tz = itz; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - 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_OR_ERROR, - 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; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_TIME) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (mode == SARA_R5_UTIME_MODE_STOP) // stop UTIME does not require a sensor - sprintf(command, "%s=%d", SARA_R5_GNSS_REQUEST_TIME, mode); - else - sprintf(command, "%s=%d,%d", SARA_R5_GNSS_REQUEST_TIME, mode, sensor); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_10_SEC_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getUtimeMode(SARA_R5_utime_mode_t *mode, SARA_R5_utime_sensor_t *sensor) -{ - SARA_R5_error_t err; - char *command; - char *response; - - SARA_R5_utime_mode_t m; - SARA_R5_utime_sensor_t s; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_TIME) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_GNSS_REQUEST_TIME); - - 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_OR_ERROR, - response, SARA_R5_10_SEC_TIMEOUT); - - // Response format: \r\n+UTIME: [,]\r\n\r\nOK\r\n - if (err == SARA_R5_ERROR_SUCCESS) - { - int mStore, sStore, scanned = 0; - char *searchPtr = strstr(response, "+UTIME: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+UTIME: %d,%d\r\n", &mStore, &sStore); - m = (SARA_R5_utime_mode_t)mStore; - s = (SARA_R5_utime_sensor_t)sStore; - if (scanned == 2) - { - *mode = m; - *sensor = s; - } - else if (scanned == 1) - { - *mode = m; - *sensor = SARA_R5_UTIME_SENSOR_NONE; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::setUtimeIndication(SARA_R5_utime_urc_configuration_t config) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_INDICATION) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_GNSS_TIME_INDICATION, config); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getUtimeIndication(SARA_R5_utime_urc_configuration_t *config) -{ - SARA_R5_error_t err; - char *command; - char *response; - - SARA_R5_utime_urc_configuration_t c; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_INDICATION) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_GNSS_TIME_INDICATION); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - // Response format: \r\n+UTIMEIND: \r\n\r\nOK\r\n - if (err == SARA_R5_ERROR_SUCCESS) - { - int cStore, scanned = 0; - char *searchPtr = strstr(response, "+UTIMEIND: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+UTIMEIND: %d\r\n", &cStore); - c = (SARA_R5_utime_urc_configuration_t)cStore; - if (scanned == 1) - { - *config = c; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::setUtimeConfiguration(int32_t offsetNanoseconds, int32_t offsetSeconds) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_CONFIGURATION) + 48); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; -#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) - sprintf(command, "%s=%d,%d", SARA_R5_GNSS_TIME_CONFIGURATION, offsetNanoseconds, offsetSeconds); -#else - sprintf(command, "%s=%ld,%ld", SARA_R5_GNSS_TIME_CONFIGURATION, offsetNanoseconds, offsetSeconds); -#endif - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getUtimeConfiguration(int32_t *offsetNanoseconds, int32_t *offsetSeconds) -{ - SARA_R5_error_t err; - char *command; - char *response; - - int32_t ons; - int32_t os; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_TIME_CONFIGURATION) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_GNSS_TIME_CONFIGURATION); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - // Response format: \r\n+UTIMECFG: ,\r\n\r\nOK\r\n - if (err == SARA_R5_ERROR_SUCCESS) - { - int scanned = 0; - char *searchPtr = strstr(response, "+UTIMECFG: "); - if (searchPtr != NULL) -#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) - scanned = sscanf(searchPtr, "+UTIMECFG: %d,%d\r\n", &ons, &os); -#else - scanned = sscanf(searchPtr, "+UTIMECFG: %ld,%ld\r\n", &ons, &os); -#endif - if (scanned == 2) - { - *offsetNanoseconds = ons; - *offsetSeconds = os; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::autoTimeZone(bool enable) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_AUTO_TZ) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_COMMAND_AUTO_TZ, enable ? 1 : 0); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -int8_t SARA_R5::rssi(void) -{ - char *command; - char *response; - SARA_R5_error_t err; - int rssi; - - command = sara_r5_calloc_char(strlen(SARA_R5_SIGNAL_QUALITY) + 1); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s", SARA_R5_SIGNAL_QUALITY); - - 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_OR_ERROR, response, 10000, - minimumResponseAllocation, AT_COMMAND); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return -1; - } - - int scanned = 0; - char *searchPtr = strstr(response, "+CSQ: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+CSQ: %d,%*d", &rssi); - if (scanned != 1) - { - rssi = -1; - } - - free(command); - free(response); - return rssi; -} - -SARA_R5_registration_status_t SARA_R5::registration(bool eps) -{ - char *command; - char *response; - SARA_R5_error_t err; - int status; - const char* tag = eps ? SARA_R5_EPSREGISTRATION_STATUS : SARA_R5_REGISTRATION_STATUS; - command = sara_r5_calloc_char(strlen(tag) + 3); - if (command == NULL) - return SARA_R5_REGISTRATION_INVALID; - sprintf(command, "%s?", tag); - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_REGISTRATION_INVALID; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT, - minimumResponseAllocation, AT_COMMAND); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return SARA_R5_REGISTRATION_INVALID; - } - - int scanned = 0; - const char *startTag = eps ? "+CEREG: " : "+CREG: "; - char *searchPtr = strstr(response, startTag); - if (searchPtr != NULL) { - const char *format = eps ? "+CEREG: %*d,%d" : "+CREG: %*d,%d"; - scanned = sscanf(searchPtr, format, &status); - } - if (scanned != 1) - status = SARA_R5_REGISTRATION_INVALID; - - free(command); - free(response); - return (SARA_R5_registration_status_t)status; -} - -bool SARA_R5::setNetworkProfile(mobile_network_operator_t mno, bool autoReset, bool urcNotification) -{ - mobile_network_operator_t currentMno; - - // Check currently set MNO profile - if (getMNOprofile(¤tMno) != SARA_R5_ERROR_SUCCESS) - { - return false; - } - - if (currentMno == mno) - { - return true; - } - - // Disable transmit and receive so we can change operator - if (functionality(MINIMUM_FUNCTIONALITY) != SARA_R5_ERROR_SUCCESS) - { - return false; - } - - if (setMNOprofile(mno, autoReset, urcNotification) != SARA_R5_ERROR_SUCCESS) - { - return false; - } - - if (reset() != SARA_R5_ERROR_SUCCESS) - { - return false; - } - - return true; -} - -mobile_network_operator_t SARA_R5::getNetworkProfile(void) -{ - mobile_network_operator_t mno; - SARA_R5_error_t err; - - err = getMNOprofile(&mno); - if (err != SARA_R5_ERROR_SUCCESS) - { - return MNO_INVALID; - } - return mno; -} - -SARA_R5_error_t SARA_R5::setAPN(String apn, uint8_t cid, SARA_R5_pdp_type pdpType) -{ - SARA_R5_error_t err; - char *command; - char pdpStr[8]; - - memset(pdpStr, 0, 8); - - if (cid >= 8) - return SARA_R5_ERROR_UNEXPECTED_PARAM; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_DEF) + strlen(apn.c_str()) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - switch (pdpType) - { - case PDP_TYPE_INVALID: - free(command); - return SARA_R5_ERROR_UNEXPECTED_PARAM; - break; - case PDP_TYPE_IP: - memcpy(pdpStr, "IP", 2); - break; - case PDP_TYPE_NONIP: - memcpy(pdpStr, "NONIP", 5); - break; - case PDP_TYPE_IPV4V6: - memcpy(pdpStr, "IPV4V6", 6); - break; - case PDP_TYPE_IPV6: - memcpy(pdpStr, "IPV6", 4); - break; - default: - free(command); - return SARA_R5_ERROR_UNEXPECTED_PARAM; - break; - } - if (apn == NULL) - { - if (_printDebug == true) - _debugPort->println(F("setAPN: NULL")); - sprintf(command, "%s=%d,\"%s\",\"\"", SARA_R5_MESSAGE_PDP_DEF, - cid, pdpStr); - } - else - { - if (_printDebug == true) - { - _debugPort->print(F("setAPN: ")); - _debugPort->println(apn); - } - sprintf(command, "%s=%d,\"%s\",\"%s\"", SARA_R5_MESSAGE_PDP_DEF, - cid, pdpStr, apn.c_str()); - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - - return err; -} - -// Return the Access Point Name and IP address for the chosen context identifier -SARA_R5_error_t SARA_R5::getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp_type* pdpType) -{ - SARA_R5_error_t err; - char *command; - char *response; - - if (cid > SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_DEF) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_MESSAGE_PDP_DEF); - - response = sara_r5_calloc_char(1024); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT, 1024); - - if (err == SARA_R5_ERROR_SUCCESS) - { - // Example: - // +CGDCONT: 0,"IP","payandgo.o2.co.uk","0.0.0.0",0,0,0,0,0,0,0,0,0,0 - // +CGDCONT: 1,"IP","payandgo.o2.co.uk.mnc010.mcc234.gprs","10.160.182.234",0,0,0,2,0,0,0,0,0,0 - int rcid = -1; - char *searchPtr = response; - - bool keepGoing = true; - while (keepGoing == true) - { - int scanned = 0; - // Find the first/next occurrence of +CGDCONT: - searchPtr = strstr(searchPtr, "+CGDCONT: "); - if (searchPtr != NULL) - { - char strPdpType[10]; - char strApn[128]; - int ipOct[4]; - - searchPtr += strlen("+CGDCONT: "); // Point to the cid - scanned = sscanf(searchPtr, "%d,\"%[^\"]\",\"%[^\"]\",\"%d.%d.%d.%d", - &rcid, strPdpType, strApn, - &ipOct[0], &ipOct[1], &ipOct[2], &ipOct[3]); - if ((scanned == 7) && (rcid == cid)) { - if (apn) *apn = strApn; - for (int o = 0; ip && (o < 4); o++) - { - (*ip)[o] = (uint8_t)ipOct[o]; - } - if (pdpType) { - *pdpType = (0 == strcmp(strPdpType, "IPV4V6")) ? PDP_TYPE_IPV4V6 : - (0 == strcmp(strPdpType, "IPV6")) ? PDP_TYPE_IPV6 : - (0 == strcmp(strPdpType, "IP")) ? PDP_TYPE_IP : - PDP_TYPE_INVALID; - } - keepGoing = false; - } - } - else // We don't have a match so let's clear the APN and IP address - { - if (apn) *apn = ""; - if (pdpType) *pdpType = PDP_TYPE_INVALID; - if (ip) *ip = {0, 0, 0, 0}; - keepGoing = false; - } - } - } - else - { - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::getSimStatus(String* code) -{ - SARA_R5_error_t err; - char *command; - char *response; - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_SIMPIN) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_COMMAND_SIMPIN); - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - int scanned = 0; - char c[16]; - char *searchPtr = strstr(response, "+CPIN: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+CPIN: %s\r\n", c); - if (scanned == 1) - { - if(code) - *code = c; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::setSimPin(String pin) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_SIMPIN) + 4 + pin.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=\"%s\"", SARA_R5_COMMAND_SIMPIN, pin.c_str()); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setSIMstateReportingMode(int mode) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_SIM_STATE) + 4); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_SIM_STATE, mode); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getSIMstateReportingMode(int *mode) -{ - SARA_R5_error_t err; - char *command; - char *response; - - int m; - - command = sara_r5_calloc_char(strlen(SARA_R5_SIM_STATE) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_SIM_STATE); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - int scanned = 0; - char *searchPtr = strstr(response, "+USIMSTAT: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+USIMSTAT: %d\r\n", &m); - if (scanned == 1) - { - *mode = m; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - return err; -} - -const char *PPP_L2P[5] = { - "", - "PPP", - "M-HEX", - "M-RAW_IP", - "M-OPT-PPP", -}; - -SARA_R5_error_t SARA_R5::enterPPP(uint8_t cid, char dialing_type_char, - unsigned long dialNumber, SARA_R5::SARA_R5_l2p_t l2p) -{ - SARA_R5_error_t err; - char *command; - - if ((dialing_type_char != 0) && (dialing_type_char != 'T') && - (dialing_type_char != 'P')) - { - return SARA_R5_ERROR_UNEXPECTED_PARAM; - } - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_ENTER_PPP) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (dialing_type_char != 0) - { - sprintf(command, "%s%c*%lu**%s*%u#", SARA_R5_MESSAGE_ENTER_PPP, dialing_type_char, - dialNumber, PPP_L2P[l2p], (unsigned int)cid); - } - else - { - sprintf(command, "%s*%lu**%s*%u#", SARA_R5_MESSAGE_ENTER_PPP, - dialNumber, PPP_L2P[l2p], (unsigned int)cid); - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_CONNECT, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -uint8_t SARA_R5::getOperators(struct operator_stats *opRet, int maxOps) -{ - SARA_R5_error_t err; - char *command; - char *response; - uint8_t opsSeen = 0; - - command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=?", SARA_R5_OPERATOR_SELECTION); - - int responseSize = (maxOps + 1) * 48; - response = sara_r5_calloc_char(responseSize); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - // AT+COPS maximum response time is 3 minutes (180000 ms) - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, - SARA_R5_3_MIN_TIMEOUT, responseSize); - - // Sample responses: - // +COPS: (3,"Verizon Wireless","VzW","311480",8),,(0,1,2,3,4),(0,1,2) - // +COPS: (1,"313 100","313 100","313100",8),(2,"AT&T","AT&T","310410",8),(3,"311 480","311 480","311480",8),,(0,1,2,3,4),(0,1,2) - - if (_printDebug == true) - { - _debugPort->print(F("getOperators: Response: {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *opBegin; - char *opEnd; - int op = 0; - int stat; - char longOp[26]; - char shortOp[11]; - int act; - unsigned long numOp; - - opBegin = response; - - for (; op < maxOps; op++) - { - opBegin = strchr(opBegin, '('); - if (opBegin == NULL) - break; - opEnd = strchr(opBegin, ')'); - if (opEnd == NULL) - break; - - int sscanRead = sscanf(opBegin, "(%d,\"%[^\"]\",\"%[^\"]\",\"%lu\",%d)%*s", - &stat, longOp, shortOp, &numOp, &act); - if (sscanRead == 5) - { - opRet[op].stat = stat; - opRet[op].longOp = (String)(longOp); - opRet[op].shortOp = (String)(shortOp); - opRet[op].numOp = numOp; - opRet[op].act = act; - opsSeen += 1; - } - // TODO: Search for other possible patterns here - else - { - break; // Break out if pattern doesn't match. - } - opBegin = opEnd + 1; // Move opBegin to beginning of next value - } - } - - free(command); - free(response); - - return opsSeen; -} - -SARA_R5_error_t SARA_R5::registerOperator(struct operator_stats oper) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 24); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=1,2,\"%lu\"", SARA_R5_OPERATOR_SELECTION, oper.numOp); - - // AT+COPS maximum response time is 3 minutes (180000 ms) - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_3_MIN_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::automaticOperatorSelection() -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 6); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=0,0", SARA_R5_OPERATOR_SELECTION); - - // AT+COPS maximum response time is 3 minutes (180000 ms) - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_3_MIN_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getOperator(String *oper) -{ - SARA_R5_error_t err; - char *command; - char *response; - char *searchPtr; - char mode; - - command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_OPERATOR_SELECTION); - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - // AT+COPS maximum response time is 3 minutes (180000 ms) - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, - SARA_R5_3_MIN_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - searchPtr = strstr(response, "+COPS: "); - if (searchPtr != NULL) - { - searchPtr += strlen("+COPS: "); // Move searchPtr to first char - mode = *searchPtr; // Read first char -- should be mode - if (mode == '2') // Check for de-register - { - err = SARA_R5_ERROR_DEREGISTERED; - } - // Otherwise if it's default, manual, set-only, or automatic - else if ((mode == '0') || (mode == '1') || (mode == '3') || (mode == '4')) - { - *oper = ""; - searchPtr = strchr(searchPtr, '\"'); // Move to first quote - if (searchPtr == NULL) - { - err = SARA_R5_ERROR_DEREGISTERED; - } - else - { - while ((*(++searchPtr) != '\"') && (*searchPtr != '\0')) - { - oper->concat(*(searchPtr)); - } - } - if (_printDebug == true) - { - _debugPort->print(F("getOperator: ")); - _debugPort->println(*oper); - } - } - } - } - - free(response); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::deregisterOperator(void) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_OPERATOR_SELECTION) + 4); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=2", SARA_R5_OPERATOR_SELECTION); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_3_MIN_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setSMSMessageFormat(SARA_R5_message_format_t textMode) -{ - char *command; - SARA_R5_error_t err; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_FORMAT) + 4); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_MESSAGE_FORMAT, - (textMode == SARA_R5_MESSAGE_FORMAT_TEXT) ? 1 : 0); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::sendSMS(String number, String message) -{ - char *command; - char *messageCStr; - char *numberCStr; - SARA_R5_error_t err; - - numberCStr = sara_r5_calloc_char(number.length() + 2); - if (numberCStr == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - number.toCharArray(numberCStr, number.length() + 1); - - command = sara_r5_calloc_char(strlen(SARA_R5_SEND_TEXT) + strlen(numberCStr) + 8); - if (command != NULL) - { - sprintf(command, "%s=\"%s\"", SARA_R5_SEND_TEXT, numberCStr); - - err = sendCommandWithResponse(command, ">", NULL, - SARA_R5_3_MIN_TIMEOUT); - free(command); - free(numberCStr); - if (err != SARA_R5_ERROR_SUCCESS) - return err; - - messageCStr = sara_r5_calloc_char(message.length() + 1); - if (messageCStr == NULL) - { - hwWrite(ASCII_CTRL_Z); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - message.toCharArray(messageCStr, message.length() + 1); - messageCStr[message.length()] = ASCII_CTRL_Z; - - err = sendCommandWithResponse(messageCStr, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_3_MIN_TIMEOUT, minimumResponseAllocation, NOT_AT_COMMAND); - - free(messageCStr); - } - else - { - free(numberCStr); - err = SARA_R5_ERROR_OUT_OF_MEMORY; - } - - return err; -} - -SARA_R5_error_t SARA_R5::getPreferredMessageStorage(int *used, int *total, String memory) -{ - SARA_R5_error_t err; - char *command; - char *response; - int u; - int t; - - command = sara_r5_calloc_char(strlen(SARA_R5_PREF_MESSAGE_STORE) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=\"%s\"", SARA_R5_PREF_MESSAGE_STORE, memory.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_OR_ERROR, response, - SARA_R5_3_MIN_TIMEOUT); - - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return err; - } - - int scanned = 0; - char *searchPtr = strstr(response, "+CPMS: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+CPMS: %d,%d", &u, &t); - if (scanned == 2) - { - if (_printDebug == true) - { - _debugPort->print(F("getPreferredMessageStorage: memory1 (read and delete): ")); - _debugPort->print(memory); - _debugPort->print(F(" used: ")); - _debugPort->print(u); - _debugPort->print(F(" total: ")); - _debugPort->println(t); - } - *used = u; - *total = t; - } - else - { - err = SARA_R5_ERROR_INVALID; - } - - free(response); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::readSMSmessage(int location, String *unread, String *from, String *dateTime, String *message) -{ - SARA_R5_error_t err; - char *command; - char *response; - - command = sara_r5_calloc_char(strlen(SARA_R5_READ_TEXT_MESSAGE) + 5); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_READ_TEXT_MESSAGE, location); - - response = sara_r5_calloc_char(1024); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, - SARA_R5_10_SEC_TIMEOUT, 1024); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = response; - - // Find the first occurrence of +CGDCONT: - searchPtr = strstr(searchPtr, "+CMGR: "); - if (searchPtr != NULL) - { - searchPtr += strlen("+CMGR: "); // Point to the originator address - int pointer = 0; - while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 12)) - { - unread->concat(*(searchPtr)); - pointer++; - } - if ((*searchPtr == '\0') || (pointer == 12)) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - // Search to the next quote - searchPtr = strchr(++searchPtr, '\"'); - pointer = 0; - while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 24)) - { - from->concat(*(searchPtr)); - pointer++; - } - if ((*searchPtr == '\0') || (pointer == 24)) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - // Skip two commas - searchPtr = strchr(++searchPtr, ','); - searchPtr = strchr(++searchPtr, ','); - // Search to the next quote - searchPtr = strchr(++searchPtr, '\"'); - pointer = 0; - while ((*(++searchPtr) != '\"') && (*searchPtr != '\0') && (pointer < 24)) - { - dateTime->concat(*(searchPtr)); - pointer++; - } - if ((*searchPtr == '\0') || (pointer == 24)) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - // Search to the next new line - searchPtr = strchr(++searchPtr, '\n'); - pointer = 0; - while ((*(++searchPtr) != '\r') && (*searchPtr != '\n') && (*searchPtr != '\0') && (pointer < 512)) - { - message->concat(*(searchPtr)); - pointer++; - } - if ((*searchPtr == '\0') || (pointer == 512)) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - } - else - { - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - } - else - { - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::deleteSMSmessage(int location, int deleteFlag) -{ - char *command; - SARA_R5_error_t err; - - command = sara_r5_calloc_char(strlen(SARA_R5_DELETE_MESSAGE) + 12); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (deleteFlag == 0) - sprintf(command, "%s=%d", SARA_R5_DELETE_MESSAGE, location); - else - sprintf(command, "%s=%d,%d", SARA_R5_DELETE_MESSAGE, location, deleteFlag); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_55_SECS_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setBaud(unsigned long baud) -{ - SARA_R5_error_t err; - char *command; - int b = 0; - - // Error check -- ensure supported baud - for (; b < NUM_SUPPORTED_BAUD; b++) - { - if (SARA_R5_SUPPORTED_BAUD[b] == baud) - { - break; - } - } - if (b >= NUM_SUPPORTED_BAUD) - { - return SARA_R5_ERROR_UNEXPECTED_PARAM; - } - - // Construct command - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_BAUD) + 7 + 12); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%lu", SARA_R5_COMMAND_BAUD, baud); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_SET_BAUD_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5_error_t SARA_R5::setFlowControl(SARA_R5_flow_control_t value) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_FLOW_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s%d", SARA_R5_FLOW_CONTROL, value); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5_error_t SARA_R5::setGpioMode(SARA_R5_gpio_t gpio, - SARA_R5_gpio_mode_t mode, int value) -{ - SARA_R5_error_t err; - char *command; - - // Example command: AT+UGPIOC=16,2 - // Example command: AT+UGPIOC=23,0,1 - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_GPIO) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (mode == GPIO_OUTPUT) - sprintf(command, "%s=%d,%d,%d", SARA_R5_COMMAND_GPIO, gpio, mode, value); - else - sprintf(command, "%s=%d,%d", SARA_R5_COMMAND_GPIO, gpio, mode); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_10_SEC_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5::SARA_R5_gpio_mode_t SARA_R5::getGpioMode(SARA_R5_gpio_t gpio) -{ - SARA_R5_error_t err; - char *command; - char *response; - char gpioChar[4]; - char *gpioStart; - int gpioMode; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_GPIO) + 2); - if (command == NULL) - return GPIO_MODE_INVALID; - sprintf(command, "%s?", SARA_R5_COMMAND_GPIO); - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return GPIO_MODE_INVALID; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return GPIO_MODE_INVALID; - } - - sprintf(gpioChar, "%d", gpio); // Convert GPIO to char array - gpioStart = strstr(response, gpioChar); // Find first occurence of GPIO in response - - free(command); - free(response); - - if (gpioStart == NULL) - return GPIO_MODE_INVALID; // If not found return invalid - sscanf(gpioStart, "%*d,%d\r\n", &gpioMode); - - return (SARA_R5_gpio_mode_t)gpioMode; -} - -int SARA_R5::socketOpen(SARA_R5_socket_protocol_t protocol, unsigned int localPort) -{ - SARA_R5_error_t err; - char *command; - char *response; - int sockId = -1; - char *responseStart; - - command = sara_r5_calloc_char(strlen(SARA_R5_CREATE_SOCKET) + 10); - if (command == NULL) - return -1; - if (localPort == 0) - sprintf(command, "%s=%d", SARA_R5_CREATE_SOCKET, (int)protocol); - else - sprintf(command, "%s=%d,%d", SARA_R5_CREATE_SOCKET, (int)protocol, localPort); - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - if (_printDebug == true) - _debugPort->println(F("socketOpen: Fail: NULL response")); - free(command); - return -1; - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("socketOpen: Fail: Error: ")); - _debugPort->print(err); - _debugPort->print(F(" Response: {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - free(command); - free(response); - return -1; - } - - responseStart = strstr(response, "+USOCR"); - if (responseStart == NULL) - { - if (_printDebug == true) - { - _debugPort->print(F("socketOpen: Failure: {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - free(command); - free(response); - return -1; - } - - sscanf(responseStart, "+USOCR: %d", &sockId); - _lastSocketProtocol[sockId] = (int)protocol; - - free(command); - free(response); - - return sockId; -} - -SARA_R5_error_t SARA_R5::socketClose(int socket, unsigned long timeout) -{ - SARA_R5_error_t err; - char *command; - char *response; - - command = sara_r5_calloc_char(strlen(SARA_R5_CLOSE_SOCKET) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - sprintf(command, "%s=%d", SARA_R5_CLOSE_SOCKET, socket); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, timeout); - - if ((err != SARA_R5_ERROR_SUCCESS) && (_printDebug == true)) - { - _debugPort->print(F("socketClose: Error: ")); - _debugPort->println(socketGetLastError()); - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::socketConnect(int socket, const char *address, - unsigned int port) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_CONNECT_SOCKET) + strlen(address) + 11); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,\"%s\",%d", SARA_R5_CONNECT_SOCKET, socket, address, port); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_IP_CONNECT_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5_error_t SARA_R5::socketConnect(int socket, IPAddress address, - unsigned int port) -{ - char *charAddress = sara_r5_calloc_char(16); - if (charAddress == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - memset(charAddress, 0, 16); - sprintf(charAddress, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); - - return (socketConnect(socket, (const char *)charAddress, port)); -} - -SARA_R5_error_t SARA_R5::socketWrite(int socket, const char *str, int len) -{ - char *command; - char *response; - SARA_R5_error_t err; - - command = sara_r5_calloc_char(strlen(SARA_R5_WRITE_SOCKET) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - int dataLen = len == -1 ? strlen(str) : len; - sprintf(command, "%s=%d,%d", SARA_R5_WRITE_SOCKET, socket, dataLen); - - err = sendCommandWithResponse(command, "@", response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); - - if (err == SARA_R5_ERROR_SUCCESS) - { - unsigned long writeDelay = millis(); - while (millis() < (writeDelay + 50)) - delay(1); //u-blox specification says to wait 50ms after receiving "@" to write data. - - if (len == -1) - { - if (_printDebug == true) - { - _debugPort->print(F("socketWrite: writing: ")); - _debugPort->println(str); - } - hwPrint(str); - } - else - { - if (_printDebug == true) - { - _debugPort->print(F("socketWrite: writing ")); - _debugPort->print(len); - _debugPort->println(F(" bytes")); - } - hwWriteData(str, len); - } - - err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_SOCKET_WRITE_TIMEOUT); - } - - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("socketWrite: Error: ")); - _debugPort->print(err); - _debugPort->print(F(" => {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::socketWrite(int socket, String str) -{ - return socketWrite(socket, str.c_str(), str.length()); -} - -SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, const char *address, int port, const char *str, int len) -{ - char *command; - char *response; - SARA_R5_error_t err; - int dataLen = len == -1 ? strlen(str) : len; - - command = sara_r5_calloc_char(64); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - sprintf(command, "%s=%d,\"%s\",%d,%d", SARA_R5_WRITE_UDP_SOCKET, - socket, address, port, dataLen); - err = sendCommandWithResponse(command, "@", response, SARA_R5_STANDARD_RESPONSE_TIMEOUT * 5); - - if (err == SARA_R5_ERROR_SUCCESS) - { - if (len == -1) //If binary data we need to send a length. - { - hwPrint(str); - } - else - { - hwWriteData(str, len); - } - err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_SOCKET_WRITE_TIMEOUT); - } - else - { - if (_printDebug == true) - _debugPort->print(F("socketWriteUDP: Error: ")); - if (_printDebug == true) - _debugPort->println(socketGetLastError()); - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, IPAddress address, int port, const char *str, int len) -{ - char *charAddress = sara_r5_calloc_char(16); - if (charAddress == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - memset(charAddress, 0, 16); - sprintf(charAddress, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); - - return (socketWriteUDP(socket, (const char *)charAddress, port, str, len)); -} - -SARA_R5_error_t SARA_R5::socketWriteUDP(int socket, String address, int port, String str) -{ - 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, int *bytesRead) -{ - char *command; - char *response; - char *strBegin; - 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) - { - if (_printDebug == true) - _debugPort->print(F("socketRead: length is 0! Call socketReadAvailable?")); - 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; - - // 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) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - // If there are more than _saraR5maxSocketRead (1024) bytes to be read, - // we need to do multiple reads to get all the data - - while (bytesLeftToRead > 0) - { - 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_OR_ERROR, 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", - &socketStore, &readLength); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("socketRead: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Check that readLength == bytesToRead - if (readLength != bytesToRead) - { - if (_printDebug == true) - { - _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(searchPtr, '\"'); - - if (strBegin == NULL) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Now copy the data into readDest - readIndexThisRead = 1; // Start after the quote - while (readIndexThisRead < (readLength + 1)) - { - 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 SARA_R5_ERROR_SUCCESS; -} - -SARA_R5_error_t SARA_R5::socketReadAvailable(int socket, int *length) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int readLength = 0; - int socketStore = 0; - - 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,0", SARA_R5_READ_SOCKET, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USORD: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USORD: %d,%d", - &socketStore, &readLength); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("socketReadAvailable: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *length = readLength; - } - - free(command); - free(response); - - return err; -} - -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 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) - { - if (_printDebug == true) - _debugPort->print(F("socketReadUDP: length is 0! Call socketReadAvailableUDP?")); - 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; - - // 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) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - // If there are more than _saraR5maxSocketRead (1024) bytes to be read, - // we need to do multiple reads to get all the data - - while (bytesLeftToRead > 0) - { - 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_OR_ERROR, 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", - &socketStore, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3], - &portStore, &readLength); - if (scanNum != 7) - { - if (_printDebug == true) - { - _debugPort->print(F("socketReadUDP: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Check that readLength == bytesToRead - if (readLength != bytesToRead) - { - if (_printDebug == true) - { - _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(searchPtr, '\"'); - strBegin = strchr(strBegin + 1, '\"'); - strBegin = strchr(strBegin + 1, '\"'); - - if (strBegin == NULL) - { - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Now copy the data into readDest - readIndexThisRead = 1; // Start after the quote - while (readIndexThisRead < (readLength + 1)) - { - readDest[readIndexTotal] = strBegin[readIndexThisRead]; - readIndexTotal++; - readIndexThisRead++; - } - - // If remoteIPaddress is not NULL, copy the remote IP address - if (remoteIPAddress != NULL) - { - IPAddress tempAddress; - for (int i = 0; i <= 3; i++) - { - tempAddress[i] = (uint8_t)remoteIPstore[i]; - } - *remoteIPAddress = tempAddress; - } - - // If remotePort is not NULL, copy the remote port - if (remotePort != NULL) - { - *remotePort = portStore; - } - - 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 SARA_R5_ERROR_SUCCESS; -} - -SARA_R5_error_t SARA_R5::socketReadAvailableUDP(int socket, int *length) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int readLength = 0; - int socketStore = 0; - - 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,0", SARA_R5_READ_UDP_SOCKET, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USORF: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USORF: %d,%d", - &socketStore, &readLength); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("socketReadAvailableUDP: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *length = readLength; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::socketListen(int socket, unsigned int port) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_LISTEN_SOCKET) + 9); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d", SARA_R5_LISTEN_SOCKET, socket, port); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::socketDirectLinkMode(int socket) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_DIRECT_LINK) + 8); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_SOCKET_DIRECT_LINK, socket); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_CONNECT, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::socketDirectLinkTimeTrigger(int socket, unsigned long timerTrigger) -{ - // valid range is 0 (trigger disabled), 100-120000 - if (!((timerTrigger == 0) || ((timerTrigger >= 100) && (timerTrigger <= 120000)))) - return SARA_R5_ERROR_ERROR; - - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=5,%d,%ld", SARA_R5_UD_CONFIGURATION, socket, timerTrigger); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::socketDirectLinkDataLengthTrigger(int socket, int dataLengthTrigger) -{ - // valid range is 0, 3-1472 for UDP - if (!((dataLengthTrigger == 0) || ((dataLengthTrigger >= 3) && (dataLengthTrigger <= 1472)))) - return SARA_R5_ERROR_ERROR; - - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=6,%d,%d", SARA_R5_UD_CONFIGURATION, socket, dataLengthTrigger); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::socketDirectLinkCharacterTrigger(int socket, int characterTrigger) -{ - // The allowed range is -1, 0-255, the factory-programmed value is -1; -1 means trigger disabled. - if (!((characterTrigger >= -1) && (characterTrigger <= 255))) - return SARA_R5_ERROR_ERROR; - - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=7,%d,%d", SARA_R5_UD_CONFIGURATION, socket, characterTrigger); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::socketDirectLinkCongestionTimer(int socket, unsigned long congestionTimer) -{ - // valid range is 0, 1000-72000 - if (!((congestionTimer == 0) || ((congestionTimer >= 1000) && (congestionTimer <= 72000)))) - return SARA_R5_ERROR_ERROR; - - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_UD_CONFIGURATION) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=8,%d,%ld", SARA_R5_UD_CONFIGURATION, socket, congestionTimer); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::querySocketType(int socket, SARA_R5_socket_protocol_t *protocol) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,0", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,0,%d", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketType: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *protocol = (SARA_R5_socket_protocol_t)paramVal; - _lastSocketProtocol[socketStore] = paramVal; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketLastError(int socket, int *error) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,1", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,1,%d", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketLastError: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *error = paramVal; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketTotalBytesSent(int socket, uint32_t *total) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - long unsigned int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,2", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,2,%lu", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketTotalBytesSent: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *total = (uint32_t)paramVal; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketTotalBytesReceived(int socket, uint32_t *total) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - long unsigned int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,3", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,3,%lu", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketTotalBytesReceived: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *total = (uint32_t)paramVal; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketRemoteIPAddress(int socket, IPAddress *address, int *port) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - int paramVals[5]; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,4", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,4,\"%d.%d.%d.%d\",%d", - &socketStore, - ¶mVals[0], ¶mVals[1], ¶mVals[2], ¶mVals[3], - ¶mVals[4]); - if (scanNum != 6) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketRemoteIPAddress: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - IPAddress tempAddress = { (uint8_t)paramVals[0], (uint8_t)paramVals[1], - (uint8_t)paramVals[2], (uint8_t)paramVals[3] }; - *address = tempAddress; - *port = paramVals[4]; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketStatusTCP(int socket, SARA_R5_tcp_socket_status_t *status) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,10", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,10,%d", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketStatusTCP: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *status = (SARA_R5_tcp_socket_status_t)paramVal; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::querySocketOutUnackData(int socket, uint32_t *total) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int socketStore = 0; - long unsigned int paramVal; - - command = sara_r5_calloc_char(strlen(SARA_R5_SOCKET_CONTROL) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,11", SARA_R5_SOCKET_CONTROL, socket); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOCTL: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+USOCTL: %d,11,%lu", - &socketStore, ¶mVal); - if (scanNum != 2) - { - if (_printDebug == true) - { - _debugPort->print(F("querySocketOutUnackData: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - *total = (uint32_t)paramVal; - } - - free(command); - free(response); - - return err; -} - -//Issues command to get last socket error, then prints to serial. Also updates rx/backlog buffers. -int SARA_R5::socketGetLastError() -{ - SARA_R5_error_t err; - char *command; - char *response; - int errorCode = -1; - - command = sara_r5_calloc_char(64); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - sprintf(command, "%s", SARA_R5_GET_ERROR); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+USOER: "); - if (searchPtr != NULL) - sscanf(searchPtr, "+USOER: %d", &errorCode); - } - - free(command); - free(response); - - return errorCode; -} - -IPAddress SARA_R5::lastRemoteIP(void) -{ - return _lastRemoteIP; -} - -SARA_R5_error_t SARA_R5::resetHTTPprofile(int profile) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_HTTP_PROFILE, profile); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPserverIPaddress(int profile, IPAddress address) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 64); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%d.%d.%d.%d\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_IP, - address[0], address[1], address[2], address[3]); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPserverName(int profile, String server) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + server.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_NAME, - server.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPusername(int profile, String username) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + username.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_USERNAME, - username.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPpassword(int profile, String password) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + password.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_PASSWORD, - password.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPauthentication(int profile, bool authenticate) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_AUTHENTICATION, - authenticate); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPserverPort(int profile, int port) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SERVER_PORT, - port); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPcustomHeader(int profile, String header) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 12 + header.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_ADD_CUSTOM_HEADERS, - header.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setHTTPsecure(int profile, bool secure, int secprofile) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROFILE) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (secprofile == -1) - sprintf(command, "%s=%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SECURE, - secure); - else sprintf(command, "%s=%d,%d,%d,%d", SARA_R5_HTTP_PROFILE, profile, SARA_R5_HTTP_OP_CODE_SECURE, - secure, secprofile); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::ping(String remote_host, int retry, int p_size, - unsigned long timeout, int ttl) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_PING_COMMAND) + 48 + - remote_host.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=\"%s\",%d,%d,%ld,%d", SARA_R5_PING_COMMAND, - remote_host.c_str(), retry, p_size, timeout, ttl); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::sendHTTPGET(int profile, String path, String responseFilename) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + - path.length() + responseFilename.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\",\"%s\"", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_GET, - path.c_str(), responseFilename.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::sendHTTPPOSTdata(int profile, String path, String responseFilename, - String data, SARA_R5_http_content_types_t httpContentType) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + - path.length() + responseFilename.length() + data.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\",\"%s\",\"%s\",%d", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_POST_DATA, - path.c_str(), responseFilename.c_str(), data.c_str(), httpContentType); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::sendHTTPPOSTfile(int profile, String path, String responseFilename, - String requestFile, SARA_R5_http_content_types_t httpContentType) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_HTTP_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_COMMAND) + 24 + - path.length() + responseFilename.length() + requestFile.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\",\"%s\",\"%s\",%d", SARA_R5_HTTP_COMMAND, profile, SARA_R5_HTTP_COMMAND_POST_FILE, - path.c_str(), responseFilename.c_str(), requestFile.c_str(), httpContentType); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getHTTPprotocolError(int profile, int *error_class, int *error_code) -{ - SARA_R5_error_t err; - char *command; - char *response; - - int rprofile, eclass, ecode; - - command = sara_r5_calloc_char(strlen(SARA_R5_HTTP_PROTOCOL_ERROR) + 4); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_HTTP_PROTOCOL_ERROR, profile); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - int scanned = 0; - char *searchPtr = strstr(response, "+UHTTPER: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+UHTTPER: %d,%d,%d\r\n", - &rprofile, &eclass, &ecode); - if (scanned == 3) - { - *error_class = eclass; - *error_code = ecode; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::nvMQTT(SARA_R5_mqtt_nv_parameter_t parameter) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_NVM) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_MQTT_NVM, parameter); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setMQTTclientId(String clientId) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + clientId.length() + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,\"%s\"", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_CLIENT_ID, clientId.c_str()); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setMQTTserver(String serverName, int port) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + serverName.length() + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,\"%s\",%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SERVERNAME, serverName.c_str(), port); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setMQTTsecure(bool secure, int secprofile) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (secprofile == -1) sprintf(command, "%s=%d,%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SECURE, secure); - else sprintf(command, "%s=%d,%d,%d", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_SECURE, secure, secprofile); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::connectMQTT(void) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_LOGIN); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::disconnectMQTT(void) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_LOGOUT); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::subscribeMQTTtopic(int max_Qos, String topic) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 16 + topic.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_SUBSCRIBE, max_Qos, topic.c_str()); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::unsubscribeMQTTtopic(String topic) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 16 + topic.length()); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_UNSUBSCRIBE, topic.c_str()); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int total_length, topic_length, data_length; - - // Set *bytesRead to zero - if (bytesRead != NULL) - *bytesRead = 0; - - // Allocate memory for the command - command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - // Allocate memory for the response - int responseLength = readLength + minimumResponseAllocation; - response = sara_r5_calloc_char(responseLength); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - // 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\n\r\nOK\r\n there is a extra \r\n beetween " and the the standard \r\nOK\r\n - const char mqttReadTerm[] = "\r\n\r\nOK\r\n"; - sprintf(command, "%s=%d,%d", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_READ, 1); - err = sendCommandWithResponse(command, mqttReadTerm, response, - (5 * SARA_R5_STANDARD_RESPONSE_TIMEOUT), responseLength); - - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("readMQTT: sendCommandWithResponse err ")); - _debugPort->println(err); - } - free(command); - free(response); - return err; - } - - // Extract the data - char *searchPtr = strstr(response, "+UMQTTC: 6"); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+UMQTTC: 6,%d,%d,%d,\"%*[^\"]\",%d,\"", pQos, &total_length, &topic_length, &data_length); - if (scanNum != 4) - { - if (_printDebug == true) - { - _debugPort->print(F("readMQTT: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - err = SARA_R5_ERROR_SUCCESS; - searchPtr = strstr(searchPtr, "\""); - if (searchPtr!= NULL) { - if (pTopic) { - searchPtr[topic_length + 1] = '\0'; // zero terminate - *pTopic = searchPtr + 1; - searchPtr[topic_length + 1] = '\"'; // restore - } - searchPtr = strstr(searchPtr + topic_length + 2, "\""); - if (readDest && (searchPtr != NULL) && (searchPtr[data_length + 1] == '"')) { - if (data_length > readLength) { - data_length = readLength; - if (_printDebug == true) { - _debugPort->print(F("readMQTT: error: trucate message")); - } - err = SARA_R5_ERROR_OUT_OF_MEMORY; - } - memcpy(readDest, searchPtr+1, data_length); - *bytesRead = data_length; - } else { - if (_printDebug == true) { - _debugPort->print(F("readMQTT: error: message end ")); - } - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - } - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::getMQTTprotocolError(int *error_code, int *error_code2) -{ - SARA_R5_error_t err; - char *response; - - int code, code2; - - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - err = sendCommandWithResponse(SARA_R5_MQTT_PROTOCOL_ERROR, SARA_R5_RESPONSE_OK_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - int scanned = 0; - char *searchPtr = strstr(response, "+UMQTTER: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+UMQTTER:%d,%d\r\n", - &code, &code2); - if (scanned == 2) - { - *error_code = code; - *error_code2 = code2; - } - else - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::resetSecurityProfile(int secprofile) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + 6); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - sprintf(command, "%s=%d", SARA_R5_SEC_PROFILE, secprofile); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::configSecurityProfile(int secprofile, SARA_R5_sec_profile_parameter_t parameter, int value) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,%d", SARA_R5_SEC_PROFILE, secprofile,parameter,value); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::configSecurityProfileString(int secprofile, SARA_R5_sec_profile_parameter_t parameter, String value) -{ - SARA_R5_error_t err; - char *command; - command = sara_r5_calloc_char(strlen(SARA_R5_SEC_PROFILE) + value.length() + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_SEC_PROFILE, secprofile,parameter,value.c_str()); - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setSecurityManager(SARA_R5_sec_manager_opcode_t opcode, SARA_R5_sec_manager_parameter_t parameter, String name, String data) -{ - char *command; - char *response; - SARA_R5_error_t err; - - command = sara_r5_calloc_char(strlen(SARA_R5_SEC_MANAGER) + name.length() + 20); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - int dataLen = data.length(); - sprintf(command, "%s=%d,%d,\"%s\",%d", SARA_R5_SEC_MANAGER, opcode, parameter, name.c_str(), dataLen); - - err = sendCommandWithResponse(command, ">", response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("dataDownload: writing ")); - _debugPort->print(dataLen); - _debugPort->println(F(" bytes")); - } - hwWriteData(data.c_str(), dataLen); - err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT*3); - } - - - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("dataDownload: Error: ")); - _debugPort->print(err); - _debugPort->print(F(" => {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, int value) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_PSD_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 24); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,%d", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, - value); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, SARA_R5_pdp_protocol_type_t value) -{ - return (setPDPconfiguration(profile, parameter, (int)value)); -} - -SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, String value) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_PSD_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 64); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%s\"", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, - value.c_str()); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, IPAddress value) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_PSD_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONFIG) + 64); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d,\"%d.%d.%d.%d\"", SARA_R5_MESSAGE_PDP_CONFIG, profile, parameter, - value[0], value[1], value[2], value[3]); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::performPDPaction(int profile, SARA_R5_pdp_actions_t action) -{ - SARA_R5_error_t err; - char *command; - - if (profile >= SARA_R5_NUM_PSD_PROFILES) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_ACTION) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d", SARA_R5_MESSAGE_PDP_ACTION, profile, action); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::activatePDPcontext(bool status, int cid) -{ - SARA_R5_error_t err; - char *command; - - if (cid >= SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS) - return SARA_R5_ERROR_ERROR; - - command = sara_r5_calloc_char(strlen(SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE) + 32); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (cid == -1) - sprintf(command, "%s=%d", SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE, status); - else - sprintf(command, "%s=%d,%d", SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE, status, cid); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::getNetworkAssignedIPAddress(int profile, IPAddress *address) -{ - char *command; - char *response; - SARA_R5_error_t err; - int scanNum = 0; - int profileStore = 0; - int paramTag = 0; // 0: IP address: dynamic IP address assigned during PDP context activation - int paramVals[4]; - - command = sara_r5_calloc_char(strlen(SARA_R5_NETWORK_ASSIGNED_DATA) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d,%d", SARA_R5_NETWORK_ASSIGNED_DATA, profile, paramTag); - - 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_OR_ERROR, response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - char *searchPtr = strstr(response, "+UPSND: "); - if (searchPtr != NULL) - scanNum = sscanf(searchPtr, "+UPSND: %d,%d,\"%d.%d.%d.%d\"", - &profileStore, ¶mTag, - ¶mVals[0], ¶mVals[1], ¶mVals[2], ¶mVals[3]); - if (scanNum != 6) - { - if (_printDebug == true) - { - _debugPort->print(F("getNetworkAssignedIPAddress: error: scanNum is ")); - _debugPort->println(scanNum); - } - free(command); - free(response); - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - IPAddress tempAddress = { (uint8_t)paramVals[0], (uint8_t)paramVals[1], - (uint8_t)paramVals[2], (uint8_t)paramVals[3] }; - *address = tempAddress; - } - - free(command); - free(response); - - return err; -} - -bool SARA_R5::isGPSon(void) -{ - SARA_R5_error_t err; - char *command; - char *response; - bool on = false; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_POWER) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_GNSS_POWER); - - 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_OR_ERROR, response, - SARA_R5_10_SEC_TIMEOUT); - - if (err == SARA_R5_ERROR_SUCCESS) - { - // Example response: "+UGPS: 0" for off "+UGPS: 1,0,1" for on - // Search for a ':' followed by a '1' or ' 1' - char *pch1 = strchr(response, ':'); - if (pch1 != NULL) - { - char *pch2 = strchr(response, '1'); - if ((pch2 != NULL) && ((pch2 == pch1 + 1) || (pch2 == pch1 + 2))) - on = true; - } - } - - free(command); - free(response); - - return on; -} - -SARA_R5_error_t SARA_R5::gpsPower(bool enable, gnss_system_t gnss_sys, gnss_aiding_mode_t gnss_aiding) -{ - SARA_R5_error_t err; - char *command; - bool gpsState; - - // Don't turn GPS on/off if it's already on/off - gpsState = isGPSon(); - if ((enable && gpsState) || (!enable && !gpsState)) - { - return SARA_R5_ERROR_SUCCESS; - } - - // GPS power management - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_POWER) + 32); // gnss_sys could be up to three digits - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (enable) - { - sprintf(command, "%s=1,%d,%d", SARA_R5_GNSS_POWER, gnss_aiding, gnss_sys); - } - else - { - sprintf(command, "%s=0", SARA_R5_GNSS_POWER); - } - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, 10000); - - free(command); - return err; -} - -/* -SARA_R5_error_t SARA_R5::gpsEnableClock(bool enable) -{ - // AT+UGZDA=<0,1> - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetClock(struct ClockData *clock) -{ - // AT+UGZDA? - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsEnableFix(bool enable) -{ - // AT+UGGGA=<0,1> - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetFix(struct PositionData *pos) -{ - // AT+UGGGA? - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsEnablePos(bool enable) -{ - // AT+UGGLL=<0,1> - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetPos(struct PositionData *pos) -{ - // AT+UGGLL? - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsEnableSat(bool enable) -{ - // AT+UGGSV=<0,1> - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetSat(uint8_t *sats) -{ - // AT+UGGSV? - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} -*/ - -SARA_R5_error_t SARA_R5::gpsEnableRmc(bool enable) -{ - // AT+UGRMC=<0,1> - SARA_R5_error_t err; - char *command; - - // ** Don't call gpsPower here. It causes problems for +UTIME and the PPS signal ** - // ** Call isGPSon and gpsPower externally if required ** - // if (!isGPSon()) - // { - // err = gpsPower(true); - // if (err != SARA_R5_ERROR_SUCCESS) - // { - // return err; - // } - // } - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_GPRMC) + 3); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_GNSS_GPRMC, enable ? 1 : 0); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_10_SEC_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetRmc(struct PositionData *pos, struct SpeedData *spd, - struct ClockData *clk, bool *valid) -{ - SARA_R5_error_t err; - char *command; - char *response; - char *rmcBegin; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_GPRMC) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_GNSS_GPRMC); - - 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_OR_ERROR, response, SARA_R5_10_SEC_TIMEOUT); - if (err == SARA_R5_ERROR_SUCCESS) - { - // Fast-forward response string to $GPRMC starter - rmcBegin = strstr(response, "$GPRMC"); - if (rmcBegin == NULL) - { - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - else - { - *valid = parseGPRMCString(rmcBegin, pos, clk, spd); - } - } - - free(command); - free(response); - return err; -} - -/* -SARA_R5_error_t SARA_R5::gpsEnableSpeed(bool enable) -{ - // AT+UGVTG=<0,1> - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} - -SARA_R5_error_t SARA_R5::gpsGetSpeed(struct SpeedData *speed) -{ - // AT+UGVTG? - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - return err; -} -*/ - -SARA_R5_error_t SARA_R5::gpsRequest(unsigned int timeout, uint32_t accuracy, - bool detailed, unsigned int sensor) -{ - // AT+ULOC=2,,,, - SARA_R5_error_t err; - char *command; - - // This function will only work if the GPS module is initially turned off. - if (isGPSon()) - { - gpsPower(false); - } - - if (timeout > 999) - timeout = 999; - if (accuracy > 999999) - accuracy = 999999; - - command = sara_r5_calloc_char(strlen(SARA_R5_GNSS_REQUEST_LOCATION) + 24); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; -#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) - sprintf(command, "%s=2,%d,%d,%d,%d", SARA_R5_GNSS_REQUEST_LOCATION, - sensor, detailed ? 1 : 0, timeout, accuracy); -#else - sprintf(command, "%s=2,%d,%d,%d,%ld", SARA_R5_GNSS_REQUEST_LOCATION, - sensor, detailed ? 1 : 0, timeout, accuracy); -#endif - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, SARA_R5_10_SEC_TIMEOUT); - - free(command); - return err; -} - -SARA_R5_error_t SARA_R5::gpsAidingServerConf(const char *primaryServer, const char *secondaryServer, const char *authToken, - unsigned int days, unsigned int period, unsigned int resolution, - unsigned int gnssTypes, unsigned int mode, unsigned int dataType) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_AIDING_SERVER_CONFIGURATION) + 256); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - sprintf(command, "%s=\"%s\",\"%s\",\"%s\",%d,%d,%d,%d,%d,%d", SARA_R5_AIDING_SERVER_CONFIGURATION, - primaryServer, secondaryServer, authToken, - days, period, resolution, gnssTypes, mode, dataType); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - return err; -} - - -// OK for text files. But will fail with binary files (containing \0) on some platforms. -SARA_R5_error_t SARA_R5::appendFileContents(String filename, const char *str, int len) -{ - char *command; - char *response; - SARA_R5_error_t err; - - command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_DOWNLOAD_FILE) + filename.length() + 10); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == NULL) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - int dataLen = len == -1 ? strlen(str) : len; - sprintf(command, "%s=\"%s\",%d", SARA_R5_FILE_SYSTEM_DOWNLOAD_FILE, filename.c_str(), dataLen); - - err = sendCommandWithResponse(command, ">", response, - SARA_R5_STANDARD_RESPONSE_TIMEOUT*2); - - unsigned long writeDelay = millis(); - while (millis() < (writeDelay + 50)) - delay(1); //uBlox specification says to wait 50ms after receiving "@" to write data. - - if (err == SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("fileDownload: writing ")); - _debugPort->print(dataLen); - _debugPort->println(F(" bytes")); - } - hwWriteData(str, dataLen); - - err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT*5); - } - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("fileDownload: Error: ")); - _debugPort->print(err); - _debugPort->print(F(" => {")); - _debugPort->print(response); - _debugPort->println(F("}")); - } - } - - free(command); - free(response); - return err; -} - -SARA_R5_error_t SARA_R5::appendFileContents(String filename, String str) -{ - return appendFileContents(filename, str.c_str(), str.length()); -} - - -// 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; - 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 + 2*minimumResponseAllocation); - if (response == NULL) - { - if (_printDebug == true) - { - _debugPort->print(F("getFileContents: response alloc failed: ")); - _debugPort->println(fileSize + 2*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, (10 * SARA_R5_STANDARD_RESPONSE_TIMEOUT), - (fileSize + 2*minimumResponseAllocation)); - - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - { - _debugPort->print(F("getFileContents: sendCommandWithResponse returned err ")); - _debugPort->print(">>>");_debugPort->print(response);_debugPort->print("<<<"); - _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 - // 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++; - } - 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; -} - -// 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; - 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_OR_ERROR, 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_OR_ERROR, 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) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_POWER_OFF) + 6); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - sprintf(command, "%s", SARA_R5_COMMAND_POWER_OFF); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, NULL, - SARA_R5_POWER_OFF_TIMEOUT); - - free(command); - return err; -} - -void SARA_R5::modulePowerOn(void) -{ - if (_powerPin >= 0) - { - powerOn(); - } - else - { - if (_printDebug == true) - _debugPort->println(F("modulePowerOn: not supported. _powerPin not defined.")); - } -} - -///////////// -// Private // -///////////// - -SARA_R5_error_t SARA_R5::init(unsigned long baud, - SARA_R5::SARA_R5_init_type_t initType) -{ - int retries = _maxInitTries; - SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - - beginSerial(baud); - - do - { - if (_printDebug == true) - _debugPort->println(F("init: Begin module init.")); - - if (initType == SARA_R5_INIT_AUTOBAUD) - { - if (_printDebug == true) - _debugPort->println(F("init: Attempting autobaud connection to module.")); - - err = autobaud(baud); - - if (err != SARA_R5_ERROR_SUCCESS) { - initType = SARA_R5_INIT_RESET; - } - } - else if (initType == SARA_R5_INIT_RESET) - { - if (_printDebug == true) - _debugPort->println(F("init: Power cycling module.")); - - powerOff(); - delay(SARA_R5_POWER_OFF_PULSE_PERIOD); - powerOn(); - beginSerial(baud); - delay(2000); - - err = at(); - if (err != SARA_R5_ERROR_SUCCESS) - { - initType = SARA_R5_INIT_AUTOBAUD; - } - } - if (err == SARA_R5_ERROR_SUCCESS) - { - err = enableEcho(false); // = disableEcho - if (err != SARA_R5_ERROR_SUCCESS) - { - if (_printDebug == true) - _debugPort->println(F("init: Module failed echo test.")); - initType = SARA_R5_INIT_AUTOBAUD; - } - } - } - while ((retries --) && (err != SARA_R5_ERROR_SUCCESS)); - - // we tried but seems failed - if (err != SARA_R5_ERROR_SUCCESS) { - if (_printDebug == true) - _debugPort->println(F("init: Module failed to init. Exiting.")); - return (SARA_R5_ERROR_NO_RESPONSE); - } - - if (_printDebug == true) - _debugPort->println(F("init: Module responded successfully.")); - - _baud = baud; - setGpioMode(GPIO1, NETWORK_STATUS); - //setGpioMode(GPIO2, GNSS_SUPPLY_ENABLE); - setGpioMode(GPIO6, TIME_PULSE_OUTPUT); - setSMSMessageFormat(SARA_R5_MESSAGE_FORMAT_TEXT); - autoTimeZone(_autoTimeZoneForBegin); - for (int i = 0; i < SARA_R5_NUM_SOCKETS; i++) - { - socketClose(i, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - } - - return SARA_R5_ERROR_SUCCESS; -} - -void SARA_R5::invertPowerPin(bool invert) -{ - _invertPowerPin = invert; -} - -// Do a graceful power off. Hold the PWR_ON pin low for SARA_R5_POWER_OFF_PULSE_PERIOD -// Note: +CPWROFF () is preferred to this. -void SARA_R5::powerOff(void) -{ - if (_powerPin >= 0) - { - if (_invertPowerPin) // Set the pin state before making it an output - digitalWrite(_powerPin, HIGH); - else - digitalWrite(_powerPin, LOW); - pinMode(_powerPin, OUTPUT); - if (_invertPowerPin) // Set the pin state - digitalWrite(_powerPin, HIGH); - else - digitalWrite(_powerPin, LOW); - delay(SARA_R5_POWER_OFF_PULSE_PERIOD); - pinMode(_powerPin, INPUT); // Return to high-impedance, rely on (e.g.) SARA module internal pull-up - if (_printDebug == true) - _debugPort->println(F("powerOff: complete")); - } -} - -void SARA_R5::powerOn(void) -{ - if (_powerPin >= 0) - { - if (_invertPowerPin) // Set the pin state before making it an output - digitalWrite(_powerPin, HIGH); - else - digitalWrite(_powerPin, LOW); - pinMode(_powerPin, OUTPUT); - if (_invertPowerPin) // Set the pin state - digitalWrite(_powerPin, HIGH); - else - digitalWrite(_powerPin, LOW); - delay(SARA_R5_POWER_ON_PULSE_PERIOD); - pinMode(_powerPin, INPUT); // Return to high-impedance, rely on (e.g.) SARA module internal pull-up - //delay(2000); // Do this in init. Wait before sending AT commands to module. 100 is too short. - if (_printDebug == true) - _debugPort->println(F("powerOn: complete")); - } -} - -//This does an abrupt emergency hardware shutdown of the SARA-R5 series modules. -//It only works if you have access to both the RESET_N and PWR_ON pins. -//You cannot use this function on the SparkFun Asset Tracker and RESET_N is tied to the MicroMod processor !RESET!... -void SARA_R5::hwReset(void) -{ - if ((_resetPin >= 0) && (_powerPin >= 0)) - { - digitalWrite(_resetPin, HIGH); // Start by making sure the RESET_N pin is high - pinMode(_resetPin, OUTPUT); - digitalWrite(_resetPin, HIGH); - - if (_invertPowerPin) // Now pull PWR_ON low - invert as necessary (on the Asset Tracker) - { - digitalWrite(_powerPin, HIGH); // Inverted - Asset Tracker - pinMode(_powerPin, OUTPUT); - digitalWrite(_powerPin, HIGH); - } - else - { - digitalWrite(_powerPin, LOW); // Not inverted - pinMode(_powerPin, OUTPUT); - digitalWrite(_powerPin, LOW); - } - - delay(SARA_R5_RESET_PULSE_PERIOD); // Wait 23 seconds... (Yes, really!) - - digitalWrite(_resetPin, LOW); // Now pull RESET_N low - - delay(100); // Wait a little... (The data sheet doesn't say how long for) - - if (_invertPowerPin) // Now pull PWR_ON high - invert as necessary (on the Asset Tracker) - { - digitalWrite(_powerPin, LOW); // Inverted - Asset Tracker - } - else - { - digitalWrite(_powerPin, HIGH); // Not inverted - } - - delay(1500); // Wait 1.5 seconds - - digitalWrite(_resetPin, HIGH); // Now pull RESET_N high again - - pinMode(_resetPin, INPUT); // Return to high-impedance, rely on SARA module internal pull-up - pinMode(_powerPin, INPUT); // Return to high-impedance, rely on SARA module internal pull-up - } -} - -SARA_R5_error_t SARA_R5::functionality(SARA_R5_functionality_t function) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_FUNC) + 16); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s=%d", SARA_R5_COMMAND_FUNC, function); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_3_MIN_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5_error_t SARA_R5::setMNOprofile(mobile_network_operator_t mno, bool autoReset, bool urcNotification) -{ - SARA_R5_error_t err; - char *command; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_MNO) + 9); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - if (mno == MNO_SIM_ICCID) // Only add autoReset and urcNotification if mno is MNO_SIM_ICCID - sprintf(command, "%s=%d,%d,%d", SARA_R5_COMMAND_MNO, (uint8_t)mno, (uint8_t)autoReset, (uint8_t)urcNotification); - else - sprintf(command, "%s=%d", SARA_R5_COMMAND_MNO, (uint8_t)mno); - - err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, - NULL, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - - free(command); - - return err; -} - -SARA_R5_error_t SARA_R5::getMNOprofile(mobile_network_operator_t *mno) -{ - SARA_R5_error_t err; - char *command; - char *response; - mobile_network_operator_t o; - int d; - int r; - int u; - int oStore; - - command = sara_r5_calloc_char(strlen(SARA_R5_COMMAND_MNO) + 2); - if (command == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - sprintf(command, "%s?", SARA_R5_COMMAND_MNO); - - 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_OR_ERROR, - response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(command); - free(response); - return err; - } - - int scanned = 0; - char *searchPtr = strstr(response, "+UMNOPROF: "); - if (searchPtr != NULL) - scanned = sscanf(searchPtr, "+UMNOPROF: %d,%d,%d,%d", &oStore, &d, &r, &u); - o = (mobile_network_operator_t)oStore; - - if (scanned >= 1) - { - if (_printDebug == true) - { - _debugPort->print(F("getMNOprofile: MNO is: ")); - _debugPort->println(o); - } - *mno = o; - } - else - { - err = SARA_R5_ERROR_INVALID; - } - - free(command); - free(response); - - return err; -} - -SARA_R5_error_t SARA_R5::waitForResponse(const char *expectedResponse, const char *expectedError, uint16_t timeout) -{ - unsigned long timeIn; - bool found = false; - bool error = false; - int responseIndex = 0, errorIndex = 0; - // bool printedSomething = false; - - timeIn = millis(); - - int responseLen = (int)strlen(expectedResponse); - int errorLen = (int)strlen(expectedError); - - while ((!found) && ((timeIn + timeout) > millis())) - { - 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("waitForResponse: ")); - // _debugPort->write(c); - // printedSomething = true; - // } - if ((responseIndex < responseLen) && (c == expectedResponse[responseIndex])) - { - if (++responseIndex == responseLen) - { - found = true; - } - } - else - { - responseIndex = ((responseIndex < responseLen) && (c == expectedResponse[0])) ? 1 : 0; - } - if ((errorIndex < errorLen) && (c == expectedError[errorIndex])) - { - if (++errorIndex == errorLen) - { - error = true; - found = true; - } - } - else - { - errorIndex = ((errorIndex < errorLen) && (c == expectedError[0])) ? 1 : 0; - } - //_saraResponseBacklog is a global array that holds the backlog of any events - //that came in while waiting for response. To be processed later within bufferedPoll(). - //Note: the expectedResponse or expectedError will also be added to the backlog. - //The backlog is only used by bufferedPoll to process the URCs - which are all readable. - //bufferedPoll uses strtok - which does not like NULL characters. - //So let's make sure no NULLs end up in the backlog! - if (_saraResponseBacklogLength < _RXBuffSize) // Don't overflow the buffer - { - if (c == '\0') - _saraResponseBacklog[_saraResponseBacklogLength++] = '0'; // Change NULLs to ASCII Zeros - else - _saraResponseBacklog[_saraResponseBacklogLength++] = c; - } - } else { - yield(); - } - } - - // if (_printDebug == true) - // if (printedSomething) - // _debugPort->println(); - - pruneBacklog(); // Prune any incoming non-actionable URC's and responses/errors from the backlog - - if (found == true) - { - if (true == _printAtDebug) { - _debugAtPort->print((error == true) ? expectedError : expectedResponse); - } - - return (error == true) ? SARA_R5_ERROR_ERROR : SARA_R5_ERROR_SUCCESS; - } - - return SARA_R5_ERROR_NO_RESPONSE; -} - -SARA_R5_error_t SARA_R5::sendCommandWithResponse( - const char *command, const char *expectedResponse, char *responseDest, - unsigned long commandTimeout, int destSize, bool at) -{ - bool found = false; - bool error = false; - int responseIndex = 0; - int errorIndex = 0; - int destIndex = 0; - unsigned int charsRead = 0; - int responseLen = 0; - int errorLen = 0; - const char* expectedError= NULL; - //bool printedSomething = false; - - if (_printDebug == true) - { - _debugPort->print(F("sendCommandWithResponse: Command: ")); - _debugPort->println(String(command)); - } - - sendCommand(command, at); //Sending command needs to dump data to backlog buffer as well. - unsigned long timeIn = millis(); - if (SARA_R5_RESPONSE_OK_OR_ERROR == expectedResponse) { - expectedResponse = SARA_R5_RESPONSE_OK; - expectedError = SARA_R5_RESPONSE_ERROR; - responseLen = sizeof(SARA_R5_RESPONSE_OK)-1; - errorLen = sizeof(SARA_R5_RESPONSE_ERROR)-1; - } else { - responseLen = (int)strlen(expectedResponse); - } - - while ((!found) && ((timeIn + commandTimeout) > millis())) - { - 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 (responseDest != NULL) - { - if (destIndex < destSize) // Only add this char to response if there is room for it - responseDest[destIndex] = c; - destIndex++; - if (destIndex == destSize) - { - if (_printDebug == true) - { - // if (printedSomething) - // _debugPort->println(); - _debugPort->print(F("sendCommandWithResponse: Panic! responseDest is full!")); - // if (printedSomething) - // _debugPort->print(F("sendCommandWithResponse: Ignored response: ")); - } - } - } - charsRead++; - if ((errorIndex < errorLen) && (c == expectedError[errorIndex])) - { - if (++errorIndex == errorLen) - { - error = true; - found = true; - } - } - else - { - errorIndex = ((errorIndex < errorLen) && (c == expectedError[0])) ? 1 : 0; - } - if ((responseIndex < responseLen) && (c == expectedResponse[responseIndex])) - { - if (++responseIndex == responseLen) - { - found = true; - } - } - else - { - responseIndex = ((responseIndex < responseLen) && (c == expectedResponse[0])) ? 1 : 0; - } - //_saraResponseBacklog is a global array that holds the backlog of any events - //that came in while waiting for response. To be processed later within bufferedPoll(). - //Note: the expectedResponse or expectedError will also be added to the backlog - //The backlog is only used by bufferedPoll to process the URCs - which are all readable. - //bufferedPoll uses strtok - which does not like NULL characters. - //So let's make sure no NULLs end up in the backlog! - if (_saraResponseBacklogLength < _RXBuffSize) // Don't overflow the buffer - { - if (c == '\0') - _saraResponseBacklog[_saraResponseBacklogLength++] = '0'; // Change NULLs to ASCII Zeros - else - _saraResponseBacklog[_saraResponseBacklogLength++] = c; - } - } else { - yield(); - } - } - - // if (_printDebug == true) - // if (printedSomething) - // _debugPort->println(); - - pruneBacklog(); // Prune any incoming non-actionable URC's and responses/errors from the backlog - - if (found) - { - if ((true == _printAtDebug) && ((NULL != responseDest) || (NULL != expectedResponse))) { - _debugAtPort->print((NULL != responseDest) ? responseDest : expectedResponse); - } - return error ? SARA_R5_ERROR_ERROR : SARA_R5_ERROR_SUCCESS; - } - else if (charsRead == 0) - { - return SARA_R5_ERROR_NO_RESPONSE; - } - else - { - if ((true == _printAtDebug) && (NULL != responseDest)) { - _debugAtPort->print(responseDest); - } - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } -} - -// Send a custom command with an expected (potentially partial) response, store entire response -SARA_R5_error_t SARA_R5::sendCustomCommandWithResponse(const char *command, const char *expectedResponse, - char *responseDest, unsigned long commandTimeout, bool at) -{ - // Assume the user has allocated enough storage for any response. Set destSize to 32766. - return sendCommandWithResponse(command, expectedResponse, responseDest, commandTimeout, 32766, at); -} - -void SARA_R5::sendCommand(const char *command, bool at) -{ - //Check for incoming serial data. Copy it into the backlog - - // Important note: - // On ESP32, Serial.available only provides an update every ~120 bytes during the reception of long messages: - // https://gitter.im/espressif/arduino-esp32?at=5e25d6370a1cf54144909c85 - // Be aware that if a long message is being received, the code below will timeout after _rxWindowMillis = 2 millis. - // At 115200 baud, hwAvailable takes ~120 * 10 / 115200 = 10.4 millis before it indicates that data is being received. - - unsigned long timeIn = millis(); - if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL - { - while (((millis() - timeIn) < _rxWindowMillis) && (_saraResponseBacklogLength < _RXBuffSize)) //May need to escape on newline? - { - if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is NULL - { - //_saraResponseBacklog is a global array that holds the backlog of any events - //that came in while waiting for response. To be processed later within bufferedPoll(). - //Note: the expectedResponse or expectedError will also be added to the backlog - //The backlog is only used by bufferedPoll to process the URCs - which are all readable. - //bufferedPoll uses strtok - which does not like NULL characters. - //So let's make sure no NULLs end up in the backlog! - char c = readChar(); - if (c == '\0') // Make sure no NULL characters end up in the backlog! Change them to ASCII Zeros - c = '0'; - _saraResponseBacklog[_saraResponseBacklogLength++] = c; - timeIn = millis(); - } else { - yield(); - } - } - } - - //Now send the command - if (at) - { - hwPrint(SARA_R5_COMMAND_AT); - hwPrint(command); - hwPrint("\r\n"); - } - else - { - hwPrint(command); - } -} - -SARA_R5_error_t SARA_R5::parseSocketReadIndication(int socket, int length) -{ - SARA_R5_error_t err; - char *readDest; - - if ((socket < 0) || (length < 0)) - { - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Return now if both callbacks pointers are NULL - otherwise the data will be read and lost! - if ((_socketReadCallback == NULL) && (_socketReadCallbackPlus == NULL)) - return SARA_R5_ERROR_INVALID; - - readDest = sara_r5_calloc_char(length + 1); - if (readDest == NULL) - return SARA_R5_ERROR_OUT_OF_MEMORY; - - int bytesRead; - err = socketRead(socket, length, readDest, &bytesRead); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(readDest); - return err; - } - - if (_socketReadCallback != NULL) - { - String dataAsString = ""; // Create an empty string - // 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); - } - - if (_socketReadCallbackPlus != NULL) - { - IPAddress dummyAddress = { 0, 0, 0, 0 }; - int dummyPort = 0; - _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, dummyAddress, dummyPort); - } - - free(readDest); - return SARA_R5_ERROR_SUCCESS; -} - -SARA_R5_error_t SARA_R5::parseSocketReadIndicationUDP(int socket, int length) -{ - SARA_R5_error_t err; - char *readDest; - IPAddress remoteAddress = { 0, 0, 0, 0 }; - int remotePort = 0; - - if ((socket < 0) || (length < 0)) - { - return SARA_R5_ERROR_UNEXPECTED_RESPONSE; - } - - // Return now if both callbacks pointers are NULL - otherwise the data will be read and lost! - if ((_socketReadCallback == NULL) && (_socketReadCallbackPlus == NULL)) - return SARA_R5_ERROR_INVALID; - - readDest = sara_r5_calloc_char(length + 1); - if (readDest == NULL) - { - return SARA_R5_ERROR_OUT_OF_MEMORY; - } - - int bytesRead; - err = socketReadUDP(socket, length, readDest, &remoteAddress, &remotePort, &bytesRead); - if (err != SARA_R5_ERROR_SUCCESS) - { - free(readDest); - return err; - } - - 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); - } - - if (_socketReadCallbackPlus != NULL) - { - _socketReadCallbackPlus(socket, (const char *)readDest, bytesRead, remoteAddress, remotePort); - } - - free(readDest); - return SARA_R5_ERROR_SUCCESS; -} - -SARA_R5_error_t SARA_R5::parseSocketListenIndication(int listeningSocket, IPAddress localIP, unsigned int listeningPort, int socket, IPAddress remoteIP, unsigned int port) -{ - _lastLocalIP = localIP; - _lastRemoteIP = remoteIP; - - if (_socketListenCallback != NULL) - { - _socketListenCallback(listeningSocket, localIP, listeningPort, socket, remoteIP, port); - } - - return SARA_R5_ERROR_SUCCESS; -} - -SARA_R5_error_t SARA_R5::parseSocketCloseIndication(String *closeIndication) -{ - int search; - int socket; - - search = closeIndication->indexOf("UUSOCL: ") + strlen("UUSOCL: "); - - // Socket will be first integer, should be single-digit number between 0-6: - socket = closeIndication->substring(search, search + 1).toInt(); - - if (_socketCloseCallback != NULL) - { - _socketCloseCallback(socket); - } - - return SARA_R5_ERROR_SUCCESS; -} - -size_t SARA_R5::hwPrint(const char *s) -{ - if ((true == _printAtDebug) && (NULL != s)) { - _debugAtPort->print(s); - } - if (_hardSerial != NULL) - { - return _hardSerial->print(s); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - return _softSerial->print(s); - } -#endif - - return (size_t)0; -} - -size_t SARA_R5::hwWriteData(const char *buff, int len) -{ - if ((true == _printAtDebug) && (NULL != buff) && (0 < len) ) { - _debugAtPort->write(buff,len); - } - if (_hardSerial != NULL) - { - return _hardSerial->write((const uint8_t *)buff, len); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - return _softSerial->write((const uint8_t *)buff, len); - } -#endif - return (size_t)0; -} - -size_t SARA_R5::hwWrite(const char c) -{ - if (true == _printAtDebug) { - _debugAtPort->write(c); - } - if (_hardSerial != NULL) - { - return _hardSerial->write(c); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - return _softSerial->write(c); - } -#endif - - return (size_t)0; -} - -int SARA_R5::readAvailable(char *inString) -{ - int len = 0; - - if (_hardSerial != NULL) - { - while (_hardSerial->available()) - { - char c = (char)_hardSerial->read(); - if (inString != NULL) - { - inString[len++] = c; - } - } - if (inString != NULL) - { - inString[len] = 0; - } - //if (_printDebug == true) - // _debugPort->println(inString); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - while (_softSerial->available()) - { - char c = (char)_softSerial->read(); - if (inString != NULL) - { - inString[len++] = c; - } - } - if (inString != NULL) - { - inString[len] = 0; - } - } -#endif - - return len; -} - -char SARA_R5::readChar(void) -{ - char ret = 0; - - if (_hardSerial != NULL) - { - ret = (char)_hardSerial->read(); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - ret = (char)_softSerial->read(); - } -#endif - - return ret; -} - -int SARA_R5::hwAvailable(void) -{ - if (_hardSerial != NULL) - { - return _hardSerial->available(); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - return _softSerial->available(); - } -#endif - - return -1; -} - -void SARA_R5::beginSerial(unsigned long baud) -{ - delay(100); - if (_hardSerial != NULL) - { - _hardSerial->updateBaudRate(baud); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - _softSerial->end(); - _softSerial->begin(baud); - } -#endif - delay(100); -} - -void SARA_R5::setTimeout(unsigned long timeout) -{ - if (_hardSerial != NULL) - { - _hardSerial->setTimeout(timeout); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - _softSerial->setTimeout(timeout); - } -#endif -} - -bool SARA_R5::find(char *target) -{ - bool found = false; - if (_hardSerial != NULL) - { - found = _hardSerial->find(target); - } -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - else if (_softSerial != NULL) - { - found = _softSerial->find(target); - } -#endif - return found; -} - -SARA_R5_error_t SARA_R5::autobaud(unsigned long desiredBaud) -{ - SARA_R5_error_t err = SARA_R5_ERROR_INVALID; - int b = 0; - - while ((err != SARA_R5_ERROR_SUCCESS) && (b < NUM_SUPPORTED_BAUD)) - { - beginSerial(SARA_R5_SUPPORTED_BAUD[b++]); - setBaud(desiredBaud); - beginSerial(desiredBaud); - err = at(); - } - if (err == SARA_R5_ERROR_SUCCESS) - { - beginSerial(desiredBaud); - } - return err; -} - -char *SARA_R5::sara_r5_calloc_char(size_t num) -{ - return (char *)calloc(num, sizeof(char)); -} - -//This prunes the backlog of non-actionable events. If new actionable events are added, you must modify the if statement. -void SARA_R5::pruneBacklog() -{ - char *event; - - // if (_printDebug == true) - // { - // if (_saraResponseBacklogLength > 0) //Handy for debugging new parsing. - // { - // _debugPort->println(F("pruneBacklog: before pruning, backlog was:")); - // _debugPort->println(_saraResponseBacklog); - // _debugPort->println(F("pruneBacklog: end of backlog")); - // } - // else - // { - // _debugPort->println(F("pruneBacklog: backlog was empty")); - // } - // } - - memset(_pruneBuffer, 0, _RXBuffSize); // Clear the _pruneBuffer - - _saraResponseBacklogLength = 0; // Zero the backlog length - - char *preservedEvent; - event = strtok_r(_saraResponseBacklog, "\r\n", &preservedEvent); // Look for an 'event' - something ending in \r\n - - while (event != NULL) //If event is actionable, add it to pruneBuffer. - { - // These are the events we want to keep so they can be processed by poll / bufferedPoll - if ((strstr(event, "+UUSORD:") != NULL) - || (strstr(event, "+UUSORF:") != NULL) - || (strstr(event, "+UUSOLI:") != NULL) - || (strstr(event, "+UUSOCL:") != NULL) - || (strstr(event, "+UULOC:") != NULL) - || (strstr(event, "+UUSIMSTAT:") != NULL) - || (strstr(event, "+UUPSDA:") != NULL) - || (strstr(event, "+UUPING:") != NULL) - || (strstr(event, "+UUMQTTC:") != NULL) - || (strstr(event, "+UUCREG:") != NULL) - || (strstr(event, "+UUCEREG:") != NULL) - || (strstr(event, "+UUHTTPCR:") != NULL)) - { - strcat(_pruneBuffer, event); // The URCs are all readable text so using strcat is OK - strcat(_pruneBuffer, "\r\n"); // strtok blows away delimiter, but we want that for later. - _saraResponseBacklogLength += strlen(event) + 2; // Add the length of this event to _saraResponseBacklogLength - } - - event = strtok_r(NULL, "\r\n", &preservedEvent); // Walk though any remaining events - } - - memset(_saraResponseBacklog, 0, _RXBuffSize); //Clear out backlog buffer. - memcpy(_saraResponseBacklog, _pruneBuffer, _saraResponseBacklogLength); //Copy the pruned buffer back into the backlog - - // if (_printDebug == true) - // { - // if (_saraResponseBacklogLength > 0) //Handy for debugging new parsing. - // { - // _debugPort->println(F("pruneBacklog: after pruning, backlog is now:")); - // _debugPort->println(_saraResponseBacklog); - // _debugPort->println(F("pruneBacklog: end of backlog")); - // } - // else - // { - // _debugPort->println(F("pruneBacklog: backlog is now empty")); - // } - // } - - free(event); -} - -// GPS Helper Functions: - -// Read a source string until a delimiter is hit, store the result in destination -char *SARA_R5::readDataUntil(char *destination, unsigned int destSize, - char *source, char delimiter) -{ - - char *strEnd; - size_t len; - - strEnd = strchr(source, delimiter); - - if (strEnd != NULL) - { - len = strEnd - source; - memset(destination, 0, destSize); - memcpy(destination, source, len); - } - - return strEnd; -} - -bool SARA_R5::parseGPRMCString(char *rmcString, PositionData *pos, - ClockData *clk, SpeedData *spd) -{ - char *ptr, *search; - unsigned long tTemp; - char tempData[TEMP_NMEA_DATA_SIZE]; - - // if (_printDebug == true) - // { - // _debugPort->println(F("parseGPRMCString: rmcString: ")); - // _debugPort->println(rmcString); - // } - - // Fast-forward test to first value: - ptr = strchr(rmcString, ','); - ptr++; // Move ptr past first comma - - // If the next character is another comma, there's no time data - // Find time: - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - // Next comma should be present and not the next position - if ((search != NULL) && (search != ptr)) - { - pos->utc = atof(tempData); // Extract hhmmss.ss as float - tTemp = pos->utc; // Convert to unsigned long (discard the digits beyond the decimal point) - clk->time.ms = ((unsigned int)(pos->utc * 100)) % 100; // Extract the milliseconds - clk->time.hour = tTemp / 10000; - tTemp -= ((unsigned long)clk->time.hour * 10000); - clk->time.minute = tTemp / 100; - tTemp -= ((unsigned long)clk->time.minute * 100); - clk->time.second = tTemp; - } - else - { - pos->utc = 0.0; - clk->time.hour = 0; - clk->time.minute = 0; - clk->time.second = 0; - } - ptr = search + 1; // Move pointer to next value - - // Find status character: - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - // Should be a single character: V = Data invalid, A = Data valid - if ((search != NULL) && (search == ptr + 1)) - { - pos->status = *ptr; // Assign char at ptr to status - } - else - { - pos->status = 'X'; // Made up very bad status - } - ptr = search + 1; - - // Find latitude: - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - pos->lat = atof(tempData); // Extract ddmm.mmmmm as float - unsigned long lat_deg = pos->lat / 100; // Extract the degrees - pos->lat -= (float)lat_deg * 100.0; // Subtract the degrees leaving only the minutes - pos->lat /= 60.0; // Convert minutes into degrees - pos->lat += (float)lat_deg; // Finally add the degrees back on again - } - else - { - pos->lat = 0.0; - } - ptr = search + 1; - - // Find latitude hemishpere - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search == ptr + 1)) - { - if (*ptr == 'S') // Is the latitude South - pos->lat *= -1.0; // Make lat negative - } - ptr = search + 1; - - // Find longitude: - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - pos->lon = atof(tempData); // Extract dddmm.mmmmm as float - unsigned long lon_deg = pos->lon / 100; // Extract the degrees - pos->lon -= (float)lon_deg * 100.0; // Subtract the degrees leaving only the minutes - pos->lon /= 60.0; // Convert minutes into degrees - pos->lon += (float)lon_deg; // Finally add the degrees back on again - } - else - { - pos->lon = 0.0; - } - ptr = search + 1; - - // Find longitude hemishpere - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search == ptr + 1)) - { - if (*ptr == 'W') // Is the longitude West - pos->lon *= -1.0; // Make lon negative - } - ptr = search + 1; - - // Find speed - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - spd->speed = atof(tempData); // Extract speed over ground in knots - spd->speed *= 0.514444; // Convert to m/s - } - else - { - spd->speed = 0.0; - } - ptr = search + 1; - - // Find course over ground - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - spd->cog = atof(tempData); - } - else - { - spd->cog = 0.0; - } - ptr = search + 1; - - // Find date - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - tTemp = atol(tempData); - clk->date.day = tTemp / 10000; - tTemp -= ((unsigned long)clk->date.day * 10000); - clk->date.month = tTemp / 100; - tTemp -= ((unsigned long)clk->date.month * 100); - clk->date.year = tTemp; - } - else - { - clk->date.day = 0; - clk->date.month = 0; - clk->date.year = 0; - } - ptr = search + 1; - - // Find magnetic variation in degrees: - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search != ptr)) - { - spd->magVar = atof(tempData); - } - else - { - spd->magVar = 0.0; - } - ptr = search + 1; - - // Find magnetic variation direction - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, ','); - if ((search != NULL) && (search == ptr + 1)) - { - if (*ptr == 'W') // Is the magnetic variation West - spd->magVar *= -1.0; // Make magnetic variation negative - } - ptr = search + 1; - - // Find position system mode - // Possible values for posMode: N = No fix, E = Estimated/Dead reckoning fix, A = Autonomous GNSS fix, - // D = Differential GNSS fix, F = RTK float, R = RTK fixed - search = readDataUntil(tempData, TEMP_NMEA_DATA_SIZE, ptr, '*'); - if ((search != NULL) && (search = ptr + 1)) - { - pos->mode = *ptr; - } - else - { - pos->mode = 'X'; - } - ptr = search + 1; - - if (pos->status == 'A') - { - return true; - } - return false; -} From b7f959a1f1ec07a5b0f083b1dbad8b5b4477982d Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Sun, 8 May 2022 18:54:09 +0200 Subject: [PATCH 03/23] Delete SparkFun_u-blox_SARA-R5_Arduino_Library.h --- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 1048 ----------------- 1 file changed, 1048 deletions(-) delete mode 100644 src/SparkFun_u-blox_SARA-R5_Arduino_Library.h diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h deleted file mode 100644 index 482a025..0000000 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ /dev/null @@ -1,1048 +0,0 @@ -/* - Arduino library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud, as used on the SparkFun MicroMod Asset Tracker - By: Paul Clark - October 19th 2020 - - Based extensively on the: - Arduino Library for the SparkFun LTE CAT M1/NB-IoT Shield - SARA-R4 - Written by Jim Lindblom @ SparkFun Electronics, September 5, 2018 - - This Arduino library provides mechanisms to initialize and use - the SARA-R5 module over either a SoftwareSerial or hardware serial port. - - Please see LICENSE.md for the license information - -*/ - -#ifndef SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H -#define SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H - -#if (ARDUINO >= 100) -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#ifdef ARDUINO_ARCH_AVR // Arduino AVR boards (Uno, Pro Micro, etc.) -#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial -#endif - -#ifdef ARDUINO_ARCH_SAMD // Arduino SAMD boards (SAMD21, etc.) -#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial -#endif - -#ifdef ARDUINO_ARCH_APOLLO3 // Arduino Apollo boards (Artemis module, RedBoard Artemis, etc) -#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial (no longer supported with v2 of Apollo3) -// Note: paulvha has provided software serial support for v2 of the Apollo3 / Artemis core. -// Further details are available at: -// https://github.com/paulvha/apollo3/tree/master/SoftwareSerial -#endif - -#ifdef ARDUINO_ARCH_STM32 // STM32 based boards (Disco, Nucleo, etc) -#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial -#endif - -#ifdef ARDUINO_ARCH_ESP32 // ESP32 based boards -// Check to see if ESP Software Serial has been included -// Note: you need to #include at the very start of your script, -// _before_ the #include , for this to work. -// See SARA-R5_Example2_Identification_ESPSoftwareSerial for more details. -#if __has_include( ) -#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial -#else -#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial -#endif -#endif - -#ifdef ARDUINO_ARCH_ESP8266 // ESP8266 based boards -// Check to see if ESP Software Serial has been included -// Note: you need to #include at the very start of your script, -// _before_ the #include , for this to work. -// See SARA-R5_Example2_Identification_ESPSoftwareSerial for more details. -#if __has_include( ) -#define SARA_R5_SOFTWARE_SERIAL_ENABLED // Enable software serial -#else -#define SARA_R5_SOFTWARE_SERIAL_ENABLEDx // Disable software serial -#endif -#endif - -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED -#include // SoftwareSerial.h is guarded. It is OK to include it twice. -#endif - -#include - -#define SARA_R5_POWER_PIN -1 // Default to no pin -#define SARA_R5_RESET_PIN -1 - -// Timing -#define SARA_R5_STANDARD_RESPONSE_TIMEOUT 1000 -#define SARA_R5_10_SEC_TIMEOUT 10000 -#define SARA_R5_55_SECS_TIMEOUT 55000 -#define SARA_R5_2_MIN_TIMEOUT 120000 -#define SARA_R5_3_MIN_TIMEOUT 180000 -#define SARA_R5_SET_BAUD_TIMEOUT 500 -#define SARA_R5_POWER_OFF_PULSE_PERIOD 3200 // Hold PWR_ON low for this long to power the module off -#define SARA_R5_POWER_ON_PULSE_PERIOD 100 // Hold PWR_ON low for this long to power the module on (SARA-R510M8S) -#define SARA_R5_RESET_PULSE_PERIOD 23000 // Used to perform an abrupt emergency hardware shutdown. 23 seconds... (Yes, really!) -#define SARA_R5_POWER_OFF_TIMEOUT 40000 // Datasheet says 40 seconds... -#define SARA_R5_IP_CONNECT_TIMEOUT 130000 -#define SARA_R5_POLL_DELAY 1 -#define SARA_R5_SOCKET_WRITE_TIMEOUT 10000 - -// ## Suported AT Commands -// ### General -const char SARA_R5_COMMAND_AT[] = "AT"; // AT "Test" -const char SARA_R5_COMMAND_ECHO[] = "E"; // Local Echo -const char SARA_R5_COMMAND_MANU_ID[] = "+CGMI"; // Manufacturer identification -const char SARA_R5_COMMAND_MODEL_ID[] = "+CGMM"; // Model identification -const char SARA_R5_COMMAND_FW_VER_ID[] = "+CGMR"; // Firmware version identification -const char SARA_R5_COMMAND_SERIAL_NO[] = "+CGSN"; // Product serial number -const char SARA_R5_COMMAND_IMEI[] = "+CSN"; // IMEI identification -const char SARA_R5_COMMAND_IMSI[] = "+CIMI"; // IMSI identification -const char SARA_R5_COMMAND_CCID[] = "+CCID"; // SIM CCID -const char SARA_R5_COMMAND_REQ_CAP[] = "+GCAP"; // Request capabilities list -// ### Control and status -const char SARA_R5_COMMAND_POWER_OFF[] = "+CPWROFF"; // Module switch off -const char SARA_R5_COMMAND_FUNC[] = "+CFUN"; // Functionality (reset, etc.) -const char SARA_R5_COMMAND_CLOCK[] = "+CCLK"; // Real-time clock -const char SARA_R5_COMMAND_AUTO_TZ[] = "+CTZU"; // Automatic time zone update -const char SARA_R5_COMMAND_TZ_REPORT[] = "+CTZR"; // Time zone reporting -// ### Network service -const char SARA_R5_COMMAND_CNUM[] = "+CNUM"; // Subscriber number -const char SARA_R5_SIGNAL_QUALITY[] = "+CSQ"; -const char SARA_R5_OPERATOR_SELECTION[] = "+COPS"; -const char SARA_R5_REGISTRATION_STATUS[] = "+CREG"; -const char SARA_R5_EPSREGISTRATION_STATUS[] = "+CEREG"; -const char SARA_R5_READ_OPERATOR_NAMES[] = "+COPN"; -const char SARA_R5_COMMAND_MNO[] = "+UMNOPROF"; // MNO (mobile network operator) Profile -// ### SIM -const char SARA_R5_SIM_STATE[] = "+USIMSTAT"; -const char SARA_R5_COMMAND_SIMPIN[] = "+CPIN"; // SIM PIN -// ### SMS -const char SARA_R5_MESSAGE_FORMAT[] = "+CMGF"; // Set SMS message format -const char SARA_R5_SEND_TEXT[] = "+CMGS"; // Send SMS message -const char SARA_R5_NEW_MESSAGE_IND[] = "+CNMI"; // New [SMS] message indication -const char SARA_R5_PREF_MESSAGE_STORE[] = "+CPMS"; // Preferred message storage -const char SARA_R5_READ_TEXT_MESSAGE[] = "+CMGR"; // Read message -const char SARA_R5_DELETE_MESSAGE[] = "+CMGD"; // Delete message -// V24 control and V25ter (UART interface) -const char SARA_R5_FLOW_CONTROL[] = "&K"; // Flow control -const char SARA_R5_COMMAND_BAUD[] = "+IPR"; // Baud rate -// ### Packet switched data services -const char SARA_R5_MESSAGE_PDP_DEF[] = "+CGDCONT"; // Packet switched Data Profile context definition -const char SARA_R5_MESSAGE_PDP_CONFIG[] = "+UPSD"; // Packet switched Data Profile configuration -const char SARA_R5_MESSAGE_PDP_ACTION[] = "+UPSDA"; // Perform the action for the specified PSD profile -const char SARA_R5_MESSAGE_PDP_CONTEXT_ACTIVATE[] = "+CGACT"; // Activates or deactivates the specified PDP context -const char SARA_R5_MESSAGE_ENTER_PPP[] = "D"; -const char SARA_R5_NETWORK_ASSIGNED_DATA[] = "+UPSND"; // Packet switched network-assigned data -// ### GPIO -const char SARA_R5_COMMAND_GPIO[] = "+UGPIOC"; // GPIO Configuration -// ### IP -const char SARA_R5_CREATE_SOCKET[] = "+USOCR"; // Create a new socket -const char SARA_R5_CLOSE_SOCKET[] = "+USOCL"; // Close a socket -const char SARA_R5_CONNECT_SOCKET[] = "+USOCO"; // Connect to server on socket -const char SARA_R5_WRITE_SOCKET[] = "+USOWR"; // Write data to a socket -const char SARA_R5_WRITE_UDP_SOCKET[] = "+USOST"; // Write data to a UDP socket -const char SARA_R5_READ_SOCKET[] = "+USORD"; // Read from a socket -const char SARA_R5_READ_UDP_SOCKET[] = "+USORF"; // Read UDP data from a socket -const char SARA_R5_LISTEN_SOCKET[] = "+USOLI"; // Listen for connection on socket -const char SARA_R5_GET_ERROR[] = "+USOER"; // Get last socket error. -const char SARA_R5_SOCKET_DIRECT_LINK[] = "+USODL"; // Set socket in Direct Link mode -const char SARA_R5_SOCKET_CONTROL[] = "+USOCTL"; // Query the socket parameters -const char SARA_R5_UD_CONFIGURATION[] = "+UDCONF"; // User Datagram Configuration -// ### Ping -const char SARA_R5_PING_COMMAND[] = "+UPING"; // Ping -// ### HTTP -const char SARA_R5_HTTP_PROFILE[] = "+UHTTP"; // Configure the HTTP profile. Up to 4 different profiles can be defined -const char SARA_R5_HTTP_COMMAND[] = "+UHTTPC"; // Trigger the specified HTTP command -const char SARA_R5_HTTP_PROTOCOL_ERROR[] = "+UHTTPER"; // Retrieves the error class and code of the latest HTTP operation on the specified HTTP profile. - -const char SARA_R5_MQTT_NVM[] = "+UMQTTNV"; -const char SARA_R5_MQTT_PROFILE[] = "+UMQTT"; -const char SARA_R5_MQTT_COMMAND[] = "+UMQTTC"; -const char SARA_R5_MQTT_PROTOCOL_ERROR[] = "+UMQTTER"; - -// ### GNSS -const char SARA_R5_GNSS_POWER[] = "+UGPS"; // GNSS power management configuration -const char SARA_R5_GNSS_ASSISTED_IND[] = "+UGIND"; // Assisted GNSS unsolicited indication -const char SARA_R5_GNSS_REQUEST_LOCATION[] = "+ULOC"; // Ask for localization information -const char SARA_R5_GNSS_GPRMC[] = "+UGRMC"; // Ask for localization information -const char SARA_R5_GNSS_REQUEST_TIME[] = "+UTIME"; // Ask for time information from cellular modem (CellTime) -const char SARA_R5_GNSS_TIME_INDICATION[] = "+UTIMEIND"; // Time information request status unsolicited indication -const char SARA_R5_GNSS_TIME_CONFIGURATION[] = "+UTIMECFG"; // Sets time configuration -const char SARA_R5_GNSS_CONFIGURE_SENSOR[] = "+ULOCGNSS"; // Configure GNSS sensor -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 -// 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_DOWNLOAD_FILE[] = "+UDWNFILE"; // Download a file into the module -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 -// ### File System -// TO DO: Add support for file tags. Default tag to USER -const char SARA_R5_SEC_PROFILE[] = "+USECPRF"; -const char SARA_R5_SEC_MANAGER[] = "+USECMNG"; - -// ### Response -const char SARA_R5_RESPONSE_OK[] = "\nOK\r\n"; -const char SARA_R5_RESPONSE_ERROR[] = "\nERROR\r\n"; -const char SARA_R5_RESPONSE_CONNECT[] = "\r\nCONNECT\r\n"; -#define SARA_R5_RESPONSE_OK_OR_ERROR NULL - -// CTRL+Z and ESC ASCII codes for SMS message sends -const char ASCII_CTRL_Z = 0x1A; -const char ASCII_ESC = 0x1B; - -// NMEA data size - used by parseGPRMCString -#define TEMP_NMEA_DATA_SIZE 16 - -#define NOT_AT_COMMAND false -#define AT_COMMAND true - -// The minimum memory allocation for responses from sendCommandWithResponse -// This needs to be large enough to hold the response you're expecting plus and URC's that may arrive during the timeout -#define minimumResponseAllocation 128 - -#define SARA_R5_NUM_SOCKETS 6 - -#define NUM_SUPPORTED_BAUD 9 -const unsigned long SARA_R5_SUPPORTED_BAUD[NUM_SUPPORTED_BAUD] = - { - 115200, - 9600, - 19200, - 38400, - 57600, - 230400, - 460800, - 921600, - 3000000}; -#define SARA_R5_DEFAULT_BAUD_RATE 115200 - -// Flow control definitions for AT&K -// Note: SW (XON/XOFF) flow control is not supported on the SARA_R5 -typedef enum -{ - SARA_R5_DISABLE_FLOW_CONTROL = 0, - SARA_R5_ENABLE_FLOW_CONTROL = 3 -} SARA_R5_flow_control_t; - -// The standard Europe profile should be used as the basis for all other MNOs in Europe outside of Vodafone -// and Deutsche Telekom. However, there may be changes that need to be applied to the module for proper -// operation with any given European MNO such as attach type, RAT preference, band selection, etc. Please -// consult with the preferred network provider. -typedef enum -{ - MNO_INVALID = -1, - MNO_SW_DEFAULT = 0, // Undefined / regulatory - MNO_SIM_ICCID = 1, - MNO_ATT = 2, // AT&T - MNO_VERIZON = 3, - MNO_TELSTRA = 4, - MNO_TMO = 5, // T-Mobile US - MNO_CT = 6, // China Telecom - MNO_SPRINT = 8, - MNO_VODAFONE = 19, - MNO_NTT_DOCOMO = 20, - MNO_TELUS = 21, - MNO_SOFTBANK = 28, - MNO_DT = 31, // Deutsche Telekom - MNO_US_CELLULAR = 32, - MNO_SKT = 39, - MNO_GLOBAL = 90, - MNO_STD_EUROPE = 100, - MNO_STD_EU_NOEPCO = 101 -} mobile_network_operator_t; - -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_ZERO_READ_LENGTH, // 7 - SARA_R5_ERROR_ERROR // 8 -} SARA_R5_error_t; -#define SARA_R5_SUCCESS SARA_R5_ERROR_SUCCESS - -typedef enum -{ - SARA_R5_REGISTRATION_INVALID = -1, - SARA_R5_REGISTRATION_NOT_REGISTERED = 0, - SARA_R5_REGISTRATION_HOME = 1, - SARA_R5_REGISTRATION_SEARCHING = 2, - SARA_R5_REGISTRATION_DENIED = 3, - SARA_R5_REGISTRATION_UNKNOWN = 4, - SARA_R5_REGISTRATION_ROAMING = 5, - SARA_R5_REGISTRATION_HOME_SMS_ONLY = 6, - SARA_R5_REGISTRATION_ROAMING_SMS_ONLY = 7, - SARA_R5_REGISTRATION_EMERGENCY_SERV_ONLY = 8, - SARA_R5_REGISTRATION_HOME_CSFB_NOT_PREFERRED = 9, - SARA_R5_REGISTRATION_ROAMING_CSFB_NOT_PREFERRED = 10 -} SARA_R5_registration_status_t; - -struct DateData -{ - uint8_t day; - uint8_t month; - unsigned int year; -}; - -struct TimeData -{ - uint8_t hour; - uint8_t minute; - uint8_t second; - unsigned int ms; - uint8_t tzh; - uint8_t tzm; -}; - -struct ClockData -{ - struct DateData date; - struct TimeData time; -}; - -struct PositionData -{ - float utc; - float lat; // Degrees: +/- 90 - float lon; // Degrees: +/- 180 - float alt; - char mode; - char status; -}; - -struct SpeedData -{ - float speed; // m/s - float cog; // Degrees - float magVar; // Degrees -}; - -struct operator_stats -{ - uint8_t stat; - String shortOp; - String longOp; - unsigned long numOp; - uint8_t act; -}; - -typedef enum -{ - SARA_R5_TCP = 6, - SARA_R5_UDP = 17 -} SARA_R5_socket_protocol_t; - -typedef enum -{ - SARA_R5_TCP_SOCKET_STATUS_INACTIVE, - SARA_R5_TCP_SOCKET_STATUS_LISTEN, - SARA_R5_TCP_SOCKET_STATUS_SYN_SENT, - SARA_R5_TCP_SOCKET_STATUS_SYN_RCVD, - SARA_R5_TCP_SOCKET_STATUS_ESTABLISHED, - SARA_R5_TCP_SOCKET_STATUS_FIN_WAIT_1, - SARA_R5_TCP_SOCKET_STATUS_FIN_WAIT_2, - SARA_R5_TCP_SOCKET_STATUS_CLOSE_WAIT, - SARA_R5_TCP_SOCKET_STATUS_CLOSING, - SARA_R5_TCP_SOCKET_STATUS_LAST_ACK, - SARA_R5_TCP_SOCKET_STATUS_TIME_WAIT -} SARA_R5_tcp_socket_status_t; - -typedef enum -{ - SARA_R5_MESSAGE_FORMAT_PDU = 0, - SARA_R5_MESSAGE_FORMAT_TEXT = 1 -} SARA_R5_message_format_t; - -typedef enum -{ - SARA_R5_UTIME_MODE_STOP = 0, - SARA_R5_UTIME_MODE_PPS, - SARA_R5_UTIME_MODE_ONE_SHOT, - SARA_R5_UTIME_MODE_EXT_INT -} SARA_R5_utime_mode_t; - -typedef enum -{ - SARA_R5_UTIME_SENSOR_NONE = 0, - SARA_R5_UTIME_SENSOR_GNSS_LTE = 1, - SARA_R5_UTIME_SENSOR_LTE -} SARA_R5_utime_sensor_t; - -typedef enum -{ - SARA_R5_UTIME_URC_CONFIGURATION_DISABLED = 0, - SARA_R5_UTIME_URC_CONFIGURATION_ENABLED -} SARA_R5_utime_urc_configuration_t; - -typedef enum -{ - SARA_R5_SIM_NOT_PRESENT = 0, - SARA_R5_SIM_PIN_NEEDED, - SARA_R5_SIM_PIN_BLOCKED, - SARA_R5_SIM_PUK_BLOCKED, - SARA_R5_SIM_NOT_OPERATIONAL, - SARA_R5_SIM_RESTRICTED, - SARA_R5_SIM_OPERATIONAL - //SARA_R5_SIM_PHONEBOOK_READY, // Not reported by SARA-R5 - //SARA_R5_SIM_USIM_PHONEBOOK_READY, // Not reported by SARA-R5 - //SARA_R5_SIM_TOOLKIT_REFRESH_SUCCESSFUL, // Not reported by SARA-R5 - //SARA_R5_SIM_TOOLKIT_REFRESH_UNSUCCESSFUL, // Not reported by SARA-R5 - //SARA_R5_SIM_PPP_CONNECTION_ACTIVE, // Not reported by SARA-R5 - //SARA_R5_SIM_VOICE_CALL_ACTIVE, // Not reported by SARA-R5 - //SARA_R5_SIM_CSD_CALL_ACTIVE // Not reported by SARA-R5 -} SARA_R5_sim_states_t; - -#define SARA_R5_NUM_PSD_PROFILES 6 // Number of supported PSD profiles -#define SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS 11 // Number of supported PDP context identifiers -#define SARA_R5_NUM_HTTP_PROFILES 4 // Number of supported HTTP profiles - -typedef enum -{ - SARA_R5_HTTP_OP_CODE_SERVER_IP = 0, - SARA_R5_HTTP_OP_CODE_SERVER_NAME, - SARA_R5_HTTP_OP_CODE_USERNAME, - SARA_R5_HTTP_OP_CODE_PASSWORD, - SARA_R5_HTTP_OP_CODE_AUTHENTICATION, - SARA_R5_HTTP_OP_CODE_SERVER_PORT, - SARA_R5_HTTP_OP_CODE_SECURE, - SARA_R5_HTTP_OP_CODE_REQUEST_TIMEOUT, - SARA_R5_HTTP_OP_CODE_ADD_CUSTOM_HEADERS = 9 -} SARA_R5_http_op_codes_t; - -typedef enum -{ - SARA_R5_HTTP_COMMAND_HEAD = 0, - SARA_R5_HTTP_COMMAND_GET, - SARA_R5_HTTP_COMMAND_DELETE, - SARA_R5_HTTP_COMMAND_PUT, - SARA_R5_HTTP_COMMAND_POST_FILE, - SARA_R5_HTTP_COMMAND_POST_DATA, - SARA_R5_HTTP_COMMAND_GET_FOTA = 100 -} SARA_R5_http_commands_t; - -typedef enum -{ - SARA_R5_HTTP_CONTENT_APPLICATION_X_WWW = 0, - SARA_R5_HTTP_CONTENT_TEXT_PLAIN, - SARA_R5_HTTP_CONTENT_APPLICATION_OCTET, - SARA_R5_HTTP_CONTENT_MULTIPART_FORM, - SARA_R5_HTTP_CONTENT_APPLICATION_JSON, - SARA_R5_HTTP_CONTENT_APPLICATION_XML, - SARA_R5_HTTP_CONTENT_USER_DEFINED -} SARA_R5_http_content_types_t; - -typedef enum -{ - SARA_R5_MQTT_NV_RESTORE = 0, - SARA_R5_MQTT_NV_SET, - SARA_R5_MQTT_NV_STORE, -} SARA_R5_mqtt_nv_parameter_t; - -typedef enum -{ - SARA_R5_MQTT_PROFILE_CLIENT_ID = 0, - SARA_R5_MQTT_PROFILE_SERVERNAME = 2, - SARA_R5_MQTT_PROFILE_IPADDRESS, - SARA_R5_MQTT_PROFILE_USERNAMEPWD, - SARA_R5_MQTT_PROFILE_QOS = 6, - SARA_R5_MQTT_PROFILE_RETAIN, - SARA_R5_MQTT_PROFILE_TOPIC, - SARA_R5_MQTT_PROFILE_MESSAGE, - SARA_R5_MQTT_PROFILE_INACTIVITYTIMEOUT, - SARA_R5_MQTT_PROFILE_SECURE, -} SARA_R5_mqtt_profile_opcode_t; - -typedef enum -{ - SARA_R5_MQTT_COMMAND_LOGOUT = 0, - SARA_R5_MQTT_COMMAND_LOGIN, - SARA_R5_MQTT_COMMAND_PUBLISH, - SARA_R5_MQTT_COMMAND_PUBLISHFILE, - SARA_R5_MQTT_COMMAND_SUBSCRIBE, - SARA_R5_MQTT_COMMAND_UNSUBSCRIBE, - SARA_R5_MQTT_COMMAND_READ, - SARA_R5_MQTT_COMMAND_PING, - SARA_R5_MQTT_COMMAND_PUBLISHBINARY, -} SARA_R5_mqtt_command_opcode_t; - -typedef enum -{ - SARA_R5_PSD_CONFIG_PARAM_PROTOCOL = 0, - SARA_R5_PSD_CONFIG_PARAM_APN, - //SARA_R5_PSD_CONFIG_PARAM_USERNAME, // Not allowed on SARA-R5 - //SARA_R5_PSD_CONFIG_PARAM_PASSWORD, // Not allowed on SARA-R5 - SARA_R5_PSD_CONFIG_PARAM_DNS1 = 4, - SARA_R5_PSD_CONFIG_PARAM_DNS2, - //SARA_R5_PSD_CONFIG_PARAM_AUTHENTICATION, // Not allowed on SARA-R5 - //SARA_R5_PSD_CONFIG_PARAM_IP_ADDRESS, // Not allowed on SARA-R5 - //SARA_R5_PSD_CONFIG_PARAM_DATA_COMPRESSION, // Not allowed on SARA-R5 - //SARA_R5_PSD_CONFIG_PARAM_HEADER_COMPRESSION, // Not allowed on SARA-R5 - SARA_R5_PSD_CONFIG_PARAM_MAP_TO_CID = 100 -} SARA_R5_pdp_configuration_parameter_t; - -typedef enum -{ - SARA_R5_PSD_PROTOCOL_IPV4 = 0, - SARA_R5_PSD_PROTOCOL_IPV6, - SARA_R5_PSD_PROTOCOL_IPV4V6_V4_PREF, - SARA_R5_PSD_PROTOCOL_IPV4V6_V6_PREF -} SARA_R5_pdp_protocol_type_t; - -typedef enum -{ - SARA_R5_PSD_ACTION_RESET = 0, - SARA_R5_PSD_ACTION_STORE, - SARA_R5_PSD_ACTION_LOAD, - SARA_R5_PSD_ACTION_ACTIVATE, - SARA_R5_PSD_ACTION_DEACTIVATE -} SARA_R5_pdp_actions_t; - -typedef enum -{ - SARA_R5_SEC_PROFILE_PARAM_CERT_VAL_LEVEL = 0, - SARA_R5_SEC_PROFILE_PARAM_TLS_VER, - SARA_R5_SEC_PROFILE_PARAM_CYPHER_SUITE, - SARA_R5_SEC_PROFILE_PARAM_ROOT_CA, - SARA_R5_SEC_PROFILE_PARAM_HOSTNAME, - SARA_R5_SEC_PROFILE_PARAM_CLIENT_CERT, - SARA_R5_SEC_PROFILE_PARAM_CLIENT_KEY, - SARA_R5_SEC_PROFILE_PARAM_CLIENT_KEY_PWD, - SARA_R5_SEC_PROFILE_PARAM_PSK, - SARA_R5_SEC_PROFILE_PARAM_PSK_IDENT, - SARA_R5_SEC_PROFILE_PARAM_SNI, -} SARA_R5_sec_profile_parameter_t; - -typedef enum -{ - SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_NO = 0, - SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_YESNOURL, - SARA_R5_SEC_PROFILE_CERVTAL_OPCODE_YESURL, - SARA_R5_SEC_PROFILE_CERTVAL_OPCODE_YESURLDATE, -} SARA_R5_sec_profile_certval_op_code_t; - -typedef enum -{ - SARA_R5_SEC_PROFILE_TLS_OPCODE_ANYVER = 0, - SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_0, - SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_1, - SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_2, - SARA_R5_SEC_PROFILE_TLS_OPCODE_VER1_3, -} SARA_R5_sec_profile_tls_op_code_t; - -typedef enum -{ - SARA_R5_SEC_PROFILE_SUITE_OPCODE_PROPOSEDDEFAULT = 0, -} SARA_R5_sec_profile_suite_op_code_t; - -typedef enum -{ - SARA_R5_SEC_MANAGER_OPCODE_IMPORT = 0, -} SARA_R5_sec_manager_opcode_t; - -typedef enum -{ - SARA_R5_SEC_MANAGER_ROOTCA = 0, - SARA_R5_SEC_MANAGER_CLIENT_CERT, - SARA_R5_SEC_MANAGER_CLIENT_KEY, - SARA_R5_SEC_MANAGER_SERVER_CERT -} SARA_R5_sec_manager_parameter_t; - -typedef enum -{ - MINIMUM_FUNCTIONALITY = 0, // (disable both transmit and receive RF circuits by deactivating both CS and PS services) - FULL_FUNCTIONALITY = 1, - AIRPLANE_MODE = 4, - SIM_TOOLKIT_ENABLE_DEDICATED = 6, - SIM_TOOLKIT_DISABLE_DEDICATED = 7, - SIM_TOOLKIT_ENABLE_RAW = 9, - FAST_SAFE_POWER_OFF = 10, - //SILENT_RESET_WITHOUT_SIM = 15, // Not supported on SARA-R5 - SILENT_RESET_WITH_SIM = 16 - //MINIMUM_FUNCTIONALITY = 19, // Not supported on SARA-R5 - //DEEP_LOW_POWER_STATE = 127 // Not supported on SARA-R5 -} SARA_R5_functionality_t; - -class SARA_R5 : public Print -{ -public: - // Constructor - // The library will use the powerPin and resetPin (if provided) to power the module off/on and perform an emergency reset - // maxInitTries sets the maximum number of initialization attempts. .init is called by .begin. - SARA_R5(int powerPin = SARA_R5_POWER_PIN, int resetPin = SARA_R5_RESET_PIN, uint8_t maxInitTries = 9); - - ~SARA_R5(); - // Begin -- initialize module and ensure it's connected -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - bool begin(SoftwareSerial &softSerial, unsigned long baud = 9600); -#endif - bool begin(HardwareSerial &hardSerial, unsigned long baud = 9600, bool doBegin = true); - - // Debug prints - void enableDebugging(Print &debugPort = Serial); //Turn on debug printing. If user doesn't specify then Serial will be used. - void enableAtDebugging(Print &debugPort = Serial); //Turn on AT debug printing. If user doesn't specify then Serial will be used. - - // Invert the polarity of the power pin - if required - // Normally the SARA's power pin is pulled low and released to toggle the power - // But the Asset Tracker needs this to be pulled high and released instead - void invertPowerPin(bool invert = false); - - SARA_R5_error_t modulePowerOff(void); // Graceful disconnect and shutdown using +CPWROFF. - void modulePowerOn(void); // Requires access to the PWR_ON pin - - // Loop polling and polling setup - process URC's etc. from the module - - // This function was originally written by Matthew Menze for the LTE Shield (SARA-R4) library - // See: https://github.com/sparkfun/SparkFun_LTE_Shield_Arduino_Library/pull/8 - // It does the same job as ::poll but also processes any 'old' data stored in the backlog first - // It also has a built-in timeout - which ::poll does not - // Use this - it is way better than ::poll. Thank you Matthew! - bool bufferedPoll(void); - - // This is the original poll function. - // It is 'blocking' - it does not return when serial data is available until it receives a `\n`. - // ::bufferedPoll is the new improved version. It processes any data in the backlog and includes a timeout. - // Retained for backward-compatibility and just in case you do want to (temporarily) ignore any data in the backlog - bool poll(void); - - // Callbacks (called during polling) - void setSocketListenCallback(void (*socketListenCallback)(int, IPAddress, unsigned int, int, IPAddress, unsigned int)); // listen Socket, local IP Address, listen Port, socket, remote IP Address, port - // This is the original read socket callback - called when a +UUSORD or +UUSORF URC is received - // It works - and handles binary data correctly - but the remote IP Address and Port are lost for UDP connections - // setSocketReadCallbackPlus is preferred! - void setSocketReadCallback(void (*socketReadCallback)(int, String)); // socket, read data - void setSocketReadCallbackPlus(void (*socketReadCallbackPlus)(int, const char *, int, IPAddress, int)); // socket, read data, length, remoteAddress, remotePort - void setSocketCloseCallback(void (*socketCloseCallback)(int)); // socket - void setGpsReadCallback(void (*gpsRequestCallback)(ClockData time, - PositionData gps, SpeedData spd, unsigned long uncertainty)); - void setSIMstateReportCallback(void (*simStateRequestCallback)(SARA_R5_sim_states_t state)); - void setPSDActionCallback(void (*psdActionRequestCallback)(int result, IPAddress ip)); - void setPingCallback(void (*pingRequestCallback)(int retry, int p_size, String remote_hostname, IPAddress ip, int ttl, long rtt)); - void setHTTPCommandCallback(void (*httpCommandRequestCallback)(int profile, int command, int result)); - void setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)); - SARA_R5_error_t setRegistrationCallback(void (*registrationCallback)(SARA_R5_registration_status_t status, - unsigned int lac, unsigned int ci, int Act)); - SARA_R5_error_t setEpsRegistrationCallback(void (*epsRegistrationCallback)(SARA_R5_registration_status_t status, - unsigned int tac, unsigned int ci, int Act)); - - // Direct write/print to cell serial port - virtual size_t write(uint8_t c); - virtual size_t write(const char *str); - virtual size_t write(const char *buffer, size_t size); - - // General AT Commands - SARA_R5_error_t at(void); - SARA_R5_error_t enableEcho(bool enable = true); - String getManufacturerID(void); - String getModelID(void); - String getFirmwareVersion(void); - String getSerialNo(void); - String getIMEI(void); - String getIMSI(void); - String getCCID(void); - String getSubscriberNo(void); - String getCapabilities(void); - - // Control and status AT commands - SARA_R5_error_t reset(void); - String clock(void); - // 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); - SARA_R5_error_t setUtimeIndication(SARA_R5_utime_urc_configuration_t config = SARA_R5_UTIME_URC_CONFIGURATION_ENABLED); // +UTIMEIND - SARA_R5_error_t getUtimeIndication(SARA_R5_utime_urc_configuration_t *config); - SARA_R5_error_t setUtimeConfiguration(int32_t offsetNanoseconds = 0, int32_t offsetSeconds = 0); // +UTIMECFG - SARA_R5_error_t getUtimeConfiguration(int32_t *offsetNanoseconds, int32_t *offsetSeconds); - - // Network service AT commands - int8_t rssi(void); // Receive signal strength - SARA_R5_registration_status_t registration(bool eps = true); - bool setNetworkProfile(mobile_network_operator_t mno, bool autoReset = false, bool urcNotification = false); - mobile_network_operator_t getNetworkProfile(void); - typedef enum - { - PDP_TYPE_INVALID = -1, - PDP_TYPE_IP = 0, - PDP_TYPE_NONIP = 1, - PDP_TYPE_IPV4V6 = 2, - PDP_TYPE_IPV6 = 3 - } SARA_R5_pdp_type; - SARA_R5_error_t setAPN(String apn, uint8_t cid = 1, SARA_R5_pdp_type pdpType = PDP_TYPE_IP); // Set the Access Point Name - SARA_R5_error_t getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp_type* pdpType = NULL); // Return the apn and IP address for the chosen context identifier - - SARA_R5_error_t getSimStatus(String* code); - SARA_R5_error_t setSimPin(String pin); - - // SIM - // Status report Mode: - // Bit States reported - // 0 Reports the (U)SIM initialization status ('s from 0 to 6 may be reported) - // 1 Reports the (U)SIM phonebook initialization status ('s from 7 to 8 may be reported) - // 2 Reports the (U)SIM toolkit REFRESH proactive command execution result ('s from 9 to 13 may be reported) - // Note: For the SARA-R5: =7, 8, 9, 10, 11, 12 and 13 are not reported. - SARA_R5_error_t setSIMstateReportingMode(int mode); - SARA_R5_error_t getSIMstateReportingMode(int *mode); - - typedef enum - { - L2P_DEFAULT, - L2P_PPP, - L2P_M_HEX, - L2P_M_RAW_IP, - L2P_M_OPT_PPP - } SARA_R5_l2p_t; - SARA_R5_error_t enterPPP(uint8_t cid = 1, char dialing_type_char = 0, - unsigned long dialNumber = 99, SARA_R5_l2p_t l2p = L2P_DEFAULT); - - uint8_t getOperators(struct operator_stats *op, int maxOps = 3); - SARA_R5_error_t registerOperator(struct operator_stats oper); - SARA_R5_error_t automaticOperatorSelection(); - SARA_R5_error_t getOperator(String *oper); - SARA_R5_error_t deregisterOperator(void); - - // SMS -- Short Messages Service - SARA_R5_error_t setSMSMessageFormat(SARA_R5_message_format_t textMode = SARA_R5_MESSAGE_FORMAT_TEXT); - SARA_R5_error_t sendSMS(String number, String message); - SARA_R5_error_t getPreferredMessageStorage(int *used, int *total, String memory = "ME"); - SARA_R5_error_t readSMSmessage(int location, String *unread, String *from, String *dateTime, String *message); - SARA_R5_error_t deleteSMSmessage(int location, int deleteFlag = 0); // Default to deleting the single message at the specified location - SARA_R5_error_t deleteReadSMSmessages(void) { return (deleteSMSmessage( 1, 1 )); }; // Delete all the read messages from preferred storage - SARA_R5_error_t deleteReadSentSMSmessages(void) { return (deleteSMSmessage( 1, 2 )); }; // Delete the read and sent messages from preferred storage - SARA_R5_error_t deleteReadSentUnsentSMSmessages(void) { return (deleteSMSmessage( 1, 3 )); }; // Delete the read, sent and unsent messages from preferred storage - SARA_R5_error_t deleteAllSMSmessages(void) { return (deleteSMSmessage( 1, 4 )); }; // Delete the read, sent, unsent and unread messages from preferred storage - - // V24 Control and V25ter (UART interface) AT commands - SARA_R5_error_t setBaud(unsigned long baud); - SARA_R5_error_t setFlowControl(SARA_R5_flow_control_t value = SARA_R5_ENABLE_FLOW_CONTROL); - - // GPIO - // GPIO pin map - typedef enum - { - GPIO1 = 16, - GPIO2 = 23, - GPIO3 = 24, - GPIO4 = 25, - GPIO5 = 42, - GPIO6 = 19 - } SARA_R5_gpio_t; - // GPIO pin modes - typedef enum - { - GPIO_MODE_INVALID = -1, - GPIO_OUTPUT = 0, - GPIO_INPUT, - NETWORK_STATUS, - GNSS_SUPPLY_ENABLE, - GNSS_DATA_READY, - GNSS_RTC_SHARING, - JAMMING_DETECTION, - SIM_CARD_DETECTION, - HEADSET_DETECTION, - GSM_TX_BURST_INDICATION, - MODULE_STATUS_INDICATION, - MODULE_OPERATING_MODE_INDICATION, - I2S_DIGITAL_AUDIO_INTERFACE, - SPI_SERIAL_INTERFACE, - MASTER_CLOCK_GENRATION, - UART_INTERFACE, - WIFI_ENABLE, - RING_INDICATION = 18, - LAST_GASP_ENABLE, - EXTERNAL_GNSS_ANTENNA, - TIME_PULSE_GNSS, - TIME_PULSE_OUTPUT, - TIMESTAMP, - FAST_POWER_OFF, - LWM2M_PULSE, - HARDWARE_FLOW_CONTROL, - ANTENNA_TUNING, - EXT_GNSS_TIME_PULSE, - EXT_GNSS_TIMESTAMP, - DTR_MODE, - KHZ_32768_OUT = 32, - PAD_DISABLED = 255 - } SARA_R5_gpio_mode_t; - SARA_R5_error_t setGpioMode(SARA_R5_gpio_t gpio, SARA_R5_gpio_mode_t mode, int value = 0); - 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. - 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. 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 - SARA_R5_error_t socketWrite(int socket, const char *str, int len = -1); - SARA_R5_error_t socketWrite(int socket, String str); // OK for binary data - // Write UDP data to the specified IP Address and port. - // Works with binary data - but you must specify the data length when using the const char * versions - // If you let len default to -1, strlen is used to calculate the data length - and will be incorrect for binary data - SARA_R5_error_t socketWriteUDP(int socket, const char *address, int port, const char *str, int len = -1); - SARA_R5_error_t socketWriteUDP(int socket, IPAddress address, int port, const char *str, int len = -1); - SARA_R5_error_t socketWriteUDP(int socket, String address, int port, String str); - // 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 - // 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) - // 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 - SARA_R5_error_t socketListen(int socket, unsigned int port); - // Place the socket into direct link mode - making it easy to transfer binary data. Wait two seconds and then send +++ to exit the link. - SARA_R5_error_t socketDirectLinkMode(int socket); - // Configure when direct link data is sent - SARA_R5_error_t socketDirectLinkTimeTrigger(int socket, unsigned long timerTrigger); - SARA_R5_error_t socketDirectLinkDataLengthTrigger(int socket, int dataLengthTrigger); - SARA_R5_error_t socketDirectLinkCharacterTrigger(int socket, int characterTrigger); - SARA_R5_error_t socketDirectLinkCongestionTimer(int socket, unsigned long congestionTimer); - // Use +USOCTL (Socket control) to query the socket parameters - SARA_R5_error_t querySocketType(int socket, SARA_R5_socket_protocol_t *protocol); - SARA_R5_error_t querySocketLastError(int socket, int *error); - SARA_R5_error_t querySocketTotalBytesSent(int socket, uint32_t *total); - SARA_R5_error_t querySocketTotalBytesReceived(int socket, uint32_t *total); - SARA_R5_error_t querySocketRemoteIPAddress(int socket, IPAddress *address, int *port); - SARA_R5_error_t querySocketStatusTCP(int socket, SARA_R5_tcp_socket_status_t *status); - SARA_R5_error_t querySocketOutUnackData(int socket, uint32_t *total); - // Return the most recent socket error - int socketGetLastError(); - // Return the remote IP Address from the most recent socket listen indication (socket connection) - // Use the socket listen callback to get the full address and port information - IPAddress lastRemoteIP(void); - - // Ping - SARA_R5_error_t ping(String remote_host, int retry = 4, int p_size = 32, unsigned long timeout = 5000, int ttl = 32); - - // HTTP - SARA_R5_error_t resetHTTPprofile(int profile); // Reset the HTTP profile. Note: The configured HTTP profile parameters are not saved in the non volatile memory. - SARA_R5_error_t setHTTPserverIPaddress(int profile, IPAddress address); // Default: empty string - SARA_R5_error_t setHTTPserverName(int profile, String server); // Default: empty string - SARA_R5_error_t setHTTPusername(int profile, String username); // Default: empty string - SARA_R5_error_t setHTTPpassword(int profile, String password); // Default: empty string - SARA_R5_error_t setHTTPauthentication(int profile, bool authenticate); // Default: no authentication - SARA_R5_error_t setHTTPserverPort(int profile, int port); // Default: 80 - SARA_R5_error_t setHTTPcustomHeader(int profile, String header); // Default: format 0:Content-Type:application/json" - SARA_R5_error_t setHTTPsecure(int profile, bool secure, int secprofile = -1); // Default: disabled (HTTP on port 80). Set to true for HTTPS on port 443 - // TO DO: Add custom request headers - SARA_R5_error_t getHTTPprotocolError(int profile, int *error_class, int *error_code); // Read the most recent HTTP protocol error for this profile - SARA_R5_error_t sendHTTPGET(int profile, String path, String responseFilename); - SARA_R5_error_t sendHTTPPOSTdata(int profile, String path, String responseFilename, String data, SARA_R5_http_content_types_t httpContentType); - SARA_R5_error_t sendHTTPPOSTfile(int profile, String path, String responseFilename, String requestFile, SARA_R5_http_content_types_t httpContentType); - - SARA_R5_error_t nvMQTT(SARA_R5_mqtt_nv_parameter_t parameter); - SARA_R5_error_t setMQTTclientId(String clientId); - SARA_R5_error_t setMQTTserver(String serverName, int port); - SARA_R5_error_t setMQTTsecure(bool secure, int secprofile = -1); - SARA_R5_error_t connectMQTT(void); - SARA_R5_error_t disconnectMQTT(void); - SARA_R5_error_t subscribeMQTTtopic(int max_Qos, String topic); - SARA_R5_error_t unsubscribeMQTTtopic(String topic); - SARA_R5_error_t readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead); - SARA_R5_error_t getMQTTprotocolError(int *error_code, int *error_code2); - - // Configure security profiles - SARA_R5_error_t resetSecurityProfile(int secprofile); - SARA_R5_error_t configSecurityProfileString(int secprofile, SARA_R5_sec_profile_parameter_t parameter, String value); - SARA_R5_error_t configSecurityProfile(int secprofile, SARA_R5_sec_profile_parameter_t parameter, int value); - SARA_R5_error_t setSecurityManager(SARA_R5_sec_manager_opcode_t opcode, SARA_R5_sec_manager_parameter_t parameter, String name, String data); - - // Packet Switched Data - // Configure the PDP using +UPSD. See SARA_R5_pdp_configuration_parameter_t for the list of parameters: protocol, APN, username, DNS, etc. - SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, int value); // Set parameters in the chosen PSD profile - SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, SARA_R5_pdp_protocol_type_t value); // Set parameters in the chosen PSD profile - SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, String value); // Set parameters in the chosen PSD profile - SARA_R5_error_t setPDPconfiguration(int profile, SARA_R5_pdp_configuration_parameter_t parameter, IPAddress value); // Set parameters in the chosen PSD profile - SARA_R5_error_t performPDPaction(int profile, SARA_R5_pdp_actions_t action); // Performs the requested action for the specified PSD profile: reset, store, load, activate, deactivate - SARA_R5_error_t activatePDPcontext(bool status, int cid = -1); // Activates or deactivates the specified PDP context. Default to all (cid = -1) - SARA_R5_error_t getNetworkAssignedIPAddress(int profile, IPAddress *address); // Get the dynamic IP address assigned during PDP context activation - - // GPS - typedef enum - { - GNSS_SYSTEM_GPS = 1, - GNSS_SYSTEM_SBAS = 2, - GNSS_SYSTEM_GALILEO = 4, - GNSS_SYSTEM_BEIDOU = 8, - GNSS_SYSTEM_IMES = 16, - GNSS_SYSTEM_QZSS = 32, - GNSS_SYSTEM_GLONASS = 64 - } gnss_system_t; - typedef enum - { - GNSS_AIDING_MODE_NONE = 0, - GNSS_AIDING_MODE_AUTOMATIC = 1, - GNSS_AIDING_MODE_ASSISTNOW_OFFLINE = 2, - GNSS_AIDING_MODE_ASSISTNOW_ONLINE = 4, - GNSS_AIDING_MODE_ASSISTNOW_AUTONOMOUS = 8 - } gnss_aiding_mode_t; - bool isGPSon(void); - SARA_R5_error_t gpsPower(bool enable = true, - gnss_system_t gnss_sys = GNSS_SYSTEM_GPS, - gnss_aiding_mode_t gnss_aiding = GNSS_AIDING_MODE_AUTOMATIC); - //SARA_R5_error_t gpsEnableClock(bool enable = true); - //SARA_R5_error_t gpsGetClock(struct ClockData *clock); - //SARA_R5_error_t gpsEnableFix(bool enable = true); - //SARA_R5_error_t gpsGetFix(float *lat, float *lon, unsigned int *alt, uint8_t *quality, uint8_t *sat); - //SARA_R5_error_t gpsGetFix(struct PositionData *pos); - //SARA_R5_error_t gpsEnablePos(bool enable = true); - //SARA_R5_error_t gpsGetPos(struct PositionData *pos); - //SARA_R5_error_t gpsEnableSat(bool enable = true); - //SARA_R5_error_t gpsGetSat(uint8_t *sats); - SARA_R5_error_t gpsEnableRmc(bool enable = true); // Enable GPRMC messages - SARA_R5_error_t gpsGetRmc(struct PositionData *pos, struct SpeedData *speed, struct ClockData *clk, bool *valid); //Parse a GPRMC message - //SARA_R5_error_t gpsEnableSpeed(bool enable = true); - //SARA_R5_error_t gpsGetSpeed(struct SpeedData *speed); - - SARA_R5_error_t gpsRequest(unsigned int timeout, uint32_t accuracy, bool detailed = true, unsigned int sensor = 3); - - //CellLocate - SARA_R5_error_t gpsAidingServerConf(const char *primaryServer, const char *secondaryServer, const char *authToken, - unsigned int days = 14, unsigned int period = 4, unsigned int resolution = 1, - 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); // 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. - // Append data to a file, delete file first to not appends the data. - SARA_R5_error_t appendFileContents(String filename, String str); - SARA_R5_error_t appendFileContents(String filename, const char *str, int len); - 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); - - // Send a custom command with an expected (potentially partial) response, store entire response - SARA_R5_error_t sendCustomCommandWithResponse(const char *command, const char *expectedResponse, - char *responseDest, unsigned long commandTimeout = SARA_R5_STANDARD_RESPONSE_TIMEOUT, bool at = true); - -private: - HardwareSerial *_hardSerial; -#ifdef SARA_R5_SOFTWARE_SERIAL_ENABLED - SoftwareSerial *_softSerial; -#endif - - Print *_debugPort; //The stream to send debug messages to if enabled. Usually Serial. - bool _printDebug = false; //Flag to print debugging variables - Print *_debugAtPort; //The stream to send debug messages to if enabled. Usually Serial. - bool _printAtDebug = false; //Flag to print debugging variables - - int _powerPin; - int _resetPin; - bool _invertPowerPin = false; - - unsigned long _baud; - IPAddress _lastRemoteIP; - IPAddress _lastLocalIP; - uint8_t _maxInitTries; - 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. See notes in .cpp re. ESP32! - 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); - void (*_socketReadCallback)(int, String); - void (*_socketReadCallbackPlus)(int, const char *, int, IPAddress, int); // socket, data, length, remoteAddress, remotePort - void (*_socketCloseCallback)(int); - void (*_gpsRequestCallback)(ClockData, PositionData, SpeedData, unsigned long); - void (*_simStateReportCallback)(SARA_R5_sim_states_t); - void (*_psdActionRequestCallback)(int, IPAddress); - void (*_pingRequestCallback)(int, int, String, IPAddress, int, long); - void (*_httpCommandRequestCallback)(int, int, int); - void (*_mqttCommandRequestCallback)(int, int); - void (*_registrationCallback)(SARA_R5_registration_status_t status, unsigned int lac, unsigned int ci, int Act); - void (*_epsRegistrationCallback)(SARA_R5_registration_status_t status, unsigned int tac, unsigned int ci, int Act); - - - int _lastSocketProtocol[SARA_R5_NUM_SOCKETS]; // Record the protocol for each socket to avoid having to call querySocketType in parseSocketReadIndication - - typedef enum - { - SARA_R5_INIT_STANDARD, - SARA_R5_INIT_AUTOBAUD, - SARA_R5_INIT_RESET - } SARA_R5_init_type_t; - - SARA_R5_error_t init(unsigned long baud, SARA_R5_init_type_t initType = SARA_R5_INIT_STANDARD); - - void powerOn(void); // Brief pulse on PWR_ON to turn module back on - void powerOff(void); // Long pulse on PWR_ON to do a graceful shutdown. Note modulePowerOff (+CPWROFF) is preferred. - - void hwReset(void); - - SARA_R5_error_t setMNOprofile(mobile_network_operator_t mno, bool autoReset = false, bool urcNotification = false); - SARA_R5_error_t getMNOprofile(mobile_network_operator_t *mno); - - // Wait for an expected response (don't send a command) - SARA_R5_error_t waitForResponse(const char *expectedResponse, const char *expectedError, uint16_t timeout); - - // Send command with an expected (potentially partial) response, store entire response - SARA_R5_error_t sendCommandWithResponse(const char *command, const char *expectedResponse, - char *responseDest, unsigned long commandTimeout, int destSize = minimumResponseAllocation, bool at = true); - - // 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); - SARA_R5_error_t parseSocketCloseIndication(String *closeIndication); - - // UART Functions - size_t hwPrint(const char *s); - size_t hwWriteData(const char *buff, int len); - size_t hwWrite(const char c); - int readAvailable(char *inString); - char readChar(void); - int hwAvailable(void); - void beginSerial(unsigned long baud); - void setTimeout(unsigned long timeout); - bool find(char *target); - - SARA_R5_error_t autobaud(unsigned long desiredBaud); - - char *sara_r5_calloc_char(size_t num); - - bool processURCEvent(const char *event); - void pruneBacklog(void); - - // GPS Helper functions - char *readDataUntil(char *destination, unsigned int destSize, char *source, char delimiter); - bool parseGPRMCString(char *rmcString, PositionData *pos, ClockData *clk, SpeedData *spd); -}; - -#endif //SPARKFUN_SARA_R5_ARDUINO_LIBRARY_H From 1203e6e69509ed0e6f3eca55129551134e2250f4 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Wed, 29 Jun 2022 13:41:25 +0100 Subject: [PATCH 04/23] Update SparkFun_u-blox_GNSS_Arduino_Library.h --- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index a60ee2b..78687cf 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -861,7 +861,7 @@ class SFE_UBLOX_GNSS // Functions used for RTK and base station setup bool getSurveyMode(uint16_t maxWait = defaultMaxWait); // Get the current TimeMode3 settings - bool getSurveyMode(UBX_CFG_TMODE3_data_t *data = NULL, uint16_t maxWait = defaultMaxWait); // Get the current TimeMode3 settings + bool getSurveyMode(UBX_CFG_TMODE3_data_t *data = NULL, uint16_t maxWait = defaultMaxWait); // Get the current TimeMode3 settings bool setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode bool setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode bool enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Begin Survey-In for NEO-M8P / ZED-F9x From c1f95246241c9a84c6f3381666d9fa6245b394a9 Mon Sep 17 00:00:00 2001 From: rislec Date: Fri, 1 Jul 2022 08:38:21 -0400 Subject: [PATCH 05/23] Add missing initialization. --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index d74ff8b..2860948 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -9842,6 +9842,7 @@ bool SFE_UBLOX_GNSS::initPacketUBXNAVDOP() } packetUBXNAVDOP->automaticFlags.flags.all = 0; packetUBXNAVDOP->callbackPointer = NULL; + packetUBXNAVDOP->callbackPointerPtr = NULL; packetUBXNAVDOP->callbackData = NULL; packetUBXNAVDOP->moduleQueried.moduleQueried.all = 0; return (true); From d2e76e6affbe3d604f1cb8f6adba08f444a7218f Mon Sep 17 00:00:00 2001 From: rislec Date: Fri, 1 Jul 2022 08:53:50 -0400 Subject: [PATCH 06/23] Correct paranthesis. --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 2860948..88b18b4 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -17458,7 +17458,7 @@ bool SFE_UBLOX_GNSS::getSensorFusionMeasurement(UBX_ESF_MEAS_sensorData_t *senso if (packetUBXESFMEAS == NULL) // Bail if the RAM allocation failed return (false); - if (packetUBXESFMEAS->moduleQueried.moduleQueried.bits.data & ((1 << sensor) == 0)) + if ((packetUBXESFMEAS->moduleQueried.moduleQueried.bits.data & (1 << sensor)) == 0) getESFMEAS(maxWait); packetUBXESFMEAS->moduleQueried.moduleQueried.bits.data &= ~(1 << sensor); // Since we are about to give this to user, mark this data as stale packetUBXESFMEAS->moduleQueried.moduleQueried.bits.all = false; @@ -17479,7 +17479,7 @@ bool SFE_UBLOX_GNSS::getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorDat if (packetUBXESFRAW == NULL) // Bail if the RAM allocation failed return (false); - if (packetUBXESFRAW->moduleQueried.moduleQueried.bits.data & ((1 << sensor) == 0)) + if ((packetUBXESFRAW->moduleQueried.moduleQueried.bits.data & (1 << sensor)) == 0) getESFRAW(maxWait); packetUBXESFRAW->moduleQueried.moduleQueried.bits.data &= ~(1 << sensor); // Since we are about to give this to user, mark this data as stale packetUBXESFRAW->moduleQueried.moduleQueried.bits.all = false; @@ -17502,7 +17502,7 @@ bool SFE_UBLOX_GNSS::getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensor if (packetUBXESFSTATUS == NULL) // Bail if the RAM allocation failed return (false); - if (packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.status & ((1 << sensor) == 0)) + if ((packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.status & (1 << sensor)) == 0) getESFSTATUS(maxWait); packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.status &= ~(1 << sensor); // Since we are about to give this to user, mark this data as stale packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.all = false; From 2386888962fef2343297a85a718d4b18ba4e9d02 Mon Sep 17 00:00:00 2001 From: rislec Date: Fri, 1 Jul 2022 10:28:47 -0400 Subject: [PATCH 07/23] Add EOE and TIMEUTC messaging. --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 511 +++++++++++++++++++ src/SparkFun_u-blox_GNSS_Arduino_Library.h | 24 + src/u-blox_structs.h | 32 ++ 3 files changed, 567 insertions(+) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 88b18b4..0c71457 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -1295,6 +1295,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVDOP != NULL) result = true; break; + case UBX_NAV_EOE: + if (packetUBXNAVEOE != NULL) + result = true; + break; case UBX_NAV_ATT: if (packetUBXNAVATT != NULL) result = true; @@ -1307,6 +1311,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVODO != NULL) result = true; break; + case UBX_NAV_TIMEUTC: + if (packetUBXNAVTIMEUTC != NULL) + result = true; + break; case UBX_NAV_VELECEF: if (packetUBXNAVVELECEF != NULL) result = true; @@ -1487,6 +1495,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_DOP: maxSize = UBX_NAV_DOP_LEN; break; + case UBX_NAV_EOE: + maxSize = UBX_NAV_EOE_LEN; + break; case UBX_NAV_ATT: maxSize = UBX_NAV_ATT_LEN; break; @@ -1496,6 +1507,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_ODO: maxSize = UBX_NAV_ODO_LEN; break; + case UBX_NAV_TIMEUTC: + maxSize = UBX_NAV_TIMEUTC; + break; case UBX_NAV_VELECEF: maxSize = UBX_NAV_VELECEF_LEN; break; @@ -3231,6 +3245,31 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } + else if (msg->id == UBX_NAV_EOE && msg->len == UBX_NAV_EOE_LEN) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXNAVEOE != NULL) + { + packetUBXNAVEOE->data.iTOW = extractLong(msg, 0); + + // Mark all datums as fresh (not read before) + packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0xFFFFFFFF; + + // Check if we need to copy the data for the callback + if ((packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXNAVEOE->callbackData->iTOW, &packetUBXNAVEOE->data.iTOW, sizeof(UBX_NAV_EOE_data_t)); + packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer) + { + storePacket(msg); + } + } + } else if (msg->id == UBX_NAV_ATT && msg->len == UBX_NAV_ATT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -3349,6 +3388,40 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } + else if (msg->id == UBX_NAV_TIMEUTC && msg->len == UBX_NAV_TIMEUTC_LEN) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXNAVTIMEUTC != NULL) + { + packetUBXNAVTIMEUTC->data.iTOW = extractLong(msg, 0); + packetUBXNAVTIMEUTC->data.tAcc = extractLong(msg, 4); + packetUBXNAVTIMEUTC->data.nano = extractSignedLong(msg, 8); + packetUBXNAVTIMEUTC->data.year = extractInt(msg, 12); + packetUBXNAVTIMEUTC->data.month = extractByte(msg, 14); + packetUBXNAVTIMEUTC->data.day = extractByte(msg, 15); + packetUBXNAVTIMEUTC->data.hour = extractByte(msg, 16); + packetUBXNAVTIMEUTC->data.min = extractByte(msg, 17); + packetUBXNAVTIMEUTC->data.sec = extractByte(msg, 18); + packetUBXNAVTIMEUTC->data.valid.all = extractByte(msg, 19); + + // Mark all datums as fresh (not read before) + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0xFFFFFFFF; + + // Check if we need to copy the data for the callback + if ((packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXNAVTIMEUTC->callbackData->iTOW, &packetUBXNAVTIMEUTC->data.iTOW, sizeof(UBX_NAV_TIMEUTC_data_t)); + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer) + { + storePacket(msg); + } + } + } else if (msg->id == UBX_NAV_VELECEF && msg->len == UBX_NAV_VELECEF_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -5182,6 +5255,25 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + if ((packetUBXNAVEOE != NULL) // If RAM has been allocated for message storage + && (packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + if (packetUBXNAVEOE->callbackPointer != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callback for NAV EOE")); + packetUBXNAVEOE->callbackPointer(*packetUBXNAVEOE->callbackData); // Call the callback + } + if (packetUBXNAVEOE->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV EOE")); + packetUBXNAVEOE->callbackPointerPtr(packetUBXNAVEOE->callbackData); // Call the callback + } + packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + if ((packetUBXNAVATT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid @@ -5239,6 +5331,25 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + if ((packetUBXNAVTIMEUTC != NULL) // If RAM has been allocated for message storage + && (packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + if (packetUBXNAVTIMEUTC->callbackPointer != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callback for NAV TIMEUTC")); + packetUBXNAVTIMEUTC->callbackPointer(*packetUBXNAVTIMEUTC->callbackData); // Call the callback + } + if (packetUBXNAVTIMEUTC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV TIMEUTC")); + packetUBXNAVTIMEUTC->callbackPointerPtr(packetUBXNAVTIMEUTC->callbackData); // Call the callback + } + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + if ((packetUBXNAVVELECEF != NULL) // If RAM has been allocated for message storage && (packetUBXNAVVELECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid @@ -9864,6 +9975,217 @@ void SFE_UBLOX_GNSS::logNAVDOP(bool enabled) packetUBXNAVDOP->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** EOE automatic support + +bool SFE_UBLOX_GNSS::getEOE(uint16_t maxWait) +{ + if (packetUBXNAVEOE == NULL) + initPacketUBXNAVEOE(); // Check that RAM has been allocated for the EOE data + if (packetUBXNAVEOE == NULL) // Bail if the RAM allocation failed + return (false); + + if (packetUBXNAVEOE->automaticFlags.flags.bits.automatic && packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate) + { + // The GPS is automatically reporting, we just check whether we got unread data + // if (_printDebug == true) + // { + // _debugSerial->println(F("getEOE: Autoreporting")); + // } + checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_EOE); + return packetUBXNAVEOE->moduleQueried.moduleQueried.bits.all; + } + else if (packetUBXNAVEOE->automaticFlags.flags.bits.automatic && !packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate) + { + // Someone else has to call checkUblox for us... + // if (_printDebug == true) + // { + // _debugSerial->println(F("getEOE: Exit immediately")); + // } + return (false); + } + else + { + // if (_printDebug == true) + // { + // _debugSerial->println(F("getEOE: Polling")); + // } + + // The GPS is not automatically reporting navigation position so we have to poll explicitly + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_EOE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + // if (_printDebug == true) + // { + // _debugSerial->println(F("getEOE: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); + // } + return (true); + } + + // if (_printDebug == true) + // { + // _debugSerial->print(F("getEOE retVal: ")); + // _debugSerial->println(statusString(retVal)); + // } + return (false); + } +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE +// works. +bool SFE_UBLOX_GNSS::setAutoEOE(bool enable, uint16_t maxWait) +{ + return setAutoEOErate(enable ? 1 : 0, true, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE +// works. +bool SFE_UBLOX_GNSS::setAutoEOE(bool enable, bool implicitUpdate, uint16_t maxWait) +{ + return setAutoEOErate(enable ? 1 : 0, implicitUpdate, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE +// works. +bool SFE_UBLOX_GNSS::setAutoEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) +{ + if (packetUBXNAVEOE == NULL) + initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data + if (packetUBXNAVEOE == NULL) // Only attempt this if RAM allocation was successful + return false; + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_MSG; + packetCfg.len = 3; + packetCfg.startingSpot = 0; + payloadCfg[0] = UBX_CLASS_NAV; + payloadCfg[1] = UBX_NAV_EOE; + payloadCfg[2] = rate; // rate relative to navigation freq. + + bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + if (ok) + { + packetUBXNAVEOE->automaticFlags.flags.bits.automatic = (rate > 0); + packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + packetUBXNAVEOE->moduleQueried.moduleQueried.bits.all = false; + return ok; +} + +// Enable automatic navigation message generation by the GNSS. +bool SFE_UBLOX_GNSS::setAutoEOEcallback(void (*callbackPointer)(UBX_NAV_EOE_data_t), uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoEOE(true, false, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXNAVEOE->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXNAVEOE->callbackData = new UBX_NAV_EOE_data_t; // Allocate RAM for the main struct + } + + if (packetUBXNAVEOE->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoEOEcallback: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXNAVEOE->callbackPointer = callbackPointer; + return (true); +} + +bool SFE_UBLOX_GNSS::setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoEOE(true, false, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXNAVEOE->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXNAVEOE->callbackData = new UBX_NAV_EOE_data_t; // Allocate RAM for the main struct + } + + if (packetUBXNAVEOE->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoEOEcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXNAVEOE->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// In case no config access to the GNSS is possible and EOE is send cyclically already +// set config to suitable parameters +bool SFE_UBLOX_GNSS::assumeAutoEOE(bool enabled, bool implicitUpdate) +{ + if (packetUBXNAVEOE == NULL) + initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data + if (packetUBXNAVEOE == NULL) // Only attempt this if RAM allocation was successful + return false; + + bool changes = packetUBXNAVEOE->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; + if (changes) + { + packetUBXNAVEOE->automaticFlags.flags.bits.automatic = enabled; + packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + return changes; +} + +// PRIVATE: Allocate RAM for packetUBXNAVEOE and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXNAVEOE() +{ + packetUBXNAVEOE = new UBX_NAV_EOE_t; // Allocate RAM for the main struct + if (packetUBXNAVEOE == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXNAVEOE: RAM alloc failed!")); +#endif + return (false); + } + packetUBXNAVEOE->automaticFlags.flags.all = 0; + packetUBXNAVEOE->callbackPointer = NULL; + packetUBXNAVEOE->callbackPointerPtr = NULL; + packetUBXNAVEOE->callbackData = NULL; + packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0; + return (true); +} + +// Mark all the EOE data as read/stale. This is handy to get data alignment after CRC failure +void SFE_UBLOX_GNSS::flushEOE() +{ + if (packetUBXNAVEOE == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0; // Mark all EOEs as stale (read before) +} + +// Log this data in file buffer +void SFE_UBLOX_GNSS::logNAVEOE(bool enabled) +{ + if (packetUBXNAVEOE == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; +} + // ***** VEH ATT automatic support bool SFE_UBLOX_GNSS::getVehAtt(uint16_t maxWait) @@ -10475,6 +10797,195 @@ void SFE_UBLOX_GNSS::logNAVODO(bool enabled) packetUBXNAVODO->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** NAV TIMEUTC automatic support + +bool SFE_UBLOX_GNSS::getNAVTIMEUTC(uint16_t maxWait) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the TIMEUTC data + if (packetUBXNAVTIMEUTC == NULL) // Bail if the RAM allocation failed + return (false); + + if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + { + // The GPS is automatically reporting, we just check whether we got unread data + checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_TIMEUTC); + return packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all; + } + else if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && !packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + { + // Someone else has to call checkUblox for us... + return (false); + } + else + { + // The GPS is not automatically reporting navigation position so we have to poll explicitly + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_TIMEUTC; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + return (true); + } + + return (false); + } +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, uint16_t maxWait) +{ + return setAutoNAVTIMEUTCrate(enable ? 1 : 0, true, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, bool implicitUpdate, uint16_t maxWait) +{ + return setAutoNAVTIMEUTCrate(enable ? 1 : 0, implicitUpdate, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data + if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful + return false; + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_MSG; + packetCfg.len = 3; + packetCfg.startingSpot = 0; + payloadCfg[0] = UBX_CLASS_NAV; + payloadCfg[1] = UBX_NAV_TIMEUTC; + payloadCfg[2] = rate; // rate relative to navigation freq. + + bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + if (ok) + { + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = (rate > 0); + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all = false; + return ok; +} + +// Enable automatic navigation message generation by the GNSS. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallback(void (*callbackPointer)(UBX_NAV_TIMEUTC_data_t), uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoNAVTIMEUTC(true, false, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct + } + + if (packetUBXNAVTIMEUTC->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoNAVTIMEUTCcallback: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXNAVTIMEUTC->callbackPointer = callbackPointer; + return (true); +} + +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoNAVTIMEUTC(true, false, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct + } + + if (packetUBXNAVTIMEUTC->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoNAVTIMEUTCcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXNAVTIMEUTC->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// In case no config access to the GNSS is possible and TIMEUTC is send cyclically already +// set config to suitable parameters +bool SFE_UBLOX_GNSS::assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data + if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful + return false; + + bool changes = packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; + if (changes) + { + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = enabled; + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + return changes; +} + +// PRIVATE: Allocate RAM for packetUBXNAVTIMEUTC and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXNAVTIMEUTC() +{ + packetUBXNAVTIMEUTC = new UBX_NAV_TIMEUTC_t; // Allocate RAM for the main struct + if (packetUBXNAVTIMEUTC == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXNAVTIMEUTC: RAM alloc failed!")); +#endif + return (false); + } + packetUBXNAVTIMEUTC->automaticFlags.flags.all = 0; + packetUBXNAVTIMEUTC->callbackPointer = NULL; + packetUBXNAVTIMEUTC->callbackPointerPtr = NULL; + packetUBXNAVTIMEUTC->callbackData = NULL; + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; + return (true); +} + +// Mark all the data as read/stale +void SFE_UBLOX_GNSS::flushNAVTIMEUTC() +{ + if (packetUBXNAVTIMEUTC == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) +} + +// Log this data in file buffer +void SFE_UBLOX_GNSS::logNAVTIMEUTC(bool enabled) +{ + if (packetUBXNAVTIMEUTC == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; +} + // ***** NAV VELECEF automatic support bool SFE_UBLOX_GNSS::getNAVVELECEF(uint16_t maxWait) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index a60ee2b..1fbd364 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1021,6 +1021,16 @@ class SFE_UBLOX_GNSS void flushDOP(); // Mark all the DOP data as read/stale void logNAVDOP(bool enabled = true); // Log data to file buffer + bool getEOE(uint16_t maxWait = defaultMaxWait); // Query module for latest dilution of precision values and load global vars:. If autoEOE is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new EOE is available. + bool setAutoEOE(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency + bool setAutoEOE(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update + bool setAutoEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic EOE reports + bool setAutoEOEcallback(void (*callbackPointer)(UBX_NAV_EOE_data_t), uint16_t maxWait = defaultMaxWait); // Enable automatic EOE reports at the navigation frequency. Data is accessed from the callback. + bool setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic EOE reports at the navigation frequency. Data is accessed from the callback. + bool assumeAutoEOE(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and EOE is send cyclically already + void flushEOE(); // Mark all the EOE data as read/stale + void logNAVEOE(bool enabled = true); // Log data to file buffer + bool getVehAtt(uint16_t maxWait = defaultMaxWait); // NAV ATT Helper bool getNAVATT(uint16_t maxWait = defaultMaxWait); // NAV ATT bool setAutoNAVATT(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic vehicle attitude reports at the navigation frequency @@ -1052,6 +1062,16 @@ class SFE_UBLOX_GNSS void flushNAVODO(); // Mark all the data as read/stale void logNAVODO(bool enabled = true); // Log data to file buffer + bool getNAVTIMEUTC(uint16_t maxWait = defaultMaxWait); // NAV TIMEUTC + bool setAutoNAVTIMEUTC(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency + bool setAutoNAVTIMEUTC(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update + bool setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic TIMEUTC reports + bool setAutoNAVTIMEUTCcallback(void (*callbackPointer)(UBX_NAV_TIMEUTC_data_t), uint16_t maxWait = defaultMaxWait); // Enable automatic TIMEUTC reports at the navigation frequency. Data is accessed from the callback. + bool setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic TIMEUTC reports at the navigation frequency. Data is accessed from the callback. + bool assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and TIMEUTC is send cyclically already + void flushNAVTIMEUTC(); // Mark all the data as read/stale + void logNAVTIMEUTC(bool enabled = true); // Log data to file buffer + bool getNAVVELECEF(uint16_t maxWait = defaultMaxWait); // NAV VELECEF bool setAutoNAVVELECEF(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic VELECEF reports at the navigation frequency bool setAutoNAVVELECEF(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic VELECEF reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update @@ -1501,9 +1521,11 @@ class SFE_UBLOX_GNSS UBX_NAV_POSECEF_t *packetUBXNAVPOSECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_STATUS_t *packetUBXNAVSTATUS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_DOP_t *packetUBXNAVDOP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_NAV_EOE_t *packetUBXNAVEOE = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_ATT_t *packetUBXNAVATT = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_PVT_t *packetUBXNAVPVT = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_ODO_t *packetUBXNAVODO = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_NAV_TIMEUTC_t *packetUBXNAVTIMEUTC = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_VELECEF_t *packetUBXNAVVELECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_VELNED_t *packetUBXNAVVELNED = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_HPPOSECEF_t *packetUBXNAVHPPOSECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1601,9 +1623,11 @@ class SFE_UBLOX_GNSS bool initPacketUBXNAVPOSECEF(); // Allocate RAM for packetUBXNAVPOSECEF and initialize it bool initPacketUBXNAVSTATUS(); // Allocate RAM for packetUBXNAVSTATUS and initialize it bool initPacketUBXNAVDOP(); // Allocate RAM for packetUBXNAVDOP and initialize it + bool initPacketUBXNAVEOE(); // Allocate RAM for packetUBXNAVEOE and initialize it bool initPacketUBXNAVATT(); // Allocate RAM for packetUBXNAVATT and initialize it bool initPacketUBXNAVPVT(); // Allocate RAM for packetUBXNAVPVT and initialize it bool initPacketUBXNAVODO(); // Allocate RAM for packetUBXNAVODO and initialize it + bool initPacketUBXNAVTIMEUTC(); // Allocate RAM for packetUBXNAVTIMEUTC and initialize it bool initPacketUBXNAVVELECEF(); // Allocate RAM for packetUBXNAVVELECEF and initialize it bool initPacketUBXNAVVELNED(); // Allocate RAM for packetUBXNAVVELNED and initialize it bool initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 019aebb..fcaff3f 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -298,6 +298,38 @@ typedef struct UBX_NAV_DOP_data_t *callbackData; } UBX_NAV_DOP_t; +// UBX-NAV-EOE (0x01 0x61): End of Epoch +const uint16_t UBX_NAV_EOE_LEN = 4; + +typedef struct +{ + uint32_t iTOW; // GPS time of week of the navigation epoch: ms +} UBX_NAV_EOE_data_t; + +typedef struct +{ + union + { + uint32_t all; + struct + { + uint32_t all : 1; + + uint32_t iTOW : 1; + } bits; + } moduleQueried; +} UBX_NAV_EOE_moduleQueried_t; + +typedef struct +{ + ubxAutomaticFlags automaticFlags; + UBX_NAV_EOE_data_t data; + UBX_NAV_EOE_moduleQueried_t moduleQueried; + void (*callbackPointer)(UBX_NAV_EOE_data_t); + void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *); + UBX_NAV_EOE_data_t *callbackData; +} UBX_NAV_EOE_t; + // UBX-NAV-ATT (0x01 0x05): Attitude solution const uint16_t UBX_NAV_ATT_LEN = 32; From 81d058f66e7743a1059ca0581e7cf453ce343d90 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 2 Jul 2022 12:37:34 +0100 Subject: [PATCH 08/23] Update keywords.txt. Add TIMEUTC and EOE to end(). Rename NAVEOE functions. --- Theory.md | 3 +- keywords.txt | 17 + src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 650 +++++++++---------- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 42 +- src/u-blox_structs.h | 64 +- 5 files changed, 373 insertions(+), 403 deletions(-) diff --git a/Theory.md b/Theory.md index 608860e..70170c3 100644 --- a/Theory.md +++ b/Theory.md @@ -55,11 +55,13 @@ In v2.0, the full list of messages which can be processed and logged automatical - UBX-NAV-HPPOSECEF (0x01 0x13): High precision position solution in ECEF - UBX-NAV-HPPOSLLH (0x01 0x14): High precision geodetic position solution - UBX-NAV-PVAT (0x01 0x17): Navigation position velocity attitude time solution (**only with ADR or UDR products**) +- UBX-NAV-TIMEUTC (0x01 0x21): UTC time solution - UBX-NAV-CLOCK (0x01 0x22): Clock solution - UBX-NAV-SAT (0x01 0x35): Satellite information - UBX-NAV-SVIN (0x01 0x3B): Survey-in data (**only with High Precision GNSS products**) - UBX-NAV-RELPOSNED (0x01 0x3C): Relative positioning information in NED frame (**only with High Precision GNSS products**) - UBX-NAV-AOPSTATUS (0x01 0x60): AssistNow Autonomous status +- UBX-NAV-EOE (0x01 0x61): End of epoch - UBX-RXM-SFRBX (0x02 0x13): Broadcast navigation data subframe - UBX-RXM-RAWX (0x02 0x15): Multi-GNSS raw measurement data (**only with ADR or High Precision GNSS or Time Sync products**) - UBX-TIM-TM2 (0x0D 0x03): Time mark data @@ -76,7 +78,6 @@ Please see [Adding_New_Messages](./Adding_New_Messages.md) for details on how to Notes: - UBX-NAV-POSLLH is not supported as UBX-NAV-PVT contains the same information -- UBX-NAV-TIMEUTC is not supported as UBX-NAV-PVT contains the same information ## Migrating your code to v2.0 diff --git a/keywords.txt b/keywords.txt index 1dd8706..c261965 100644 --- a/keywords.txt +++ b/keywords.txt @@ -45,6 +45,7 @@ UBX_NAV_SVIN_data_t KEYWORD1 UBX_NAV_RELPOSNED_data_t KEYWORD1 UBX_NAV_TIMELS_data_t KEYWORD1 UBX_NAV_AOPSTATUS_data_t KEYWORD1 +UBX_NAV_EOE_data_t KEYWORD1 UBX_RXM_PMP_data_t KEYWORD1 UBX_RXM_PMP_message_data_t KEYWORD1 @@ -344,6 +345,14 @@ assumeAutoNAVPVAT KEYWORD2 flushNAVPVAT KEYWORD2 logNAVPVAT KEYWORD2 +getNAVTIMEUTC KEYWORD2 +setAutoNAVTIMEUTC KEYWORD2 +setAutoNAVTIMEUTCrate KEYWORD2 +setAutoNAVTIMEUTCcallbackPtr KEYWORD2 +assumeAutoNAVTIMEUTC KEYWORD2 +flushNAVTIMEUTC KEYWORD2 +logNAVTIMEUTC KEYWORD2 + getNAVCLOCK KEYWORD2 setAutoNAVCLOCK KEYWORD2 setAutoNAVCLOCKrate KEYWORD2 @@ -392,6 +401,14 @@ assumeAutoAOPSTATUS KEYWORD2 flushAOPSTATUS KEYWORD2 logAOPSTATUS KEYWORD2 +getNAVEOE KEYWORD2 +setAutoNAVEOE KEYWORD2 +setAutoNAVEOErate KEYWORD2 +setAutoNAVEOEcallbackPtr KEYWORD2 +assumeAutoNAVEOE KEYWORD2 +flushNAVEOE KEYWORD2 +logNAVEOE KEYWORD2 + setRXMPMPcallbackPtr KEYWORD2 setRXMPMPmessageCallbackPtr KEYWORD2 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 0c71457..25c2abe 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -251,6 +251,16 @@ void SFE_UBLOX_GNSS::end(void) packetUBXNAVPVAT = NULL; // Redundant? } + if (packetUBXNAVTIMEUTC != NULL) + { + if (packetUBXNAVTIMEUTC->callbackData != NULL) + { + delete packetUBXNAVTIMEUTC->callbackData; + } + delete packetUBXNAVTIMEUTC; + packetUBXNAVTIMEUTC = NULL; // Redundant? + } + if (packetUBXNAVCLOCK != NULL) { if (packetUBXNAVCLOCK->callbackData != NULL) @@ -301,6 +311,16 @@ void SFE_UBLOX_GNSS::end(void) packetUBXNAVAOPSTATUS = NULL; // Redundant? } + if (packetUBXNAVEOE != NULL) + { + if (packetUBXNAVEOE->callbackData != NULL) + { + delete packetUBXNAVEOE->callbackData; + } + delete packetUBXNAVEOE; + packetUBXNAVEOE = NULL; // Redundant? + } + if (packetUBXRXMPMP != NULL) { if (packetUBXRXMPMP->callbackData != NULL) @@ -1295,10 +1315,6 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVDOP != NULL) result = true; break; - case UBX_NAV_EOE: - if (packetUBXNAVEOE != NULL) - result = true; - break; case UBX_NAV_ATT: if (packetUBXNAVATT != NULL) result = true; @@ -1311,10 +1327,6 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVODO != NULL) result = true; break; - case UBX_NAV_TIMEUTC: - if (packetUBXNAVTIMEUTC != NULL) - result = true; - break; case UBX_NAV_VELECEF: if (packetUBXNAVVELECEF != NULL) result = true; @@ -1335,6 +1347,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVPVAT != NULL) result = true; break; + case UBX_NAV_TIMEUTC: + if (packetUBXNAVTIMEUTC != NULL) + result = true; + break; case UBX_NAV_CLOCK: if (packetUBXNAVCLOCK != NULL) result = true; @@ -1359,6 +1375,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if (packetUBXNAVAOPSTATUS != NULL) result = true; break; + case UBX_NAV_EOE: + if (packetUBXNAVEOE != NULL) + result = true; + break; } } break; @@ -1495,9 +1515,6 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_DOP: maxSize = UBX_NAV_DOP_LEN; break; - case UBX_NAV_EOE: - maxSize = UBX_NAV_EOE_LEN; - break; case UBX_NAV_ATT: maxSize = UBX_NAV_ATT_LEN; break; @@ -1507,9 +1524,6 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_ODO: maxSize = UBX_NAV_ODO_LEN; break; - case UBX_NAV_TIMEUTC: - maxSize = UBX_NAV_TIMEUTC; - break; case UBX_NAV_VELECEF: maxSize = UBX_NAV_VELECEF_LEN; break; @@ -1525,6 +1539,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_PVAT: maxSize = UBX_NAV_PVAT_LEN; break; + case UBX_NAV_TIMEUTC: + maxSize = UBX_NAV_TIMEUTC; + break; case UBX_NAV_CLOCK: maxSize = UBX_NAV_CLOCK_LEN; break; @@ -1543,6 +1560,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_AOPSTATUS: maxSize = UBX_NAV_AOPSTATUS_LEN; break; + case UBX_NAV_EOE: + maxSize = UBX_NAV_EOE_LEN; + break; } } break; @@ -3245,31 +3265,6 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } - else if (msg->id == UBX_NAV_EOE && msg->len == UBX_NAV_EOE_LEN) - { - // Parse various byte fields into storage - but only if we have memory allocated for it - if (packetUBXNAVEOE != NULL) - { - packetUBXNAVEOE->data.iTOW = extractLong(msg, 0); - - // Mark all datums as fresh (not read before) - packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0xFFFFFFFF; - - // Check if we need to copy the data for the callback - if ((packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale - { - memcpy(&packetUBXNAVEOE->callbackData->iTOW, &packetUBXNAVEOE->data.iTOW, sizeof(UBX_NAV_EOE_data_t)); - packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = true; - } - - // Check if we need to copy the data into the file buffer - if (packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer) - { - storePacket(msg); - } - } - } else if (msg->id == UBX_NAV_ATT && msg->len == UBX_NAV_ATT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -3388,40 +3383,6 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } - else if (msg->id == UBX_NAV_TIMEUTC && msg->len == UBX_NAV_TIMEUTC_LEN) - { - // Parse various byte fields into storage - but only if we have memory allocated for it - if (packetUBXNAVTIMEUTC != NULL) - { - packetUBXNAVTIMEUTC->data.iTOW = extractLong(msg, 0); - packetUBXNAVTIMEUTC->data.tAcc = extractLong(msg, 4); - packetUBXNAVTIMEUTC->data.nano = extractSignedLong(msg, 8); - packetUBXNAVTIMEUTC->data.year = extractInt(msg, 12); - packetUBXNAVTIMEUTC->data.month = extractByte(msg, 14); - packetUBXNAVTIMEUTC->data.day = extractByte(msg, 15); - packetUBXNAVTIMEUTC->data.hour = extractByte(msg, 16); - packetUBXNAVTIMEUTC->data.min = extractByte(msg, 17); - packetUBXNAVTIMEUTC->data.sec = extractByte(msg, 18); - packetUBXNAVTIMEUTC->data.valid.all = extractByte(msg, 19); - - // Mark all datums as fresh (not read before) - packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0xFFFFFFFF; - - // Check if we need to copy the data for the callback - if ((packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale - { - memcpy(&packetUBXNAVTIMEUTC->callbackData->iTOW, &packetUBXNAVTIMEUTC->data.iTOW, sizeof(UBX_NAV_TIMEUTC_data_t)); - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = true; - } - - // Check if we need to copy the data into the file buffer - if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer) - { - storePacket(msg); - } - } - } else if (msg->id == UBX_NAV_VELECEF && msg->len == UBX_NAV_VELECEF_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -3618,6 +3579,40 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } + else if (msg->id == UBX_NAV_TIMEUTC && msg->len == UBX_NAV_TIMEUTC_LEN) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXNAVTIMEUTC != NULL) + { + packetUBXNAVTIMEUTC->data.iTOW = extractLong(msg, 0); + packetUBXNAVTIMEUTC->data.tAcc = extractLong(msg, 4); + packetUBXNAVTIMEUTC->data.nano = extractSignedLong(msg, 8); + packetUBXNAVTIMEUTC->data.year = extractInt(msg, 12); + packetUBXNAVTIMEUTC->data.month = extractByte(msg, 14); + packetUBXNAVTIMEUTC->data.day = extractByte(msg, 15); + packetUBXNAVTIMEUTC->data.hour = extractByte(msg, 16); + packetUBXNAVTIMEUTC->data.min = extractByte(msg, 17); + packetUBXNAVTIMEUTC->data.sec = extractByte(msg, 18); + packetUBXNAVTIMEUTC->data.valid.all = extractByte(msg, 19); + + // Mark all datums as fresh (not read before) + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0xFFFFFFFF; + + // Check if we need to copy the data for the callback + if ((packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXNAVTIMEUTC->callbackData->iTOW, &packetUBXNAVTIMEUTC->data.iTOW, sizeof(UBX_NAV_TIMEUTC_data_t)); + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer) + { + storePacket(msg); + } + } + } else if (msg->id == UBX_NAV_CLOCK && msg->len == UBX_NAV_CLOCK_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -3838,6 +3833,31 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } + else if (msg->id == UBX_NAV_EOE && msg->len == UBX_NAV_EOE_LEN) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXNAVEOE != NULL) + { + packetUBXNAVEOE->data.iTOW = extractLong(msg, 0); + + // Mark all datums as fresh (not read before) + packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0xFFFFFFFF; + + // Check if we need to copy the data for the callback + if ((packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXNAVEOE->callbackData->iTOW, &packetUBXNAVEOE->data.iTOW, sizeof(UBX_NAV_EOE_data_t)); + packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer) + { + storePacket(msg); + } + } + } break; case UBX_CLASS_RXM: if (msg->id == UBX_RXM_PMP) @@ -5255,25 +5275,6 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } - if ((packetUBXNAVEOE != NULL) // If RAM has been allocated for message storage - && (packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid - { - if (packetUBXNAVEOE->callbackPointer != NULL) // If the pointer to the callback has been defined - { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callback for NAV EOE")); - packetUBXNAVEOE->callbackPointer(*packetUBXNAVEOE->callbackData); // Call the callback - } - if (packetUBXNAVEOE->callbackPointerPtr != NULL) // If the pointer to the callback has been defined - { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV EOE")); - packetUBXNAVEOE->callbackPointerPtr(packetUBXNAVEOE->callbackData); // Call the callback - } - packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale - } - if ((packetUBXNAVATT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid @@ -5331,25 +5332,6 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } - if ((packetUBXNAVTIMEUTC != NULL) // If RAM has been allocated for message storage - && (packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid - { - if (packetUBXNAVTIMEUTC->callbackPointer != NULL) // If the pointer to the callback has been defined - { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callback for NAV TIMEUTC")); - packetUBXNAVTIMEUTC->callbackPointer(*packetUBXNAVTIMEUTC->callbackData); // Call the callback - } - if (packetUBXNAVTIMEUTC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined - { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV TIMEUTC")); - packetUBXNAVTIMEUTC->callbackPointerPtr(packetUBXNAVTIMEUTC->callbackData); // Call the callback - } - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale - } - if ((packetUBXNAVVELECEF != NULL) // If RAM has been allocated for message storage && (packetUBXNAVVELECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid @@ -5445,6 +5427,19 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + if ((packetUBXNAVTIMEUTC != NULL) // If RAM has been allocated for message storage + && (packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + if (packetUBXNAVTIMEUTC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV TIMEUTC")); + packetUBXNAVTIMEUTC->callbackPointerPtr(packetUBXNAVTIMEUTC->callbackData); // Call the callback + } + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + if ((packetUBXNAVCLOCK != NULL) // If RAM has been allocated for message storage && (packetUBXNAVCLOCK->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVCLOCK->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid @@ -5534,6 +5529,19 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + if ((packetUBXNAVEOE != NULL) // If RAM has been allocated for message storage + && (packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + if (packetUBXNAVEOE->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV EOE")); + packetUBXNAVEOE->callbackPointerPtr(packetUBXNAVEOE->callbackData); // Call the callback + } + packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + if ((packetUBXRXMPMP != NULL) // If RAM has been allocated for message storage && (packetUBXRXMPMP->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMPMP->callbackPointerPtr != NULL) // If the pointer to the callback has been defined @@ -9977,7 +9985,7 @@ void SFE_UBLOX_GNSS::logNAVDOP(bool enabled) // ***** EOE automatic support -bool SFE_UBLOX_GNSS::getEOE(uint16_t maxWait) +bool SFE_UBLOX_GNSS::getNAVEOE(uint16_t maxWait) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the EOE data @@ -10042,21 +10050,21 @@ bool SFE_UBLOX_GNSS::getEOE(uint16_t maxWait) // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. -bool SFE_UBLOX_GNSS::setAutoEOE(bool enable, uint16_t maxWait) +bool SFE_UBLOX_GNSS::setAutoNAVEOE(bool enable, uint16_t maxWait) { - return setAutoEOErate(enable ? 1 : 0, true, maxWait); + return setAutoNAVEOErate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. -bool SFE_UBLOX_GNSS::setAutoEOE(bool enable, bool implicitUpdate, uint16_t maxWait) +bool SFE_UBLOX_GNSS::setAutoNAVEOE(bool enable, bool implicitUpdate, uint16_t maxWait) { - return setAutoEOErate(enable ? 1 : 0, implicitUpdate, maxWait); + return setAutoNAVEOErate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. -bool SFE_UBLOX_GNSS::setAutoEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) +bool SFE_UBLOX_GNSS::setAutoNAVEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data @@ -10082,35 +10090,10 @@ bool SFE_UBLOX_GNSS::setAutoEOErate(uint8_t rate, bool implicitUpdate, uint16_t } // Enable automatic navigation message generation by the GNSS. -bool SFE_UBLOX_GNSS::setAutoEOEcallback(void (*callbackPointer)(UBX_NAV_EOE_data_t), uint16_t maxWait) -{ - // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. - bool result = setAutoEOE(true, false, maxWait); - if (!result) - return (result); // Bail if setAuto failed - - if (packetUBXNAVEOE->callbackData == NULL) // Check if RAM has been allocated for the callback copy - { - packetUBXNAVEOE->callbackData = new UBX_NAV_EOE_data_t; // Allocate RAM for the main struct - } - - if (packetUBXNAVEOE->callbackData == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("setAutoEOEcallback: RAM alloc failed!")); -#endif - return (false); - } - - packetUBXNAVEOE->callbackPointer = callbackPointer; - return (true); -} - -bool SFE_UBLOX_GNSS::setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait) +bool SFE_UBLOX_GNSS::setAutoNAVEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. - bool result = setAutoEOE(true, false, maxWait); + bool result = setAutoNAVEOE(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed @@ -10123,7 +10106,7 @@ bool SFE_UBLOX_GNSS::setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EO { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("setAutoEOEcallbackPtr: RAM alloc failed!")); + _debugSerial->println(F("setAutoNAVEOEcallbackPtr: RAM alloc failed!")); #endif return (false); } @@ -10134,7 +10117,7 @@ bool SFE_UBLOX_GNSS::setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EO // In case no config access to the GNSS is possible and EOE is send cyclically already // set config to suitable parameters -bool SFE_UBLOX_GNSS::assumeAutoEOE(bool enabled, bool implicitUpdate) +bool SFE_UBLOX_GNSS::assumeAutoNAVEOE(bool enabled, bool implicitUpdate) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data @@ -10163,7 +10146,6 @@ bool SFE_UBLOX_GNSS::initPacketUBXNAVEOE() return (false); } packetUBXNAVEOE->automaticFlags.flags.all = 0; - packetUBXNAVEOE->callbackPointer = NULL; packetUBXNAVEOE->callbackPointerPtr = NULL; packetUBXNAVEOE->callbackData = NULL; packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0; @@ -10171,7 +10153,7 @@ bool SFE_UBLOX_GNSS::initPacketUBXNAVEOE() } // Mark all the EOE data as read/stale. This is handy to get data alignment after CRC failure -void SFE_UBLOX_GNSS::flushEOE() +void SFE_UBLOX_GNSS::flushNAVEOE() { if (packetUBXNAVEOE == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) @@ -10797,22 +10779,22 @@ void SFE_UBLOX_GNSS::logNAVODO(bool enabled) packetUBXNAVODO->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } -// ***** NAV TIMEUTC automatic support +// ***** NAV VELECEF automatic support -bool SFE_UBLOX_GNSS::getNAVTIMEUTC(uint16_t maxWait) +bool SFE_UBLOX_GNSS::getNAVVELECEF(uint16_t maxWait) { - if (packetUBXNAVTIMEUTC == NULL) - initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the TIMEUTC data - if (packetUBXNAVTIMEUTC == NULL) // Bail if the RAM allocation failed + if (packetUBXNAVVELECEF == NULL) + initPacketUBXNAVVELECEF(); // Check that RAM has been allocated for the VELECEF data + if (packetUBXNAVVELECEF == NULL) // Bail if the RAM allocation failed return (false); - if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data - checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_TIMEUTC); - return packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all; + checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_VELECEF); + return packetUBXNAVVELECEF->moduleQueried.moduleQueried.bits.all; } - else if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && !packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + else if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && !packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); @@ -10821,196 +10803,7 @@ bool SFE_UBLOX_GNSS::getNAVTIMEUTC(uint16_t maxWait) { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; - packetCfg.id = UBX_NAV_TIMEUTC; - packetCfg.len = 0; - packetCfg.startingSpot = 0; - - // The data is parsed as part of processing the response - sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); - - if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) - return (true); - - if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) - { - return (true); - } - - return (false); - } -} - -// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC -// works. -bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, uint16_t maxWait) -{ - return setAutoNAVTIMEUTCrate(enable ? 1 : 0, true, maxWait); -} - -// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC -// works. -bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, bool implicitUpdate, uint16_t maxWait) -{ - return setAutoNAVTIMEUTCrate(enable ? 1 : 0, implicitUpdate, maxWait); -} - -// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC -// works. -bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) -{ - if (packetUBXNAVTIMEUTC == NULL) - initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data - if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful - return false; - - packetCfg.cls = UBX_CLASS_CFG; - packetCfg.id = UBX_CFG_MSG; - packetCfg.len = 3; - packetCfg.startingSpot = 0; - payloadCfg[0] = UBX_CLASS_NAV; - payloadCfg[1] = UBX_NAV_TIMEUTC; - payloadCfg[2] = rate; // rate relative to navigation freq. - - bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK - if (ok) - { - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = (rate > 0); - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; - } - packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all = false; - return ok; -} - -// Enable automatic navigation message generation by the GNSS. -bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallback(void (*callbackPointer)(UBX_NAV_TIMEUTC_data_t), uint16_t maxWait) -{ - // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. - bool result = setAutoNAVTIMEUTC(true, false, maxWait); - if (!result) - return (result); // Bail if setAuto failed - - if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy - { - packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct - } - - if (packetUBXNAVTIMEUTC->callbackData == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("setAutoNAVTIMEUTCcallback: RAM alloc failed!")); -#endif - return (false); - } - - packetUBXNAVTIMEUTC->callbackPointer = callbackPointer; - return (true); -} - -bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait) -{ - // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. - bool result = setAutoNAVTIMEUTC(true, false, maxWait); - if (!result) - return (result); // Bail if setAuto failed - - if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy - { - packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct - } - - if (packetUBXNAVTIMEUTC->callbackData == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("setAutoNAVTIMEUTCcallbackPtr: RAM alloc failed!")); -#endif - return (false); - } - - packetUBXNAVTIMEUTC->callbackPointerPtr = callbackPointerPtr; - return (true); -} - -// In case no config access to the GNSS is possible and TIMEUTC is send cyclically already -// set config to suitable parameters -bool SFE_UBLOX_GNSS::assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate) -{ - if (packetUBXNAVTIMEUTC == NULL) - initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data - if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful - return false; - - bool changes = packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; - if (changes) - { - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = enabled; - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; - } - return changes; -} - -// PRIVATE: Allocate RAM for packetUBXNAVTIMEUTC and initialize it -bool SFE_UBLOX_GNSS::initPacketUBXNAVTIMEUTC() -{ - packetUBXNAVTIMEUTC = new UBX_NAV_TIMEUTC_t; // Allocate RAM for the main struct - if (packetUBXNAVTIMEUTC == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("initPacketUBXNAVTIMEUTC: RAM alloc failed!")); -#endif - return (false); - } - packetUBXNAVTIMEUTC->automaticFlags.flags.all = 0; - packetUBXNAVTIMEUTC->callbackPointer = NULL; - packetUBXNAVTIMEUTC->callbackPointerPtr = NULL; - packetUBXNAVTIMEUTC->callbackData = NULL; - packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; - return (true); -} - -// Mark all the data as read/stale -void SFE_UBLOX_GNSS::flushNAVTIMEUTC() -{ - if (packetUBXNAVTIMEUTC == NULL) - return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) - packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) -} - -// Log this data in file buffer -void SFE_UBLOX_GNSS::logNAVTIMEUTC(bool enabled) -{ - if (packetUBXNAVTIMEUTC == NULL) - return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) - packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; -} - -// ***** NAV VELECEF automatic support - -bool SFE_UBLOX_GNSS::getNAVVELECEF(uint16_t maxWait) -{ - if (packetUBXNAVVELECEF == NULL) - initPacketUBXNAVVELECEF(); // Check that RAM has been allocated for the VELECEF data - if (packetUBXNAVVELECEF == NULL) // Bail if the RAM allocation failed - return (false); - - if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) - { - // The GPS is automatically reporting, we just check whether we got unread data - checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_VELECEF); - return packetUBXNAVVELECEF->moduleQueried.moduleQueried.bits.all; - } - else if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && !packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) - { - // Someone else has to call checkUblox for us... - return (false); - } - else - { - // The GPS is not automatically reporting navigation position so we have to poll explicitly - packetCfg.cls = UBX_CLASS_NAV; - packetCfg.id = UBX_NAV_VELECEF; + packetCfg.id = UBX_NAV_VELECEF; packetCfg.len = 0; packetCfg.startingSpot = 0; @@ -11969,6 +11762,169 @@ void SFE_UBLOX_GNSS::logNAVPVAT(bool enabled) packetUBXNAVPVAT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** NAV TIMEUTC automatic support + +bool SFE_UBLOX_GNSS::getNAVTIMEUTC(uint16_t maxWait) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the TIMEUTC data + if (packetUBXNAVTIMEUTC == NULL) // Bail if the RAM allocation failed + return (false); + + if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + { + // The GPS is automatically reporting, we just check whether we got unread data + checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_TIMEUTC); + return packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all; + } + else if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && !packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) + { + // Someone else has to call checkUblox for us... + return (false); + } + else + { + // The GPS is not automatically reporting navigation position so we have to poll explicitly + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_TIMEUTC; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + // The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + return (true); + } + + return (false); + } +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, uint16_t maxWait) +{ + return setAutoNAVTIMEUTCrate(enable ? 1 : 0, true, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, bool implicitUpdate, uint16_t maxWait) +{ + return setAutoNAVTIMEUTCrate(enable ? 1 : 0, implicitUpdate, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC +// works. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data + if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful + return false; + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_MSG; + packetCfg.len = 3; + packetCfg.startingSpot = 0; + payloadCfg[0] = UBX_CLASS_NAV; + payloadCfg[1] = UBX_NAV_TIMEUTC; + payloadCfg[2] = rate; // rate relative to navigation freq. + + bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + if (ok) + { + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = (rate > 0); + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all = false; + return ok; +} + +// Enable automatic navigation message generation by the GNSS. +bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoNAVTIMEUTC(true, false, maxWait); + if (!result) + return (result); // Bail if setAuto failed + + if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct + } + + if (packetUBXNAVTIMEUTC->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoNAVTIMEUTCcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXNAVTIMEUTC->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// In case no config access to the GNSS is possible and TIMEUTC is send cyclically already +// set config to suitable parameters +bool SFE_UBLOX_GNSS::assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate) +{ + if (packetUBXNAVTIMEUTC == NULL) + initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data + if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful + return false; + + bool changes = packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; + if (changes) + { + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = enabled; + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + return changes; +} + +// PRIVATE: Allocate RAM for packetUBXNAVTIMEUTC and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXNAVTIMEUTC() +{ + packetUBXNAVTIMEUTC = new UBX_NAV_TIMEUTC_t; // Allocate RAM for the main struct + if (packetUBXNAVTIMEUTC == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXNAVTIMEUTC: RAM alloc failed!")); +#endif + return (false); + } + packetUBXNAVTIMEUTC->automaticFlags.flags.all = 0; + packetUBXNAVTIMEUTC->callbackPointerPtr = NULL; + packetUBXNAVTIMEUTC->callbackData = NULL; + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; + return (true); +} + +// Mark all the data as read/stale +void SFE_UBLOX_GNSS::flushNAVTIMEUTC() +{ + if (packetUBXNAVTIMEUTC == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) +} + +// Log this data in file buffer +void SFE_UBLOX_GNSS::logNAVTIMEUTC(bool enabled) +{ + if (packetUBXNAVTIMEUTC == NULL) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; +} + // ***** NAV CLOCK automatic support bool SFE_UBLOX_GNSS::getNAVCLOCK(uint16_t maxWait) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 78eb312..3f11018 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1021,16 +1021,6 @@ class SFE_UBLOX_GNSS void flushDOP(); // Mark all the DOP data as read/stale void logNAVDOP(bool enabled = true); // Log data to file buffer - bool getEOE(uint16_t maxWait = defaultMaxWait); // Query module for latest dilution of precision values and load global vars:. If autoEOE is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new EOE is available. - bool setAutoEOE(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency - bool setAutoEOE(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update - bool setAutoEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic EOE reports - bool setAutoEOEcallback(void (*callbackPointer)(UBX_NAV_EOE_data_t), uint16_t maxWait = defaultMaxWait); // Enable automatic EOE reports at the navigation frequency. Data is accessed from the callback. - bool setAutoEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic EOE reports at the navigation frequency. Data is accessed from the callback. - bool assumeAutoEOE(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and EOE is send cyclically already - void flushEOE(); // Mark all the EOE data as read/stale - void logNAVEOE(bool enabled = true); // Log data to file buffer - bool getVehAtt(uint16_t maxWait = defaultMaxWait); // NAV ATT Helper bool getNAVATT(uint16_t maxWait = defaultMaxWait); // NAV ATT bool setAutoNAVATT(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic vehicle attitude reports at the navigation frequency @@ -1062,16 +1052,6 @@ class SFE_UBLOX_GNSS void flushNAVODO(); // Mark all the data as read/stale void logNAVODO(bool enabled = true); // Log data to file buffer - bool getNAVTIMEUTC(uint16_t maxWait = defaultMaxWait); // NAV TIMEUTC - bool setAutoNAVTIMEUTC(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency - bool setAutoNAVTIMEUTC(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update - bool setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic TIMEUTC reports - bool setAutoNAVTIMEUTCcallback(void (*callbackPointer)(UBX_NAV_TIMEUTC_data_t), uint16_t maxWait = defaultMaxWait); // Enable automatic TIMEUTC reports at the navigation frequency. Data is accessed from the callback. - bool setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic TIMEUTC reports at the navigation frequency. Data is accessed from the callback. - bool assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and TIMEUTC is send cyclically already - void flushNAVTIMEUTC(); // Mark all the data as read/stale - void logNAVTIMEUTC(bool enabled = true); // Log data to file buffer - bool getNAVVELECEF(uint16_t maxWait = defaultMaxWait); // NAV VELECEF bool setAutoNAVVELECEF(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic VELECEF reports at the navigation frequency bool setAutoNAVVELECEF(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic VELECEF reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update @@ -1122,6 +1102,15 @@ class SFE_UBLOX_GNSS void flushNAVPVAT(); // Mark all the PVAT data as read/stale void logNAVPVAT(bool enabled = true); // Log data to file buffer + bool getNAVTIMEUTC(uint16_t maxWait = defaultMaxWait); // NAV TIMEUTC + bool setAutoNAVTIMEUTC(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency + bool setAutoNAVTIMEUTC(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic TIMEUTC reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update + bool setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic TIMEUTC reports + bool setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic TIMEUTC reports at the navigation frequency. Data is accessed from the callback. + bool assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and TIMEUTC is send cyclically already + void flushNAVTIMEUTC(); // Mark all the data as read/stale + void logNAVTIMEUTC(bool enabled = true); // Log data to file buffer + bool getNAVCLOCK(uint16_t maxWait = defaultMaxWait); // NAV CLOCK bool setAutoNAVCLOCK(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic clock reports at the navigation frequency bool setAutoNAVCLOCK(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic clock reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update @@ -1141,6 +1130,15 @@ class SFE_UBLOX_GNSS void flushNAVSVIN(); // Mark all the data as read/stale void logNAVSVIN(bool enabled = true); // Log data to file buffer + bool getNAVEOE(uint16_t maxWait = defaultMaxWait); // Query module for latest dilution of precision values and load global vars:. If autoEOE is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new EOE is available. + bool setAutoNAVEOE(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency + bool setAutoNAVEOE(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic EOE reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update + bool setAutoNAVEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic EOE reports + bool setAutoNAVEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait = defaultMaxWait); // Enable automatic EOE reports at the navigation frequency. Data is accessed from the callback. + bool assumeAutoNAVEOE(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and EOE is send cyclically already + void flushNAVEOE(); // Mark all the EOE data as read/stale + void logNAVEOE(bool enabled = true); // Log data to file buffer + // Add "auto" support for NAV TIMELS - to avoid needing 'global' storage bool getLeapSecondEvent(uint16_t maxWait = defaultMaxWait); // Reads leap second event info @@ -1623,22 +1621,22 @@ class SFE_UBLOX_GNSS bool initPacketUBXNAVPOSECEF(); // Allocate RAM for packetUBXNAVPOSECEF and initialize it bool initPacketUBXNAVSTATUS(); // Allocate RAM for packetUBXNAVSTATUS and initialize it bool initPacketUBXNAVDOP(); // Allocate RAM for packetUBXNAVDOP and initialize it - bool initPacketUBXNAVEOE(); // Allocate RAM for packetUBXNAVEOE and initialize it bool initPacketUBXNAVATT(); // Allocate RAM for packetUBXNAVATT and initialize it bool initPacketUBXNAVPVT(); // Allocate RAM for packetUBXNAVPVT and initialize it bool initPacketUBXNAVODO(); // Allocate RAM for packetUBXNAVODO and initialize it - bool initPacketUBXNAVTIMEUTC(); // Allocate RAM for packetUBXNAVTIMEUTC and initialize it bool initPacketUBXNAVVELECEF(); // Allocate RAM for packetUBXNAVVELECEF and initialize it bool initPacketUBXNAVVELNED(); // Allocate RAM for packetUBXNAVVELNED and initialize it bool initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it bool initPacketUBXNAVHPPOSLLH(); // Allocate RAM for packetUBXNAVHPPOSLLH and initialize it bool initPacketUBXNAVPVAT(); // Allocate RAM for packetUBXNAVPVAT and initialize it + bool initPacketUBXNAVTIMEUTC(); // Allocate RAM for packetUBXNAVTIMEUTC and initialize it bool initPacketUBXNAVCLOCK(); // Allocate RAM for packetUBXNAVCLOCK and initialize it bool initPacketUBXNAVTIMELS(); // Allocate RAM for packetUBXNAVTIMELS and initialize it bool initPacketUBXNAVSVIN(); // Allocate RAM for packetUBXNAVSVIN and initialize it bool initPacketUBXNAVSAT(); // Allocate RAM for packetUBXNAVSAT and initialize it bool initPacketUBXNAVRELPOSNED(); // Allocate RAM for packetUBXNAVRELPOSNED and initialize it bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it + bool initPacketUBXNAVEOE(); // Allocate RAM for packetUBXNAVEOE and initialize it bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index fcaff3f..ce2883b 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -298,38 +298,6 @@ typedef struct UBX_NAV_DOP_data_t *callbackData; } UBX_NAV_DOP_t; -// UBX-NAV-EOE (0x01 0x61): End of Epoch -const uint16_t UBX_NAV_EOE_LEN = 4; - -typedef struct -{ - uint32_t iTOW; // GPS time of week of the navigation epoch: ms -} UBX_NAV_EOE_data_t; - -typedef struct -{ - union - { - uint32_t all; - struct - { - uint32_t all : 1; - - uint32_t iTOW : 1; - } bits; - } moduleQueried; -} UBX_NAV_EOE_moduleQueried_t; - -typedef struct -{ - ubxAutomaticFlags automaticFlags; - UBX_NAV_EOE_data_t data; - UBX_NAV_EOE_moduleQueried_t moduleQueried; - void (*callbackPointer)(UBX_NAV_EOE_data_t); - void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *); - UBX_NAV_EOE_data_t *callbackData; -} UBX_NAV_EOE_t; - // UBX-NAV-ATT (0x01 0x05): Attitude solution const uint16_t UBX_NAV_ATT_LEN = 32; @@ -1031,7 +999,6 @@ typedef struct ubxAutomaticFlags automaticFlags; UBX_NAV_TIMEUTC_data_t data; UBX_NAV_TIMEUTC_moduleQueried_t moduleQueried; - void (*callbackPointer)(UBX_NAV_TIMEUTC_data_t); void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *); UBX_NAV_TIMEUTC_data_t *callbackData; } UBX_NAV_TIMEUTC_t; @@ -1415,6 +1382,37 @@ typedef struct UBX_NAV_AOPSTATUS_data_t *callbackData; } UBX_NAV_AOPSTATUS_t; +// UBX-NAV-EOE (0x01 0x61): End of Epoch +const uint16_t UBX_NAV_EOE_LEN = 4; + +typedef struct +{ + uint32_t iTOW; // GPS time of week of the navigation epoch: ms +} UBX_NAV_EOE_data_t; + +typedef struct +{ + union + { + uint32_t all; + struct + { + uint32_t all : 1; + + uint32_t iTOW : 1; + } bits; + } moduleQueried; +} UBX_NAV_EOE_moduleQueried_t; + +typedef struct +{ + ubxAutomaticFlags automaticFlags; + UBX_NAV_EOE_data_t data; + UBX_NAV_EOE_moduleQueried_t moduleQueried; + void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *); + UBX_NAV_EOE_data_t *callbackData; +} UBX_NAV_EOE_t; + // RXM-specific structs // UBX-RXM-SFRBX (0x02 0x13): Broadcast navigation data subframe From a5b1da4001cb87b9d6e287dc2208f6def4ac4204 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Sat, 2 Jul 2022 16:09:59 +0100 Subject: [PATCH 09/23] v2.2.12 --- README.md | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db53b78..e3a4f22 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ v2.1 of the library adds support for u-blox AssistNowTM Assisted GNSS This library is the new and improved version of the very popular SparkFun u-blox GNSS Arduino Library. v2.0 contains some big changes and improvements: * Seamless support for "automatic" message delivery: - * In v1.8, you could ask for the NAV PVT (Navigation Position Velocity Time) message to be delivered _automatically_, without polling. v2.0 adds automatic support for [**27 messages**](./Theory.md#auto-messages), covering the full range of: standard and High Precision position, velocity, attitude and time information; relative positioning; event capture with nanosecond time resolution; raw GNSS signal data including carrier phase; Sensor Fusion; and High Navigation Rate data. + * In v1.8, you could ask for the NAV PVT (Navigation Position Velocity Time) message to be delivered _automatically_, without polling. v2.0 adds automatic support for [**29 messages**](./Theory.md#auto-messages), covering the full range of: standard and High Precision position, velocity, attitude and time information; relative positioning; event capture with nanosecond time resolution; raw GNSS signal data including carrier phase; Sensor Fusion; and High Navigation Rate data. * Don't see the message you really need? [Adding_New_Messages](./Adding_New_Messages.md) provides details on how to add "auto" support for your favourite message. * Dynamic memory allocation with clearly-defined data storage structs for each message: * There are no static 'global' variables to eat up your RAM. v2.0 automatically allocates memory for the automatic messages when they are enabled. You may find your total RAM use is lower with v2.0 than with v1.8. diff --git a/library.properties b/library.properties index b598d40..7959456 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS Arduino Library -version=2.2.11 +version=2.2.12 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules

From 9d007c64176e470d72f6cd0c85b61fe8be40e710 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 19 Jul 2022 19:02:49 +0200 Subject: [PATCH 10/23] Add files via upload --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 179 ++++++++++++++++++- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 12 ++ src/u-blox_config_keys.h | 16 ++ src/u-blox_structs.h | 48 +++++ 4 files changed, 250 insertions(+), 5 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index d74ff8b..4a066d3 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -311,14 +311,14 @@ void SFE_UBLOX_GNSS::end(void) packetUBXRXMPMP = NULL; // Redundant? } - if (packetUBXRXMPMPmessage != NULL) + if (packetUBXRXMQZSSL6message != NULL) { - if (packetUBXRXMPMPmessage->callbackData != NULL) + if (packetUBXRXMQZSSL6message->callbackData != NULL) { - delete packetUBXRXMPMPmessage->callbackData; + delete packetUBXRXMQZSSL6message->callbackData; } - delete packetUBXRXMPMPmessage; - packetUBXRXMPMPmessage = NULL; // Redundant? + delete packetUBXRXMQZSSL6message; + packetUBXRXMQZSSL6message = NULL; // Redundant? } if (packetUBXRXMCOR != NULL) @@ -1370,6 +1370,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if ((packetUBXRXMPMP != NULL) || (packetUBXRXMPMPmessage != NULL)) result = true; break; + case UBX_RXM_QZSSL6: + if ((packetUBXRXMQZSSL6 != NULL) || (packetUBXRXMQZSSL6message != NULL)) + result = true; + break; case UBX_RXM_COR: if (packetUBXRXMCOR != NULL) result = true; @@ -1545,6 +1549,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_RXM_PMP: maxSize = UBX_RXM_PMP_MAX_LEN; break; + case UBX_RXM_QZSSL6: + maxSize = UBX_RXM_QZSSL6_MAX_LEN; + break; case UBX_RXM_COR: maxSize = UBX_RXM_COR_LEN; break; @@ -3830,6 +3837,55 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } } + if (msg->id == UBX_RXM_QZSSL6) + // Note: length is variable with version 0x01 + // Note: the field positions depend on the version + { + // Parse various byte fields into storage - but only if we have memory allocated for it. + // By default, new QZSSL6 data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). + // To prevent this, uncomment the line two lines below + if ((packetUBXRXMQZSSL6 != NULL) && (packetUBXRXMQZSSL6->callbackData != NULL) + //&& (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' + ) + { + packetUBXRXMQZSSL6->callbackData->version = extractByte(msg, 0); + packetUBXRXMQZSSL6->callbackData->svId = extractByte(msg, 1); + packetUBXRXMQZSSL6->callbackData->cno = extractInt(msg, 2); + packetUBXRXMQZSSL6->callbackData->timeTag = extractLong(msg, 4); + packetUBXRXMQZSSL6->callbackData->groupDelay = extractByte(msg, 8); + packetUBXRXMQZSSL6->callbackData->bitErrCorr = extractByte(msg, 9); + packetUBXRXMQZSSL6->callbackData->chInfo = extractInt(msg, 10); + packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 12); + packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 13); + for (uint16_t i = 0; (i < 250); i++) + { + packetUBXRXMQZSSL6->callbackData->msgBytes[i] = extractByte(msg, i + 14); + } + packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid + } + + // Full QZSSL6 message, including Class, ID and checksum + // By default, new QZSSL6 data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). + // To prevent this, uncomment the line two lines below + if ((packetUBXRXMQZSSL6message != NULL) && (packetUBXRXMQZSSL6message->callbackData != NULL) + //&& (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' + ) + { + packetUBXRXMQZSSL6message->callbackData->sync1 = UBX_SYNCH_1; + packetUBXRXMQZSSL6message->callbackData->sync2 = UBX_SYNCH_2; + packetUBXRXMQZSSL6message->callbackData->cls = UBX_CLASS_RXM; + packetUBXRXMQZSSL6message->callbackData->ID = UBX_RXM_QZSSL6; + packetUBXRXMQZSSL6message->callbackData->lengthLSB = msg->len & 0xFF; + packetUBXRXMQZSSL6message->callbackData->lengthMSB = msg->len >> 8; + + memcpy(packetUBXRXMQZSSL6message->callbackData->payload, msg->payload, msg->len); + + packetUBXRXMQZSSL6message->callbackData->checksumA = msg->checksumA; + packetUBXRXMQZSSL6message->callbackData->checksumB = msg->checksumB; + + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid + } + } else if (msg->id == UBX_RXM_COR) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -5444,6 +5500,28 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXRXMPMPmessage->callbackPointerPtr(packetUBXRXMPMPmessage->callbackData); // Call the callback packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + + if ((packetUBXRXMQZSSL6 != NULL) // If RAM has been allocated for message storage + && (packetUBXRXMQZSSL6->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXRXMQZSSL6->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + && (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6")); + packetUBXRXMQZSSL6->callbackPointerPtr(packetUBXRXMQZSSL6->callbackData); // Call the callback + packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + + if ((packetUBXRXMQZSSL6message != NULL) // If RAM has been allocated for message storage + && (packetUBXRXMQZSSL6message->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXRXMQZSSL6message->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + && (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6 message")); + packetUBXRXMQZSSL6message->callbackPointerPtr(packetUBXRXMQZSSL6message->callbackData); // Call the callback + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage && (packetUBXRXMCOR->callbackData != NULL) // If RAM has been allocated for the copy of the data @@ -12561,6 +12639,97 @@ bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage() return (true); } +// ***** RXM QZSSL6 automatic support + +// Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! +bool SFE_UBLOX_GNSS::setRXMQZSSL6callbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_data_t *)) +{ + if (packetUBXRXMQZSSL6 == NULL) + initPacketUBXRXMQZSSL6(); // Check that RAM has been allocated for the data + if (packetUBXRXMQZSSL6 == NULL) // Only attempt this if RAM allocation was successful + return false; + + if (packetUBXRXMQZSSL6->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXRXMQZSSL6->callbackData = new UBX_RXM_QZSSL6_data_t; // Allocate RAM for the main struct + } + + if (packetUBXRXMQZSSL6->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoRXMQZSSL6callbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXRXMQZSSL6->callbackPointerPtr = callbackPointer; + return (true); +} + +// PRIVATE: Allocate RAM for packetUBXRXMQZSSL6 and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXRXMQZSSL6() +{ + packetUBXRXMQZSSL6 = new UBX_RXM_QZSSL6_t; // Allocate RAM for the main struct + if (packetUBXRXMQZSSL6 == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXRXMQZSSL6: RAM alloc failed!")); +#endif + return (false); + } + packetUBXRXMQZSSL6->automaticFlags.flags.all = 0; + packetUBXRXMQZSSL6->callbackPointerPtr = NULL; + packetUBXRXMQZSSL6->callbackData = NULL; + return (true); +} + +// Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! +bool SFE_UBLOX_GNSS::setRXMQZSSL6messageCallbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_message_data_t *)) +{ + if (packetUBXRXMQZSSL6message == NULL) + initPacketUBXRXMQZSSL6message(); // Check that RAM has been allocated for the data + if (packetUBXRXMQZSSL6message == NULL) // Only attempt this if RAM allocation was successful + return false; + + if (packetUBXRXMQZSSL6message->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXRXMQZSSL6message->callbackData = new UBX_RXM_QZSSL6_message_data_t; // Allocate RAM for the main struct + } + + if (packetUBXRXMQZSSL6message->callbackData == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("setAutoRXMQZSSL6messagecallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXRXMQZSSL6message->callbackPointerPtr = callbackPointer; + return (true); +} + +// PRIVATE: Allocate RAM for packetUBXRXMQZSSL6message and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXRXMQZSSL6message() +{ + packetUBXRXMQZSSL6message = new UBX_RXM_QZSSL6_message_t; // Allocate RAM for the main struct + if (packetUBXRXMQZSSL6message == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXRXMQZSSL6message: RAM alloc failed!")); +#endif + return (false); + } + packetUBXRXMQZSSL6message->automaticFlags.flags.all = 0; + packetUBXRXMQZSSL6message->callbackPointerPtr = NULL; + packetUBXRXMQZSSL6message->callbackData = NULL; + return (true); +} + + bool SFE_UBLOX_GNSS::setRXMCORcallbackPtr(void (*callbackPointer)(UBX_RXM_COR_data_t *)) { if (packetUBXRXMCOR == NULL) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index a60ee2b..bad937c 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -397,6 +397,7 @@ const uint8_t UBX_NAV_AOPSTATUS = 0x60; // AssistNow Autonomous status const uint8_t UBX_RXM_COR = 0x34; // Differential correction input status const uint8_t UBX_RXM_MEASX = 0x14; // Satellite Measurements for RRLP const uint8_t UBX_RXM_PMP = 0x72; // PMP raw data (NEO-D9S) (two different versions) (packet size for version 0x01 is variable) +const uint8_t UBX_RXM_QZSSL6 = 0x73; // QZSSL6 data (NEO-D9C) const uint8_t UBX_RXM_PMREQ = 0x41; // Requests a Power Management task (two different packet sizes) const uint8_t UBX_RXM_RAWX = 0x15; // Multi-GNSS Raw Measurement Data const uint8_t UBX_RXM_RLM = 0x59; // Galileo SAR Short-RLM report (two different packet sizes) @@ -1163,6 +1164,13 @@ class SFE_UBLOX_GNSS bool setRXMPMPcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool setRXMPMPmessageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_message_data_t *)); // Use this if you want all of the PMP message (including sync chars, checksum, etc.) to push to a GNSS + // Configure a callback for the UBX-RXM-QZSSL6 messages produced by the NEO-D9C + // Note: on the NEO-D9C, the UBX-RXM-QZSSL6 messages are enabled by default on all ports. + // You can disable them by calling (e.g.) setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C, 0) + // The NEO-D9C does not support UBX-CFG-MSG + bool setRXMQZSSL6callbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! + bool setRXMQZSSL6messageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *)); // Use this if you want all of the QZSSL6 message (including sync chars, checksum, etc.) to push to a GNSS + bool setRXMCORcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_COR_data_t *)); // RXM COR bool getRXMSFRBX(uint16_t maxWait = defaultMaxWait); // RXM SFRBX @@ -1518,6 +1526,8 @@ class SFE_UBLOX_GNSS UBX_RXM_PMP_t *packetUBXRXMPMP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_PMP_message_t *packetUBXRXMPMPmessage = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_RXM_QZSSL6_t *packetUBXRXMQZSSL6 = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_RXM_QZSSL6_message_t *packetUBXRXMQZSSL6message = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_COR_t *packetUBXRXMCOR = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_SFRBX_t *packetUBXRXMSFRBX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_RAWX_t *packetUBXRXMRAWX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1617,6 +1627,8 @@ class SFE_UBLOX_GNSS bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it + bool initPacketUBXRXMQZSSL6(); // Allocate RAM for packetUBXRXMQZSSL6and initialize it + bool initPacketUBXRXMQZSSL6message(); // Allocate RAM for packetUBXRXMQZSSL6raw and initialize it bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it bool initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it bool initPacketUBXRXMRAWX(); // Allocate RAM for packetUBXRXMRAWX and initialize it diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index d8893c2..b1827e7 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -826,6 +826,14 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_UART1 = 0x20910323; // Output rate o const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_UART2 = 0x20910324; // Output rate of the UBX_MON_PMP message on port UART2 const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_USB = 0x20910325; // Output rate of the UBX_MON_PMP message on port USB +// Additional CFG_MSGOUT keys for the NEO-D9S +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x20910339; // Output rate of the UBX_RXM_QZSSL6 message on port I2C +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033a; // Output rate of the UBX_RXM_QZSSL6 message on port SPI +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART1 = 0x2091033b; // Output rate of the UBX_RXM_QZSSL6 message on port UART1 +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2 = 0x2091033c; // Output rate of the UBX_RXM_QZSSL6 message on port UART2 +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_USB = 0x2091033d; // Output rate of the UBX_RXM_QZSSL6 message on port USB + // CFG-NAV2: Secondary output configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const uint32_t UBLOX_CFG_NAV2_OUT_ENABLED = 0x10170001; // Enable secondary (NAV2) output @@ -928,6 +936,14 @@ const uint32_t UBLOX_CFG_PMP_DESCRAMBLER_INIT = 0x30b10015; // Descrambler init const uint32_t UBLOX_CFG_PMP_USE_PRESCRAMBLING = 0x10b10019; // Use prescrambling. Enables/disables the prescrambling. const uint32_t UBLOX_CFG_PMP_UNIQUE_WORD = 0x50b1001a; // Unique word. Defines value of unique word. +// CFG-QZSS-L6: QZSS system configuration configuration (NEO-D9C) +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const uint32_t UBLOX_CFG_QZSSL6_SVIDA = 0x20370020; // QZSS L6 SV Id to be decoded by channel A +const uint32_t UBLOX_CFG_QZSSL6_SVIDB = 0x20370030; // QZSS L6 SV Id to be decoded by channel B +const uint32_t UBLOX_CFG_QZSSL6_MSGA = 0x20370050; // QZSS L6 messages to be decoded by channel A +const uint32_t UBLOX_CFG_QZSSL6_MSGB = 0x20370060; // QZSS L6 messages to be decoded by channel B +const uint32_t UBLOX_CFG_QZSSL6_RSDECODER = 0x20370080; // QZSS L6 message Reed-Solomon decoder mode + // CFG-QZSS: QZSS system configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const uint32_t UBLOX_CFG_QZSS_USE_SLAS_DGNSS = 0x10370005; // Apply QZSS SLAS DGNSS corrections diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 019aebb..c194802 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1599,6 +1599,54 @@ typedef struct UBX_RXM_PMP_message_data_t *callbackData; } UBX_RXM_PMP_message_t; +// UBX-RXM-QZSSL6 (0x02 0x73): QZSS L6 raw data (D9C modules) +const uint16_t UBX_RXM_QZSSL6_MAX_USER_DATA = 250; +const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_PMP_MAX_USER_DATA + 14; + +typedef struct +{ + uint8_t version; // Message version (0x00 / 0x01) + uint8_t svId; // Satellite identifier + uint16_t cno; // Mean C/N0 + uint32_t timeTag; // Time since startup when frame started : ms + uint8_t groupDelay; // L6 group delay w.r.t. L2 on channel + uint8_t bitErrCorr; // Number of bit errors corrected by Reed-Solomon decoder + uint16_t chInfo; // Information about receiver channel associated with a received QZSS L6 message + uint8_t reserved0[2]; // Reserved + uint8_t msgBytes[UBX_RXM_QZSSL6_MAX_USER_DATA]; // Bytes in a QZSS L6 message +} UBX_RXM_QZSSL6_data_t; + +// The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. +typedef struct +{ + ubxAutomaticFlags automaticFlags; + void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *); + UBX_RXM_QZSSL6_data_t *callbackData; +} UBX_RXM_QZSSL6_t; + +// Define a struct to hold the entire QZSSL6 message so the whole thing can be pushed to a GNSS. +// Remember that the length of the payload could be variable (with version 1 messages). +typedef struct +{ + uint8_t sync1; // 0xB5 + uint8_t sync2; // 0x62 + uint8_t cls; + uint8_t ID; + uint8_t lengthLSB; + uint8_t lengthMSB; + uint8_t payload[UBX_RXM_QZSSL6_MAX_LEN]; + uint8_t checksumA; + uint8_t checksumB; +} UBX_RXM_QZSSL6_message_data_t; + +// The PMP data can only be accessed via a callback. QZSSL6 cannot be polled. +typedef struct +{ + ubxAutomaticFlags automaticFlags; + void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *); + UBX_RXM_QZSSL6_message_data_t *callbackData; +} UBX_RXM_QZSSL6_message_t; + // CFG-specific structs // UBX-CFG-PRT (0x06 0x00): Port configuration From 9817099446b1810ac3677e191600848366c7fb14 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 19 Jul 2022 19:07:45 +0200 Subject: [PATCH 11/23] fix pointers init --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 22 +++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 4a066d3..d4c510e 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -311,6 +311,26 @@ void SFE_UBLOX_GNSS::end(void) packetUBXRXMPMP = NULL; // Redundant? } + if (packetUBXRXMPMPmessage != NULL) + { + if (packetUBXRXMPMPmessage->callbackData != NULL) + { + delete packetUBXRXMPMPmessage->callbackData; + } + delete packetUBXRXMPMPmessage; + packetUBXRXMPMPmessage = NULL; // Redundant? + } + + if (packetUBXRXMQZSSL6 != NULL) + { + if (packetUBXRXMQZSSL6->callbackData != NULL) + { + delete packetUBXRXMPMP->callbackData; + } + delete packetUBXRXMQZSSL6; + packetUBXRXMQZSSL6 = NULL; // Redundant? + } + if (packetUBXRXMQZSSL6message != NULL) { if (packetUBXRXMQZSSL6message->callbackData != NULL) @@ -320,7 +340,7 @@ void SFE_UBLOX_GNSS::end(void) delete packetUBXRXMQZSSL6message; packetUBXRXMQZSSL6message = NULL; // Redundant? } - + if (packetUBXRXMCOR != NULL) { if (packetUBXRXMCOR->callbackData != NULL) From bbcdf2215389a41bfed6f5e245496048ce45c98c Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 19 Jul 2022 19:10:29 +0200 Subject: [PATCH 12/23] Fixing some PMP QZSSL6 typos --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 2 +- src/u-blox_structs.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index d4c510e..d097284 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -325,7 +325,7 @@ void SFE_UBLOX_GNSS::end(void) { if (packetUBXRXMQZSSL6->callbackData != NULL) { - delete packetUBXRXMPMP->callbackData; + delete packetUBXRXMQZSSL6->callbackData; } delete packetUBXRXMQZSSL6; packetUBXRXMQZSSL6 = NULL; // Redundant? diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index c194802..24763ff 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1601,7 +1601,7 @@ typedef struct // UBX-RXM-QZSSL6 (0x02 0x73): QZSS L6 raw data (D9C modules) const uint16_t UBX_RXM_QZSSL6_MAX_USER_DATA = 250; -const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_PMP_MAX_USER_DATA + 14; +const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_QZSSL6_MAX_USER_DATA + 14; typedef struct { @@ -1639,7 +1639,7 @@ typedef struct uint8_t checksumB; } UBX_RXM_QZSSL6_message_data_t; -// The PMP data can only be accessed via a callback. QZSSL6 cannot be polled. +// The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. typedef struct { ubxAutomaticFlags automaticFlags; From d4a1f16765b584d6c0031ed249709cafb78e1002 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Fri, 22 Jul 2022 09:04:36 +0200 Subject: [PATCH 13/23] Fix config value --- src/u-blox_config_keys.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index b1827e7..b23590b 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -828,8 +828,8 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_USB = 0x20910325; // Output rate o // Additional CFG_MSGOUT keys for the NEO-D9S //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x20910339; // Output rate of the UBX_RXM_QZSSL6 message on port I2C -const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033a; // Output rate of the UBX_RXM_QZSSL6 message on port SPI +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x2091033F; // Output rate of the UBX_RXM_QZSSL6 message on port I2C +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033E; // Output rate of the UBX_RXM_QZSSL6 message on port SPI const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART1 = 0x2091033b; // Output rate of the UBX_RXM_QZSSL6 message on port UART1 const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2 = 0x2091033c; // Output rate of the UBX_RXM_QZSSL6 message on port UART2 const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_USB = 0x2091033d; // Output rate of the UBX_RXM_QZSSL6 message on port USB From 8afafa6d01db274bbae6f6371795cb7d3a757bbf Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Fri, 22 Jul 2022 20:54:38 +0200 Subject: [PATCH 14/23] fix wrong config keys --- src/u-blox_config_keys.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index b23590b..b1589fa 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -828,8 +828,8 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_USB = 0x20910325; // Output rate o // Additional CFG_MSGOUT keys for the NEO-D9S //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x2091033F; // Output rate of the UBX_RXM_QZSSL6 message on port I2C -const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033E; // Output rate of the UBX_RXM_QZSSL6 message on port SPI +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x2091033f; // Output rate of the UBX_RXM_QZSSL6 message on port I2C +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033e; // Output rate of the UBX_RXM_QZSSL6 message on port SPI const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART1 = 0x2091033b; // Output rate of the UBX_RXM_QZSSL6 message on port UART1 const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2 = 0x2091033c; // Output rate of the UBX_RXM_QZSSL6 message on port UART2 const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_USB = 0x2091033d; // Output rate of the UBX_RXM_QZSSL6 message on port USB From 11b9cf40d4a3c5269f749c533b826c4a88b6aa76 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Sat, 23 Jul 2022 18:36:04 +0200 Subject: [PATCH 15/23] use of a define to allow make code dependent --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 2 +- src/u-blox_structs.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index d097284..df68993 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -3877,7 +3877,7 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) packetUBXRXMQZSSL6->callbackData->chInfo = extractInt(msg, 10); packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 12); packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 13); - for (uint16_t i = 0; (i < 250); i++) + for (uint16_t i = 0; (i < UBX_RXM_QZSSL6_DATALEN); i++) { packetUBXRXMQZSSL6->callbackData->msgBytes[i] = extractByte(msg, i + 14); } diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 24763ff..d9e4a3c 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1600,8 +1600,8 @@ typedef struct } UBX_RXM_PMP_message_t; // UBX-RXM-QZSSL6 (0x02 0x73): QZSS L6 raw data (D9C modules) -const uint16_t UBX_RXM_QZSSL6_MAX_USER_DATA = 250; -const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_QZSSL6_MAX_USER_DATA + 14; +#define UBX_RXM_QZSSL6_DATALEN 250 +const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_QZSSL6_DATALEN + 14; typedef struct { @@ -1613,7 +1613,7 @@ typedef struct uint8_t bitErrCorr; // Number of bit errors corrected by Reed-Solomon decoder uint16_t chInfo; // Information about receiver channel associated with a received QZSS L6 message uint8_t reserved0[2]; // Reserved - uint8_t msgBytes[UBX_RXM_QZSSL6_MAX_USER_DATA]; // Bytes in a QZSS L6 message + uint8_t msgBytes[UBX_RXM_QZSSL6_DATALEN]; // Bytes in a QZSS L6 message } UBX_RXM_QZSSL6_data_t; // The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. From 7014e5bd464739c71b480d1f6fdf1b89d8e25857 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 26 Jul 2022 10:07:19 +0200 Subject: [PATCH 16/23] store multiple RXM-QZSSL6 messages --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 147 ++++--------------- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 2 - src/u-blox_structs.h | 22 ++- 3 files changed, 45 insertions(+), 126 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index df68993..1bd117c 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -321,21 +321,11 @@ void SFE_UBLOX_GNSS::end(void) packetUBXRXMPMPmessage = NULL; // Redundant? } - if (packetUBXRXMQZSSL6 != NULL) - { - if (packetUBXRXMQZSSL6->callbackData != NULL) - { - delete packetUBXRXMQZSSL6->callbackData; - } - delete packetUBXRXMQZSSL6; - packetUBXRXMQZSSL6 = NULL; // Redundant? - } - if (packetUBXRXMQZSSL6message != NULL) { if (packetUBXRXMQZSSL6message->callbackData != NULL) { - delete packetUBXRXMQZSSL6message->callbackData; + delete [] packetUBXRXMQZSSL6message->callbackData; } delete packetUBXRXMQZSSL6message; packetUBXRXMQZSSL6message = NULL; // Redundant? @@ -1391,7 +1381,7 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) result = true; break; case UBX_RXM_QZSSL6: - if ((packetUBXRXMQZSSL6 != NULL) || (packetUBXRXMQZSSL6message != NULL)) + if (packetUBXRXMQZSSL6message != NULL) result = true; break; case UBX_RXM_COR: @@ -3861,50 +3851,26 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) // Note: length is variable with version 0x01 // Note: the field positions depend on the version { - // Parse various byte fields into storage - but only if we have memory allocated for it. - // By default, new QZSSL6 data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). - // To prevent this, uncomment the line two lines below - if ((packetUBXRXMQZSSL6 != NULL) && (packetUBXRXMQZSSL6->callbackData != NULL) - //&& (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' - ) - { - packetUBXRXMQZSSL6->callbackData->version = extractByte(msg, 0); - packetUBXRXMQZSSL6->callbackData->svId = extractByte(msg, 1); - packetUBXRXMQZSSL6->callbackData->cno = extractInt(msg, 2); - packetUBXRXMQZSSL6->callbackData->timeTag = extractLong(msg, 4); - packetUBXRXMQZSSL6->callbackData->groupDelay = extractByte(msg, 8); - packetUBXRXMQZSSL6->callbackData->bitErrCorr = extractByte(msg, 9); - packetUBXRXMQZSSL6->callbackData->chInfo = extractInt(msg, 10); - packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 12); - packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 13); - for (uint16_t i = 0; (i < UBX_RXM_QZSSL6_DATALEN); i++) - { - packetUBXRXMQZSSL6->callbackData->msgBytes[i] = extractByte(msg, i + 14); - } - packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid - } + // Full QZSSL6 message, including Class, ID and checksum + for (int ch = 0; ch < UBX_RXM_QZSSL6_NUM_CHANNELS; ch ++) { + if (0 == (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1<callbackData[ch].sync1 = UBX_SYNCH_1; + packetUBXRXMQZSSL6message->callbackData[ch].sync2 = UBX_SYNCH_2; + packetUBXRXMQZSSL6message->callbackData[ch].cls = UBX_CLASS_RXM; + packetUBXRXMQZSSL6message->callbackData[ch].ID = UBX_RXM_QZSSL6; + packetUBXRXMQZSSL6message->callbackData[ch].lengthLSB = msg->len & 0xFF; + packetUBXRXMQZSSL6message->callbackData[ch].lengthMSB = msg->len >> 8; - // Full QZSSL6 message, including Class, ID and checksum - // By default, new QZSSL6 data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). - // To prevent this, uncomment the line two lines below - if ((packetUBXRXMQZSSL6message != NULL) && (packetUBXRXMQZSSL6message->callbackData != NULL) - //&& (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' - ) - { - packetUBXRXMQZSSL6message->callbackData->sync1 = UBX_SYNCH_1; - packetUBXRXMQZSSL6message->callbackData->sync2 = UBX_SYNCH_2; - packetUBXRXMQZSSL6message->callbackData->cls = UBX_CLASS_RXM; - packetUBXRXMQZSSL6message->callbackData->ID = UBX_RXM_QZSSL6; - packetUBXRXMQZSSL6message->callbackData->lengthLSB = msg->len & 0xFF; - packetUBXRXMQZSSL6message->callbackData->lengthMSB = msg->len >> 8; - - memcpy(packetUBXRXMQZSSL6message->callbackData->payload, msg->payload, msg->len); + memcpy(packetUBXRXMQZSSL6message->callbackData[ch].payload, msg->payload, msg->len); - packetUBXRXMQZSSL6message->callbackData->checksumA = msg->checksumA; - packetUBXRXMQZSSL6message->callbackData->checksumB = msg->checksumB; + packetUBXRXMQZSSL6message->callbackData[ch].checksumA = msg->checksumA; + packetUBXRXMQZSSL6message->callbackData[ch].checksumB = msg->checksumB; - packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid - } + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid |= (1 << ch); + break; // abort when added + } + } } else if (msg->id == UBX_RXM_COR) { @@ -5521,26 +5487,17 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } - if ((packetUBXRXMQZSSL6 != NULL) // If RAM has been allocated for message storage - && (packetUBXRXMQZSSL6->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXRXMQZSSL6->callbackPointerPtr != NULL) // If the pointer to the callback has been defined - && (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + if ((packetUBXRXMQZSSL6message != NULL) && // If RAM has been allocated for message storage + (packetUBXRXMQZSSL6message->callbackData != NULL) && // If RAM has been allocated for the copy of the data + (packetUBXRXMQZSSL6message->callbackPointerPtr != NULL)) // If the pointer to the callback has been defined { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6")); - packetUBXRXMQZSSL6->callbackPointerPtr(packetUBXRXMQZSSL6->callbackData); // Call the callback - packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale - } - - if ((packetUBXRXMQZSSL6message != NULL) // If RAM has been allocated for message storage - && (packetUBXRXMQZSSL6message->callbackData != NULL) // If RAM has been allocated for the copy of the data - && (packetUBXRXMQZSSL6message->callbackPointerPtr != NULL) // If the pointer to the callback has been defined - && (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid - { - // if (_printDebug == true) - // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6 message")); - packetUBXRXMQZSSL6message->callbackPointerPtr(packetUBXRXMQZSSL6message->callbackData); // Call the callback - packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + for (int ch = 0; ch < UBX_RXM_QZSSL6_NUM_CHANNELS; ch ++) { + if (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1 << ch)) // If the copy of the data is valid + { + packetUBXRXMQZSSL6message->callbackPointerPtr( &packetUBXRXMQZSSL6message->callbackData[ch] ); // Call the callback + } + } + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage @@ -12661,50 +12618,6 @@ bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage() // ***** RXM QZSSL6 automatic support -// Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! -bool SFE_UBLOX_GNSS::setRXMQZSSL6callbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_data_t *)) -{ - if (packetUBXRXMQZSSL6 == NULL) - initPacketUBXRXMQZSSL6(); // Check that RAM has been allocated for the data - if (packetUBXRXMQZSSL6 == NULL) // Only attempt this if RAM allocation was successful - return false; - - if (packetUBXRXMQZSSL6->callbackData == NULL) // Check if RAM has been allocated for the callback copy - { - packetUBXRXMQZSSL6->callbackData = new UBX_RXM_QZSSL6_data_t; // Allocate RAM for the main struct - } - - if (packetUBXRXMQZSSL6->callbackData == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("setAutoRXMQZSSL6callbackPtr: RAM alloc failed!")); -#endif - return (false); - } - - packetUBXRXMQZSSL6->callbackPointerPtr = callbackPointer; - return (true); -} - -// PRIVATE: Allocate RAM for packetUBXRXMQZSSL6 and initialize it -bool SFE_UBLOX_GNSS::initPacketUBXRXMQZSSL6() -{ - packetUBXRXMQZSSL6 = new UBX_RXM_QZSSL6_t; // Allocate RAM for the main struct - if (packetUBXRXMQZSSL6 == NULL) - { -#ifndef SFE_UBLOX_REDUCED_PROG_MEM - if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging - _debugSerial->println(F("initPacketUBXRXMQZSSL6: RAM alloc failed!")); -#endif - return (false); - } - packetUBXRXMQZSSL6->automaticFlags.flags.all = 0; - packetUBXRXMQZSSL6->callbackPointerPtr = NULL; - packetUBXRXMQZSSL6->callbackData = NULL; - return (true); -} - // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool SFE_UBLOX_GNSS::setRXMQZSSL6messageCallbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_message_data_t *)) { @@ -12715,7 +12628,7 @@ bool SFE_UBLOX_GNSS::setRXMQZSSL6messageCallbackPtr(void (*callbackPointer)(UBX_ if (packetUBXRXMQZSSL6message->callbackData == NULL) // Check if RAM has been allocated for the callback copy { - packetUBXRXMQZSSL6message->callbackData = new UBX_RXM_QZSSL6_message_data_t; // Allocate RAM for the main struct + packetUBXRXMQZSSL6message->callbackData = new UBX_RXM_QZSSL6_message_data_t[UBX_RXM_QZSSL6_NUM_CHANNELS]; // Allocate RAM for the main struct } if (packetUBXRXMQZSSL6message->callbackData == NULL) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index bad937c..d3ff385 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1168,7 +1168,6 @@ class SFE_UBLOX_GNSS // Note: on the NEO-D9C, the UBX-RXM-QZSSL6 messages are enabled by default on all ports. // You can disable them by calling (e.g.) setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C, 0) // The NEO-D9C does not support UBX-CFG-MSG - bool setRXMQZSSL6callbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool setRXMQZSSL6messageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *)); // Use this if you want all of the QZSSL6 message (including sync chars, checksum, etc.) to push to a GNSS bool setRXMCORcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_COR_data_t *)); // RXM COR @@ -1526,7 +1525,6 @@ class SFE_UBLOX_GNSS UBX_RXM_PMP_t *packetUBXRXMPMP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_PMP_message_t *packetUBXRXMPMPmessage = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary - UBX_RXM_QZSSL6_t *packetUBXRXMQZSSL6 = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_QZSSL6_message_t *packetUBXRXMQZSSL6message = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_COR_t *packetUBXRXMCOR = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_SFRBX_t *packetUBXRXMSFRBX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index d9e4a3c..d427611 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1600,7 +1600,8 @@ typedef struct } UBX_RXM_PMP_message_t; // UBX-RXM-QZSSL6 (0x02 0x73): QZSS L6 raw data (D9C modules) -#define UBX_RXM_QZSSL6_DATALEN 250 +#define UBX_RXM_QZSSL6_NUM_CHANNELS 2 +const uint16_t UBX_RXM_QZSSL6_DATALEN = 250; const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_QZSSL6_DATALEN + 14; typedef struct @@ -1616,13 +1617,20 @@ typedef struct uint8_t msgBytes[UBX_RXM_QZSSL6_DATALEN]; // Bytes in a QZSS L6 message } UBX_RXM_QZSSL6_data_t; -// The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. -typedef struct +struct ubxQZSSL6AutomaticFlags { - ubxAutomaticFlags automaticFlags; - void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *); - UBX_RXM_QZSSL6_data_t *callbackData; -} UBX_RXM_QZSSL6_t; + union + { + uint8_t all; + struct + { + uint8_t automatic : 1; // Will this message be delivered and parsed "automatically" (without polling) + uint8_t implicitUpdate : 1; // Is the update triggered by accessing stale data (=true) or by a call to checkUblox (=false) + uint8_t addToFileBuffer : 1; // Should the raw UBX data be added to the file buffer? + uint8_t callbackCopyValid : UBX_RXM_QZSSL6_NUM_CHANNELS; // Is the copies of the data structs used by the callback valid/fresh? + } bits; + } flags; +}; // Define a struct to hold the entire QZSSL6 message so the whole thing can be pushed to a GNSS. // Remember that the length of the payload could be variable (with version 1 messages). From a7f135acee9514dd74d078aeaee8d6d661da279c Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 26 Jul 2022 10:33:31 +0200 Subject: [PATCH 17/23] Update SparkFun_u-blox_GNSS_Arduino_Library.h --- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index d3ff385..58e705b 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1625,7 +1625,6 @@ class SFE_UBLOX_GNSS bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it - bool initPacketUBXRXMQZSSL6(); // Allocate RAM for packetUBXRXMQZSSL6and initialize it bool initPacketUBXRXMQZSSL6message(); // Allocate RAM for packetUBXRXMQZSSL6raw and initialize it bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it bool initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it From f9b408ec9a3c5ac89a6369898d0f003abf355d71 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Thu, 28 Jul 2022 10:27:42 +0200 Subject: [PATCH 18/23] fixed automatic flags for multiple channels --- src/u-blox_structs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index d427611..3ddd0a0 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1650,7 +1650,7 @@ typedef struct // The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. typedef struct { - ubxAutomaticFlags automaticFlags; + ubxQZSSL6AutomaticFlags automaticFlags; void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *); UBX_RXM_QZSSL6_message_data_t *callbackData; } UBX_RXM_QZSSL6_message_t; From 1e5c02cddd18deb6a9fdea974035651966472e1d Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Thu, 28 Jul 2022 13:36:35 +0200 Subject: [PATCH 19/23] make sure we have latest versions --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 1bd117c..606da94 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -3853,7 +3853,7 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) { // Full QZSSL6 message, including Class, ID and checksum for (int ch = 0; ch < UBX_RXM_QZSSL6_NUM_CHANNELS; ch ++) { - if (0 == (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1<automaticFlags.flags.bits.callbackCopyValid & (1 << ch))) { packetUBXRXMQZSSL6message->callbackData[ch].sync1 = UBX_SYNCH_1; packetUBXRXMQZSSL6message->callbackData[ch].sync2 = UBX_SYNCH_2; @@ -5495,9 +5495,9 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) if (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1 << ch)) // If the copy of the data is valid { packetUBXRXMQZSSL6message->callbackPointerPtr( &packetUBXRXMQZSSL6message->callbackData[ch] ); // Call the callback + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid &= ~(1 << ch); // clear it } } - packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage From fc5c231b0898a539946867aeb409a47146a4a4ab Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 29 Jul 2022 08:50:43 +0100 Subject: [PATCH 20/23] Fix error in Example20 --- .../Example20_SendCustomCommand/Example20_SendCustomCommand.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Example20_SendCustomCommand/Example20_SendCustomCommand.ino b/examples/Example20_SendCustomCommand/Example20_SendCustomCommand.ino index b468f93..003f801 100644 --- a/examples/Example20_SendCustomCommand/Example20_SendCustomCommand.ino +++ b/examples/Example20_SendCustomCommand/Example20_SendCustomCommand.ino @@ -167,6 +167,6 @@ void loop() uint16_t milliseconds = myGNSS.getMillisecond(); Serial.print(F(" Milliseconds: ")); - Serial.print(altitude); + Serial.print(milliseconds); Serial.println(); } From 152364a0dc78ca4125da81f94d57862609ab6675 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 29 Jul 2022 09:19:38 +0100 Subject: [PATCH 21/23] Update u-blox_config_keys_sorted.txt --- ...ceDescription_UBX-21031777_keys_sorted.txt | 37 +++++++++++++++++++ keys/u-blox_config_keys_sorted.txt | 10 +++++ 2 files changed, 47 insertions(+) create mode 100644 keys/u-blox-D9-QZS-1.01_InterfaceDescription_UBX-21031777_keys_sorted.txt diff --git a/keys/u-blox-D9-QZS-1.01_InterfaceDescription_UBX-21031777_keys_sorted.txt b/keys/u-blox-D9-QZS-1.01_InterfaceDescription_UBX-21031777_keys_sorted.txt new file mode 100644 index 0000000..3ca6e9b --- /dev/null +++ b/keys/u-blox-D9-QZS-1.01_InterfaceDescription_UBX-21031777_keys_sorted.txt @@ -0,0 +1,37 @@ +0x10520005 +0x10650001 +0x10650002 +0x10730001 +0x10730003 +0x10740001 +0x10770001 +0x10770003 +0x10780001 +0x20370020 +0x20370030 +0x20370050 +0x20370060 +0x20370080 +0x20520002 +0x20520003 +0x20520004 +0x2091033b +0x2091033d +0x20920002 +0x20920004 +0x3065000a +0x3065000b +0x3065000c +0x40520001 +0x5065000d +0x5065000e +0x5065000f +0x50650010 +0x50650011 +0x50650012 +0x50650013 +0x50650014 +0x50650015 +0x50650016 +0x50650017 +0x50650018 diff --git a/keys/u-blox_config_keys_sorted.txt b/keys/u-blox_config_keys_sorted.txt index e6cc655..0e4a7a9 100644 --- a/keys/u-blox_config_keys_sorted.txt +++ b/keys/u-blox_config_keys_sorted.txt @@ -198,6 +198,11 @@ 0x20240013 0x20240014 0x20250038 +0x20370020 +0x20370030 +0x20370050 +0x20370060 +0x20370080 0x20410001 0x20410002 0x20410010 @@ -584,6 +589,11 @@ 0x20910338 0x20910339 0x2091033a +0x2091033b +0x2091033c +0x2091033d +0x2091033e +0x2091033f 0x20910345 0x20910346 0x20910347 From ff2bbe5b358fb96c4f105874ac104dd512c11521 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 29 Jul 2022 09:35:47 +0100 Subject: [PATCH 22/23] Update keywords.txt --- keywords.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/keywords.txt b/keywords.txt index c261965..206b751 100644 --- a/keywords.txt +++ b/keywords.txt @@ -52,6 +52,7 @@ UBX_RXM_PMP_message_data_t KEYWORD1 UBX_RXM_COR_data_t KEYWORD1 UBX_RXM_SFRBX_data_t KEYWORD1 UBX_RXM_RAWX_data_t KEYWORD1 +UBX_RXM_QZSSL6_message_data_t KEYWORD1 UBX_TIM_TM2_data_t KEYWORD1 @@ -412,6 +413,8 @@ logNAVEOE KEYWORD2 setRXMPMPcallbackPtr KEYWORD2 setRXMPMPmessageCallbackPtr KEYWORD2 +setRXMQZSSL6messageCallbackPtr KEYWORD2 + setRXMCORcallbackPtr KEYWORD2 getRXMSFRBX KEYWORD2 @@ -787,6 +790,7 @@ UBX_RXM_COR LITERAL1 UBX_RXM_RAWX LITERAL1 UBX_RXM_SFRBX LITERAL1 UBX_RXM_SPARTN LITERAL1 +UBX_RXM_QZSSL6 LITERAL1 UBX_TIM_TM2 LITERAL1 From e8b03e770f757f709dec2df284938be576c140a9 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Fri, 29 Jul 2022 09:39:26 +0100 Subject: [PATCH 23/23] Add missing else - will speed up execution a _tiny_ bit! ;-) --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 00ee008..1b50c19 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -3940,7 +3940,7 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } } - if (msg->id == UBX_RXM_QZSSL6) + else if (msg->id == UBX_RXM_QZSSL6) // Note: length is variable with version 0x01 // Note: the field positions depend on the version {