diff --git a/interface/src/app/status/SystemMonitor.tsx b/interface/src/app/status/SystemMonitor.tsx
index b6fa12d1f..2a9e74343 100644
--- a/interface/src/app/status/SystemMonitor.tsx
+++ b/interface/src/app/status/SystemMonitor.tsx
@@ -145,9 +145,21 @@ const SystemMonitor = () => {
{LL.PLEASE_WAIT()}…
{isUploading && (
-
-
-
+ <>
+
+
+
+
+ }
+ variant="outlined"
+ color="secondary"
+ onClick={onCancel}
+ >
+ {LL.CANCEL()}
+
+ >
)}
>
)}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index c26628fdf..df63c2930 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -3149,6 +3149,18 @@ bool System::uploadFirmwareURL(const char * url) {
int last_pct = -1;
while (total_read < (size_t)firmware_size) {
+ // a cancel is signalled by the WebUI dropping the status below UPLOADING (back to NORMAL)
+ // via the systemStatus action, which runs on the AsyncTCP task while we're blocked here
+ if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
+ LOG_WARNING("Firmware upload cancelled at %u of %d bytes", (unsigned)total_read, firmware_size);
+ Update.abort(); // release the OTA partition handle so a later attempt can start cleanly
+ ssl_client.stop(); // drop the connection
+ saved_url.clear(); // prevent it from downloading again
+ EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
+ Shell::loop_all(); // flush log buffers so the cancel message shows in the console
+ return true; // not an error - don't trigger the failure/reset path in emsesp.cpp
+ }
+
// wait for some data or for the connection to drop
uint32_t wait_start = millis();
while (!stream->available()) {
@@ -3158,10 +3170,18 @@ bool System::uploadFirmwareURL(const char * url) {
if (millis() - wait_start > READ_TIMEOUT_MS) {
break;
}
+ // also bail out promptly if a cancel arrives mid-stall
+ if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
+ break;
+ }
delay(1);
}
if (!stream->available()) {
+ // if the inner wait broke because of a cancel, loop back so the top-of-loop handler runs
+ if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
+ continue;
+ }
LOG_ERROR("Firmware upload failed - read stalled at %u of %d bytes", (unsigned)total_read, firmware_size);
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
return false;
diff --git a/src/core/system.h b/src/core/system.h
index 1aa9351ab..6c6f62e2c 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -358,7 +358,7 @@ class System {
static uint32_t heap_mem_;
static uint32_t min_free_mem_;
- uint8_t systemStatus_; // uses SYSTEM_STATUS enum
+ volatile uint8_t systemStatus_; // uses SYSTEM_STATUS enum - written from the AsyncTCP task (e.g. cancel) and read from the main loop during OTA
void set_partition_install_date();