summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-08-06 14:47:49 +0300
committerSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-08-07 15:52:22 +0300
commitc1a58778eecd0115d746afbca1079683b244b672 (patch)
tree63bbc35791b58032391186480d61a88f399a8d3b
parente1855057708468bc5383d948be70da7179730231 (diff)
downloadlibmts-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.
-rw-r--r--include/mts/MTS_IO_QuectelRadio.h2
-rw-r--r--src/MTS_IO_QuectelRadio.cpp71
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<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) {