|
|
- /*
-
- OTA MODULE
-
- Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
-
- */
-
- #include "ArduinoOTA.h"
-
- // -----------------------------------------------------------------------------
- // Arduino OTA
- // -----------------------------------------------------------------------------
-
- void _otaConfigure() {
- ArduinoOTA.setPort(OTA_PORT);
- ArduinoOTA.setHostname(getSetting("hostname").c_str());
- #if USE_PASSWORD
- ArduinoOTA.setPassword(getAdminPass().c_str());
- #endif
- }
-
- void _otaLoop() {
- ArduinoOTA.handle();
- }
-
- // -----------------------------------------------------------------------------
- // Terminal OTA
- // -----------------------------------------------------------------------------
-
- #if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
-
- #include <ESPAsyncTCP.h>
- AsyncClient * _ota_client;
- char * _ota_host;
- char * _ota_url;
- unsigned int _ota_port = 80;
- unsigned long _ota_size = 0;
-
- 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";
-
-
- void _otaFrom(const char * host, unsigned int port, const char * url) {
-
- if (_ota_host) free(_ota_host);
- if (_ota_url) free(_ota_url);
- _ota_host = strdup(host);
- _ota_url = strdup(url);
- _ota_port = port;
- _ota_size = 0;
-
- 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);
- } else {
- #ifdef DEBUG_PORT
- Update.printError(DEBUG_PORT);
- #endif
- eepromRotate(true);
- }
-
- 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);
-
- delay(0);
-
- }, NULL);
-
- _ota_client->onConnect([](void * arg, AsyncClient * client) {
-
- #if ASYNC_TCP_SSL_ENABLED
- if (443 == _ota_port) {
- uint8_t fp[20] = {0};
- sslFingerPrintArray(getSetting("otafp", OTA_GITHUB_FP).c_str(), fp);
- SSL * ssl = _ota_client->getSSL();
- if (ssl_match_fingerprint(ssl, fp) != SSL_OK) {
- DEBUG_MSG_P(PSTR("[OTA] Warning: certificate doesn't match\n"));
- }
- }
- #endif
-
- // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
- eepromRotate(false);
-
- 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);
-
- #if ASYNC_TCP_SSL_ENABLED
- bool connected = _ota_client->connect(host, port, 443 == port);
- #else
- bool connected = _ota_client->connect(host, port);
- #endif
-
- if (!connected) {
- DEBUG_MSG_P(PSTR("[OTA] Connection failed\n"));
- _ota_client->close(true);
- }
-
- }
-
- void _otaFrom(String url) {
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
- DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
- return;
- }
-
- // 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());
-
- }
-
- #endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
-
-
- #if TERMINAL_SUPPORT
-
- void _otaInitCommands() {
-
- terminalRegisterCommand(F("OTA"), [](Embedis* e) {
- if (e->argc < 2) {
- terminalError(F("Wrong arguments"));
- } else {
- terminalOK();
- String url = String(e->argv[1]);
- _otaFrom(url);
- }
- });
-
- }
-
- #endif // TERMINAL_SUPPORT
-
- #if OTA_MQTT_SUPPORT
-
- void _otaMQTTCallback(unsigned int type, const char * topic, const char * payload) {
- if (type == MQTT_CONNECT_EVENT) {
- mqttSubscribe(MQTT_TOPIC_OTA);
- }
-
- if (type == MQTT_MESSAGE_EVENT) {
- // Match topic
- String t = mqttMagnitude((char *) topic);
- if (t.equals(MQTT_TOPIC_OTA)) {
- DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload);
- _otaFrom(payload);
- }
- }
- }
-
- #endif // OTA_MQTT_SUPPORT
-
- // -----------------------------------------------------------------------------
-
- void otaSetup() {
-
- _otaConfigure();
-
- #if TERMINAL_SUPPORT
- _otaInitCommands();
- #endif
-
- #if OTA_MQTT_SUPPORT
- mqttRegister(_otaMQTTCallback);
- #endif
-
- // Main callbacks
- espurnaRegisterLoop(_otaLoop);
- espurnaRegisterReload(_otaConfigure);
-
- // -------------------------------------------------------------------------
-
- ArduinoOTA.onStart([]() {
-
- // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
- eepromRotate(false);
-
- // Because ArduinoOTA is synchronous, force backup right now instead of waiting for the next loop()
- eepromBackup(0);
-
- DEBUG_MSG_P(PSTR("[OTA] Start\n"));
-
- #if WEB_SUPPORT
- wsSend_P(PSTR("{\"message\": 2}"));
- #endif
-
- });
-
- ArduinoOTA.onEnd([]() {
- DEBUG_MSG_P(PSTR("\n"));
- DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n"));
- #if WEB_SUPPORT
- wsSend_P(PSTR("{\"action\": \"reload\"}"));
- #endif
- deferredReset(100, CUSTOM_RESET_OTA);
- });
-
- ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
- static unsigned int _progOld;
-
- unsigned int _prog = (progress / (total / 100));
- if (_prog != _progOld) {
- DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%\r"), _prog);
- _progOld = _prog;
- }
- });
-
- ArduinoOTA.onError([](ota_error_t error) {
- #if DEBUG_SUPPORT
- DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error);
- if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n"));
- else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n"));
- else if (error == OTA_CONNECT_ERROR) DEBUG_MSG_P(PSTR("Connect Failed\n"));
- else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG_P(PSTR("Receive Failed\n"));
- else if (error == OTA_END_ERROR) DEBUG_MSG_P(PSTR("End Failed\n"));
- #endif
- eepromRotate(true);
- });
-
- ArduinoOTA.begin();
-
- }
|