ESP32-PaxCounter/src/ota.cpp

349 lines
10 KiB
C++
Raw Normal View History

2019-03-24 00:15:04 +01:00
#if (USE_OTA)
2018-09-24 16:36:11 +02:00
2018-09-15 16:29:52 +02:00
/*
Parts of this code:
Copyright (c) 2014-present PlatformIO <contact@platformio.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2018-09-21 19:30:02 +02:00
#include "ota.h"
2018-08-12 15:42:58 +02:00
2018-09-22 19:39:31 +02:00
using namespace std;
2018-08-12 15:42:58 +02:00
const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
2018-09-15 16:29:52 +02:00
// Connection port (HTTPS)
const int port = 443;
2018-09-15 16:29:52 +02:00
// Variables to validate firmware content
2018-09-23 22:12:10 +02:00
int volatile contentLength = 0;
bool volatile isValidContentType = false;
2018-08-16 21:10:13 +02:00
2018-09-15 16:29:52 +02:00
// Local logging tag
2019-02-27 00:49:32 +01:00
static const char TAG[] = __FILE__;
2018-09-23 18:07:40 +02:00
// helper function to extract header value from header
inline String getHeaderValue(String header, String headerName) {
return header.substring(strlen(headerName.c_str()));
2018-09-23 15:07:00 +02:00
}
2018-09-15 16:29:52 +02:00
void start_ota_update() {
// check battery status if we can before doing ota
2019-09-20 19:45:37 +02:00
if (!batt_sufficient()) {
2018-11-05 21:40:07 +01:00
ESP_LOGE(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
return;
}
2018-09-23 18:45:46 +02:00
switch_LED(LED_ON);
2018-09-23 15:07:00 +02:00
2019-03-13 22:08:05 +01:00
#ifdef HAS_DISPLAY
2019-09-27 12:47:00 +02:00
// init display
#ifndef DISPLAY_FLIP
int rc = oledInit(OLED_128x64, ANGLE_0, false, -1, -1, 100000L);
#else
int rc = oledInit(OLED_128x64, ANGLE_FLIPY, false, -1, -1, 100000L);
2018-09-22 19:39:31 +02:00
#endif
2019-09-27 12:47:00 +02:00
oledFill(0, 1);
dp_printf(0, 0, 0, 1, "SOFTWARE UPDATE");
dp_printf(0, 1, 0, 0, "WiFi connect ..");
dp_printf(0, 2, 0, 0, "Has Update? ..");
dp_printf(0, 3, 0, 0, "Fetching ..");
dp_printf(0, 4, 0, 0, "Downloading ..");
dp_printf(0, 5, 0, 0, "Rebooting ..");
2019-09-27 13:03:55 +02:00
#endif
2019-09-27 12:47:00 +02:00
2018-09-15 16:29:52 +02:00
ESP_LOGI(TAG, "Starting Wifi OTA update");
2019-09-27 12:47:00 +02:00
ota_display(1, "**", WIFI_SSID);
WiFi.mode(WIFI_STA);
2018-09-15 16:29:52 +02:00
WiFi.begin(WIFI_SSID, WIFI_PASS);
2018-11-27 21:31:20 +01:00
uint8_t i = WIFI_MAX_TRY;
int ret = 1; // 0 = finished, 1 = retry, -1 = abort
2018-09-23 15:07:00 +02:00
2018-09-16 12:18:11 +02:00
while (i--) {
2018-11-27 21:31:20 +01:00
ESP_LOGI(TAG, "Trying to connect to %s, attempt %u of %u", WIFI_SSID,
WIFI_MAX_TRY - i, WIFI_MAX_TRY);
2018-12-19 12:32:25 +01:00
delay(10000); // wait for stable connect
if (WiFi.status() == WL_CONNECTED) {
// we now have wifi connection and try to do an OTA over wifi update
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
2019-09-27 12:47:00 +02:00
ota_display(1, "OK", "WiFi connected");
2018-11-04 19:54:09 +01:00
// do a number of tries to update firmware limited by OTA_MAX_TRY
2018-11-27 21:31:20 +01:00
uint8_t j = OTA_MAX_TRY;
2018-11-05 00:30:32 +01:00
while ((j--) && (ret > 0)) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Starting OTA update, attempt %u of %u", OTA_MAX_TRY - j,
OTA_MAX_TRY);
2018-11-04 19:54:09 +01:00
ret = do_ota_update();
2018-11-04 20:51:48 +01:00
}
2018-11-27 21:31:20 +01:00
if (WiFi.status() == WL_CONNECTED)
goto end; // OTA update finished or OTA max attemps reached
}
2018-11-05 16:06:17 +01:00
WiFi.reconnect();
2018-09-22 19:39:31 +02:00
}
2018-08-12 15:42:58 +02:00
2018-11-04 19:54:09 +01:00
// wifi did not connect
ESP_LOGI(TAG, "Could not connect to %s", WIFI_SSID);
2019-09-27 12:47:00 +02:00
ota_display(1, " E", "no WiFi connect");
2018-12-19 12:32:25 +01:00
delay(5000);
2018-09-23 15:07:00 +02:00
end:
switch_LED(LED_OFF);
2018-11-05 00:32:38 +01:00
ESP_LOGI(TAG, "Rebooting to %s firmware", (ret == 0) ? "new" : "current");
2019-09-27 12:47:00 +02:00
ota_display(5, "**", ""); // mark line rebooting
2018-12-19 12:32:25 +01:00
delay(5000);
2018-09-23 15:07:00 +02:00
ESP.restart();
2018-09-15 16:29:52 +02:00
} // start_ota_update
// Reads data vom wifi client and flashes it to ota partition
// returns: 0 = finished, 1 = retry, -1 = abort
int do_ota_update() {
2018-09-23 18:07:40 +02:00
char buf[17];
bool redirect = true;
size_t written = 0;
2018-09-23 18:07:40 +02:00
2018-09-15 16:29:52 +02:00
// Fetch the latest firmware version
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Checking latest firmware version on server");
2019-09-27 12:47:00 +02:00
ota_display(2, "**", "checking version");
2018-11-20 21:03:03 +01:00
if (WiFi.status() != WL_CONNECTED)
return 1;
2018-09-15 16:29:52 +02:00
const String latest = bintray.getLatestVersion();
2018-09-17 17:23:02 +02:00
2018-09-15 16:29:52 +02:00
if (latest.length() == 0) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Could not fetch info on latest firmware");
2019-09-27 12:47:00 +02:00
ota_display(2, " E", "file not found");
2018-11-05 00:30:32 +01:00
return -1;
2018-09-17 17:23:02 +02:00
} else if (version_compare(latest, cfg.version) <= 0) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Current firmware is up to date");
2019-09-27 12:47:00 +02:00
ota_display(2, "NO", "no update found");
return -1;
2018-09-15 16:29:52 +02:00
}
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "New firmware version v%s available", latest.c_str());
2019-09-27 12:47:00 +02:00
ota_display(2, "OK", latest.c_str());
2018-09-23 18:45:46 +02:00
2019-09-27 12:47:00 +02:00
ota_display(3, "**", "");
2018-11-20 21:03:03 +01:00
if (WiFi.status() != WL_CONNECTED)
return 1;
2018-09-23 18:07:40 +02:00
String firmwarePath = bintray.getBinaryPath(latest);
2018-09-15 16:29:52 +02:00
if (!firmwarePath.endsWith(".bin")) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Unsupported binary format");
2019-09-27 12:47:00 +02:00
ota_display(3, " E", "file type error");
2018-11-05 00:30:32 +01:00
return -1;
2018-09-15 16:29:52 +02:00
}
2018-08-12 15:42:58 +02:00
2018-09-15 16:29:52 +02:00
String currentHost = bintray.getStorageHost();
String prevHost = currentHost;
WiFiClientSecure client;
2018-11-05 00:03:47 +01:00
2018-09-15 16:29:52 +02:00
client.setCACert(bintray.getCertificate(currentHost));
client.setTimeout(RESPONSE_TIMEOUT_MS);
2018-09-15 16:29:52 +02:00
if (!client.connect(currentHost.c_str(), port)) {
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
2019-09-27 12:47:00 +02:00
ota_display(3, " E", "connection lost");
2018-11-05 00:30:32 +01:00
goto abort;
2018-09-15 16:29:52 +02:00
}
while (redirect) {
if (currentHost != prevHost) {
client.stop();
client.setCACert(bintray.getCertificate(currentHost));
if (!client.connect(currentHost.c_str(), port)) {
2018-09-15 21:10:11 +02:00
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
2018-09-15 16:29:52 +02:00
currentHost.c_str());
2019-09-27 12:47:00 +02:00
ota_display(3, " E", "server error");
2018-11-05 00:30:32 +01:00
goto abort;
2018-09-15 16:29:52 +02:00
}
}
2018-09-19 01:35:20 +02:00
ESP_LOGI(TAG, "Requesting %s", firmwarePath.c_str());
2018-09-15 16:29:52 +02:00
client.print(String("GET ") + firmwarePath + " HTTP/1.1\r\n");
client.print(String("Host: ") + currentHost + "\r\n");
client.print("Cache-Control: no-cache\r\n");
client.print("Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if ((millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Client timeout");
2019-09-27 12:47:00 +02:00
ota_display(3, " E", "client timeout");
2018-11-05 00:30:32 +01:00
goto abort;
2018-09-15 16:29:52 +02:00
}
}
while (client.available()) {
String line = client.readStringUntil('\n');
// Check if the line is end of headers by removing space symbol
line.trim();
// if the the line is empty, this is the end of the headers
if (!line.length()) {
break; // proceed to OTA update
}
// Check allowed HTTP responses
if (line.startsWith("HTTP/1.1")) {
if (line.indexOf("200") > 0) {
ESP_LOGI(TAG, "Got 200 status code from server. Proceeding to "
"firmware flashing");
redirect = false;
} else if (line.indexOf("302") > 0) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to "
2018-09-15 16:29:52 +02:00
"new address");
redirect = true;
} else {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Could not get firmware download URL");
goto retry;
2018-09-15 16:29:52 +02:00
}
}
// Extracting new redirect location
if (line.startsWith("Location: ")) {
String newUrl = getHeaderValue(line, "Location: ");
ESP_LOGI(TAG, "Got new url: %s", newUrl.c_str());
newUrl.remove(0, newUrl.indexOf("//") + 2);
currentHost = newUrl.substring(0, newUrl.indexOf('/'));
newUrl.remove(newUrl.indexOf(currentHost), currentHost.length());
firmwarePath = newUrl;
continue;
}
// Checking headers
if (line.startsWith("Content-Length: ")) {
contentLength =
atoi((getHeaderValue(line, "Content-Length: ")).c_str());
ESP_LOGI(TAG, "Got %d bytes from server", contentLength);
}
if (line.startsWith("Content-Type: ")) {
String contentType = getHeaderValue(line, "Content-Type: ");
ESP_LOGI(TAG, "Got %s payload", contentType.c_str());
if (contentType == "application/octet-stream") {
isValidContentType = true;
}
}
} // while (client.available())
} // while (redirect)
2018-09-15 16:29:52 +02:00
2019-09-27 12:47:00 +02:00
ota_display(3, "OK", ""); // line download
2018-09-22 19:39:31 +02:00
2018-09-15 16:29:52 +02:00
// check whether we have everything for OTA update
if (!(contentLength && isValidContentType)) {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Invalid OTA server response");
2019-09-27 12:47:00 +02:00
ota_display(4, " E", "response error");
2018-11-05 00:30:32 +01:00
goto retry;
}
#ifdef HAS_LED
#ifndef LED_ACTIVE_LOW
if (!Update.begin(contentLength, U_FLASH, HAS_LED, HIGH)) {
#else
if (!Update.begin(contentLength, U_FLASH, HAS_LED, LOW)) {
#endif
#else
if (!Update.begin(contentLength)) {
#endif
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Not enough space to start OTA update");
2019-09-27 12:47:00 +02:00
ota_display(4, " E", "disk full");
2018-11-05 00:30:32 +01:00
goto abort;
}
2019-03-13 22:08:05 +01:00
#ifdef HAS_DISPLAY
// register callback function for showing progress while streaming data
Update.onProgress(&show_progress);
2018-09-24 16:36:11 +02:00
#endif
2019-09-27 12:47:00 +02:00
ota_display(4, "**", "writing...");
written = Update.writeStream(client); // this is a blocking call
2018-09-19 01:35:20 +02:00
if (written == contentLength) {
ESP_LOGI(TAG, "Written %u bytes successfully", written);
snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024));
2019-09-27 12:47:00 +02:00
ota_display(4, "OK", buf);
} else {
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "Written only %u of %u bytes, OTA update attempt cancelled",
written, contentLength);
}
if (Update.end()) {
goto finished;
2018-09-15 16:29:52 +02:00
} else {
2018-11-05 00:03:47 +01:00
ESP_LOGI(TAG, "An error occurred. Error#: %d", Update.getError());
snprintf(buf, 17, "Error#: %d", Update.getError());
2019-09-27 12:47:00 +02:00
ota_display(4, " E", buf);
2018-11-05 00:30:32 +01:00
goto retry;
2018-09-15 16:29:52 +02:00
}
finished:
client.stop();
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "OTA update finished");
2018-11-05 00:30:32 +01:00
return 0;
2018-11-05 00:30:32 +01:00
abort:
client.stop();
2018-11-04 21:02:37 +01:00
ESP_LOGI(TAG, "OTA update failed");
2018-11-05 00:30:32 +01:00
return -1;
retry:
return 1;
2018-09-23 18:07:40 +02:00
} // do_ota_update
2019-09-27 12:47:00 +02:00
void ota_display(const uint8_t row, const std::string status,
const std::string msg) {
2019-03-13 22:08:05 +01:00
#ifdef HAS_DISPLAY
2019-09-27 12:47:00 +02:00
dp_printf(112, row, 0, 0, status.substr(0, 2).c_str());
2018-09-23 18:07:40 +02:00
if (!msg.empty()) {
2019-09-27 12:47:00 +02:00
dp_printf(0, 7, 0, 0, " ");
dp_printf(0, 7, 0, 0, msg.substr(0, 16).c_str());
2018-09-23 18:07:40 +02:00
}
#endif
2018-09-23 18:07:40 +02:00
}
// callback function to show download progress while streaming data
void show_progress(unsigned long current, unsigned long size) {
2019-09-27 12:47:00 +02:00
#ifdef HAS_DISPLAY
2018-09-23 18:07:40 +02:00
char buf[17];
2018-09-23 18:45:46 +02:00
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
2019-09-27 12:47:00 +02:00
ota_display(4, "**", buf);
#endif
2019-09-27 12:47:00 +02:00
}
2018-09-17 17:23:02 +02:00
// helper function to convert strings into lower case
bool comp(char s1, char s2) { return tolower(s1) < tolower(s2); }
2018-09-17 17:23:02 +02:00
// helper function to lexicographically compare two versions. Returns 1 if v2 is
// smaller, -1 if v1 is smaller, 0 if equal
2018-09-17 17:23:02 +02:00
int version_compare(const String v1, const String v2) {
if (v1 == v2)
return 0;
2018-09-17 17:23:02 +02:00
const char *a1 = v1.c_str(), *a2 = v2.c_str();
2018-09-17 17:23:02 +02:00
if (lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp))
return -1;
else
return 1;
2018-09-18 17:25:18 +02:00
}
2018-09-24 16:36:11 +02:00
#endif // USE_OTA