From dc9072b94b0e629ceca498647efffe0d47fe2c72 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 30 Jun 2015 23:48:09 +0300 Subject: [PATCH 01/22] Initial Upload From IDE For Test ONLY --- boards.txt | 22 ++++-- libraries/ESP8266mDNS/ESP8266mDNS.cpp | 8 ++- .../DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino | 50 ++++++++++++++ platform.txt | 9 +++ tools/espota.py | 67 +++++++++++++++++++ 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino create mode 100755 tools/espota.py diff --git a/boards.txt b/boards.txt index d90802e6d..f92c773b4 100644 --- a/boards.txt +++ b/boards.txt @@ -1,7 +1,9 @@ menu.UploadSpeed=Upload Speed menu.CpuFrequency=CPU Frequency menu.FlashSize=Flash Size +menu.FlashMode=Flash Mode menu.FlashFreq=Flash Frequency +menu.UploadTool=Upload Using ############################################################## generic.name=Generic ESP8266 Module @@ -23,11 +25,26 @@ generic.build.variant=generic generic.build.flash_mode=qio generic.build.spiffs_pagesize=256 +generic.menu.UploadTool.esptool=Serial +generic.menu.UploadTool.esptool.upload.tool=esptool +generic.menu.UploadTool.espota=OTA +generic.menu.UploadTool.espota.upload.tool=espota + generic.menu.CpuFrequency.80=80 MHz generic.menu.CpuFrequency.80.build.f_cpu=80000000L generic.menu.CpuFrequency.160=160 MHz generic.menu.CpuFrequency.160.build.f_cpu=160000000L +generic.menu.FlashFreq.40=40MHz +generic.menu.FlashFreq.40.build.flash_freq=40 +generic.menu.FlashFreq.80=80MHz +generic.menu.FlashFreq.80.build.flash_freq=80 + +generic.menu.FlashMode.dio=DIO +generic.menu.FlashMode.dio.build.flash_mode=dio +generic.menu.FlashMode.qio=QIO +generic.menu.FlashMode.qio.build.flash_mode=qio + generic.menu.UploadSpeed.115200=115200 generic.menu.UploadSpeed.115200.upload.speed=115200 generic.menu.UploadSpeed.9600=9600 @@ -117,11 +134,6 @@ generic.menu.FlashSize.4M.upload.maximum_size=1044464 # generic.menu.FlashSize.16M.build.spiffs_end=0x1000000 # generic.menu.FlashSize.16M.build.spiffs_blocksize=8192 -generic.menu.FlashFreq.40=40MHz -generic.menu.FlashFreq.40.build.flash_freq=40 -generic.menu.FlashFreq.80=80MHz -generic.menu.FlashFreq.80.build.flash_freq=80 - ############################################################## modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp index 3868a5c8b..6e50313a4 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -66,11 +66,16 @@ extern "C" { #define MDNS_TYPE_PTR 0x000C #define MDNS_TYPE_SRV 0x0021 #define MDNS_TYPE_TXT 0x0010 -#define MDNS_TYPE_NSEC 0x002F #define MDNS_CLASS_IN 0x0001 #define MDNS_CLASS_IN_FLUSH_CACHE 0x8001 +#define MDNS_ANSWERS_ALL 0x0F +#define MDNS_ANSWER_PTR 0x08 +#define MDNS_ANSWER_TXT 0x04 +#define MDNS_ANSWER_SRV 0x02 +#define MDNS_ANSWER_A 0x01 + #define _conn_read32() (((uint32_t)_conn->read() << 24) | ((uint32_t)_conn->read() << 16) | ((uint32_t)_conn->read() << 8) | _conn->read()) #define _conn_read16() (((uint16_t)_conn->read() << 8) | _conn->read()) #define _conn_read8() _conn->read() @@ -341,7 +346,6 @@ void MDNSResponder::_parsePacket(){ else if(currentType == MDNS_TYPE_PTR) os_printf(" PTR "); else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV "); else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT "); - else if(currentType == MDNS_TYPE_NSEC) os_printf(" NSEC "); else os_printf(" 0x%04X ", currentType); if(currentClass == MDNS_CLASS_IN) os_printf(" IN "); diff --git a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino new file mode 100644 index 000000000..12f8cad82 --- /dev/null +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -0,0 +1,50 @@ +#include +#include +#include + +const char* host = "esp8266-ota"; +const char* ssid = "**********"; +const char* pass = "**********"; +const uint16_t aport = 8266; + +WiFiUDP listener; + +void setup() { + Serial.begin(115200); + Serial.setDebugOutput(true); + Serial.println(""); + Serial.println("Arduino OTA Test"); + + Serial.printf("Sketch size: %u\n", ESP.getSketchSize()); + Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace()); + + WiFi.begin(ssid, pass); + if(WiFi.waitForConnectResult() == WL_CONNECTED){ + MDNS.begin(host); + MDNS.addService("arduino", "tcp", aport); + listener.begin(aport); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } +} + +void loop() { + if (ota_cmd_listener.parsePacket()) { + IPAddress remote = listener.remoteIP(); + int cmd = listener.parseInt(); + int port = listener.parseInt(); + int sz = listener.parseInt(); + Serial.printf("Starting Update: cmd:%d, port:%d, size:%d\r\n", cmd, port, sz); + WiFiClient cl; + if (!cl.connect(remote, port)) { + Serial.println("Failed to connect"); + return; + } + listener.stop(); + if (!ESP.updateSketch(cl, sz)) { + Serial.println("Update failed"); + listener.begin(aport); + } + } + delay(100); +} diff --git a/platform.txt b/platform.txt index bf3b930b5..8437e509c 100644 --- a/platform.txt +++ b/platform.txt @@ -93,3 +93,12 @@ tools.esptool.upload.protocol=esp tools.esptool.upload.params.verbose=-vv tools.esptool.upload.params.quiet= tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" + +tools.espota.cmd=python +tools.espota.cmd.windows=python.exe +tools.espota.path={runtime.platform.path}/tools + +tools.espota.upload.protocol=espota +tools.espota.upload.params.verbose= +tools.espota.upload.params.quiet= +tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" "{build.path}/{build.project_name}.bin" diff --git a/tools/espota.py b/tools/espota.py new file mode 100755 index 000000000..18aaec10e --- /dev/null +++ b/tools/espota.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# +# this script will push an OTA update to the ESP +# +# use it like: python ota_server.py +# +# on the ESP side you need code like this: https://gist.github.com/igrr/43d5c52328e955bb6b09 to handle the update +# + +import socket +import sys +import os + +def serve(remoteAddr, filename): + # Create a TCP/IP socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serverPort = 48266 + server_address = ('0.0.0.0', serverPort) + print >>sys.stderr, 'starting up on %s port %s' % server_address + sock.bind(server_address) + sock.listen(1) + + sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + remote_address = (remoteAddr, 8266) + content_size = os.path.getsize(filename) + print >>sys.stderr, 'upload size: %d' % content_size + message = '%d %d %d\n' % (0, serverPort, content_size) + print >>sys.stderr, 'sending invitation %s' % message + sent = sock2.sendto(message, remote_address) + sent = sock2.sendto(message, remote_address) + sent = sock2.sendto(message, remote_address) + + + while True: + # Wait for a connection + print >>sys.stderr, 'waiting for connection' + connection, client_address = sock.accept() + try: + print >>sys.stderr, 'connection from', client_address + + print >>sys.stderr, 'opening file %s' % filename + f = open(filename, "rb") + + while True: + chunk = f.read(4096) + if not chunk: + break + + print >>sys.stderr, 'sending %d' % len(chunk) + connection.sendall(chunk) + + print >>sys.stderr, 'done!' + return 0 + + finally: + connection.close() + f.close() + return 1 + +def main(args): + return serve(args[1], args[2]) + + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) + From 83eeaa31e0e91dc7e758c889cbb849ce4af4a048 Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 00:06:40 +0300 Subject: [PATCH 02/22] typo --- .../examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino index 12f8cad82..ca611218d 100644 --- a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -29,7 +29,7 @@ void setup() { } void loop() { - if (ota_cmd_listener.parsePacket()) { + if (listener.parsePacket()) { IPAddress remote = listener.remoteIP(); int cmd = listener.parseInt(); int port = listener.parseInt(); From 5efd9a1f81d6c96e575ccb50a0e0e52b65d9166c Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 01:46:34 +0300 Subject: [PATCH 03/22] Some print enhancements works on newer python as well --- tools/espota.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/espota.py b/tools/espota.py index 18aaec10e..2ad639584 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -7,6 +7,7 @@ # on the ESP side you need code like this: https://gist.github.com/igrr/43d5c52328e955bb6b09 to handle the update # +from __future__ import print_function import socket import sys import os @@ -16,16 +17,16 @@ def serve(remoteAddr, filename): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverPort = 48266 server_address = ('0.0.0.0', serverPort) - print >>sys.stderr, 'starting up on %s port %s' % server_address + print('starting up on %s port %s' % server_address, file=sys.stderr) sock.bind(server_address) sock.listen(1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) remote_address = (remoteAddr, 8266) content_size = os.path.getsize(filename) - print >>sys.stderr, 'upload size: %d' % content_size + print('upload size: %d' % content_size, file=sys.stderr) message = '%d %d %d\n' % (0, serverPort, content_size) - print >>sys.stderr, 'sending invitation %s' % message + print('sending invitation', file=sys.stderr) sent = sock2.sendto(message, remote_address) sent = sock2.sendto(message, remote_address) sent = sock2.sendto(message, remote_address) @@ -33,23 +34,25 @@ def serve(remoteAddr, filename): while True: # Wait for a connection - print >>sys.stderr, 'waiting for connection' + print('waiting...', file=sys.stderr) connection, client_address = sock.accept() try: - print >>sys.stderr, 'connection from', client_address + print('connection from', client_address, file=sys.stderr) - print >>sys.stderr, 'opening file %s' % filename + print('sending file %s\n' % filename, file=sys.stderr) f = open(filename, "rb") while True: chunk = f.read(4096) if not chunk: break - - print >>sys.stderr, 'sending %d' % len(chunk) + + sys.stderr.write('.') + sys.stderr.flush() + #print('sending %d' % len(chunk), file=sys.stderr) connection.sendall(chunk) - print >>sys.stderr, 'done!' + print('\ndone!', file=sys.stderr) return 0 finally: From 03a2b4808b240ac4b7825857dc74f3d31b0c3fdb Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 14:38:13 +0300 Subject: [PATCH 04/22] add 10 seconds timeout for waiting on ESP to start the update --- tools/espota.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/espota.py b/tools/espota.py index 2ad639584..f1f2baca6 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -26,16 +26,16 @@ def serve(remoteAddr, filename): content_size = os.path.getsize(filename) print('upload size: %d' % content_size, file=sys.stderr) message = '%d %d %d\n' % (0, serverPort, content_size) - print('sending invitation', file=sys.stderr) - sent = sock2.sendto(message, remote_address) - sent = sock2.sendto(message, remote_address) - sent = sock2.sendto(message, remote_address) - while True: # Wait for a connection + print('sending invitation', file=sys.stderr) + sent = sock2.sendto(message, remote_address) + sock.settimeout(10) print('waiting...', file=sys.stderr) connection, client_address = sock.accept() + sock.settimeout(None) + connection.settimeout(None) try: print('connection from', client_address, file=sys.stderr) From 2e08c5d7978915de40bd913fe3bf3794dfa5dec4 Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 15:38:30 +0300 Subject: [PATCH 05/22] Add an option to reboot on failed update --- cores/esp8266/Esp.cpp | 13 ++++++++++--- cores/esp8266/Esp.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index cda089b04..bf374814e 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -358,10 +358,12 @@ uint32_t EspClass::getFreeSketchSpace() { return freeSpaceEnd - freeSpaceStart; } -bool EspClass::updateSketch(Stream& in, uint32_t size) { +bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail) { - if (size > getFreeSketchSpace()) + if (size > getFreeSketchSpace()){ + if(restartOnFail) ESP.restart(); return false; + } uint32_t usedSize = getSketchSize(); uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); @@ -374,8 +376,10 @@ bool EspClass::updateSketch(Stream& in, uint32_t size) { noInterrupts(); int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize); interrupts(); - if (rc) + if (rc){ + if(restartOnFail) ESP.restart(); return false; + } #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("erase done"); @@ -397,12 +401,14 @@ bool EspClass::updateSketch(Stream& in, uint32_t size) { #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("stream read failed"); #endif + if(restartOnFail) ESP.restart(); return false; } if(addr == freeSpaceStart) { // check for valid first magic byte if(*((uint8 *) buffer.get()) != 0xE9) { + if(restartOnFail) ESP.restart(); return false; } } @@ -414,6 +420,7 @@ bool EspClass::updateSketch(Stream& in, uint32_t size) { #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("write failed"); #endif + if(restartOnFail) ESP.restart(); return false; } diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 80555d591..8021ed198 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -116,7 +116,7 @@ class EspClass { uint32_t getSketchSize(); uint32_t getFreeSketchSpace(); - bool updateSketch(Stream& in, uint32_t size); + bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false); String getResetInfo(); struct rst_info * getResetInfoPtr(); From d828312299457312222effbd8c93f9cb5058d8e8 Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 17:06:59 +0300 Subject: [PATCH 06/22] fix fail on slow streams --- cores/esp8266/Esp.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index bf374814e..a33cfa2a0 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -399,12 +399,18 @@ bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail) { size_t rd = in.readBytes(buffer.get(), willRead); if (rd != willRead) { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("stream read failed"); + DEBUG_SERIAL.printf("stream read less: %u/%u\n", rd, willRead); #endif - if(restartOnFail) ESP.restart(); - return false; + if(rd == 0){ //we got nothing from the client + //we should actually give it a bit of a chance to send us something + //connection could be slow ;) + if(restartOnFail) ESP.restart(); + return false; + } + //we at least got some data, lets write it to the flash + willRead = rd; } - + if(addr == freeSpaceStart) { // check for valid first magic byte if(*((uint8 *) buffer.get()) != 0xE9) { From 37bd383c59abba9394b9c9c04d6502182dc3996e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 6 Jul 2015 12:29:56 +0300 Subject: [PATCH 07/22] Part of 7cd54a4 without IDE changes --- platform.txt | 3 ++- tools/espota.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/platform.txt b/platform.txt index 8437e509c..050140f03 100644 --- a/platform.txt +++ b/platform.txt @@ -93,6 +93,7 @@ tools.esptool.upload.protocol=esp tools.esptool.upload.params.verbose=-vv tools.esptool.upload.params.quiet= tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" +tools.esptool.network.pattern="{cmd}" "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin" tools.espota.cmd=python tools.espota.cmd.windows=python.exe @@ -101,4 +102,4 @@ tools.espota.path={runtime.platform.path}/tools tools.espota.upload.protocol=espota tools.espota.upload.params.verbose= tools.espota.upload.params.quiet= -tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" "{build.path}/{build.project_name}.bin" +tools.espota.upload.pattern="{cmd}" "{path}/espota.py" "{serial.port}" 8266 "{build.path}/{build.project_name}.bin" diff --git a/tools/espota.py b/tools/espota.py index f1f2baca6..af421b319 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -12,7 +12,7 @@ import socket import sys import os -def serve(remoteAddr, filename): +def serve(remoteAddr, remotePort, filename): # Create a TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverPort = 48266 @@ -22,7 +22,7 @@ def serve(remoteAddr, filename): sock.listen(1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - remote_address = (remoteAddr, 8266) + remote_address = (remoteAddr, remotePort) content_size = os.path.getsize(filename) print('upload size: %d' % content_size, file=sys.stderr) message = '%d %d %d\n' % (0, serverPort, content_size) @@ -61,7 +61,7 @@ def serve(remoteAddr, filename): return 1 def main(args): - return serve(args[1], args[2]) + return serve(args[1], args[2], args[3]) From 44ac799481256dcaf1fcd72503ecf073effe811b Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 1 Jul 2015 23:56:53 +0300 Subject: [PATCH 08/22] bad command --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index 050140f03..169dd96ad 100644 --- a/platform.txt +++ b/platform.txt @@ -93,7 +93,7 @@ tools.esptool.upload.protocol=esp tools.esptool.upload.params.verbose=-vv tools.esptool.upload.params.quiet= tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" -tools.esptool.network.pattern="{cmd}" "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin" +tools.esptool.network.pattern=python "{path}/espota.py" "{serial.port}" "{network.port}" "{build.path}/{build.project_name}.bin" tools.espota.cmd=python tools.espota.cmd.windows=python.exe From 86cf9b2c4fe102d396e586d055798f06c3be43d7 Mon Sep 17 00:00:00 2001 From: John Doe Date: Thu, 2 Jul 2015 00:03:31 +0300 Subject: [PATCH 09/22] need to be int --- tools/espota.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/espota.py b/tools/espota.py index af421b319..b98d610aa 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -22,7 +22,7 @@ def serve(remoteAddr, remotePort, filename): sock.listen(1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - remote_address = (remoteAddr, remotePort) + remote_address = (remoteAddr, int(remotePort)) content_size = os.path.getsize(filename) print('upload size: %d' % content_size, file=sys.stderr) message = '%d %d %d\n' % (0, serverPort, content_size) From 6f2069deac3efaa624ad99b5e4479ca09c32ef04 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 13:59:11 +0300 Subject: [PATCH 10/22] New Update library, example, upload and more Proper error handling in the uploading python script Much faster OTA example sketch with better results New Update class that simplifies updating the firmware from any source Updated Esp.updateSketch() to use the new class --- cores/esp8266/Arduino.h | 1 + cores/esp8266/Esp.cpp | 106 +++------- cores/esp8266/Esp.h | 2 +- cores/esp8266/Updater.cpp | 190 ++++++++++++++++++ cores/esp8266/Updater.h | 119 +++++++++++ .../DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino | 78 +++++-- tools/espota.py | 80 +++++--- 7 files changed, 446 insertions(+), 130 deletions(-) create mode 100644 cores/esp8266/Updater.cpp create mode 100644 cores/esp8266/Updater.h diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index a715f5215..61e5711a4 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -233,6 +233,7 @@ void loop(void); #include "HardwareSerial.h" #include "Esp.h" +#include "Updater.h" #include "debug.h" #define min(a,b) ((a)<(b)?(a):(b)) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index a33cfa2a0..4e0b75106 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -30,7 +30,7 @@ extern struct rst_info resetInfo; } -// #define DEBUG_SERIAL Serial +//#define DEBUG_SERIAL Serial /** @@ -358,96 +358,38 @@ uint32_t EspClass::getFreeSketchSpace() { return freeSpaceEnd - freeSpaceStart; } -bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail) { - - if (size > getFreeSketchSpace()){ - if(restartOnFail) ESP.restart(); - return false; - } - - uint32_t usedSize = getSketchSize(); - uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - +bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) { + if(!Update.begin(size)){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.printf("erase @0x%x size=0x%x\r\n", freeSpaceStart, roundedSize); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif + if(restartOnFail) ESP.restart(); + return false; + } - noInterrupts(); - int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize); - interrupts(); - if (rc){ - if(restartOnFail) ESP.restart(); - return false; - } - + if(Update.writeStream(in) != size){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("erase done"); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif + if(restartOnFail) ESP.restart(); + return false; + } - uint32_t addr = freeSpaceStart; - uint32_t left = size; - - const uint32_t bufferSize = FLASH_SECTOR_SIZE; - std::unique_ptr buffer(new uint8_t[bufferSize]); - + if(!Update.end()){ #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("writing"); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - while (left > 0) { - size_t willRead = (left < bufferSize) ? left : bufferSize; - size_t rd = in.readBytes(buffer.get(), willRead); - if (rd != willRead) { -#ifdef DEBUG_SERIAL - DEBUG_SERIAL.printf("stream read less: %u/%u\n", rd, willRead); -#endif - if(rd == 0){ //we got nothing from the client - //we should actually give it a bit of a chance to send us something - //connection could be slow ;) - if(restartOnFail) ESP.restart(); - return false; - } - //we at least got some data, lets write it to the flash - willRead = rd; - } - - if(addr == freeSpaceStart) { - // check for valid first magic byte - if(*((uint8 *) buffer.get()) != 0xE9) { - if(restartOnFail) ESP.restart(); - return false; - } - } - - noInterrupts(); - rc = SPIWrite(addr, buffer.get(), willRead); - interrupts(); - if (rc) { -#ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("write failed"); -#endif - if(restartOnFail) ESP.restart(); - return false; - } - - addr += willRead; - left -= willRead; -#ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("."); -#endif - } + if(restartOnFail) ESP.restart(); + return false; + } #ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("\r\nrestarting"); -#endif - eboot_command ebcmd; - ebcmd.action = ACTION_COPY_RAW; - ebcmd.args[0] = freeSpaceStart; - ebcmd.args[1] = 0x00000; - ebcmd.args[2] = size; - eboot_command_write(&ebcmd); - - ESP.restart(); - return true; // never happens + DEBUG_SERIAL.println("Update SUCCESS"); +#endif + if(restartOnSuccess) ESP.restart(); + return true; } diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 8021ed198..8e66f8f88 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -116,7 +116,7 @@ class EspClass { uint32_t getSketchSize(); uint32_t getFreeSketchSpace(); - bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false); + bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true); String getResetInfo(); struct rst_info * getResetInfoPtr(); diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp new file mode 100644 index 000000000..0009f2f4a --- /dev/null +++ b/cores/esp8266/Updater.cpp @@ -0,0 +1,190 @@ +#include "Updater.h" +#include "Arduino.h" +#include "eboot_command.h" +extern "C"{ + #include "mem.h" +} +#define DEBUG_UPDATER Serial + +extern "C" uint32_t _SPIFFS_start; + +UpdaterClass::UpdaterClass() : _error(0), _buffer(0), _bufferLen(0), _size(0), _startAddress(0), _currentAddress(0) {} + +bool UpdaterClass::begin(size_t size){ + if(_size > 0){ +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.println("already running"); +#endif + return false; + } + + if(size == 0){ + _error = UPDATE_ERROR_SIZE; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return false; + } + + if(_buffer) os_free(_buffer); + + _bufferLen = 0; + _startAddress = 0; + _currentAddress = 0; + _size = 0; + _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 roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + + if(roundedSize > (freeSpaceEnd - freeSpaceStart)){ + _error = UPDATE_ERROR_SPACE; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return false; + } + noInterrupts(); + int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize); + interrupts(); + if (rc){ + _error = UPDATE_ERROR_ERASE; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return false; + } + _startAddress = freeSpaceStart; + _currentAddress = _startAddress; + _size = size; + _buffer = (uint8_t*)os_malloc(FLASH_SECTOR_SIZE); + + return true; +} + +bool UpdaterClass::end(){ + if(_size == 0){ +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.println("no update"); +#endif + return false; + } + + if(_buffer) os_free(_buffer); + _bufferLen = 0; + + if(hasError() || !isFinished()){ +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size); +#endif + _currentAddress = 0; + _startAddress = 0; + _size = 0; + return false; + } + + 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; + _startAddress = 0; + _size = 0; + _error = UPDATE_ERROR_OK; + return true; +} + +bool UpdaterClass::_writeBuffer(){ + WDT_FEED(); + noInterrupts(); + int rc = SPIWrite(_currentAddress, _buffer, _bufferLen); + interrupts(); + if (rc) { + _error = UPDATE_ERROR_WRITE; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return false; + } + _currentAddress += _bufferLen; + _bufferLen = 0; + return true; +} + +size_t UpdaterClass::write(uint8_t *data, size_t len){ + size_t left = len; + if(hasError()) + return 0; + + while((_bufferLen + left) > FLASH_SECTOR_SIZE){ + size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen; + memcpy(_buffer + _bufferLen, data + (len - left), toBuff); + _bufferLen += toBuff; + if(!_writeBuffer()){ + return len - left; + } + left -= toBuff; + yield(); + } + //lets see whats left + memcpy(_buffer + _bufferLen, data + (len - left), left); + _bufferLen += left; + if(_bufferLen == remaining()){ + //we are at the end of the update, so should write what's left to flash + if(!_writeBuffer()){ + return len - left; + } + } + return len; +} + +size_t UpdaterClass::writeStream(Stream &data){ + size_t written = 0; + size_t toRead = 0; + if(hasError()) + return 0; + + while(remaining()){ + toRead = FLASH_SECTOR_SIZE - _bufferLen; + toRead = data.readBytes(_buffer + _bufferLen, toRead); + if(toRead == 0){ //Timeout + _error = UPDATE_ERROR_STREAM; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return written; + } + _bufferLen += toRead; + if((_bufferLen == remaining() || _bufferLen == FLASH_SECTOR_SIZE) && !_writeBuffer()) + return written; + written += toRead; + yield(); + } + return written; +} + +void UpdaterClass::printError(Stream &out){ + out.printf("ERROR[%u]: ", _error); + if(_error == UPDATE_ERROR_OK){ + out.println("No Error"); + } else if(_error == UPDATE_ERROR_WRITE){ + out.println("Flash Write Failed"); + } else if(_error == UPDATE_ERROR_ERASE){ + out.println("Flash Erase Failed"); + } else if(_error == UPDATE_ERROR_SPACE){ + out.println("Not Enough Space"); + } else if(_error == UPDATE_ERROR_SIZE){ + out.println("Bad Size Given"); + } else if(_error == UPDATE_ERROR_STREAM){ + out.println("Stream Read Timeout"); + } else { + out.println("UNKNOWN"); + } +} + +UpdaterClass Update; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h new file mode 100644 index 000000000..107706060 --- /dev/null +++ b/cores/esp8266/Updater.h @@ -0,0 +1,119 @@ +#ifndef ESP8266UPDATER_H +#define ESP8266UPDATER_H + +#include "Arduino.h" +#include "flash_utils.h" + +#define UPDATE_ERROR_OK 0 +#define UPDATE_ERROR_WRITE 1 +#define UPDATE_ERROR_ERASE 2 +#define UPDATE_ERROR_SPACE 3 +#define UPDATE_ERROR_SIZE 4 +#define UPDATE_ERROR_STREAM 5 + +class UpdaterClass { + public: + UpdaterClass(); + /* + Call this to check and erase the space needed for the update + Will return false if there is not enough space + Or the erase of the flash failed + */ + bool begin(size_t size); + + /* + 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 Sream 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 + or there is an error + this will clear everything and return false + the last error is available through getError() + */ + bool end(); + + /* + Prints the last error to an output stream + */ + void printError(Stream &out); + + //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 hasError()?true:(_currentAddress == (_startAddress + _size)); } + size_t size(){ return _size; } + size_t progress(){ return _currentAddress - _startAddress; } + size_t remaining(){ return hasError()?0:(size() - progress()); } + + /* + Template to write from objects that expose + available() and read(uint8_t*, size_t) methods + faster than the readStream method + writes only what is available + */ + template + size_t write(T &data){ + size_t written = 0; + if(hasError()) + return 0; + size_t available = data.available(); + while(available){ + if((_bufferLen + available) > remaining()){ + available = (remaining() - _bufferLen); + } + if((_bufferLen + available) > FLASH_SECTOR_SIZE){ + size_t toBuff = FLASH_SECTOR_SIZE - _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; + yield(); + available = data.available(); + } + return written; + } + + private: + uint8_t *_buffer; + uint8_t _error; + size_t _bufferLen; + size_t _size; + uint32_t _startAddress; + uint32_t _currentAddress; + + bool _writeBuffer(); +}; + +extern UpdaterClass Update; + +#endif \ No newline at end of file diff --git a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino index ca611218d..8ceb2be8a 100644 --- a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -7,11 +7,12 @@ const char* ssid = "**********"; const char* pass = "**********"; const uint16_t aport = 8266; -WiFiUDP listener; +WiFiServer TelnetServer(aport); +WiFiClient Telnet; +WiFiUDP OTA; void setup() { Serial.begin(115200); - Serial.setDebugOutput(true); Serial.println(""); Serial.println("Arduino OTA Test"); @@ -22,29 +23,74 @@ void setup() { if(WiFi.waitForConnectResult() == WL_CONNECTED){ MDNS.begin(host); MDNS.addService("arduino", "tcp", aport); - listener.begin(aport); + OTA.begin(aport); + TelnetServer.begin(); + TelnetServer.setNoDelay(true); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } } void loop() { - if (listener.parsePacket()) { - IPAddress remote = listener.remoteIP(); - int cmd = listener.parseInt(); - int port = listener.parseInt(); - int sz = listener.parseInt(); - Serial.printf("Starting Update: cmd:%d, port:%d, size:%d\r\n", cmd, port, sz); - WiFiClient cl; - if (!cl.connect(remote, port)) { - Serial.println("Failed to connect"); + //OTA Sketch + if (OTA.parsePacket()) { + IPAddress remote = OTA.remoteIP(); + int cmd = OTA.parseInt(); + int port = OTA.parseInt(); + int size = OTA.parseInt(); + + Serial.print("Update Start: ip:"); + Serial.print(remote); + Serial.printf(", port:%d, size:%d\n", port, size); + uint32_t startTime = millis(); + + if(!Update.begin(size)){ + Serial.println("Update Begin Error"); return; } - listener.stop(); - if (!ESP.updateSketch(cl, sz)) { - Serial.println("Update failed"); - listener.begin(aport); + + WiFiClient client; + if (client.connect(remote, port)) { + + Serial.setDebugOutput(true); + while(!Update.isFinished()) Update.write(client); + Serial.setDebugOutput(false); + + if(Update.end()){ + client.println("OK"); + Serial.printf("Update Success: %u\nRebooting...\n", millis() - startTime); + ESP.restart(); + } else { + Update.printError(client); + Update.printError(Serial); + } + } else { + Serial.printf("Connect Failed: %u\n", millis() - startTime); } } + //IDE Monitor (connected to Serial) + if (TelnetServer.hasClient()){ + if (!Telnet || !Telnet.connected()){ + if(Telnet) Telnet.stop(); + Telnet = TelnetServer.available(); + } else { + WiFiClient toKill = TelnetServer.available(); + toKill.stop(); + } + } + if (Telnet && Telnet.connected() && Telnet.available()){ + while(Telnet.available()) + Serial.write(Telnet.read()); + } + if(Serial.available()){ + size_t len = Serial.available(); + uint8_t * sbuf = (uint8_t *)malloc(len); + Serial.readBytes(sbuf, len); + if (Telnet && Telnet.connected()){ + Telnet.write((uint8_t *)sbuf, len); + yield(); + } + free(sbuf); + } delay(100); } diff --git a/tools/espota.py b/tools/espota.py index b98d610aa..58305c6df 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -17,48 +17,66 @@ def serve(remoteAddr, remotePort, filename): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverPort = 48266 server_address = ('0.0.0.0', serverPort) - print('starting up on %s port %s' % server_address, file=sys.stderr) - sock.bind(server_address) - sock.listen(1) + print('Starting on %s:%s' % server_address, file=sys.stderr) + try: + sock.bind(server_address) + sock.listen(1) + except: + print('Socket Failed', file=sys.stderr) + return 1 - sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - remote_address = (remoteAddr, int(remotePort)) content_size = os.path.getsize(filename) - print('upload size: %d' % content_size, file=sys.stderr) + print('Upload size: %d' % content_size, file=sys.stderr) message = '%d %d %d\n' % (0, serverPort, content_size) - while True: - # Wait for a connection - print('sending invitation', file=sys.stderr) - sent = sock2.sendto(message, remote_address) + # Wait for a connection + print('Sending invitation to:', remoteAddr, file=sys.stderr) + sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + remote_address = (remoteAddr, int(remotePort)) + sent = sock2.sendto(message, remote_address) + sock2.close() + + print('Waiting for device...\n', file=sys.stderr) + try: sock.settimeout(10) - print('waiting...', file=sys.stderr) connection, client_address = sock.accept() sock.settimeout(None) connection.settimeout(None) + except: + print('No response from device', file=sys.stderr) + sock.close() + return 1 + + try: + f = open(filename, "rb") + sys.stderr.write('Uploading') + sys.stderr.flush() + while True: + chunk = f.read(4096) + if not chunk: break + sys.stderr.write('.') + sys.stderr.flush() + connection.sendall(chunk) + + print('\nWaiting for result...\n', file=sys.stderr) try: - print('connection from', client_address, file=sys.stderr) - - print('sending file %s\n' % filename, file=sys.stderr) - f = open(filename, "rb") - - while True: - chunk = f.read(4096) - if not chunk: - break - - sys.stderr.write('.') - sys.stderr.flush() - #print('sending %d' % len(chunk), file=sys.stderr) - connection.sendall(chunk) - - print('\ndone!', file=sys.stderr) - return 0 - - finally: + connection.settimeout(60) + data = connection.recv(32) + print('Result: %s' % data, file=sys.stderr) connection.close() f.close() - return 1 + return 0 + except: + print('Result: No Answer!', file=sys.stderr) + connection.close() + f.close() + return 1 + + finally: + connection.close() + f.close() + sock.close() + return 1 def main(args): return serve(args[1], args[2], args[3]) From b797287359f4fd76b6125fea29012a3f15fc9e92 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 14:08:10 +0300 Subject: [PATCH 11/22] Catch lost connection while uploading --- tools/espota.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/espota.py b/tools/espota.py index 58305c6df..52ef3e95a 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -56,7 +56,14 @@ def serve(remoteAddr, remotePort, filename): if not chunk: break sys.stderr.write('.') sys.stderr.flush() - connection.sendall(chunk) + try: + connection.sendall(chunk) + except: + print('\nError Uploading', file=sys.stderr) + connection.close() + f.close() + sock.close() + return 1 print('\nWaiting for result...\n', file=sys.stderr) try: @@ -65,11 +72,13 @@ def serve(remoteAddr, remotePort, filename): print('Result: %s' % data, file=sys.stderr) connection.close() f.close() + sock.close() return 0 except: print('Result: No Answer!', file=sys.stderr) connection.close() f.close() + sock.close() return 1 finally: From bfbfd313159dcb4a8809cd7a64bfcbfc7112e02e Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 14:13:09 +0300 Subject: [PATCH 12/22] disable updater debug --- cores/esp8266/Updater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 0009f2f4a..740ec3202 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -4,7 +4,7 @@ extern "C"{ #include "mem.h" } -#define DEBUG_UPDATER Serial +//#define DEBUG_UPDATER Serial extern "C" uint32_t _SPIFFS_start; From d969115cda34b1e1cc52719586c9d085dfb6d6cc Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 14:19:23 +0300 Subject: [PATCH 13/22] protect the write method writing more than supposed to --- cores/esp8266/Updater.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 740ec3202..728700c2f 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -121,6 +121,9 @@ size_t UpdaterClass::write(uint8_t *data, size_t len){ if(hasError()) return 0; + if(len > remaining()) + len = remaining(); + while((_bufferLen + left) > FLASH_SECTOR_SIZE){ size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen; memcpy(_buffer + _bufferLen, data + (len - left), toBuff); From ace974aede0489b70d9292f84b4dd05f736b56f1 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 14:21:41 +0300 Subject: [PATCH 14/22] let's not wait too much :) telnet running here --- .../examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino index 8ceb2be8a..4320bbbba 100644 --- a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -92,5 +92,5 @@ void loop() { } free(sbuf); } - delay(100); + delay(1); } From 342729906574279a4d012946cb64508976ce3077 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 16:14:04 +0300 Subject: [PATCH 15/22] minor enchancement --- cores/esp8266/Updater.cpp | 2 ++ cores/esp8266/Updater.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 728700c2f..c957428ff 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -106,6 +106,7 @@ bool UpdaterClass::_writeBuffer(){ interrupts(); if (rc) { _error = UPDATE_ERROR_WRITE; + _currentAddress = (_startAddress + _size); #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif @@ -157,6 +158,7 @@ size_t UpdaterClass::writeStream(Stream &data){ toRead = data.readBytes(_buffer + _bufferLen, toRead); if(toRead == 0){ //Timeout _error = UPDATE_ERROR_STREAM; + _currentAddress = (_startAddress + _size); #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 107706060..ec5b0c4d9 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -57,10 +57,10 @@ class UpdaterClass { void clearError(){ _error = UPDATE_ERROR_OK; } bool hasError(){ return _error != UPDATE_ERROR_OK; } bool isRunning(){ return _size > 0; } - bool isFinished(){ return hasError()?true:(_currentAddress == (_startAddress + _size)); } + bool isFinished(){ return _currentAddress == (_startAddress + _size); } size_t size(){ return _size; } size_t progress(){ return _currentAddress - _startAddress; } - size_t remaining(){ return hasError()?0:(size() - progress()); } + size_t remaining(){ return _size - (_currentAddress - _startAddress); } /* Template to write from objects that expose @@ -116,4 +116,4 @@ class UpdaterClass { extern UpdaterClass Update; -#endif \ No newline at end of file +#endif From 0d969e97600c76b83bb0f8f41fe981ae629c1d57 Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 22:03:01 +0300 Subject: [PATCH 16/22] 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); +} From 7596ed07429a2aad01d27b046265478c6241117a Mon Sep 17 00:00:00 2001 From: John Doe Date: Fri, 3 Jul 2015 22:31:10 +0300 Subject: [PATCH 17/22] inlining and enchancements --- cores/esp8266/Updater.h | 17 +++++++++-------- .../examples/WebUpdate/WebUpdate.ino | 1 + .../DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino | 4 +++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index df3e8fa38..f8c02859f 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -44,6 +44,7 @@ class UpdaterClass { 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); @@ -53,14 +54,14 @@ class UpdaterClass { void printError(Stream &out); //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 _currentAddress == (_startAddress + _size); } - size_t size(){ return _size; } - size_t progress(){ return _currentAddress - _startAddress; } - size_t remaining(){ return _size - (_currentAddress - _startAddress); } + inline uint8_t getError(){ return _error; } + inline void clearError(){ _error = UPDATE_ERROR_OK; } + inline bool hasError(){ return _error != UPDATE_ERROR_OK; } + inline bool isRunning(){ return _size > 0; } + inline bool isFinished(){ return _currentAddress == (_startAddress + _size); } + inline size_t size(){ return _size; } + inline size_t progress(){ return _currentAddress - _startAddress; } + inline size_t remaining(){ return _size - (_currentAddress - _startAddress); } /* Template to write from objects that expose diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino index d72ab1345..b411d234e 100644 --- a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -28,6 +28,7 @@ void setup(void){ HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ Serial.setDebugOutput(true); + WiFiUDP::stopAll(); 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 diff --git a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino index 4320bbbba..e7dc97795 100644 --- a/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -43,7 +43,9 @@ void loop() { Serial.print(remote); Serial.printf(", port:%d, size:%d\n", port, size); uint32_t startTime = millis(); - + + WiFiUDP::stopAll(); + if(!Update.begin(size)){ Serial.println("Update Begin Error"); return; From f3f500936d831be890a62a959e21ceff4fd246ef Mon Sep 17 00:00:00 2001 From: John Doe Date: Sat, 4 Jul 2015 00:27:13 +0300 Subject: [PATCH 18/22] make eboot erase/read/write sector by sector that makes possible having sketches with size up to the free size --- bootloaders/eboot/Makefile | 3 +- bootloaders/eboot/eboot.c | 56 ++++++++++++++++-------------------- bootloaders/eboot/eboot.elf | Bin 11233 -> 10064 bytes bootloaders/eboot/flash.c | 4 +-- bootloaders/eboot/flash.h | 2 +- cores/esp8266/Updater.cpp | 25 +++++++++------- tools/espota.py | 11 +++---- 7 files changed, 49 insertions(+), 52 deletions(-) diff --git a/bootloaders/eboot/Makefile b/bootloaders/eboot/Makefile index 0872ee35f..a465a76f6 100644 --- a/bootloaders/eboot/Makefile +++ b/bootloaders/eboot/Makefile @@ -1,4 +1,5 @@ -XTENSA_TOOLCHAIN ?= +XTENSA_TOOLCHAIN ?= ../../tools/xtensa-lx106-elf/bin/ +ESPTOOL ?= ../../tools/esptool BIN_DIR := ./ TARGET_DIR := ./ diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 0b74fcdf0..ffa1238b3 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -76,38 +76,31 @@ int copy_raw(const uint32_t src_addr, const uint32_t dst_addr, const uint32_t size) { - ets_putc('\n'); - ets_putc('c'); - ets_putc('p'); - ets_putc('\n'); // require regions to be aligned if (src_addr & 0xfff != 0 || dst_addr & 0xfff != 0) { return 1; } - if (SPIEraseAreaEx(dst_addr, size)) { - return 2; - } - - const uint32_t buffer_size = 4096; + const uint32_t buffer_size = FLASH_SECTOR_SIZE; uint8_t buffer[buffer_size]; - - const uint32_t end = src_addr + size; + uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1)); uint32_t saddr = src_addr; uint32_t daddr = dst_addr; - uint32_t left = size; - while (saddr < end) { - uint32_t will_copy = (left < buffer_size) ? left : buffer_size; - if (SPIRead(saddr, buffer, will_copy)) { - return 3; - } - if (SPIWrite(daddr, buffer, will_copy)) { - return 4; - } - saddr += will_copy; - daddr += will_copy; - left -= will_copy; + + while (left) { + if (SPIEraseSector(daddr/buffer_size)) { + return 2; + } + if (SPIRead(saddr, buffer, buffer_size)) { + return 3; + } + if (SPIWrite(daddr, buffer, buffer_size)) { + return 4; + } + saddr += buffer_size; + daddr += buffer_size; + left -= buffer_size; } return 0; @@ -123,14 +116,16 @@ void main() if (eboot_command_read(&cmd)) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = 0; - ets_putc('e'); + ets_putc('~'); } else { ets_putc('@'); } eboot_command_clear(); - + if (cmd.action == ACTION_COPY_RAW) { + ets_putc('c'); ets_putc('p'); ets_putc(':'); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]); + ets_putc((char)0x30+res); ets_putc('\n'); if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; @@ -138,15 +133,14 @@ void main() } if (cmd.action == ACTION_LOAD_APP) { - res = load_app_from_flash_raw(cmd.args[0]); + ets_putc('l'); ets_putc('d'); ets_putc('\n'); + res = load_app_from_flash_raw(cmd.args[0]); + //we will get to this only on load fail + ets_putc('e'); ets_putc(':'); ets_putc((char)0x30+res); ets_putc('\n'); } if (res) { - ets_putc('\n'); - ets_putc('#'); - ets_putc('0' + res); - ets_putc('\n'); - SWRST; + SWRST; } while(true){} diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 97e25c14616a9f6e768711b46c6cafa6f62df21e..2a46a359c2621a4115be7aa19e2268a8659dbc78 100755 GIT binary patch delta 4192 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 J{k1>f{{z3*d8YsX literal 11233 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 #include #include @@ -46,4 +46,4 @@ int SPIEraseAreaEx(const uint32_t start, const uint32_t size) return 0; } - +*/ diff --git a/bootloaders/eboot/flash.h b/bootloaders/eboot/flash.h index ea8b65c1f..f762296cf 100644 --- a/bootloaders/eboot/flash.h +++ b/bootloaders/eboot/flash.h @@ -12,7 +12,7 @@ int SPIEraseBlock(uint32_t block); int SPIEraseSector(uint32_t sector); int SPIRead(uint32_t addr, void *dest, size_t size); int SPIWrite(uint32_t addr, void *src, size_t size); -int SPIEraseAreaEx(const uint32_t start, const uint32_t size); +//int SPIEraseAreaEx(const uint32_t start, const uint32_t size); #define FLASH_SECTOR_SIZE 0x1000 #define FLASH_BLOCK_SIZE 0x10000 diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 194a21e9b..aba181fe7 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -27,30 +27,33 @@ bool UpdaterClass::begin(size_t size){ } if(_buffer) os_free(_buffer); - _bufferLen = 0; _startAddress = 0; _currentAddress = 0; _size = 0; _error = 0; - uint32_t usedSize = ESP.getSketchSize(); - uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000 - (5 * FLASH_SECTOR_SIZE); + //size of current sketch rounded to a sector + uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + //address of the end of the space available for sketch and update (5 sectors are for EEPROM and init data) + uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000 - (5 * FLASH_SECTOR_SIZE); + //size of the update rounded to a sector uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t freeSpaceStart = freeSpaceEnd - roundedSize; + //address where we will start writing the update + uint32_t updateStartAddress = updateEndAddress - roundedSize; - //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)){ + //make sure that the size of both sketches is less than the total space (updateEndAddress) + if(updateStartAddress < currentSketchSize){ _error = UPDATE_ERROR_SPACE; #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif return false; } + + //erase the neede space noInterrupts(); - int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize); + int rc = SPIEraseAreaEx(updateStartAddress, roundedSize); interrupts(); if (rc){ _error = UPDATE_ERROR_ERASE; @@ -59,7 +62,9 @@ bool UpdaterClass::begin(size_t size){ #endif return false; } - _startAddress = freeSpaceStart; + + //initialize + _startAddress = updateStartAddress; _currentAddress = _startAddress; _size = size; _buffer = (uint8_t*)os_malloc(FLASH_SECTOR_SIZE); diff --git a/tools/espota.py b/tools/espota.py index 52ef3e95a..dbd22eb45 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -1,11 +1,7 @@ #!/usr/bin/env python # # this script will push an OTA update to the ESP -# -# use it like: python ota_server.py -# -# on the ESP side you need code like this: https://gist.github.com/igrr/43d5c52328e955bb6b09 to handle the update -# +# use it like: python espota.py from __future__ import print_function import socket @@ -22,7 +18,7 @@ def serve(remoteAddr, remotePort, filename): sock.bind(server_address) sock.listen(1) except: - print('Socket Failed', file=sys.stderr) + print('Listen Failed', file=sys.stderr) return 1 content_size = os.path.getsize(filename) @@ -80,10 +76,11 @@ def serve(remoteAddr, remotePort, filename): f.close() sock.close() return 1 - + finally: connection.close() f.close() + sock.close() return 1 From 9d0a69042191843bc4de880e16870f4f3d849a6b Mon Sep 17 00:00:00 2001 From: John Doe Date: Sat, 4 Jul 2015 00:46:58 +0300 Subject: [PATCH 19/22] fix WebUpload example --- libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino index b411d234e..4962b35e8 100644 --- a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -30,15 +30,13 @@ void setup(void){ Serial.setDebugOutput(true); WiFiUDP::stopAll(); Serial.printf("Update: %s\n", upload.filename.c_str()); - uint32_t maxSketchSpace = ((ESP.getFreeSketchSpace() + ESP.getSketchSize()) / 2) & 0xFFFFF000; + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 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 @@ -59,7 +57,7 @@ void setup(void){ server.begin(); MDNS.addService("http", "tcp", 80); - Serial.println("Ready! Open http:\/\/%s.local in your browser\n", host); + Serial.printf("Ready! Open http://%s.local in your browser\n", host); } else { Serial.println("WiFi Failed"); } From 703ab8df641c0028245d400cf9d1b2d609571f13 Mon Sep 17 00:00:00 2001 From: John Doe Date: Sat, 4 Jul 2015 01:20:33 +0300 Subject: [PATCH 20/22] make Update erase/write sector by sector as well --- cores/esp8266/Updater.cpp | 26 +++++++++---------- .../examples/WebUpdate/WebUpdate.ino | 4 +++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index aba181fe7..e9ecd5d5d 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -51,18 +51,6 @@ bool UpdaterClass::begin(size_t size){ return false; } - //erase the neede space - noInterrupts(); - int rc = SPIEraseAreaEx(updateStartAddress, roundedSize); - interrupts(); - if (rc){ - _error = UPDATE_ERROR_ERASE; -#ifdef DEBUG_UPDATER - printError(DEBUG_UPDATER); -#endif - return false; - } - //initialize _startAddress = updateStartAddress; _currentAddress = _startAddress; @@ -122,7 +110,19 @@ bool UpdaterClass::end(bool evenIfRemaining){ bool UpdaterClass::_writeBuffer(){ WDT_FEED(); noInterrupts(); - int rc = SPIWrite(_currentAddress, _buffer, _bufferLen); + int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE); + interrupts(); + if (rc){ + _error = UPDATE_ERROR_ERASE; +#ifdef DEBUG_UPDATER + printError(DEBUG_UPDATER); +#endif + return false; + } + + WDT_FEED(); + noInterrupts(); + rc = SPIWrite(_currentAddress, _buffer, _bufferLen); interrupts(); if (rc) { _error = UPDATE_ERROR_WRITE; diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino index 4962b35e8..665dd6199 100644 --- a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -1,3 +1,7 @@ +/* + To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update +*/ + #include #include #include From 1741bc68b6e2f1df7cc9e97a6048d05fe51eb022 Mon Sep 17 00:00:00 2001 From: John Doe Date: Sat, 4 Jul 2015 01:46:51 +0300 Subject: [PATCH 21/22] speed :) and prevent write if we are not running --- cores/esp8266/Updater.cpp | 18 +++--------------- cores/esp8266/Updater.h | 5 ++--- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index e9ecd5d5d..1504b2e97 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -108,23 +108,11 @@ bool UpdaterClass::end(bool evenIfRemaining){ } bool UpdaterClass::_writeBuffer(){ - WDT_FEED(); noInterrupts(); int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE); + if(!rc) rc = SPIWrite(_currentAddress, _buffer, _bufferLen); interrupts(); if (rc){ - _error = UPDATE_ERROR_ERASE; -#ifdef DEBUG_UPDATER - printError(DEBUG_UPDATER); -#endif - return false; - } - - WDT_FEED(); - noInterrupts(); - rc = SPIWrite(_currentAddress, _buffer, _bufferLen); - interrupts(); - if (rc) { _error = UPDATE_ERROR_WRITE; _currentAddress = (_startAddress + _size); #ifdef DEBUG_UPDATER @@ -139,7 +127,7 @@ bool UpdaterClass::_writeBuffer(){ size_t UpdaterClass::write(uint8_t *data, size_t len){ size_t left = len; - if(hasError()) + if(hasError()||!isRunning()) return 0; if(len > remaining()) @@ -170,7 +158,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len){ size_t UpdaterClass::writeStream(Stream &data){ size_t written = 0; size_t toRead = 0; - if(hasError()) + if(hasError()||!isRunning()) return 0; while(remaining()){ diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index f8c02859f..e495ef0e7 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -15,9 +15,8 @@ class UpdaterClass { public: UpdaterClass(); /* - Call this to check and erase the space needed for the update + Call this to check the space needed for the update Will return false if there is not enough space - Or the erase of the flash failed */ bool begin(size_t size); @@ -72,7 +71,7 @@ class UpdaterClass { template size_t write(T &data){ size_t written = 0; - if(hasError()) + if(hasError()||!isRunning()) return 0; size_t available = data.available(); while(available){ From 5763dbba3b1ce1c6ff927054f03c664a73b42cce Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 6 Jul 2015 21:04:08 +0300 Subject: [PATCH 22/22] Code review --- bootloaders/eboot/Makefile | 2 +- bootloaders/eboot/eboot.c | 4 +-- bootloaders/eboot/eboot.elf | Bin 10064 -> 11229 bytes bootloaders/eboot/flash.c | 4 +-- bootloaders/eboot/flash.h | 2 +- cores/esp8266/Esp.cpp | 2 +- cores/esp8266/Updater.cpp | 70 +++++++++++++++++++----------------- cores/esp8266/Updater.h | 42 +++++++++++----------- 8 files changed, 67 insertions(+), 59 deletions(-) diff --git a/bootloaders/eboot/Makefile b/bootloaders/eboot/Makefile index a465a76f6..7a07d7615 100644 --- a/bootloaders/eboot/Makefile +++ b/bootloaders/eboot/Makefile @@ -7,7 +7,7 @@ TARGET_DIR := ./ TARGET_OBJ_FILES := \ eboot.o \ eboot_command.o \ - flash.o \ + TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES)) diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index ffa1238b3..cbc87044b 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -125,7 +125,7 @@ void main() if (cmd.action == ACTION_COPY_RAW) { ets_putc('c'); ets_putc('p'); ets_putc(':'); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]); - ets_putc((char)0x30+res); ets_putc('\n'); + ets_putc('0'+res); ets_putc('\n'); if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; @@ -136,7 +136,7 @@ void main() ets_putc('l'); ets_putc('d'); ets_putc('\n'); res = load_app_from_flash_raw(cmd.args[0]); //we will get to this only on load fail - ets_putc('e'); ets_putc(':'); ets_putc((char)0x30+res); ets_putc('\n'); + ets_putc('e'); ets_putc(':'); ets_putc('0'+res); ets_putc('\n'); } if (res) { diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 2a46a359c2621a4115be7aa19e2268a8659dbc78..08536aa72a4c83c9788ce38dd40d440db9669dc0 100755 GIT binary patch delta 4449 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 Cv52?; delta 3463 zcmb_ee{fV+6~6cV+68tuyPKbELbCgIlij3{O&}zzrY6vYE|3;%QyPH^AqmrkBulfY zMPyiYrnM?+*E=|l#+eMA>R8jU%+{F>6BTG>l(92GDTY#~Rv8tcEeWWo)%82?hsm`6 zvNQYccfRkObI*P6?AiCbhiC48rebMg-rL#27&}~*)4xzQt1w1i6%4n&Q>r*0EX&O# zMUYqWcSJj{U-!qtDaMYjcIjusELQXty}l@xuJ3S3`W9!bqxoVXx<+yNP2)l#nsyh|l1fN7f@xA_g^vMA}LWDkffbS>Jew?RhjddipBzJXREuiQk{Y(=d zAl7x9_Zw=_ul5|CuaC#J)OR3})TK`j^Bttmb6=X@dxm(TwE*uD&uLNKBwo|vNcbPEjYq^-CL{U^u8uU7rubZNbzb6~*v@$nQ~{qNw&Xs*&ne3{LZ$MU5-_u!1ff*EhXjhY zj`%8aacY;vz9rSG0$^NELgfBhTPM2C`ceI7a&kD3{JJU=6?-fiSd0{bXh=F)euxXQ_OGgZ+v z=k0WmHSsJ}LACSEW{$1f-KR!w!OY@v0 z^2RHGpT*1`BK(%PMlt3O?=86w744qbbf`liiyfVg09);ut zuKcic5KTPY;6Xs< zG<)a>pyD)p$Z&C97D-oiJN3{qj?;3P-02|S!pbK;iTtjCu>ng!54qolFPBM>a)a&~ zSsZtDdg<}|ULj>UC72fTuGj-EN?1d`9^>b4B-G&J8Wq1H@-F=*C2606OT?(_8{R6>=c~#W7d1%YRi790>~&vFx&27tV|@Nhx!E;wm%nn$ z-qgU*cskxSl-WO?J`nGIaBE_9YioSn*x=)-(e$d3)aX!r&*Q1#!T3o3Kss|U{zQ6g zzb!ZRA2<-dCp|hoG&-J%Z|eVazu4#hBJax2`{%jyZ^kyyak0`Lxa0dYl=j~7ocsJO z;$rDO9?7pO+rYQQAIc1kW#V`K4y_O^%ge;iD_Zj#mj9NEV5~$WD%-V8>d?@xagnL~ zsJLC3$}&tcdYeFx8uO4Iof0!~6c~4x?S~KPGy;Z!-B@*eX$oR7c*0e{#2;J46Snyi zz@?VYSzr&t{|W_MfKg!y*MR|zq=bttz5r|&!7wtz79R!%H2euuaa1&oRV>Vj!w_Bx zO#Dt@6)WxAKbKty?uW3;5>5e=GoAPhaJj`_0tPgA!gg`j7x9E`z5x9HqA?C>xY5RK zz*v|wi|Xiwt_cQhNRt+eu>s+ifr$@d%wR+q!C_!P!>0vz#S2WE&<_xc@%RW6Pq8hF z{eersQ4vYttXyQb8WJD+@T&|8c&q;kj-0oHC9SW^9hSlggvXsf996BEBH%s zGgvCkDq?9!SEsP|qoO$!)=C`cJymQ6`4<=3L3}8%Z>#dYa564L0 zKfPVTVif%*R-F1@kt3A*P;Db#EPqfNm%zPI`*B(Vb&c7DM0157;zljD&%n0-*l7RP zVAsqQ3ib|HVbg;i1ws1zu&Il$SvLJWGIpbbu2fM8Z2M)4{8!5O;n#$8-FDanhs6c# z_&|c;59o&sJ<@5%ABDX*-xTb)zL5VM`CEg #include #include @@ -46,4 +46,4 @@ int SPIEraseAreaEx(const uint32_t start, const uint32_t size) return 0; } -*/ + diff --git a/bootloaders/eboot/flash.h b/bootloaders/eboot/flash.h index f762296cf..ea8b65c1f 100644 --- a/bootloaders/eboot/flash.h +++ b/bootloaders/eboot/flash.h @@ -12,7 +12,7 @@ int SPIEraseBlock(uint32_t block); int SPIEraseSector(uint32_t sector); int SPIRead(uint32_t addr, void *dest, size_t size); int SPIWrite(uint32_t addr, void *src, size_t size); -//int SPIEraseAreaEx(const uint32_t start, const uint32_t size); +int SPIEraseAreaEx(const uint32_t start, const uint32_t size); #define FLASH_SECTOR_SIZE 0x1000 #define FLASH_BLOCK_SIZE 0x10000 diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 3bec3830c..4e0b75106 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 - (5 * FLASH_SECTOR_SIZE); + uint32_t freeSpaceEnd = (uint32_t)&_SPIFFS_start - 0x40200000; #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 1504b2e97..4d9dca9e2 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -1,14 +1,30 @@ #include "Updater.h" #include "Arduino.h" #include "eboot_command.h" -extern "C"{ - #include "mem.h" -} + //#define DEBUG_UPDATER Serial extern "C" uint32_t _SPIFFS_start; -UpdaterClass::UpdaterClass() : _error(0), _buffer(0), _bufferLen(0), _size(0), _startAddress(0), _currentAddress(0) {} +UpdaterClass::UpdaterClass() +: _error(0) +, _buffer(0) +, _bufferLen(0) +, _size(0) +, _startAddress(0) +, _currentAddress(0) +{ +} + +void UpdaterClass::_reset() { + if (_buffer) + delete[] _buffer; + _buffer = 0; + _bufferLen = 0; + _startAddress = 0; + _currentAddress = 0; + _size = 0; +} bool UpdaterClass::begin(size_t size){ if(_size > 0){ @@ -26,17 +42,13 @@ bool UpdaterClass::begin(size_t size){ return false; } - if(_buffer) os_free(_buffer); - _bufferLen = 0; - _startAddress = 0; - _currentAddress = 0; - _size = 0; + _reset(); _error = 0; //size of current sketch rounded to a sector uint32_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - //address of the end of the space available for sketch and update (5 sectors are for EEPROM and init data) - uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000 - (5 * FLASH_SECTOR_SIZE); + //address of the end of the space available for sketch and update + uint32_t updateEndAddress = (uint32_t)&_SPIFFS_start - 0x40200000; //size of the update rounded to a sector uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); //address where we will start writing the update @@ -55,7 +67,7 @@ bool UpdaterClass::begin(size_t size){ _startAddress = updateStartAddress; _currentAddress = _startAddress; _size = size; - _buffer = (uint8_t*)os_malloc(FLASH_SECTOR_SIZE); + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; return true; } @@ -72,11 +84,8 @@ bool UpdaterClass::end(bool 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; + + _reset(); return false; } @@ -86,9 +95,6 @@ bool UpdaterClass::end(bool evenIfRemaining){ } _size = progress(); } - if(_buffer) os_free(_buffer); - _bufferLen = 0; - _currentAddress = 0; eboot_command ebcmd; ebcmd.action = ACTION_COPY_RAW; @@ -100,19 +106,19 @@ bool UpdaterClass::end(bool evenIfRemaining){ #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; + + _reset(); return true; } bool UpdaterClass::_writeBuffer(){ noInterrupts(); int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE); - if(!rc) rc = SPIWrite(_currentAddress, _buffer, _bufferLen); + if (!rc) { + rc = SPIWrite(_currentAddress, _buffer, _bufferLen); + } interrupts(); - if (rc){ + if (rc) { _error = UPDATE_ERROR_WRITE; _currentAddress = (_startAddress + _size); #ifdef DEBUG_UPDATER @@ -125,15 +131,15 @@ bool UpdaterClass::_writeBuffer(){ return true; } -size_t UpdaterClass::write(uint8_t *data, size_t len){ +size_t UpdaterClass::write(uint8_t *data, size_t len) { size_t left = len; - if(hasError()||!isRunning()) + if(hasError() || !isRunning()) return 0; if(len > remaining()) len = remaining(); - while((_bufferLen + left) > FLASH_SECTOR_SIZE){ + while((_bufferLen + left) > FLASH_SECTOR_SIZE) { size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen; memcpy(_buffer + _bufferLen, data + (len - left), toBuff); _bufferLen += toBuff; @@ -155,13 +161,13 @@ size_t UpdaterClass::write(uint8_t *data, size_t len){ return len; } -size_t UpdaterClass::writeStream(Stream &data){ +size_t UpdaterClass::writeStream(Stream &data) { size_t written = 0; size_t toRead = 0; - if(hasError()||!isRunning()) + if(hasError() || !isRunning()) return 0; - while(remaining()){ + while(remaining()) { toRead = FLASH_SECTOR_SIZE - _bufferLen; toRead = data.readBytes(_buffer + _bufferLen, toRead); if(toRead == 0){ //Timeout diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index e495ef0e7..be1a04dd6 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -27,7 +27,7 @@ class UpdaterClass { size_t write(uint8_t *data, size_t len); /* - Writes the remaining bytes from the Sream to the flash + 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 @@ -53,32 +53,33 @@ class UpdaterClass { void printError(Stream &out); //Helpers - inline uint8_t getError(){ return _error; } - inline void clearError(){ _error = UPDATE_ERROR_OK; } - inline bool hasError(){ return _error != UPDATE_ERROR_OK; } - inline bool isRunning(){ return _size > 0; } - inline bool isFinished(){ return _currentAddress == (_startAddress + _size); } - inline size_t size(){ return _size; } - inline size_t progress(){ return _currentAddress - _startAddress; } - inline size_t remaining(){ return _size - (_currentAddress - _startAddress); } + 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 _currentAddress == (_startAddress + _size); } + size_t size(){ return _size; } + size_t progress(){ return _currentAddress - _startAddress; } + size_t remaining(){ return _size - (_currentAddress - _startAddress); } /* Template to write from objects that expose available() and read(uint8_t*, size_t) methods - faster than the readStream method + faster than the writeStream method writes only what is available */ template size_t write(T &data){ size_t written = 0; - if(hasError()||!isRunning()) + if (hasError() || !isRunning()) return 0; + size_t available = data.available(); - while(available){ - if((_bufferLen + available) > remaining()){ - available = (remaining() - _bufferLen); + while(available) { + if(_bufferLen + available > remaining()){ + available = remaining() - _bufferLen; } - if((_bufferLen + available) > FLASH_SECTOR_SIZE){ + if(_bufferLen + available > FLASH_SECTOR_SIZE) { size_t toBuff = FLASH_SECTOR_SIZE - _bufferLen; data.read(_buffer + _bufferLen, toBuff); _bufferLen += toBuff; @@ -89,8 +90,8 @@ class UpdaterClass { data.read(_buffer + _bufferLen, available); _bufferLen += available; written += available; - if(_bufferLen == remaining()){ - if(!_writeBuffer()){ + if(_bufferLen == remaining()) { + if(!_writeBuffer()) { return written; } } @@ -104,14 +105,15 @@ class UpdaterClass { } private: + void _reset(); + bool _writeBuffer(); + uint8_t *_buffer; - uint8_t _error; size_t _bufferLen; size_t _size; uint32_t _startAddress; uint32_t _currentAddress; - - bool _writeBuffer(); + uint8_t _error; }; extern UpdaterClass Update;