Files
OpenEPaperLink/ESP32_AP-Flasher/src/ota.cpp
Nic Limper e169dbab6e added äöüßÄÖÜ to fonts + small updates
- fonts re-rendered: added äöüßÄÖÜ.
- shuffled a bit with fonts to optimize disk usage
- ability to delete files during OTA
- prettify tagDB.json
- error handling in get_DB
- small cosmetic changes

* Due to new ability to delete files: After firmware update via OTA, please perform an OTA update of the filesystem again to perform the deletion of unneeded fonts.
* If updating via platform.io: don't forget to update the filesystem image.
2023-06-06 16:04:18 +02:00

289 lines
9.3 KiB
C++

#include "ota.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include <HTTPClient.h>
#include <LittleFS.h>
#include <MD5Builder.h>
#include <Update.h>
#include "tag_db.h"
#include "web.h"
#ifndef BUILD_ENV_NAME
#define BUILD_ENV_NAME unknown
#endif
#ifndef BUILD_TIME
#define BUILD_TIME 0
#endif
#ifndef BUILD_VERSION
#define BUILD_VERSION custom
#endif
#ifndef SHA
#define SHA 0
#endif
#define STR_IMPL(x) #x
#define STR(x) STR_IMPL(x)
void handleSysinfoRequest(AsyncWebServerRequest* request) {
StaticJsonDocument<250> doc;
doc["alias"] = config.alias;
doc["env"] = STR(BUILD_ENV_NAME);
doc["buildtime"] = STR(BUILD_TIME);
doc["buildversion"] = STR(BUILD_VERSION);
doc["sha"] = STR(SHA);
doc["psramsize"] = ESP.getPsramSize();
doc["flashsize"] = ESP.getFlashChipSize();
doc["rollback"] = Update.canRollBack();
size_t bufferSize = measureJson(doc) + 1;
AsyncResponseStream* response = request->beginResponseStream("application/json", bufferSize);
serializeJson(doc, *response);
request->send(response);
};
void handleCheckFile(AsyncWebServerRequest* request) {
if (!request->hasParam("path")) {
request->send(400);
return;
}
String filePath = request->getParam("path")->value();
File file = LittleFS.open(filePath, "r");
if (!file) {
StaticJsonDocument<64> doc;
doc["filesize"] = 0;
doc["md5"] = "";
String jsonResponse;
serializeJson(doc, jsonResponse);
request->send(200, "application/json", jsonResponse);
return;
}
size_t fileSize = file.size();
MD5Builder md5;
md5.begin();
md5.addStream(file, fileSize);
md5.calculate();
String md5Hash = md5.toString();
file.close();
StaticJsonDocument<128> doc;
doc["filesize"] = fileSize;
doc["md5"] = md5Hash;
String jsonResponse;
serializeJson(doc, jsonResponse);
request->send(200, "application/json", jsonResponse);
}
void handleGetExtUrl(AsyncWebServerRequest* request) {
if (request->hasParam("url")) {
String url = request->getParam("url")->value();
HTTPClient http;
http.begin(url);
http.setConnectTimeout(5000);
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
Serial.println(httpResponseCode);
String contentType = http.header("Content-Type");
size_t contentLength = http.getSize();
if (contentLength > 0) {
String content = http.getString();
AsyncWebServerResponse* response = request->beginResponse(200, contentType, content);
request->send(response);
} else {
request->send(500, "text/plain", "no size header");
}
} else {
request->send(httpResponseCode, "text/plain", "Failed to fetch URL");
wsSerial(http.errorToString(httpResponseCode));
}
http.end();
} else {
request->send(400, "text/plain", "Missing 'url' parameter");
}
}
void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
bool error = false;
if (!index) {
String path;
if (!request->hasParam("path", true)) {
path = "/temp/null.bin";
final = true;
error = true;
} else {
path = request->getParam("path", true)->value();
Serial.println("update " + path);
request->_tempFile = LittleFS.open(path, "w", true);
}
}
if (len) {
if (!request->_tempFile.write(data, len)) {
error = true;
final = true;
}
}
if (final) {
request->_tempFile.close();
if (error) {
request->send(507, "text/plain", "Error. Disk full?");
} else {
request->send(200, "text/plain", "Ok, file written");
}
}
}
struct FirmwareUpdateParams {
String url;
String md5;
size_t size;
};
void handleUpdateOTA(AsyncWebServerRequest* request) {
if (request->hasParam("url", true) && request->hasParam("md5", true) && request->hasParam("size", true)) {
FirmwareUpdateParams* params = new FirmwareUpdateParams;
params->url = request->getParam("url", true)->value();
params->md5 = request->getParam("md5", true)->value();
params->size = request->getParam("size", true)->value().toInt();
xTaskCreate(firmwareUpdateTask, "OTAUpdateTask", 6144, params, 10, NULL);
request->send(200, "text/plain", "In progress");
} else {
request->send(400, "Bad request");
}
}
void firmwareUpdateTask(void* parameter) {
FirmwareUpdateParams* params = reinterpret_cast<FirmwareUpdateParams*>(parameter);
if (ESP.getMaxAllocHeap() < 22000) {
wsSerial("Error: Not enough memory left. Restart the esp32 and try updating again.");
wsSerial("[reboot]");
} else {
const char* url = params->url.c_str();
const char* md5 = params->md5.c_str();
size_t size = params->size;
updateFirmware(url, md5, size);
}
delete params;
vTaskDelete(NULL);
}
void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
uint32_t freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
config.runStatus = RUNSTATUS_STOP;
vTaskDelay(3000 / portTICK_PERIOD_MS);
// xSemaphoreTake(tagDBOwner, portMAX_DELAY);
saveDB("/current/tagDB.json");
// destroyDB();
HTTPClient httpClient;
wsSerial("start downloading");
wsSerial(url);
httpClient.begin(url);
httpClient.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
int httpCode = httpClient.GET();
freeStack = uxTaskGetStackHighWaterMark(NULL);
Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack);
if (httpCode == HTTP_CODE_OK) {
if (Update.begin(size)) {
Update.setMD5(expectedMd5);
unsigned long progressTimer = millis();
Update.onProgress([&progressTimer](size_t progress, size_t total) {
if (millis() - progressTimer > 500 || progress == total) {
char buffer[50];
sprintf(buffer, "Progress: %u%% %d %d", progress * 100 / total, progress, total);
wsSerial(String(buffer));
progressTimer = millis();
vTaskDelay(1 / portTICK_PERIOD_MS);
}
});
size_t written = Update.writeStream(httpClient.getStream());
if (written == httpClient.getSize()) {
if (Update.end(true)) {
wsSerial("Firmware update successful");
wsSerial("Reboot system now");
wsSerial("[reboot]");
vTaskDelay(1000 / portTICK_PERIOD_MS);
// ESP.restart();
} else {
wsSerial("Error updating firmware:");
wsSerial(Update.errorString());
}
} else {
wsSerial("Error writing firmware data:");
wsSerial(Update.errorString());
}
} else {
wsSerial("Failed to begin firmware update");
wsSerial(Update.errorString());
}
} else {
wsSerial("Failed to download firmware file (HTTP code " + String(httpCode) + ")");
wsSerial(httpClient.errorToString(httpCode));
}
httpClient.end();
// loadDB("/current/tagDB.json");
config.runStatus = RUNSTATUS_RUN;
// xSemaphoreGive(tagDBOwner);
}
void handleRollback(AsyncWebServerRequest* request) {
if (Update.canRollBack()) {
bool rollbackSuccess = Update.rollBack();
if (rollbackSuccess) {
request->send(200, "Rollback successful");
wsSerial("Rollback successful");
wsSerial("Reboot system now");
wsSerial("[reboot]");
vTaskDelay(1000 / portTICK_PERIOD_MS);
// ESP.restart();
} else {
wsSerial("Rollback failed");
request->send(400, "Rollback failed");
}
} else {
wsSerial("Rollback not allowed");
request->send(400, "Rollback not allowed");
}
}
void handleUpdateActions(AsyncWebServerRequest* request) {
wsSerial("Performing cleanup");
File file = LittleFS.open("/update_actions.json", "r");
if (!file) {
wsSerial("No update_actions.json present");
request->send(200, "No update actions needed");
return;
}
StaticJsonDocument<1000> doc;
DeserializationError error = deserializeJson(doc, file);
JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
for (const auto& filePath : deleteFiles) {
if (LittleFS.remove(filePath.as<const char*>())) {
wsSerial("deleted file: " + filePath.as<String>());
}
}
file.close();
wsSerial("Cleanup finished");
request->send(200, "Clean up finished");
LittleFS.remove("/update_actions.json");
}