Prvni ulozeni z chegewara githubu
This commit is contained in:
@ -0,0 +1,283 @@
|
||||
/**
|
||||
AWS S3 OTA Update
|
||||
Date: 14th June 2017
|
||||
Author: Arvind Ravulavaru <https://github.com/arvindr21>
|
||||
Purpose: Perform an OTA update from a bin located in Amazon S3 (HTTP Only)
|
||||
|
||||
Upload:
|
||||
Step 1 : Download the sample bin file from the examples folder
|
||||
Step 2 : Upload it to your Amazon S3 account, in a bucket of your choice
|
||||
Step 3 : Once uploaded, inside S3, select the bin file >> More (button on top of the file list) >> Make Public
|
||||
Step 4 : You S3 URL => http://bucket-name.s3.ap-south-1.amazonaws.com/sketch-name.ino.bin
|
||||
Step 5 : Build the above URL and fire it either in your browser or curl it `curl -I -v http://bucket-name.ap-south-1.amazonaws.com/sketch-name.ino.bin` to validate the same
|
||||
Step 6: Plug in your SSID, Password, S3 Host and Bin file below
|
||||
|
||||
Build & upload
|
||||
Step 1 : Menu > Sketch > Export Compiled Library. The bin file will be saved in the sketch folder (Menu > Sketch > Show Sketch folder)
|
||||
Step 2 : Upload bin to S3 and continue the above process
|
||||
|
||||
// Check the bottom of this sketch for sample serial monitor log, during and after successful OTA Update
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <Update.h>
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
// Variables to validate
|
||||
// response from S3
|
||||
long contentLength = 0;
|
||||
bool isValidContentType = false;
|
||||
|
||||
// Your SSID and PSWD that the chip needs
|
||||
// to connect to
|
||||
const char* SSID = "YOUR-SSID";
|
||||
const char* PSWD = "YOUR-SSID-PSWD";
|
||||
|
||||
// S3 Bucket Config
|
||||
String host = "bucket-name.s3.ap-south-1.amazonaws.com"; // Host => bucket-name.s3.region.amazonaws.com
|
||||
int port = 80; // Non https. For HTTPS 443. As of today, HTTPS doesn't work.
|
||||
String bin = "/sketch-name.ino.bin"; // bin file name with a slash in front.
|
||||
|
||||
// Utility to extract header value from headers
|
||||
String getHeaderValue(String header, String headerName) {
|
||||
return header.substring(strlen(headerName.c_str()));
|
||||
}
|
||||
|
||||
// OTA Logic
|
||||
void execOTA() {
|
||||
Serial.println("Connecting to: " + String(host));
|
||||
// Connect to S3
|
||||
if (client.connect(host.c_str(), port)) {
|
||||
// Connection Succeed.
|
||||
// Fecthing the bin
|
||||
Serial.println("Fetching Bin: " + String(bin));
|
||||
|
||||
// Get the contents of the bin file
|
||||
client.print(String("GET ") + bin + " HTTP/1.1\r\n" +
|
||||
"Host: " + host + "\r\n" +
|
||||
"Cache-Control: no-cache\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
|
||||
// Check what is being sent
|
||||
// Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" +
|
||||
// "Host: " + host + "\r\n" +
|
||||
// "Cache-Control: no-cache\r\n" +
|
||||
// "Connection: close\r\n\r\n");
|
||||
|
||||
unsigned long timeout = millis();
|
||||
while (client.available() == 0) {
|
||||
if (millis() - timeout > 5000) {
|
||||
Serial.println("Client Timeout !");
|
||||
client.stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Once the response is available,
|
||||
// check stuff
|
||||
|
||||
/*
|
||||
Response Structure
|
||||
HTTP/1.1 200 OK
|
||||
x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0=
|
||||
x-amz-request-id: 2D56B47560B764EC
|
||||
Date: Wed, 14 Jun 2017 03:33:59 GMT
|
||||
Last-Modified: Fri, 02 Jun 2017 14:50:11 GMT
|
||||
ETag: "d2afebbaaebc38cd669ce36727152af9"
|
||||
Accept-Ranges: bytes
|
||||
Content-Type: application/octet-stream
|
||||
Content-Length: 357280
|
||||
Server: AmazonS3
|
||||
|
||||
{{BIN FILE CONTENTS}}
|
||||
|
||||
*/
|
||||
while (client.available()) {
|
||||
// read line till /n
|
||||
String line = client.readStringUntil('\n');
|
||||
// remove space, to check if the line is end of headers
|
||||
line.trim();
|
||||
|
||||
// if the the line is empty,
|
||||
// this is end of headers
|
||||
// break the while and feed the
|
||||
// remaining `client` to the
|
||||
// Update.writeStream();
|
||||
if (!line.length()) {
|
||||
//headers ended
|
||||
break; // and get the OTA started
|
||||
}
|
||||
|
||||
// Check if the HTTP Response is 200
|
||||
// else break and Exit Update
|
||||
if (line.startsWith("HTTP/1.1")) {
|
||||
if (line.indexOf("200") < 0) {
|
||||
Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// extract headers here
|
||||
// Start with content length
|
||||
if (line.startsWith("Content-Length: ")) {
|
||||
contentLength = atol((getHeaderValue(line, "Content-Length: ")).c_str());
|
||||
Serial.println("Got " + String(contentLength) + " bytes from server");
|
||||
}
|
||||
|
||||
// Next, the content type
|
||||
if (line.startsWith("Content-Type: ")) {
|
||||
String contentType = getHeaderValue(line, "Content-Type: ");
|
||||
Serial.println("Got " + contentType + " payload.");
|
||||
if (contentType == "application/octet-stream") {
|
||||
isValidContentType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Connect to S3 failed
|
||||
// May be try?
|
||||
// Probably a choppy network?
|
||||
Serial.println("Connection to " + String(host) + " failed. Please check your setup");
|
||||
// retry??
|
||||
// execOTA();
|
||||
}
|
||||
|
||||
// Check what is the contentLength and if content type is `application/octet-stream`
|
||||
Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));
|
||||
|
||||
// check contentLength and content type
|
||||
if (contentLength && isValidContentType) {
|
||||
// Check if there is enough to OTA Update
|
||||
bool canBegin = Update.begin(contentLength);
|
||||
|
||||
// If yes, begin
|
||||
if (canBegin) {
|
||||
Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!");
|
||||
// No activity would appear on the Serial monitor
|
||||
// So be patient. This may take 2 - 5mins to complete
|
||||
size_t written = Update.writeStream(client);
|
||||
|
||||
if (written == contentLength) {
|
||||
Serial.println("Written : " + String(written) + " successfully");
|
||||
} else {
|
||||
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?" );
|
||||
// retry??
|
||||
// execOTA();
|
||||
}
|
||||
|
||||
if (Update.end()) {
|
||||
Serial.println("OTA done!");
|
||||
if (Update.isFinished()) {
|
||||
Serial.println("Update successfully completed. Rebooting.");
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Update not finished? Something went wrong!");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
||||
}
|
||||
} else {
|
||||
// not enough space to begin OTA
|
||||
// Understand the partitions and
|
||||
// space availability
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
client.flush();
|
||||
}
|
||||
} else {
|
||||
Serial.println("There was no content in the response");
|
||||
client.flush();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//Begin Serial
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
Serial.println("Connecting to " + String(SSID));
|
||||
|
||||
// Connect to provided SSID and PSWD
|
||||
WiFi.begin(SSID, PSWD);
|
||||
|
||||
// Wait for connection to establish
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print("."); // Keep the serial monitor lit!
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// Connection Succeed
|
||||
Serial.println("");
|
||||
Serial.println("Connected to " + String(SSID));
|
||||
|
||||
// Execute OTA Update
|
||||
execOTA();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// chill
|
||||
}
|
||||
|
||||
/*
|
||||
* Serial Monitor log for this sketch
|
||||
*
|
||||
* If the OTA succeeded, it would load the preference sketch, with a small modification. i.e.
|
||||
* Print `OTA Update succeeded!! This is an example sketch : Preferences > StartCounter`
|
||||
* And then keeps on restarting every 10 seconds, updating the preferences
|
||||
*
|
||||
*
|
||||
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
|
||||
configsip: 0, SPIWP:0x00
|
||||
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
|
||||
mode:DIO, clock div:1
|
||||
load:0x3fff0008,len:8
|
||||
load:0x3fff0010,len:160
|
||||
load:0x40078000,len:10632
|
||||
load:0x40080000,len:252
|
||||
entry 0x40080034
|
||||
Connecting to SSID
|
||||
......
|
||||
Connected to SSID
|
||||
Connecting to: bucket-name.s3.ap-south-1.amazonaws.com
|
||||
Fetching Bin: /StartCounter.ino.bin
|
||||
Got application/octet-stream payload.
|
||||
Got 357280 bytes from server
|
||||
contentLength : 357280, isValidContentType : 1
|
||||
Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!
|
||||
Written : 357280 successfully
|
||||
OTA done!
|
||||
Update successfully completed. Rebooting.
|
||||
ets Jun 8 2016 00:22:57
|
||||
|
||||
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
|
||||
configsip: 0, SPIWP:0x00
|
||||
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
|
||||
mode:DIO, clock div:1
|
||||
load:0x3fff0008,len:8
|
||||
load:0x3fff0010,len:160
|
||||
load:0x40078000,len:10632
|
||||
load:0x40080000,len:252
|
||||
entry 0x40080034
|
||||
|
||||
OTA Update succeeded!! This is an example sketch : Preferences > StartCounter
|
||||
Current counter value: 1
|
||||
Restarting in 10 seconds...
|
||||
E (102534) wifi: esp_wifi_stop 802 wifi is not init
|
||||
ets Jun 8 2016 00:22:57
|
||||
|
||||
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
|
||||
configsip: 0, SPIWP:0x00
|
||||
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
|
||||
mode:DIO, clock div:1
|
||||
load:0x3fff0008,len:8
|
||||
load:0x3fff0010,len:160
|
||||
load:0x40078000,len:10632
|
||||
load:0x40080000,len:252
|
||||
entry 0x40080034
|
||||
|
||||
OTA Update succeeded!! This is an example sketch : Preferences > StartCounter
|
||||
Current counter value: 2
|
||||
Restarting in 10 seconds...
|
||||
|
||||
....
|
||||
*
|
||||
*/
|
BIN
libraries/Update/examples/AWS_S3_OTA_Update/StartCounter.ino.bin
Normal file
BIN
libraries/Update/examples/AWS_S3_OTA_Update/StartCounter.ino.bin
Normal file
Binary file not shown.
@ -0,0 +1,98 @@
|
||||
// This sketch provide the functionality of OTA Firmware Upgrade
|
||||
#include "WiFi.h"
|
||||
#include "HttpsOTAUpdate.h"
|
||||
// This sketch shows how to implement HTTPS firmware update Over The Air.
|
||||
// Please provide your WiFi credentials, https URL to the firmware image and the server certificate.
|
||||
|
||||
static const char *ssid = "your-ssid"; // your network SSID (name of wifi network)
|
||||
static const char *password = "your-password"; // your network password
|
||||
|
||||
static const char *url = "https://example.com/firmware.bin"; //state url of your firmware image
|
||||
|
||||
static const char *server_certificate = "-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
|
||||
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
|
||||
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
|
||||
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
|
||||
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
|
||||
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
|
||||
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
|
||||
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
|
||||
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
|
||||
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
|
||||
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
|
||||
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
|
||||
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
|
||||
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
|
||||
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
|
||||
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
|
||||
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
|
||||
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
|
||||
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
|
||||
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
|
||||
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
|
||||
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
|
||||
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
|
||||
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
|
||||
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
|
||||
"-----END CERTIFICATE-----";
|
||||
|
||||
static HttpsOTAStatus_t otastatus;
|
||||
|
||||
void HttpEvent(HttpEvent_t *event)
|
||||
{
|
||||
switch(event->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
Serial.println("Http Event Error");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
Serial.println("Http Event On Connected");
|
||||
break;
|
||||
case HTTP_EVENT_HEADER_SENT:
|
||||
Serial.println("Http Event Header Sent");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
Serial.printf("Http Event On Header, key=%s, value=%s\n", event->header_key, event->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
Serial.println("Http Event On Finish");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
Serial.println("Http Event Disconnected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup(){
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.print("Attempting to connect to SSID: ");
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
// attempt to connect to Wifi network:
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
HttpsOTA.onHttpEvent(HttpEvent);
|
||||
Serial.println("Starting OTA");
|
||||
HttpsOTA.begin(url, server_certificate);
|
||||
|
||||
Serial.println("Please Wait it takes some time ...");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
otastatus = HttpsOTA.status();
|
||||
if(otastatus == HTTPS_OTA_SUCCESS) {
|
||||
Serial.println("Firmware written successfully. To reboot device, call API ESP.restart() or PUSH restart button on device");
|
||||
} else if(otastatus == HTTPS_OTA_FAIL) {
|
||||
Serial.println("Firmware Upgrade Fail");
|
||||
}
|
||||
delay(1000);
|
||||
}
|
32
libraries/Update/examples/HTTPS_OTA_Update/Readme.md
Normal file
32
libraries/Update/examples/HTTPS_OTA_Update/Readme.md
Normal file
@ -0,0 +1,32 @@
|
||||
# OTA Firmware Upgrade for Arduino
|
||||
This sketch allows Arduino user to perform Over The Air (OTA) firmware upgrade. It uses HTTPS.
|
||||
|
||||
# API introduced for OTA
|
||||
|
||||
## HttpsOTA.begin(const char * url, const char * server_certificate, bool skip_cert_common_name_check)
|
||||
|
||||
Main API which starts firmware upgrade
|
||||
|
||||
### Parameters
|
||||
* url : URL for the uploaded firmware image
|
||||
* server_certificate : Provide the ota server certificate for authentication via HTTPS
|
||||
* skip_cert_common_name_check : Skip any validation of server certificate CN field
|
||||
|
||||
The default value provided to skip_cert_common_name_check is true
|
||||
|
||||
## HttpsOTA.onHttpEvent(function)
|
||||
|
||||
This API exposes HTTP Events to the user
|
||||
|
||||
### Parameter
|
||||
Function passed has following signature
|
||||
void HttpEvent (HttpEvent_t * event);
|
||||
|
||||
# HttpsOTA.otaStatus()
|
||||
|
||||
It tracks the progress of OTA firmware upgrade.
|
||||
* HTTPS_OTA_IDLE : OTA upgrade have not started yet.
|
||||
* HTTPS_OTA_UPDATNG : OTA upgarde is in progress.
|
||||
* HTTPS_OTA_SUCCESS : OTA upgrade is successful.
|
||||
* HTTPS_OTA_FAIL : OTA upgrade failed.
|
||||
* HTTPS_OTA_ERR : Error occured while creating xEventGroup().
|
112
libraries/Update/examples/SD_Update/SD_Update.ino
Normal file
112
libraries/Update/examples/SD_Update/SD_Update.ino
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
Name: SD_Update.ino
|
||||
Created: 12.09.2017 15:07:17
|
||||
Author: Frederik Merz <frederik.merz@novalight.de>
|
||||
Purpose: Update firmware from SD card
|
||||
|
||||
Steps:
|
||||
1. Flash this image to the ESP32 an run it
|
||||
2. Copy update.bin to a SD-Card, you can basically
|
||||
compile this or any other example
|
||||
then copy and rename the app binary to the sd card root
|
||||
3. Connect SD-Card as shown in SD example,
|
||||
this can also be adapted for SPI
|
||||
3. After successfull update and reboot, ESP32 shall start the new app
|
||||
*/
|
||||
|
||||
#include <Update.h>
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
|
||||
// perform the actual update from a given stream
|
||||
void performUpdate(Stream &updateSource, size_t updateSize) {
|
||||
if (Update.begin(updateSize)) {
|
||||
size_t written = Update.writeStream(updateSource);
|
||||
if (written == updateSize) {
|
||||
Serial.println("Written : " + String(written) + " successfully");
|
||||
}
|
||||
else {
|
||||
Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
|
||||
}
|
||||
if (Update.end()) {
|
||||
Serial.println("OTA done!");
|
||||
if (Update.isFinished()) {
|
||||
Serial.println("Update successfully completed. Rebooting.");
|
||||
}
|
||||
else {
|
||||
Serial.println("Update not finished? Something went wrong!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
}
|
||||
}
|
||||
|
||||
// check given FS for valid update.bin and perform update if available
|
||||
void updateFromFS(fs::FS &fs) {
|
||||
File updateBin = fs.open("/update.bin");
|
||||
if (updateBin) {
|
||||
if(updateBin.isDirectory()){
|
||||
Serial.println("Error, update.bin is not a file");
|
||||
updateBin.close();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t updateSize = updateBin.size();
|
||||
|
||||
if (updateSize > 0) {
|
||||
Serial.println("Try to start update");
|
||||
performUpdate(updateBin, updateSize);
|
||||
}
|
||||
else {
|
||||
Serial.println("Error, file is empty");
|
||||
}
|
||||
|
||||
updateBin.close();
|
||||
|
||||
// whe finished remove the binary from sd card to indicate end of the process
|
||||
fs.remove("/update.bin");
|
||||
}
|
||||
else {
|
||||
Serial.println("Could not load update.bin from sd root");
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
uint8_t cardType;
|
||||
Serial.begin(115200);
|
||||
Serial.println("Welcome to the SD-Update example!");
|
||||
|
||||
// You can uncomment this and build again
|
||||
// Serial.println("Update successfull");
|
||||
|
||||
//first init and check SD card
|
||||
if (!SD.begin()) {
|
||||
rebootEspWithReason("Card Mount Failed");
|
||||
}
|
||||
|
||||
cardType = SD.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
rebootEspWithReason("No SD_MMC card attached");
|
||||
}else{
|
||||
updateFromFS(SD);
|
||||
}
|
||||
}
|
||||
|
||||
void rebootEspWithReason(String reason){
|
||||
Serial.println(reason);
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
//will not be reached
|
||||
void loop() {
|
||||
|
||||
}
|
24
libraries/Update/keywords.txt
Normal file
24
libraries/Update/keywords.txt
Normal file
@ -0,0 +1,24 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ultrasound
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Update KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
write KEYWORD2
|
||||
writeStream KEYWORD2
|
||||
printError KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
9
libraries/Update/library.properties
Normal file
9
libraries/Update/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=Update
|
||||
version=2.0.0
|
||||
author=Hristo Gochkov
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=ESP32 Sketch Update Library
|
||||
paragraph=
|
||||
category=Other
|
||||
url=
|
||||
architectures=esp32
|
107
libraries/Update/src/HttpsOTAUpdate.cpp
Normal file
107
libraries/Update/src/HttpsOTAUpdate.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/* OTA task
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
|
||||
#include "HttpsOTAUpdate.h"
|
||||
#define OTA_TASK_STACK_SIZE 9216
|
||||
|
||||
typedef void (*HttpEventCb)(HttpEvent_t*);
|
||||
|
||||
static esp_http_client_config_t config;
|
||||
static HttpEventCb cb;
|
||||
static EventGroupHandle_t ota_status = NULL;//check for ota status
|
||||
static EventBits_t set_bit;
|
||||
|
||||
const int OTA_IDLE_BIT = BIT0;
|
||||
const int OTA_UPDATING_BIT = BIT1;
|
||||
const int OTA_SUCCESS_BIT = BIT2;
|
||||
const int OTA_FAIL_BIT = BIT3;
|
||||
|
||||
esp_err_t http_event_handler(esp_http_client_event_t *event)
|
||||
{
|
||||
cb(event);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void https_ota_task(void *param)
|
||||
{
|
||||
if(ota_status) {
|
||||
xEventGroupSetBits(ota_status, OTA_UPDATING_BIT);
|
||||
xEventGroupClearBits(ota_status, OTA_IDLE_BIT);
|
||||
}
|
||||
esp_err_t ret = esp_https_ota((const esp_http_client_config_t *)param);
|
||||
if(ret == ESP_OK) {
|
||||
if(ota_status) {
|
||||
xEventGroupClearBits(ota_status, OTA_UPDATING_BIT);
|
||||
xEventGroupSetBits(ota_status, OTA_SUCCESS_BIT);
|
||||
}
|
||||
} else {
|
||||
if(ota_status) {
|
||||
xEventGroupClearBits(ota_status, OTA_UPDATING_BIT);
|
||||
xEventGroupSetBits(ota_status, OTA_FAIL_BIT);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
HttpsOTAStatus_t HttpsOTAUpdateClass::status()
|
||||
{
|
||||
if(ota_status) {
|
||||
set_bit = xEventGroupGetBits(ota_status);
|
||||
if(set_bit == OTA_IDLE_BIT) {
|
||||
return HTTPS_OTA_IDLE;
|
||||
}
|
||||
if(set_bit == OTA_UPDATING_BIT) {
|
||||
return HTTPS_OTA_UPDATING;
|
||||
}
|
||||
if(set_bit == OTA_SUCCESS_BIT) {
|
||||
return HTTPS_OTA_SUCCESS;
|
||||
}
|
||||
if(set_bit == OTA_FAIL_BIT) {
|
||||
return HTTPS_OTA_FAIL;
|
||||
}
|
||||
}
|
||||
return HTTPS_OTA_ERR;
|
||||
}
|
||||
|
||||
void HttpsOTAUpdateClass::onHttpEvent(HttpEventCb cbEvent)
|
||||
{
|
||||
cb = cbEvent;
|
||||
}
|
||||
|
||||
void HttpsOTAUpdateClass::begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check)
|
||||
{
|
||||
config.url = url;
|
||||
config.cert_pem = cert_pem;
|
||||
config.skip_cert_common_name_check = skip_cert_common_name_check;
|
||||
config.event_handler = http_event_handler;
|
||||
|
||||
if(!ota_status) {
|
||||
ota_status = xEventGroupCreate();
|
||||
if(!ota_status) {
|
||||
log_e("OTA Event Group Create Failed");
|
||||
}
|
||||
xEventGroupSetBits(ota_status, OTA_IDLE_BIT);
|
||||
}
|
||||
|
||||
if (xTaskCreate(&https_ota_task, "https_ota_task", OTA_TASK_STACK_SIZE, &config, 5, NULL) != pdPASS) {
|
||||
log_e("Couldn't create ota task\n");
|
||||
}
|
||||
}
|
||||
|
||||
HttpsOTAUpdateClass HttpsOTA;
|
24
libraries/Update/src/HttpsOTAUpdate.h
Normal file
24
libraries/Update/src/HttpsOTAUpdate.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef HTPSPOTUADATE_H
|
||||
#define HTPSPOTUADATE_H
|
||||
#include "esp_http_client.h"
|
||||
#define HttpEvent_t esp_http_client_event_t
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HTTPS_OTA_IDLE,
|
||||
HTTPS_OTA_UPDATING,
|
||||
HTTPS_OTA_SUCCESS,
|
||||
HTTPS_OTA_FAIL,
|
||||
HTTPS_OTA_ERR
|
||||
}HttpsOTAStatus_t;
|
||||
|
||||
class HttpsOTAUpdateClass {
|
||||
|
||||
public:
|
||||
void begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check = true);
|
||||
void onHttpEvent(void (*http_event_cb_t)(HttpEvent_t *));
|
||||
HttpsOTAStatus_t status();
|
||||
};
|
||||
|
||||
extern HttpsOTAUpdateClass HttpsOTA;
|
||||
#endif
|
195
libraries/Update/src/Update.h
Normal file
195
libraries/Update/src/Update.h
Normal file
@ -0,0 +1,195 @@
|
||||
#ifndef ESP8266UPDATER_H
|
||||
#define ESP8266UPDATER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MD5Builder.h>
|
||||
#include <functional>
|
||||
#include "esp_partition.h"
|
||||
|
||||
#define UPDATE_ERROR_OK (0)
|
||||
#define UPDATE_ERROR_WRITE (1)
|
||||
#define UPDATE_ERROR_ERASE (2)
|
||||
#define UPDATE_ERROR_READ (3)
|
||||
#define UPDATE_ERROR_SPACE (4)
|
||||
#define UPDATE_ERROR_SIZE (5)
|
||||
#define UPDATE_ERROR_STREAM (6)
|
||||
#define UPDATE_ERROR_MD5 (7)
|
||||
#define UPDATE_ERROR_MAGIC_BYTE (8)
|
||||
#define UPDATE_ERROR_ACTIVATE (9)
|
||||
#define UPDATE_ERROR_NO_PARTITION (10)
|
||||
#define UPDATE_ERROR_BAD_ARGUMENT (11)
|
||||
#define UPDATE_ERROR_ABORT (12)
|
||||
|
||||
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
|
||||
|
||||
#define U_FLASH 0
|
||||
#define U_SPIFFS 100
|
||||
#define U_AUTH 200
|
||||
|
||||
#define ENCRYPTED_BLOCK_SIZE 16
|
||||
|
||||
#define SPI_SECTORS_PER_BLOCK 16 // usually large erase block is 32k/64k
|
||||
#define SPI_FLASH_BLOCK_SIZE (SPI_SECTORS_PER_BLOCK*SPI_FLASH_SEC_SIZE)
|
||||
|
||||
class UpdateClass {
|
||||
public:
|
||||
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
|
||||
|
||||
UpdateClass();
|
||||
|
||||
/*
|
||||
This callback will be called when Update is receiving data
|
||||
*/
|
||||
UpdateClass& onProgress(THandlerFunction_Progress fn);
|
||||
|
||||
/*
|
||||
Call this to check the space needed for the update
|
||||
Will return false if there is not enough space
|
||||
*/
|
||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW, const char *label = NULL);
|
||||
|
||||
/*
|
||||
Writes a buffer to the flash and increments the address
|
||||
Returns the amount written
|
||||
*/
|
||||
size_t write(uint8_t *data, size_t len);
|
||||
|
||||
/*
|
||||
Writes the remaining bytes from the Stream to the flash
|
||||
Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout
|
||||
Returns the bytes written
|
||||
Should be equal to the remaining bytes when called
|
||||
Usable for slow streams like Serial
|
||||
*/
|
||||
size_t writeStream(Stream &data);
|
||||
|
||||
/*
|
||||
If all bytes are written
|
||||
this call will write the config to eboot
|
||||
and return true
|
||||
If there is already an update running but is not finished and !evenIfRemaining
|
||||
or there is an error
|
||||
this will clear everything and return false
|
||||
the last error is available through getError()
|
||||
evenIfRemaining is helpfull when you update without knowing the final size first
|
||||
*/
|
||||
bool end(bool evenIfRemaining = false);
|
||||
|
||||
/*
|
||||
Aborts the running update
|
||||
*/
|
||||
void abort();
|
||||
|
||||
/*
|
||||
Prints the last error to an output stream
|
||||
*/
|
||||
void printError(Print &out);
|
||||
|
||||
const char * errorString();
|
||||
|
||||
/*
|
||||
sets the expected MD5 for the firmware (hexString)
|
||||
*/
|
||||
bool setMD5(const char * expected_md5);
|
||||
|
||||
/*
|
||||
returns the MD5 String of the successfully ended firmware
|
||||
*/
|
||||
String md5String(void){ return _md5.toString(); }
|
||||
|
||||
/*
|
||||
populated the result with the md5 bytes of the successfully ended firmware
|
||||
*/
|
||||
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
||||
|
||||
//Helpers
|
||||
uint8_t getError(){ return _error; }
|
||||
void clearError(){ _error = UPDATE_ERROR_OK; }
|
||||
bool hasError(){ return _error != UPDATE_ERROR_OK; }
|
||||
bool isRunning(){ return _size > 0; }
|
||||
bool isFinished(){ return _progress == _size; }
|
||||
size_t size(){ return _size; }
|
||||
size_t progress(){ return _progress; }
|
||||
size_t remaining(){ return _size - _progress; }
|
||||
|
||||
/*
|
||||
Template to write from objects that expose
|
||||
available() and read(uint8_t*, size_t) methods
|
||||
faster than the writeStream method
|
||||
writes only what is available
|
||||
*/
|
||||
template<typename T>
|
||||
size_t write(T &data){
|
||||
size_t written = 0;
|
||||
if (hasError() || !isRunning())
|
||||
return 0;
|
||||
|
||||
size_t available = data.available();
|
||||
while(available) {
|
||||
if(_bufferLen + available > remaining()){
|
||||
available = remaining() - _bufferLen;
|
||||
}
|
||||
if(_bufferLen + available > 4096) {
|
||||
size_t toBuff = 4096 - _bufferLen;
|
||||
data.read(_buffer + _bufferLen, toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer())
|
||||
return written;
|
||||
written += toBuff;
|
||||
} else {
|
||||
data.read(_buffer + _bufferLen, available);
|
||||
_bufferLen += available;
|
||||
written += available;
|
||||
if(_bufferLen == remaining()) {
|
||||
if(!_writeBuffer()) {
|
||||
return written;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(remaining() == 0)
|
||||
return written;
|
||||
available = data.available();
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/*
|
||||
check if there is a firmware on the other OTA partition that you can bootinto
|
||||
*/
|
||||
bool canRollBack();
|
||||
/*
|
||||
set the other OTA partition as bootable (reboot to enable)
|
||||
*/
|
||||
bool rollBack();
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _abort(uint8_t err);
|
||||
bool _writeBuffer();
|
||||
bool _verifyHeader(uint8_t data);
|
||||
bool _verifyEnd();
|
||||
bool _enablePartition(const esp_partition_t* partition);
|
||||
bool _chkDataInBlock(const uint8_t *data, size_t len) const; // check if block contains any data or is empty
|
||||
|
||||
|
||||
uint8_t _error;
|
||||
uint8_t *_buffer;
|
||||
uint8_t *_skipBuffer;
|
||||
size_t _bufferLen;
|
||||
size_t _size;
|
||||
THandlerFunction_Progress _progress_callback;
|
||||
uint32_t _progress;
|
||||
uint32_t _paroffset;
|
||||
uint32_t _command;
|
||||
const esp_partition_t* _partition;
|
||||
|
||||
String _target_md5;
|
||||
MD5Builder _md5;
|
||||
|
||||
int _ledPin;
|
||||
uint8_t _ledOn;
|
||||
};
|
||||
|
||||
extern UpdateClass Update;
|
||||
|
||||
#endif
|
417
libraries/Update/src/Updater.cpp
Normal file
417
libraries/Update/src/Updater.cpp
Normal file
@ -0,0 +1,417 @@
|
||||
#include "Update.h"
|
||||
#include "Arduino.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
|
||||
static const char * _err2str(uint8_t _error){
|
||||
if(_error == UPDATE_ERROR_OK){
|
||||
return ("No Error");
|
||||
} else if(_error == UPDATE_ERROR_WRITE){
|
||||
return ("Flash Write Failed");
|
||||
} else if(_error == UPDATE_ERROR_ERASE){
|
||||
return ("Flash Erase Failed");
|
||||
} else if(_error == UPDATE_ERROR_READ){
|
||||
return ("Flash Read Failed");
|
||||
} else if(_error == UPDATE_ERROR_SPACE){
|
||||
return ("Not Enough Space");
|
||||
} else if(_error == UPDATE_ERROR_SIZE){
|
||||
return ("Bad Size Given");
|
||||
} else if(_error == UPDATE_ERROR_STREAM){
|
||||
return ("Stream Read Timeout");
|
||||
} else if(_error == UPDATE_ERROR_MD5){
|
||||
return ("MD5 Check Failed");
|
||||
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
|
||||
return ("Wrong Magic Byte");
|
||||
} else if(_error == UPDATE_ERROR_ACTIVATE){
|
||||
return ("Could Not Activate The Firmware");
|
||||
} else if(_error == UPDATE_ERROR_NO_PARTITION){
|
||||
return ("Partition Could Not be Found");
|
||||
} else if(_error == UPDATE_ERROR_BAD_ARGUMENT){
|
||||
return ("Bad Argument");
|
||||
} else if(_error == UPDATE_ERROR_ABORT){
|
||||
return ("Aborted");
|
||||
}
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
static bool _partitionIsBootable(const esp_partition_t* partition){
|
||||
uint8_t buf[ENCRYPTED_BLOCK_SIZE];
|
||||
if(!partition){
|
||||
return false;
|
||||
}
|
||||
if(!ESP.partitionRead(partition, 0, (uint32_t*)buf, ENCRYPTED_BLOCK_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateClass::_enablePartition(const esp_partition_t* partition){
|
||||
if(!partition){
|
||||
return false;
|
||||
}
|
||||
return ESP.partitionWrite(partition, 0, (uint32_t*) _skipBuffer, ENCRYPTED_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
UpdateClass::UpdateClass()
|
||||
: _error(0)
|
||||
, _buffer(0)
|
||||
, _bufferLen(0)
|
||||
, _size(0)
|
||||
, _progress_callback(NULL)
|
||||
, _progress(0)
|
||||
, _paroffset(0)
|
||||
, _command(U_FLASH)
|
||||
, _partition(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
UpdateClass& UpdateClass::onProgress(THandlerFunction_Progress fn) {
|
||||
_progress_callback = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UpdateClass::_reset() {
|
||||
if (_buffer)
|
||||
delete[] _buffer;
|
||||
_buffer = 0;
|
||||
_bufferLen = 0;
|
||||
_progress = 0;
|
||||
_size = 0;
|
||||
_command = U_FLASH;
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // off
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateClass::canRollBack(){
|
||||
if(_buffer){ //Update is running
|
||||
return false;
|
||||
}
|
||||
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||
return _partitionIsBootable(partition);
|
||||
}
|
||||
|
||||
bool UpdateClass::rollBack(){
|
||||
if(_buffer){ //Update is running
|
||||
return false;
|
||||
}
|
||||
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
|
||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||
}
|
||||
|
||||
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, const char *label) {
|
||||
if(_size > 0){
|
||||
log_w("already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
_ledPin = ledPin;
|
||||
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||
|
||||
_reset();
|
||||
_error = 0;
|
||||
_target_md5 = emptyString;
|
||||
_md5 = MD5Builder();
|
||||
|
||||
if(size == 0) {
|
||||
_error = UPDATE_ERROR_SIZE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command == U_FLASH) {
|
||||
_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if(!_partition){
|
||||
_error = UPDATE_ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
log_d("OTA Partition: %s", _partition->label);
|
||||
}
|
||||
else if (command == U_SPIFFS) {
|
||||
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, label);
|
||||
_paroffset = 0;
|
||||
if(!_partition){
|
||||
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
_paroffset = 0x1000; //Offset for ffat, assuming size is already corrected
|
||||
if(!_partition){
|
||||
_error = UPDATE_ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_error = UPDATE_ERROR_BAD_ARGUMENT;
|
||||
log_e("bad command %u", command);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(size == UPDATE_SIZE_UNKNOWN){
|
||||
size = _partition->size;
|
||||
} else if(size > _partition->size){
|
||||
_error = UPDATE_ERROR_SIZE;
|
||||
log_e("too large %u > %u", size, _partition->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
//initialize
|
||||
_buffer = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE);
|
||||
if(!_buffer){
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
_size = size;
|
||||
_command = command;
|
||||
_md5.begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateClass::_abort(uint8_t err){
|
||||
_reset();
|
||||
_error = err;
|
||||
}
|
||||
|
||||
void UpdateClass::abort(){
|
||||
_abort(UPDATE_ERROR_ABORT);
|
||||
}
|
||||
|
||||
bool UpdateClass::_writeBuffer(){
|
||||
//first bytes of new firmware
|
||||
uint8_t skip = 0;
|
||||
if(!_progress && _command == U_FLASH){
|
||||
//check magic
|
||||
if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){
|
||||
_abort(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Stash the first 16 bytes of data and set the offset so they are
|
||||
//not written at this point so that partially written firmware
|
||||
//will not be bootable
|
||||
skip = ENCRYPTED_BLOCK_SIZE;
|
||||
_skipBuffer = (uint8_t*)malloc(skip);
|
||||
if(!_skipBuffer){
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
memcpy(_skipBuffer, _buffer, skip);
|
||||
}
|
||||
if (!_progress && _progress_callback) {
|
||||
_progress_callback(0, _size);
|
||||
}
|
||||
size_t offset = _partition->address + _progress;
|
||||
bool block_erase = (_size - _progress >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here
|
||||
bool part_head_sectors = _partition->address % SPI_FLASH_BLOCK_SIZE && offset < (_partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block
|
||||
bool part_tail_sectors = offset >= (_partition->address + _size) / SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition tailing block
|
||||
if (block_erase || part_head_sectors || part_tail_sectors){
|
||||
if(!ESP.partitionEraseRange(_partition, _progress, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_SEC_SIZE)){
|
||||
_abort(UPDATE_ERROR_ERASE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// try to skip empty blocks on unecrypted partitions
|
||||
if ((_partition->encrypted || _chkDataInBlock(_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) && !ESP.partitionWrite(_partition, _progress + skip, (uint32_t*)_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) {
|
||||
_abort(UPDATE_ERROR_WRITE);
|
||||
return false;
|
||||
}
|
||||
|
||||
//restore magic or md5 will fail
|
||||
if(!_progress && _command == U_FLASH){
|
||||
_buffer[0] = ESP_IMAGE_HEADER_MAGIC;
|
||||
}
|
||||
_md5.add(_buffer, _bufferLen);
|
||||
_progress += _bufferLen;
|
||||
_bufferLen = 0;
|
||||
if (_progress_callback) {
|
||||
_progress_callback(_progress, _size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateClass::_verifyHeader(uint8_t data) {
|
||||
if(_command == U_FLASH) {
|
||||
if(data != ESP_IMAGE_HEADER_MAGIC) {
|
||||
_abort(UPDATE_ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdateClass::_verifyEnd() {
|
||||
if(_command == U_FLASH) {
|
||||
if(!_enablePartition(_partition) || !_partitionIsBootable(_partition)) {
|
||||
_abort(UPDATE_ERROR_READ);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(esp_ota_set_boot_partition(_partition)){
|
||||
_abort(UPDATE_ERROR_ACTIVATE);
|
||||
return false;
|
||||
}
|
||||
_reset();
|
||||
return true;
|
||||
} else if(_command == U_SPIFFS) {
|
||||
_reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdateClass::setMD5(const char * expected_md5){
|
||||
if(strlen(expected_md5) != 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_target_md5 = expected_md5;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateClass::end(bool evenIfRemaining){
|
||||
if(hasError() || _size == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isFinished() && !evenIfRemaining){
|
||||
log_e("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
|
||||
_abort(UPDATE_ERROR_ABORT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(evenIfRemaining) {
|
||||
if(_bufferLen > 0) {
|
||||
_writeBuffer();
|
||||
}
|
||||
_size = progress();
|
||||
}
|
||||
|
||||
_md5.calculate();
|
||||
if(_target_md5.length()) {
|
||||
if(_target_md5 != _md5.toString()){
|
||||
_abort(UPDATE_ERROR_MD5);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _verifyEnd();
|
||||
}
|
||||
|
||||
size_t UpdateClass::write(uint8_t *data, size_t len) {
|
||||
if(hasError() || !isRunning()){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(len > remaining()){
|
||||
_abort(UPDATE_ERROR_SPACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t left = len;
|
||||
|
||||
while((_bufferLen + left) > SPI_FLASH_SEC_SIZE) {
|
||||
size_t toBuff = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
|
||||
_bufferLen += toBuff;
|
||||
if(!_writeBuffer()){
|
||||
return len - left;
|
||||
}
|
||||
left -= toBuff;
|
||||
}
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
||||
_bufferLen += left;
|
||||
if(_bufferLen == remaining()){
|
||||
if(!_writeBuffer()){
|
||||
return len - left;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t UpdateClass::writeStream(Stream &data) {
|
||||
size_t written = 0;
|
||||
size_t toRead = 0;
|
||||
int timeout_failures = 0;
|
||||
|
||||
if(hasError() || !isRunning())
|
||||
return 0;
|
||||
|
||||
if(!_verifyHeader(data.peek())) {
|
||||
_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
while(remaining()) {
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||
}
|
||||
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||
if(bytesToRead > remaining()) {
|
||||
bytesToRead = remaining();
|
||||
}
|
||||
|
||||
/*
|
||||
Init read&timeout counters and try to read, if read failed, increase counter,
|
||||
wait 100ms and try to read again. If counter > 300 (30 sec), give up/abort
|
||||
*/
|
||||
toRead = 0;
|
||||
timeout_failures = 0;
|
||||
while(!toRead) {
|
||||
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||
if(toRead == 0) {
|
||||
timeout_failures++;
|
||||
if (timeout_failures >= 300) {
|
||||
_abort(UPDATE_ERROR_STREAM);
|
||||
return written;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
if(_ledPin != -1) {
|
||||
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||
}
|
||||
_bufferLen += toRead;
|
||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||
return written;
|
||||
written += toRead;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
void UpdateClass::printError(Print &out){
|
||||
out.println(_err2str(_error));
|
||||
}
|
||||
|
||||
const char * UpdateClass::errorString(){
|
||||
return _err2str(_error);
|
||||
}
|
||||
|
||||
bool UpdateClass::_chkDataInBlock(const uint8_t *data, size_t len) const {
|
||||
// check 32-bit aligned blocks only
|
||||
if (!len || len % sizeof(uint32_t))
|
||||
return true;
|
||||
|
||||
size_t dwl = len / sizeof(uint32_t);
|
||||
|
||||
do {
|
||||
if (*(uint32_t*)data ^ 0xffffffff) // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte
|
||||
return true;
|
||||
|
||||
data += sizeof(uint32_t);
|
||||
} while (--dwl);
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateClass Update;
|
Reference in New Issue
Block a user