From 44931567396b6e50dcdabfdcdf32f6f6b0e7d7d6 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 2 May 2024 10:19:49 +0700 Subject: [PATCH 1/5] Implement regular OTA update attempt / indicate OTA processing on display --- examples/OneOpenAir/OneOpenAir.ino | 10 ++++- examples/OneOpenAir/OtaHandler.h | 62 ++++++++++++++++++++++++--- src/AgConfigure.cpp | 68 +++++++++++++++++++++++++++++- src/AgConfigure.h | 5 +++ src/AgOledDisplay.cpp | 49 +++++++++++++++++++++ src/AgOledDisplay.h | 7 +++ src/AgStateMachine.cpp | 44 +++++++++++++++++++ src/AgStateMachine.h | 8 ++++ src/AgWiFiConnector.cpp | 6 +++ src/App/AppDef.h | 3 ++ 10 files changed, 252 insertions(+), 10 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 240cfaf..52a4190 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -82,7 +82,7 @@ static WifiConnector wifiConnector(oledDisplay, Serial, stateMachine, configuration); static OpenMetrics openMetrics(measurements, configuration, wifiConnector, apiClient); -static OtaHandler otaHandler; +static OtaHandler otaHandler(stateMachine, configuration); static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector); @@ -157,6 +157,7 @@ void setup() { apiClient.setAirGradient(ag); openMetrics.setAirGradient(ag); localServer.setAirGraident(ag); + otaHandler.setAirGradient(ag); /** Connecting wifi */ bool connectToWifi = false; @@ -184,7 +185,7 @@ void setup() { #ifdef ESP8266 // ota not supported #else - otaHandler.updateFirmwareIfOutdated(ag->deviceId()); + // otaHandler.updateFirmwareIfOutdated(ag->deviceId()); #endif apiClient.fetchServerConfiguration(); @@ -733,6 +734,11 @@ static void configUpdateHandle() { String(configuration.getDisplayBrightness())); } + String newVer = configuration.newFirmwareVersion(); + if (newVer.length()) { + otaHandler.updateFirmwareIfOutdated(newVer); + } + appDispHandler(); appLedHandler(); } diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index 067d5bf..8775503 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -5,8 +5,11 @@ #include #include #include +#include "AgConfigure.h" +#include "AgStateMachine.h" +#include "AirGradient.h" -#define OTA_BUF_SIZE 512 +#define OTA_BUF_SIZE 1024 #define URL_BUF_SIZE 256 enum OtaUpdateOutcome { @@ -18,10 +21,23 @@ enum OtaUpdateOutcome { class OtaHandler { public: - void updateFirmwareIfOutdated(String deviceId) { + OtaHandler(StateMachine &sm, Configuration &config) + : sm(sm), config(config) {} + void setAirGradient(AirGradient *ag) { this->ag = ag; }; - String url = "http://hw.airgradient.com/sensors/airgradient:" + deviceId + - "/generic/os/firmware.bin"; + 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; + } + + String url = + "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + + "/generic/os/firmware.bin"; url += "?current_firmware="; url += GIT_VERSION; char urlAsChar[URL_BUF_SIZE]; @@ -30,16 +46,31 @@ public: esp_http_client_config_t config = {}; config.url = urlAsChar; - esp_err_t ret = attemptToPerformOta(&config); + OtaUpdateOutcome ret = attemptToPerformOta(&config, newVersion); + + // Update last OTA time whatever result. + this->config.updateLastOta(); + Serial.println(ret); if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { Serial.println("OTA update performed, restarting ..."); + int i = 6; + while (i != 0) { + i = i - 1; + sm.executeOTA(StateMachine::OtaState::OTA_STATE_SUCCESS, "", i); + delay(1000); + } esp_restart(); } } private: - OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config) { + AirGradient *ag; + StateMachine &sm; + Configuration &config; + + OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config, + String newVersion) { esp_http_client_handle_t client = esp_http_client_init(config); if (client == NULL) { Serial.println("Failed to initialize HTTP connection"); @@ -94,6 +125,14 @@ private: } 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. + sm.executeOTA(StateMachine::OtaState::OTA_STATE_BEGIN, newVersion, 0); + + // 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); @@ -103,16 +142,25 @@ private: } if (data_read < 0) { Serial.println("Data read error"); + sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0); 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) { + sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0); break; } binary_file_len += data_read; - // Serial.printf("Written image length %d\n", binary_file_len); + + 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(); + } } } free(upgrade_data_buf); diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 7027895..2c3cdad 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -6,6 +6,7 @@ #else #include "EEPROM.h" #endif +#include #define EEPROM_CONFIG_SIZE 512 #define CONFIG_FILE_NAME "/cfg.bin" @@ -163,6 +164,7 @@ void Configuration::defaultConfig(void) { config.temperatureUnit = 'c'; config.ledBarBrightness = 100; config.displayBrightness = 100; + config.lastOta = 0; saveConfig(); } @@ -171,7 +173,10 @@ void Configuration::defaultConfig(void) { * @brief Show configuration as JSON string message over log * */ -void Configuration::printConfig(void) { logInfo(toString().c_str()); } +void Configuration::printConfig(void) { + logInfo(toString().c_str()); + logInfo("Last OTA time: " + String(config.lastOta)); +} /** * @brief Construct a new Ag Configure:: Ag Configure object @@ -638,6 +643,18 @@ bool Configuration::parse(String data, bool isLocal) { } } + if (JSON.typeof_(root["targetFirmware"]) == "string") { + String newVer = root["targetFirmware"]; + String curVer = String(GIT_VERSION); + if (curVer != newVer) { + logInfo("Detected new firwmare version: " + newVer); + otaNewFirmwareVersion = newVer; + udpated = true; + } else { + otaNewFirmwareVersion = String(""); + } + } + if (changed) { udpated = true; saveConfig(); @@ -938,3 +955,52 @@ bool Configuration::isDisplayBrightnessChanged(void) { displayBrightnessChanged = false; return changed; } + +/** + * @brief Get number of sec from last OTA + * + * @return int < 0 is invalid, 0 mean there is no OTA trigger. + */ +int Configuration::getLastOta(void) { + struct tm timeInfo; + if (getLocalTime(&timeInfo) == false) { + logError("Get localtime failed"); + return -1; + } + int curYear = timeInfo.tm_year + 1900; + if (curYear < 2024) { + logError("Current year " + String(curYear) + String(" invalid")); + return -1; + } + time_t curTime = mktime(&timeInfo); + logInfo("Last ota time: " + String(config.lastOta)); + if (config.lastOta == 0) { + return 0; + } + + int sec = curTime - config.lastOta; + logInfo("Last ota secconds: " + String(sec)); + return sec; +} + +void Configuration::updateLastOta(void) { + struct tm timeInfo; + if (getLocalTime(&timeInfo) == false) { + logError("updateLastOta: Get localtime failed"); + return; + } + int curYear = timeInfo.tm_year + 1900; + if (curYear < 2024) { + logError("updateLastOta: lolcal time invalid"); + return; + } + config.lastOta = mktime(&timeInfo); + logInfo("Last OTA: " + String(config.lastOta)); + saveConfig(); +} + +String Configuration::newFirmwareVersion(void) { + String newFw = otaNewFirmwareVersion; + otaNewFirmwareVersion = String(""); + return newFw; +} diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 6759ba6..bb07626 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -28,6 +28,7 @@ private: int tvocLearningOffset; int noxLearningOffset; char temperatureUnit; // 'f' or 'c' + time_t lastOta; uint32_t _check; }; @@ -40,6 +41,7 @@ private: bool _tvocLearningOffsetChanged; bool ledBarBrightnessChanged = false; bool displayBrightnessChanged = false; + String otaNewFirmwareVersion; AirGradient* ag; @@ -97,6 +99,9 @@ public: int getLedBarBrightness(void); bool isDisplayBrightnessChanged(void); int getDisplayBrightness(void); + int getLastOta(void); + void updateLastOta(void); + String newFirmwareVersion(void); }; #endif /** _AG_CONFIG_H_ */ diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 8a033bb..3e20c54 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -50,6 +50,16 @@ void OledDisplay::showTempHum(bool hasStatus) { } } +void OledDisplay::setCentralText(int y, String text) { + setCentralText(y, text.c_str()); +} + +void OledDisplay::setCentralText(int y, const char *text) { + int x = (DISP()->getWidth() - DISP()->getStrWidth(text)) / 2; + DISP()->drawStr(x, y, text); +} + + /** * @brief Construct a new Ag Oled Display:: Ag Oled Display object * @@ -314,3 +324,42 @@ void OledDisplay::showWiFiQrCode(String content, String label) { void OledDisplay::setBrightness(int percent) { DISP()->setContrast((127 * percent) / 100); } + +void OledDisplay::showNewFirmwareVersion(String version) { + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(40, "New version"); + setCentralText(60, version.c_str()); + } while (DISP()->nextPage()); +} + +void OledDisplay::showNewFirmwareUpdating(String percent) { + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(50, String("Updating... ") + percent + String("%")); + } while (DISP()->nextPage()); +} + +void OledDisplay::showNewFirmwareSuccess(String count) { + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(40, "Success"); + setCentralText(60, String("Rebooting... ") + count); + } while (DISP()->nextPage()); +} + +void OledDisplay::showNewFirmwareFailed(void) { + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(40, "Failed"); + setCentralText(60, String("Retry after 24h")); + } while (DISP()->nextPage()); +} diff --git a/src/AgOledDisplay.h b/src/AgOledDisplay.h index 69fb398..4a5044b 100644 --- a/src/AgOledDisplay.h +++ b/src/AgOledDisplay.h @@ -16,6 +16,9 @@ private: Measurements &value; void showTempHum(bool hasStatus); + void setCentralText(int y, String text); + void setCentralText(int y, const char *text); + public: OledDisplay(Configuration &config, Measurements &value, Stream &log); @@ -33,6 +36,10 @@ public: void showDashboard(const char *status); void showWiFiQrCode(String content, String label); void setBrightness(int percent); + void showNewFirmwareVersion(String version); + void showNewFirmwareUpdating(String percent); + void showNewFirmwareSuccess(String count); + void showNewFirmwareFailed(void); }; #endif /** _AG_OLED_DISPLAY_H_ */ diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 5efb6f7..57635fe 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -759,3 +759,47 @@ void StateMachine::executeCo2Calibration(void) { void StateMachine::executeLedBarTest(void) { handleLeds(AgStateMachineLedBarTest); } + +void StateMachine::executeOTA(StateMachine::OtaState state, String msg, + int processing) { + switch (state) { + case OtaState::OTA_STATE_BEGIN: { + if (ag->isOne()) { + disp.showNewFirmwareVersion(msg); + } else { + logInfo("New firmware: " + msg); + } + delay(2500); + break; + } + case OtaState::OTA_STATE_FAIL: { + if (ag->isOne()) { + disp.showNewFirmwareFailed(); + } else { + logError("Firmware update: failed"); + } + + delay(2500); + break; + } + case OtaState::OTA_STATE_PROCESSING: { + if (ag->isOne()) { + disp.showNewFirmwareUpdating(String(processing)); + } else { + logInfo("Firmware update: " + String(processing) + String("%")); + } + + break; + } + case OtaState::OTA_STATE_SUCCESS: { + if (ag->isOne()) { + disp.showNewFirmwareSuccess(String(processing)); + } else { + logInfo("Rebooting... " + String(processing)); + } + break; + } + default: + break; + } +} diff --git a/src/AgStateMachine.h b/src/AgStateMachine.h index 7299bb3..a783810 100644 --- a/src/AgStateMachine.h +++ b/src/AgStateMachine.h @@ -49,6 +49,14 @@ public: AgStateMachineState getLedState(void); void executeCo2Calibration(void); void executeLedBarTest(void); + + enum OtaState { + OTA_STATE_BEGIN, + OTA_STATE_FAIL, + OTA_STATE_PROCESSING, + OTA_STATE_SUCCESS + }; + void executeOTA(OtaState state, String msg, int processing); }; #endif /** _AG_STATE_MACHINE_H_ */ diff --git a/src/AgWiFiConnector.cpp b/src/AgWiFiConnector.cpp index 0e7f291..8ac9495 100644 --- a/src/AgWiFiConnector.cpp +++ b/src/AgWiFiConnector.cpp @@ -1,5 +1,6 @@ #include "AgWiFiConnector.h" #include "Libraries/WiFiManager/WiFiManager.h" +#include #define WIFI_CONNECT_COUNTDOWN_MAX 180 #define WIFI_HOTSPOT_PASSWORD_DEFAULT "cleanair" @@ -158,6 +159,11 @@ bool WifiConnector::connect(void) { config.setPostToAirGradient(result != "T"); } hasPortalConfig = false; + + /** Configure internet time */ + const char *ntp_server = "pool.ntp.org"; + configTime(0, 0, ntp_server); + logInfo("Set internet time server: " + String(ntp_server)); } #else _wifiProcess(); diff --git a/src/App/AppDef.h b/src/App/AppDef.h index 7009f92..2cc8ee8 100644 --- a/src/App/AppDef.h +++ b/src/App/AppDef.h @@ -60,6 +60,9 @@ enum AgStateMachineState { /* LED bar testing */ AgStateMachineLedBarTest, + /** OTA perform, show display status */ + AgStateMachineOtaPerform, + /** LED: Show working state. * Display: Show dashboard */ AgStateMachineNormal, From d2723de0f803c5113655275cfd61ceb7d262a39b Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Thu, 2 May 2024 18:22:26 +0700 Subject: [PATCH 2/5] Remove `OtaHandler` constructor --- examples/OneOpenAir/OneOpenAir.ino | 39 +++++++++++++--- examples/OneOpenAir/OtaHandler.h | 72 +++++++++++++++--------------- 2 files changed, 69 insertions(+), 42 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 52a4190..f3d5111 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -82,7 +82,7 @@ static WifiConnector wifiConnector(oledDisplay, Serial, stateMachine, configuration); static OpenMetrics openMetrics(measurements, configuration, wifiConnector, apiClient); -static OtaHandler otaHandler(stateMachine, configuration); +static OtaHandler otaHandler; static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector); @@ -93,6 +93,7 @@ static bool offlineMode = false; static AgFirmwareMode fwMode = FW_MODE_I_9PSL; static bool ledBarButtonTest = false; +static String fwNewVersion; static void boardInit(void); static void failedHandler(String msg); @@ -112,6 +113,7 @@ static void factoryConfigReset(void); static void wdgFeedUpdate(void); static void ledBarEnabledUpdate(void); static bool sgp41Init(void); +static void otaHandlerCallback(StateMachine::OtaState state, String mesasge); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule); AgSchedule configSchedule(SERVER_CONFIG_UPDATE_INTERVAL, @@ -157,7 +159,6 @@ void setup() { apiClient.setAirGradient(ag); openMetrics.setAirGradient(ag); localServer.setAirGraident(ag); - otaHandler.setAirGradient(ag); /** Connecting wifi */ bool connectToWifi = false; @@ -434,6 +435,25 @@ static bool sgp41Init(void) { return false; } +static void otaHandlerCallback(StateMachine::OtaState state, String mesasge) { + switch (state) { + case StateMachine::OtaState::OTA_STATE_BEGIN: + stateMachine.executeOTA(state, fwNewVersion, 0); + break; + case StateMachine::OtaState::OTA_STATE_FAIL: + stateMachine.executeOTA(state, "", 0); + break; + case StateMachine::OtaState::OTA_STATE_PROCESSING: + stateMachine.executeOTA(state, "", mesasge.toInt()); + break; + case StateMachine::OtaState::OTA_STATE_SUCCESS: + stateMachine.executeOTA(state, "", mesasge.toInt()); + break; + default: + break; + } +} + static void sendDataToAg() { /** Change oledDisplay and led state */ if (ag->isOne()) { @@ -734,9 +754,18 @@ static void configUpdateHandle() { String(configuration.getDisplayBrightness())); } - String newVer = configuration.newFirmwareVersion(); - if (newVer.length()) { - otaHandler.updateFirmwareIfOutdated(newVer); + fwNewVersion = configuration.newFirmwareVersion(); + if (fwNewVersion.length()) { + int lastOta = configuration.getLastOta(); + 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"); + } else { + configuration.updateLastOta(); + otaHandler.setHandlerCallback(otaHandlerCallback); + otaHandler.updateFirmwareIfOutdated(ag->deviceId()); + } } appDispHandler(); diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index 8775503..5fb7a31 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -1,13 +1,13 @@ #ifndef _OTA_HANDLER_H_ #define _OTA_HANDLER_H_ +#include "AgConfigure.h" +#include "AgStateMachine.h" +#include "AirGradient.h" #include #include #include #include -#include "AgConfigure.h" -#include "AgStateMachine.h" -#include "AirGradient.h" #define OTA_BUF_SIZE 1024 #define URL_BUF_SIZE 256 @@ -19,25 +19,14 @@ enum OtaUpdateOutcome { UDPATE_SKIPPED }; +typedef void(*OtaHandlerCallback_t)(StateMachine::OtaState state, + String message); + class OtaHandler { public: - 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; - } - - String url = - "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + - "/generic/os/firmware.bin"; + 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]; @@ -46,31 +35,30 @@ public: esp_http_client_config_t config = {}; config.url = urlAsChar; - OtaUpdateOutcome ret = attemptToPerformOta(&config, newVersion); - - // Update last OTA time whatever result. - this->config.updateLastOta(); - + OtaUpdateOutcome ret = attemptToPerformOta(&config); Serial.println(ret); if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { Serial.println("OTA update performed, restarting ..."); int i = 6; while (i != 0) { i = i - 1; - sm.executeOTA(StateMachine::OtaState::OTA_STATE_SUCCESS, "", i); + if (this->callback) { + this->callback(StateMachine::OtaState::OTA_STATE_SUCCESS, String(i)); + } delay(1000); } esp_restart(); } } -private: - AirGradient *ag; - StateMachine &sm; - Configuration &config; + void setHandlerCallback(OtaHandlerCallback_t callback) { + this->callback = callback; + } - OtaUpdateOutcome attemptToPerformOta(const esp_http_client_config_t *config, - String newVersion) { +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"); @@ -129,7 +117,9 @@ private: Serial.println("File size: " + String(totalSize) + String(" bytes")); // Show display start update new firmware. - sm.executeOTA(StateMachine::OtaState::OTA_STATE_BEGIN, newVersion, 0); + if (this->callback) { + this->callback(StateMachine::OtaState::OTA_STATE_BEGIN, ""); + } // Download file and write new firmware to OTA partition uint32_t lastUpdate = millis(); @@ -142,14 +132,18 @@ private: } if (data_read < 0) { Serial.println("Data read error"); - sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0); + if (this->callback) { + this->callback(StateMachine::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) { - sm.executeOTA(StateMachine::OtaState::OTA_STATE_FAIL, "", 0); + if (this->callback) { + this->callback(StateMachine::OtaState::OTA_STATE_FAIL, ""); + } break; } binary_file_len += data_read; @@ -157,8 +151,12 @@ private: 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); + // sm.executeOTA(StateMachine::OtaState::OTA_STATE_PROCESSING, "", + // percent); + if (this->callback) { + this->callback(StateMachine::OtaState::OTA_STATE_PROCESSING, + String(percent)); + } lastUpdate = millis(); } } From 8eb8d4a1ec77356e32067080b45fc94fa0b72e58 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Fri, 3 May 2024 17:27:05 +0700 Subject: [PATCH 3/5] Remove dependency from `StateMachine` --- examples/OneOpenAir/OneOpenAir.ino | 65 +++++++++++++++++++++++++----- examples/OneOpenAir/OtaHandler.h | 23 ++++++----- src/AgStateMachine.cpp | 44 -------------------- src/AgStateMachine.h | 8 ---- 4 files changed, 68 insertions(+), 72 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index f3d5111..f0f2038 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -113,7 +113,9 @@ static void factoryConfigReset(void); static void wdgFeedUpdate(void); static void ledBarEnabledUpdate(void); static bool sgp41Init(void); -static void otaHandlerCallback(StateMachine::OtaState state, String mesasge); +static void otaHandlerCallback(OtaState state, String mesasge); +static void displayExecuteOta(OtaState state, String msg, + int processing); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule); AgSchedule configSchedule(SERVER_CONFIG_UPDATE_INTERVAL, @@ -435,25 +437,68 @@ static bool sgp41Init(void) { return false; } -static void otaHandlerCallback(StateMachine::OtaState state, String mesasge) { +static void otaHandlerCallback(OtaState state, String mesasge) { switch (state) { - case StateMachine::OtaState::OTA_STATE_BEGIN: - stateMachine.executeOTA(state, fwNewVersion, 0); + case OtaState::OTA_STATE_BEGIN: + displayExecuteOta(state, fwNewVersion, 0); break; - case StateMachine::OtaState::OTA_STATE_FAIL: - stateMachine.executeOTA(state, "", 0); + case OtaState::OTA_STATE_FAIL: + displayExecuteOta(state, "", 0); break; - case StateMachine::OtaState::OTA_STATE_PROCESSING: - stateMachine.executeOTA(state, "", mesasge.toInt()); + case OtaState::OTA_STATE_PROCESSING: + displayExecuteOta(state, "", mesasge.toInt()); break; - case StateMachine::OtaState::OTA_STATE_SUCCESS: - stateMachine.executeOTA(state, "", mesasge.toInt()); + case OtaState::OTA_STATE_SUCCESS: + displayExecuteOta(state, "", mesasge.toInt()); break; default: break; } } +static void displayExecuteOta(OtaState state, String msg, int processing) { + switch (state) { + case OtaState::OTA_STATE_BEGIN: { + if (ag->isOne()) { + oledDisplay.showNewFirmwareVersion(msg); + } else { + Serial.println("New firmware: " + msg); + } + delay(2500); + break; + } + case OtaState::OTA_STATE_FAIL: { + if (ag->isOne()) { + oledDisplay.showNewFirmwareFailed(); + } else { + Serial.println("Error: Firmware update: failed"); + } + + delay(2500); + break; + } + case OtaState::OTA_STATE_PROCESSING: { + if (ag->isOne()) { + oledDisplay.showNewFirmwareUpdating(String(processing)); + } else { + Serial.println("Firmware update: " + String(processing) + String("%")); + } + + break; + } + case OtaState::OTA_STATE_SUCCESS: { + if (ag->isOne()) { + oledDisplay.showNewFirmwareSuccess(String(processing)); + } else { + Serial.println("Rebooting... " + String(processing)); + } + break; + } + default: + break; + } +} + static void sendDataToAg() { /** Change oledDisplay and led state */ if (ag->isOne()) { diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index 5fb7a31..f5ace5b 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -1,9 +1,5 @@ #ifndef _OTA_HANDLER_H_ #define _OTA_HANDLER_H_ - -#include "AgConfigure.h" -#include "AgStateMachine.h" -#include "AirGradient.h" #include #include #include @@ -19,7 +15,14 @@ enum OtaUpdateOutcome { UDPATE_SKIPPED }; -typedef void(*OtaHandlerCallback_t)(StateMachine::OtaState state, +enum OtaState { + OTA_STATE_BEGIN, + OTA_STATE_FAIL, + OTA_STATE_PROCESSING, + OTA_STATE_SUCCESS +}; + +typedef void(*OtaHandlerCallback_t)(OtaState state, String message); class OtaHandler { @@ -43,7 +46,7 @@ public: while (i != 0) { i = i - 1; if (this->callback) { - this->callback(StateMachine::OtaState::OTA_STATE_SUCCESS, String(i)); + this->callback(OtaState::OTA_STATE_SUCCESS, String(i)); } delay(1000); } @@ -118,7 +121,7 @@ private: // Show display start update new firmware. if (this->callback) { - this->callback(StateMachine::OtaState::OTA_STATE_BEGIN, ""); + this->callback(OtaState::OTA_STATE_BEGIN, ""); } // Download file and write new firmware to OTA partition @@ -133,7 +136,7 @@ private: if (data_read < 0) { Serial.println("Data read error"); if (this->callback) { - this->callback(StateMachine::OtaState::OTA_STATE_FAIL, ""); + this->callback(OtaState::OTA_STATE_FAIL, ""); } break; } @@ -142,7 +145,7 @@ private: update_handle, (const void *)upgrade_data_buf, data_read); if (ota_write_err != ESP_OK) { if (this->callback) { - this->callback(StateMachine::OtaState::OTA_STATE_FAIL, ""); + this->callback(OtaState::OTA_STATE_FAIL, ""); } break; } @@ -154,7 +157,7 @@ private: // sm.executeOTA(StateMachine::OtaState::OTA_STATE_PROCESSING, "", // percent); if (this->callback) { - this->callback(StateMachine::OtaState::OTA_STATE_PROCESSING, + this->callback(OtaState::OTA_STATE_PROCESSING, String(percent)); } lastUpdate = millis(); diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 57635fe..5efb6f7 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -759,47 +759,3 @@ void StateMachine::executeCo2Calibration(void) { void StateMachine::executeLedBarTest(void) { handleLeds(AgStateMachineLedBarTest); } - -void StateMachine::executeOTA(StateMachine::OtaState state, String msg, - int processing) { - switch (state) { - case OtaState::OTA_STATE_BEGIN: { - if (ag->isOne()) { - disp.showNewFirmwareVersion(msg); - } else { - logInfo("New firmware: " + msg); - } - delay(2500); - break; - } - case OtaState::OTA_STATE_FAIL: { - if (ag->isOne()) { - disp.showNewFirmwareFailed(); - } else { - logError("Firmware update: failed"); - } - - delay(2500); - break; - } - case OtaState::OTA_STATE_PROCESSING: { - if (ag->isOne()) { - disp.showNewFirmwareUpdating(String(processing)); - } else { - logInfo("Firmware update: " + String(processing) + String("%")); - } - - break; - } - case OtaState::OTA_STATE_SUCCESS: { - if (ag->isOne()) { - disp.showNewFirmwareSuccess(String(processing)); - } else { - logInfo("Rebooting... " + String(processing)); - } - break; - } - default: - break; - } -} diff --git a/src/AgStateMachine.h b/src/AgStateMachine.h index a783810..7299bb3 100644 --- a/src/AgStateMachine.h +++ b/src/AgStateMachine.h @@ -49,14 +49,6 @@ public: AgStateMachineState getLedState(void); void executeCo2Calibration(void); void executeLedBarTest(void); - - enum OtaState { - OTA_STATE_BEGIN, - OTA_STATE_FAIL, - OTA_STATE_PROCESSING, - OTA_STATE_SUCCESS - }; - void executeOTA(OtaState state, String msg, int processing); }; #endif /** _AG_STATE_MACHINE_H_ */ From 7c2f8e5b9b863981b2e4654f12f3d487a441fe18 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Tue, 7 May 2024 15:00:32 +0700 Subject: [PATCH 4/5] Add WiFi reset to factory default: connect to SSID `airgradient` after led bar test and button still keep pressed. --- examples/OneOpenAir/OneOpenAir.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 2f29ee8..29c0af2 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -51,6 +51,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #include "OpenMetrics.h" #include "WebServer.h" #include +#include #define LED_BAR_ANIMATION_PERIOD 100 /** ms */ #define DISP_UPDATE_INTERVAL 2500 /** ms */ @@ -163,6 +164,11 @@ void setup() { if (ag->isOne()) { if (ledBarButtonTest) { stateMachine.executeLedBarPowerUpTest(); + if (ag->button.getState() == PushButton::BUTTON_PRESSED) { + WiFi.begin("airgradient", "cleanair"); + Serial.println("WiFi Credential reset to factory defaults"); + ESP.restart(); + } } else { ledBarEnabledUpdate(); connectToWifi = true; From cca1ab69bbcb4afcd14e4f2a1ab05aba65c43f2e Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Wed, 8 May 2024 12:31:54 +0700 Subject: [PATCH 5/5] Move rebooting process out of `OtaHandler` --- examples/OneOpenAir/OneOpenAir.ino | 20 ++++++++++++++++---- examples/OneOpenAir/OtaHandler.h | 15 ++++++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index f0f2038..54c2815 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -487,10 +487,22 @@ static void displayExecuteOta(OtaState state, String msg, int processing) { break; } case OtaState::OTA_STATE_SUCCESS: { - if (ag->isOne()) { - oledDisplay.showNewFirmwareSuccess(String(processing)); - } else { - Serial.println("Rebooting... " + String(processing)); + int i = 6; + while(i != 0) { + i = i - 1; + Serial.println("OTA update performed, restarting ..."); + int i = 6; + while (i != 0) { + i = i - 1; + if (ag->isOne()) { + oledDisplay.showNewFirmwareSuccess(String(i)); + } else { + Serial.println("Rebooting... " + String(i)); + } + + delay(1000); + } + esp_restart(); } break; } diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index f5ace5b..3dc4242 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -41,16 +41,13 @@ public: OtaUpdateOutcome ret = attemptToPerformOta(&config); Serial.println(ret); if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { - Serial.println("OTA update performed, restarting ..."); - int i = 6; - while (i != 0) { - i = i - 1; - if (this->callback) { - this->callback(OtaState::OTA_STATE_SUCCESS, String(i)); - } - delay(1000); + if (this->callback) { + this->callback(OtaState::OTA_STATE_SUCCESS, ""); + } + } else { + if(this->callback) { + this->callback(OtaState::OTA_STATE_FAIL, ""); } - esp_restart(); } }