refactor(ota): implement polling-based update system and fix API typo
Test project compilation / test (push) Successful in 3m52s
Test project compilation / test (push) Successful in 3m52s
- Fix typo in OTA update status API endpoint (/ota_udpate_status → /ota_update_status) - Replace WebSocket-based update progress with polling mechanism - Add OTA lifecycle callbacks (started, finished, progress, error) - Implement check_update_task with internet connectivity verification - Add LittleFS/SPIFFS update support - Simplify update progress page to use 90-second delay and polling instead of real-time WebSocket updates This refactor improves reliability by checking internet connectivity before attempting updates and simplifies the frontend by removing WebSocket complexity in favor of a timeout-based polling approach.
This commit is contained in:
+1
-1
@@ -49,7 +49,7 @@ function fetchWaterData(gauge) {
|
||||
}
|
||||
|
||||
function fetchUpdateData(gauge) {
|
||||
const apiUrl = '/ota_udpate_status';
|
||||
const apiUrl = '/ota_update_status';
|
||||
// Fetching data from the API
|
||||
fetch(apiUrl)
|
||||
.then(response => response.json())
|
||||
|
||||
+20
-32
@@ -15,44 +15,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const webSocket = new WebSocket('webSocket://' + window.location.host + '/webSocket');
|
||||
<script>
|
||||
// Wait 90 seconds after page load, then start checking if server is up
|
||||
window.addEventListener('load', function() {
|
||||
setTimeout(checkServerAndRedirect, 90000); // 90 seconds = 90000 ms
|
||||
});
|
||||
|
||||
webSocket.onopen = function() {
|
||||
console.log('WebSocket connection opened.');
|
||||
};
|
||||
|
||||
webSocket.onmessage = function(event) {
|
||||
console.log('Progress:', event.data);
|
||||
// Update the progress bar
|
||||
let progress = parseInt(event.data);
|
||||
if (progress == -1) {
|
||||
document.getElementById('progress').textContent = "Upgrade Done, wait for reboot...";
|
||||
|
||||
const checkStatus = setInterval(() => {
|
||||
fetch('/ota_udpate_status')
|
||||
function checkServerAndRedirect() {
|
||||
// Try to fetch the home page to check if server is up
|
||||
fetch('/', {
|
||||
method: 'GET',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
.then(response => {
|
||||
// If we get a response (server is up), redirect to home page
|
||||
if (response.ok) {
|
||||
clearInterval(checkStatus);
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
// Server responded but with an error, wait and retry
|
||||
setTimeout(checkServerAndRedirect, 5000); // 5 seconds = 5000 ms
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error checking OTA update status:', error));
|
||||
}, 1000);
|
||||
} else {
|
||||
document.getElementById('progress').textContent = progress + '%';
|
||||
.catch(error => {
|
||||
// Fetch failed (server not up yet), wait and retry
|
||||
setTimeout(checkServerAndRedirect, 5000); // 5 seconds = 5000 ms
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
webSocket.onerror = function(error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
webSocket.onclose = function() {
|
||||
console.log('WebSocket connection closed.');
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -72,7 +72,7 @@ struct OTAStatus {
|
||||
Version current_version; /**< Current version of the firmware */
|
||||
Version latest_version; /**< Latest available version of the firmware */
|
||||
int update_progress; /**< Progress of the update as a percentage */
|
||||
String update_url; /**< URL to download the update */
|
||||
char update_url[256]; /**< URL to download the update (max 255 chars + null terminator) */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,7 +96,8 @@ typedef enum {
|
||||
DATA_TYPE_WATER,
|
||||
DATA_TYPE_WIFI,
|
||||
DATA_TYPE_ETHERNET,
|
||||
DATA_TYPE_TELEMETRY
|
||||
DATA_TYPE_TELEMETRY,
|
||||
DATA_TYPE_OTA
|
||||
} DataType;
|
||||
|
||||
// Union to hold different types of data
|
||||
@@ -105,6 +106,7 @@ typedef union {
|
||||
WaterData waterData;
|
||||
NetworkData networkData;
|
||||
DeviceTelemetry telemetryData;
|
||||
OTAStatus otaStatus;
|
||||
} DataUnion;
|
||||
|
||||
// Structure to hold data type and the actual data
|
||||
|
||||
+9
-10
@@ -26,7 +26,7 @@
|
||||
#include <fetchOTA.h>
|
||||
#include "time.h"
|
||||
#include "tools/log.h"
|
||||
#include "tools/ota_handler.h"
|
||||
#include "tools/ota.h"
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -35,8 +35,6 @@
|
||||
|
||||
Preferences prefs;
|
||||
|
||||
extern AsyncWebSocket webSocket;
|
||||
|
||||
Version current_spiffs_version;
|
||||
|
||||
// Variable to store the current state of the system
|
||||
@@ -65,10 +63,6 @@ void setup()
|
||||
prefs.begin("waterlevel", false);
|
||||
Serial.begin(115200);
|
||||
|
||||
delay(500);
|
||||
LOG(ELOG_LEVEL_DEBUG, "Init Sensor");
|
||||
init_sensor();
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Beginning LittleFS");
|
||||
LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED);
|
||||
|
||||
@@ -95,9 +89,7 @@ void setup()
|
||||
xTaskCreate(collect_internal_telemetry_task, "InternalTelemetryTask", 1024 * 2, dataQueue, 1, NULL);
|
||||
xTaskCreate(display_task, "DisplayTask", 1024 * 2, NULL, 1, NULL);
|
||||
xTaskCreate(get_time_task, "GetTimeTask", 1024 * 2, NULL, 1, NULL);
|
||||
|
||||
delay(5000);
|
||||
xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 6, NULL, 1, NULL);
|
||||
xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 6, dataQueue, 1, NULL);
|
||||
xTaskCreate(webserver_task, "WebServerTask", 1024 * 4, stateForWebserverQueue, 1, NULL);
|
||||
xTaskCreate(ota_handler_task, "OTAHandlerTask", 1024 * 4, NULL, 3, NULL);
|
||||
}
|
||||
@@ -141,6 +133,13 @@ void loop()
|
||||
current_state.telemetryData.heap_used_percent, current_state.telemetryData.uptime_seconds,
|
||||
current_state.telemetryData.temperature);
|
||||
break;
|
||||
case DATA_TYPE_OTA:
|
||||
current_state.otaStatus = dataMessage.data.otaStatus;
|
||||
LOG(ELOG_LEVEL_DEBUG, "Received OTA status: update_available=%d, current=%d.%d.%d, latest=%d.%d.%d",
|
||||
current_state.otaStatus.update_available,
|
||||
current_state.otaStatus.current_version.major, current_state.otaStatus.current_version.minor, current_state.otaStatus.current_version.patch,
|
||||
current_state.otaStatus.latest_version.major, current_state.otaStatus.latest_version.minor, current_state.otaStatus.latest_version.patch);
|
||||
break;
|
||||
default:
|
||||
LOG(ELOG_LEVEL_ERROR, "Unknown data type received");
|
||||
break;
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
#include "../global_data/global_data.h"
|
||||
#include "../tools/tools.h"
|
||||
#include "../tools/readers_writer_lock.h"
|
||||
#include "../tools/ota.h"
|
||||
#include "../global_data/defines.h"
|
||||
#include "json_builder.h"
|
||||
|
||||
extern Preferences prefs;
|
||||
|
||||
AsyncWebSocket webSocket("/webSocket");
|
||||
AsyncWebServer server(80);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ ReadersWriterLock otaStatusLock;
|
||||
* @brief Sets up all routes for the webserver.
|
||||
*
|
||||
* Configures routes for serving HTML pages, handling form submissions,
|
||||
* REST API endpoints, WebSocket connections, and static assets.
|
||||
* REST API endpoints and static assets.
|
||||
*
|
||||
* @note Calls setup_api_endpoints() to configure API-specific routes.
|
||||
*/
|
||||
@@ -74,8 +74,6 @@ void setup_routes() {
|
||||
});
|
||||
|
||||
setup_api_endpoints();
|
||||
webSocket.onEvent(onWsEvent);
|
||||
server.addHandler(&webSocket);
|
||||
|
||||
server.on("/chota.css", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/chota.css", "text/css", false); });
|
||||
server.on("/gauge.js", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/gauge.js", "application/javascript", false); });
|
||||
@@ -331,7 +329,8 @@ void webserver_task(void *pvParameters) {
|
||||
local_ota_status.current_version = {0, 0, 0};
|
||||
local_ota_status.latest_version = {0, 0, 0};
|
||||
local_ota_status.update_progress = -1;
|
||||
local_ota_status.update_url = "UNKNOWN";
|
||||
strncpy(local_ota_status.update_url, "UNKNOWN", sizeof(local_ota_status.update_url) - 1);
|
||||
local_ota_status.update_url[sizeof(local_ota_status.update_url) - 1] = '\0';
|
||||
rwLockReleaseWrite(&otaStatusLock);
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Starting webserver");
|
||||
|
||||
@@ -55,6 +55,7 @@ void read_sensor_task(void* parameter)
|
||||
}
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Starting read sensor tasks");
|
||||
init_sensor();
|
||||
while (true) {
|
||||
// Get Values from sensor
|
||||
#ifndef USE_INA226
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#include <Arduino.h>
|
||||
#include "ota.h"
|
||||
#include <Elog.h>
|
||||
#include "../global_data/defines.h"
|
||||
#include <Preferences.h>
|
||||
#include <fetchOTA.h>
|
||||
#include <utils.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifdef USE_INA226
|
||||
#define BOARD_VARIANT "INA226REV2"
|
||||
#else
|
||||
#define BOARD_VARIANT "INA233REV2"
|
||||
#endif
|
||||
|
||||
extern Preferences prefs;
|
||||
extern Version current_spiffs_version;
|
||||
|
||||
// OTA Callbacks
|
||||
void update_started() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update started");
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update finished, rebooting after 2 seconds");
|
||||
delay(2000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update progress: %d/%d", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
LOG(ELOG_LEVEL_ERROR, "OTA Update error: %d", err);
|
||||
}
|
||||
|
||||
void check_update_task(void* parameter) {
|
||||
// Extract the queue handle from the task parameters
|
||||
QueueHandle_t dataQueue = (QueueHandle_t)parameter;
|
||||
OTAStatus ota_status;
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Starting check Update Task");
|
||||
ota_status.current_version = current_software_version;
|
||||
ota_status.update_progress = -1;
|
||||
|
||||
OTA ota("https://iot.tobiasmaier.me/firmware/waterlevel", current_software_version, BOARD_VARIANT);
|
||||
OTA littlefs_ota("https://iot.tobiasmaier.me/filesystem/waterlevel", REQUIRED_SPIFFS_VERSION, "generic");
|
||||
|
||||
while (true) {
|
||||
// Check if internet connection exists before running update, the web client seems to fail otherwise
|
||||
if (check_for_internet_connection()) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Ping sucessful, starting update checks");
|
||||
check_and_update_littleFS(littlefs_ota);
|
||||
check_for_new_version(ota, &ota_status);
|
||||
} else {
|
||||
LOG(ELOG_LEVEL_WARNING, "Server did not respond to ping, waiting...");
|
||||
}
|
||||
|
||||
DataMessage dataMessage;
|
||||
dataMessage.type = DATA_TYPE_OTA;
|
||||
dataMessage.data.otaStatus = ota_status;
|
||||
|
||||
// Send the OTA data to the queue
|
||||
if (xQueueSend(dataQueue, &dataMessage, 0) != pdTRUE) {
|
||||
LOG(ELOG_LEVEL_ERROR, "Failed to send OTA data to queue");
|
||||
}
|
||||
|
||||
delay(1000 * 60 * 1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_and_update_littleFS(OTA littlefs) {
|
||||
Firmware latest_fs_version = littlefs.getLatestVersionOnServer();
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Required SPIFFS Version: %d.%d.%d; Current SPIFFS version: %d.%d.%d; Server SPIFFS Version: %d.%d.%d", REQUIRED_SPIFFS_VERSION.major, REQUIRED_SPIFFS_VERSION.minor, REQUIRED_SPIFFS_VERSION.patch, current_spiffs_version.major, current_spiffs_version.minor, current_spiffs_version.patch, latest_fs_version.version.major, latest_fs_version.version.minor, latest_fs_version.version.patch);
|
||||
|
||||
if (latest_fs_version.valid) {
|
||||
|
||||
// If we need new version and the server has a new version
|
||||
if (isVersionNewer(current_spiffs_version, REQUIRED_SPIFFS_VERSION) && isVersionNewer(current_spiffs_version, latest_fs_version.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "New SPIFFS version, running update now");
|
||||
run_ota_spiffs_update(latest_fs_version.url, update_started, update_finished, update_progress, update_error);
|
||||
|
||||
// If we do not need a new version but one is available on the server
|
||||
} else if (isVersionNewer(REQUIRED_SPIFFS_VERSION, latest_fs_version.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "New SPIFFS version available: %d.%d.%d, current version: %d.%d.%d but not necessary to update", latest_fs_version.version.major, latest_fs_version.version.minor, latest_fs_version.version.patch, REQUIRED_SPIFFS_VERSION.major, REQUIRED_SPIFFS_VERSION.minor, REQUIRED_SPIFFS_VERSION.patch);
|
||||
|
||||
// If we need a new version but server has no new version
|
||||
} else if (isVersionNewer(current_spiffs_version, REQUIRED_SPIFFS_VERSION)) {
|
||||
LOG(ELOG_LEVEL_ERROR, "New LittleFS Version is needed, but not found on server");
|
||||
|
||||
// Catch case for the rest
|
||||
} else {
|
||||
LOG(ELOG_LEVEL_DEBUG, "No new SPIFFS version available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_for_new_version(OTA ota, OTAStatus *status){
|
||||
Firmware fw = ota.getLatestVersionOnServer();
|
||||
if (fw.valid) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Firmware available on server: %d.%d.%d, current version: %d.%d.%d", fw.version.major, fw.version.minor, fw.version.patch, current_software_version.major, current_software_version.minor, current_software_version.patch);
|
||||
status->latest_version = fw.version;
|
||||
strncpy(status->update_url, fw.url.c_str(), sizeof(status->update_url) - 1);
|
||||
status->update_url[sizeof(status->update_url) - 1] = '\0';
|
||||
if (isVersionNewer(current_software_version, fw.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Remote version is newer than current version");
|
||||
status->update_available = true;
|
||||
} else {
|
||||
status->update_available = false;
|
||||
}
|
||||
} else {
|
||||
if (fw.version.major != 0 && fw.version.minor != 0 && fw.version.patch != 0) {
|
||||
status->latest_version = fw.version;
|
||||
status->update_available = false;
|
||||
}
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "No new firmware available");
|
||||
}
|
||||
}
|
||||
|
||||
bool check_for_internet_connection() {
|
||||
HTTPClient http;
|
||||
http.begin("https://iot.tobiasmaier.me");
|
||||
int code = http.sendRequest("HEAD");
|
||||
if (code > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void run_ota_update_task(void* parameter) {
|
||||
TaskArgs_t *args = (TaskArgs_t *) parameter;
|
||||
LOG(ELOG_LEVEL_DEBUG, "Running OTA upgrade now with URL: %s", args->ota_status.update_url);
|
||||
run_ota_update(args->ota_status.update_url, update_started, update_finished, update_progress, update_error);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// OTA Handler task
|
||||
void ota_handler_task(void *parameter) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Setting up OTA handler");
|
||||
|
||||
// Configure OTA settings
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_SPIFFS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
LOG(ELOG_LEVEL_DEBUG, "Start updating %s", type.c_str());
|
||||
})
|
||||
.onEnd([]() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "\nEnd");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "End Failed");
|
||||
}
|
||||
});
|
||||
// Start the OTA handler
|
||||
ArduinoOTA.begin();
|
||||
|
||||
// Run indefinitely to handle OTA updates
|
||||
while (1) {
|
||||
ArduinoOTA.handle();
|
||||
vTaskDelay(250 / portTICK_PERIOD_MS); // Even more frequent handling
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef OTA_H
|
||||
#define OTA_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <fetchOTA.h>
|
||||
#include "log.h"
|
||||
#include "../global_data/global_data.h"
|
||||
|
||||
// OTA Callback functions
|
||||
void update_started();
|
||||
void update_finished();
|
||||
void update_progress(int cur, int total);
|
||||
void update_error(int err);
|
||||
|
||||
// OTA Update functions
|
||||
void check_update_task(void* parameter);
|
||||
void run_ota_update_task(void* parameter);
|
||||
void check_and_update_littleFS(OTA littlefs);
|
||||
void check_for_new_version(OTA ota, OTAStatus *status);
|
||||
bool check_for_internet_connection();
|
||||
|
||||
// OTA Handler task
|
||||
void ota_handler_task(void *pvParameters);
|
||||
|
||||
typedef struct {
|
||||
OTAStatus ota_status;
|
||||
Version spiffs_version;
|
||||
} TaskArgs_t;
|
||||
|
||||
#endif // OTA_H
|
||||
@@ -1,59 +0,0 @@
|
||||
#include <ArduinoOTA.h>
|
||||
#include <Elog.h>
|
||||
#include "tools/log.h"
|
||||
|
||||
/**
|
||||
* @brief Task to handle ArduinoOTA updates.
|
||||
*
|
||||
* This task initializes the ArduinoOTA library and handles OTA updates.
|
||||
* It runs indefinitely to process OTA update requests.
|
||||
*
|
||||
* @param pvParameters Task parameters (unused).
|
||||
*/
|
||||
void ota_handler_task(void *pvParameters) {
|
||||
(void)pvParameters; // Suppress unused parameter warning
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Setting up OTA handler");
|
||||
|
||||
// Configure OTA settings
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||
type = "sketch";
|
||||
} else { // U_SPIFFS
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
LOG(ELOG_LEVEL_DEBUG, "Start updating %s", type.c_str());
|
||||
})
|
||||
.onEnd([]() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "\nEnd");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Auth Failed");
|
||||
} else if (error == OTA_BEGIN_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Begin Failed");
|
||||
} else if (error == OTA_CONNECT_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Connect Failed");
|
||||
} else if (error == OTA_RECEIVE_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Receive Failed");
|
||||
} else if (error == OTA_END_ERROR) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "End Failed");
|
||||
}
|
||||
});
|
||||
// Start the OTA handler
|
||||
ArduinoOTA.begin();
|
||||
|
||||
// Run indefinitely to handle OTA updates
|
||||
while (1) {
|
||||
ArduinoOTA.handle();
|
||||
vTaskDelay(250 / portTICK_PERIOD_MS); // Even more frequent handling
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef OTA_HANDLER_H
|
||||
#define OTA_HANDLER_H
|
||||
|
||||
/**
|
||||
* @brief Task to handle ArduinoOTA updates.
|
||||
*
|
||||
* This task initializes the ArduinoOTA library and handles OTA updates.
|
||||
* It runs indefinitely to process OTA update requests.
|
||||
*
|
||||
* @param pvParameters Task parameters (unused).
|
||||
*/
|
||||
void ota_handler_task(void *pvParameters);
|
||||
|
||||
#endif // OTA_HANDLER_H
|
||||
@@ -3,23 +3,10 @@
|
||||
#include <Elog.h>
|
||||
#include "../global_data/defines.h"
|
||||
#include <Preferences.h>
|
||||
#include <fetchOTA.h>
|
||||
#include <AsyncWebSocket.h>
|
||||
#include "log.h"
|
||||
#include "esp_sntp.h"
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifdef USE_INA226
|
||||
#define BOARD_VARIANT "INA226REV2"
|
||||
#else
|
||||
#define BOARD_VARIANT "INA233REV2"
|
||||
#endif
|
||||
|
||||
extern Preferences prefs;
|
||||
extern OTAStatus ota_status;
|
||||
extern AsyncWebSocket webSocket;
|
||||
|
||||
extern Version current_spiffs_version;
|
||||
|
||||
String processor(const String& var)
|
||||
{
|
||||
@@ -38,135 +25,6 @@ String processor(const String& var)
|
||||
return String("");
|
||||
}
|
||||
|
||||
// OTA Callbacks
|
||||
void update_started() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update started");
|
||||
ota_status.update_progress = 0;
|
||||
}
|
||||
|
||||
void update_finished() {
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update finished, rebooting after 2 seconds");
|
||||
ota_status.update_progress = -1;
|
||||
webSocket.textAll(String(-1).c_str());
|
||||
delay(2000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void update_progress(int cur, int total) {
|
||||
ota_status.update_progress = 0;
|
||||
if (cur != 0 ) {
|
||||
ota_status.update_progress = int(float(cur)/float(total)*100);
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update progress: %d/%d, %i", cur, total, ota_status.update_progress);
|
||||
}
|
||||
webSocket.textAll(String(ota_status.update_progress).c_str());
|
||||
LOG(ELOG_LEVEL_DEBUG, "OTA Update progress: %d/%d", cur, total);
|
||||
}
|
||||
|
||||
void update_error(int err) {
|
||||
LOG(ELOG_LEVEL_ERROR, "OTA Update error: %d", err);
|
||||
ota_status.update_progress = -2;
|
||||
}
|
||||
|
||||
void check_update_task(void* parameter) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Starting check Update Task");
|
||||
ota_status.current_version = current_software_version;
|
||||
ota_status.update_progress = -1;
|
||||
|
||||
OTA ota("https://iot.tobiasmaier.me/firmware/waterlevel", current_software_version, BOARD_VARIANT);
|
||||
OTA littlefs_ota("https://iot.tobiasmaier.me/filesystem/waterlevel", REQUIRED_SPIFFS_VERSION, "generic");
|
||||
|
||||
while (true) {
|
||||
// Check if internet connection exists before running update, the web client seems to fail otherwise
|
||||
|
||||
if (check_for_internet_connection()) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Ping sucessful, starting update checks");
|
||||
check_and_update_littleFS(littlefs_ota);
|
||||
check_and_update_firmware(ota);
|
||||
} else {
|
||||
LOG(ELOG_LEVEL_WARNING, "Server did not respond to ping, waiting...");
|
||||
}
|
||||
delay(1000 * 60 * 1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_and_update_littleFS(OTA littlefs) {
|
||||
Firmware latest_fs_version = littlefs.getLatestVersionOnServer();
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "Required SPIFFS Version: %d.%d.%d; Current SPIFFS version: %d.%d.%d; Server SPIFFS Version: %d.%d.%d", REQUIRED_SPIFFS_VERSION.major, REQUIRED_SPIFFS_VERSION.minor, REQUIRED_SPIFFS_VERSION.patch, current_spiffs_version.major, current_spiffs_version.minor, current_spiffs_version.patch, latest_fs_version.version.major, latest_fs_version.version.minor, latest_fs_version.version.patch);
|
||||
|
||||
if (latest_fs_version.valid) {
|
||||
|
||||
// If we need new version and the server has a new version
|
||||
if (isVersionNewer(current_spiffs_version, REQUIRED_SPIFFS_VERSION) && isVersionNewer(current_spiffs_version, latest_fs_version.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "New SPIFFS version, running update now");
|
||||
run_ota_spiffs_update(latest_fs_version.url, update_started, update_finished, update_progress, update_error);
|
||||
|
||||
// If we do not need a new version but one is available on the server
|
||||
} else if (isVersionNewer(REQUIRED_SPIFFS_VERSION, latest_fs_version.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "New SPIFFS version available: %d.%d.%d, current version: %d.%d.%d but not necessary to update", latest_fs_version.version.major, latest_fs_version.version.minor, latest_fs_version.version.patch, REQUIRED_SPIFFS_VERSION.major, REQUIRED_SPIFFS_VERSION.minor, REQUIRED_SPIFFS_VERSION.patch);
|
||||
|
||||
// If we need a new version but server has no new version
|
||||
} else if (isVersionNewer(current_spiffs_version, REQUIRED_SPIFFS_VERSION)) {
|
||||
LOG(ELOG_LEVEL_ERROR, "New LittleFS Version is needed, but not found on server");
|
||||
|
||||
// Catch case for the rest
|
||||
} else {
|
||||
LOG(ELOG_LEVEL_DEBUG, "No new SPIFFS version available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_and_update_firmware(OTA ota){
|
||||
Firmware fw = ota.getLatestVersionOnServer();
|
||||
if (fw.valid) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Firmware available on server: %d.%d.%d, current version: %d.%d.%d", fw.version.major, fw.version.minor, fw.version.patch, current_software_version.major, current_software_version.minor, current_software_version.patch);
|
||||
ota_status.latest_version = fw.version;
|
||||
ota_status.update_url = fw.url;
|
||||
if (isVersionNewer(current_software_version, fw.version)) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Remote version is newer than current version");
|
||||
ota_status.update_available = true;
|
||||
} else {
|
||||
ota_status.update_available = false;
|
||||
}
|
||||
} else {
|
||||
if (fw.version.major != 0 && fw.version.minor != 0 && fw.version.patch != 0) {
|
||||
ota_status.latest_version = fw.version;
|
||||
ota_status.update_available = false;
|
||||
}
|
||||
|
||||
LOG(ELOG_LEVEL_DEBUG, "No new firmware available");
|
||||
}
|
||||
}
|
||||
|
||||
bool check_for_internet_connection() {
|
||||
HTTPClient http;
|
||||
http.begin("https://iot.tobiasmaier.me");
|
||||
int code = http.sendRequest("HEAD");
|
||||
if (code > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void run_ota_update_task(void* parameter) {
|
||||
TaskArgs_t *args = (TaskArgs_t *) parameter;
|
||||
LOG(ELOG_LEVEL_DEBUG, "Running OTA upgrade now with URL: %s", args->ota_status.update_url.c_str());
|
||||
run_ota_update(args->ota_status.update_url, update_started, update_finished, update_progress, update_error);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
|
||||
void *arg, uint8_t *data, size_t len) {
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
Serial.println("WebSocket client connected");
|
||||
} else if (type == WS_EVT_DISCONNECT) {
|
||||
Serial.println("WebSocket client disconnected");
|
||||
} else if (type == WS_EVT_DATA) {
|
||||
// Optionally process data received from the client
|
||||
}
|
||||
}
|
||||
|
||||
void get_time_task(void* parameter) {
|
||||
LOG(ELOG_LEVEL_DEBUG, "Starting GetTimeTask");
|
||||
LOG(ELOG_LEVEL_DEBUG, "Trying to get time from Internet");
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
#include <Arduino.h>
|
||||
#include "../global_data/global_data.h"
|
||||
#include <fetchOTA.h>
|
||||
#include <AsyncWebSocket.h>
|
||||
|
||||
|
||||
void printSuffix(Print* _logOutput, int logLevel);
|
||||
void print_prefix(Print* _logOutput, int logLevel);
|
||||
String processor(const String& var);
|
||||
void check_update_task(void* parameter);
|
||||
void run_ota_update_task(void* parameter);
|
||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,void *arg, uint8_t *data, size_t len);
|
||||
void get_time_task(void* parameter);
|
||||
|
||||
void onTimeSync(struct timeval* tv);
|
||||
bool waitForTime();
|
||||
void check_and_update_firmware(OTA fw);
|
||||
void check_and_update_littleFS(OTA littlefs);
|
||||
bool check_for_internet_connection();
|
||||
|
||||
typedef struct {
|
||||
OTAStatus ota_status;
|
||||
Version spiffs_version;
|
||||
} TaskArgs_t;
|
||||
Reference in New Issue
Block a user