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/bootloaders/eboot/Makefile b/bootloaders/eboot/Makefile index 0872ee35f..7a07d7615 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 := ./ @@ -6,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 0b74fcdf0..cbc87044b 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('0'+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('0'+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 97e25c146..08536aa72 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ diff --git a/bootloaders/eboot/flash.c b/bootloaders/eboot/flash.c index f90e25b34..8359e168b 100644 --- a/bootloaders/eboot/flash.c +++ b/bootloaders/eboot/flash.c @@ -4,7 +4,7 @@ * Redistribution and use is permitted according to the conditions of the * 3-clause BSD license to be found in the LICENSE file. */ - + #include #include #include 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 cda089b04..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,83 +358,38 @@ uint32_t EspClass::getFreeSketchSpace() { return freeSpaceEnd - freeSpaceStart; } -bool EspClass::updateSketch(Stream& in, uint32_t size) { - - if (size > getFreeSketchSpace()) - 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) - 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.println("stream read failed"); -#endif - return false; - } - - if(addr == freeSpaceStart) { - // check for valid first magic byte - if(*((uint8 *) buffer.get()) != 0xE9) { - return false; - } - } - - noInterrupts(); - rc = SPIWrite(addr, buffer.get(), willRead); - interrupts(); - if (rc) { -#ifdef DEBUG_SERIAL - DEBUG_SERIAL.println("write failed"); -#endif - 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 80555d591..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 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..4d9dca9e2 --- /dev/null +++ b/cores/esp8266/Updater.cpp @@ -0,0 +1,209 @@ +#include "Updater.h" +#include "Arduino.h" +#include "eboot_command.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) +{ +} + +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){ +#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; + } + + _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 + 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 + uint32_t updateStartAddress = updateEndAddress - roundedSize; + + //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; + } + + //initialize + _startAddress = updateStartAddress; + _currentAddress = _startAddress; + _size = size; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + return true; +} + +bool UpdaterClass::end(bool evenIfRemaining){ + if(_size == 0){ +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.println("no update"); +#endif + return false; + } + + if(hasError() || (!isFinished() && !evenIfRemaining)){ +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size); +#endif + + _reset(); + return false; + } + + if(evenIfRemaining){ + if(_bufferLen > 0){ + _writeBuffer(); + } + _size = progress(); + } + + eboot_command ebcmd; + ebcmd.action = ACTION_COPY_RAW; + ebcmd.args[0] = _startAddress; + ebcmd.args[1] = 0x00000; + ebcmd.args[2] = _size; + eboot_command_write(&ebcmd); + +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf("Staged: address:0x%08X, size:0x%08X\n", _startAddress, _size); +#endif + + _reset(); + return true; +} + +bool UpdaterClass::_writeBuffer(){ + noInterrupts(); + int rc = SPIEraseSector(_currentAddress/FLASH_SECTOR_SIZE); + if (!rc) { + rc = SPIWrite(_currentAddress, _buffer, _bufferLen); + } + interrupts(); + if (rc) { + _error = UPDATE_ERROR_WRITE; + _currentAddress = (_startAddress + _size); +#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() || !isRunning()) + 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); + _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() || !isRunning()) + return 0; + + while(remaining()) { + toRead = FLASH_SECTOR_SIZE - _bufferLen; + toRead = data.readBytes(_buffer + _bufferLen, toRead); + if(toRead == 0){ //Timeout + _error = UPDATE_ERROR_STREAM; + _currentAddress = (_startAddress + _size); +#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..be1a04dd6 --- /dev/null +++ b/cores/esp8266/Updater.h @@ -0,0 +1,121 @@ +#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 the space needed for the update + Will return false if there is not enough space + */ + 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 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 !evenIfRemainanig + 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); + + /* + 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 _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 writeStream method + writes only what is available + */ + template + 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 > 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: + void _reset(); + bool _writeBuffer(); + + uint8_t *_buffer; + size_t _bufferLen; + size_t _size; + uint32_t _startAddress; + uint32_t _currentAddress; + uint8_t _error; +}; + +extern UpdaterClass Update; + +#endif diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino new file mode 100644 index 000000000..665dd6199 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -0,0 +1,73 @@ +/* + To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update +*/ + +#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); + WiFiUDP::stopAll(); + Serial.printf("Update: %s\n", upload.filename.c_str()); + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if(!Update.begin(maxSketchSpace)){//start with max available size + Update.printError(Serial); + } + } else if(upload.status == UPLOAD_FILE_WRITE){ + if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ + Update.printError(Serial); + } + } 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.printf("Ready! Open http://%s.local in your browser\n", host); + } else { + Serial.println("WiFi Failed"); + } +} + +void loop(void){ + server.handleClient(); + delay(1); +} 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..e7dc97795 --- /dev/null +++ b/libraries/ESP8266mDNS/examples/DNS_SD_Arduino_OTA/DNS_SD_Arduino_OTA.ino @@ -0,0 +1,98 @@ +#include +#include +#include + +const char* host = "esp8266-ota"; +const char* ssid = "**********"; +const char* pass = "**********"; +const uint16_t aport = 8266; + +WiFiServer TelnetServer(aport); +WiFiClient Telnet; +WiFiUDP OTA; + +void setup() { + Serial.begin(115200); + 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); + OTA.begin(aport); + TelnetServer.begin(); + TelnetServer.setNoDelay(true); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } +} + +void loop() { + //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(); + + WiFiUDP::stopAll(); + + if(!Update.begin(size)){ + Serial.println("Update Begin Error"); + return; + } + + 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(1); +} diff --git a/platform.txt b/platform.txt index bf3b930b5..169dd96ad 100644 --- a/platform.txt +++ b/platform.txt @@ -93,3 +93,13 @@ 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=python "{path}/espota.py" "{serial.port}" "{network.port}" "{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}" 8266 "{build.path}/{build.project_name}.bin" diff --git a/tools/espota.py b/tools/espota.py new file mode 100755 index 000000000..dbd22eb45 --- /dev/null +++ b/tools/espota.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# this script will push an OTA update to the ESP +# use it like: python espota.py + +from __future__ import print_function +import socket +import sys +import os + +def serve(remoteAddr, remotePort, 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('Starting on %s:%s' % server_address, file=sys.stderr) + try: + sock.bind(server_address) + sock.listen(1) + except: + print('Listen Failed', file=sys.stderr) + return 1 + + content_size = os.path.getsize(filename) + print('Upload size: %d' % content_size, file=sys.stderr) + message = '%d %d %d\n' % (0, serverPort, content_size) + + # 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) + 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() + 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: + connection.settimeout(60) + data = connection.recv(32) + 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: + connection.close() + f.close() + + sock.close() + return 1 + +def main(args): + return serve(args[1], args[2], args[3]) + + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) +