crashmode

This commit is contained in:
proddy
2026-10-07 23:00:14 +02:00
parent 6411b6ffbd
commit 965d93661a
19 changed files with 600 additions and 273 deletions
+30 -18
View File
@@ -8,7 +8,8 @@ UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager *
: _securityManager(securityManager)
, _is_firmware(false)
, _is_filesystem(false)
, _is_recovery(false) {
, _is_recovery(false)
, _response_sent(false) {
server->on(
UPLOAD_FILE_PATH,
HTTP_POST,
@@ -31,9 +32,10 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
// check details of the file, to see if its a valid bin or json file
const std::size_t filesize = request->contentLength();
_is_firmware = false;
_is_filesystem = false;
_is_recovery = false;
_is_firmware = false;
_is_filesystem = false;
_is_recovery = false;
_response_sent = false;
switch (OtaUpdater::classify(filename, filesize, emsesp::System::MIN_FIRMWARE_SIZE)) {
case OtaUpdater::FileType::FILESYSTEM:
@@ -96,6 +98,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
}
request->onDisconnect([this] { handleDisconnect(); }); // success, let's make sure we end the update if the client hangs up
} else {
emsesp::EMSESP::logger().err("OTA begin failed: %s", _ota.errorString());
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
return;
}
@@ -147,6 +150,19 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
}
void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
if (request->_tempObject) {
const int code = static_cast<int>(reinterpret_cast<intptr_t>(request->_tempObject));
if (!_response_sent && code >= 400) {
request->send(request->beginResponse(code));
_response_sent = true;
}
_is_firmware = false;
_is_filesystem = false;
_is_recovery = false;
emsesp::EMSESP::system_.systemStatus(emsesp::SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
return;
}
emsesp::EMSESP::logger().info("Upload successful");
// did we just complete uploading a json file?
@@ -217,26 +233,25 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
}
void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
emsesp::EMSESP::logger().info("Upload error: %d", code);
emsesp::EMSESP::system_.uart_init(); // re-enable UART
// if we have had an error already, do nothing
if (request->_tempObject) {
return;
}
request->_tempObject = reinterpret_cast<void *>(static_cast<intptr_t>(code)); // stop later chunks from retrying the write
// send the error code to the client and record the error code in the temp object
AsyncWebServerResponse * response = request->beginResponse(code);
request->send(response);
emsesp::EMSESP::logger().info("Upload error: %d", code);
emsesp::EMSESP::system_.uart_init(); // re-enable UART
_ota.abort();
// check for invalid extension and immediately kill the connection, which will throw an error
// that is caught by the web code. Unfortunately the http error code is not sent to the client on fast network connections
// 406: the client sent a file we won't accept. Close now so the browser fails fast.
// Other codes wait for uploadComplete() to send, so we don't destroy the request mid-stream.
if (code == 406) {
request->send(request->beginResponse(code));
_response_sent = true;
request->client()->close();
_is_firmware = false;
_is_filesystem = false;
_is_recovery = false;
_ota.abort();
}
// if we aborted a filesystem upload, remount LittleFS so the device keeps working
@@ -249,13 +264,10 @@ void UploadFileService::handleDisconnect() {
emsesp::EMSESP::logger().info("Upload finished");
emsesp::EMSESP::system_.uart_init(); // re-enable UART
#ifdef EMSESP_HAS_RECOVERY
if (_is_recovery) {
// the client hung up before the upload completed
_ota.abort();
_ota.abort();
if (_is_recovery || _is_firmware || _is_filesystem) {
emsesp::EMSESP::system_.systemStatus(emsesp::SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
}
#endif
_is_firmware = false;
_is_filesystem = false;
+1
View File
@@ -22,6 +22,7 @@ class UploadFileService {
bool _is_firmware;
bool _is_filesystem;
bool _is_recovery; // firmware for the factory partition, which doesn't change the boot partition
bool _response_sent;
OtaUpdater _ota;
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
+57 -8
View File
@@ -573,8 +573,12 @@ void System::recovery_installed() {
if (factory != nullptr) {
char c[20];
snprintf(c, sizeof(c), "d_%s", factory->label);
EMSESP::nvs_.remove(factory->label);
EMSESP::nvs_.remove(c);
if (EMSESP::nvs_.isKey(factory->label)) {
EMSESP::nvs_.remove(factory->label);
}
if (EMSESP::nvs_.isKey(c)) {
EMSESP::nvs_.remove(c);
}
partition_info_.erase(factory->label); // on the AsyncTCP task, same as the web handlers reading it
}
LOG_INFO("Recovery firmware v%s installed", recovery_version().c_str());
@@ -639,6 +643,12 @@ bool System::set_partition([[maybe_unused]] const char * partitionname) {
return false;
}
#ifdef EMSESP_HAS_RECOVERY
if (partition->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) {
EMSESP::nvs_.putString(EMSESP_NVS_RECOVERY_REASON, "request");
}
#endif
// initiate the restart
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED);
return true;
@@ -1323,6 +1333,16 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(" [total %d]", available.size());
// List all partitions and their version info
shell.println(" Partitions:");
#ifndef EMSESP_STANDALONE
const esp_partition_t * running = esp_ota_get_running_partition();
const esp_partition_t * boot = esp_ota_get_boot_partition();
const char * running_label = running ? running->label : "";
const char * boot_label = boot ? boot->label : "";
#else
const char * running_label = "";
const char * boot_label = "";
#endif
bool boot_listed = false;
for (const auto & partition : partition_info_) {
if (partition.second.version.empty()) {
continue; // no version, empty string
@@ -1334,13 +1354,42 @@ void System::show_system(uuid::console::Shell & shell) {
strftime(time_string, sizeof(time_string), "%FT%T", localtime(&d));
installed = std::string(", installed on ") + time_string;
}
shell.printfln(" %s: v%s (%d KB%s) %s",
partition.first.c_str(),
partition.second.version.c_str(),
partition.second.size,
installed.c_str(),
(strcmp(esp_ota_get_running_partition()->label, partition.first.c_str()) == 0) ? "** active **" : "");
const bool is_running = strcmp(running_label, partition.first.c_str()) == 0;
const bool is_boot = strcmp(boot_label, partition.first.c_str()) == 0;
if (is_boot) {
boot_listed = true;
}
const char * mark = is_running && is_boot ? "** active, boot **" : is_running ? "** active **" : is_boot ? "** boot **" : "";
shell.printfln(" %s: v%s (%d KB%s) %s", partition.first.c_str(), partition.second.version.c_str(), partition.second.size, installed.c_str(), mark);
}
#ifndef EMSESP_STANDALONE
#ifdef EMSESP_HAS_RECOVERY
// 16MB boards: factory ("boot") holds the recovery app and is omitted from partition_info_
{
const esp_partition_t * factory = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
esp_app_desc_t desc;
if (OtaUpdater::isRecoveryPartition(factory, &desc)) {
const bool is_running = running && factory == running;
const bool is_boot = boot && factory == boot;
if (is_boot) {
boot_listed = true;
}
int size_kb = 0;
esp_image_metadata_t meta = {};
esp_partition_pos_t part_pos = {.offset = factory->address, .size = factory->size};
if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &part_pos, &meta) == ESP_OK) {
size_kb = static_cast<int>(meta.image_len / 1024);
}
const char * mark = is_running && is_boot ? "** active, boot **" : is_running ? "** active **" : is_boot ? "** boot **" : "";
shell.printfln(" %s: Recovery v%s (%d KB) %s", factory->label, desc.version, size_kb, mark);
}
}
#endif
// recovery/factory is omitted from partition_info_, still show it if it's the boot target
if (!boot_listed && boot_label[0] != '\0') {
shell.printfln(" %s: ** boot **", boot_label);
}
#endif
// List all NVS values
#ifndef EMSESP_STANDALONE
shell.println(" NVS values:");
+2 -1
View File
@@ -375,7 +375,8 @@ class System {
static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2
static constexpr uint8_t HEALTHCHECK_RESET = (1 << 7); // 128
// smallest .bin accepted as a firmware image, for both URL and web uploads - 1.6 MB
// smallest .bin accepted as a main firmware image, for both URL and web uploads - 1.6 MB
// recovery firmware uses OtaUpdater::MIN_RECOVERY_FIRMWARE_SIZE when the filename contains "recovery"
static constexpr size_t MIN_FIRMWARE_SIZE = 1677721;
private:
+1 -1
View File
@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.9.0-dev.16"
#define EMSESP_APP_VERSION "3.9.0-dev.17"
+3 -4
View File
@@ -134,13 +134,12 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
root["temperature"] = (int)Helpers::transformNumFloat(EMSESP::system_.temperature(), 0, EMSESP::system_.fahrenheit() ? 2 : 0); // only 2 decimal places
#endif
// check for a factory partition first
// factory partition is only on 16MB boards; recovery_version is empty until a recovery .bin is installed
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
root["has_loader"] = partition != NULL && partition != esp_ota_get_running_partition();
if (partition != NULL) {
root["recovery_version"] = EMSESP::system_.recovery_version(); // empty if not installed
root["recovery_version"] = EMSESP::system_.recovery_version();
}
partition = esp_ota_get_next_update_partition(nullptr);
partition = esp_ota_get_next_update_partition(nullptr);
if (partition) {
uint64_t buffer;
esp_partition_read(partition, 0, &buffer, 8);