Skip to main content
Adding code
Source Link
Nino
  • 411
  • 4
  • 9

EDIT:

As per Juraj request, I am adding the code as a guide you can use to get ideas. You can't run it as is, you would need to add variable declaration and wrap it in a function.

As updating wrong file will most likely brick your board, I would just add that before running this code I check the filename to make sure it is the correct BIN for the board downloading it, I am using constants like WIFI, GSM, and board name and expect to see them in the filename.

I also send the file length from the server as part of the MQTT update request and check it against my own bytes count and I am not relying on TCP to provide an error on corrupted data and do a my own checksum test.

#include <ArduinoOTA.h>  // https://github.com/jandrassy/ArduinoOTA

// Access the file
HttpClient client(*transport, upgradeServer, upgradePort); 
client.get(upgradePath);

int statusCode = client.responseStatusCode();
  /*
   0   HTTP_SUCCESS
  -1  HTTP_ERROR_CONNECTION_FAILED  The end of the headers has been reached.  This consumes the '\n' Could not connect to the server
  -2  HTTP_ERROR_API                This call was made when the HttpClient class wasn't expecting it to be called.
                                    Usually indicates your code is using the class incorrectly
  -3  HTTP_ERROR_TIMED_OUT          Spent too long waiting for a reply
  -4  HTTP_ERROR_INVALID_RESPONSE   The response from the server is invalid, is it definitely an HTTP server?
  */

if (statusCode != 200) {
  client.stop();
  // here you can do something with statusCode like notifying over MQTT
}

long length = client.contentLength();
if (length == HttpClient::kNoContentLengthHeader) {
  client.stop();
  //Server didn't provide Content-length header, abort.
  return; 
}
  
if (!InternalStorage.open(length)) {
  client.stop();
  // Not enough space to store the file, abort.
  return;
}

// Start update, length holds the file size in bytes

client.setTimeout(5000);
byte b;
bool ok;
long fileLength = length;
unsigned long otaStartMillis = millis();
int localChecksum = 0;

while (length > 0) {

  ok = client.readBytes(&b, 1);
  if (!ok) {
   // Abort, stopped after ((millis() - otaStartMillis)/1000) Sec
    return;
  }

  InternalStorage.write(b);
  int bValue = (int)b;
  localChecksum = localChecksum + bValue;
  
  length--;
}

InternalStorage.close();
client.stop();

if (localChecksum != remoteCkecksum) {
 // Checksum mismatch, abort.
  return;
}

  
if (length > 0) {
  // Timeout at byte length, abort.
  return;
}

InternalStorage.apply(); // this doesn't return, board should restart

Serial.println("Update failed"); //shown only if update failed

Another opportunity to thank Jurak for this library!

EDIT:

As per Juraj request, I am adding the code as a guide you can use to get ideas. You can't run it as is, you would need to add variable declaration and wrap it in a function.

As updating wrong file will most likely brick your board, I would just add that before running this code I check the filename to make sure it is the correct BIN for the board downloading it, I am using constants like WIFI, GSM, and board name and expect to see them in the filename.

I also send the file length from the server as part of the MQTT update request and check it against my own bytes count and I am not relying on TCP to provide an error on corrupted data and do a my own checksum test.

#include <ArduinoOTA.h>  // https://github.com/jandrassy/ArduinoOTA

// Access the file
HttpClient client(*transport, upgradeServer, upgradePort); 
client.get(upgradePath);

int statusCode = client.responseStatusCode();
  /*
   0   HTTP_SUCCESS
  -1  HTTP_ERROR_CONNECTION_FAILED  The end of the headers has been reached.  This consumes the '\n' Could not connect to the server
  -2  HTTP_ERROR_API                This call was made when the HttpClient class wasn't expecting it to be called.
                                    Usually indicates your code is using the class incorrectly
  -3  HTTP_ERROR_TIMED_OUT          Spent too long waiting for a reply
  -4  HTTP_ERROR_INVALID_RESPONSE   The response from the server is invalid, is it definitely an HTTP server?
  */

if (statusCode != 200) {
  client.stop();
  // here you can do something with statusCode like notifying over MQTT
}

long length = client.contentLength();
if (length == HttpClient::kNoContentLengthHeader) {
  client.stop();
  //Server didn't provide Content-length header, abort.
  return; 
}
  
if (!InternalStorage.open(length)) {
  client.stop();
  // Not enough space to store the file, abort.
  return;
}

// Start update, length holds the file size in bytes

client.setTimeout(5000);
byte b;
bool ok;
long fileLength = length;
unsigned long otaStartMillis = millis();
int localChecksum = 0;

while (length > 0) {

  ok = client.readBytes(&b, 1);
  if (!ok) {
   // Abort, stopped after ((millis() - otaStartMillis)/1000) Sec
    return;
  }

  InternalStorage.write(b);
  int bValue = (int)b;
  localChecksum = localChecksum + bValue;
  
  length--;
}

InternalStorage.close();
client.stop();

if (localChecksum != remoteCkecksum) {
 // Checksum mismatch, abort.
  return;
}

  
if (length > 0) {
  // Timeout at byte length, abort.
  return;
}

InternalStorage.apply(); // this doesn't return, board should restart

Serial.println("Update failed"); //shown only if update failed

Another opportunity to thank Jurak for this library!

Source Link
Nino
  • 411
  • 4
  • 9

I am not familiar with OTA for ESP32 but with SAMD21 using ArduinoOTA library and there the download is not done over MQTT but HTTPS.

Link to download is sent over MQTT but that's where it ends, download over HTTPS, verification of received data using checksum and bytes count and once InternalStorage.apply() executed it swaps the program downloaded with the older version and reboot.