From 75ea5d4db87f18c312979230f3c9dbfb52bedc0b Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Sun, 8 May 2022 18:53:14 +0200 Subject: [PATCH 01/13] 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/13] 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/13] 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 9d007c64176e470d72f6cd0c85b61fe8be40e710 Mon Sep 17 00:00:00 2001 From: Michael Ammann Date: Tue, 19 Jul 2022 19:02:49 +0200 Subject: [PATCH 04/13] 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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 09/13] 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 10/13] 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 11/13] 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 12/13] 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 13/13] 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