mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-03 15:04:17 +01:00
ota: additional code for when the web server support is disabled
Using Core's ESP8266WebServer + some additional handlers. Includes barebones page, without authorization. Allows to explicitly set OTA_WEB_SUPPORT=1 with the ESPURNA_CORE and still get a web page. Around the same size as ArduinoOTA + MDNS, so it might also be a good suggestion to disable those when web support is enabled.
This commit is contained in:
@@ -842,7 +842,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef OTA_WEB_SUPPORT
|
||||
#define OTA_WEB_SUPPORT 1 // Support `/upgrade` endpoint and WebUI OTA handler
|
||||
#define OTA_WEB_SUPPORT WEB_SUPPORT // 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"
|
||||
|
||||
@@ -81,7 +81,8 @@
|
||||
//#define BUTTON_SUPPORT 0 // don't need / have buttons
|
||||
//#define LED_SUPPORT 0 // don't need wifi indicator
|
||||
//#define RELAY_SUPPORT 0 // don't need to preserve pin state between resets
|
||||
//#define OTA_ARDUINOOTA_SUPPORT 0 // when only using `ota` command
|
||||
//#define OTA_ARDUINOOTA_SUPPORT 0 // when only using the `ota` command
|
||||
//#define OTA_WEB_SUPPORT 0 //
|
||||
//#define MDNS_SERVER_SUPPORT 0 //
|
||||
//#define TELNET_SUPPORT 0 // when only using espota.py
|
||||
//#define TERMINAL_SUPPORT 0 //
|
||||
|
||||
@@ -36,6 +36,10 @@ bool otaFinalize(size_t size, CustomResetReason reason, bool evenIfRemaining) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool otaFinalize(size_t size, CustomResetReason reason) {
|
||||
return otaFinalize(size, reason, false);
|
||||
}
|
||||
|
||||
// Helper methods from UpdaterClass that need to be called manually for async mode,
|
||||
// because we are not using Stream interface to feed it data.
|
||||
bool otaVerifyHeader(uint8_t* data, size_t len) {
|
||||
@@ -82,6 +86,11 @@ void otaProgress(size_t bytes, size_t each) {
|
||||
}
|
||||
}
|
||||
|
||||
void otaProgress(size_t bytes) {
|
||||
constexpr size_t Each { 8192 };
|
||||
otaProgress(bytes, Each);
|
||||
}
|
||||
|
||||
void otaSetup() {
|
||||
// Some magic to allow seamless Tasmota OTA upgrades
|
||||
// - inject dummy data sequence that is expected to hold current version info
|
||||
@@ -117,7 +126,10 @@ void otaSetup() {
|
||||
}
|
||||
|
||||
#if OTA_ARDUINOOTA_SUPPORT
|
||||
arduinoOtaSetup();
|
||||
otaArduinoSetup();
|
||||
#endif
|
||||
#if !WEB_SUPPORT && OTA_WEB_SUPPORT
|
||||
otaWebSetup();
|
||||
#endif
|
||||
#if OTA_CLIENT != OTA_CLIENT_NONE
|
||||
otaClientSetup();
|
||||
|
||||
@@ -8,36 +8,22 @@ OTA MODULE
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if OTA_WEB_SUPPORT
|
||||
// Main entrypoint for basic OTA methods
|
||||
// (like clients, arduinoota and basic web)
|
||||
void otaSetup();
|
||||
|
||||
void otaWebSetup();
|
||||
|
||||
#endif // OTA_WEB_SUPPORT == 1
|
||||
|
||||
#if OTA_ARDUINOOTA_SUPPORT
|
||||
|
||||
void arduinoOtaSetup();
|
||||
|
||||
#endif // OTA_ARDUINOOTA_SUPPORT == 1
|
||||
|
||||
#if OTA_CLIENT == OTA_CLIENT_ASYNCTCP
|
||||
|
||||
void otaArduinoSetup();
|
||||
void otaClientSetup();
|
||||
|
||||
#endif // OTA_CLIENT == OTA_CLIENT_ASYNCTCP
|
||||
|
||||
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
|
||||
|
||||
void otaClientSetup();
|
||||
|
||||
#endif // OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
|
||||
|
||||
void otaSetup();
|
||||
void otaPrintError();
|
||||
bool otaFinalize(size_t size, CustomResetReason reason, bool evenIfRemaining = false);
|
||||
|
||||
// Helper methods from UpdaterClass that need to be called manually for async mode,
|
||||
// because we are not using Stream interface to feed it data.
|
||||
bool otaVerifyHeader(uint8_t* data, size_t len);
|
||||
|
||||
void otaProgress(size_t bytes, size_t each = 8192u);
|
||||
void otaProgress(size_t bytes, size_t each);
|
||||
void otaProgress(size_t bytes);
|
||||
|
||||
void otaPrintError();
|
||||
bool otaFinalize(size_t size, CustomResetReason reason, bool evenIfRemaining);
|
||||
bool otaFinalize(size_t size, CustomResetReason reason);
|
||||
|
||||
@@ -121,7 +121,7 @@ void setup() {
|
||||
} // namespace arduino
|
||||
} // namespace ota
|
||||
|
||||
void arduinoOtaSetup() {
|
||||
void otaArduinoSetup() {
|
||||
ota::arduino::setup();
|
||||
}
|
||||
|
||||
|
||||
197
code/espurna/ota_basicweb.cpp
Normal file
197
code/espurna/ota_basicweb.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
|
||||
Part of the OTA MODULE
|
||||
|
||||
Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#if !WEB_SUPPORT && OTA_WEB_SUPPORT
|
||||
|
||||
// When there's no WEB_SUPPORT, enable a basic server with a form upload and /upgrade endpoint.
|
||||
// Based on the async-web-server code and the ESP8266HTTPUpdateServer.h bundled with the Core
|
||||
// effectively repeats what is done in the async variant, but does not involve async-web-server
|
||||
// (...but, still suffers from a similar std::function API, forcing to self-reference the server object and manage various global state & result objects)
|
||||
//
|
||||
|
||||
#include "ota.h"
|
||||
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
namespace ota {
|
||||
namespace basic_web {
|
||||
namespace {
|
||||
|
||||
const char HomePage[] PROGMEM = R"(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<form method="POST" action="/upgrade" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>Firmware upgrade</legend>
|
||||
<div>
|
||||
<input type="file" name="filename">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
const char UpgradePageHead[] PROGMEM = R"(
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="5; url=./">
|
||||
</head>
|
||||
)";
|
||||
|
||||
namespace handlers {
|
||||
namespace internal {
|
||||
|
||||
struct Result {
|
||||
template <typename T>
|
||||
void set(int code, T&& value) {
|
||||
_code = code;
|
||||
_output = std::forward<T>(value);
|
||||
}
|
||||
|
||||
void setFromUpdate() {
|
||||
if (Update.hasError()) {
|
||||
StreamString stream;
|
||||
Update.printError(stream);
|
||||
set(500, stream);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Server>
|
||||
void send(Server& server) {
|
||||
String output;
|
||||
output.reserve(_output.length() + sizeof(UpgradePageHead) + 16);
|
||||
|
||||
output.concat(UpgradePageHead, sizeof(UpgradePageHead) - 1);
|
||||
output += F("<code>");
|
||||
output += _output;
|
||||
output += F("</code>");
|
||||
|
||||
server.send(_code, PSTR("text/html"), output);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_code = 200;
|
||||
_output = "";
|
||||
}
|
||||
|
||||
private:
|
||||
int _code { 200 };
|
||||
String _output;
|
||||
};
|
||||
|
||||
Result result;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename Server>
|
||||
void result(Server& server) {
|
||||
internal::result.send(server);
|
||||
}
|
||||
|
||||
template <typename Server>
|
||||
void upload(Server& server) {
|
||||
auto& upload = server.upload();
|
||||
|
||||
switch (upload.status) {
|
||||
|
||||
case UPLOAD_FILE_START: {
|
||||
if (Update.isRunning()) {
|
||||
server.client().stop();
|
||||
return;
|
||||
}
|
||||
|
||||
internal::result.reset();
|
||||
const size_t Available { (ESP.getFreeSketchSpace() - 0x1000ul) & 0xfffff000ul };
|
||||
if (!Update.begin(Available, U_FLASH)) {
|
||||
server.client().stop();
|
||||
internal::result.set(500, F("Not enough available space"));
|
||||
eepromRotate(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UPLOAD_FILE_END:
|
||||
if (otaFinalize(upload.totalSize, CustomResetReason::Ota, true)) {
|
||||
internal::result.set(200, F("Rebooting..."));
|
||||
} else {
|
||||
internal::result.setFromUpdate();
|
||||
}
|
||||
break;
|
||||
|
||||
case UPLOAD_FILE_WRITE:
|
||||
if (!Update.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
internal::result.set(500, F("Error during write()"));
|
||||
server.client().stop();
|
||||
Update.end();
|
||||
eepromRotate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UPLOAD_FILE_ABORTED:
|
||||
internal::result.set(500, F("Upload aborted"));
|
||||
Update.end();
|
||||
eepromRotate(true);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Server>
|
||||
void home(Server& server) {
|
||||
server.send_P(200, PSTR("text/html"), HomePage);
|
||||
}
|
||||
|
||||
} // namespace handlers
|
||||
|
||||
template <typename Server>
|
||||
void setup(Server& server) {
|
||||
server.on("/", HTTP_GET, [&]() {
|
||||
handlers::home(server);
|
||||
});
|
||||
|
||||
server.on("/upgrade", HTTP_POST,
|
||||
[&]() {
|
||||
handlers::result(server);
|
||||
},
|
||||
[&]() {
|
||||
handlers::upload(server);
|
||||
}
|
||||
);
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace basic_web
|
||||
} // namespace ota
|
||||
|
||||
void otaWebSetup() {
|
||||
static ESP8266WebServer server(WEB_PORT);
|
||||
ota::basic_web::setup(server);
|
||||
|
||||
::espurnaRegisterLoop([]() {
|
||||
server.handleClient();
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user