mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
241 lines
7.4 KiB
C++
241 lines
7.4 KiB
C++
#include "OtaUpdateActivity.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <WiFi.h>
|
|
|
|
#include "MappedInputManager.h"
|
|
#include "activities/network/WifiSelectionActivity.h"
|
|
#include "components/UITheme.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", "", "");
|
|
GUI.drawButtonHints(renderer, 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();
|
|
}
|
|
}
|