From 42d3d78fb79775ac2329d66e1d8735c094101bbc Mon Sep 17 00:00:00 2001 From: David Taylor Date: Fri, 18 Aug 2023 11:01:48 +1000 Subject: [PATCH 1/5] Fixed a couple of memory bugs related to URC processing. Enhanced support for MQTT. Added support for FTP. Added URC string constants. Added getFileBlock method. --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 599 +++++++++++++++--- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 138 +++- 2 files changed, 615 insertions(+), 122 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 78ee353..eb4fa21 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -96,7 +96,7 @@ bool SARA_R5::begin(SoftwareSerial &softSerial, unsigned long baud) } } memset(_pruneBuffer, 0, _RXBuffSize); - + if (nullptr == _saraResponseBacklog) { _saraResponseBacklog = new char[_RXBuffSize]; @@ -147,7 +147,7 @@ bool SARA_R5::begin(HardwareSerial &hardSerial, unsigned long baud) } } memset(_pruneBuffer, 0, _RXBuffSize); - + if (nullptr == _saraResponseBacklog) { _saraResponseBacklog = new char[_RXBuffSize]; @@ -308,8 +308,10 @@ bool SARA_R5::bufferedPoll(void) } } - free(event); - + // This is a bug. event could be nullptr, and even if it is not it is pointing into + // _saraRXBuffer meaning it is calling free on a random pointer whose memory + // may be referenced later. + //free(event); _bufferedPollReentrant = false; return handled; @@ -320,11 +322,11 @@ bool SARA_R5::processURCEvent(const char *event) { { // URC: +UUSORD (Read Socket Data) int socket, length; - char *searchPtr = strstr(event, "+UUSORD:"); + char *searchPtr = strstr(event, SARA_R5_READ_SOCKET_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUSORD:"); // Move searchPtr to first character - probably a space - while (*searchPtr == ' ') searchPtr++; // skip spaces + searchPtr += strlen(SARA_R5_READ_SOCKET_URC); // Move searchPtr to first character - probably a space + while (*searchPtr == ' ') searchPtr++; // Skip spaces int ret = sscanf(searchPtr, "%d,%d", &socket, &length); if (ret == 2) { @@ -351,10 +353,10 @@ bool SARA_R5::processURCEvent(const char *event) } { // URC: +UUSORF (Receive From command (UDP only)) int socket, length; - char *searchPtr = strstr(event, "+UUSORF:"); + char *searchPtr = strstr(event, SARA_R5_READ_UDP_SOCKET_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUSORF:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_READ_UDP_SOCKET_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces int ret = sscanf(searchPtr, "%d,%d", &socket, &length); if (ret == 2) @@ -376,10 +378,10 @@ bool SARA_R5::processURCEvent(const char *event) int remoteIPstore[4] = {0,0,0,0}; int localIPstore[4] = {0,0,0,0}; - char *searchPtr = strstr(event, "+UUSOLI:"); + char *searchPtr = strstr(event, SARA_R5_LISTEN_SOCKET_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUSOLI:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_LISTEN_SOCKET_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces int ret = sscanf(searchPtr, "%d,\"%d.%d.%d.%d\",%u,%d,\"%d.%d.%d.%d\",%u", @@ -406,10 +408,10 @@ bool SARA_R5::processURCEvent(const char *event) } { // URC: +UUSOCL (Close Socket) int socket; - char *searchPtr = strstr(event, "+UUSOCL:"); + char *searchPtr = strstr(event, SARA_R5_CLOSE_SOCKET_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUSOCL:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_CLOSE_SOCKET_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces int ret = sscanf(searchPtr, "%d", &socket); if (ret == 1) @@ -441,10 +443,10 @@ bool SARA_R5::processURCEvent(const char *event) // 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 - char *searchPtr = strstr(event, "+UULOC:"); + char *searchPtr = strstr(event, SARA_R5_GNSS_REQUEST_LOCATION_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UULOC:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_GNSS_REQUEST_LOCATION_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces scanNum = sscanf(searchPtr, "%d/%d/%d,%d:%d:%d.%d,%d.%[^,],%d.%[^,],%d,%lu,%u,%u,%*s", @@ -509,10 +511,10 @@ bool SARA_R5::processURCEvent(const char *event) int scanNum; int stateStore; - char *searchPtr = strstr(event, "+UUSIMSTAT:"); + char *searchPtr = strstr(event, SARA_R5_SIM_STATE_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUSIMSTAT:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_SIM_STATE_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces scanNum = sscanf(searchPtr, "%d", &stateStore); @@ -538,10 +540,10 @@ bool SARA_R5::processURCEvent(const char *event) int scanNum; int remoteIPstore[4]; - char *searchPtr = strstr(event, "+UUPSDA:"); + char *searchPtr = strstr(event, SARA_R5_MESSAGE_PDP_ACTION_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUPSDA:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_MESSAGE_PDP_ACTION_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces scanNum = sscanf(searchPtr, "%d,\"%d.%d.%d.%d\"", &result, &remoteIPstore[0], &remoteIPstore[1], &remoteIPstore[2], &remoteIPstore[3]); @@ -569,10 +571,10 @@ bool SARA_R5::processURCEvent(const char *event) int profile, command, result; int scanNum; - char *searchPtr = strstr(event, "+UUHTTPCR:"); + char *searchPtr = strstr(event, SARA_R5_HTTP_COMMAND_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUHTTPCR:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_HTTP_COMMAND_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces scanNum = sscanf(searchPtr, "%d,%d,%d", &profile, &command, &result); @@ -594,18 +596,24 @@ bool SARA_R5::processURCEvent(const char *event) } } { // URC: +UUMQTTC (MQTT Command Result) - int command, result; + SARA_R5_mqtt_command_opcode_t mqttCmd; + int mqttResult; int scanNum; int qos = -1; String topic; - char *searchPtr = strstr(event, "+UUMQTTC:"); + char *searchPtr = strstr(event, SARA_R5_MQTT_COMMAND_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUMQTTC:"); // Move searchPtr to first character - probably a space - while (*searchPtr == ' ') searchPtr++; // skip spaces - scanNum = sscanf(searchPtr, "%d,%d", &command, &result); - if ((scanNum == 2) && (command == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) { + searchPtr += strlen(SARA_R5_MQTT_COMMAND_URC); // Move searchPtr to first character - probably a space + while (*searchPtr == ' ') + { + searchPtr++; // skip spaces + } + + scanNum = sscanf(searchPtr, "%d,%d", &mqttCmd, &mqttResult); + if ((scanNum == 2) && (mqttCmd == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) + { char topicC[100] = ""; scanNum = sscanf(searchPtr, "%*d,%*d,%d,\"%[^\"]\"", &qos, topicC); topic = topicC; @@ -613,13 +621,36 @@ bool SARA_R5::processURCEvent(const char *event) if ((scanNum == 2) || (scanNum == 4)) { if (_printDebug == true) + { _debugPort->println(F("processReadEvent: MQTT command result")); + } if (_mqttCommandRequestCallback != nullptr) { - _mqttCommandRequestCallback(command, result); + _mqttCommandRequestCallback(mqttCmd, mqttResult); } - + + return true; + } + } + } + { // URC: +UUFTPCR (FTP Command Result) + SARA_R5_ftp_command_opcode_t ftpCmd; + int ftpResult; + int scanNum; + char *searchPtr = strstr(event, SARA_R5_FTP_COMMAND_URC); + if (searchPtr != nullptr) + { + searchPtr += strlen(SARA_R5_FTP_COMMAND_URC); // Move searchPtr to first character - probably a space + while (*searchPtr == ' ') + { + searchPtr++; // skip spaces + } + + scanNum = sscanf(searchPtr, "%d,%d", &ftpCmd, &ftpResult); + if (scanNum == 2 && _ftpCommandRequestCallback != nullptr) + { + _ftpCommandRequestCallback(ftpCmd, ftpResult); return true; } } @@ -634,10 +665,10 @@ bool SARA_R5::processURCEvent(const char *event) int scanNum; // Try to extract the UUPING retries and payload size - char *searchPtr = strstr(event, "+UUPING:"); + char *searchPtr = strstr(event, SARA_R5_PING_COMMAND_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+UUPING:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_PING_COMMAND_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces scanNum = sscanf(searchPtr, "%d,%d,", &retry, &p_size); @@ -681,10 +712,10 @@ bool SARA_R5::processURCEvent(const char *event) { // URC: +CREG int status = 0; unsigned int lac = 0, ci = 0, Act = 0; - char *searchPtr = strstr(event, "+CREG:"); + char *searchPtr = strstr(event, SARA_R5_REGISTRATION_STATUS_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+CREG:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_REGISTRATION_STATUS_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces int scanNum = sscanf(searchPtr, "%d,\"%4x\",\"%4x\",%d", &status, &lac, &ci, &Act); if (scanNum == 4) @@ -696,7 +727,7 @@ bool SARA_R5::processURCEvent(const char *event) { _registrationCallback((SARA_R5_registration_status_t)status, lac, ci, Act); } - + return true; } } @@ -704,10 +735,10 @@ bool SARA_R5::processURCEvent(const char *event) { // URC: +CEREG int status = 0; unsigned int tac = 0, ci = 0, Act = 0; - char *searchPtr = strstr(event, "+CEREG:"); + char *searchPtr = strstr(event, SARA_R5_EPSREGISTRATION_STATUS_URC); if (searchPtr != nullptr) { - searchPtr += strlen("+CEREG:"); // Move searchPtr to first character - probably a space + searchPtr += strlen(SARA_R5_EPSREGISTRATION_STATUS_URC); // Move searchPtr to first character - probably a space while (*searchPtr == ' ') searchPtr++; // skip spaces int scanNum = sscanf(searchPtr, "%d,\"%4x\",\"%4x\",%d", &status, &tac, &ci, &Act); if (scanNum == 4) @@ -719,13 +750,13 @@ bool SARA_R5::processURCEvent(const char *event) { _epsRegistrationCallback((SARA_R5_registration_status_t)status, tac, ci, Act); } - + return true; } } } // NOTE: When adding new URC messages, remember to update pruneBacklog too! - + return false; } @@ -827,15 +858,20 @@ void SARA_R5::setHTTPCommandCallback(void (*httpCommandRequestCallback)(int prof _httpCommandRequestCallback = httpCommandRequestCallback; } -void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)) +void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(SARA_R5_mqtt_command_opcode_t command, int result)) { _mqttCommandRequestCallback = mqttCommandRequestCallback; } +void SARA_R5::setFTPCommandCallback(void (*ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t command, int result)) +{ + _ftpCommandRequestCallback = ftpCommandRequestCallback; +} + 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 == nullptr) return SARA_R5_ERROR_OUT_OF_MEMORY; @@ -878,7 +914,7 @@ size_t SARA_R5::write(const char *buffer, size_t size) SARA_R5_error_t SARA_R5::at(void) { SARA_R5_error_t err; - + err = sendCommandWithResponse(nullptr, SARA_R5_RESPONSE_OK, nullptr, SARA_R5_STANDARD_RESPONSE_TIMEOUT); @@ -1580,6 +1616,55 @@ int8_t SARA_R5::rssi(void) return rssi; } +SARA_R5_error_t SARA_R5::getExtSignalQuality(signal_quality& signal_quality) +{ + char *command; + char *response; + SARA_R5_error_t err; + + command = sara_r5_calloc_char(strlen(SARA_R5_EXT_SIGNAL_QUALITY) + 1); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s", SARA_R5_EXT_SIGNAL_QUALITY); + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == nullptr) + { + 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 SARA_R5_ERROR_ERROR; + } + + int scanned = 0; + char *searchPtr = strstr(response, "+CESQ: "); + if (searchPtr != nullptr) { + scanned = sscanf(searchPtr, "+CESQ: %u,%u,%u,%u,%u,%u", &signal_quality.rxlev, &signal_quality.ber, + &signal_quality.rscp, &signal_quality.enc0, &signal_quality.rsrq, &signal_quality.rsrp); + } + + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + if (scanned == 6) + { + err = SARA_R5_ERROR_SUCCESS; + } + + free(command); + free(response); + return err; +} + SARA_R5_registration_status_t SARA_R5::registration(bool eps) { char *command; @@ -1608,13 +1693,13 @@ SARA_R5_registration_status_t SARA_R5::registration(bool eps) free(response); return SARA_R5_REGISTRATION_INVALID; } - + int scanned = 0; - const char *startTag = eps ? "+CEREG:" : "+CREG:"; + const char *startTag = eps ? SARA_R5_EPSREGISTRATION_STATUS_URC : SARA_R5_REGISTRATION_STATUS_URC; char *searchPtr = strstr(response, startTag); if (searchPtr != nullptr) { - searchPtr += eps ? strlen("+CEREG:") : strlen("+CREG:"); // Move searchPtr to first char + searchPtr += eps ? strlen(SARA_R5_EPSREGISTRATION_STATUS_URC) : strlen(SARA_R5_REGISTRATION_STATUS_URC); // Move searchPtr to first char while (*searchPtr == ' ') searchPtr++; // skip spaces scanned = sscanf(searchPtr, "%*d,%d", &status); } @@ -1742,7 +1827,7 @@ SARA_R5_error_t SARA_R5::getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp SARA_R5_error_t err; char *command; char *response; - + if (cid > SARA_R5_NUM_PDP_CONTEXT_IDENTIFIERS) return SARA_R5_ERROR_ERROR; @@ -1780,7 +1865,7 @@ SARA_R5_error_t SARA_R5::getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp char strPdpType[10]; char strApn[128]; int ipOct[4]; - + searchPtr += strlen("+CGDCONT:"); // Point to the cid while (*searchPtr == ' ') searchPtr++; // skip spaces scanned = sscanf(searchPtr, "%d,\"%[^\"]\",\"%[^\"]\",\"%d.%d.%d.%d", @@ -1807,7 +1892,7 @@ SARA_R5_error_t SARA_R5::getAPN(int cid, String *apn, IPAddress *ip, SARA_R5_pdp if (pdpType) *pdpType = PDP_TYPE_INVALID; if (ip) *ip = {0, 0, 0, 0}; keepGoing = false; - } + } } } else @@ -1838,7 +1923,7 @@ SARA_R5_error_t SARA_R5::getSimStatus(String* code) } err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, response, SARA_R5_STANDARD_RESPONSE_TIMEOUT); - + if (err == SARA_R5_ERROR_SUCCESS) { int scanned = 0; @@ -1857,13 +1942,13 @@ SARA_R5_error_t SARA_R5::getSimStatus(String* code) 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; @@ -1922,7 +2007,7 @@ SARA_R5_error_t SARA_R5::getSIMstateReportingMode(int *mode) int scanned = 0; char *searchPtr = strstr(response, "+USIMSTAT:"); if (searchPtr != nullptr) - { + { searchPtr += strlen("+USIMSTAT:"); // Move searchPtr to first char while (*searchPtr == ' ') searchPtr++; // skip spaces scanned = sscanf(searchPtr, "%d\r\n", &m); @@ -4168,7 +4253,7 @@ SARA_R5_error_t SARA_R5::nvMQTT(SARA_R5_mqtt_nv_parameter_t parameter) return err; } -SARA_R5_error_t SARA_R5::setMQTTclientId(String clientId) +SARA_R5_error_t SARA_R5::setMQTTclientId(const String& clientId) { SARA_R5_error_t err; char *command; @@ -4182,7 +4267,7 @@ SARA_R5_error_t SARA_R5::setMQTTclientId(String clientId) return err; } -SARA_R5_error_t SARA_R5::setMQTTserver(String serverName, int port) +SARA_R5_error_t SARA_R5::setMQTTserver(const String& serverName, int port) { SARA_R5_error_t err; char *command; @@ -4196,6 +4281,21 @@ SARA_R5_error_t SARA_R5::setMQTTserver(String serverName, int port) return err; } +SARA_R5_error_t SARA_R5::setMQTTcredentials(const String& userName, const String& pwd) +{ + SARA_R5_error_t err; + char *command; + command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_PROFILE) + userName.length() + pwd.length() + 16); + if (command == nullptr) { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + sprintf(command, "%s=%d,\"%s\",\"%s\"", SARA_R5_MQTT_PROFILE, SARA_R5_MQTT_PROFILE_USERNAMEPWD, userName.c_str(), pwd.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + 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; @@ -4224,7 +4324,7 @@ SARA_R5_error_t SARA_R5::connectMQTT(void) free(command); return err; } - + SARA_R5_error_t SARA_R5::disconnectMQTT(void) { SARA_R5_error_t err; @@ -4238,8 +4338,8 @@ SARA_R5_error_t SARA_R5::disconnectMQTT(void) free(command); return err; } - -SARA_R5_error_t SARA_R5::subscribeMQTTtopic(int max_Qos, String topic) + +SARA_R5_error_t SARA_R5::subscribeMQTTtopic(int max_Qos, const String& topic) { SARA_R5_error_t err; char *command; @@ -4252,8 +4352,8 @@ SARA_R5_error_t SARA_R5::subscribeMQTTtopic(int max_Qos, String topic) free(command); return err; } - -SARA_R5_error_t SARA_R5::unsubscribeMQTTtopic(String topic) + +SARA_R5_error_t SARA_R5::unsubscribeMQTTtopic(const String& topic) { SARA_R5_error_t err; char *command; @@ -4266,7 +4366,7 @@ SARA_R5_error_t SARA_R5::unsubscribeMQTTtopic(String topic) free(command); return err; } - + SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead) { char *command; @@ -4274,7 +4374,7 @@ SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, SARA_R5_error_t err; int scanNum = 0; int total_length, topic_length, data_length; - + // Set *bytesRead to zero if (bytesRead != nullptr) *bytesRead = 0; @@ -4292,14 +4392,14 @@ SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, 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) @@ -4332,7 +4432,7 @@ SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, free(response); return SARA_R5_ERROR_UNEXPECTED_RESPONSE; } - + err = SARA_R5_ERROR_SUCCESS; searchPtr = strstr(searchPtr, "\""); if (searchPtr!= nullptr) { @@ -4365,6 +4465,104 @@ SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, return err; } +SARA_R5_error_t SARA_R5::mqttPublishTextMsg(const String& topic, const char * const msg, uint8_t qos, bool retain) +{ + if (topic.isEmpty() || msg == nullptr) + { + return SARA_R5_ERROR_INVALID; + } + + SARA_R5_error_t err; + + // Check the message length and truncate if necessary. + size_t msg_len = strnlen(msg, MAX_MQTT_DIRECT_MSG_LEN); + if (msg_len > MAX_MQTT_DIRECT_MSG_LEN) + { + msg_len = MAX_MQTT_DIRECT_MSG_LEN; + } + + String str_msg(msg, msg_len); + + char *command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 20 + topic.length() + msg_len); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s=%d,%u,%u,0,\"%s\",\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_PUBLISH, qos, (retain ? 1:0), topic.c_str(), str_msg.c_str()); + + sendCommand(command, true); + err = waitForResponse(SARA_R5_RESPONSE_MORE, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + sendCommand(msg, false); + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + } + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::mqttPublishBinaryMsg(const String& topic, const char * const msg, size_t msg_len, uint8_t qos, bool retain) +{ + /* + * The modem prints the '>' as the signal to send the binary message content. + * at+umqttc=9,0,0,"topic",4 + * + * >"xY" + * OK + * + * +UUMQTTC: 9,1 + */ + if (topic.isEmpty() || msg == nullptr || msg_len > MAX_MQTT_DIRECT_MSG_LEN) + { + return SARA_R5_ERROR_INVALID; + } + + SARA_R5_error_t err; + char *command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 20 + topic.length()); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s=%d,%u,%u,\"%s\",%u", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_PUBLISHBINARY, qos, (retain ? 1:0), topic.c_str(), msg_len); + + sendCommand(command, true); + err = waitForResponse(SARA_R5_RESPONSE_MORE, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err == SARA_R5_ERROR_SUCCESS) + { + sendCommand(msg, false); + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + } + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::mqttPublishFromFile(const String& topic, const String& filename, uint8_t qos, bool retain) +{ + if (topic.isEmpty() || filename.isEmpty()) + { + return SARA_R5_ERROR_INVALID; + } + + SARA_R5_error_t err; + char *command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 20 + topic.length() + filename.length()); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s=%d,%u,%u,\"%s\",\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_PUBLISHFILE, qos, (retain ? 1:0), topic.c_str(), filename.c_str()); + + sendCommand(command, true); + err = waitForResponse(SARA_R5_RESPONSE_OK, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + SARA_R5_error_t SARA_R5::getMQTTprotocolError(int *error_code, int *error_code2) { SARA_R5_error_t err; @@ -4405,6 +4603,125 @@ SARA_R5_error_t SARA_R5::getMQTTprotocolError(int *error_code, int *error_code2) return err; } +SARA_R5_error_t SARA_R5::setFTPserver(const String& serverName) +{ + constexpr size_t cmd_len = 145; + char command[cmd_len]; // long enough for AT+UFTP=1,<128 bytes> + + snprintf(command, cmd_len - 1, "%s=%d,\"%s\"", SARA_R5_FTP_PROFILE, SARA_R5_FTP_PROFILE_SERVERNAME, serverName.c_str()); + return sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); +} + +SARA_R5_error_t SARA_R5::setFTPtimeouts(const unsigned int timeout, const unsigned int cmd_linger, const unsigned int data_linger) +{ + constexpr size_t cmd_len = 64; + char command[cmd_len]; // long enough for AT+UFTP=1,<128 bytes> + + snprintf(command, cmd_len - 1, "%s=%d,%u,%u,%u", SARA_R5_FTP_PROFILE, SARA_R5_FTP_PROFILE_TIMEOUT, timeout, cmd_linger, data_linger); + return sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); +} + +SARA_R5_error_t SARA_R5::setFTPcredentials(const String& userName, const String& pwd) +{ + SARA_R5_error_t err; + constexpr size_t cmd_len = 48; + char command[cmd_len]; // long enough for AT+UFTP=n,<30 bytes> + + snprintf(command, cmd_len - 1, "%s=%d,\"%s\"", SARA_R5_FTP_PROFILE, SARA_R5_FTP_PROFILE_USERNAME, userName.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + if (err != SARA_R5_ERROR_SUCCESS) + { + return err; + } + + snprintf(command, cmd_len - 1, "%s=%d,\"%s\"", SARA_R5_FTP_PROFILE, SARA_R5_FTP_PROFILE_PWD, pwd.c_str()); + err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + return err; +} + +SARA_R5_error_t SARA_R5::connectFTP(void) +{ + constexpr size_t cmd_len = 16; + char command[cmd_len]; // long enough for AT+UFTPC=n + + snprintf(command, cmd_len - 1, "%s=%d", SARA_R5_FTP_COMMAND, SARA_R5_FTP_COMMAND_LOGIN); + return sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); +} + +SARA_R5_error_t SARA_R5::disconnectFTP(void) +{ + constexpr size_t cmd_len = 16; + char command[cmd_len]; // long enough for AT+UFTPC=n + + snprintf(command, cmd_len - 1, "%s=%d", SARA_R5_FTP_COMMAND, SARA_R5_FTP_COMMAND_LOGOUT); + return sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); +} + +SARA_R5_error_t SARA_R5::ftpGetFile(const String& filename) +{ + char * command = sara_r5_calloc_char(strlen(SARA_R5_FTP_COMMAND) + (2 * filename.length()) + 16); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + sprintf(command, "%s=%d,\"%s\",\"%s\"", SARA_R5_FTP_COMMAND, SARA_R5_FTP_COMMAND_GET_FILE, filename.c_str(), filename.c_str()); + //memset(response, 0, sizeof(response)); + //sendCommandWithResponse(command, SARA_R5_RESPONSE_CONNECT, response, 8000 /* ms */, response_len); + SARA_R5_error_t err = sendCommandWithResponse(command, SARA_R5_RESPONSE_OK_OR_ERROR, nullptr, + SARA_R5_STANDARD_RESPONSE_TIMEOUT); + + free(command); + return err; +} + +SARA_R5_error_t SARA_R5::getFTPprotocolError(int *error_code, int *error_code2) +{ + SARA_R5_error_t err; + char *response; + + int code, code2; + + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } + + err = sendCommandWithResponse(SARA_R5_FTP_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, "+UFTPER:"); + if (searchPtr != nullptr) + { + scanned = sscanf(searchPtr, "+UFTPER:%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; @@ -4482,7 +4799,7 @@ SARA_R5_error_t SARA_R5::setSecurityManager(SARA_R5_sec_manager_opcode_t opcode, 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) { @@ -4965,7 +5282,7 @@ SARA_R5_error_t SARA_R5::appendFileContents(String filename, const char *str, in 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. @@ -4979,7 +5296,7 @@ SARA_R5_error_t SARA_R5::appendFileContents(String filename, const char *str, in _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) @@ -5024,7 +5341,7 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, String *contents) } return err; } - + command = sara_r5_calloc_char(strlen(SARA_R5_FILE_SYSTEM_READ_FILE) + filename.length() + 8); if (command == nullptr) return SARA_R5_ERROR_OUT_OF_MEMORY; @@ -5246,6 +5563,92 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, char *contents) return err; } +SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size_t offset, size_t requested_length, size_t& bytes_read) +{ + bytes_read = 0; + if (filename.isEmpty() || buffer == nullptr || requested_length < 1) + { + return SARA_R5_ERROR_UNEXPECTED_PARAM; + } + + // trying to get a byte at a time does not seem to be reliable so this method must use + // a real UART. + if (_hardSerial == nullptr) + { + if (_printDebug == true) + { + _debugPort->println(F("getFileBlock: only works with a hardware UART")); + } + return SARA_R5_ERROR_INVALID; + } + + size_t cmd_len = filename.length() + 32; + char* cmd = sara_r5_calloc_char(cmd_len); + sprintf(cmd, "at+urdblock=\"%s\",%zu,%zu\r\n", filename.c_str(), offset, requested_length); + if (_printDebug == true) + { + _debugPort->printf(F("getFileBlock: sending command: %s\r\n"), cmd); + } + sendCommand(cmd, false); + + int ich; + char ch; + int quote_count = 0; + size_t comma_idx = 0; + + while (quote_count < 3) + { + ich = _hardSerial->read(); + if (ich < 0) + { + continue; + } + ch = (char)(ich & 0xFF); + cmd[bytes_read++] = ch; + if (ch == '"') + { + quote_count++; + } + else if (ch == ',' && comma_idx == 0) + { + comma_idx = bytes_read; + } + } + + cmd[bytes_read] = 0; + if (_printDebug == true) + { + _debugPort->printf(F("getFileBlock: header: [%s]\r\n"), cmd); + } + cmd[bytes_read - 2] = 0; + + // Example response: + // +URDBLOCK: "wombat.bin",64000,"... " + size_t data_length = strtoul(&cmd[comma_idx], nullptr, 10); + free(cmd); + if (_printDebug == true) + { + _debugPort->printf(F("getFileBlock: reading %zu bytes\r\n"), data_length); + } + + bytes_read = 0; + size_t bytes_remaining = data_length; + while (bytes_read < data_length) + { + // This method seems more reliable than reading a byte at a time. + size_t rc = _hardSerial->readBytes(&buffer[bytes_read], bytes_remaining); + bytes_read += rc; + bytes_remaining -= rc; + } + + if (_printDebug == true) + { + _debugPort->printf(F("getFileBlock: read %zu bytes\r\n"), bytes_read); + } + + return SARA_R5_ERROR_SUCCESS; +} + SARA_R5_error_t SARA_R5::getFileSize(String filename, int *size) { SARA_R5_error_t err; @@ -5370,9 +5773,9 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, { int retries = _maxInitTries; SARA_R5_error_t err = SARA_R5_ERROR_SUCCESS; - + beginSerial(baud); - + do { if (_printDebug == true) @@ -5382,9 +5785,9 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, { 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; } @@ -5393,13 +5796,13 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, { 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) { @@ -5418,7 +5821,7 @@ SARA_R5_error_t SARA_R5::init(unsigned long baud, } } while ((retries --) && (err != SARA_R5_ERROR_SUCCESS)); - + // we tried but seems failed if (err != SARA_R5_ERROR_SUCCESS) { if (_printDebug == true) @@ -5652,7 +6055,7 @@ SARA_R5_error_t SARA_R5::waitForResponse(const char *expectedResponse, const cha 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 nullptr @@ -5717,7 +6120,7 @@ SARA_R5_error_t SARA_R5::waitForResponse(const char *expectedResponse, const cha if (true == _printAtDebug) { _debugAtPort->print((error == true) ? expectedError : expectedResponse); } - + return (error == true) ? SARA_R5_ERROR_ERROR : SARA_R5_ERROR_SUCCESS; } @@ -5745,7 +6148,7 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( _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) { @@ -5756,7 +6159,7 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( } else { responseLen = (int)strlen(expectedResponse); } - + while ((!found) && ((timeIn + commandTimeout) > millis())) { if (hwAvailable() > 0) //hwAvailable can return -1 if the serial port is nullptr @@ -5829,7 +6232,7 @@ SARA_R5_error_t SARA_R5::sendCommandWithResponse( yield(); } } - + if (_printDebug == true) if ((printResponse = true) && (printedSomething)) _debugPort->println(); @@ -5867,7 +6270,7 @@ SARA_R5_error_t SARA_R5::sendCustomCommandWithResponse(const char *command, cons 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 @@ -6028,8 +6431,8 @@ SARA_R5_error_t SARA_R5::parseSocketCloseIndication(String *closeIndication) int search; int socket; - search = closeIndication->indexOf("+UUSOCL:"); - search += strlen("+UUSOCL:"); + search = closeIndication->indexOf(SARA_R5_CLOSE_SOCKET_URC); + search += strlen(SARA_R5_CLOSE_SOCKET_URC); while (closeIndication->charAt(search) == ' ') search ++; // skip spaces // Socket will be first integer, should be single-digit number between 0-6: @@ -6276,18 +6679,19 @@ void SARA_R5::pruneBacklog() while (event != nullptr) //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:") != nullptr) - || (strstr(event, "+UUSORF:") != nullptr) - || (strstr(event, "+UUSOLI:") != nullptr) - || (strstr(event, "+UUSOCL:") != nullptr) - || (strstr(event, "+UULOC:") != nullptr) - || (strstr(event, "+UUSIMSTAT:") != nullptr) - || (strstr(event, "+UUPSDA:") != nullptr) - || (strstr(event, "+UUHTTPCR:") != nullptr) - || (strstr(event, "+UUMQTTC:") != nullptr) - || (strstr(event, "+UUPING:") != nullptr) - || (strstr(event, "+CREG:") != nullptr) - || (strstr(event, "+CEREG:") != nullptr)) + if ((strstr(event, SARA_R5_READ_SOCKET_URC) != nullptr) + || (strstr(event, SARA_R5_READ_UDP_SOCKET_URC) != nullptr) + || (strstr(event, SARA_R5_LISTEN_SOCKET_URC) != nullptr) + || (strstr(event, SARA_R5_CLOSE_SOCKET_URC) != nullptr) + || (strstr(event, SARA_R5_GNSS_REQUEST_LOCATION_URC) != nullptr) + || (strstr(event, SARA_R5_SIM_STATE_URC) != nullptr) + || (strstr(event, SARA_R5_MESSAGE_PDP_ACTION_URC) != nullptr) + || (strstr(event, SARA_R5_HTTP_COMMAND_URC) != nullptr) + || (strstr(event, SARA_R5_MQTT_COMMAND_URC) != nullptr) + || (strstr(event, SARA_R5_PING_COMMAND_URC) != nullptr) + || (strstr(event, SARA_R5_REGISTRATION_STATUS_URC) != nullptr) + || (strstr(event, SARA_R5_EPSREGISTRATION_STATUS_URC) != nullptr) + || (strstr(event, SARA_R5_FTP_COMMAND_URC) != nullptr)) { 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. @@ -6314,7 +6718,10 @@ void SARA_R5::pruneBacklog() // } // } - free(event); + // This is a bug. event could be nullptr, and even if it is not it is pointing into + // _saraResponseBacklog meaning it is calling free on a random pointer whose memory + // may be referenced later. + //free(event); } // GPS Helper Functions: diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index ed4f743..4146220 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -111,6 +111,7 @@ 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_EXT_SIGNAL_QUALITY[] = "+CESQ"; const char SARA_R5_OPERATOR_SELECTION[] = "+COPS"; const char SARA_R5_REGISTRATION_STATUS[] = "+CREG"; const char SARA_R5_EPSREGISTRATION_STATUS[] = "+CEREG"; @@ -162,7 +163,10 @@ 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"; - +// ### FTP +const char SARA_R5_FTP_PROFILE[] = "+UFTP"; +const char SARA_R5_FTP_COMMAND[] = "+UFTPC"; +const char SARA_R5_FTP_PROTOCOL_ERROR[] = "+UFTPER"; // ### GNSS const char SARA_R5_GNSS_POWER[] = "+UGPS"; // GNSS power management configuration const char SARA_R5_GNSS_ASSISTED_IND[] = "+UGIND"; // Assisted GNSS unsolicited indication @@ -177,6 +181,7 @@ const char SARA_R5_AIDING_SERVER_CONFIGURATION[] = "+UGSRV"; // Configure aiding // ### 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_READ_BLOCK[] = "+URDBLOCK"; // Read a block from 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 @@ -185,7 +190,24 @@ const char SARA_R5_FILE_SYSTEM_DELETE_FILE[] = "+UDELFILE"; // Delete a file const char SARA_R5_SEC_PROFILE[] = "+USECPRF"; const char SARA_R5_SEC_MANAGER[] = "+USECMNG"; + +// ### URC strings +const char SARA_R5_READ_SOCKET_URC[] = "+UUSORD:"; +const char SARA_R5_READ_UDP_SOCKET_URC[] = "+UUSORF:"; +const char SARA_R5_LISTEN_SOCKET_URC[] = "+UUSOLI:"; +const char SARA_R5_CLOSE_SOCKET_URC[] = "+UUSOCL:"; +const char SARA_R5_GNSS_REQUEST_LOCATION_URC[] = "+UULOC:"; +const char SARA_R5_SIM_STATE_URC[] = "+UUSIMSTAT:"; +const char SARA_R5_MESSAGE_PDP_ACTION_URC[] = "+UUPSDA:"; +const char SARA_R5_HTTP_COMMAND_URC[] = "+UUHTTPCR:"; +const char SARA_R5_MQTT_COMMAND_URC[] = "+UUMQTTC:"; +const char SARA_R5_PING_COMMAND_URC[] = "+UUPING:"; +const char SARA_R5_REGISTRATION_STATUS_URC[] = "+CREG:"; +const char SARA_R5_EPSREGISTRATION_STATUS_URC[] = "+CEREG:"; +const char SARA_R5_FTP_COMMAND_URC[] = "+UUFTPCR:"; + // ### Response +const char SARA_R5_RESPONSE_MORE[] = "\n>"; 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"; @@ -333,6 +355,15 @@ struct operator_stats uint8_t act; }; +typedef struct ext_signal_quality_ { + unsigned int rxlev; + unsigned int ber; + unsigned int rscp; + unsigned int enc0; + unsigned int rsrq; + unsigned int rsrp; +} signal_quality; + typedef enum { SARA_R5_TCP = 6, @@ -444,7 +475,7 @@ typedef enum 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, @@ -461,17 +492,52 @@ typedef enum 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_INVALID = -1, + 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_RCVMSGFORMAT, + SARA_R5_MQTT_COMMAND_PING, + SARA_R5_MQTT_COMMAND_PUBLISHBINARY, } SARA_R5_mqtt_command_opcode_t; +constexpr uint16_t MAX_MQTT_HEX_MSG_LEN = 512; +constexpr uint16_t MAX_MQTT_DIRECT_MSG_LEN = 1024; + +typedef enum +{ + SARA_R5_FTP_PROFILE_IPADDRESS = 0, + SARA_R5_FTP_PROFILE_SERVERNAME, + SARA_R5_FTP_PROFILE_USERNAME, + SARA_R5_FTP_PROFILE_PWD, + SARA_R5_FTP_PROFILE_ACCOUNT, + SARA_R5_FTP_PROFILE_TIMEOUT, + SARA_R5_FTP_PROFILE_MODE +} SARA_R5_ftp_profile_opcode_t; + +typedef enum +{ + SARA_R5_FTP_COMMAND_INVALID = -1, + SARA_R5_FTP_COMMAND_LOGOUT = 0, + SARA_R5_FTP_COMMAND_LOGIN, + SARA_R5_FTP_COMMAND_DELETE_FILE, + SARA_R5_FTP_COMMAND_RENAME_FILE, + SARA_R5_FTP_COMMAND_GET_FILE, + SARA_R5_FTP_COMMAND_PUT_FILE, + SARA_R5_FTP_COMMAND_GET_FILE_DIRECT, + SARA_R5_FTP_COMMAND_PUT_FILE_DIRECT, + SARA_R5_FTP_COMMAND_CHANGE_DIR, + SARA_R5_FTP_COMMAND_MKDIR = 10, + SARA_R5_FTP_COMMAND_RMDIR, + SARA_R5_FTP_COMMAND_DIR_INFO = 13, + SARA_R5_FTP_COMMAND_LS, + SARA_R5_FTP_COMMAND_GET_FOTA_FILE +} SARA_R5_ftp_command_opcode_t; + typedef enum { SARA_R5_PSD_CONFIG_PARAM_PROTOCOL = 0, @@ -587,7 +653,7 @@ class SARA_R5 : public Print // 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 @@ -604,7 +670,7 @@ class SARA_R5 : public Print // 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. @@ -625,12 +691,14 @@ class SARA_R5 : public Print 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)); + void setMQTTCommandCallback(void (*mqttCommandRequestCallback)(SARA_R5_mqtt_command_opcode_t command, int result)); + void setFTPCommandCallback(void (*ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t 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); @@ -666,9 +734,11 @@ class SARA_R5 : public Print 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_error_t getExtSignalQuality(signal_quality& signal_quality); + 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); @@ -685,7 +755,7 @@ class SARA_R5 : public Print SARA_R5_error_t getSimStatus(String* code); SARA_R5_error_t setSimPin(String pin); - + // SIM // Status report Mode: // Bit States reported @@ -852,22 +922,35 @@ class SARA_R5 : public Print 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 setMQTTclientId(const String& clientId); + SARA_R5_error_t setMQTTserver(const String& serverName, int port); + SARA_R5_error_t setMQTTcredentials(const String& userName, const String& pwd); 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 subscribeMQTTtopic(int max_Qos, const String& topic); + SARA_R5_error_t unsubscribeMQTTtopic(const String& topic); SARA_R5_error_t readMQTT(int* pQos, String* pTopic, uint8_t *readDest, int readLength, int *bytesRead); + SARA_R5_error_t mqttPublishTextMsg(const String& topic, const char * const msg, uint8_t qos = 0, bool retain = false); + SARA_R5_error_t mqttPublishBinaryMsg(const String& topic, const char * const msg, size_t msg_len, uint8_t qos = 0, bool retain = false); + SARA_R5_error_t mqttPublishFromFile(const String& topic, const String& filename, uint8_t qos = 0, bool retain = false); SARA_R5_error_t getMQTTprotocolError(int *error_code, int *error_code2); - + + // FTP + SARA_R5_error_t setFTPserver(const String& serverName); + SARA_R5_error_t setFTPtimeouts(const unsigned int timeout, const unsigned int cmd_linger, const unsigned int data_linger); + SARA_R5_error_t setFTPcredentials(const String& userName, const String& pwd); + SARA_R5_error_t connectFTP(void); + SARA_R5_error_t disconnectFTP(void); + SARA_R5_error_t ftpGetFile(const String& filename); + SARA_R5_error_t getFTPprotocolError(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 @@ -926,12 +1009,14 @@ class SARA_R5 : public Print // 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. + SARA_R5_error_t getFileBlock(const String& filename, char* buffer, size_t offset, size_t length, size_t& bytes_read); // OK for binary files. Make sure buffer can hold the requested block size. + // 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); @@ -949,7 +1034,7 @@ class SARA_R5 : public Print 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; @@ -978,7 +1063,8 @@ class SARA_R5 : public Print void (*_psdActionRequestCallback)(int, IPAddress); void (*_pingRequestCallback)(int, int, String, IPAddress, int, long); void (*_httpCommandRequestCallback)(int, int, int); - void (*_mqttCommandRequestCallback)(int, int); + void (*_mqttCommandRequestCallback)(SARA_R5_mqtt_command_opcode_t, int); + void (*_ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t, 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); From f18e6dbde1bd186ae88caa976edc4f5ed767b0fa Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 21 Aug 2023 09:24:56 +1000 Subject: [PATCH 2/5] Addressed comments from pull request review. Removed F macro from Serial.printf calls. --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 99 +++++++++---------- src/SparkFun_u-blox_SARA-R5_Arduino_Library.h | 10 +- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index eb4fa21..456d02e 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -308,10 +308,6 @@ bool SARA_R5::bufferedPoll(void) } } - // This is a bug. event could be nullptr, and even if it is not it is pointing into - // _saraRXBuffer meaning it is calling free on a random pointer whose memory - // may be referenced later. - //free(event); _bufferedPollReentrant = false; return handled; @@ -858,12 +854,12 @@ void SARA_R5::setHTTPCommandCallback(void (*httpCommandRequestCallback)(int prof _httpCommandRequestCallback = httpCommandRequestCallback; } -void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(SARA_R5_mqtt_command_opcode_t command, int result)) +void SARA_R5::setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)) { _mqttCommandRequestCallback = mqttCommandRequestCallback; } -void SARA_R5::setFTPCommandCallback(void (*ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t command, int result)) +void SARA_R5::setFTPCommandCallback(void (*ftpCommandRequestCallback)(int command, int result)) { _ftpCommandRequestCallback = ftpCommandRequestCallback; } @@ -1618,51 +1614,53 @@ int8_t SARA_R5::rssi(void) SARA_R5_error_t SARA_R5::getExtSignalQuality(signal_quality& signal_quality) { - char *command; - char *response; - SARA_R5_error_t err; + char *command; + char *response; + SARA_R5_error_t err; - command = sara_r5_calloc_char(strlen(SARA_R5_EXT_SIGNAL_QUALITY) + 1); - if (command == nullptr) - { - return SARA_R5_ERROR_OUT_OF_MEMORY; - } + command = sara_r5_calloc_char(strlen(SARA_R5_EXT_SIGNAL_QUALITY) + 1); + if (command == nullptr) + { + return SARA_R5_ERROR_OUT_OF_MEMORY; + } - sprintf(command, "%s", SARA_R5_EXT_SIGNAL_QUALITY); + sprintf(command, "%s", SARA_R5_EXT_SIGNAL_QUALITY); - response = sara_r5_calloc_char(minimumResponseAllocation); - if (response == nullptr) - { - free(command); - return SARA_R5_ERROR_OUT_OF_MEMORY; - } + response = sara_r5_calloc_char(minimumResponseAllocation); + if (response == nullptr) + { + 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 SARA_R5_ERROR_ERROR; - } + 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 SARA_R5_ERROR_ERROR; + } - int scanned = 0; - char *searchPtr = strstr(response, "+CESQ: "); - if (searchPtr != nullptr) { - scanned = sscanf(searchPtr, "+CESQ: %u,%u,%u,%u,%u,%u", &signal_quality.rxlev, &signal_quality.ber, - &signal_quality.rscp, &signal_quality.enc0, &signal_quality.rsrq, &signal_quality.rsrp); - } + int scanned = 0; + char *searchPtr = strstr(response, "+CESQ: "); + if (searchPtr != nullptr) + { + while (*searchPtr == ' ') searchPtr++; // skip spaces + scanned = sscanf(searchPtr, "%u,%u,%u,%u,%u,%u", &signal_quality.rxlev, &signal_quality.ber, + &signal_quality.rscp, &signal_quality.enc0, &signal_quality.rsrq, &signal_quality.rsrp); + } - err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; - if (scanned == 6) - { - err = SARA_R5_ERROR_SUCCESS; - } + err = SARA_R5_ERROR_UNEXPECTED_RESPONSE; + if (scanned == 6) + { + err = SARA_R5_ERROR_SUCCESS; + } - free(command); - free(response); - return err; + free(command); + free(response); + return err; } SARA_R5_registration_status_t SARA_R5::registration(bool eps) @@ -5587,7 +5585,7 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size sprintf(cmd, "at+urdblock=\"%s\",%zu,%zu\r\n", filename.c_str(), offset, requested_length); if (_printDebug == true) { - _debugPort->printf(F("getFileBlock: sending command: %s\r\n"), cmd); + _debugPort->printf("getFileBlock: sending command: %s\r\n", cmd); } sendCommand(cmd, false); @@ -5618,7 +5616,7 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size cmd[bytes_read] = 0; if (_printDebug == true) { - _debugPort->printf(F("getFileBlock: header: [%s]\r\n"), cmd); + _debugPort->printf("getFileBlock: header: [%s]\r\n", cmd); } cmd[bytes_read - 2] = 0; @@ -5628,7 +5626,7 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size free(cmd); if (_printDebug == true) { - _debugPort->printf(F("getFileBlock: reading %zu bytes\r\n"), data_length); + _debugPort->printf("getFileBlock: reading %zu bytes\r\n", data_length); } bytes_read = 0; @@ -5643,7 +5641,7 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size if (_printDebug == true) { - _debugPort->printf(F("getFileBlock: read %zu bytes\r\n"), bytes_read); + _debugPort->printf("getFileBlock: read %zu bytes\r\n", bytes_read); } return SARA_R5_ERROR_SUCCESS; @@ -6717,11 +6715,6 @@ void SARA_R5::pruneBacklog() // _debugPort->println(F("pruneBacklog: backlog is now empty")); // } // } - - // This is a bug. event could be nullptr, and even if it is not it is pointing into - // _saraResponseBacklog meaning it is calling free on a random pointer whose memory - // may be referenced later. - //free(event); } // GPS Helper Functions: diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h index 4146220..32b28f9 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.h @@ -535,7 +535,7 @@ typedef enum SARA_R5_FTP_COMMAND_RMDIR, SARA_R5_FTP_COMMAND_DIR_INFO = 13, SARA_R5_FTP_COMMAND_LS, - SARA_R5_FTP_COMMAND_GET_FOTA_FILE + SARA_R5_FTP_COMMAND_GET_FOTA_FILE = 100 } SARA_R5_ftp_command_opcode_t; typedef enum @@ -691,8 +691,8 @@ class SARA_R5 : public Print 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)(SARA_R5_mqtt_command_opcode_t command, int result)); - void setFTPCommandCallback(void (*ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t command, int result)); + void setMQTTCommandCallback(void (*mqttCommandRequestCallback)(int command, int result)); + void setFTPCommandCallback(void (*ftpCommandRequestCallback)(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)); @@ -1063,8 +1063,8 @@ class SARA_R5 : public Print void (*_psdActionRequestCallback)(int, IPAddress); void (*_pingRequestCallback)(int, int, String, IPAddress, int, long); void (*_httpCommandRequestCallback)(int, int, int); - void (*_mqttCommandRequestCallback)(SARA_R5_mqtt_command_opcode_t, int); - void (*_ftpCommandRequestCallback)(SARA_R5_ftp_command_opcode_t, int); + void (*_mqttCommandRequestCallback)(int, int); + void (*_ftpCommandRequestCallback)(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); From 713cca8cb3197ec22d5d0f96f92145e59f87ae8e Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 21 Aug 2023 09:43:23 +1000 Subject: [PATCH 3/5] Fixed getExtSignalQuality response parsing. --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 456d02e..8e5c902 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -592,8 +592,7 @@ bool SARA_R5::processURCEvent(const char *event) } } { // URC: +UUMQTTC (MQTT Command Result) - SARA_R5_mqtt_command_opcode_t mqttCmd; - int mqttResult; + int command, result; int scanNum; int qos = -1; String topic; @@ -607,8 +606,8 @@ bool SARA_R5::processURCEvent(const char *event) searchPtr++; // skip spaces } - scanNum = sscanf(searchPtr, "%d,%d", &mqttCmd, &mqttResult); - if ((scanNum == 2) && (mqttCmd == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) + scanNum = sscanf(searchPtr, "%d,%d", &command, &result); + if ((scanNum == 2) && (command == SARA_R5_MQTT_COMMAND_SUBSCRIBE)) { char topicC[100] = ""; scanNum = sscanf(searchPtr, "%*d,%*d,%d,\"%[^\"]\"", &qos, topicC); @@ -623,7 +622,7 @@ bool SARA_R5::processURCEvent(const char *event) if (_mqttCommandRequestCallback != nullptr) { - _mqttCommandRequestCallback(mqttCmd, mqttResult); + _mqttCommandRequestCallback(command, result); } return true; @@ -1644,9 +1643,11 @@ SARA_R5_error_t SARA_R5::getExtSignalQuality(signal_quality& signal_quality) } int scanned = 0; - char *searchPtr = strstr(response, "+CESQ: "); + const char * responseStr = "+CESQ:"; + char *searchPtr = strstr(response, responseStr); if (searchPtr != nullptr) { + searchPtr += strlen(responseStr); // Move searchPtr to first char while (*searchPtr == ' ') searchPtr++; // skip spaces scanned = sscanf(searchPtr, "%u,%u,%u,%u,%u,%u", &signal_quality.rxlev, &signal_quality.ber, &signal_quality.rscp, &signal_quality.enc0, &signal_quality.rsrq, &signal_quality.rsrp); @@ -4702,7 +4703,12 @@ SARA_R5_error_t SARA_R5::getFTPprotocolError(int *error_code, int *error_code2) char *searchPtr = strstr(response, "+UFTPER:"); if (searchPtr != nullptr) { - scanned = sscanf(searchPtr, "+UFTPER:%d,%d\r\n", &code, &code2); + searchPtr += strlen("+UFTPER:"); // Move searchPtr to first char + while (*searchPtr == ' ') + { + searchPtr++; // skip spaces + } + scanned = sscanf(searchPtr, "%d,%d\r\n", &code, &code2); } if (scanned == 2) From 629ec6bb39ccbec4126d27caf57b8f5aee7d00fa Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 21 Aug 2023 16:53:51 +1000 Subject: [PATCH 4/5] First attempt to get non-ESP32 platforms to compile. Fixed enum as sscanf destination. Replaced String.isEmpty() with String.length() < 1. Removed odd String usage in mqttPublishTextMsg. Removed Stream.printf() calls. --- ...parkFun_u-blox_SARA-R5_Arduino_Library.cpp | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp index 8e5c902..800dcef 100644 --- a/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_SARA-R5_Arduino_Library.cpp @@ -630,7 +630,7 @@ bool SARA_R5::processURCEvent(const char *event) } } { // URC: +UUFTPCR (FTP Command Result) - SARA_R5_ftp_command_opcode_t ftpCmd; + int ftpCmd; int ftpResult; int scanNum; char *searchPtr = strstr(event, SARA_R5_FTP_COMMAND_URC); @@ -4466,13 +4466,16 @@ SARA_R5_error_t SARA_R5::readMQTT(int* pQos, String* pTopic, uint8_t *readDest, SARA_R5_error_t SARA_R5::mqttPublishTextMsg(const String& topic, const char * const msg, uint8_t qos, bool retain) { - if (topic.isEmpty() || msg == nullptr) + if (topic.length() < 1 || msg == nullptr) { return SARA_R5_ERROR_INVALID; } SARA_R5_error_t err; + char sanitized_msg[MAX_MQTT_DIRECT_MSG_LEN + 1]; + memset(sanitized_msg, 0, sizeof(sanitized_msg)); + // Check the message length and truncate if necessary. size_t msg_len = strnlen(msg, MAX_MQTT_DIRECT_MSG_LEN); if (msg_len > MAX_MQTT_DIRECT_MSG_LEN) @@ -4480,7 +4483,17 @@ SARA_R5_error_t SARA_R5::mqttPublishTextMsg(const String& topic, const char * co msg_len = MAX_MQTT_DIRECT_MSG_LEN; } - String str_msg(msg, msg_len); + strncpy(sanitized_msg, msg, msg_len); + char * msg_ptr = sanitized_msg; + while (*msg_ptr != 0) + { + if (*msg_ptr == '"') + { + *msg_ptr = ' '; + } + + msg_ptr++; + } char *command = sara_r5_calloc_char(strlen(SARA_R5_MQTT_COMMAND) + 20 + topic.length() + msg_len); if (command == nullptr) @@ -4488,7 +4501,7 @@ SARA_R5_error_t SARA_R5::mqttPublishTextMsg(const String& topic, const char * co return SARA_R5_ERROR_OUT_OF_MEMORY; } - sprintf(command, "%s=%d,%u,%u,0,\"%s\",\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_PUBLISH, qos, (retain ? 1:0), topic.c_str(), str_msg.c_str()); + sprintf(command, "%s=%d,%u,%u,0,\"%s\",\"%s\"", SARA_R5_MQTT_COMMAND, SARA_R5_MQTT_COMMAND_PUBLISH, qos, (retain ? 1:0), topic.c_str(), sanitized_msg); sendCommand(command, true); err = waitForResponse(SARA_R5_RESPONSE_MORE, SARA_R5_RESPONSE_ERROR, SARA_R5_STANDARD_RESPONSE_TIMEOUT); @@ -4513,7 +4526,7 @@ SARA_R5_error_t SARA_R5::mqttPublishBinaryMsg(const String& topic, const char * * * +UUMQTTC: 9,1 */ - if (topic.isEmpty() || msg == nullptr || msg_len > MAX_MQTT_DIRECT_MSG_LEN) + if (topic.length() < 1|| msg == nullptr || msg_len > MAX_MQTT_DIRECT_MSG_LEN) { return SARA_R5_ERROR_INVALID; } @@ -4541,7 +4554,7 @@ SARA_R5_error_t SARA_R5::mqttPublishBinaryMsg(const String& topic, const char * SARA_R5_error_t SARA_R5::mqttPublishFromFile(const String& topic, const String& filename, uint8_t qos, bool retain) { - if (topic.isEmpty() || filename.isEmpty()) + if (topic.length() < 1|| filename.length() < 1) { return SARA_R5_ERROR_INVALID; } @@ -5570,7 +5583,7 @@ SARA_R5_error_t SARA_R5::getFileContents(String filename, char *contents) SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size_t offset, size_t requested_length, size_t& bytes_read) { bytes_read = 0; - if (filename.isEmpty() || buffer == nullptr || requested_length < 1) + if (filename.length() < 1 || buffer == nullptr || requested_length < 1) { return SARA_R5_ERROR_UNEXPECTED_PARAM; } @@ -5589,10 +5602,6 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size size_t cmd_len = filename.length() + 32; char* cmd = sara_r5_calloc_char(cmd_len); sprintf(cmd, "at+urdblock=\"%s\",%zu,%zu\r\n", filename.c_str(), offset, requested_length); - if (_printDebug == true) - { - _debugPort->printf("getFileBlock: sending command: %s\r\n", cmd); - } sendCommand(cmd, false); int ich; @@ -5620,20 +5629,12 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size } cmd[bytes_read] = 0; - if (_printDebug == true) - { - _debugPort->printf("getFileBlock: header: [%s]\r\n", cmd); - } cmd[bytes_read - 2] = 0; // Example response: // +URDBLOCK: "wombat.bin",64000,"... " size_t data_length = strtoul(&cmd[comma_idx], nullptr, 10); free(cmd); - if (_printDebug == true) - { - _debugPort->printf("getFileBlock: reading %zu bytes\r\n", data_length); - } bytes_read = 0; size_t bytes_remaining = data_length; @@ -5645,11 +5646,6 @@ SARA_R5_error_t SARA_R5::getFileBlock(const String& filename, char* buffer, size bytes_remaining -= rc; } - if (_printDebug == true) - { - _debugPort->printf("getFileBlock: read %zu bytes\r\n", bytes_read); - } - return SARA_R5_ERROR_SUCCESS; } From 1eb7eed8db0edede0ab48fd3ac1c3ca2aba66212 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 21 Aug 2023 09:14:40 +0100 Subject: [PATCH 5/5] Update library.properties for v1.1.7 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 3a39ee5..d96de7d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox SARA-R5 Arduino Library -version=1.1.6 +version=1.1.7 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for the u-blox SARA-R5 LTE-M / NB-IoT modules with secure cloud