diff options
author | Serhii Kostiuk <serhii.o.kostiuk@globallogic.com> | 2020-08-06 14:47:49 +0300 |
---|---|---|
committer | Serhii Kostiuk <serhii.o.kostiuk@globallogic.com> | 2020-08-07 15:52:22 +0300 |
commit | c1a58778eecd0115d746afbca1079683b244b672 (patch) | |
tree | 63bbc35791b58032391186480d61a88f399a8d3b /src | |
parent | e1855057708468bc5383d948be70da7179730231 (diff) | |
download | libmts-io-c1a58778eecd0115d746afbca1079683b244b672.tar.gz libmts-io-c1a58778eecd0115d746afbca1079683b244b672.tar.bz2 libmts-io-c1a58778eecd0115d746afbca1079683b244b672.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/MTS_IO_QuectelRadio.cpp | 71 |
1 files changed, 48 insertions, 23 deletions
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<char, FILE_CHUNK_SIZE> 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<std::string> vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; const int dTimeout = 1000; //ms std::string sCommand, sResult; + // Format: AT+QFUPL=<filename>[,<file_size>[,<timeout>[,<ackmode>]] + 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) { |