From 0d969e97600c76b83bb0f8f41fe981ae629c1d57 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 22:03:01 +0300 Subject: [PATCH] Fixes and HTTP Post update example Because eboot first erases the space required for the new sketch, and if the new sketch is larger then the old one, with the old way, part of the beginning of new sketch will be deleted. Therefore for now I opted to keep the max update size either half the available space for sketches or what's left from the first one, whichever is smaller. To be able to create a simple post mechanism for updates, I needed to have a way to write the new binary, without knowing it's final size, so I added an option to the end() method. Example in the WebServer examples. --- cores/esp8266/Esp.cpp | 2 +- cores/esp8266/Updater.cpp | 35 +++++++--- cores/esp8266/Updater.h | 4 +- .../examples/WebUpdate/WebUpdate.ino | 70 +++++++++++++++++++ 4 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 4e0b75106..3bec3830c 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -350,7 +350,7 @@ uint32_t EspClass::getFreeSketchSpace() { uint32_t usedSize = getSketchSize(); // round one sector up uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000; + uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000 - (5 * FLASH_SECTOR_SIZE); #ifdef DEBUG_SERIAL DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd); diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index c957428ff..194a21e9b 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -35,11 +35,14 @@ bool UpdaterClass::begin(size_t size){ _error = 0; uint32_t usedSize = ESP.getSketchSize(); - uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000; + uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000 - (5 * FLASH_SECTOR_SIZE); uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + uint32_t freeSpaceStart = freeSpaceEnd - roundedSize; - if(roundedSize > (freeSpaceEnd - freeSpaceStart)){ + //new sketch can not be more then half the size or more than the free space + //this means that max sketch size is (1MB - 20KB) / 2 for flash 2MB and above + //and the current sketch should not be more than that either + if(freeSpaceStart < usedSize || roundedSize > (freeSpaceEnd/2)){ _error = UPDATE_ERROR_SPACE; #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); @@ -64,7 +67,7 @@ bool UpdaterClass::begin(size_t size){ return true; } -bool UpdaterClass::end(){ +bool UpdaterClass::end(bool evenIfRemaining){ if(_size == 0){ #ifdef DEBUG_UPDATER DEBUG_UPDATER.println("no update"); @@ -72,27 +75,39 @@ bool UpdaterClass::end(){ return false; } - if(_buffer) os_free(_buffer); - _bufferLen = 0; - - if(hasError() || !isFinished()){ + if(hasError() || (!isFinished() && !evenIfRemaining)){ #ifdef DEBUG_UPDATER DEBUG_UPDATER.printf("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size); #endif + if(_buffer) os_free(_buffer); + _bufferLen = 0; _currentAddress = 0; _startAddress = 0; _size = 0; return false; } + if(evenIfRemaining){ + if(_bufferLen > 0){ + _writeBuffer(); + } + _size = progress(); + } + if(_buffer) os_free(_buffer); + _bufferLen = 0; + _currentAddress = 0; + eboot_command ebcmd; ebcmd.action = ACTION_COPY_RAW; ebcmd.args[0] = _startAddress; ebcmd.args[1] = 0x00000; ebcmd.args[2] = _size; eboot_command_write(&ebcmd); - - _currentAddress = 0; + +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf("Staged: address:0x%08X, size:0x%08X\n", _startAddress, _size); +#endif + _startAddress = 0; _size = 0; _error = UPDATE_ERROR_OK; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index ec5b0c4d9..df3e8fa38 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -40,12 +40,12 @@ class UpdaterClass { 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 + If there is already an update running but is not finished and !evenIfRemainanig or there is an error this will clear everything and return false the last error is available through getError() */ - bool end(); + bool end(bool evenIfRemaining = false); /* Prints the last error to an output stream diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino new file mode 100644 index 000000000..d72ab1345 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +const char* host = "esp8266-webupdate"; +const char* ssid = "........"; +const char* password = "........"; + +ESP8266WebServer server(80); +const char* serverIndex = "
"; + +void setup(void){ + Serial.begin(115200); + Serial.println(); + Serial.println("Booting Sketch..."); + WiFi.mode(WIFI_AP_STA); + WiFi.begin(ssid, password); + if(WiFi.waitForConnectResult() == WL_CONNECTED){ + MDNS.begin(host); + server.on("/", HTTP_GET, [](){ + server.sendHeader("Connection", "close"); + server.sendHeader("Access-Control-Allow-Origin", "*"); + server.send(200, "text/html", serverIndex); + }); + server.onFileUpload([](){ + if(server.uri() != "/update") return; + HTTPUpload& upload = server.upload(); + if(upload.status == UPLOAD_FILE_START){ + Serial.setDebugOutput(true); + Serial.printf("Update: %s\n", upload.filename.c_str()); + uint32_t maxSketchSpace = ((ESP.getFreeSketchSpace() + ESP.getSketchSize()) / 2) & 0xFFFFF000; + if(!Update.begin(maxSketchSpace)){//start with max available size + Update.printError(Serial); + return; + } + } else if(upload.status == UPLOAD_FILE_WRITE){ + if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ + Update.printError(Serial); + return; + } + } else if(upload.status == UPLOAD_FILE_END){ + if(Update.end(true)){ //true to set the size to the current progress + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } else { + Update.printError(Serial); + } + Serial.setDebugOutput(false); + } + yield(); + }); + server.on("/update", HTTP_POST, [](){ + server.sendHeader("Connection", "close"); + server.sendHeader("Access-Control-Allow-Origin", "*"); + server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK"); + ESP.restart(); + }); + server.begin(); + MDNS.addService("http", "tcp", 80); + + Serial.println("Ready! Open http:\/\/%s.local in your browser\n", host); + } else { + Serial.println("WiFi Failed"); + } +} + +void loop(void){ + server.handleClient(); + delay(1); +}