mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 15:17:37 +03:00
## Summary Finally, I have received my device and got to chance to work on OTA. https://github.com/crosspoint-reader/crosspoint-reader/issues/176 * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) Existing OTA functionality is very buggy, many of times (I would say 8 out of 10) are end up with fail for me. When the time that it works it is very slow and take ages. For others looks like end up with crash or different issues. * **What changes are included?** To be honest, I'm not familiar with Arduino APIs of OTA process, but looks like not good as much esp-idf itself. I always found Arduino APIs very bulky for esp32. Wrappers and wrappers. ## Additional Context Right now, OTA takes ~ 3min 10sec (of course depends on size of .bin file). Can be tested with playing version info inside from `platform.ini` file. ``` [crosspoint] version = 0.14.0 ``` --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< NO >**_
240 lines
7.4 KiB
C++
240 lines
7.4 KiB
C++
#include "OtaUpdateActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <WiFi.h>
|
|
|
|
#include "MappedInputManager.h"
|
|
#include "activities/network/WifiSelectionActivity.h"
|
|
#include "fontIds.h"
|
|
#include "network/OtaUpdater.h"
|
|
|
|
void OtaUpdateActivity::taskTrampoline(void* param) {
|
|
auto* self = static_cast<OtaUpdateActivity*>(param);
|
|
self->displayTaskLoop();
|
|
}
|
|
|
|
void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
|
|
exitActivity();
|
|
|
|
if (!success) {
|
|
Serial.printf("[%lu] [OTA] WiFi connection failed, exiting\n", millis());
|
|
goBack();
|
|
return;
|
|
}
|
|
|
|
Serial.printf("[%lu] [OTA] WiFi connected, checking for update\n", millis());
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = CHECKING_FOR_UPDATE;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
const auto res = updater.checkForUpdate();
|
|
if (res != OtaUpdater::OK) {
|
|
Serial.printf("[%lu] [OTA] Update check failed: %d\n", millis(), res);
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = FAILED;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
return;
|
|
}
|
|
|
|
if (!updater.isUpdateNewer()) {
|
|
Serial.printf("[%lu] [OTA] No new update available\n", millis());
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = NO_UPDATE;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
return;
|
|
}
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = WAITING_CONFIRMATION;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
}
|
|
|
|
void OtaUpdateActivity::onEnter() {
|
|
ActivityWithSubactivity::onEnter();
|
|
|
|
renderingMutex = xSemaphoreCreateMutex();
|
|
|
|
xTaskCreate(&OtaUpdateActivity::taskTrampoline, "OtaUpdateActivityTask",
|
|
2048, // Stack size
|
|
this, // Parameters
|
|
1, // Priority
|
|
&displayTaskHandle // Task handle
|
|
);
|
|
|
|
// Turn on WiFi immediately
|
|
Serial.printf("[%lu] [OTA] Turning on WiFi...\n", millis());
|
|
WiFi.mode(WIFI_STA);
|
|
|
|
// Launch WiFi selection subactivity
|
|
Serial.printf("[%lu] [OTA] Launching WifiSelectionActivity...\n", millis());
|
|
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
|
|
[this](const bool connected) { onWifiSelectionComplete(connected); }));
|
|
}
|
|
|
|
void OtaUpdateActivity::onExit() {
|
|
ActivityWithSubactivity::onExit();
|
|
|
|
// Turn off wifi
|
|
WiFi.disconnect(false); // false = don't erase credentials, send disconnect frame
|
|
delay(100); // Allow disconnect frame to be sent
|
|
WiFi.mode(WIFI_OFF);
|
|
delay(100); // Allow WiFi hardware to fully power down
|
|
|
|
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
if (displayTaskHandle) {
|
|
vTaskDelete(displayTaskHandle);
|
|
displayTaskHandle = nullptr;
|
|
}
|
|
vSemaphoreDelete(renderingMutex);
|
|
renderingMutex = nullptr;
|
|
}
|
|
|
|
void OtaUpdateActivity::displayTaskLoop() {
|
|
while (true) {
|
|
if (updateRequired || updater.getRender()) {
|
|
updateRequired = false;
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
render();
|
|
xSemaphoreGive(renderingMutex);
|
|
}
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void OtaUpdateActivity::render() {
|
|
if (subActivity) {
|
|
// Subactivity handles its own rendering
|
|
return;
|
|
}
|
|
|
|
float updaterProgress = 0;
|
|
if (state == UPDATE_IN_PROGRESS) {
|
|
Serial.printf("[%lu] [OTA] Update progress: %d / %d\n", millis(), updater.getProcessedSize(),
|
|
updater.getTotalSize());
|
|
updaterProgress = static_cast<float>(updater.getProcessedSize()) / static_cast<float>(updater.getTotalSize());
|
|
// Only update every 2% at the most
|
|
if (static_cast<int>(updaterProgress * 50) == lastUpdaterPercentage / 2) {
|
|
return;
|
|
}
|
|
lastUpdaterPercentage = static_cast<int>(updaterProgress * 100);
|
|
}
|
|
|
|
const auto pageWidth = renderer.getScreenWidth();
|
|
|
|
renderer.clearScreen();
|
|
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, EpdFontFamily::BOLD);
|
|
|
|
if (state == CHECKING_FOR_UPDATE) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, EpdFontFamily::BOLD);
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
if (state == WAITING_CONFIRMATION) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, EpdFontFamily::BOLD);
|
|
renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION);
|
|
renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str());
|
|
|
|
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
|
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
if (state == UPDATE_IN_PROGRESS) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, EpdFontFamily::BOLD);
|
|
renderer.drawRect(20, 350, pageWidth - 40, 50);
|
|
renderer.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 420,
|
|
(std::to_string(static_cast<int>(updaterProgress * 100)) + "%").c_str());
|
|
renderer.drawCenteredText(
|
|
UI_10_FONT_ID, 440,
|
|
(std::to_string(updater.getProcessedSize()) + " / " + std::to_string(updater.getTotalSize())).c_str());
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
if (state == NO_UPDATE) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, EpdFontFamily::BOLD);
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
if (state == FAILED) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, EpdFontFamily::BOLD);
|
|
renderer.displayBuffer();
|
|
return;
|
|
}
|
|
|
|
if (state == FINISHED) {
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update complete", true, EpdFontFamily::BOLD);
|
|
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Press and hold power button to turn back on");
|
|
renderer.displayBuffer();
|
|
state = SHUTTING_DOWN;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void OtaUpdateActivity::loop() {
|
|
if (subActivity) {
|
|
subActivity->loop();
|
|
return;
|
|
}
|
|
|
|
if (state == WAITING_CONFIRMATION) {
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
|
Serial.printf("[%lu] [OTA] New update available, starting download...\n", millis());
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = UPDATE_IN_PROGRESS;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
const auto res = updater.installUpdate();
|
|
|
|
if (res != OtaUpdater::OK) {
|
|
Serial.printf("[%lu] [OTA] Update failed: %d\n", millis(), res);
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = FAILED;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
return;
|
|
}
|
|
|
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
state = FINISHED;
|
|
xSemaphoreGive(renderingMutex);
|
|
updateRequired = true;
|
|
}
|
|
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
|
goBack();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (state == FAILED) {
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
|
goBack();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (state == NO_UPDATE) {
|
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
|
goBack();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (state == SHUTTING_DOWN) {
|
|
ESP.restart();
|
|
}
|
|
}
|