diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index ea1bb1b..eed4818 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -41,7 +41,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define LED_BAR_COUNT_INIT_VALUE (-1) /** */ #define LED_BAR_ANIMATION_PERIOD 100 /** ms */ #define DISP_UPDATE_INTERVAL 5000 /** ms */ -#define SERVER_CONFIG_UPDATE_INTERVAL 30000 /** ms */ +#define SERVER_CONFIG_SYNC_INTERVAL 60000 /** ms */ #define SERVER_SYNC_INTERVAL 60000 /** ms */ #define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */ #define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */ @@ -82,7 +82,7 @@ bool hasSensorPMS = true; bool hasSensorSHT = true; int pmFailCount = 0; int getCO2FailCount = 0; -AgSchedule configSchedule(SERVER_CONFIG_UPDATE_INTERVAL, +AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, updateServerConfiguration); AgSchedule serverSchedule(SERVER_SYNC_INTERVAL, sendDataToServer); AgSchedule dispSchedule(DISP_UPDATE_INTERVAL, dispHandler); diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 5edc5c0..d3ec231 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -55,7 +55,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define LED_BAR_ANIMATION_PERIOD 100 /** ms */ #define DISP_UPDATE_INTERVAL 2500 /** ms */ -#define SERVER_CONFIG_UPDATE_INTERVAL 15000 /** ms */ +#define SERVER_CONFIG_SYNC_INTERVAL 60000 /** ms */ #define SERVER_SYNC_INTERVAL 60000 /** ms */ #define MQTT_SYNC_INTERVAL 60000 /** ms */ #define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */ @@ -64,6 +64,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */ #define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */ #define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */ +#define FIRMWARE_CHECK_FOR_UPDATE_MS (60*60*1000) /** ms */ /** I2C define */ #define I2C_SDA_PIN 7 @@ -113,12 +114,13 @@ static void factoryConfigReset(void); static void wdgFeedUpdate(void); static void ledBarEnabledUpdate(void); static bool sgp41Init(void); +static void firmwareCheckForUpdate(void); 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, +AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, configurationUpdateSchedule); AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer); AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Update); @@ -126,6 +128,7 @@ AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, updatePm); AgSchedule tempHumSchedule(SENSOR_TEMP_HUM_UPDATE_INTERVAL, tempHumUpdate); AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc); AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate); +AgSchedule checkForUpdateSchedule(FIRMWARE_CHECK_FOR_UPDATE_MS, firmwareCheckForUpdate); void setup() { /** Serial for print debug message */ @@ -166,16 +169,6 @@ void setup() { /** Connecting wifi */ bool connectToWifi = false; if (ag->isOne()) { - if (ledBarButtonTest) { - if (ag->button.getState() == PushButton::BUTTON_PRESSED) { - WiFi.begin("airgradient", "cleanair"); - Serial.println("WiFi Credential reset to factory defaults"); - ESP.restart(); - } - } else { - ledBarEnabledUpdate(); - } - /** Show message confirm offline mode, should me perform if LED bar button * test pressed */ if (ledBarButtonTest == false) { @@ -219,10 +212,8 @@ void setup() { #ifdef ESP8266 // ota not supported #else - otaHandler.updateFirmwareIfOutdated(ag->deviceId()); - - /** Update first OTA */ - measurements.otaBootCount = 0; + firmwareCheckForUpdate(); + checkForUpdateSchedule.update(); #endif apiClient.fetchServerConfiguration(); @@ -241,8 +232,12 @@ void setup() { ledBarEnabledUpdate(); } } else { - oledDisplay.showRebooting(); - delay(2500); + if (wifiConnector.isConfigurePorttalTimeout()) { + oledDisplay.showRebooting(); + delay(2500); + oledDisplay.setText("", "", ""); + ESP.restart(); + } } } } @@ -312,6 +307,9 @@ void loop() { /** check that local configura changed then do some action */ configUpdateHandle(); + + /** Firmware check for update handle */ + checkForUpdateSchedule.run(); } static void co2Update(void) { @@ -420,6 +418,7 @@ static void factoryConfigReset(void) { } /** Reset WIFI */ + WiFi.enableSTA(true); // Incase offline mode WiFi.disconnect(true, true); /** Reset local config */ @@ -431,6 +430,7 @@ static void factoryConfigReset(void) { Serial.println("Factory reset successful"); } delay(3000); + oledDisplay.setText("","",""); ESP.restart(); } } @@ -488,7 +488,22 @@ static bool sgp41Init(void) { return false; } +static void firmwareCheckForUpdate(void) { + Serial.println(); + Serial.println("firmwareCheckForUpdate:"); + + if (wifiConnector.isConnected()) { + Serial.println("firmwareCheckForUpdate: Perform"); + otaHandler.setHandlerCallback(otaHandlerCallback); + otaHandler.updateFirmwareIfOutdated(ag->deviceId()); + } else { + Serial.println("firmwareCheckForUpdate: Ignored"); + } + Serial.println(); +} + static void otaHandlerCallback(OtaState state, String mesasge) { + Serial.println("OTA message: " + mesasge); switch (state) { case OtaState::OTA_STATE_BEGIN: displayExecuteOta(state, fwNewVersion, 0); @@ -511,7 +526,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) { switch (state) { case OtaState::OTA_STATE_BEGIN: { if (ag->isOne()) { - oledDisplay.showNewFirmwareVersion(msg); + oledDisplay.showFirmwareUpdateVersion(msg); } else { Serial.println("New firmware: " + msg); } @@ -520,7 +535,7 @@ static void displayExecuteOta(OtaState state, String msg, int processing) { } case OtaState::OTA_STATE_FAIL: { if (ag->isOne()) { - oledDisplay.showNewFirmwareFailed(); + oledDisplay.showFirmwareUpdateFailed(); } else { Serial.println("Error: Firmware update: failed"); } @@ -528,9 +543,29 @@ static void displayExecuteOta(OtaState state, String msg, int processing) { delay(2500); break; } + case OtaState::OTA_STATE_SKIP: { + if (ag->isOne()) { + oledDisplay.showFirmwareUpdateSkipped(); + } else { + Serial.println("Firmware update: Skipped"); + } + + delay(2500); + break; + } + case OtaState::OTA_STATE_UP_TO_DATE: { + if (ag->isOne()) { + oledDisplay.showFirmwareUpdateUpToDate(); + } else { + Serial.println("Firmware update: up to date"); + } + + delay(2500); + break; + } case OtaState::OTA_STATE_PROCESSING: { if (ag->isOne()) { - oledDisplay.showNewFirmwareUpdating(String(processing)); + oledDisplay.showFirmwareUpdateProgress(processing); } else { Serial.println("Firmware update: " + String(processing) + String("%")); } @@ -546,13 +581,14 @@ static void displayExecuteOta(OtaState state, String msg, int processing) { while (i != 0) { i = i - 1; if (ag->isOne()) { - oledDisplay.showNewFirmwareSuccess(String(i)); + oledDisplay.showFirmwareUpdateSuccess(i); } else { Serial.println("Rebooting... " + String(i)); } delay(1000); } + oledDisplay.setBrightness(0); esp_restart(); } break; @@ -641,6 +677,24 @@ static void oneIndoorInit(void) { } } + /** Check for button to reset WiFi connecto to "airgraident" after test LED + * bar */ + if (ledBarButtonTest) { + if (ag->button.getState() == ag->button.BUTTON_PRESSED) { + WiFi.begin("airgradient", "cleanair"); + oledDisplay.setText("Configure WiFi", "connect to", "\'airgradient\'"); + delay(2500); + oledDisplay.setText("Rebooting...", "",""); + delay(2500); + oledDisplay.setText("","",""); + ESP.restart(); + } + } + ledBarEnabledUpdate(); + + /** Show message init sensor */ + oledDisplay.setText("Sensor", "initializing...", ""); + /** Init sensor SGP41 */ if (sgp41Init() == false) { dispSensorNotFound("SGP41"); @@ -881,32 +935,6 @@ static void configUpdateHandle() { stateMachine.executeLedBarTest(); } - fwNewVersion = configuration.newFirmwareVersion(); - if (fwNewVersion.length()) { - bool doOta = false; - if (measurements.otaBootCount == 0) { - doOta = true; - Serial.println("First OTA"); - } else { - /** Only check for update each 1h*/ - const float otaBootCount = 60.0f / (SERVER_SYNC_INTERVAL / 60000.0f); - if ((measurements.bootCount - measurements.otaBootCount) >= (int)otaBootCount) { - doOta = true; - } else { - Serial.println( - "OTA ignore, try again next " + - String(30 - (measurements.bootCount - measurements.otaBootCount)) + - String(" boots")); - } - } - - if (doOta) { - measurements.otaBootCount = measurements.bootCount; - otaHandler.setHandlerCallback(otaHandlerCallback); - otaHandler.updateFirmwareIfOutdated(ag->deviceId()); - } - } - appDispHandler(); appLedHandler(); } diff --git a/examples/OneOpenAir/OtaHandler.h b/examples/OneOpenAir/OtaHandler.h index 3dc4242..a845f8b 100644 --- a/examples/OneOpenAir/OtaHandler.h +++ b/examples/OneOpenAir/OtaHandler.h @@ -18,6 +18,8 @@ enum OtaUpdateOutcome { enum OtaState { OTA_STATE_BEGIN, OTA_STATE_FAIL, + OTA_STATE_SKIP, + OTA_STATE_UP_TO_DATE, OTA_STATE_PROCESSING, OTA_STATE_SUCCESS }; @@ -40,13 +42,22 @@ public: config.url = urlAsChar; OtaUpdateOutcome ret = attemptToPerformOta(&config); Serial.println(ret); - if (ret == OtaUpdateOutcome::UPDATE_PERFORMED) { - if (this->callback) { + if (this->callback) { + switch (ret) { + case OtaUpdateOutcome::UPDATE_PERFORMED: this->callback(OtaState::OTA_STATE_SUCCESS, ""); - } - } else { - if(this->callback) { + 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; } } } @@ -127,6 +138,9 @@ private: 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; } diff --git a/library.properties b/library.properties index 6dfaf5b..2d34956 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AirGradient Air Quality Sensor -version=3.1.0-beta.1 +version=3.1.3 author=AirGradient maintainer=AirGradient sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display. diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index d59d09d..329d56f 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -330,7 +330,7 @@ void OledDisplay::setBrightness(int percent) { } } -void OledDisplay::showNewFirmwareVersion(String version) { +void OledDisplay::showFirmwareUpdateVersion(String version) { if (isDisplayOff) { return; } @@ -344,7 +344,7 @@ void OledDisplay::showNewFirmwareVersion(String version) { } while (DISP()->nextPage()); } -void OledDisplay::showNewFirmwareUpdating(String percent) { +void OledDisplay::showFirmwareUpdateProgress(int percent) { if (isDisplayOff) { return; } @@ -353,11 +353,11 @@ void OledDisplay::showNewFirmwareUpdating(String percent) { do { DISP()->setFont(u8g2_font_t0_16_tf); setCentralText(20, "Firmware Update"); - setCentralText(50, String("Updating... ") + percent + String("%")); + setCentralText(50, String("Updating... ") + String(percent) + String("%")); } while (DISP()->nextPage()); } -void OledDisplay::showNewFirmwareSuccess(String count) { +void OledDisplay::showFirmwareUpdateSuccess(int count) { if (isDisplayOff) { return; } @@ -367,11 +367,11 @@ void OledDisplay::showNewFirmwareSuccess(String count) { DISP()->setFont(u8g2_font_t0_16_tf); setCentralText(20, "Firmware Update"); setCentralText(40, "Success"); - setCentralText(60, String("Rebooting... ") + count); + setCentralText(60, String("Rebooting... ") + String(count)); } while (DISP()->nextPage()); } -void OledDisplay::showNewFirmwareFailed(void) { +void OledDisplay::showFirmwareUpdateFailed(void) { if (isDisplayOff) { return; } @@ -380,8 +380,34 @@ void OledDisplay::showNewFirmwareFailed(void) { do { DISP()->setFont(u8g2_font_t0_16_tf); setCentralText(20, "Firmware Update"); - setCentralText(40, "Failed"); - setCentralText(60, String("Retry after 24h")); + setCentralText(40, "fail, will retry"); + // setCentralText(60, "will retry"); + } while (DISP()->nextPage()); +} + +void OledDisplay::showFirmwareUpdateSkipped(void) { + if (isDisplayOff) { + return; + } + + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(40, "skipped"); + } while (DISP()->nextPage()); +} + +void OledDisplay::showFirmwareUpdateUpToDate(void) { + if (isDisplayOff) { + return; + } + + DISP()->firstPage(); + do { + DISP()->setFont(u8g2_font_t0_16_tf); + setCentralText(20, "Firmware Update"); + setCentralText(40, "up to date"); } while (DISP()->nextPage()); } diff --git a/src/AgOledDisplay.h b/src/AgOledDisplay.h index 65ce2de..85bd2e0 100644 --- a/src/AgOledDisplay.h +++ b/src/AgOledDisplay.h @@ -36,10 +36,12 @@ public: void showDashboard(void); void showDashboard(const char *status); void setBrightness(int percent); - void showNewFirmwareVersion(String version); - void showNewFirmwareUpdating(String percent); - void showNewFirmwareSuccess(String count); - void showNewFirmwareFailed(void); + void showFirmwareUpdateVersion(String version); + void showFirmwareUpdateProgress(int percent); + void showFirmwareUpdateSuccess(int count); + void showFirmwareUpdateFailed(void); + void showFirmwareUpdateSkipped(void); + void showFirmwareUpdateUpToDate(void); void showRebooting(void); }; diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 14580ff..c3bcc85 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -174,6 +174,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } root["boot"] = bootCount; + root["bootCount"] = bootCount; if (localServer) { root["ledMode"] = config->getLedBarModeName(); diff --git a/src/AgValue.h b/src/AgValue.h index 7446411..3176482 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -69,7 +69,6 @@ public: int countPosition; const int targetCount = 20; int bootCount; - int otaBootCount; String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void* _ag, void* _config); }; diff --git a/src/AgWiFiConnector.cpp b/src/AgWiFiConnector.cpp index c8a9d8d..f71a9cd 100644 --- a/src/AgWiFiConnector.cpp +++ b/src/AgWiFiConnector.cpp @@ -53,6 +53,7 @@ bool WifiConnector::connect(void) { WIFI()->setAPCallback([this](WiFiManager *obj) { _wifiApCallback(); }); WIFI()->setSaveConfigCallback([this]() { _wifiSaveConfig(); }); WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); }); + WIFI()->setConfigPortalTimeoutCallback([this](){}); if (ag->isOne()) { disp.setText("Connecting to", "WiFi", "..."); } else { @@ -245,6 +246,7 @@ void WifiConnector::_wifiSaveParamCallback(void) { bool WifiConnector::_wifiConfigPortalActive(void) { return WIFI()->getConfigPortalActive(); } +void WifiConnector::_wifiTimeoutCallback(void) { connectorTimeout = true; } #endif /** * @brief Process WiFiManager connection @@ -352,3 +354,11 @@ bool WifiConnector::hasConfigurated(void) { } return true; } + +/** + * @brief Get WiFi connection porttal timeout. + * + * @return true + * @return false + */ +bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; } diff --git a/src/AgWiFiConnector.h b/src/AgWiFiConnector.h index 090f0d2..b4e76a7 100644 --- a/src/AgWiFiConnector.h +++ b/src/AgWiFiConnector.h @@ -24,6 +24,7 @@ private: bool hasConfig; uint32_t lastRetry; bool hasPortalConfig = false; + bool connectorTimeout = false; bool wifiClientConnected(void); @@ -44,6 +45,7 @@ public: void _wifiSaveConfig(void); void _wifiSaveParamCallback(void); bool _wifiConfigPortalActive(void); + void _wifiTimeoutCallback(void); #endif void _wifiProcess(); bool isConnected(void); @@ -51,6 +53,7 @@ public: int RSSI(void); String localIpStr(void); bool hasConfigurated(void); + bool isConfigurePorttalTimeout(void); }; #endif /** _AG_WIFI_CONNECTOR_H_ */