1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Merge pull request #512 from esp8266/feature/ota

OTA
This commit is contained in:
Ivan Grokhotkov 2015-07-06 21:16:10 +03:00
commit 7891a84e8b
15 changed files with 683 additions and 111 deletions

View File

@ -1,7 +1,9 @@
menu.UploadSpeed=Upload Speed menu.UploadSpeed=Upload Speed
menu.CpuFrequency=CPU Frequency menu.CpuFrequency=CPU Frequency
menu.FlashSize=Flash Size menu.FlashSize=Flash Size
menu.FlashMode=Flash Mode
menu.FlashFreq=Flash Frequency menu.FlashFreq=Flash Frequency
menu.UploadTool=Upload Using
############################################################## ##############################################################
generic.name=Generic ESP8266 Module generic.name=Generic ESP8266 Module
@ -23,11 +25,26 @@ generic.build.variant=generic
generic.build.flash_mode=qio generic.build.flash_mode=qio
generic.build.spiffs_pagesize=256 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=80 MHz
generic.menu.CpuFrequency.80.build.f_cpu=80000000L generic.menu.CpuFrequency.80.build.f_cpu=80000000L
generic.menu.CpuFrequency.160=160 MHz generic.menu.CpuFrequency.160=160 MHz
generic.menu.CpuFrequency.160.build.f_cpu=160000000L 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=115200
generic.menu.UploadSpeed.115200.upload.speed=115200 generic.menu.UploadSpeed.115200.upload.speed=115200
generic.menu.UploadSpeed.9600=9600 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_end=0x1000000
# generic.menu.FlashSize.16M.build.spiffs_blocksize=8192 # 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) modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV)

View File

@ -1,4 +1,5 @@
XTENSA_TOOLCHAIN ?= XTENSA_TOOLCHAIN ?= ../../tools/xtensa-lx106-elf/bin/
ESPTOOL ?= ../../tools/esptool
BIN_DIR := ./ BIN_DIR := ./
TARGET_DIR := ./ TARGET_DIR := ./
@ -6,7 +7,7 @@ TARGET_DIR := ./
TARGET_OBJ_FILES := \ TARGET_OBJ_FILES := \
eboot.o \ eboot.o \
eboot_command.o \ eboot_command.o \
flash.o \
TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES)) TARGET_OBJ_PATHS := $(addprefix $(TARGET_DIR)/,$(TARGET_OBJ_FILES))

View File

@ -76,38 +76,31 @@ int copy_raw(const uint32_t src_addr,
const uint32_t dst_addr, const uint32_t dst_addr,
const uint32_t size) const uint32_t size)
{ {
ets_putc('\n');
ets_putc('c');
ets_putc('p');
ets_putc('\n');
// require regions to be aligned // require regions to be aligned
if (src_addr & 0xfff != 0 || if (src_addr & 0xfff != 0 ||
dst_addr & 0xfff != 0) { dst_addr & 0xfff != 0) {
return 1; return 1;
} }
if (SPIEraseAreaEx(dst_addr, size)) { const uint32_t buffer_size = FLASH_SECTOR_SIZE;
return 2;
}
const uint32_t buffer_size = 4096;
uint8_t buffer[buffer_size]; uint8_t buffer[buffer_size];
uint32_t left = ((size+buffer_size-1) & ~(buffer_size-1));
const uint32_t end = src_addr + size;
uint32_t saddr = src_addr; uint32_t saddr = src_addr;
uint32_t daddr = dst_addr; uint32_t daddr = dst_addr;
uint32_t left = size;
while (saddr < end) { while (left) {
uint32_t will_copy = (left < buffer_size) ? left : buffer_size; if (SPIEraseSector(daddr/buffer_size)) {
if (SPIRead(saddr, buffer, will_copy)) { return 2;
return 3; }
} if (SPIRead(saddr, buffer, buffer_size)) {
if (SPIWrite(daddr, buffer, will_copy)) { return 3;
return 4; }
} if (SPIWrite(daddr, buffer, buffer_size)) {
saddr += will_copy; return 4;
daddr += will_copy; }
left -= will_copy; saddr += buffer_size;
daddr += buffer_size;
left -= buffer_size;
} }
return 0; return 0;
@ -123,14 +116,16 @@ void main()
if (eboot_command_read(&cmd)) { if (eboot_command_read(&cmd)) {
cmd.action = ACTION_LOAD_APP; cmd.action = ACTION_LOAD_APP;
cmd.args[0] = 0; cmd.args[0] = 0;
ets_putc('e'); ets_putc('~');
} else { } else {
ets_putc('@'); ets_putc('@');
} }
eboot_command_clear(); eboot_command_clear();
if (cmd.action == ACTION_COPY_RAW) { 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]); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]);
ets_putc('0'+res); ets_putc('\n');
if (res == 0) { if (res == 0) {
cmd.action = ACTION_LOAD_APP; cmd.action = ACTION_LOAD_APP;
cmd.args[0] = cmd.args[1]; cmd.args[0] = cmd.args[1];
@ -138,15 +133,14 @@ void main()
} }
if (cmd.action == ACTION_LOAD_APP) { 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) { if (res) {
ets_putc('\n'); SWRST;
ets_putc('#');
ets_putc('0' + res);
ets_putc('\n');
SWRST;
} }
while(true){} while(true){}

Binary file not shown.

View File

@ -233,6 +233,7 @@ void loop(void);
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "Esp.h" #include "Esp.h"
#include "Updater.h"
#include "debug.h" #include "debug.h"
#define min(a,b) ((a)<(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b))

View File

@ -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; return freeSpaceEnd - freeSpaceStart;
} }
bool EspClass::updateSketch(Stream& in, uint32_t size) { bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) {
if(!Update.begin(size)){
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
if(restartOnFail) ESP.restart();
return false;
}
if (size > getFreeSketchSpace()) if(Update.writeStream(in) != size){
return false; #ifdef DEBUG_SERIAL
DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
if(restartOnFail) ESP.restart();
return false;
}
uint32_t usedSize = getSketchSize(); if(!Update.end()){
uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); #ifdef DEBUG_SERIAL
uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); DEBUG_SERIAL.print("Update ");
Update.printError(DEBUG_SERIAL);
#endif
if(restartOnFail) ESP.restart();
return false;
}
#ifdef DEBUG_SERIAL #ifdef DEBUG_SERIAL
DEBUG_SERIAL.printf("erase @0x%x size=0x%x\r\n", freeSpaceStart, roundedSize); DEBUG_SERIAL.println("Update SUCCESS");
#endif #endif
if(restartOnSuccess) ESP.restart();
noInterrupts(); return true;
int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize);
interrupts();
if (rc)
return false;
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("erase done");
#endif
uint32_t addr = freeSpaceStart;
uint32_t left = size;
const uint32_t bufferSize = FLASH_SECTOR_SIZE;
std::unique_ptr<uint8_t> buffer(new uint8_t[bufferSize]);
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("writing");
#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
}
#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
} }

View File

@ -116,7 +116,7 @@ class EspClass {
uint32_t getSketchSize(); uint32_t getSketchSize();
uint32_t getFreeSketchSpace(); 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(); String getResetInfo();
struct rst_info * getResetInfoPtr(); struct rst_info * getResetInfoPtr();

209
cores/esp8266/Updater.cpp Normal file
View File

@ -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;

121
cores/esp8266/Updater.h Normal file
View File

@ -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<typename T>
size_t write(T &data){
size_t written = 0;
if (hasError() || !isRunning())
return 0;
size_t available = data.available();
while(available) {
if(_bufferLen + available > remaining()){
available = remaining() - _bufferLen;
}
if(_bufferLen + available > 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

View File

@ -0,0 +1,73 @@
/*
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
const char* host = "esp8266-webupdate";
const char* ssid = "........";
const char* password = "........";
ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
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);
}

View File

@ -66,11 +66,16 @@ extern "C" {
#define MDNS_TYPE_PTR 0x000C #define MDNS_TYPE_PTR 0x000C
#define MDNS_TYPE_SRV 0x0021 #define MDNS_TYPE_SRV 0x0021
#define MDNS_TYPE_TXT 0x0010 #define MDNS_TYPE_TXT 0x0010
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_CLASS_IN 0x0001 #define MDNS_CLASS_IN 0x0001
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001 #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_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_read16() (((uint16_t)_conn->read() << 8) | _conn->read())
#define _conn_read8() _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_PTR) os_printf(" PTR ");
else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV "); else if(currentType == MDNS_TYPE_SRV) os_printf(" SRV ");
else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT "); else if(currentType == MDNS_TYPE_TXT) os_printf(" TXT ");
else if(currentType == MDNS_TYPE_NSEC) os_printf(" NSEC ");
else os_printf(" 0x%04X ", currentType); else os_printf(" 0x%04X ", currentType);
if(currentClass == MDNS_CLASS_IN) os_printf(" IN "); if(currentClass == MDNS_CLASS_IN) os_printf(" IN ");

View File

@ -0,0 +1,98 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUDP.h>
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);
}

View File

@ -93,3 +93,13 @@ tools.esptool.upload.protocol=esp
tools.esptool.upload.params.verbose=-vv tools.esptool.upload.params.verbose=-vv
tools.esptool.upload.params.quiet= 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.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"

94
tools/espota.py Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python
#
# this script will push an OTA update to the ESP
# use it like: python espota.py <ESP_IP_address> <sketch.bin>
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))