673 lines
21 KiB
C++
673 lines
21 KiB
C++
#include "AppUpgrade.h"
|
|
#include "esp_log.h"
|
|
#include <MD5Builder.h>
|
|
#include <LittleFS.h>
|
|
#include <memory>
|
|
#include "global.h"
|
|
#include "JsonConstrain.h"
|
|
#include "BLE_UpdateService.h"
|
|
#include <HTTPClient.h>
|
|
#include <Update.h>
|
|
|
|
static const char* TAG = "AppUpdater";
|
|
TaskHandle_t Update_Task_Handle = NULL;
|
|
TaskHandle_t versionCheckTask_Handle = NULL;
|
|
|
|
// Queue handle for firmware update messages
|
|
//QueueHandle_t updateMsgQueue = NULL;
|
|
|
|
String updateUrl = "";
|
|
|
|
Version otaVersion;
|
|
|
|
|
|
AppUpdater::AppUpdater(fs::FS& fs, Version localVersion, const char* bucket, const char* manifestName, const char* appBin)
|
|
: localVersion(localVersion), manifestName(manifestName), appName(appBin), fileSystem(fs), downloadBuffer(new uint8_t[BUFFER_SIZE])
|
|
{
|
|
baseUrl = bucket ? String(bucket) : String(DEFAULT_MANIFEST_URL);
|
|
// Ensure baseUrl ends with a single '/'
|
|
if(!baseUrl.endsWith("/")) baseUrl += "/";
|
|
ESP_LOGI(TAG, "AppUpdater initialized (local v%s) baseUrl=%s", localVersion.toString().c_str(), baseUrl.c_str());
|
|
}
|
|
|
|
void AppUpdater::setProgressCallback(void (*callback)( UpdateStatus status, int percentage, const char* message)) {
|
|
progressCb = callback;
|
|
}
|
|
|
|
void AppUpdater::updateProgress(UpdateStatus newStatus, int percentage, const char* message) {
|
|
status = newStatus;
|
|
if (progressCb) {
|
|
progressCb(status, percentage, message);
|
|
}
|
|
}
|
|
|
|
bool AppUpdater::checkManifest() {
|
|
String url = buildUrl(manifestName);
|
|
ESP_LOGD(TAG, "Fetching manifest from: %s", url.c_str());
|
|
|
|
// Start the HTTP client and Send GET request for manifest
|
|
HTTPClient http;
|
|
http.begin(url);
|
|
int httpCode = http.GET();
|
|
if (httpCode != HTTP_CODE_OK) {
|
|
ESP_LOGE(TAG, "HTTP GET failed, error: %d", httpCode);
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Read the response
|
|
String payload = http.getString();
|
|
http.end();
|
|
|
|
// Parse JSON
|
|
DeserializationError error = deserializeJson(jsonManifest, payload);
|
|
ESP_LOGD(TAG, "Manifest deserialized");
|
|
if (error) {
|
|
ESP_LOGE(TAG, "Failed to parse manifest: %s", error.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Check for files section
|
|
jsonFilesArray = jsonManifest["files"];
|
|
if (jsonFilesArray.isNull()) {
|
|
ESP_LOGE(TAG, "No files section in manifest");
|
|
return false;
|
|
}else{
|
|
ESP_LOGD(TAG, "%d Files found", jsonFilesArray.size());
|
|
}
|
|
|
|
// Check for version section
|
|
JsonObject jsonVersion = jsonManifest["version"];
|
|
ESP_LOGD(TAG, "Version section found");
|
|
if (jsonVersion.isNull()) {
|
|
ESP_LOGE(TAG, "No version section in manifest");
|
|
return false;
|
|
}
|
|
|
|
// Get the remote version
|
|
byte major = jsonVersion["major"] | 0;
|
|
byte minor = jsonVersion["minor"] | 0;
|
|
byte patch = jsonVersion["patch"] | 0;
|
|
otaVersion = {major, minor, patch};
|
|
|
|
//Version localVersion;
|
|
//::sscanf(localVersion, "%d.%d.%d", &localVersion.major, &localVersion.minor, &localVersion.patch);
|
|
|
|
// Check if an update is available
|
|
updateAvailable = false;
|
|
// Only mark update available if remote is strictly newer than local
|
|
if (otaVersion <= localVersion) {
|
|
ESP_LOGI(TAG, "No updates available");
|
|
return false;
|
|
}else{
|
|
updateAvailable = true;
|
|
ESP_LOGD(TAG, "Update available");
|
|
}
|
|
|
|
//ESP_LOGD(TAG, "Manifest content: %s", payload.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AppUpdater::updateFile(const char* remotePath, const char* localPath, const char* expectedMd5) {
|
|
//updateProgress(UpdateStatus::DOWNLOADING, 0, localPath);
|
|
|
|
// Construct full URL
|
|
String url = buildUrl(remotePath);
|
|
ESP_LOGD(TAG, "Downloading: %s -> %s", url.c_str(), localPath);
|
|
|
|
String localMd5 = getLocalMD5(localPath);
|
|
|
|
if (localMd5.equals(expectedMd5)) {
|
|
ESP_LOGI(TAG, "File already up to date: %s", localPath);
|
|
updateProgress(UpdateStatus::FILE_SKIPPED, 100, localPath);
|
|
return true;
|
|
}
|
|
|
|
// Start the download
|
|
HTTPClient http;
|
|
http.begin(url);
|
|
int httpCode = http.GET();
|
|
if (httpCode != HTTP_CODE_OK) {
|
|
ESP_LOGE(TAG, "Download failed: %d", httpCode);
|
|
updateProgress(UpdateStatus::ERROR, 0, "Download failed");
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Get the stream and content length
|
|
WiFiClient* stream = http.getStreamPtr();
|
|
size_t contentLength = http.getSize();
|
|
|
|
// Verify and save the file
|
|
bool success = verifyAndSaveFile(stream, contentLength, localPath, expectedMd5);
|
|
http.end();
|
|
if(!success){
|
|
updateProgress( UpdateStatus::ERROR, 0, "MD5 verification failed");
|
|
}else{
|
|
updateProgress( UpdateStatus::FILE_SAVED, 100, localPath);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, const char* localPath, const char* expectedMd5)
|
|
{
|
|
MD5Builder md5;
|
|
md5.begin();
|
|
size_t totalRead = 0;
|
|
|
|
// Create temporary filename
|
|
String tempPath = String(localPath) + ".tmp";
|
|
|
|
// Open temporary file for writing
|
|
File file = fileSystem.open(tempPath.c_str(), FILE_WRITE);
|
|
if (!file) {
|
|
ESP_LOGE(TAG, "Failed to open temporary file for writing");
|
|
return false;
|
|
}
|
|
|
|
//updateProgress(UpdateStatus::DOWNLOADING, 0, localPath);
|
|
|
|
if (contentLength > 0) {
|
|
// Single pass with known content length
|
|
while (totalRead < contentLength) {
|
|
size_t available = stream->available();
|
|
if (available) {
|
|
size_t readLen = stream->readBytes(downloadBuffer.get(), std::min(available, size_t(BUFFER_SIZE)));
|
|
|
|
// Write to temp file and update MD5
|
|
if (file.write(downloadBuffer.get(), readLen) != readLen) {
|
|
ESP_LOGE(TAG, "Failed to write to temporary file");
|
|
|
|
file.close();
|
|
fileSystem.remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
md5.add(downloadBuffer.get(), readLen);
|
|
totalRead += readLen;
|
|
updateProgress(UpdateStatus::DOWNLOADING, (totalRead * 90) / contentLength , localPath);
|
|
}
|
|
yield();
|
|
}
|
|
} else {
|
|
// Unknown content length: read until stream ends
|
|
for (;;) {
|
|
size_t readLen = stream->readBytes(downloadBuffer.get(), BUFFER_SIZE);
|
|
if (readLen == 0) {
|
|
break;
|
|
}
|
|
if (file.write(downloadBuffer.get(), readLen) != readLen) {
|
|
ESP_LOGE(TAG, "Failed to write to temporary file");
|
|
file.close();
|
|
fileSystem.remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
md5.add(downloadBuffer.get(), readLen);
|
|
totalRead += readLen;
|
|
// Progress unknown; emit periodic heartbeats at 0%
|
|
updateProgress(UpdateStatus::DOWNLOADING, 0, localPath);
|
|
yield();
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
md5.calculate();
|
|
String calculatedMd5 = md5.toString();
|
|
|
|
// Verify MD5 hash
|
|
if (!calculatedMd5.equals(expectedMd5)) {
|
|
//ESP_LOGE(TAG, "MD5 mismatch for %s", localPath);
|
|
fileSystem.remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Replace original file with verified temp file
|
|
if (fileSystem.exists(localPath)) {
|
|
fileSystem.remove(localPath);
|
|
}
|
|
if (!fileSystem.rename(tempPath.c_str(), localPath)) {
|
|
ESP_LOGE(TAG, "Failed to rename temporary file");
|
|
fileSystem.remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
String AppUpdater::getLocalMD5(const char* filePath){
|
|
File file = fileSystem.open(filePath, "r");
|
|
if(!file){
|
|
ESP_LOGE(TAG, "Error opening %s...", filePath);
|
|
return String();
|
|
}
|
|
|
|
MD5Builder md5Builder;
|
|
md5Builder.begin();
|
|
size_t fileSize = file.size();
|
|
size_t totalRead = 0;
|
|
size_t readLen = 0;
|
|
while (totalRead < fileSize) {
|
|
readLen = file.readBytes(reinterpret_cast<char*>(downloadBuffer.get()), std::min(fileSize - totalRead, size_t(BUFFER_SIZE)));
|
|
md5Builder.add(downloadBuffer.get(), readLen);
|
|
totalRead += readLen;
|
|
}
|
|
|
|
md5Builder.calculate();
|
|
file.close();
|
|
return md5Builder.toString();
|
|
}
|
|
|
|
bool AppUpdater::updateFilesArray() {
|
|
int successCount = 0;
|
|
int totalFiles = jsonFilesArray.size();
|
|
ESP_LOGI(TAG, "Found %d files in manifest", totalFiles);
|
|
|
|
// Iterate over each file entry in the manifest
|
|
for (JsonObject file : jsonFilesArray) {
|
|
const char* remotePath = file["remote"];
|
|
const char* localPath = file["local"];
|
|
const char* expectedMd5 = file["md5"];
|
|
|
|
// Skip invalid entries
|
|
if (!remotePath || !localPath || !expectedMd5) {
|
|
ESP_LOGE(TAG, "Invalid file entry in manifest");
|
|
continue;
|
|
}
|
|
|
|
// Attempt to update the file
|
|
if (updateFile(remotePath, localPath, expectedMd5)) {
|
|
successCount++;
|
|
}
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Manifest update complete: %d/%d files updated", successCount, totalFiles);
|
|
return successCount == totalFiles;
|
|
}
|
|
|
|
bool AppUpdater::updateApp() {
|
|
updateProgress(UpdateStatus::MESSAGE, 0, "Starting firmware update");
|
|
|
|
// Check for firmware section in manifest
|
|
if (!jsonManifest["firmware"].is<JsonObject>() || !jsonManifest["firmware"]["md5"].is<const char*>()) {
|
|
ESP_LOGE(TAG, "Invalid firmware section in manifest");
|
|
updateProgress(UpdateStatus::ERROR, 0, "Firmware: Invalid firmware section in manifest");
|
|
return false;
|
|
}
|
|
|
|
// Get the firmware MD5 hash and URL
|
|
const char* expectedMd5 = jsonManifest["firmware"]["md5"];
|
|
String firmwareUrl = buildUrl(appName);
|
|
|
|
// Download the firmware
|
|
HTTPClient http;
|
|
http.begin(firmwareUrl);
|
|
int httpCode = http.GET();
|
|
if (httpCode != HTTP_CODE_OK) {
|
|
ESP_LOGE(TAG, "Firmware download failed: %d", httpCode);
|
|
updateProgress(UpdateStatus::ERROR, 0, "Firmware: Firmware download failed");
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Check available space
|
|
size_t firmwareSize = http.getSize();
|
|
if (!Update.begin(firmwareSize > 0 ? firmwareSize : UPDATE_SIZE_UNKNOWN)) {
|
|
ESP_LOGE(TAG, "Firmware: Not enough space for update");
|
|
updateProgress(UpdateStatus::ERROR, 0, "Firmware: Not enough space for update");
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Set up MD5 checking
|
|
MD5Builder md5;
|
|
md5.begin();
|
|
|
|
// Download and verify firmware
|
|
WiFiClient* stream = http.getStreamPtr();
|
|
if (firmwareSize > 0) {
|
|
size_t remaining = firmwareSize;
|
|
while (remaining > 0) {
|
|
size_t chunk = std::min(remaining, size_t(BUFFER_SIZE));
|
|
size_t read = stream->readBytes(downloadBuffer.get(), chunk);
|
|
|
|
// Check for timeout
|
|
if (read == 0) {
|
|
ESP_LOGE(TAG, "Read timeout");
|
|
Update.abort();
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Update MD5 and write firmware
|
|
md5.add(downloadBuffer.get(), read);
|
|
if (Update.write(downloadBuffer.get(), read) != read) {
|
|
ESP_LOGE(TAG, "Write failed");
|
|
Update.abort();
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
remaining -= read;
|
|
updateProgress(UpdateStatus::DOWNLOADING, (firmwareSize - remaining) * 100 / firmwareSize, "firmware");
|
|
}
|
|
} else {
|
|
// Unknown size: stream until end
|
|
for (;;) {
|
|
size_t read = stream->readBytes(downloadBuffer.get(), BUFFER_SIZE);
|
|
if (read == 0) break;
|
|
md5.add(downloadBuffer.get(), read);
|
|
if (Update.write(downloadBuffer.get(), read) != read) {
|
|
ESP_LOGE(TAG, "Write failed");
|
|
Update.abort();
|
|
http.end();
|
|
return false;
|
|
}
|
|
updateProgress(UpdateStatus::DOWNLOADING, 0, "firmware");
|
|
}
|
|
}
|
|
|
|
// Verify MD5
|
|
md5.calculate();
|
|
String calculatedMd5 = md5.toString();
|
|
if (!calculatedMd5.equals(expectedMd5)) {
|
|
ESP_LOGE(TAG, "MD5 mismatch. Expected: %s, Got: %s", expectedMd5, calculatedMd5.c_str());
|
|
updateProgress(UpdateStatus::MD5_FAILED, 0, "Firmware: MD5 mismatch");
|
|
Update.abort();
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Finish update
|
|
if (!Update.end()) {
|
|
ESP_LOGE(TAG, "Update end failed");
|
|
updateProgress(UpdateStatus::ERROR, 0, "Firmware: Update failed");
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
http.end();
|
|
updateProgress(UpdateStatus::COMPLETE, 0, "Firmware: Complete");
|
|
return true;
|
|
}
|
|
|
|
bool AppUpdater::IsUpdateAvailable(){
|
|
return updateAvailable;
|
|
}
|
|
|
|
String AppUpdater::buildUrl(const char* path) const {
|
|
if(!path || !*path) return baseUrl; // just base
|
|
String p(path);
|
|
// If already absolute URL, pass through
|
|
if(p.startsWith("http://") || p.startsWith("https://")) return p;
|
|
// Strip leading slashes to avoid double
|
|
while(p.startsWith("/")) p.remove(0,1);
|
|
// Ensure baseUrl has single trailing slash
|
|
String b = baseUrl;
|
|
if(!b.endsWith("/")) b += "/";
|
|
return b + p;
|
|
}
|
|
|
|
|
|
AsyncEventSource* eventProgress = nullptr;
|
|
void startFirmwareUpdateTask(AsyncEventSource* evProg) {
|
|
eventProgress = evProg;
|
|
if(Update_Task_Handle) {
|
|
ESP_LOGW(TAG, "Firmware update task already running");
|
|
return;
|
|
}
|
|
xTaskCreate(firmwareUpdateTask, "FirmwareUpdate", 1024*8, NULL, 1, &Update_Task_Handle);
|
|
}
|
|
|
|
void firmwareUpdateTask(void* parameter) {
|
|
static const char* TAG = "UpdateTask";
|
|
AppUpdater* updater = nullptr;
|
|
|
|
try {
|
|
loadUpdateJson();
|
|
|
|
// Initialize updater
|
|
updater = new AppUpdater(LittleFS, localVersion, updateUrl.c_str(), "update.json", "firmware.bin");
|
|
updater->setProgressCallback(updateProgress);
|
|
|
|
ESP_LOGI(TAG, "Starting update check from: %s", updateUrl.c_str());
|
|
|
|
// Check and perform updates
|
|
if (!updater->checkManifest()) { throw std::runtime_error("Failed to check manifest"); }
|
|
|
|
if (updater->IsUpdateAvailable()) {
|
|
ESP_LOGI(TAG, "Update available, updating files...");
|
|
|
|
if (!updater->updateFilesArray()) {
|
|
throw std::runtime_error("Failed to update files");
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Updating firmware...");
|
|
if (!updater->updateApp()) {
|
|
throw std::runtime_error("Failed to update firmware");
|
|
}
|
|
ESP_LOGI(TAG, "Update successful, restarting...");
|
|
|
|
sendUpdateMessage("Restarting ", true, 100);
|
|
vTaskDelay(2000);
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
|
ESP_LOGE(TAG, "Update failed: %s", e.what());
|
|
}
|
|
end:
|
|
delete updater;
|
|
Update_Task_Handle = NULL;
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
void startVersionCheckTask() {
|
|
if(versionCheckTask_Handle != NULL) {
|
|
ESP_LOGW(TAG, "Version Check Tak already running");
|
|
return;
|
|
}
|
|
xTaskCreate(versionCheckTask, "VersionCheckTask", 1024*8, NULL, 1, &versionCheckTask_Handle);
|
|
}
|
|
|
|
void versionCheckTask(void* parameter){
|
|
|
|
if(updateUrl == ""){
|
|
loadUpdateJson();
|
|
}
|
|
|
|
if(checkManifest(otaVersion) == false){
|
|
ESP_LOGE(TAG, "Error checking manifest");
|
|
}
|
|
|
|
versionCheckTask_Handle = NULL;
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
void loadUpdateJson(void) {
|
|
try {
|
|
ESP_LOGD(TAG, "loadUpdateJaon function...");
|
|
if(updateUrl == "") {
|
|
String updateJsonPath = "/system/update.json";
|
|
|
|
// Read and parse update.json
|
|
File file = LittleFS.open(updateJsonPath);
|
|
if (!file) {
|
|
throw std::runtime_error("Failed to open update.json");
|
|
}
|
|
|
|
JsonDocument doc;
|
|
DeserializationError error = deserializeJson(doc, file);
|
|
file.close();
|
|
|
|
if (error) { throw std::runtime_error("Failed to parse update.json"); }
|
|
|
|
// Get update configuration
|
|
JsonObject jObj = doc.as<JsonObject>();
|
|
String folderName = jsonConstrainString(TAG, jObj, "folder", "latest/");
|
|
String baseUrl = jsonConstrainString(TAG, jObj, "baseurl", "https://s3-minio.boothwizard.com/boothifier/");
|
|
updateUrl = baseUrl + folderName;
|
|
|
|
ESP_LOGD(TAG, "updateUrl: %s", updateUrl.c_str());
|
|
}
|
|
} catch (const std::exception& e) {
|
|
ESP_LOGE(TAG, "Update failed: %s", e.what());
|
|
}
|
|
}
|
|
|
|
void updateProgress(AppUpdater::UpdateStatus newStatus, int percentage, const char* message = nullptr) {
|
|
|
|
char buffer[128];
|
|
const char* msg;
|
|
bool isComplete = false;
|
|
|
|
const char* safeMsg = message ? message : "";
|
|
switch (newStatus) {
|
|
case AppUpdater::UpdateStatus::IDLE:
|
|
snprintf(buffer, sizeof(buffer), "Update idle");
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::MESSAGE:
|
|
msg = message ? message : "";
|
|
break;
|
|
case AppUpdater::UpdateStatus::DOWNLOADING:
|
|
snprintf(buffer, sizeof(buffer), "%s: Download progress: %d%%", safeMsg, percentage);
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::VERIFYING:
|
|
snprintf(buffer, sizeof(buffer), "%s: Verifying update: %d%%", safeMsg, percentage);
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::FILE_SKIPPED:
|
|
snprintf(buffer, sizeof(buffer), "%s: Skipping file update, already up to date", safeMsg);
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::FILE_SAVED:
|
|
snprintf(buffer, sizeof(buffer), "%s: File Saved", safeMsg);
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::MD5_FAILED:
|
|
snprintf(buffer, sizeof(buffer), "%s: MD5 Verification Failed", safeMsg);
|
|
msg = buffer;
|
|
break;
|
|
case AppUpdater::UpdateStatus::COMPLETE:
|
|
snprintf(buffer, sizeof(buffer), "Firmware Update Complete!!!");
|
|
msg = buffer;
|
|
isComplete = true;
|
|
break;
|
|
case AppUpdater::UpdateStatus::ERROR:
|
|
snprintf(buffer, sizeof(buffer), "Error!: %s", safeMsg);
|
|
msg = buffer;
|
|
break;
|
|
default:
|
|
snprintf(buffer, sizeof(buffer), "Unknown update status: %d", (int)newStatus);
|
|
msg = buffer;
|
|
break;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "%s", msg);
|
|
sendUpdateMessage(msg, isComplete, percentage);
|
|
}
|
|
|
|
void sendUpdateMessage(const char* message, bool complete, int progress = -1) {
|
|
if(eventProgress && eventProgress->count() > 0) {
|
|
JsonDocument jsonDoc;
|
|
jsonDoc["message"] = message;
|
|
jsonDoc["complete"] = complete;
|
|
jsonDoc["progress"] = progress;
|
|
String strMessage;
|
|
serializeJson(jsonDoc, strMessage);
|
|
eventProgress->send(strMessage.c_str(), "update", millis());
|
|
}
|
|
else{
|
|
ESP_LOGW(TAG, "No clients connected to event source");
|
|
}
|
|
|
|
bleUpgrade_send_message(message);
|
|
}
|
|
|
|
bool checkManifest(Version& remoteVersion) {
|
|
const char* TAG = "manifestCheck";
|
|
String url = updateUrl + "update.json";
|
|
ESP_LOGD(TAG, "Fetching manifest from: %s", url.c_str());
|
|
|
|
// Start the HTTP client and send GET request for manifest
|
|
HTTPClient http;
|
|
http.begin(url);
|
|
int httpCode = http.GET();
|
|
if (httpCode != HTTP_CODE_OK) {
|
|
ESP_LOGE(TAG, "HTTP GET failed, error: %d", httpCode);
|
|
http.end();
|
|
return false;
|
|
}
|
|
|
|
// Read the response
|
|
String payload = http.getString();
|
|
http.end();
|
|
|
|
//ESP_LOGD(TAG, "%s", payload.c_str());
|
|
|
|
// Parse JSON
|
|
JsonDocument jsonManifest;
|
|
DeserializationError error = deserializeJson(jsonManifest, payload);
|
|
if (error) {
|
|
ESP_LOGE(TAG, "Failed to parse manifest: %s", error.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Check for version section
|
|
JsonObject jsonVersion = jsonManifest["version"];
|
|
|
|
if (jsonVersion.isNull()) {
|
|
ESP_LOGE(TAG, "No version section in manifest");
|
|
return false;
|
|
}
|
|
|
|
// Get the remote version
|
|
byte major = jsonVersion["major"] | 0;
|
|
byte minor = jsonVersion["minor"] | 0;
|
|
byte patch = jsonVersion["patch"] | 0;
|
|
remoteVersion = {major, minor, patch};
|
|
ESP_LOGI(TAG, "Remote version: %s", remoteVersion.toString().c_str());
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
|
|
// Initialize WiFi connection first
|
|
// ... WiFi connection code ...
|
|
|
|
// Initialize filesystem
|
|
if(!LittleFS.begin()) {
|
|
Serial.println("LittleFS Mount Failed");
|
|
return;
|
|
}
|
|
|
|
// Create updater instance with:
|
|
// - Current version: "1.0.0"
|
|
// - Update server URL: "https://my-update-server.com/"
|
|
// - Filesystem: LittleFS
|
|
AppUpdater updater("1.0.0", "https://storage.googleapis.com/boothifier/latest/", LittleFS);
|
|
|
|
// Set progress callback
|
|
updater.setProgressCallback([](int progress) {
|
|
Serial.printf("Update progress: %d%%\n", progress);
|
|
});
|
|
|
|
// Check and update firmware
|
|
if (updater.checkAndUpdate()) {
|
|
Serial.println("Update successful! Rebooting...");
|
|
ESP.restart();
|
|
}
|
|
|
|
// Update specific files from manifest
|
|
int updatedFiles = updater.updateFilesFromManifest("test_update.json");
|
|
Serial.printf("Updated %d files\n", updatedFiles);
|
|
}
|
|
|
|
*/ |