From 023ff29131436ba2a94503a3c2052c7c184a7e76 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Wed, 11 Sep 2024 20:27:40 +0200 Subject: [PATCH] Add single-click auto-update functionality --- data | 2 +- src/lib/ota.cpp | 77 +++++++++++++++++++++++++++++-------------- src/lib/ota.hpp | 10 ++++-- src/lib/shared.hpp | 7 ++-- src/lib/webserver.cpp | 24 +++----------- src/lib/webserver.hpp | 5 +-- 6 files changed, 72 insertions(+), 53 deletions(-) diff --git a/data b/data index 1c2d8dc..6c40b54 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 1c2d8dcdd0efd846f39b0f24977c982a96f16392 +Subproject commit 6c40b54273b7f7c7d6c2624d3c2a066435f27756 diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp index 92838db..3a391ca 100644 --- a/src/lib/ota.cpp +++ b/src/lib/ota.cpp @@ -4,6 +4,8 @@ TaskHandle_t taskOtaHandle = NULL; bool isOtaUpdating = false; QueueHandle_t otaQueue; + + void setupOTA() { if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED)) @@ -65,7 +67,7 @@ void onOTAStart() vTaskSuspend(workerTaskHandle); vTaskSuspend(taskScreenRotateTaskHandle); - vTaskSuspend(ledTaskHandle); +// vTaskSuspend(ledTaskHandle); vTaskSuspend(buttonTaskHandle); // stopWebServer(); @@ -81,7 +83,18 @@ void handleOTATask(void *parameter) { if (xQueueReceive(otaQueue, &msg, 0) == pdTRUE) { - int result = downloadUpdateHandler(msg.updateType); + if (msg.updateType == UPDATE_ALL) { + int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI); + int resultFw = downloadUpdateHandler(UPDATE_FIRMWARE); + + if (resultWebUi == 0 && resultFw == 0) { + ESP.restart(); + } else { + queueLedEffect(LED_FLASH_ERROR); + vTaskDelay(pdMS_TO_TICKS(3000)); + ESP.restart(); + } + } } ArduinoOTA.handle(); // Allow OTA updates to occur @@ -89,7 +102,7 @@ void handleOTATask(void *parameter) } } -String getLatestRelease(const String &fileToDownload) +ReleaseInfo getLatestRelease(const String &fileToDownload) { String releaseUrl = "https://api.github.com/repos/btclock/btclock_v3/releases/latest"; WiFiClientSecure client; @@ -100,7 +113,7 @@ String getLatestRelease(const String &fileToDownload) int httpCode = http.GET(); - String downloadUrl = ""; + ReleaseInfo info = {"", ""}; if (httpCode > 0) { @@ -113,15 +126,26 @@ String getLatestRelease(const String &fileToDownload) for (JsonObject asset : assets) { - if (asset["name"] == fileToDownload) + String assetName = asset["name"].as(); + if (assetName == fileToDownload) + { + info.fileUrl = asset["browser_download_url"].as(); + } + else if (assetName == fileToDownload + ".sha256") + { + info.checksumUrl = asset["browser_download_url"].as(); + } + + if (!info.fileUrl.isEmpty() && !info.checksumUrl.isEmpty()) { - downloadUrl = asset["browser_download_url"].as(); break; } } - Serial.printf("Latest release URL: %s\r\n", downloadUrl.c_str()); + Serial.printf("Latest release URL: %s\r\n", info.fileUrl.c_str()); + Serial.printf("Checksum URL: %s\r\n", info.checksumUrl.c_str()); } - return downloadUrl; + http.end(); + return info; } int downloadUpdateHandler(char updateType) @@ -131,7 +155,7 @@ int downloadUpdateHandler(char updateType) HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - String latestRelease = ""; + ReleaseInfo latestRelease; switch (updateType) { @@ -143,25 +167,22 @@ int downloadUpdateHandler(char updateType) case UPDATE_WEBUI: { latestRelease = getLatestRelease("littlefs.bin"); - updateWebUi(latestRelease, U_SPIFFS); - return 0; + // updateWebUi(latestRelease.fileUrl, U_SPIFFS); + // return 0; } break; } - if (latestRelease.isEmpty()) - { - return 503; - } + // First, download the expected SHA256 - String expectedSHA256 = downloadSHA256(getFirmwareFilename()); + String expectedSHA256 = downloadSHA256(latestRelease.checksumUrl); if (expectedSHA256.isEmpty()) { Serial.println("Failed to get SHA256 checksum. Aborting update."); return false; } - http.begin(client, latestRelease); + http.begin(client, latestRelease.fileUrl); http.setUserAgent(USER_AGENT); int httpCode = http.GET(); @@ -215,19 +236,21 @@ int downloadUpdateHandler(char updateType) Update.onProgress(onOTAProgress); - int updateType = (updateType == UPDATE_WEBUI) ? U_SPIFFS : U_FLASH; - if (Update.begin(contentLength, updateType)) { - size_t written = Update.writeStream(*stream); + onOTAStart(); + size_t written = Update.write(firmware, contentLength); if (written == contentLength) { Serial.println("Written : " + String(written) + " successfully"); + free(firmware); } else { Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); + free(firmware); + return 503; } if (Update.end()) @@ -236,26 +259,33 @@ int downloadUpdateHandler(char updateType) if (Update.isFinished()) { Serial.println("Update successfully completed. Rebooting."); - ESP.restart(); +// ESP.restart(); } else { Serial.println("Update not finished? Something went wrong!"); + free(firmware); + return 503; } } else { Serial.println("Error Occurred. Error #: " + String(Update.getError())); + free(firmware); + return 503; } } else { Serial.println("Not enough space to begin OTA"); + free(firmware); + return 503; } } else { Serial.println("Invalid content length"); + return 503; } } else @@ -265,7 +295,7 @@ int downloadUpdateHandler(char updateType) } http.end(); - return 200; + return 0; } void updateWebUi(String latestRelease, int command) @@ -380,9 +410,8 @@ bool getIsOTAUpdating() return isOtaUpdating; } -String downloadSHA256(const String &filename) +String downloadSHA256(const String &sha256Url) { - String sha256Url = getLatestRelease(filename + ".sha256"); if (sha256Url.isEmpty()) { Serial.println("Failed to get SHA256 file URL"); diff --git a/src/lib/ota.hpp b/src/lib/ota.hpp index fd29473..516f9a1 100644 --- a/src/lib/ota.hpp +++ b/src/lib/ota.hpp @@ -15,6 +15,11 @@ typedef struct { extern QueueHandle_t otaQueue; +struct ReleaseInfo { + String fileUrl; + String checksumUrl; +}; + void setupOTA(); void onOTAStart(); void handleOTATask(void *parameter); @@ -23,9 +28,10 @@ void onOTAProgress(unsigned int progress, unsigned int total); void onOTAError(ota_error_t error); void onOTAComplete(); int downloadUpdateHandler(char updateType); -String getLatestRelease(const String& fileToDownload); +ReleaseInfo getLatestRelease(const String& fileToDownload); bool getIsOTAUpdating(); void updateWebUi(String latestRelease, int command); -String downloadSHA256(const String& filename); \ No newline at end of file +String downloadSHA256(const String& filename); + diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index dba240c..68a64b6 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -69,9 +70,9 @@ const int usPerMinute = 60 * usPerSecond; extern const char *github_root_ca; -const PROGMEM char UPDATE_FIRMWARE = 0; -const PROGMEM char UPDATE_WEBUI = 1; - +const PROGMEM char UPDATE_FIRMWARE = U_FLASH; +const PROGMEM char UPDATE_WEBUI = U_SPIFFS; +const PROGMEM char UPDATE_ALL = 99; struct ScreenMapping { int value; diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index a789e75..3d8b6da 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -86,8 +86,7 @@ void setupWebserver() { server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler); server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler); - // server.on("/update/webui", HTTP_GET, onUpdateWebUi); - // server.on("/update/firmware", HTTP_GET, onUpdateFirmware); + server.on("/api/firmware/auto_update", HTTP_GET, onAutoUpdateFirmware); } server.on("/api/restart", HTTP_GET, onApiRestart); @@ -142,29 +141,16 @@ void onFirmwareUpdate(AsyncWebServerRequest *request) request->send(response); } -void onUpdateWebUi(AsyncWebServerRequest *request) +void onAutoUpdateFirmware(AsyncWebServerRequest *request) { - UpdateMessage msg = {UPDATE_WEBUI}; + UpdateMessage msg = {UPDATE_ALL}; if (xQueueSend(otaQueue, &msg, 0) == pdTRUE) { - request->send(200, "text/plain", "WebUI update triggered"); + request->send(200, "application/json", "{\"msg\":\"Firmware update triggered\"}"); } else { - request->send(503, "text/plain", "Update already in progress"); - } -} - -void onUpdateFirmware(AsyncWebServerRequest *request) -{ - UpdateMessage msg = {UPDATE_FIRMWARE}; - if (xQueueSend(otaQueue, &msg, 0) == pdTRUE) - { - request->send(200, "text/plain", "Firmware update triggered"); - } - else - { - request->send(503, "text/plain", "Update already in progress"); + request->send(503,"application/json", "{\"msg\":\"Update already in progress\"}"); } } diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index 9f47d02..8c49011 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -26,10 +26,6 @@ bool processEpdColorSettings(AsyncWebServerRequest *request); void onApiStatus(AsyncWebServerRequest *request); void onApiSystemStatus(AsyncWebServerRequest *request); void onApiSetWifiTxPower(AsyncWebServerRequest *request); -void onUpdateWebUi(AsyncWebServerRequest *request); -void onUpdateFirmware(AsyncWebServerRequest *request); - - void onApiScreenNext(AsyncWebServerRequest *request); void onApiScreenPrevious(AsyncWebServerRequest *request); @@ -58,6 +54,7 @@ void onFirmwareUpdate(AsyncWebServerRequest *request); void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final, int command); void asyncWebuiUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); +void onAutoUpdateFirmware(AsyncWebServerRequest *request); void onIndex(AsyncWebServerRequest *request); void onNotFound(AsyncWebServerRequest *request);