diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index e3dbf8c9..f032e5f7 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -446,7 +446,6 @@ PROGMEM const char* const custom_reset_string[] = { // ----------------------------------------------------------------------------- #define OTA_PORT 8266 // OTA port -#define OTA_TERMINAL 0 // Enable OTA from terminal #define OTA_GITHUB_FP "D79F076110B39293E349AC89845B0380C19E2F8B" // ----------------------------------------------------------------------------- diff --git a/code/espurna/ota.ino b/code/espurna/ota.ino index 89b7320b..ee20d158 100644 --- a/code/espurna/ota.ino +++ b/code/espurna/ota.ino @@ -9,7 +9,7 @@ Copyright (C) 2016-2018 by Xose PĂ©rez #include "ArduinoOTA.h" // ----------------------------------------------------------------------------- -// OTA +// Arduino OTA // ----------------------------------------------------------------------------- void _otaConfigure() { @@ -20,51 +20,143 @@ void _otaConfigure() { #endif } -#if TERMINAL_SUPPORT && OTA_TERMINAL +void _otaLoop() { + ArduinoOTA.handle(); +} -#include +// ----------------------------------------------------------------------------- +// Terminal OTA +// ----------------------------------------------------------------------------- -void _otaFrom(const char * url) { +#if TERMINAL_SUPPORT - DEBUG_MSG_P(PSTR("[OTA] Downloading from '%s'\n"), url); - #if WEB_SUPPORT - wsSend_P(PSTR("{\"message\": 2}")); - #endif +#include +AsyncClient * _ota_client; +char * _ota_host; +char * _ota_url; +unsigned long _ota_size = 0; - ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return ret; - if (strncmp(url, "https", 5) == 0) { - String fp = getSetting("otafp", OTA_GITHUB_FP); - DEBUG_MSG_P(PSTR("[OTA] Using fingerprint: '%s'\n"), fp.c_str()); - ret = ESPhttpUpdate.update(url, APP_VERSION, fp.c_str()); - } else { - ret = ESPhttpUpdate.update(url, APP_VERSION); - } +const char OTA_REQUEST_TEMPLATE[] PROGMEM = + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: ESPurna\r\n" + "Connection: close\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 0\r\n\r\n\r\n"; - switch(ret) { - case HTTP_UPDATE_FAILED: - DEBUG_MSG_P( - PSTR("[OTA] Error (%d): %s\n"), - ESPhttpUpdate.getLastError(), - ESPhttpUpdate.getLastErrorString().c_str() - ); - break; +void _otaFrom(const char * host, unsigned int port, const char * url) { - case HTTP_UPDATE_NO_UPDATES: - DEBUG_MSG_P(PSTR("[OTA] No updates available\n")); - break; + if (_ota_host) free(_ota_host); + if (_ota_url) free(_ota_url); + _ota_host = strdup(host); + _ota_url = strdup(url); + _ota_size = 0; - case HTTP_UPDATE_OK: - DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n")); - #if WEB_SUPPORT - wsSend_P(PSTR("{\"action\": \"reload\"}")); - #endif + if (_ota_client == NULL) { + _ota_client = new AsyncClient(); + } + + _ota_client->onDisconnect([](void *s, AsyncClient *c) { + + DEBUG_MSG_P(PSTR("\n")); + + if (Update.end(true)){ + DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size); deferredReset(100, CUSTOM_RESET_OTA); - break; + } else { + #ifdef DEBUG_PORT + Update.printError(DEBUG_PORT); + #endif + } + + DEBUG_MSG_P(PSTR("[OTA] Disconnected\n")); + + _ota_client->free(); + delete _ota_client; + _ota_client = NULL; + free(_ota_host); + _ota_host = NULL; + free(_ota_url); + _ota_url = NULL; + + }, 0); + + _ota_client->onTimeout([](void *s, AsyncClient *c, uint32_t time) { + _ota_client->close(true); + }, 0); + + _ota_client->onData([](void * arg, AsyncClient * c, void * data, size_t len) { + + char * p = (char *) data; + + if (_ota_size == 0) { + + Update.runAsync(true); + if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) { + #ifdef DEBUG_PORT + Update.printError(DEBUG_PORT); + #endif + } + p = strstr((char *)data, "\r\n\r\n") + 4; + len = len - (p - (char *) data); + + } + + if (!Update.hasError()) { + if (Update.write((uint8_t *) p, len) != len) { + #ifdef DEBUG_PORT + Update.printError(DEBUG_PORT); + #endif + } + } + + _ota_size += len; + DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size); + + + }, NULL); + + _ota_client->onConnect([](void * arg, AsyncClient * client) { + DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url); + char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + strlen(_ota_url) + strlen(_ota_host)]; + snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url, _ota_host); + client->write(buffer); + + }, NULL); + + + bool connected = _ota_client->connect(host, port); + if (!connected) { + DEBUG_MSG_P(PSTR("[OTA] Connection failed\n")); + _ota_client->close(true); + } + +} + +void _otaFrom(String url) { + + // Port from protocol + unsigned int port = 80; + if (url.startsWith("https://")) port = 443; + url = url.substring(url.indexOf("/") + 2); + + // Get host + String host = url.substring(0, url.indexOf("/")); + + // Explicit port + int p = host.indexOf(":"); + if (p > 0) { + port = host.substring(p + 1).toInt(); + host = host.substring(0, p); } + // Get URL + String uri = url.substring(url.indexOf("/")); + + _otaFrom(host.c_str(), port, uri.c_str()); + } void _otaInitCommands() { @@ -75,17 +167,13 @@ void _otaInitCommands() { } else { DEBUG_MSG_P(PSTR("+OK\n")); String url = String(e->argv[1]); - _otaFrom(url.c_str()); + _otaFrom(url); } }); } -#endif // TERMINAL_SUPPORT && OTA_TERMINAL - -void _otaLoop() { - ArduinoOTA.handle(); -} +#endif // TERMINAL_SUPPORT // ----------------------------------------------------------------------------- @@ -97,7 +185,7 @@ void otaSetup() { wsOnAfterParseRegister(_otaConfigure); #endif - #if TERMINAL_SUPPORT && OTA_TERMINAL + #if TERMINAL_SUPPORT _otaInitCommands(); #endif