From c1a58778eecd0115d746afbca1079683b244b672 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Thu, 6 Aug 2020 14:47:49 +0300 Subject: Quectel EG25-G Delta Radio Firmware Upgrade support - libmts-io implementation During testing I discrovered that EG25-G radio may lose some data during transmission over Serial AT interface or just freezes and stops responding over Serial AT interface. When ACK mode is not used, the radio may either return an error: ``` Expected: [+QFUPL: 24312545,fa6b], Actual: [+QFUPL: 17124608,b907 +CME ERROR: 409 // Fail to write the file ] ``` Or it may just freeze on modem_at1 interface and stop responding to AT commands: ``` 8:0:34:32|TRACE|RADIO| Sending command [AT] 8:0:34:133|DEBUG|RESULT: 8:0:34:133|DEBUG|Shutting Down ``` This commit implements an alternative, ACK mode for data transmission to prevent data losses. Data is sent in chunks and the device waits for ACK string from the radio for each chunk. --- include/mts/MTS_IO_QuectelRadio.h | 2 +- src/MTS_IO_QuectelRadio.cpp | 71 ++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 0f24db8..5199b40 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -73,7 +73,7 @@ namespace MTS { static const std::string VALUE_MTS_DELTA_NAME; static const std::string VALUE_MTS_DELTA_PATH; - CODE startFileUpload(const std::string& sTargetFilename, size_t nBytes); + CODE startFileUpload(const std::string& sTargetFilename, size_t nBytes, uint16_t uRxTimeout = 5, bool bAckEnabled = false); CODE abortFileUpload(); static inline void callNextStep(UpdateCb& stepCb, const char* csMessage); diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 942c0f7..a41e6fc 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -871,6 +871,9 @@ ICellularRadio::CODE QuectelRadio::setCellularMode(CELLULAR_MODES networks) { } ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTargetFilename, ICellularRadio::UpdateCb& stepCb) { + const uint16_t uFileTimeout = 2; // s + const int32_t iUploadResultTimeout = uFileTimeout * 2 * 1000; // ms + size_t dPayloadLength; size_t nChunks; CODE rc; @@ -888,7 +891,8 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks); printTrace("Starting file upload..."); - rc = startFileUpload(sTargetFilename, dPayloadLength); + // Start file upload, set transmission timeouts and enable ACK mode + rc = startFileUpload(sTargetFilename, dPayloadLength, uFileTimeout, true); if (rc != SUCCESS) { return rc; } @@ -900,6 +904,20 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget size_t nChunksPerCent = (nChunks / 100) + 1; size_t nFragmentLength = 0; std::array vBuffer; + std::string sResponse; + + // ACK waiter callback - wait for ACK and populate result code + IsNeedMoreData waitAck = [&rc](const std::string& iterationData, const std::string&/*allData*/)->bool { + if (iterationData.find('A') != std::string::npos) { + // No data needed + rc = SUCCESS; + return false; + } + + // We need more data + rc = NO_RESPONSE; + return true; + }; for (size_t iChunk = 1; iChunk < (nChunks + 1); iChunk++) { @@ -914,6 +932,14 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget rc = sendData(vBuffer.data(), nFragmentLength); if (rc != SUCCESS) { + // failed to send data + break; + } + + // Wait for ACK for up to 1 second and populate rc variable + sResponse = waitResponse(waitAck, 1000); + if (rc != SUCCESS) { + // timeout or end of execution - check later break; } @@ -924,38 +950,28 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget } - if (rc != SUCCESS) { - // cancel upload and terminate - callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); - abortFileUpload(); - return rc; - } - printTrace("Waiting for acknoledge from the radio"); std::string sExpectedResult = "+QFUPL: "; sExpectedResult += MTS::Text::format(dPayloadLength); sExpectedResult += ","; sExpectedResult += MTS::Text::toLowerCase(MTS::Text::formatHex(dChecksum)); - // "send" empty string to read acknoledge string - std::string sResult = sendCommand("", DEFAULT_BAIL_STRINGS, 10000, 0x00); + // Wait for confirmation of successful upload completion + sResponse += waitResponse(DEFAULT_BAIL_STRINGS, iUploadResultTimeout); - if (sResult.find(sExpectedResult) != std::string::npos) { - printDebug("Radio returned: [%s]", sResult.c_str()); + if (sResponse.find(sExpectedResult) != std::string::npos) { + printDebug("Radio returned: [%s]", sResponse.c_str()); printTrace("Upload finished, checksum matched"); - } else { - printError("Upload failed: checksum mismatch. Expected: [%s], Actual: [%s]", sExpectedResult.c_str(), sResult.c_str()); - abortFileUpload(); - rc = FAILURE; - } - - if (rc == SUCCESS) { callNextStep(stepCb, "FILE Info: Upload finished successfully"); - } else { - callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); + + return SUCCESS; } - return rc; + printError("Upload failed: checksum mismatch. Expected: [%s], Actual: [%s]", sExpectedResult.c_str(), sResponse.c_str()); + callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); + + abortFileUpload(); + return FAILURE; } ICellularRadio::CODE QuectelRadio::removeFile(const std::string& sTargetFilename) { @@ -1159,15 +1175,24 @@ std::string QuectelRadio::getFumoEarlyErrorCode(const std::string& sRadioInput) return sResult; } -ICellularRadio::CODE QuectelRadio::startFileUpload(const std::string& sTargetFilename, size_t nBytes) { +ICellularRadio::CODE QuectelRadio::startFileUpload(const std::string& sTargetFilename, size_t nBytes, uint16_t uRxTimeout, bool bAckEnabled) { const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; const int dTimeout = 1000; //ms std::string sCommand, sResult; + // Format: AT+QFUPL=[,[,[,]] + sCommand = "AT+QFUPL=\""; sCommand += sTargetFilename; sCommand += "\","; sCommand += MTS::Text::format(nBytes); + sCommand += ","; + sCommand += MTS::Text::format(uRxTimeout); + + if (bAckEnabled) { + // ACK mode enabled + sCommand += ",1"; + } sResult = sendCommand(sCommand, vBailStrings, dTimeout); if (sResult.find(ICellularRadio::RSP_CONNECT) == std::string::npos) { -- cgit v1.2.3