mirror of
https://github.com/airgradienthq/arduino.git
synced 2025-06-27 00:31:32 +02:00
Compare commits
20 Commits
feat/local
...
3.1.21
Author | SHA1 | Date | |
---|---|---|---|
92e74feabd | |||
cc0fd88068 | |||
56809a412c | |||
6a83743e2a | |||
faaf051e39 | |||
5bc1821ef9 | |||
280ea5e997 | |||
38e792b88d | |||
aeee0cad01 | |||
401326d00d | |||
fb0dcad54d | |||
3556e4a96a | |||
283646a699 | |||
6312612ada | |||
c1f22674e2 | |||
40d38a75d8 | |||
4c165b31f5 | |||
2be91b3968 | |||
3ca2d1d208 | |||
aad12fc868 |
@ -331,7 +331,7 @@ static void sendDataToAg() {
|
|||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
||||||
|
|
||||||
delay(1500);
|
delay(1500);
|
||||||
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
|
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount())) {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
||||||
} else {
|
} else {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
||||||
@ -518,7 +518,8 @@ static void updatePm(void) {
|
|||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
/** Increment bootcount when send measurements data is scheduled */
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
measurements.bootCount++;
|
int bootCount = measurements.bootCount() + 1;
|
||||||
|
measurements.setBootCount(bootCount);
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
|
@ -388,7 +388,7 @@ static void sendDataToAg() {
|
|||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
||||||
|
|
||||||
delay(1500);
|
delay(1500);
|
||||||
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
|
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount())) {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
||||||
} else {
|
} else {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
||||||
@ -570,7 +570,8 @@ static void updatePm(void) {
|
|||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
/** Increment bootcount when send measurements data is scheduled */
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
measurements.bootCount++;
|
int bootCount = measurements.bootCount() + 1;
|
||||||
|
measurements.setBootCount(bootCount);
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
|
@ -411,7 +411,7 @@ static void sendDataToAg() {
|
|||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnecting);
|
||||||
|
|
||||||
delay(1500);
|
delay(1500);
|
||||||
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
|
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount())) {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
||||||
} else {
|
} else {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnectFailed);
|
||||||
@ -611,7 +611,8 @@ static void updatePm(void) {
|
|||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
/** Increment bootcount when send measurements data is scheduled */
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
measurements.bootCount++;
|
int bootCount = measurements.bootCount() + 1;
|
||||||
|
measurements.setBootCount(bootCount);
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false ||
|
if (configuration.isPostDataToAirGradient() == false ||
|
||||||
|
@ -36,20 +36,21 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
|
||||||
#include "AirGradient.h"
|
|
||||||
#include "OtaHandler.h"
|
|
||||||
#include "AgApiClient.h"
|
#include "AgApiClient.h"
|
||||||
#include "AgConfigure.h"
|
#include "AgConfigure.h"
|
||||||
#include "AgSchedule.h"
|
#include "AgSchedule.h"
|
||||||
#include "AgStateMachine.h"
|
#include "AgStateMachine.h"
|
||||||
#include "AgWiFiConnector.h"
|
#include "AgWiFiConnector.h"
|
||||||
|
#include "AirGradient.h"
|
||||||
#include "EEPROM.h"
|
#include "EEPROM.h"
|
||||||
#include "ESPmDNS.h"
|
#include "ESPmDNS.h"
|
||||||
#include "LocalServer.h"
|
#include "LocalServer.h"
|
||||||
#include "MqttClient.h"
|
#include "MqttClient.h"
|
||||||
#include "OpenMetrics.h"
|
#include "OpenMetrics.h"
|
||||||
|
#include "OtaHandler.h"
|
||||||
#include "WebServer.h"
|
#include "WebServer.h"
|
||||||
|
#include "esp32c3/rom/rtc.h"
|
||||||
|
#include <HardwareSerial.h>
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
@ -111,9 +112,8 @@ static void wdgFeedUpdate(void);
|
|||||||
static void ledBarEnabledUpdate(void);
|
static void ledBarEnabledUpdate(void);
|
||||||
static bool sgp41Init(void);
|
static bool sgp41Init(void);
|
||||||
static void firmwareCheckForUpdate(void);
|
static void firmwareCheckForUpdate(void);
|
||||||
static void otaHandlerCallback(OtaState state, String mesasge);
|
static void otaHandlerCallback(OtaHandler::OtaState state, String mesasge);
|
||||||
static void displayExecuteOta(OtaState state, String msg,
|
static void displayExecuteOta(OtaHandler::OtaState state, String msg, int processing);
|
||||||
int processing);
|
|
||||||
static int calculateMaxPeriod(int updateInterval);
|
static int calculateMaxPeriod(int updateInterval);
|
||||||
static void setMeasurementMaxPeriod();
|
static void setMeasurementMaxPeriod();
|
||||||
|
|
||||||
@ -136,6 +136,10 @@ void setup() {
|
|||||||
/** Print device ID into log */
|
/** Print device ID into log */
|
||||||
Serial.println("Serial nr: " + ag->deviceId());
|
Serial.println("Serial nr: " + ag->deviceId());
|
||||||
|
|
||||||
|
// Set reason why esp is reset
|
||||||
|
esp_reset_reason_t reason = esp_reset_reason();
|
||||||
|
measurements.setResetReason(reason);
|
||||||
|
|
||||||
/** Initialize local configure */
|
/** Initialize local configure */
|
||||||
configuration.begin();
|
configuration.begin();
|
||||||
|
|
||||||
@ -514,29 +518,27 @@ static void firmwareCheckForUpdate(void) {
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void otaHandlerCallback(OtaState state, String mesasge) {
|
static void otaHandlerCallback(OtaHandler::OtaState state, String message) {
|
||||||
Serial.println("OTA message: " + mesasge);
|
Serial.println("OTA message: " + message);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case OtaState::OTA_STATE_BEGIN:
|
case OtaHandler::OTA_STATE_BEGIN:
|
||||||
displayExecuteOta(state, fwNewVersion, 0);
|
displayExecuteOta(state, fwNewVersion, 0);
|
||||||
break;
|
break;
|
||||||
case OtaState::OTA_STATE_FAIL:
|
case OtaHandler::OTA_STATE_FAIL:
|
||||||
displayExecuteOta(state, "", 0);
|
displayExecuteOta(state, "", 0);
|
||||||
break;
|
break;
|
||||||
case OtaState::OTA_STATE_PROCESSING:
|
case OtaHandler::OTA_STATE_PROCESSING:
|
||||||
displayExecuteOta(state, "", mesasge.toInt());
|
case OtaHandler::OTA_STATE_SUCCESS:
|
||||||
break;
|
displayExecuteOta(state, "", message.toInt());
|
||||||
case OtaState::OTA_STATE_SUCCESS:
|
|
||||||
displayExecuteOta(state, "", mesasge.toInt());
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void displayExecuteOta(OtaState state, String msg, int processing) {
|
static void displayExecuteOta(OtaHandler::OtaState state, String msg, int processing) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case OtaState::OTA_STATE_BEGIN: {
|
case OtaHandler::OTA_STATE_BEGIN: {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateVersion(msg);
|
oledDisplay.showFirmwareUpdateVersion(msg);
|
||||||
} else {
|
} else {
|
||||||
@ -545,7 +547,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) {
|
|||||||
delay(2500);
|
delay(2500);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtaState::OTA_STATE_FAIL: {
|
case OtaHandler::OTA_STATE_FAIL: {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateFailed();
|
oledDisplay.showFirmwareUpdateFailed();
|
||||||
} else {
|
} else {
|
||||||
@ -555,7 +557,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) {
|
|||||||
delay(2500);
|
delay(2500);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtaState::OTA_STATE_SKIP: {
|
case OtaHandler::OTA_STATE_SKIP: {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateSkipped();
|
oledDisplay.showFirmwareUpdateSkipped();
|
||||||
} else {
|
} else {
|
||||||
@ -565,7 +567,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) {
|
|||||||
delay(2500);
|
delay(2500);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtaState::OTA_STATE_UP_TO_DATE: {
|
case OtaHandler::OTA_STATE_UP_TO_DATE: {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateUpToDate();
|
oledDisplay.showFirmwareUpdateUpToDate();
|
||||||
} else {
|
} else {
|
||||||
@ -575,7 +577,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) {
|
|||||||
delay(2500);
|
delay(2500);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtaState::OTA_STATE_PROCESSING: {
|
case OtaHandler::OTA_STATE_PROCESSING: {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
oledDisplay.showFirmwareUpdateProgress(processing);
|
oledDisplay.showFirmwareUpdateProgress(processing);
|
||||||
} else {
|
} else {
|
||||||
@ -584,7 +586,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtaState::OTA_STATE_SUCCESS: {
|
case OtaHandler::OTA_STATE_SUCCESS: {
|
||||||
int i = 6;
|
int i = 6;
|
||||||
while(i != 0) {
|
while(i != 0) {
|
||||||
i = i - 1;
|
i = i - 1;
|
||||||
@ -634,7 +636,7 @@ static void sendDataToAg() {
|
|||||||
"task_led", 2048, NULL, 5, NULL);
|
"task_led", 2048, NULL, 5, NULL);
|
||||||
|
|
||||||
delay(1500);
|
delay(1500);
|
||||||
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount)) {
|
if (apiClient.sendPing(wifiConnector.RSSI(), measurements.bootCount())) {
|
||||||
if (ag->isOne()) {
|
if (ag->isOne()) {
|
||||||
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
stateMachine.displayHandle(AgStateMachineWiFiOkServerConnected);
|
||||||
}
|
}
|
||||||
@ -1135,7 +1137,8 @@ static void updatePm(void) {
|
|||||||
|
|
||||||
static void sendDataToServer(void) {
|
static void sendDataToServer(void) {
|
||||||
/** Increment bootcount when send measurements data is scheduled */
|
/** Increment bootcount when send measurements data is scheduled */
|
||||||
measurements.bootCount++;
|
int bootCount = measurements.bootCount() + 1;
|
||||||
|
measurements.setBootCount(bootCount);
|
||||||
|
|
||||||
/** Ignore send data to server if postToAirGradient disabled */
|
/** Ignore send data to server if postToAirGradient disabled */
|
||||||
if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) {
|
if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) {
|
||||||
@ -1149,6 +1152,9 @@ static void sendDataToServer(void) {
|
|||||||
"Online mode and isPostToAirGradient = true: watchdog reset");
|
"Online mode and isPostToAirGradient = true: watchdog reset");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Log current free heap size */
|
||||||
|
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempHumUpdate(void) {
|
static void tempHumUpdate(void) {
|
||||||
|
@ -1,206 +0,0 @@
|
|||||||
#ifndef _OTA_HANDLER_H_
|
|
||||||
#define _OTA_HANDLER_H_
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <esp_err.h>
|
|
||||||
#include <esp_http_client.h>
|
|
||||||
#include <esp_ota_ops.h>
|
|
||||||
|
|
||||||
#define OTA_BUF_SIZE 1024
|
|
||||||
#define URL_BUF_SIZE 256
|
|
||||||
|
|
||||||
enum OtaUpdateOutcome {
|
|
||||||
UPDATE_PERFORMED,
|
|
||||||
ALREADY_UP_TO_DATE,
|
|
||||||
UPDATE_FAILED,
|
|
||||||
UDPATE_SKIPPED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum OtaState {
|
|
||||||
OTA_STATE_BEGIN,
|
|
||||||
OTA_STATE_FAIL,
|
|
||||||
OTA_STATE_SKIP,
|
|
||||||
OTA_STATE_UP_TO_DATE,
|
|
||||||
OTA_STATE_PROCESSING,
|
|
||||||
OTA_STATE_SUCCESS
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void(*OtaHandlerCallback_t)(OtaState state,
|
|
||||||
String message);
|
|
||||||
|
|
||||||
class OtaHandler {
|
|
||||||
public:
|
|
||||||
void updateFirmwareIfOutdated(String deviceId) {
|
|
||||||
String url = "http://hw.airgradient.com/sensors/airgradient:" + deviceId +
|
|
||||||
"/generic/os/firmware.bin";
|
|
||||||
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;
|
|
||||||
OtaUpdateOutcome ret = attemptToPerformOta(&config);
|
|
||||||
Serial.println(ret);
|
|
||||||
if (this->callback) {
|
|
||||||
switch (ret) {
|
|
||||||
case OtaUpdateOutcome::UPDATE_PERFORMED:
|
|
||||||
this->callback(OtaState::OTA_STATE_SUCCESS, "");
|
|
||||||
break;
|
|
||||||
case OtaUpdateOutcome::UDPATE_SKIPPED:
|
|
||||||
this->callback(OtaState::OTA_STATE_SKIP, "");
|
|
||||||
break;
|
|
||||||
case OtaUpdateOutcome::ALREADY_UP_TO_DATE:
|
|
||||||
this->callback(OtaState::OTA_STATE_UP_TO_DATE, "");
|
|
||||||
break;
|
|
||||||
case OtaUpdateOutcome::UPDATE_FAILED:
|
|
||||||
this->callback(OtaState::OTA_STATE_FAIL, "");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setHandlerCallback(OtaHandlerCallback_t callback) {
|
|
||||||
this->callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
OtaHandlerCallback_t callback;
|
|
||||||
|
|
||||||
OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int binary_file_len = 0;
|
|
||||||
int totalSize = esp_http_client_get_content_length(client);
|
|
||||||
Serial.println("File size: " + String(totalSize) + String(" bytes"));
|
|
||||||
|
|
||||||
// Show display start update new firmware.
|
|
||||||
if (this->callback) {
|
|
||||||
this->callback(OtaState::OTA_STATE_BEGIN, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download file and write new firmware to OTA partition
|
|
||||||
uint32_t lastUpdate = millis();
|
|
||||||
while (1) {
|
|
||||||
int data_read =
|
|
||||||
esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE);
|
|
||||||
if (data_read == 0) {
|
|
||||||
if (this->callback) {
|
|
||||||
this->callback(OtaState::OTA_STATE_PROCESSING, String(100));
|
|
||||||
}
|
|
||||||
Serial.println("Connection closed, all data received");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (data_read < 0) {
|
|
||||||
Serial.println("Data read error");
|
|
||||||
if (this->callback) {
|
|
||||||
this->callback(OtaState::OTA_STATE_FAIL, "");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (data_read > 0) {
|
|
||||||
ota_write_err = esp_ota_write(
|
|
||||||
update_handle, (const void *)upgrade_data_buf, data_read);
|
|
||||||
if (ota_write_err != ESP_OK) {
|
|
||||||
if (this->callback) {
|
|
||||||
this->callback(OtaState::OTA_STATE_FAIL, "");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
binary_file_len += data_read;
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (this->callback) {
|
|
||||||
this->callback(OtaState::OTA_STATE_PROCESSING,
|
|
||||||
String(percent));
|
|
||||||
}
|
|
||||||
lastUpdate = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return OtaUpdateOutcome::UPDATE_PERFORMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanupHttp(esp_http_client_handle_t client) {
|
|
||||||
esp_http_client_close(client);
|
|
||||||
esp_http_client_cleanup(client);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.1.13
|
version=3.1.21
|
||||||
author=AirGradient <support@airgradient.com>
|
author=AirGradient <support@airgradient.com>
|
||||||
maintainer=AirGradient <support@airgradient.com>
|
maintainer=AirGradient <support@airgradient.com>
|
||||||
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||||
|
@ -59,9 +59,20 @@ bool AgApiClient::fetchServerConfiguration(void) {
|
|||||||
#else
|
#else
|
||||||
HTTPClient client;
|
HTTPClient client;
|
||||||
client.setTimeout(timeoutMs);
|
client.setTimeout(timeoutMs);
|
||||||
if (client.begin(uri) == false) {
|
if (apiRootChanged) {
|
||||||
getConfigFailed = true;
|
// If apiRoot is changed, assume not using https
|
||||||
return false;
|
if (client.begin(uri) == false) {
|
||||||
|
logError("Begin HTTPClient failed (GET)");
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// By default, airgradient using https
|
||||||
|
if (client.begin(uri, AG_SERVER_ROOT_CA) == false) {
|
||||||
|
logError("Begin HTTPClient using tls failed (GET)");
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -90,8 +101,6 @@ bool AgApiClient::fetchServerConfiguration(void) {
|
|||||||
String respContent = client.getString();
|
String respContent = client.getString();
|
||||||
client.end();
|
client.end();
|
||||||
|
|
||||||
// logInfo("Get configuration: " + respContent);
|
|
||||||
|
|
||||||
/** Parse configuration and return result */
|
/** Parse configuration and return result */
|
||||||
return config.parse(respContent, false);
|
return config.parse(respContent, false);
|
||||||
}
|
}
|
||||||
@ -115,22 +124,37 @@ bool AgApiClient::postToServer(String data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures";
|
String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures";
|
||||||
// logInfo("Post uri: " + uri);
|
#ifdef ESP8266
|
||||||
// logInfo("Post data: " + data);
|
|
||||||
|
|
||||||
WiFiClient wifiClient;
|
|
||||||
HTTPClient client;
|
HTTPClient client;
|
||||||
client.setTimeout(timeoutMs);
|
WiFiClient wifiClient;
|
||||||
if (client.begin(wifiClient, uri.c_str()) == false) {
|
if (client.begin(wifiClient, uri) == false) {
|
||||||
logError("Init client failed");
|
getConfigFailed = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
HTTPClient client;
|
||||||
|
client.setTimeout(timeoutMs);
|
||||||
|
if (apiRootChanged) {
|
||||||
|
// If apiRoot is changed, assume not using https
|
||||||
|
if (client.begin(uri) == false) {
|
||||||
|
logError("Begin HTTPClient failed (POST)");
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// By default, airgradient using https
|
||||||
|
if (client.begin(uri, AG_SERVER_ROOT_CA) == false) {
|
||||||
|
logError("Begin HTTPClient using tls failed (POST)");
|
||||||
|
getConfigFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
client.addHeader("content-type", "application/json");
|
client.addHeader("content-type", "application/json");
|
||||||
int retCode = client.POST(data);
|
int retCode = client.POST(data);
|
||||||
client.end();
|
client.end();
|
||||||
|
|
||||||
logInfo(String("POST: ") + uri);
|
logInfo(String("POST: ") + uri);
|
||||||
// logInfo(String("DATA: ") + data);
|
|
||||||
logInfo(String("Return code: ") + String(retCode));
|
logInfo(String("Return code: ") + String(retCode));
|
||||||
|
|
||||||
if ((retCode == 200) || (retCode == 429)) {
|
if ((retCode == 200) || (retCode == 429)) {
|
||||||
@ -189,7 +213,10 @@ bool AgApiClient::sendPing(int rssi, int bootCount) {
|
|||||||
|
|
||||||
String AgApiClient::getApiRoot() const { return apiRoot; }
|
String AgApiClient::getApiRoot() const { return apiRoot; }
|
||||||
|
|
||||||
void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; }
|
void AgApiClient::setApiRoot(const String &apiRoot) {
|
||||||
|
this->apiRootChanged = true;
|
||||||
|
this->apiRoot = apiRoot;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set http request timeout. (Default: 10s)
|
* @brief Set http request timeout. (Default: 10s)
|
||||||
|
@ -20,8 +20,14 @@ class AgApiClient : public PrintLog {
|
|||||||
private:
|
private:
|
||||||
Configuration &config;
|
Configuration &config;
|
||||||
AirGradient *ag;
|
AirGradient *ag;
|
||||||
|
#ifdef ESP8266
|
||||||
|
// ESP8266 not support HTTPS
|
||||||
String apiRoot = "http://hw.airgradient.com";
|
String apiRoot = "http://hw.airgradient.com";
|
||||||
|
#else
|
||||||
|
String apiRoot = "https://hw.airgradient.com";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool apiRootChanged = false; // Indicate if setApiRoot() is called
|
||||||
bool getConfigFailed;
|
bool getConfigFailed;
|
||||||
bool postToServerFailed;
|
bool postToServerFailed;
|
||||||
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
|
bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard.
|
||||||
|
@ -27,6 +27,12 @@
|
|||||||
#define json_prop_noxRaw "noxRaw"
|
#define json_prop_noxRaw "noxRaw"
|
||||||
#define json_prop_co2 "rco2"
|
#define json_prop_co2 "rco2"
|
||||||
|
|
||||||
|
Measurements::Measurements() {
|
||||||
|
#ifndef ESP8266
|
||||||
|
_resetReason = (int)ESP_RST_UNKNOWN;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Measurements::maxPeriod(MeasurementType type, int max) {
|
void Measurements::maxPeriod(MeasurementType type, int max) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Temperature:
|
case Temperature:
|
||||||
@ -601,8 +607,8 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root["boot"] = bootCount;
|
root["boot"] = _bootCount;
|
||||||
root["bootCount"] = bootCount;
|
root["bootCount"] = _bootCount;
|
||||||
root["wifi"] = rssi;
|
root["wifi"] = rssi;
|
||||||
|
|
||||||
if (localServer) {
|
if (localServer) {
|
||||||
@ -612,6 +618,11 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi,
|
|||||||
root["serialno"] = ag.deviceId();
|
root["serialno"] = ag.deviceId();
|
||||||
root["firmware"] = ag.getVersion();
|
root["firmware"] = ag.getVersion();
|
||||||
root["model"] = AgFirmwareModeName(fwMode);
|
root["model"] = AgFirmwareModeName(fwMode);
|
||||||
|
} else {
|
||||||
|
#ifndef ESP8266
|
||||||
|
root["resetReason"] = _resetReason;
|
||||||
|
root["freeHeap"] = ESP.getFreeHeap();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = JSON.stringify(root);
|
String result = JSON.stringify(root);
|
||||||
@ -1065,4 +1076,50 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem
|
|||||||
return pms;
|
return pms;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Measurements::setDebug(bool debug) { _debug = debug; }
|
void Measurements::setDebug(bool debug) { _debug = debug; }
|
||||||
|
|
||||||
|
int Measurements::bootCount() { return _bootCount; }
|
||||||
|
|
||||||
|
void Measurements::setBootCount(int bootCount) { _bootCount = bootCount; }
|
||||||
|
|
||||||
|
#ifndef ESP8266
|
||||||
|
void Measurements::setResetReason(esp_reset_reason_t reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case ESP_RST_UNKNOWN:
|
||||||
|
Serial.println("Reset reason: ESP_RST_UNKNOWN");
|
||||||
|
break;
|
||||||
|
case ESP_RST_POWERON:
|
||||||
|
Serial.println("Reset reason: ESP_RST_POWERON");
|
||||||
|
break;
|
||||||
|
case ESP_RST_EXT:
|
||||||
|
Serial.println("Reset reason: ESP_RST_EXT");
|
||||||
|
break;
|
||||||
|
case ESP_RST_SW:
|
||||||
|
Serial.println("Reset reason: ESP_RST_SW");
|
||||||
|
break;
|
||||||
|
case ESP_RST_PANIC:
|
||||||
|
Serial.println("Reset reason: ESP_RST_PANIC");
|
||||||
|
break;
|
||||||
|
case ESP_RST_INT_WDT:
|
||||||
|
Serial.println("Reset reason: ESP_RST_INT_WDT");
|
||||||
|
break;
|
||||||
|
case ESP_RST_TASK_WDT:
|
||||||
|
Serial.println("Reset reason: ESP_RST_TASK_WDT");
|
||||||
|
break;
|
||||||
|
case ESP_RST_WDT:
|
||||||
|
Serial.println("Reset reason: ESP_RST_WDT");
|
||||||
|
break;
|
||||||
|
case ESP_RST_BROWNOUT:
|
||||||
|
Serial.println("Reset reason: ESP_RST_BROWNOUT");
|
||||||
|
break;
|
||||||
|
case ESP_RST_SDIO:
|
||||||
|
Serial.println("Reset reason: ESP_RST_SDIO");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.println("Reset reason: unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetReason = (int)reason;
|
||||||
|
}
|
||||||
|
#endif
|
@ -34,7 +34,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Measurements() {}
|
Measurements();
|
||||||
~Measurements() {}
|
~Measurements() {}
|
||||||
|
|
||||||
// Enumeration for every AG measurements
|
// Enumeration for every AG measurements
|
||||||
@ -147,8 +147,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setDebug(bool debug);
|
void setDebug(bool debug);
|
||||||
|
|
||||||
// TODO: update this to use setter
|
int bootCount();
|
||||||
int bootCount;
|
void setBootCount(int bootCount);
|
||||||
|
|
||||||
|
#ifndef ESP8266
|
||||||
|
void setResetReason(esp_reset_reason_t reason);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T
|
// Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T
|
||||||
@ -171,7 +175,8 @@ private:
|
|||||||
IntegerValue _pm_25_pc[2]; // particle count 2.5
|
IntegerValue _pm_25_pc[2]; // particle count 2.5
|
||||||
IntegerValue _pm_5_pc[2]; // particle count 5.0
|
IntegerValue _pm_5_pc[2]; // particle count 5.0
|
||||||
IntegerValue _pm_10_pc[2]; // particle count 10
|
IntegerValue _pm_10_pc[2]; // particle count 10
|
||||||
|
int _bootCount;
|
||||||
|
int _resetReason;
|
||||||
bool _debug = false;
|
bool _debug = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,46 @@
|
|||||||
#include "Main/utils.h"
|
#include "Main/utils.h"
|
||||||
|
|
||||||
#ifndef GIT_VERSION
|
#ifndef GIT_VERSION
|
||||||
#define GIT_VERSION "3.1.13-snap"
|
#define GIT_VERSION "3.1.21-snap"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ESP8266
|
||||||
|
// Airgradient server root ca certificate
|
||||||
|
const char *const AG_SERVER_ROOT_CA =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
"MIIF4jCCA8oCCQD7MgvcaVWxkTANBgkqhkiG9w0BAQsFADCBsjELMAkGA1UEBhMC\n"
|
||||||
|
"VEgxEzARBgNVBAgMCkNoaWFuZyBNYWkxEDAOBgNVBAcMB01hZSBSaW0xGTAXBgNV\n"
|
||||||
|
"BAoMEEFpckdyYWRpZW50IEx0ZC4xFDASBgNVBAsMC1NlbnNvciBMYWJzMSgwJgYD\n"
|
||||||
|
"VQQDDB9BaXJHcmFkaWVudCBTZW5zb3IgTGFicyBSb290IENBMSEwHwYJKoZIhvcN\n"
|
||||||
|
"AQkBFhJjYUBhaXJncmFkaWVudC5jb20wHhcNMjEwOTE3MTE0NDE3WhcNNDEwOTEy\n"
|
||||||
|
"MTE0NDE3WjCBsjELMAkGA1UEBhMCVEgxEzARBgNVBAgMCkNoaWFuZyBNYWkxEDAO\n"
|
||||||
|
"BgNVBAcMB01hZSBSaW0xGTAXBgNVBAoMEEFpckdyYWRpZW50IEx0ZC4xFDASBgNV\n"
|
||||||
|
"BAsMC1NlbnNvciBMYWJzMSgwJgYDVQQDDB9BaXJHcmFkaWVudCBTZW5zb3IgTGFi\n"
|
||||||
|
"cyBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJjYUBhaXJncmFkaWVudC5jb20wggIi\n"
|
||||||
|
"MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6XkVQ4O9d5GcUjPYRgF/uaY6O\n"
|
||||||
|
"5ry1xCGvotxkEeKkBk99lB1oNUUfNsP5bwuDci4XKfY9Ro6/jmkfHSVcPAwUnjAt\n"
|
||||||
|
"BcHqZtA/cMXykaynf9yXPxPQN7XLu/Rk32RIfb90sIGS318xgNziCYvzWZmlxpxc\n"
|
||||||
|
"3gUcAgGtamlgZ6wD3yOHVo8B9aFNvmP16QwkUm8fKDHunJG+iX2Bxa4ka5FJovhG\n"
|
||||||
|
"TnUwtso6Vrn0JaWF9qWcPZE0JZMjFW8PYRriyJmHwr/nAXfPPKphD1oRO+oA7/jq\n"
|
||||||
|
"dYkrJw6+OHfFXnPB1xkeh4OPBzcCZHT5XWNfwBYazYpjcJa9ngGFSmg8lX1ac23C\n"
|
||||||
|
"zea1XJmSrPwbZbWxoQznnf7Y78mRjruYKgSP8rf74KYvBe/HGPL5NQyXQ3l6kwmu\n"
|
||||||
|
"CCUqfcC0wCWEtWESxwSdFE2qQii8CZ12kQExzvR2PrOIyKQYSdkGx9/RBZtAVPXP\n"
|
||||||
|
"hmLuRBQYHrF5Cxf1oIbBK8OMoNVgBm6ftt15t9Sq9dH5Aup2YR6WEJkVaYkYzZzK\n"
|
||||||
|
"X7M+SQcdbXp+hAO8PFpABJxkaDAO2kiB5Ov7pDYPAcmNFqnJT48AY0TZJeVeCa5W\n"
|
||||||
|
"sIv3lPvB/XcFjP0+aZxxNSEEwpGPUYgvKUYUUmb0NammlYQwZHKaShPEmZ3UZ0bp\n"
|
||||||
|
"VNt4p6374nzO376sSwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQB/LfBPgTx7xKQB\n"
|
||||||
|
"JNMUhah17AFAn050NiviGJOHdPQely6u3DmJGg+ijEVlPWO1FEW3it+LOuNP5zOu\n"
|
||||||
|
"bhq8paTYIxPxtALIxw5ksykX9woDuX3H6FF9mPdQIbL7ft+3ZtZ4FWPui9dUtaPe\n"
|
||||||
|
"ZBmDFDi4U29nhWZK68JSp5QkWjfaYLV/vtag7120eVyGEPFZ0UAuTUNqpw+stOt9\n"
|
||||||
|
"gJ2ZxNx13xJ8ZnLK7qz1crPe8/8IVAdxbVLoY7JaWPLc//+VF+ceKicy8+4gV7zN\n"
|
||||||
|
"Gnq2IyM+CHFz8VYMLbW+3eVp4iJjTa72vae116kozboEIUVN9rgLqIKyVqQXiuoN\n"
|
||||||
|
"g3xY+yfncPB2+H/+lfyy6mepPIfgksd3+KeNxFADSc5EVY2JKEdorRodnAh7a8K6\n"
|
||||||
|
"WjTYgq+GjWXU2uQW2SyPt6Tu33OT8nBnu3NB80eT8WXgdVCkgsuyCuLvNRf1Xmze\n"
|
||||||
|
"igvurpU6JmQ1GlLgLJo8omJHTh1zIbkR9injPYne2v9ciHCoP6+LDEqe+rOsvPCB\n"
|
||||||
|
"C/o/iZ4svmYX4fWGuU7GgqZE8hhrC3+GdOTf2ADC752cYCZxBidXGtkrGNoHQKmQ\n"
|
||||||
|
"KCOMFBxZIvWteB3tUo3BKYz1D2CvKWz1wV4moc5JHkOgS+jqxhvOkQ/vfQBQ1pUY\n"
|
||||||
|
"TMui9BSwU7B1G2XjdLbfF3Dc67zaSg==\n"
|
||||||
|
"-----END CERTIFICATE-----\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
171
src/OtaHandler.cpp
Normal file
171
src/OtaHandler.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include "OtaHandler.h"
|
||||||
|
|
||||||
|
#ifndef ESP8266 // Only for esp32 based mcu
|
||||||
|
|
||||||
|
#include "AirGradient.h"
|
||||||
|
|
||||||
|
void OtaHandler::setHandlerCallback(OtaHandlerCallback_t callback) { _callback = callback; }
|
||||||
|
|
||||||
|
void OtaHandler::updateFirmwareIfOutdated(String deviceId) {
|
||||||
|
String url =
|
||||||
|
"https://hw.airgradient.com/sensors/airgradient:" + deviceId + "/generic/os/firmware.bin";
|
||||||
|
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;
|
||||||
|
config.cert_pem = AG_SERVER_ROOT_CA;
|
||||||
|
OtaUpdateOutcome ret = attemptToPerformOta(&config);
|
||||||
|
Serial.println(ret);
|
||||||
|
if (_callback) {
|
||||||
|
switch (ret) {
|
||||||
|
case OtaUpdateOutcome::UPDATE_PERFORMED:
|
||||||
|
_callback(OtaState::OTA_STATE_SUCCESS, "");
|
||||||
|
break;
|
||||||
|
case OtaUpdateOutcome::UPDATE_SKIPPED:
|
||||||
|
_callback(OtaState::OTA_STATE_SKIP, "");
|
||||||
|
break;
|
||||||
|
case OtaUpdateOutcome::ALREADY_UP_TO_DATE:
|
||||||
|
_callback(OtaState::OTA_STATE_UP_TO_DATE, "");
|
||||||
|
break;
|
||||||
|
case OtaUpdateOutcome::UPDATE_FAILED:
|
||||||
|
_callback(OtaState::OTA_STATE_FAIL, "");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OtaHandler::OtaUpdateOutcome
|
||||||
|
OtaHandler::attemptToPerformOta(const esp_http_client_config_t *config) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::UPDATE_SKIPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int binary_file_len = 0;
|
||||||
|
int totalSize = esp_http_client_get_content_length(client);
|
||||||
|
Serial.println("File size: " + String(totalSize) + String(" bytes"));
|
||||||
|
|
||||||
|
// Show display start update new firmware.
|
||||||
|
if (_callback) {
|
||||||
|
_callback(OtaState::OTA_STATE_BEGIN, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download file and write new firmware to OTA partition
|
||||||
|
uint32_t lastUpdate = millis();
|
||||||
|
while (1) {
|
||||||
|
int data_read = esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE);
|
||||||
|
if (data_read == 0) {
|
||||||
|
if (_callback) {
|
||||||
|
_callback(OtaState::OTA_STATE_PROCESSING, String(100));
|
||||||
|
}
|
||||||
|
Serial.println("Connection closed, all data received");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data_read < 0) {
|
||||||
|
Serial.println("Data read error");
|
||||||
|
if (_callback) {
|
||||||
|
_callback(OtaState::OTA_STATE_FAIL, "");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data_read > 0) {
|
||||||
|
ota_write_err = esp_ota_write(update_handle, (const void *)upgrade_data_buf, data_read);
|
||||||
|
if (ota_write_err != ESP_OK) {
|
||||||
|
if (_callback) {
|
||||||
|
_callback(OtaState::OTA_STATE_FAIL, "");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
binary_file_len += data_read;
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (_callback) {
|
||||||
|
_callback(OtaState::OTA_STATE_PROCESSING, String(percent));
|
||||||
|
}
|
||||||
|
lastUpdate = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return OtaUpdateOutcome::UPDATE_PERFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtaHandler::cleanupHttp(esp_http_client_handle_t client) {
|
||||||
|
esp_http_client_close(client);
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
src/OtaHandler.h
Normal file
43
src/OtaHandler.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef OTA_HANDLER_H
|
||||||
|
#define OTA_HANDLER_H
|
||||||
|
#ifndef ESP8266 // Only for esp32 based mcu
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_http_client.h>
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
|
||||||
|
#define OTA_BUF_SIZE 1024
|
||||||
|
#define URL_BUF_SIZE 256
|
||||||
|
|
||||||
|
class OtaHandler {
|
||||||
|
public:
|
||||||
|
enum OtaState {
|
||||||
|
OTA_STATE_BEGIN,
|
||||||
|
OTA_STATE_FAIL,
|
||||||
|
OTA_STATE_SKIP,
|
||||||
|
OTA_STATE_UP_TO_DATE,
|
||||||
|
OTA_STATE_PROCESSING,
|
||||||
|
OTA_STATE_SUCCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*OtaHandlerCallback_t)(OtaState state, String message);
|
||||||
|
void setHandlerCallback(OtaHandlerCallback_t callback);
|
||||||
|
void updateFirmwareIfOutdated(String deviceId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OtaHandlerCallback_t _callback;
|
||||||
|
|
||||||
|
enum OtaUpdateOutcome {
|
||||||
|
UPDATE_PERFORMED = 0,
|
||||||
|
ALREADY_UP_TO_DATE,
|
||||||
|
UPDATE_FAILED,
|
||||||
|
UPDATE_SKIPPED
|
||||||
|
}; // Internal use
|
||||||
|
|
||||||
|
OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config);
|
||||||
|
void cleanupHttp(esp_http_client_handle_t client);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ESP8266
|
||||||
|
#endif // OTA_HANDLER_H
|
Reference in New Issue
Block a user