Add single-click auto-update functionality

This commit is contained in:
Djuri Baars 2024-09-11 20:27:40 +02:00
parent d00c216126
commit 023ff29131
6 changed files with 72 additions and 53 deletions

2
data

@ -1 +1 @@
Subproject commit 1c2d8dcdd0efd846f39b0f24977c982a96f16392
Subproject commit 6c40b54273b7f7c7d6c2624d3c2a066435f27756

View file

@ -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<String>();
if (assetName == fileToDownload)
{
info.fileUrl = asset["browser_download_url"].as<String>();
}
else if (assetName == fileToDownload + ".sha256")
{
info.checksumUrl = asset["browser_download_url"].as<String>();
}
if (!info.fileUrl.isEmpty() && !info.checksumUrl.isEmpty())
{
downloadUrl = asset["browser_download_url"].as<String>();
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");

View file

@ -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);
String downloadSHA256(const String& filename);

View file

@ -9,6 +9,7 @@
#include <GxEPD2.h>
#include <GxEPD2_BW.h>
#include <mbedtls/md.h>
#include <Update.h>
#include <mutex>
#include <utils.hpp>
@ -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;

View file

@ -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\"}");
}
}

View file

@ -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);