UPDATE: I was doing something dumb (see below). The code here works!
I’m implementing my own Boron OTA update routine that pulls firmware from a web server. I’ve based my code on some of the great examples here on the forum, and all seems good until Spark_Finish_Firmware_Update fails with return code 1.
After running with debug output and looking into deviceOS, I determined the reason for the return code of 1 (failure) is caused by CRC not matching. Specifically it is a CRC mismatch (MODULE_VALIDATION_INTEGRITY).
Results of debug output:
0000025665 [app] INFO: Firmware Received!
0000025665 [app] INFO: Received 23904 bytes
0000025666 [app] INFO: Launching Update
0000025677 [hal] INFO: module fetched 1, checks=5e, result=5c
0000025677 [hal] WARN: OTA module not applied
I have verified that the firmware is being downloaded correctly by dumping the raw hex to Serial output as it is being saved by Spark_Save_Firmware_Chunk. I converted that output back to binary, and it matches byte for byte the firmware binary I am trying to flash. The binary I used was a small (about 5KB) binary built in Workbench. I used target/simple.bin (the project firmware is named simple).
Any guidance on what I’m doing wrong here? I’ve seen a few other cases where people were having trouble with the CRC, but haven’t found any solutions.
My test code does nothing in setup() or loop() except enable Serial and start the OTA. Any help would be appreciated!
ota-test.ino:
#include <OTA.h>
SerialLogHandler logHandler(LOG_LEVEL_TRACE);
char c;
// setup() runs once, when the device is first turned on.
void setup() {
// Put initialization like pinMode and begin functions here.
Serial.begin(115200);
delay(5000);
Serial.println("V2");
Serial.println("Hello! Press o for OTA.");
}
// loop() runs over and over again, as quickly as it can execute.
void loop() {
if (Serial.available()) {
c=Serial.read();
if (c=='o') {
c=0;
OTA *ota = new OTA();
ota->GetUpdate("mydomain.com","/firmware/simple.bin");
}
}
}
OTA.cpp:
#include <spark_wiring_tcpclient.h>
#include <Particle.h>
#include <system_update.h>
#include "OTA.h"
#include "version.h"
TCPClient *swclient;
OTA::OTA() {
Serial.println("OTA() created without TLS");
}
OTA::~OTA() {
delete swclient;
}
bool OTA::GetUpdate(char *host, const char *path) {
unsigned char buf[256];
char tmpstr[4];
unsigned char c = 0;
unsigned char last = 0;
system_tick_t timeout = 0;
bool done = false;
bool gotHeader = false;
bool hf = false;
bool ok = false;
int avail = 0;
char *pos;
unsigned int response = 0;
unsigned int length = 0;
unsigned int timePrg = 0;
unsigned int received = 0;
FileTransfer::Descriptor file;
swclient = new TCPClient();
String header = "";
Log("OTA: free memory %d bytes", System.freeMemory());
Log("Getting %s%s", host, path);
Log("mem: %d", System.freeMemory());
int result = swclient->connect((char *)host, 80);
Log("connect: %d", result);
if (result < 0) {
return false;
}
// Send request to web server.
int len = sprintf((char *)buf,
"GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Content-Length: 0\r\n"
"User-Agent: picsil Sense %s\r\n"
"\r\n", path, host, PICSIL_VERSION);
Log("OTA request: %s", buf);
swclient->write(buf, len );
// GET response.
memset(buf, 0, sizeof(buf));
timeout = millis();
delay(250);
while (!done) {
if (!swclient->connected()) {
Log("Connection Error!");
delay(1000);
System.reset(); //TODO: set reason
}
if (millis() - timeout > 120000) {
Log("Timeout!");
delay(1000);
System.reset(); //TODO: set reason
}
avail = swclient->available();
if (avail>0) {
//Log("avail: %d", avail);
if (!gotHeader) {
c = swclient->read();
Serial.printf("%c",c);
sprintf(tmpstr, "%c", c);
header.concat(tmpstr);
if (c=='\n' && last=='\r') {
if (hf) {
gotHeader=true;
Log("Header: %s", header.c_str());
pos=strstr(header.c_str(), " ");
if (pos) {
pos += 1;
response = atoi(pos);
Log("Response Code: %d", response);
}
pos = strstr(header.c_str(), "Content-Length: ");
if (pos) {
pos += 16;
length = atoi(pos);
Log("Download Length: %d", length);
}
if (response != 200) {
Log("Invalid Response Code %d!", response);
delay(1000);
System.reset(); //TODO: set reason
}
if (!done && length>0) {
Log("Preparing Update");
file.file_length = length;
file.file_address = 0;
file.chunk_size = 256;
file.store = FileTransfer::Store::FIRMWARE;
result = Spark_Prepare_For_Firmware_Update(file, 0, NULL);
if (result != 0) {
Log("Prepare Failed with result %d", result);
delay(1000);
System.reset(); //TODO: add reason
} else {
file.chunk_address = file.file_address;
}
}
} else {
hf = true;
}
} else {
if (c != '\r') {
hf = false;
}
}
last = c;
timePrg = millis();
} else {
//Log("avail: %d", avail);
if (avail>sizeof(buf)) avail = sizeof(buf);
avail = swclient->read(&buf[0], avail);
received += avail;
//Set chunk size from downloaded data
file.chunk_size = avail;
result = Spark_Save_Firmware_Chunk(file, &buf[0], NULL);
if (result != 0) {
Log("Save chunk failed with result %d", result);
delay(1000);
System.reset(); //TODO: set reason
}
Log("Received %d of %d bytes. Chunk Size %d. Chunk addr=%x", received, length, avail, file.chunk_address);
//Move start to next chunk
file.chunk_address += file.chunk_size;
if (length > 0 && received >= length) {
done=true;
ok=true;
Log("Firmware Received!");
}
}
timeout = millis();
}
}
if (received == 0) {
Log("Header(2): %s", header.c_str());
} else {
Log("Received %d bytes", received);
}
//swclient->stop();
if (ok) {
Log("Launching Update");
} else {
Log("Process finished with error. Resetting.");
delay(1000);
System.reset();
}
result = Spark_Finish_Firmware_Update(file, ok, NULL);
if (result != 0) {
Log("Finish failed with result code %d", result);
delay(1000);
System.reset();
} else {
Log("Success!! System resetting.");
delay(1000);
System.reset();
}
}
Finally, here is the output of the above code:
0000010682 [comm.protocol] ERROR: Channel failed to send message with error-code <0>
V2
Hello! Press o for OTA.
OTA() created without TLS
0000018736 [app] INFO: OTA: free memory 70184 bytes
0000018737 [app] INFO: Getting mydomain.com/firmware/simple.bin
0000018738 [app] INFO: mem: 70184
0000019127 [app] INFO: connect: 1
0000019127 [app] INFO: OTA request: GET /firmware/simple.bin HTTP/1.0
Host: mydomain.com
Content-Length: 0
User-Agent: picsil Sense 0.0.1
HTTP/1.1 200 OK
Date: Sun, 26 May 2019 18:12:19 GMT
Server: Apache/2.4.39 (cPanel) OpenSSL/1.0.2r mod_bwlimited/1.4
Last-Modified: Sun, 26 May 2019 16:57:04 GMT
ETag: "480419-1394-589cd523e17e7"
Accept-Ranges: bytes
Content-Length: 5012
Connection: close
Content-Type: application/octet-stream
0000019631 [app] INFO: Header: HTTP/1.1 200 OK
Date: Sun, 26 May 2019 18:12:19 GMT
Server: Apache/2.4.39 (cPanel) OpenSSL/1.0.2r mod_bwlimited/1.4
Last-Modified: Sun, 26 May 2019~
0000019633 [app] INFO: Response Code: 200
0000019634 [app] INFO: Download Length: 5012
0000019634 [app] INFO: Preparing Update
0000019635 [app] INFO: Received 79 of 5012 bytes. Chunk Size 79. Chunk addr=202a06a8
0000019636 [app] INFO: Received 207 of 5012 bytes. Chunk Size 128. Chunk addr=202a06f7
0000019638 [app] INFO: Received 335 of 5012 bytes. Chunk Size 128. Chunk addr=202a0777
0000019639 [app] INFO: Received 463 of 5012 bytes. Chunk Size 128. Chunk addr=202a07f7
0000019640 [app] INFO: Received 591 of 5012 bytes. Chunk Size 128. Chunk addr=202a0877
0000019642 [app] INFO: Received 655 of 5012 bytes. Chunk Size 64. Chunk addr=202a08f7
0000019679 [app] INFO: Received 783 of 5012 bytes. Chunk Size 128. Chunk addr=202a0937
0000019680 [app] INFO: Received 911 of 5012 bytes. Chunk Size 128. Chunk addr=202a09b7
0000019682 [app] INFO: Received 1039 of 5012 bytes. Chunk Size 128. Chunk addr=202a0a37
0000019683 [app] INFO: Received 1167 of 5012 bytes. Chunk Size 128. Chunk addr=202a0ab7
0000019684 [app] INFO: Received 1295 of 5012 bytes. Chunk Size 128. Chunk addr=202a0b37
0000019686 [app] INFO: Received 1423 of 5012 bytes. Chunk Size 128. Chunk addr=202a0bb7
0000019687 [app] INFO: Received 1551 of 5012 bytes. Chunk Size 128. Chunk addr=202a0c37
0000019688 [app] INFO: Received 1615 of 5012 bytes. Chunk Size 64. Chunk addr=202a0cb7
0000019780 [app] INFO: Received 1743 of 5012 bytes. Chunk Size 128. Chunk addr=202a0cf7
0000019782 [app] INFO: Received 1871 of 5012 bytes. Chunk Size 128. Chunk addr=202a0d77
0000019783 [app] INFO: Received 1999 of 5012 bytes. Chunk Size 128. Chunk addr=202a0df7
0000019784 [app] INFO: Received 2127 of 5012 bytes. Chunk Size 128. Chunk addr=202a0e77
0000019786 [app] INFO: Received 2255 of 5012 bytes. Chunk Size 128. Chunk addr=202a0ef7
0000019787 [app] INFO: Received 2383 of 5012 bytes. Chunk Size 128. Chunk addr=202a0f77
0000019788 [app] INFO: Received 2511 of 5012 bytes. Chunk Size 128. Chunk addr=202a0ff7
0000019790 [app] INFO: Received 2575 of 5012 bytes. Chunk Size 64. Chunk addr=202a1077
0000019881 [app] INFO: Received 2703 of 5012 bytes. Chunk Size 128. Chunk addr=202a10b7
0000019883 [app] INFO: Received 2831 of 5012 bytes. Chunk Size 128. Chunk addr=202a1137
0000019884 [app] INFO: Received 2959 of 5012 bytes. Chunk Size 128. Chunk addr=202a11b7
0000019885 [app] INFO: Received 3087 of 5012 bytes. Chunk Size 128. Chunk addr=202a1237
0000019887 [app] INFO: Received 3215 of 5012 bytes. Chunk Size 128. Chunk addr=202a12b7
0000019888 [app] INFO: Received 3343 of 5012 bytes. Chunk Size 128. Chunk addr=202a1337
0000019889 [app] INFO: Received 3471 of 5012 bytes. Chunk Size 128. Chunk addr=202a13b7
0000019891 [app] INFO: Received 3535 of 5012 bytes. Chunk Size 64. Chunk addr=202a1437
0000020034 [app] INFO: Received 3663 of 5012 bytes. Chunk Size 128. Chunk addr=202a1477
0000020035 [app] INFO: Received 3791 of 5012 bytes. Chunk Size 128. Chunk addr=202a14f7
0000020037 [app] INFO: Received 3919 of 5012 bytes. Chunk Size 128. Chunk addr=202a1577
0000020038 [app] INFO: Received 4047 of 5012 bytes. Chunk Size 128. Chunk addr=202a15f7
0000020039 [app] INFO: Received 4175 of 5012 bytes. Chunk Size 128. Chunk addr=202a1677
0000020041 [app] INFO: Received 4303 of 5012 bytes. Chunk Size 128. Chunk addr=202a16f7
0000020042 [app] INFO: Received 4431 of 5012 bytes. Chunk Size 128. Chunk addr=202a1777
0000020044 [app] INFO: Received 4559 of 5012 bytes. Chunk Size 128. Chunk addr=202a17f7
0000020045 [app] INFO: Received 4687 of 5012 bytes. Chunk Size 128. Chunk addr=202a1877
0000020046 [app] INFO: Received 4815 of 5012 bytes. Chunk Size 128. Chunk addr=202a18f7
0000020048 [app] INFO: Received 4943 of 5012 bytes. Chunk Size 128. Chunk addr=202a1977
0000020049 [app] INFO: Received 5012 of 5012 bytes. Chunk Size 69. Chunk addr=202a19f7
0000020050 [app] INFO: Firmware Received!
0000020050 [app] INFO: Received 5012 bytes
0000020051 [app] INFO: Launching Update
0000021063 [app] INFO: Finish failed with result code 1