/* Part of the OTA MODULE Copyright (C) 2019-2021 by Maxim Prokhorov */ #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 #include namespace ota { namespace basic_web { namespace { const char HomePage[] PROGMEM = R"(
Firmware upgrade
)"; const char UpgradePageHead[] PROGMEM = R"( )"; namespace handlers { namespace internal { struct Result { template void set(int code, T&& value) { _code = code; _output = std::forward(value); } void setFromUpdate() { if (Update.hasError()) { StreamString stream; Update.printError(stream); set(500, stream); } } template void send(Server& server) { String output; output.reserve(_output.length() + sizeof(UpgradePageHead) + 16); output.concat(UpgradePageHead, sizeof(UpgradePageHead) - 1); output += F(""); output += _output; output += F(""); server.send(_code, PSTR("text/html"), output); } void reset() { _code = 200; _output = ""; } private: int _code { 200 }; String _output; }; Result result; } // namespace internal template void result(Server& server) { internal::result.send(server); } template 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 void home(Server& server) { server.send_P(200, PSTR("text/html"), HomePage); } } // namespace handlers template 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 settings { uint16_t port() { constexpr uint16_t defaultPort { WEB_PORT }; return getSetting("webPort", defaultPort); } } // namespace settings } // namespace basic_web } // namespace ota void otaWebSetup() { static ESP8266WebServer server(ota::basic_web::settings::port()); ota::basic_web::setup(server); ::espurnaRegisterLoop([]() { server.handleClient(); }); } #endif