OTA memery allocation error msg
This commit is contained in:
@@ -248,7 +248,7 @@ static const char* const I18N_EN[] = {
|
|||||||
"Already up to date: v",
|
"Already up to date: v",
|
||||||
"Update available: v",
|
"Update available: v",
|
||||||
"A new firmware update will be downloaded and installed. Please don't interrupt the WiFi connection or power supply during this process. You'll be asked to restart once installation is complete. If the WebUI page (logbook/radar) is open in a browser, please close it first - it regularly polls the device for data and can interfere with the download otherwise.",
|
"A new firmware update will be downloaded and installed. Please don't interrupt the WiFi connection or power supply during this process. You'll be asked to restart once installation is complete. If the WebUI page (logbook/radar) is open in a browser, please close it first - it regularly polls the device for data and can interfere with the download otherwise.",
|
||||||
"Installing update ",
|
"Install update ",
|
||||||
"Update failed",
|
"Update failed",
|
||||||
"Update installed",
|
"Update installed",
|
||||||
"The new firmware was installed successfully. Please restart now to start using it.",
|
"The new firmware was installed successfully. Please restart now to start using it.",
|
||||||
|
|||||||
83
src/main.cpp
83
src/main.cpp
@@ -51,6 +51,7 @@ uint32_t lastPollMs = 0;
|
|||||||
uint32_t lastSweepMs = 0;
|
uint32_t lastSweepMs = 0;
|
||||||
bool gOtaUpdateAvailable = false;
|
bool gOtaUpdateAvailable = false;
|
||||||
char gOtaLatestVersion[16] = {0};
|
char gOtaLatestVersion[16] = {0};
|
||||||
|
char gOtaDownloadUrl[192] = {0};
|
||||||
uint32_t lastStatusLineMs = 0;
|
uint32_t lastStatusLineMs = 0;
|
||||||
uint32_t lastRenderedVersion = 0xFFFFFFFF;
|
uint32_t lastRenderedVersion = 0xFFFFFFFF;
|
||||||
Weather::Condition lastRenderedWeather = Weather::Condition::Unknown;
|
Weather::Condition lastRenderedWeather = Weather::Condition::Unknown;
|
||||||
@@ -650,6 +651,7 @@ tzset();
|
|||||||
if (otaInfo.result == OtaUpdate::CheckResult::UpdateAvailable) {
|
if (otaInfo.result == OtaUpdate::CheckResult::UpdateAvailable) {
|
||||||
gOtaUpdateAvailable = true;
|
gOtaUpdateAvailable = true;
|
||||||
strncpy(gOtaLatestVersion, otaInfo.latestVersion, sizeof(gOtaLatestVersion) - 1);
|
strncpy(gOtaLatestVersion, otaInfo.latestVersion, sizeof(gOtaLatestVersion) - 1);
|
||||||
|
strncpy(gOtaDownloadUrl, otaInfo.downloadUrl, sizeof(gOtaDownloadUrl) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SplashScreen::setStatusLine(tft, 1, "");
|
SplashScreen::setStatusLine(tft, 1, "");
|
||||||
@@ -663,24 +665,93 @@ tzset();
|
|||||||
SplashScreen::waitRemaining(tft);
|
SplashScreen::waitRemaining(tft);
|
||||||
|
|
||||||
if (gOtaUpdateAvailable) {
|
if (gOtaUpdateAvailable) {
|
||||||
|
struct Rect { int16_t x, y, w, h;
|
||||||
|
bool contains(int16_t px, int16_t py) const {
|
||||||
|
return px >= x && px < x + w && py >= y && py < y + h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto drawBtn = [](Rect r, const String& label, uint16_t color) {
|
||||||
|
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
|
||||||
|
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, color);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
tft.setTextColor(color, TFT_BLACK);
|
||||||
|
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
|
||||||
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
};
|
||||||
|
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
tft.setTextDatum(MC_DATUM);
|
tft.setTextDatum(MC_DATUM);
|
||||||
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
|
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
|
||||||
tft.setTextSize(1);
|
tft.setTextSize(2);
|
||||||
tft.drawString(String(I18n::t(StringId::OTA_UPDATE_AVAILABLE_PREFIX)) + gOtaLatestVersion,
|
tft.drawString(String(I18n::t(StringId::OTA_UPDATE_AVAILABLE_PREFIX)) + gOtaLatestVersion,
|
||||||
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 - 20);
|
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 - 50);
|
||||||
tft.setTextSize(1);
|
tft.setTextSize(1);
|
||||||
tft.setTextColor(TFT_DARKGREY, TFT_BLACK);
|
|
||||||
tft.drawString(String(I18n::t(StringId::MENU_CATEGORY_SYSTEM)) + " > " + String(I18n::t(StringId::MENU_CHECK_UPDATE)),
|
int16_t btnW = 100, btnH = 40, btnY = Config::SCREEN_HEIGHT / 2 + 10;
|
||||||
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 + 10);
|
int16_t gap = 16;
|
||||||
|
int16_t totalW = btnW * 2 + gap;
|
||||||
|
int16_t startX = (Config::SCREEN_WIDTH - totalW) / 2;
|
||||||
|
Rect installBtn = {startX, btnY, btnW, btnH};
|
||||||
|
Rect dismissBtn = {startX + btnW + gap, btnY, btnW, btnH};
|
||||||
|
drawBtn(installBtn, I18n::t(StringId::OTA_INSTALLING_PREFIX), TFT_GREEN);
|
||||||
|
drawBtn(dismissBtn, "Dismiss", TFT_RED);
|
||||||
|
|
||||||
tft.setTextDatum(TL_DATUM);
|
tft.setTextDatum(TL_DATUM);
|
||||||
MenuStars::reset();
|
MenuStars::reset();
|
||||||
TouchInput::Point tap;
|
TouchInput::Point tap;
|
||||||
|
bool installChosen = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (TouchInput::wasTapped(tap)) break;
|
if (TouchInput::wasTapped(tap)) {
|
||||||
|
if (installBtn.contains(tap.x, tap.y)) { installChosen = true; break; }
|
||||||
|
if (dismissBtn.contains(tap.x, tap.y)) break;
|
||||||
|
}
|
||||||
MenuStars::update(tft);
|
MenuStars::update(tft);
|
||||||
delay(20);
|
delay(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (installChosen) {
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
tft.setTextSize(1);
|
||||||
|
constexpr int16_t PROG_Y = Config::SCREEN_HEIGHT / 2 + 10;
|
||||||
|
auto progressFn = [](uint8_t pct) {
|
||||||
|
tft.fillRect(0, PROG_Y, Config::SCREEN_WIDTH, 20, TFT_BLACK);
|
||||||
|
char buf[8]; snprintf(buf, sizeof(buf), "%d%%", pct);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||||
|
tft.drawString(buf, Config::SCREEN_WIDTH / 2, PROG_Y + 2);
|
||||||
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
};
|
||||||
|
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||||
|
tft.drawString(String(I18n::t(StringId::OTA_INSTALLING_PREFIX)) + gOtaLatestVersion + "...",
|
||||||
|
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 - 20);
|
||||||
|
bool ok = OtaUpdate::performUpdate(gOtaDownloadUrl, progressFn);
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
if (ok) {
|
||||||
|
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||||
|
tft.drawString(I18n::t(StringId::OTA_UPDATE_SUCCESS),
|
||||||
|
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 - 20);
|
||||||
|
Rect restartBtn = {startX, btnY, btnW, btnH};
|
||||||
|
drawBtn(restartBtn, I18n::t(StringId::OTA_RESTART_BUTTON), TFT_GREEN);
|
||||||
|
while (true) {
|
||||||
|
if (TouchInput::wasTapped(tap) && restartBtn.contains(tap.x, tap.y)) {
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
delay(20);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tft.setTextColor(TFT_RED, TFT_BLACK);
|
||||||
|
tft.drawString(I18n::t(StringId::OTA_UPDATE_FAILED),
|
||||||
|
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 - 20);
|
||||||
|
tft.drawString("Dismiss",
|
||||||
|
Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT / 2 + 10);
|
||||||
|
while (true) {
|
||||||
|
if (TouchInput::wasTapped(tap)) break;
|
||||||
|
delay(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawHeader();
|
drawHeader();
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ namespace {
|
|||||||
NetTask::resume();
|
NetTask::resume();
|
||||||
|
|
||||||
if (info.result == OtaUpdate::CheckResult::Error) {
|
if (info.result == OtaUpdate::CheckResult::Error) {
|
||||||
infoScreen(tft, I18n::t(StringId::OTA_CHECK_FAILED), "", TFT_RED, I18n::t(StringId::OK));
|
infoScreen(tft, I18n::t(StringId::OTA_CHECK_FAILED), info.errorMsg, TFT_RED, I18n::t(StringId::OK));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (info.result == OtaUpdate::CheckResult::UpToDate) {
|
if (info.result == OtaUpdate::CheckResult::UpToDate) {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ CheckInfo checkForUpdate() {
|
|||||||
http.setTimeout(8000);
|
http.setTimeout(8000);
|
||||||
if (!http.begin(client, url)) {
|
if (!http.begin(client, url)) {
|
||||||
Serial.println("[OTA] Check failed: http.begin() returned false.");
|
Serial.println("[OTA] Check failed: http.begin() returned false.");
|
||||||
|
strncpy(info.errorMsg, "Could not start HTTPS connection (low memory?)", sizeof(info.errorMsg) - 1);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
addAuthHeader(http);
|
addAuthHeader(http);
|
||||||
@@ -69,10 +70,12 @@ CheckInfo checkForUpdate() {
|
|||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
if (code != HTTP_CODE_OK) {
|
if (code != HTTP_CODE_OK) {
|
||||||
if (code < 0) {
|
if (code < 0) {
|
||||||
Serial.printf("[OTA] Check failed: HTTP error=%d (%s)\n", code,
|
String errStr = HTTPClient::errorToString(code);
|
||||||
HTTPClient::errorToString(code).c_str());
|
Serial.printf("[OTA] Check failed: HTTP error=%d (%s)\n", code, errStr.c_str());
|
||||||
|
snprintf(info.errorMsg, sizeof(info.errorMsg), "HTTPS connection error: %s", errStr.c_str());
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("[OTA] Check failed: HTTP status=%d\n", code);
|
Serial.printf("[OTA] Check failed: HTTP status=%d\n", code);
|
||||||
|
snprintf(info.errorMsg, sizeof(info.errorMsg), "Server returned HTTP %d", code);
|
||||||
}
|
}
|
||||||
http.end();
|
http.end();
|
||||||
return info;
|
return info;
|
||||||
@@ -84,12 +87,14 @@ CheckInfo checkForUpdate() {
|
|||||||
DeserializationError jsonErr = deserializeJson(doc, body);
|
DeserializationError jsonErr = deserializeJson(doc, body);
|
||||||
if (jsonErr) {
|
if (jsonErr) {
|
||||||
Serial.printf("[OTA] Check failed: JSON error (%s)\n", jsonErr.c_str());
|
Serial.printf("[OTA] Check failed: JSON error (%s)\n", jsonErr.c_str());
|
||||||
|
snprintf(info.errorMsg, sizeof(info.errorMsg), "Invalid response from server");
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* tag = doc["tag_name"] | "";
|
const char* tag = doc["tag_name"] | "";
|
||||||
if (!tag[0]) {
|
if (!tag[0]) {
|
||||||
Serial.println("[OTA] Check failed: no tag_name in release JSON.");
|
Serial.println("[OTA] Check failed: no tag_name in release JSON.");
|
||||||
|
snprintf(info.errorMsg, sizeof(info.errorMsg), "No release found on git.rasmusbendtsen.dk");
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ namespace OtaUpdate {
|
|||||||
// Direct download link to the firmware.bin of the release - only
|
// Direct download link to the firmware.bin of the release - only
|
||||||
// set when result == UpdateAvailable.
|
// set when result == UpdateAvailable.
|
||||||
char downloadUrl[192] = {0};
|
char downloadUrl[192] = {0};
|
||||||
|
// Human-readable error explanation (set when result == Error).
|
||||||
|
// e.g. "TLS handshake failed (low memory)" or "HTTP 401".
|
||||||
|
char errorMsg[64] = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Queries the Gitea Releases API for the latest release, compares
|
// Queries the Gitea Releases API for the latest release, compares
|
||||||
|
|||||||
Reference in New Issue
Block a user