2 Commits

Author SHA1 Message Date
Yurii
40dc863530 refactor: improved OTA upgrade
https://github.com/ESP32Async/ESPAsyncWebServer/pull/329
2025-11-03 03:19:50 +03:00
Yurii
a40413aeac style: formatting 2025-11-02 23:34:46 +03:00
4 changed files with 72 additions and 61 deletions

View File

@@ -22,7 +22,8 @@ public:
UpgradeType type; UpgradeType type;
UpgradeStatus status; UpgradeStatus status;
String error; String error;
unsigned int written = 0; size_t progress = 0;
size_t size = 0;
} UpgradeResult; } UpgradeResult;
typedef std::function<bool(AsyncWebServerRequest *request, UpgradeType)> BeforeUpgradeCallback; typedef std::function<bool(AsyncWebServerRequest *request, UpgradeType)> BeforeUpgradeCallback;
@@ -57,34 +58,47 @@ public:
this->firmwareResult.status = UpgradeStatus::NONE; this->firmwareResult.status = UpgradeStatus::NONE;
this->firmwareResult.error.clear(); this->firmwareResult.error.clear();
this->firmwareResult.written = 0;
this->filesystemResult.status = UpgradeStatus::NONE; this->filesystemResult.status = UpgradeStatus::NONE;
this->filesystemResult.error.clear(); this->filesystemResult.error.clear();
this->filesystemResult.written = 0;
} }
void handleUpload(AsyncWebServerRequest *request, const String &fileName, size_t index, uint8_t *data, size_t dataLength, bool isFinal) override final { void handleUpload(AsyncWebServerRequest *request, const String &fileName, size_t index, uint8_t *data, size_t dataLength, bool isFinal) override final {
UpgradeResult* result = nullptr; UpgradeResult* result = nullptr;
unsigned int fileSize = 0;
const auto& fwName = request->hasParam("fw[name]", true) ? request->getParam("fw[name]", true)->value() : String(); if (!request->hasParam(asyncsrv::T_name, true, true)) {
const auto& fsName = request->hasParam("fs[name]", true) ? request->getParam("fs[name]", true)->value() : String(); // Missing content-disposition 'name' parameter
return;
if (fileName.equals(fwName)) {
result = &this->firmwareResult;
if (request->hasParam("fw[size]", true)) {
fileSize = request->getParam("fw[size]", true)->value().toInt();
}
} else if (fileName.equals(fsName)) {
result = &this->filesystemResult;
if (request->hasParam("fs[size]", true)) {
fileSize = request->getParam("fs[size]", true)->value().toInt();
}
} }
if (result == nullptr || result->status != UpgradeStatus::NONE) { const auto& pName = request->getParam(asyncsrv::T_name, true, true)->value();
if (pName.equals("fw")) {
result = &this->firmwareResult;
if (!index) {
result->progress = 0;
result->size = request->hasParam("fw_size", true)
? request->getParam("fw_size", true)->value().toInt()
: 0;
}
} else if (pName.equals("fs")) {
result = &this->filesystemResult;
if (!index) {
result->progress = 0;
result->size = request->hasParam("fs_size", true)
? request->getParam("fs_size", true)->value().toInt()
: 0;
}
} else {
// Unknown parameter name
return;
}
// check result status
if (result->status != UpgradeStatus::NONE) {
return; return;
} }
@@ -93,18 +107,19 @@ public:
return; return;
} }
if (!fileName.length() || !fileSize) { if (!fileName.length()) {
result->status = UpgradeStatus::NO_FILE; result->status = UpgradeStatus::NO_FILE;
return; return;
} }
if (index == 0) { if (!index) {
// reset // reset
if (Update.isRunning()) { if (Update.isRunning()) {
Update.end(false); Update.end(false);
Update.clearError(); Update.clearError();
} }
// try begin
bool begin = false; bool begin = false;
if (result->type == UpgradeType::FIRMWARE) { if (result->type == UpgradeType::FIRMWARE) {
begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH); begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH);
@@ -117,54 +132,52 @@ public:
result->status = UpgradeStatus::ERROR_ON_START; result->status = UpgradeStatus::ERROR_ON_START;
result->error = Update.errorString(); result->error = Update.errorString();
Log.serrorln(FPSTR(L_PORTAL_OTA), F("File '%s', on start: %s"), fileName.c_str(), result->error.c_str()); Log.serrorln(FPSTR(L_PORTAL_OTA), "File '%s', on start: %s", fileName.c_str(), result->error.c_str());
return; return;
} }
Log.sinfoln(FPSTR(L_PORTAL_OTA), F("File '%s', started"), fileName.c_str()); Log.sinfoln(FPSTR(L_PORTAL_OTA), "File '%s', started", fileName.c_str());
} }
if (dataLength) { if (dataLength) {
result->written += dataLength;
if (Update.write(data, dataLength) != dataLength) { if (Update.write(data, dataLength) != dataLength) {
Update.end(false); Update.end(false);
result->status = UpgradeStatus::ERROR_ON_WRITE; result->status = UpgradeStatus::ERROR_ON_WRITE;
result->error = Update.errorString(); result->error = Update.errorString();
Log.serrorln( Log.serrorln(
FPSTR(L_PORTAL_OTA), FPSTR(L_PORTAL_OTA), "File '%s', on write %d bytes, %d of %d bytes",
F("File '%s', on write %d bytes, %d of %d bytes"),
fileName.c_str(), fileName.c_str(),
dataLength, dataLength,
result->written, result->progress + dataLength,
fileSize result->size
); );
return; return;
} }
result->progress += dataLength;
Log.sinfoln( Log.sinfoln(
FPSTR(L_PORTAL_OTA), FPSTR(L_PORTAL_OTA), "File '%s', write %d bytes, %d of %d bytes",
F("File '%s', write %d bytes, %d of %d bytes"),
fileName.c_str(), fileName.c_str(),
dataLength, dataLength,
result->written, result->progress,
fileSize result->size
); );
} }
if (result->written > fileSize || (isFinal && result->written < fileSize)) { if (result->size > 0) {
Update.end(false); if (result->progress > result->size || (isFinal && result->progress < result->size)) {
result->status = UpgradeStatus::SIZE_MISMATCH; Update.end(false);
result->status = UpgradeStatus::SIZE_MISMATCH;
Log.serrorln( Log.serrorln(
FPSTR(L_PORTAL_OTA), FPSTR(L_PORTAL_OTA), "File '%s', size mismatch: %d of %d bytes",
F("File '%s', size mismatch: %d of %d bytes"), fileName.c_str(),
fileName.c_str(), result->progress,
result->written, result->size
fileSize );
); return;
return; }
} }
if (isFinal) { if (isFinal) {
@@ -172,12 +185,12 @@ public:
result->status = UpgradeStatus::ERROR_ON_FINISH; result->status = UpgradeStatus::ERROR_ON_FINISH;
result->error = Update.errorString(); result->error = Update.errorString();
Log.serrorln(FPSTR(L_PORTAL_OTA), F("File '%s', on finish: %s"), fileName.c_str(), result->error); Log.serrorln(FPSTR(L_PORTAL_OTA), "File '%s', on finish: %s", fileName.c_str(), result->error);
return; return;
} }
result->status = UpgradeStatus::SUCCESS; result->status = UpgradeStatus::SUCCESS;
Log.sinfoln(FPSTR(L_PORTAL_OTA), F("File '%s': finish"), fileName.c_str()); Log.sinfoln(FPSTR(L_PORTAL_OTA), "File '%s': finish", fileName.c_str());
} }
} }

View File

@@ -8,7 +8,7 @@ version = 1.5.7-passiveble
framework = arduino framework = arduino
lib_deps = ESP32Async/AsyncTCP lib_deps = ESP32Async/AsyncTCP
;ESP32Async/ESPAsyncWebServer ;ESP32Async/ESPAsyncWebServer
https://github.com/ESP32Async/ESPAsyncWebServer#main https://github.com/ESP32Async/ESPAsyncWebServer#upload
bblanchon/ArduinoJson@^7.4.2 bblanchon/ArduinoJson@^7.4.2
;ihormelnyk/OpenTherm Library@^1.1.5 ;ihormelnyk/OpenTherm Library@^1.1.5
https://github.com/Laxilef/opentherm_library#esp32_timer https://github.com/Laxilef/opentherm_library#esp32_timer

View File

@@ -575,10 +575,10 @@ protected:
if (!NimBLEDevice::isInitialized() && millis() > 5000) { if (!NimBLEDevice::isInitialized() && millis() > 5000) {
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Initialized")); Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Initialized"));
NimBLEDevice::init(""); NimBLEDevice::init("");
#if defined(ESP_PWR_LVL_P20) #ifdef ESP_PWR_LVL_P20
NimBLEDevice::setPower(ESP_PWR_LVL_P20); NimBLEDevice::setPower(ESP_PWR_LVL_P20);
#elif defined(ESP_PWR_LVL_P9) #elifdef ESP_PWR_LVL_P9
NimBLEDevice::setPower(ESP_PWR_LVL_P9); NimBLEDevice::setPower(ESP_PWR_LVL_P9);
#endif #endif
} }

View File

@@ -65,7 +65,7 @@
<label> <label>
<span data-i18n>upgrade.fw</span>: <span data-i18n>upgrade.fw</span>:
<div class="grid"> <div class="grid">
<input type="file" name="fw[file]" accept=".bin"> <input type="file" name="fw" accept=".bin">
<button type="button" class="fwResult hidden" disabled></button> <button type="button" class="fwResult hidden" disabled></button>
</div> </div>
</label> </label>
@@ -73,7 +73,7 @@
<label> <label>
<span data-i18n>upgrade.fs</span>: <span data-i18n>upgrade.fs</span>:
<div class="grid"> <div class="grid">
<input type="file" name="fs[file]" accept=".bin"> <input type="file" name="fs" accept=".bin">
<button type="button" class="fsResult hidden" disabled></button> <button type="button" class="fsResult hidden" disabled></button>
</div> </div>
</label> </label>
@@ -149,18 +149,16 @@
try { try {
let fd = new FormData(); let fd = new FormData();
const fw = upgradeForm.querySelector("[name='fw[file]']").files; const fw = upgradeForm.querySelector("[name='fw']").files;
if (fw.length > 0) { if (fw.length > 0) {
fd.append("fw[name]", fw[0].name); fd.append("fw_size", fw[0].size);
fd.append("fw[size]", fw[0].size); fd.append("fw", fw[0]);
fd.append("fw[file]", fw[0]);
} }
const fs = upgradeForm.querySelector("[name='fs[file]']").files; const fs = upgradeForm.querySelector("[name='fs']").files;
if (fs.length > 0) { if (fs.length > 0) {
fd.append("fs[name]", fs[0].name); fd.append("fs_size", fs[0].size);
fd.append("fs[size]", fs[0].size); fd.append("fs", fs[0]);
fd.append("fs[file]", fs[0]);
} }
let response = await fetch(upgradeForm.action, { let response = await fetch(upgradeForm.action, {