2024-04-23 05:59:58 +07:00
|
|
|
#ifndef _OTA_HANDLER_H_
|
|
|
|
#define _OTA_HANDLER_H_
|
|
|
|
|
2024-04-29 19:34:15 +07:00
|
|
|
#include <Arduino.h>
|
2024-04-29 19:46:13 +07:00
|
|
|
#include <esp_err.h>
|
|
|
|
#include <esp_http_client.h>
|
|
|
|
#include <esp_ota_ops.h>
|
2024-05-02 10:19:49 +07:00
|
|
|
#include "AgConfigure.h"
|
|
|
|
#include "AgStateMachine.h"
|
|
|
|
#include "AirGradient.h"
|
2024-04-23 06:35:21 +07:00
|
|
|
|
2024-05-02 10:19:49 +07:00
|
|
|
#define OTA_BUF_SIZE 1024
|
2024-04-24 19:56:37 +07:00
|
|
|
#define URL_BUF_SIZE 256
|
2024-04-23 06:35:21 +07:00
|
|
|
|
2024-04-25 16:11:49 +07:00
|
|
|
enum OtaUpdateOutcome {
|
2024-04-29 19:46:13 +07:00
|
|
|
UPDATE_PERFORMED,
|
|
|
|
ALREADY_UP_TO_DATE,
|
|
|
|
UPDATE_FAILED,
|
|
|
|
UDPATE_SKIPPED
|
2024-04-25 16:11:49 +07:00
|
|
|
};
|
|
|
|
|
2024-04-23 05:59:58 +07:00
|
|
|
class OtaHandler {
|
|
|
|
public:
|
2024-05-02 10:19:49 +07:00
|
|
|
OtaHandler(StateMachine &sm, Configuration &config)
|
|
|
|
: sm(sm), config(config) {}
|
|
|
|
void setAirGradient(AirGradient *ag) { this->ag = ag; };
|
|
|
|
|
|
|
|
void updateFirmwareIfOutdated(String newVersion) {
|
|
|
|
int lastOta = config.getLastOta();
|
|
|
|
// Retry OTA after last udpate 24h
|
|
|
|
if (lastOta != 0 && lastOta < (60 * 60 * 24)) {
|
|
|
|
Serial.println("Ignore OTA cause last update is " + String(lastOta) +
|
|
|
|
String("sec"));
|
|
|
|
Serial.println("Retry again after 24h");
|
|
|
|
return;
|
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
|
2024-05-02 10:19:49 +07:00
|
|
|
String url =
|
|
|
|
"http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() +
|
|
|
|
"/generic/os/firmware.bin";
|
2024-04-29 19:46:13 +07:00
|
|
|
url += "?current_firmware=";
|
|
|
|
url += GIT_VERSION;
|
|
|
|
char urlAsChar[URL_BUF_SIZE];
|
|
|
|
url.toCharArray(urlAsChar, URL_BUF_SIZE);
|
|
|
|
Serial.printf("checking for new OTA update @ %s\n", urlAsChar);
|
|
|
|
|
|
|
|
esp_http_client_config_t config = {};
|
|
|
|
config.url = urlAsChar;
|
2024-05-02 10:19:49 +07:00
|
|
|
OtaUpdateOutcome ret = attemptToPerformOta(&config, newVersion);
|
|
|
|
|
|
|
|
// Update last OTA time whatever result.
|
|
|
|
this->config.updateLastOta();
|
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
Serial.println(ret);
|
|
|
|
if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) {
|
|
|
|
Serial.println("OTA update performed, restarting ...");
|
2024-05-02 10:19:49 +07:00
|
|
|
int i = 6;
|
|
|
|
while (i != 0) {
|
|
|
|
i = i - 1;
|
|
|
|
sm.executeOTA(StateMachine::OtaState::OTA_STATE_SUCCESS, "", i);
|
|
|
|
delay(1000);
|
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
esp_restart();
|
2024-04-23 06:35:21 +07:00
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
}
|
2024-04-23 06:35:21 +07:00
|
|
|
|
|
|
|
private:
|
2024-05-02 10:19:49 +07:00
|
|
|
AirGradient *ag;
|
|
|
|
StateMachine &sm;
|
|
|
|
Configuration &config;
|
|
|
|
|
|
|
|
OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config,
|
|
|
|
String newVersion) {
|
2024-04-29 19:46:13 +07:00
|
|
|
esp_http_client_handle_t client = esp_http_client_init(config);
|
|
|
|
if (client == NULL) {
|
|
|
|
Serial.println("Failed to initialize HTTP connection");
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
}
|
2024-04-23 06:35:21 +07:00
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
esp_err_t err = esp_http_client_open(client, 0);
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
esp_http_client_cleanup(client);
|
|
|
|
Serial.printf("Failed to open HTTP connection: %s\n",
|
|
|
|
esp_err_to_name(err));
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
}
|
|
|
|
esp_http_client_fetch_headers(client);
|
|
|
|
|
|
|
|
int httpStatusCode = esp_http_client_get_status_code(client);
|
|
|
|
if (httpStatusCode == 304) {
|
|
|
|
Serial.println("Firmware is already up to date");
|
|
|
|
cleanupHttp(client);
|
|
|
|
return OtaUpdateOutcome::ALREADY_UP_TO_DATE;
|
|
|
|
} else if (httpStatusCode != 200) {
|
|
|
|
Serial.printf("Firmware update skipped, the server returned %d\n",
|
|
|
|
httpStatusCode);
|
|
|
|
cleanupHttp(client);
|
|
|
|
return OtaUpdateOutcome::UDPATE_SKIPPED;
|
|
|
|
}
|
2024-04-23 06:35:21 +07:00
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
esp_ota_handle_t update_handle = 0;
|
|
|
|
const esp_partition_t *update_partition = NULL;
|
|
|
|
Serial.println("Starting OTA update ...");
|
|
|
|
update_partition = esp_ota_get_next_update_partition(NULL);
|
|
|
|
if (update_partition == NULL) {
|
|
|
|
Serial.println("Passive OTA partition not found");
|
|
|
|
cleanupHttp(client);
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
}
|
|
|
|
Serial.printf("Writing to partition subtype %d at offset 0x%x\n",
|
|
|
|
update_partition->subtype, update_partition->address);
|
|
|
|
|
|
|
|
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
Serial.printf("esp_ota_begin failed, error=%d\n", err);
|
|
|
|
cleanupHttp(client);
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
}
|
2024-04-25 16:11:49 +07:00
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
esp_err_t ota_write_err = ESP_OK;
|
|
|
|
char *upgrade_data_buf = (char *)malloc(OTA_BUF_SIZE);
|
|
|
|
if (!upgrade_data_buf) {
|
|
|
|
Serial.println("Couldn't allocate memory for data buffer");
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
}
|
2024-04-24 19:56:37 +07:00
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
int binary_file_len = 0;
|
2024-05-02 10:19:49 +07:00
|
|
|
int totalSize = esp_http_client_get_content_length(client);
|
|
|
|
Serial.println("File size: " + String(totalSize) + String(" bytes"));
|
|
|
|
|
|
|
|
// Show display start update new firmware.
|
|
|
|
sm.executeOTA(StateMachine::OtaState::OTA_STATE_BEGIN, newVersion, 0);
|
|
|
|
|
|
|
|
// Download file and write new firmware to OTA partition
|
|
|
|
uint32_t lastUpdate = millis();
|
2024-04-29 19:46:13 +07:00
|
|
|
while (1) {
|
|
|
|
int data_read =
|
|
|
|
esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE);
|
|
|
|
if (data_read == 0) {
|
|
|
|
Serial.println("Connection closed, all data received");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (data_read < 0) {
|
|
|
|
Serial.println("Data read error");
|
2024-05-02 10:19:49 +07:00
|
|
|
sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0);
|
2024-04-29 19:46:13 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (data_read > 0) {
|
|
|
|
ota_write_err = esp_ota_write(
|
|
|
|
update_handle, (const void *)upgrade_data_buf, data_read);
|
2024-04-23 06:35:21 +07:00
|
|
|
if (ota_write_err != ESP_OK) {
|
2024-05-02 10:19:49 +07:00
|
|
|
sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0);
|
2024-04-29 19:46:13 +07:00
|
|
|
break;
|
2024-04-29 19:34:15 +07:00
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
binary_file_len += data_read;
|
2024-05-02 10:19:49 +07:00
|
|
|
|
|
|
|
int percent = (binary_file_len * 100) / totalSize;
|
|
|
|
uint32_t ms = (uint32_t)(millis() - lastUpdate);
|
|
|
|
if (ms >= 250) {
|
|
|
|
sm.executeOTA(StateMachine::OtaState::OTA_STATE_PROCESSING, "",
|
|
|
|
percent);
|
|
|
|
lastUpdate = millis();
|
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(upgrade_data_buf);
|
|
|
|
cleanupHttp(client);
|
|
|
|
Serial.printf("# of bytes written: %d\n", binary_file_len);
|
|
|
|
|
|
|
|
esp_err_t ota_end_err = esp_ota_end(update_handle);
|
|
|
|
if (ota_write_err != ESP_OK) {
|
|
|
|
Serial.printf("Error: esp_ota_write failed! err=0x%d\n", err);
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
|
|
|
} else if (ota_end_err != ESP_OK) {
|
|
|
|
Serial.printf("Error: esp_ota_end failed! err=0x%d. Image is invalid",
|
|
|
|
ota_end_err);
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
2024-04-23 05:59:58 +07:00
|
|
|
}
|
2024-04-29 09:54:37 +07:00
|
|
|
|
2024-04-29 19:46:13 +07:00
|
|
|
err = esp_ota_set_boot_partition(update_partition);
|
|
|
|
if (err != ESP_OK) {
|
|
|
|
Serial.printf("esp_ota_set_boot_partition failed! err=0x%d\n", err);
|
|
|
|
return OtaUpdateOutcome::UPDATE_FAILED;
|
2024-04-29 19:34:15 +07:00
|
|
|
}
|
2024-04-29 19:46:13 +07:00
|
|
|
return OtaUpdateOutcome::UPDATE_PERFORMED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cleanupHttp(esp_http_client_handle_t client) {
|
|
|
|
esp_http_client_close(client);
|
|
|
|
esp_http_client_cleanup(client);
|
|
|
|
}
|
2024-04-23 05:59:58 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|