Browse Source

Optional Web(UI) OTA (#2190)

* web: optional OTA support

* add prototype

* hide under websupport
pull/2191/head
Max Prokhorov 4 years ago
committed by GitHub
parent
commit
917ce8de83
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 17989 additions and 17950 deletions
  1. +6
    -1
      code/espurna/config/general.h
  2. BIN
      code/espurna/data/index.all.html.gz
  3. BIN
      code/espurna/data/index.light.html.gz
  4. BIN
      code/espurna/data/index.lightfox.html.gz
  5. BIN
      code/espurna/data/index.rfbridge.html.gz
  6. BIN
      code/espurna/data/index.rfm69.html.gz
  7. BIN
      code/espurna/data/index.sensor.html.gz
  8. BIN
      code/espurna/data/index.small.html.gz
  9. BIN
      code/espurna/data/index.thermostat.html.gz
  10. +3
    -0
      code/espurna/espurna.ino
  11. +6
    -0
      code/espurna/ota.h
  12. +149
    -0
      code/espurna/ota_web.ino
  13. +2410
    -2410
      code/espurna/static/index.all.html.gz.h
  14. +2215
    -2215
      code/espurna/static/index.light.html.gz.h
  15. +1859
    -1859
      code/espurna/static/index.lightfox.html.gz.h
  16. +2460
    -2460
      code/espurna/static/index.rfbridge.html.gz.h
  17. +3231
    -3231
      code/espurna/static/index.rfm69.html.gz.h
  18. +1940
    -1939
      code/espurna/static/index.sensor.html.gz.h
  19. +1832
    -1832
      code/espurna/static/index.small.html.gz.h
  20. +1869
    -1869
      code/espurna/static/index.thermostat.html.gz.h
  21. +8
    -133
      code/espurna/web.ino
  22. +1
    -1
      code/html/index.html

+ 6
- 1
code/espurna/config/general.h View File

@ -870,7 +870,12 @@
#ifndef OTA_CLIENT
#define OTA_CLIENT OTA_CLIENT_ASYNCTCP // Terminal / MQTT OTA support
// OTA_CLIENT_ASYNCTCP (ESPAsyncTCP library)
// OTA_CLIENT_HTTPUPDATE (Arduino Core library)
// OTA_CLIENT_HTTPUPDATE (Arduino Core library)j
// OTA_CLIENT_NONE to disable
#endif
#ifndef OTA_WEB_SUPPORT
#define OTA_WEB_SUPPORT 1 // Support `/upgrade` endpoint and WebUI OTA handler
#endif
#define OTA_GITHUB_FP "CA:06:F5:6B:25:8B:7A:0D:4F:2B:05:47:09:39:47:86:51:15:19:84"


BIN
code/espurna/data/index.all.html.gz View File


BIN
code/espurna/data/index.light.html.gz View File


BIN
code/espurna/data/index.lightfox.html.gz View File


BIN
code/espurna/data/index.rfbridge.html.gz View File


BIN
code/espurna/data/index.rfm69.html.gz View File


BIN
code/espurna/data/index.sensor.html.gz View File


BIN
code/espurna/data/index.small.html.gz View File


BIN
code/espurna/data/index.thermostat.html.gz View File


+ 3
- 0
code/espurna/espurna.ino View File

@ -184,6 +184,9 @@ void setup() {
#if DEBUG_WEB_SUPPORT
debugWebSetup();
#endif
#if OTA_WEB_SUPPORT
otaWebSetup();
#endif
#endif
#if API_SUPPORT
apiSetup();


+ 6
- 0
code/espurna/ota.h View File

@ -9,6 +9,12 @@ OTA MODULE
#include <Updater.h>
#include <ArduinoOTA.h>
#if OTA_WEB_SUPPORT
void otaWebSetup();
#endif // OTA_WEB_SUPPORT == 1
#if OTA_ARDUINOOTA_SUPPORT
void arduinoOtaSetup();


+ 149
- 0
code/espurna/ota_web.ino View File

@ -0,0 +1,149 @@
/*
Part of the WEBSERVER module
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#include "ota.h"
#include "settings.h"
#include "storage_eeprom.h"
#include "utils.h"
#include "web.h"
#include "ws.h"
#if WEB_SUPPORT && OTA_WEB_SUPPORT
void _onUpgradeResponse(AsyncWebServerRequest *request, int code, const String& payload = "") {
auto *response = request->beginResponseStream("text/plain", 256);
response->addHeader("Connection", "close");
response->addHeader("X-XSS-Protection", "1; mode=block");
response->addHeader("X-Content-Type-Options", "nosniff");
response->addHeader("X-Frame-Options", "deny");
response->setCode(code);
if (payload.length()) {
response->printf("%s", payload.c_str());
} else {
if (!Update.hasError()) {
response->print("OK");
} else {
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0)
Update.printError(reinterpret_cast<Stream&>(response));
#else
Update.printError(*response);
#endif
}
}
request->send(response);
}
void _onUpgradeStatusSet(AsyncWebServerRequest *request, int code, const String& payload = "") {
_onUpgradeResponse(request, code, payload);
request->_tempObject = malloc(sizeof(bool));
}
void _onUpgrade(AsyncWebServerRequest *request) {
webLog(request);
if (!webAuthenticate(request)) {
return request->requestAuthentication(getSetting("hostname").c_str());
}
if (request->_tempObject) {
return;
}
_onUpgradeResponse(request, 200);
}
void _onUpgradeFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!webAuthenticate(request)) {
return request->requestAuthentication(getSetting("hostname").c_str());
}
// We set this after we are done with the request
// It is still possible to re-enter this callback even after connection is already closed
// 1.14.2: TODO: see https://github.com/me-no-dev/ESPAsyncWebServer/pull/660
// remote close or request sending some data before finishing parsing of the body will leak 1460 bytes
// waiting a bit for upstream. fork and point to the fixed version if not resolved before 1.14.2
if (request->_tempObject) {
return;
}
if (!index) {
// TODO: stop network activity completely when handling Update through ArduinoOTA or `ota` command?
if (Update.isRunning()) {
_onUpgradeStatusSet(request, 400, F("ERROR: Upgrade in progress"));
return;
}
// Check that header is correct and there is more data before anything is written to the flash
if (final || !len) {
_onUpgradeStatusSet(request, 400, F("ERROR: Invalid request"));
return;
}
if (!otaVerifyHeader(data, len)) {
_onUpgradeStatusSet(request, 400, F("ERROR: No magic byte / invalid flash config"));
return;
}
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
Update.runAsync(true);
// Note: cannot use request->contentLength() for multipart/form-data
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
_onUpgradeStatusSet(request, 500);
eepromRotate(true);
return;
}
}
if (request->_tempObject) {
return;
}
// Any error will cancel the update, but request may still be alive
if (!Update.isRunning()) {
return;
}
if (Update.write(data, len) != len) {
_onUpgradeStatusSet(request, 500);
Update.end();
eepromRotate(true);
return;
}
if (final) {
otaFinalize(index + len, CUSTOM_RESET_UPGRADE, true);
} else {
otaProgress(index + len);
}
}
void otaWebSetup() {
webServer()->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeFile);
wsRegister().
onVisible([](JsonObject& root) {
root["otaVisible"] = 1;
});
}
#endif // OTA_WEB_SUPPORT

+ 2410
- 2410
code/espurna/static/index.all.html.gz.h
File diff suppressed because it is too large
View File


+ 2215
- 2215
code/espurna/static/index.light.html.gz.h
File diff suppressed because it is too large
View File


+ 1859
- 1859
code/espurna/static/index.lightfox.html.gz.h
File diff suppressed because it is too large
View File


+ 2460
- 2460
code/espurna/static/index.rfbridge.html.gz.h
File diff suppressed because it is too large
View File


+ 3231
- 3231
code/espurna/static/index.rfm69.html.gz.h
File diff suppressed because it is too large
View File


+ 1940
- 1939
code/espurna/static/index.sensor.html.gz.h
File diff suppressed because it is too large
View File


+ 1832
- 1832
code/espurna/static/index.small.html.gz.h
File diff suppressed because it is too large
View File


+ 1869
- 1869
code/espurna/static/index.thermostat.html.gz.h
File diff suppressed because it is too large
View File


+ 8
- 133
code/espurna/web.ino View File

@ -8,7 +8,6 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if WEB_SUPPORT
#include "ota.h"
#include "system.h"
#include "utils.h"
#include "web.h"
@ -276,132 +275,11 @@ int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
*buf = 0;
return 0;
#endif
}
#endif
void _onUpgradeResponse(AsyncWebServerRequest *request, int code, const String& payload = "") {
auto *response = request->beginResponseStream("text/plain", 256);
response->addHeader("Connection", "close");
response->addHeader("X-XSS-Protection", "1; mode=block");
response->addHeader("X-Content-Type-Options", "nosniff");
response->addHeader("X-Frame-Options", "deny");
response->setCode(code);
if (payload.length()) {
response->printf("%s", payload.c_str());
} else {
if (!Update.hasError()) {
response->print("OK");
} else {
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0)
Update.printError(reinterpret_cast<Stream&>(response));
#else
Update.printError(*response);
#endif
}
}
request->send(response);
}
void _onUpgradeStatusSet(AsyncWebServerRequest *request, int code, const String& payload = "") {
_onUpgradeResponse(request, code, payload);
request->_tempObject = malloc(sizeof(bool));
}
void _onUpgrade(AsyncWebServerRequest *request) {
webLog(request);
if (!webAuthenticate(request)) {
return request->requestAuthentication(getSetting("hostname").c_str());
}
if (request->_tempObject) {
return;
}
_onUpgradeResponse(request, 200);
#endif // WEB_EMBEDDED == 1
}
void _onUpgradeFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!webAuthenticate(request)) {
return request->requestAuthentication(getSetting("hostname").c_str());
}
// We set this after we are done with the request
// It is still possible to re-enter this callback even after connection is already closed
// 1.14.2: TODO: see https://github.com/me-no-dev/ESPAsyncWebServer/pull/660
// remote close or request sending some data before finishing parsing of the body will leak 1460 bytes
// waiting a bit for upstream. fork and point to the fixed version if not resolved before 1.14.2
if (request->_tempObject) {
return;
}
if (!index) {
// TODO: stop network activity completely when handling Update through ArduinoOTA or `ota` command?
if (Update.isRunning()) {
_onUpgradeStatusSet(request, 400, F("ERROR: Upgrade in progress"));
return;
}
// Check that header is correct and there is more data before anything is written to the flash
if (final || !len) {
_onUpgradeStatusSet(request, 400, F("ERROR: Invalid request"));
return;
}
if (!otaVerifyHeader(data, len)) {
_onUpgradeStatusSet(request, 400, F("ERROR: No magic byte / invalid flash config"));
return;
}
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
Update.runAsync(true);
// Note: cannot use request->contentLength() for multipart/form-data
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
_onUpgradeStatusSet(request, 500);
eepromRotate(true);
return;
}
}
if (request->_tempObject) {
return;
}
// Any error will cancel the update, but request may still be alive
if (!Update.isRunning()) {
return;
}
if (Update.write(data, len) != len) {
_onUpgradeStatusSet(request, 500);
Update.end();
eepromRotate(true);
return;
}
if (final) {
otaFinalize(index + len, CUSTOM_RESET_UPGRADE, true);
} else {
otaProgress(index + len);
}
}
#endif // WEB_SSL_ENABLED
bool _onAPModeRequest(AsyncWebServerRequest *request) {
@ -516,14 +394,7 @@ void webSetup() {
_server->on("/index.html", HTTP_GET, _onHome);
#endif
// Other entry points
_server->on("/reset", HTTP_GET, _onReset);
_server->on("/config", HTTP_GET, _onGetConfig);
_server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigFile);
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeFile);
_server->on("/discover", HTTP_GET, _onDiscover);
// Serve static files
// Serve static files (not supported, yet)
#if SPIFFS_SUPPORT
_server->serveStatic("/", SPIFFS, "/")
.setLastModified(_last_modified)
@ -533,8 +404,12 @@ void webSetup() {
});
#endif
_server->on("/reset", HTTP_GET, _onReset);
_server->on("/config", HTTP_GET, _onGetConfig);
_server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigFile);
_server->on("/discover", HTTP_GET, _onDiscover);
// Handle other requests, including 404
// Handle every other request, including 404
_server->onRequestBody(_onBody);
_server->onNotFound(_onRequest);


+ 1
- 1
code/html/index.html View File

@ -707,7 +707,7 @@
</div>
</div>
<div class="pure-g">
<div class="pure-g module module-ota">
<label class="pure-u-1 pure-u-lg-1-4">Upgrade</label>
<input class="pure-u-1-2 pure-u-lg-1-2" name="filename" type="text" readonly />
<div class="pure-u-1-4 pure-u-lg-1-8"><button type="button" class="pure-button button-upgrade-browse pure-u-23-24">Browse</button></div>


Loading…
Cancel
Save