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) {
|
function fetchUpdateData(gauge) {
|
||||||
const apiUrl = '/ota_udpate_status';
|
const apiUrl = '/ota_update_status';
|
||||||
// Fetching data from the API
|
// Fetching data from the API
|
||||||
fetch(apiUrl)
|
fetch(apiUrl)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
|||||||
+26
-38
@@ -15,44 +15,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const webSocket = new WebSocket('webSocket://' + window.location.host + '/webSocket');
|
// 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() {
|
function checkServerAndRedirect() {
|
||||||
console.log('WebSocket connection opened.');
|
// Try to fetch the home page to check if server is up
|
||||||
};
|
fetch('/', {
|
||||||
|
method: 'GET',
|
||||||
webSocket.onmessage = function(event) {
|
cache: 'no-cache'
|
||||||
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')
|
|
||||||
.then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
clearInterval(checkStatus);
|
|
||||||
window.location.href = '/';
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error checking OTA update status:', error));
|
.then(response => {
|
||||||
}, 1000);
|
// If we get a response (server is up), redirect to home page
|
||||||
} else {
|
if (response.ok) {
|
||||||
document.getElementById('progress').textContent = progress + '%';
|
window.location.href = '/';
|
||||||
}
|
} else {
|
||||||
|
// Server responded but with an error, wait and retry
|
||||||
|
setTimeout(checkServerAndRedirect, 5000); // 5 seconds = 5000 ms
|
||||||
};
|
}
|
||||||
|
})
|
||||||
webSocket.onerror = function(error) {
|
.catch(error => {
|
||||||
console.error('WebSocket error:', error);
|
// Fetch failed (server not up yet), wait and retry
|
||||||
};
|
setTimeout(checkServerAndRedirect, 5000); // 5 seconds = 5000 ms
|
||||||
|
});
|
||||||
webSocket.onclose = function() {
|
}
|
||||||
console.log('WebSocket connection closed.');
|
</script>
|
||||||
};
|
</body>
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ struct OTAStatus {
|
|||||||
Version current_version; /**< Current version of the firmware */
|
Version current_version; /**< Current version of the firmware */
|
||||||
Version latest_version; /**< Latest available version of the firmware */
|
Version latest_version; /**< Latest available version of the firmware */
|
||||||
int update_progress; /**< Progress of the update as a percentage */
|
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_WATER,
|
||||||
DATA_TYPE_WIFI,
|
DATA_TYPE_WIFI,
|
||||||
DATA_TYPE_ETHERNET,
|
DATA_TYPE_ETHERNET,
|
||||||
DATA_TYPE_TELEMETRY
|
DATA_TYPE_TELEMETRY,
|
||||||
|
DATA_TYPE_OTA
|
||||||
} DataType;
|
} DataType;
|
||||||
|
|
||||||
// Union to hold different types of data
|
// Union to hold different types of data
|
||||||
@@ -105,6 +106,7 @@ typedef union {
|
|||||||
WaterData waterData;
|
WaterData waterData;
|
||||||
NetworkData networkData;
|
NetworkData networkData;
|
||||||
DeviceTelemetry telemetryData;
|
DeviceTelemetry telemetryData;
|
||||||
|
OTAStatus otaStatus;
|
||||||
} DataUnion;
|
} DataUnion;
|
||||||
|
|
||||||
// Structure to hold data type and the actual data
|
// Structure to hold data type and the actual data
|
||||||
|
|||||||
+9
-10
@@ -26,7 +26,7 @@
|
|||||||
#include <fetchOTA.h>
|
#include <fetchOTA.h>
|
||||||
#include "time.h"
|
#include "time.h"
|
||||||
#include "tools/log.h"
|
#include "tools/log.h"
|
||||||
#include "tools/ota_handler.h"
|
#include "tools/ota.h"
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
@@ -35,8 +35,6 @@
|
|||||||
|
|
||||||
Preferences prefs;
|
Preferences prefs;
|
||||||
|
|
||||||
extern AsyncWebSocket webSocket;
|
|
||||||
|
|
||||||
Version current_spiffs_version;
|
Version current_spiffs_version;
|
||||||
|
|
||||||
// Variable to store the current state of the system
|
// Variable to store the current state of the system
|
||||||
@@ -65,10 +63,6 @@ void setup()
|
|||||||
prefs.begin("waterlevel", false);
|
prefs.begin("waterlevel", false);
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
delay(500);
|
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Init Sensor");
|
|
||||||
init_sensor();
|
|
||||||
|
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Beginning LittleFS");
|
LOG(ELOG_LEVEL_DEBUG, "Beginning LittleFS");
|
||||||
LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED);
|
LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED);
|
||||||
|
|
||||||
@@ -95,9 +89,7 @@ void setup()
|
|||||||
xTaskCreate(collect_internal_telemetry_task, "InternalTelemetryTask", 1024 * 2, dataQueue, 1, NULL);
|
xTaskCreate(collect_internal_telemetry_task, "InternalTelemetryTask", 1024 * 2, dataQueue, 1, NULL);
|
||||||
xTaskCreate(display_task, "DisplayTask", 1024 * 2, NULL, 1, NULL);
|
xTaskCreate(display_task, "DisplayTask", 1024 * 2, NULL, 1, NULL);
|
||||||
xTaskCreate(get_time_task, "GetTimeTask", 1024 * 2, NULL, 1, NULL);
|
xTaskCreate(get_time_task, "GetTimeTask", 1024 * 2, NULL, 1, NULL);
|
||||||
|
xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 6, dataQueue, 1, NULL);
|
||||||
delay(5000);
|
|
||||||
xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 6, NULL, 1, NULL);
|
|
||||||
xTaskCreate(webserver_task, "WebServerTask", 1024 * 4, stateForWebserverQueue, 1, NULL);
|
xTaskCreate(webserver_task, "WebServerTask", 1024 * 4, stateForWebserverQueue, 1, NULL);
|
||||||
xTaskCreate(ota_handler_task, "OTAHandlerTask", 1024 * 4, NULL, 3, 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.heap_used_percent, current_state.telemetryData.uptime_seconds,
|
||||||
current_state.telemetryData.temperature);
|
current_state.telemetryData.temperature);
|
||||||
break;
|
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:
|
default:
|
||||||
LOG(ELOG_LEVEL_ERROR, "Unknown data type received");
|
LOG(ELOG_LEVEL_ERROR, "Unknown data type received");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -17,12 +17,12 @@
|
|||||||
#include "../global_data/global_data.h"
|
#include "../global_data/global_data.h"
|
||||||
#include "../tools/tools.h"
|
#include "../tools/tools.h"
|
||||||
#include "../tools/readers_writer_lock.h"
|
#include "../tools/readers_writer_lock.h"
|
||||||
|
#include "../tools/ota.h"
|
||||||
#include "../global_data/defines.h"
|
#include "../global_data/defines.h"
|
||||||
#include "json_builder.h"
|
#include "json_builder.h"
|
||||||
|
|
||||||
extern Preferences prefs;
|
extern Preferences prefs;
|
||||||
|
|
||||||
AsyncWebSocket webSocket("/webSocket");
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ ReadersWriterLock otaStatusLock;
|
|||||||
* @brief Sets up all routes for the webserver.
|
* @brief Sets up all routes for the webserver.
|
||||||
*
|
*
|
||||||
* Configures routes for serving HTML pages, handling form submissions,
|
* 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.
|
* @note Calls setup_api_endpoints() to configure API-specific routes.
|
||||||
*/
|
*/
|
||||||
@@ -74,8 +74,6 @@ void setup_routes() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setup_api_endpoints();
|
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("/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); });
|
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.current_version = {0, 0, 0};
|
||||||
local_ota_status.latest_version = {0, 0, 0};
|
local_ota_status.latest_version = {0, 0, 0};
|
||||||
local_ota_status.update_progress = -1;
|
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);
|
rwLockReleaseWrite(&otaStatusLock);
|
||||||
|
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Starting webserver");
|
LOG(ELOG_LEVEL_DEBUG, "Starting webserver");
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ void read_sensor_task(void* parameter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Starting read sensor tasks");
|
LOG(ELOG_LEVEL_DEBUG, "Starting read sensor tasks");
|
||||||
|
init_sensor();
|
||||||
while (true) {
|
while (true) {
|
||||||
// Get Values from sensor
|
// Get Values from sensor
|
||||||
#ifndef USE_INA226
|
#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 <Elog.h>
|
||||||
#include "../global_data/defines.h"
|
#include "../global_data/defines.h"
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <fetchOTA.h>
|
|
||||||
#include <AsyncWebSocket.h>
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "esp_sntp.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 Preferences prefs;
|
||||||
extern OTAStatus ota_status;
|
|
||||||
extern AsyncWebSocket webSocket;
|
|
||||||
|
|
||||||
extern Version current_spiffs_version;
|
|
||||||
|
|
||||||
String processor(const String& var)
|
String processor(const String& var)
|
||||||
{
|
{
|
||||||
@@ -38,135 +25,6 @@ String processor(const String& var)
|
|||||||
return String("");
|
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) {
|
void get_time_task(void* parameter) {
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Starting GetTimeTask");
|
LOG(ELOG_LEVEL_DEBUG, "Starting GetTimeTask");
|
||||||
LOG(ELOG_LEVEL_DEBUG, "Trying to get time from Internet");
|
LOG(ELOG_LEVEL_DEBUG, "Trying to get time from Internet");
|
||||||
|
|||||||
+1
-14
@@ -1,24 +1,11 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../global_data/global_data.h"
|
#include "../global_data/global_data.h"
|
||||||
#include <fetchOTA.h>
|
|
||||||
#include <AsyncWebSocket.h>
|
|
||||||
|
|
||||||
|
|
||||||
void printSuffix(Print* _logOutput, int logLevel);
|
void printSuffix(Print* _logOutput, int logLevel);
|
||||||
void print_prefix(Print* _logOutput, int logLevel);
|
void print_prefix(Print* _logOutput, int logLevel);
|
||||||
String processor(const String& var);
|
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 get_time_task(void* parameter);
|
||||||
|
|
||||||
void onTimeSync(struct timeval* tv);
|
void onTimeSync(struct timeval* tv);
|
||||||
bool waitForTime();
|
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