From 59587ce2b784eb75883ae6f3dcfd0ed617d16688 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 11 Sep 2024 15:48:44 +0700 Subject: [PATCH 01/25] Add http request timeout number for ApiClient --- src/AgApiClient.cpp | 18 +++++++++++------- src/AgApiClient.h | 11 +++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index fe582a4..c49c0a3 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -2,13 +2,6 @@ #include "AgConfigure.h" #include "AirGradient.h" #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" -#ifdef ESP8266 -#include -#include -#include -#else -#include -#endif AgApiClient::AgApiClient(Stream &debug, Configuration &config) : PrintLog(debug, "ApiClient"), config(config) {} @@ -58,6 +51,7 @@ bool AgApiClient::fetchServerConfiguration(void) { } #else HTTPClient client; + _setHttpClientTimeout(&client); if (client.begin(uri) == false) { getConfigFailed = true; return false; @@ -121,6 +115,7 @@ bool AgApiClient::postToServer(String data) { WiFiClient wifiClient; HTTPClient client; + _setHttpClientTimeout(&client); if (client.begin(wifiClient, uri.c_str()) == false) { logError("Init client failed"); return false; @@ -190,3 +185,12 @@ bool AgApiClient::sendPing(int rssi, int bootCount) { String AgApiClient::getApiRoot() const { return apiRoot; } void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; } + +/** + * @brief Set timeout to both connect to server and tcp connection timeout + * + */ +void AgApiClient::_setHttpClientTimeout(HTTPClient *httpClient) { + httpClient->setTimeout(timeoutMs); + httpClient->setConnectTimeout(timeoutMs); +} \ No newline at end of file diff --git a/src/AgApiClient.h b/src/AgApiClient.h index 7e6037d..8027d04 100644 --- a/src/AgApiClient.h +++ b/src/AgApiClient.h @@ -16,6 +16,14 @@ #include "Main/PrintLog.h" #include +#ifdef ESP8266 +#include +#include +#include +#else +#include +#endif + class AgApiClient : public PrintLog { private: Configuration &config; @@ -25,6 +33,9 @@ private: bool getConfigFailed; bool postToServerFailed; bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard. + uint16_t timeoutMs = 10000; // Default set to 10s + + void _setHttpClientTimeout(HTTPClient *httpClient); public: AgApiClient(Stream &stream, Configuration &config); From f929623443da35b01cf952778070b8d766655c93 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 11 Sep 2024 16:01:16 +0700 Subject: [PATCH 02/25] Fix uri formatting postToServer to use apiRoot --- src/AgApiClient.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index c49c0a3..8af3004 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -107,9 +107,7 @@ bool AgApiClient::postToServer(String data) { return false; } - String uri = - "http://hw.airgradient.com/sensors/airgradient:" + ag->deviceId() + - "/measures"; + String uri = apiRoot + "/sensors/airgradient:" + ag->deviceId() + "/measures"; // logInfo("Post uri: " + uri); // logInfo("Post data: " + data); From e16373a64d3cc6ee0858cd5e2f76d2ffd9894664 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 11 Sep 2024 16:02:13 +0700 Subject: [PATCH 03/25] Add new public member to set http client timeout by caller --- src/AgApiClient.cpp | 9 +++++++++ src/AgApiClient.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index 8af3004..5b9bdb6 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -184,6 +184,15 @@ String AgApiClient::getApiRoot() const { return apiRoot; } void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; } +/** + * @brief Set http request timeout. (Default: 10s) + * + * @param timeoutMs + */ +void AgApiClient::setTimeout(uint16_t timeoutMs) { + this->timeoutMs = timeoutMs; +} + /** * @brief Set timeout to both connect to server and tcp connection timeout * diff --git a/src/AgApiClient.h b/src/AgApiClient.h index 8027d04..1c13100 100644 --- a/src/AgApiClient.h +++ b/src/AgApiClient.h @@ -51,6 +51,7 @@ public: bool sendPing(int rssi, int bootCount); String getApiRoot() const; void setApiRoot(const String &apiRoot); + void setTimeout(uint16_t timeoutMs); }; #endif /** _AG_API_CLIENT_H_ */ From 013fb94307e1a3fdd7b33ddf1de148ab109415b9 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 11 Sep 2024 16:37:50 +0700 Subject: [PATCH 04/25] Only for tcp timeout Ignoring connect to server timeout --- src/AgApiClient.cpp | 20 +++++++++----------- src/AgApiClient.h | 10 ---------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index 5b9bdb6..5933c30 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -2,6 +2,13 @@ #include "AgConfigure.h" #include "AirGradient.h" #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" +#ifdef ESP8266 +#include +#include +#include +#else +#include +#endif AgApiClient::AgApiClient(Stream &debug, Configuration &config) : PrintLog(debug, "ApiClient"), config(config) {} @@ -51,7 +58,7 @@ bool AgApiClient::fetchServerConfiguration(void) { } #else HTTPClient client; - _setHttpClientTimeout(&client); + client.setTimeout(timeoutMs); if (client.begin(uri) == false) { getConfigFailed = true; return false; @@ -113,7 +120,7 @@ bool AgApiClient::postToServer(String data) { WiFiClient wifiClient; HTTPClient client; - _setHttpClientTimeout(&client); + client.setTimeout(timeoutMs); if (client.begin(wifiClient, uri.c_str()) == false) { logError("Init client failed"); return false; @@ -191,13 +198,4 @@ void AgApiClient::setApiRoot(const String &apiRoot) { this->apiRoot = apiRoot; } */ void AgApiClient::setTimeout(uint16_t timeoutMs) { this->timeoutMs = timeoutMs; -} - -/** - * @brief Set timeout to both connect to server and tcp connection timeout - * - */ -void AgApiClient::_setHttpClientTimeout(HTTPClient *httpClient) { - httpClient->setTimeout(timeoutMs); - httpClient->setConnectTimeout(timeoutMs); } \ No newline at end of file diff --git a/src/AgApiClient.h b/src/AgApiClient.h index 1c13100..7035323 100644 --- a/src/AgApiClient.h +++ b/src/AgApiClient.h @@ -16,14 +16,6 @@ #include "Main/PrintLog.h" #include -#ifdef ESP8266 -#include -#include -#include -#else -#include -#endif - class AgApiClient : public PrintLog { private: Configuration &config; @@ -35,8 +27,6 @@ private: bool notAvailableOnDashboard = false; // Device not setup on Airgradient cloud dashboard. uint16_t timeoutMs = 10000; // Default set to 10s - void _setHttpClientTimeout(HTTPClient *httpClient); - public: AgApiClient(Stream &stream, Configuration &config); ~AgApiClient(); From 0d39643e761dc9d835e017ea8450ba96e8313eef Mon Sep 17 00:00:00 2001 From: nick-4711 Date: Sun, 15 Sep 2024 08:22:50 +0700 Subject: [PATCH 05/25] Explicitly set active mode for PM sensor upon initialization --- examples/OneOpenAir/OneOpenAir.ino | 20 ++++++++++---------- src/AgOledDisplay.cpp | 2 +- src/AgWiFiConnector.cpp | 2 +- src/PMS/PMS.cpp | 22 ++++++++++++++++++---- src/PMS/PMS.h | 3 +++ src/PMS/PMS5003.cpp | 7 ++----- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index ee7314e..2ade349 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -697,7 +697,7 @@ static void oneIndoorInit(void) { ledBarEnabledUpdate(); /** Show message init sensor */ - oledDisplay.setText("Sensor", "initializing...", ""); + oledDisplay.setText("Monitor", "initializing...", ""); /** Init sensor SGP41 */ if (sgp41Init() == false) { @@ -778,27 +778,27 @@ static void openAirInit(void) { } } - /** Try to find the PMS on other difference port with S8 */ + /** Attempt to detect PM sensors */ if (fwMode == FW_MODE_O_1PST) { bool pmInitSuccess = false; if (serial0Available) { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial0"); } else { serial0Available = false; pmInitSuccess = true; - Serial.println("Found PMS 1 on Serial0"); + Serial.println("Detected PM 1 on Serial0"); } } if (pmInitSuccess == false) { if (serial1Available) { if (ag->pms5003t_1.begin(Serial1) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial1"); } else { serial1Available = false; - Serial.println("Found PMS 1 on Serial1"); + Serial.println("Detected PM 1 on Serial1"); } } } @@ -806,15 +806,15 @@ static void openAirInit(void) { } else { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial0"); } else { - Serial.println("Found PMS 1 on Serial0"); + Serial.println("Detected PM 1 on Serial0"); } if (ag->pms5003t_2.begin(Serial1) == false) { configuration.hasSensorPMS2 = false; - Serial.println("PMS2 sensor not found"); + Serial.println("No PM sensor detected on Serial1"); } else { - Serial.println("Found PMS 2 on Serial1"); + Serial.println("Detected PM 2 on Serial1"); } if (fwMode == FW_MODE_O_1PP) { diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 109be27..8250407 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -519,7 +519,7 @@ void OledDisplay::showRebooting(void) { do { DISP()->setFont(u8g2_font_t0_16_tf); // setCentralText(20, "Firmware Update"); - setCentralText(40, "Reboot..."); + setCentralText(40, "Rebooting..."); // setCentralText(60, String("Retry after 24h")); } while (DISP()->nextPage()); } else if (ag->isBasic()) { diff --git a/src/AgWiFiConnector.cpp b/src/AgWiFiConnector.cpp index ddf4736..66fa34d 100644 --- a/src/AgWiFiConnector.cpp +++ b/src/AgWiFiConnector.cpp @@ -72,7 +72,7 @@ bool WifiConnector::connect(void) { WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); }); WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();}); if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { - disp.setText("Connect to", "WiFi", "..."); + disp.setText("Connecting to", "WiFi", "..."); } else { logInfo("Connecting to WiFi..."); } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 1107185..5b2ef27 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -2,34 +2,48 @@ #include "../Main/BoardDef.h" /** - * @brief Init and check that sensor has connected + * @brief Initializes the sensor and attempts to read data. * * @param stream UART stream * @return true Sucecss * @return false Failure */ bool PMSBase::begin(Stream *stream) { + Serial.printf("initializing PM sensor\n"); this->stream = stream; failed = true; failCount = 0; lastRead = 0; // To read buffer on handle without wait after 1.5sec - this->stream->flush(); + // empty first + int bytesCleared = 0; + while (this->stream->read() != -1) { + bytesCleared++; + } + Serial.printf("cleared %d byte(s)\n", bytesCleared); + + // explicitly put the sensor into active mode, this seems to be be needed for the Cubic PM2009X + Serial.printf("setting active mode\n"); + uint8_t activeModeCommand[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; + size_t bytesWritten = this->stream->write(activeModeCommand, sizeof(activeModeCommand)); + Serial.printf("%d byte(s) written\n", bytesWritten); // Run and check sensor data for 4sec while (1) { handle(); if (failed == false) { - return true; + Serial.printf("PM sensor initialized\n"); + return true; } - delay(1); + delay(10); uint32_t ms = (uint32_t)(millis() - lastRead); if (ms >= 4000) { break; } } + Serial.printf("PM sensor initialization failed\n"); return false; } diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index e79480e..3fd5108 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -5,6 +5,9 @@ #define PMS_FAIL_COUNT_SET_INVALID 3 +/** + * Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X + */ class PMSBase { public: bool begin(Stream *stream); diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index b632627..8daf3d8 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -38,14 +38,11 @@ bool PMS5003::begin(HardwareSerial &serial) { PMS5003::PMS5003(BoardType def) : _boardDef(def) {} /** - * @brief Init sensor - * - * @return true Success - * @return false Failure + * Initializes the sensor. */ bool PMS5003::begin(void) { if (this->_isBegin) { - AgLog("Initialized, call end() then try again"); + AgLog("Already initialized, call end() then try again"); return true; } From 90f336dee7077a56cbd7a2e262b4c1b54e80dccc Mon Sep 17 00:00:00 2001 From: nick-4711 Date: Sun, 15 Sep 2024 08:23:32 +0700 Subject: [PATCH 06/25] Revert "Explicitly set active mode for PM sensor upon initialization" This reverts commit 0d39643e761dc9d835e017ea8450ba96e8313eef. --- examples/OneOpenAir/OneOpenAir.ino | 20 ++++++++++---------- src/AgOledDisplay.cpp | 2 +- src/AgWiFiConnector.cpp | 2 +- src/PMS/PMS.cpp | 22 ++++------------------ src/PMS/PMS.h | 3 --- src/PMS/PMS5003.cpp | 7 +++++-- 6 files changed, 21 insertions(+), 35 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 2ade349..ee7314e 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -697,7 +697,7 @@ static void oneIndoorInit(void) { ledBarEnabledUpdate(); /** Show message init sensor */ - oledDisplay.setText("Monitor", "initializing...", ""); + oledDisplay.setText("Sensor", "initializing...", ""); /** Init sensor SGP41 */ if (sgp41Init() == false) { @@ -778,27 +778,27 @@ static void openAirInit(void) { } } - /** Attempt to detect PM sensors */ + /** Try to find the PMS on other difference port with S8 */ if (fwMode == FW_MODE_O_1PST) { bool pmInitSuccess = false; if (serial0Available) { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("No PM sensor detected on Serial0"); + Serial.println("PMS1 sensor not found"); } else { serial0Available = false; pmInitSuccess = true; - Serial.println("Detected PM 1 on Serial0"); + Serial.println("Found PMS 1 on Serial0"); } } if (pmInitSuccess == false) { if (serial1Available) { if (ag->pms5003t_1.begin(Serial1) == false) { configuration.hasSensorPMS1 = false; - Serial.println("No PM sensor detected on Serial1"); + Serial.println("PMS1 sensor not found"); } else { serial1Available = false; - Serial.println("Detected PM 1 on Serial1"); + Serial.println("Found PMS 1 on Serial1"); } } } @@ -806,15 +806,15 @@ static void openAirInit(void) { } else { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("No PM sensor detected on Serial0"); + Serial.println("PMS1 sensor not found"); } else { - Serial.println("Detected PM 1 on Serial0"); + Serial.println("Found PMS 1 on Serial0"); } if (ag->pms5003t_2.begin(Serial1) == false) { configuration.hasSensorPMS2 = false; - Serial.println("No PM sensor detected on Serial1"); + Serial.println("PMS2 sensor not found"); } else { - Serial.println("Detected PM 2 on Serial1"); + Serial.println("Found PMS 2 on Serial1"); } if (fwMode == FW_MODE_O_1PP) { diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 8250407..109be27 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -519,7 +519,7 @@ void OledDisplay::showRebooting(void) { do { DISP()->setFont(u8g2_font_t0_16_tf); // setCentralText(20, "Firmware Update"); - setCentralText(40, "Rebooting..."); + setCentralText(40, "Reboot..."); // setCentralText(60, String("Retry after 24h")); } while (DISP()->nextPage()); } else if (ag->isBasic()) { diff --git a/src/AgWiFiConnector.cpp b/src/AgWiFiConnector.cpp index 66fa34d..ddf4736 100644 --- a/src/AgWiFiConnector.cpp +++ b/src/AgWiFiConnector.cpp @@ -72,7 +72,7 @@ bool WifiConnector::connect(void) { WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); }); WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();}); if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { - disp.setText("Connecting to", "WiFi", "..."); + disp.setText("Connect to", "WiFi", "..."); } else { logInfo("Connecting to WiFi..."); } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 5b2ef27..1107185 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -2,48 +2,34 @@ #include "../Main/BoardDef.h" /** - * @brief Initializes the sensor and attempts to read data. + * @brief Init and check that sensor has connected * * @param stream UART stream * @return true Sucecss * @return false Failure */ bool PMSBase::begin(Stream *stream) { - Serial.printf("initializing PM sensor\n"); this->stream = stream; failed = true; failCount = 0; lastRead = 0; // To read buffer on handle without wait after 1.5sec - // empty first - int bytesCleared = 0; - while (this->stream->read() != -1) { - bytesCleared++; - } - Serial.printf("cleared %d byte(s)\n", bytesCleared); - - // explicitly put the sensor into active mode, this seems to be be needed for the Cubic PM2009X - Serial.printf("setting active mode\n"); - uint8_t activeModeCommand[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; - size_t bytesWritten = this->stream->write(activeModeCommand, sizeof(activeModeCommand)); - Serial.printf("%d byte(s) written\n", bytesWritten); + this->stream->flush(); // Run and check sensor data for 4sec while (1) { handle(); if (failed == false) { - Serial.printf("PM sensor initialized\n"); - return true; + return true; } - delay(10); + delay(1); uint32_t ms = (uint32_t)(millis() - lastRead); if (ms >= 4000) { break; } } - Serial.printf("PM sensor initialization failed\n"); return false; } diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index 3fd5108..e79480e 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -5,9 +5,6 @@ #define PMS_FAIL_COUNT_SET_INVALID 3 -/** - * Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X - */ class PMSBase { public: bool begin(Stream *stream); diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 8daf3d8..b632627 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -38,11 +38,14 @@ bool PMS5003::begin(HardwareSerial &serial) { PMS5003::PMS5003(BoardType def) : _boardDef(def) {} /** - * Initializes the sensor. + * @brief Init sensor + * + * @return true Success + * @return false Failure */ bool PMS5003::begin(void) { if (this->_isBegin) { - AgLog("Already initialized, call end() then try again"); + AgLog("Initialized, call end() then try again"); return true; } From 520550037d9ab0a65f7dde789153cbef9b53d93a Mon Sep 17 00:00:00 2001 From: nick-4711 Date: Sun, 15 Sep 2024 08:26:38 +0700 Subject: [PATCH 07/25] Explicitly set active mode for PM sensor upon initialization --- examples/OneOpenAir/OneOpenAir.ino | 20 ++++++++++---------- src/AgOledDisplay.cpp | 2 +- src/AgWiFiConnector.cpp | 2 +- src/PMS/PMS.cpp | 22 ++++++++++++++++++---- src/PMS/PMS.h | 3 +++ src/PMS/PMS5003.cpp | 7 ++----- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index ee7314e..2ade349 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -697,7 +697,7 @@ static void oneIndoorInit(void) { ledBarEnabledUpdate(); /** Show message init sensor */ - oledDisplay.setText("Sensor", "initializing...", ""); + oledDisplay.setText("Monitor", "initializing...", ""); /** Init sensor SGP41 */ if (sgp41Init() == false) { @@ -778,27 +778,27 @@ static void openAirInit(void) { } } - /** Try to find the PMS on other difference port with S8 */ + /** Attempt to detect PM sensors */ if (fwMode == FW_MODE_O_1PST) { bool pmInitSuccess = false; if (serial0Available) { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial0"); } else { serial0Available = false; pmInitSuccess = true; - Serial.println("Found PMS 1 on Serial0"); + Serial.println("Detected PM 1 on Serial0"); } } if (pmInitSuccess == false) { if (serial1Available) { if (ag->pms5003t_1.begin(Serial1) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial1"); } else { serial1Available = false; - Serial.println("Found PMS 1 on Serial1"); + Serial.println("Detected PM 1 on Serial1"); } } } @@ -806,15 +806,15 @@ static void openAirInit(void) { } else { if (ag->pms5003t_1.begin(Serial0) == false) { configuration.hasSensorPMS1 = false; - Serial.println("PMS1 sensor not found"); + Serial.println("No PM sensor detected on Serial0"); } else { - Serial.println("Found PMS 1 on Serial0"); + Serial.println("Detected PM 1 on Serial0"); } if (ag->pms5003t_2.begin(Serial1) == false) { configuration.hasSensorPMS2 = false; - Serial.println("PMS2 sensor not found"); + Serial.println("No PM sensor detected on Serial1"); } else { - Serial.println("Found PMS 2 on Serial1"); + Serial.println("Detected PM 2 on Serial1"); } if (fwMode == FW_MODE_O_1PP) { diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 109be27..8250407 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -519,7 +519,7 @@ void OledDisplay::showRebooting(void) { do { DISP()->setFont(u8g2_font_t0_16_tf); // setCentralText(20, "Firmware Update"); - setCentralText(40, "Reboot..."); + setCentralText(40, "Rebooting..."); // setCentralText(60, String("Retry after 24h")); } while (DISP()->nextPage()); } else if (ag->isBasic()) { diff --git a/src/AgWiFiConnector.cpp b/src/AgWiFiConnector.cpp index ddf4736..66fa34d 100644 --- a/src/AgWiFiConnector.cpp +++ b/src/AgWiFiConnector.cpp @@ -72,7 +72,7 @@ bool WifiConnector::connect(void) { WIFI()->setSaveParamsCallback([this]() { _wifiSaveParamCallback(); }); WIFI()->setConfigPortalTimeoutCallback([this]() {_wifiTimeoutCallback();}); if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { - disp.setText("Connect to", "WiFi", "..."); + disp.setText("Connecting to", "WiFi", "..."); } else { logInfo("Connecting to WiFi..."); } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 1107185..5b2ef27 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -2,34 +2,48 @@ #include "../Main/BoardDef.h" /** - * @brief Init and check that sensor has connected + * @brief Initializes the sensor and attempts to read data. * * @param stream UART stream * @return true Sucecss * @return false Failure */ bool PMSBase::begin(Stream *stream) { + Serial.printf("initializing PM sensor\n"); this->stream = stream; failed = true; failCount = 0; lastRead = 0; // To read buffer on handle without wait after 1.5sec - this->stream->flush(); + // empty first + int bytesCleared = 0; + while (this->stream->read() != -1) { + bytesCleared++; + } + Serial.printf("cleared %d byte(s)\n", bytesCleared); + + // explicitly put the sensor into active mode, this seems to be be needed for the Cubic PM2009X + Serial.printf("setting active mode\n"); + uint8_t activeModeCommand[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; + size_t bytesWritten = this->stream->write(activeModeCommand, sizeof(activeModeCommand)); + Serial.printf("%d byte(s) written\n", bytesWritten); // Run and check sensor data for 4sec while (1) { handle(); if (failed == false) { - return true; + Serial.printf("PM sensor initialized\n"); + return true; } - delay(1); + delay(10); uint32_t ms = (uint32_t)(millis() - lastRead); if (ms >= 4000) { break; } } + Serial.printf("PM sensor initialization failed\n"); return false; } diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index e79480e..3fd5108 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -5,6 +5,9 @@ #define PMS_FAIL_COUNT_SET_INVALID 3 +/** + * Known to work with these sensors: Plantower PMS5003, Plantower PMS5003, Cubic PM2009X + */ class PMSBase { public: bool begin(Stream *stream); diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index b632627..8daf3d8 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -38,14 +38,11 @@ bool PMS5003::begin(HardwareSerial &serial) { PMS5003::PMS5003(BoardType def) : _boardDef(def) {} /** - * @brief Init sensor - * - * @return true Success - * @return false Failure + * Initializes the sensor. */ bool PMS5003::begin(void) { if (this->_isBegin) { - AgLog("Initialized, call end() then try again"); + AgLog("Already initialized, call end() then try again"); return true; } From 826ff00f42d94dc00e7304311b7cb146dcab494f Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Mon, 16 Sep 2024 10:36:45 +0700 Subject: [PATCH 08/25] add log message PM sensor firmware version --- examples/BASIC/BASIC.ino | 1 + examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 1 + examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 1 + examples/OneOpenAir/OneOpenAir.ino | 5 ++++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 7419f13..2a574e0 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -513,6 +513,7 @@ static void updatePm(void) { Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1); Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); + Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion()); ag.pms5003.resetFailCount(); } else { ag.pms5003.updateFailCount(); diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index d842fff..743aafc 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -565,6 +565,7 @@ static void updatePm(void) { Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1); Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); + Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion()); ag.pms5003.resetFailCount(); } else { ag.pms5003.updateFailCount(); diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index 305d4a2..e0a8c34 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -606,6 +606,7 @@ static void updatePm(void) { Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1); Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); + Serial.printf("PM firmware version: %d\r\n", ag.pms5003.getFirmwareVersion()); ag.pms5003.resetFailCount(); } else { ag.pms5003.updateFailCount(); diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index ee7314e..b7f6a2e 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1020,10 +1020,11 @@ static void updatePm(void) { Serial.printf("PM2.5 ug/m3: %d\r\n", measurements.pm25_1); Serial.printf("PM10 ug/m3: %d\r\n", measurements.pm10_1); Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); + Serial.printf("PM firmware version: %d\r\n", ag->pms5003.getFirmwareVersion()); ag->pms5003.resetFailCount(); } else { ag->pms5003.updateFailCount(); - Serial.printf("PMS read faile %d times\r\n", ag->pms5003.getFailCount()); + Serial.printf("PMS read failed %d times\r\n", ag->pms5003.getFailCount()); if (ag->pms5003.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) { measurements.pm01_1 = utils::getInvalidPmValue(); measurements.pm25_1 = utils::getInvalidPmValue(); @@ -1059,6 +1060,7 @@ static void updatePm(void) { ag->pms5003t_1.compensateTemp(measurements.temp_1)); Serial.printf("[1] Relative Humidity compensated: %0.2f\r\n", ag->pms5003t_1.compensateHum(measurements.hum_1)); + Serial.printf("[1] PM firmware version: %d\r\n", ag->pms5003t_1.getFirmwareVersion()); ag->pms5003t_1.resetFailCount(); } else { @@ -1102,6 +1104,7 @@ static void updatePm(void) { ag->pms5003t_1.compensateTemp(measurements.temp_2)); Serial.printf("[2] Relative Humidity compensated: %0.2f\r\n", ag->pms5003t_1.compensateHum(measurements.hum_2)); + Serial.printf("[2] PM firmware version: %d\r\n", ag->pms5003t_2.getFirmwareVersion()); ag->pms5003t_2.resetFailCount(); } else { From 994d281e0296c5ef527d9b9c28ceb85dcd46fea6 Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:47:08 +0700 Subject: [PATCH 09/25] Update Version to 3.1.8 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index f6af924..0a9ed39 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AirGradient Air Quality Sensor -version=3.1.7 +version=3.1.8 author=AirGradient maintainer=AirGradient sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display. From b7cfdc4c4d87b0466f2688ad545e55925d5e9229 Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:47:50 +0700 Subject: [PATCH 10/25] Update AirGradient.h to v 3.1.8 --- src/AirGradient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AirGradient.h b/src/AirGradient.h index 1a7a0fc..31d296f 100644 --- a/src/AirGradient.h +++ b/src/AirGradient.h @@ -15,7 +15,7 @@ #include "Main/utils.h" #ifndef GIT_VERSION -#define GIT_VERSION "3.1.7-snap" +#define GIT_VERSION "3.1.8-snap" #endif /** From c8d5b546ed8c28ffcf2209c13a32600bdc3677c2 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Mon, 16 Sep 2024 14:52:04 +0700 Subject: [PATCH 11/25] correct PM compensate the input argument value humidity instead of temperature, fix #234 --- src/AgValue.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index c7ec1a2..867ee42 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -130,8 +130,8 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } - int pm25 = (ag->pms5003t_1.compensate(this->pm25_1, this->temp_1) + - ag->pms5003t_2.compensate(this->pm25_2, this->temp_2)) / + int pm25 = (ag->pms5003t_1.compensate(this->pm25_1, this->hum_1) + + ag->pms5003t_2.compensate(this->pm25_2, this->hum_2)) / 2; root["pm02Compensated"] = pm25; } @@ -171,7 +171,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); if (!localServer) { root[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); @@ -212,7 +212,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->temp_2); + root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2); if(!localServer) { root[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); @@ -253,7 +253,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); if(!localServer) { root[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); @@ -291,7 +291,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); + root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); if(!localServer) { root[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); @@ -332,7 +332,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->temp_1); + root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); // PMS5003T version if(!localServer) { @@ -374,7 +374,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } } } - root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->temp_2); + root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2); // PMS5003T version if(!localServer) { root["channels"]["2"][json_prop_pmFirmware] = From 507b958fdf7e59aa1f6c89a7b6a4cd8408c70213 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Tue, 17 Sep 2024 10:15:47 +0700 Subject: [PATCH 12/25] Correct LED bar show PM value use compensate --- src/AgStateMachine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index e5566e2..f2e6105 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -141,7 +141,7 @@ void StateMachine::co2handleLeds(void) { * */ void StateMachine::pm25handleLeds(void) { - int pm25Value = value.pm25_1; + int pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity); if (pm25Value < 5) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); From ca5fc8d65b05d78bc9db4dccc85344cc4631d8f0 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Wed, 18 Sep 2024 12:10:23 +0700 Subject: [PATCH 13/25] fix WiFi reset --- examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index e0a8c34..5394f82 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -327,7 +327,7 @@ static void factoryConfigReset(void) { // } /** Reset WIFI */ - WiFi.disconnect(true, true); + wifiConnector.reset(); /** Reset local config */ configuration.reset(); From c4024f49fbeedd9fc3bac77203352c4086585287 Mon Sep 17 00:00:00 2001 From: Dmitry Pustovit Date: Fri, 20 Sep 2024 00:36:34 -0700 Subject: [PATCH 14/25] Added support clearing display at 0 brightness for DIY Boards to AgOledDisplay. Currently, the only affect the brightness setting has with the DIY boards is an attempt to set the contrast. Setting the contrast to 0 does not have any effect. This appears to be a know limitation for these display boards. --- src/AgOledDisplay.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 8250407..8396e33 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -424,7 +424,17 @@ void OledDisplay::setBrightness(int percent) { DISP()->setContrast((127 * percent) / 100); } } else if (ag->isBasic()) { - ag->display.setContrast((255 * percent) / 100); + if (percent == 0) { + isDisplayOff = true; + + // Clear display. + ag->display.clear(); + ag->display.show(); + } + else { + isDisplayOff = false; + ag->display.setContrast((255 * percent) / 100); + } } } From 76e40fea8cca10a7fe619930531ea9e4c941aa27 Mon Sep 17 00:00:00 2001 From: Achim Date: Sat, 21 Sep 2024 08:09:58 +0700 Subject: [PATCH 15/25] let hw watchdog run independently of POST success --- examples/BASIC/BASIC.ino | 8 +------- examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 8 +------- examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 8 +------- examples/OneOpenAir/OneOpenAir.ino | 8 +------- 4 files changed, 4 insertions(+), 28 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 2a574e0..01c394d 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -206,11 +206,7 @@ void loop() { tvocSchedule.run(); } - /** Auto reset watchdog timer if offline mode or postDataToAirGradient */ - if (configuration.isOfflineMode() || - (configuration.isPostDataToAirGradient() == false)) { - watchdogFeedSchedule.run(); - } + watchdogFeedSchedule.run(); /** Check for handle WiFi reconnect */ wifiConnector.handle(); @@ -542,13 +538,11 @@ static void sendDataToServer(void) { String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), &ag, &configuration); if (apiClient.postToServer(syncData)) { - ag.watchdog.reset(); Serial.println(); Serial.println( "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index 743aafc..bdd79c7 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -204,11 +204,7 @@ void loop() { tvocSchedule.run(); } - /** Auto reset watchdog timer if offline mode or postDataToAirGradient */ - if (configuration.isOfflineMode() || - (configuration.isPostDataToAirGradient() == false)) { - watchdogFeedSchedule.run(); - } + watchdogFeedSchedule.run(); /** Check for handle WiFi reconnect */ wifiConnector.handle(); @@ -594,13 +590,11 @@ static void sendDataToServer(void) { String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), &ag, &configuration); if (apiClient.postToServer(syncData)) { - ag.watchdog.reset(); Serial.println(); Serial.println( "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index 5394f82..dca098c 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -231,11 +231,7 @@ void loop() { tvocSchedule.run(); } - /** Auto reset watchdog timer if offline mode or postDataToAirGradient */ - if (configuration.isOfflineMode() || - (configuration.isPostDataToAirGradient() == false)) { - watchdogFeedSchedule.run(); - } + watchdogFeedSchedule.run(); /** Check for handle WiFi reconnect */ wifiConnector.handle(); @@ -635,13 +631,11 @@ static void sendDataToServer(void) { String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), &ag, &configuration); if (apiClient.postToServer(syncData)) { - ag.watchdog.reset(); Serial.println(); Serial.println( "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index b5c9518..5495308 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -298,11 +298,7 @@ void loop() { } } - /** Auto reset watchdog timer if offline mode or postDataToAirGradient */ - if (configuration.isOfflineMode() || - (configuration.isPostDataToAirGradient() == false)) { - watchdogFeedSchedule.run(); - } + watchdogFeedSchedule.run(); /** Check for handle WiFi reconnect */ wifiConnector.handle(); @@ -1240,13 +1236,11 @@ static void sendDataToServer(void) { String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, &configuration); if (apiClient.postToServer(syncData)) { - ag->watchdog.reset(); Serial.println(); Serial.println( "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } From 3890919f5419b0d8dbbec809081254c67f19d1a5 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sat, 21 Sep 2024 08:46:05 +0700 Subject: [PATCH 16/25] Update log message --- examples/BASIC/BASIC.ino | 4 +--- examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 4 +--- examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 4 +--- examples/OneOpenAir/OneOpenAir.ino | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 01c394d..9b29fd0 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -271,9 +271,7 @@ static void initMqtt(void) { static void wdgFeedUpdate(void) { ag.watchdog.reset(); - Serial.println(); - Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset"); - Serial.println(); + Serial.println("External watchdog feed!"); } static bool sgp41Init(void) { diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index bdd79c7..c31f830 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -328,9 +328,7 @@ static void factoryConfigReset(void) { static void wdgFeedUpdate(void) { ag.watchdog.reset(); - Serial.println(); - Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset"); - Serial.println(); + Serial.println("External watchdog feed!"); } static bool sgp41Init(void) { diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index dca098c..71b0a66 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -351,9 +351,7 @@ static void factoryConfigReset(void) { static void wdgFeedUpdate(void) { ag.watchdog.reset(); - Serial.println(); - Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset"); - Serial.println(); + Serial.println("External watchdog feed!"); } static bool sgp41Init(void) { diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 5495308..1b9e8a5 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -455,9 +455,7 @@ static void factoryConfigReset(void) { static void wdgFeedUpdate(void) { ag->watchdog.reset(); - Serial.println(); - Serial.println("Offline mode or isPostToAirGradient = false: watchdog reset"); - Serial.println(); + Serial.println("External watchdog feed!"); } static void ledBarEnabledUpdate(void) { From 20245f2110787dd462749b241931d61ce3895e88 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sat, 21 Sep 2024 13:06:01 +0700 Subject: [PATCH 17/25] Saving work --- docs/local-server.md | 87 ++++++++++++++++++++++--------------------- src/AgConfigure.cpp | 68 +++++++++++++++++++++++---------- src/AgConfigure.h | 1 + src/AgOledDisplay.cpp | 76 +++++++++++++++++++++---------------- src/AgOledDisplay.h | 2 +- src/Main/utils.cpp | 5 +++ src/Main/utils.h | 1 + 7 files changed, 145 insertions(+), 95 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 1daede0..25cab52 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -41,33 +41,35 @@ You get the following response: "bootCount": 6, "ledMode": "pm", "firmware": "3.1.3", - "model": "I-9PSL" + "model": "I-9PSL", + "monitorDisplayCompensatedValues": true } ``` -| Properties | Type | Explanation | -|------------------|--------|--------------------------------------------------------------------| -| `serialno` | String | Serial Number of the monitor | -| `wifi` | Number | WiFi signal strength | -| `pm01` | Number | PM1 in ug/m3 | -| `pm02` | Number | PM2.5 in ug/m3 | -| `pm10` | Number | PM10 in ug/m3 | -| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) | -| `rco2` | Number | CO2 in ppm | -| `pm003Count` | Number | Particle count per dL | -| `atmp` | Number | Temperature in Degrees Celsius | -| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | -| `rhum` | Number | Relative Humidity | -| `rhumCompensated` | Number | Relative Humidity with correction applied | -| `tvocIndex` | Number | Senisiron VOC Index | -| `tvocRaw` | Number | VOC raw value | -| `noxIndex` | Number | Senisirion NOx Index | -| `noxRaw` | Number | NOx raw value | -| `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. | -| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. | -| `ledMode` | String | Current configuration of the LED mode | -| `firmware` | String | Current firmware version | -| `model` | String | Current model name | +| Properties | Type | Explanation | +|-----------------------------------|---------|----------------------------------------------------------------------------------------| +| `serialno` | String | Serial Number of the monitor | +| `wifi` | Number | WiFi signal strength | +| `pm01` | Number | PM1 in ug/m3 | +| `pm02` | Number | PM2.5 in ug/m3 | +| `pm10` | Number | PM10 in ug/m3 | +| `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) | +| `rco2` | Number | CO2 in ppm | +| `pm003Count` | Number | Particle count per dL | +| `atmp` | Number | Temperature in Degrees Celsius | +| `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | +| `rhum` | Number | Relative Humidity | +| `rhumCompensated` | Number | Relative Humidity with correction applied | +| `tvocIndex` | Number | Senisiron VOC Index | +| `tvocRaw` | Number | VOC raw value | +| `noxIndex` | Number | Senisirion NOx Index | +| `noxRaw` | Number | NOx raw value | +| `boot` | Number | Counts every measurement cycle. Low boot counts indicate restarts. | +| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. Will be depreciated. | +| `ledMode` | String | Current configuration of the LED mode | +| `firmware` | String | Current firmware version | +| `model` | String | Current model name | +| `monitorDisplayCompensatedValues` | Boolean | Switching Display of AirGradient ONE to Compensated / Non Compensated Values | Compensated values apply correction algorithms to make the sensor values more accurate. Temperature and relative humidity correction is only applied on the outdoor monitor Open Air but the properties _compensated will still be send also for the indoor monitor AirGradient ONE. @@ -110,21 +112,22 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf #### Configuration Parameters (GET/PUT) -| Properties | Description | Type | Accepted Values | Example | -|-------------------------|:-------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| -| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | {"country": "TH"} | -| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | {"model": "I-9PSL-DE"} | -| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3
`us-aqi`: USAQI | {"pmStandard": "ugm3"} | -| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | {"ledBarMode": "off"} | -| `displayBrightness` | Brightness of the Display. | Number | 0-100 | {"displayBrightness": 50} | -| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | {"ledBarBrightness": 40} | -| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | {"abcDays": 8} | -| `mqttBrokerUrl` | MQTT broker URL. | String | | {"mqttBrokerUrl": "mqtt://192.168.0.18:1883"} | -| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C
`f` or `F`: Degree Fahrenheit °F | {"temperatureUnit": "c"} | -| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration
`local`: Accept only local configuration
`cloud`: Accept only cloud configuration | {"configurationControl": "both"} | -| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | {"postDataToAirGradient": true} | -| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | {"co2CalibrationRequested": true} | -| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | {"ledBarTestRequested": true} | -| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | {"noxLearningOffset": 12} | -| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | {"tvocLearningOffset": 12} | -| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | {"offlineMode": true} | +| Properties | Description | Type | Accepted Values | Example | +|-----------------------------------|:-----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------| +| `country` | Country where the device is. | String | Country code as [ALPHA-2 notation](https://www.iban.com/country-codes) | `{"country": "TH"}` | +| `model` | Hardware identifier (only GET). | String | I-9PSL-DE | `{"model": "I-9PSL-DE"}` | +| `pmStandard` | Particle matter standard used on the display. | String | `ugm3`: ug/m3
`us-aqi`: USAQI | `{"pmStandard": "ugm3"}` | +| `ledBarMode` | Mode in which the led bar can be set. | String | `co2`: LED bar displays CO2
`pm`: LED bar displays PM
`off`: Turn off LED bar | `{"ledBarMode": "off"}` | +| `displayBrightness` | Brightness of the Display. | Number | 0-100 | `{"displayBrightness": 50}` | +| `ledBarBrightness` | Brightness of the LEDBar. | Number | 0-100 | `{"ledBarBrightness": 40}` | +| `abcDays` | Number of days for CO2 automatic baseline calibration. | Number | Maximum 200 days. Default 8 days. | `{"abcDays": 8}` | +| `mqttBrokerUrl` | MQTT broker URL. | String | | `{"mqttBrokerUrl": "mqtt://192.168.0.18:1883"}` | +| `temperatureUnit` | Temperature unit shown on the display. | String | `c` or `C`: Degree Celsius °C
`f` or `F`: Degree Fahrenheit °F | `{"temperatureUnit": "c"}` | +| `configurationControl` | The configuration source of the device. | String | `both`: Accept local and cloud configuration
`local`: Accept only local configuration
`cloud`: Accept only cloud configuration | `{"configurationControl": "both"}` | +| `postDataToAirGradient` | Send data to AirGradient cloud. | Boolean | `true`: Enabled
`false`: Disabled | `{"postDataToAirGradient": true}` | +| `co2CalibrationRequested` | Can be set to trigger a calibration. | Boolean | `true`: CO2 calibration (400ppm) will be triggered | `{"co2CalibrationRequested": true}` | +| `ledBarTestRequested` | Can be set to trigger a test. | Boolean | `true` : LEDs will run test sequence | `{"ledBarTestRequested": true}` | +| `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | `{"noxLearningOffset": 12}` | +| `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | `{"tvocLearningOffset": 12}` | +| `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | `{"offlineMode": true}` | +| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (From [3.1.8]()) | Boolean | `false`: Without compensate (default)
`true`: with compensate | `{"monitorDisplayCompensatedValues": false }` | diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 99e00c1..f3abd7c 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -41,21 +41,23 @@ JSON_PROP_DEF(displayBrightness); JSON_PROP_DEF(co2CalibrationRequested); JSON_PROP_DEF(ledBarTestRequested); JSON_PROP_DEF(offlineMode); +JSON_PROP_DEF(monitorDisplayCompensatedValues); -#define jprop_model_default "" -#define jprop_country_default "TH" -#define jprop_pmStandard_default getPMStandardString(false) -#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2) -#define jprop_abcDays_default 8 -#define jprop_tvocLearningOffset_default 12 -#define jprop_noxLearningOffset_default 12 -#define jprop_mqttBrokerUrl_default "" -#define jprop_temperatureUnit_default "c" -#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth]) -#define jprop_postDataToAirGradient_default true -#define jprop_ledBarBrightness_default 100 -#define jprop_displayBrightness_default 100 -#define jprop_offlineMode_default false +#define jprop_model_default "" +#define jprop_country_default "TH" +#define jprop_pmStandard_default getPMStandardString(false) +#define jprop_ledBarMode_default getLedBarModeName(LedBarMode::LedBarModeCO2) +#define jprop_abcDays_default 8 +#define jprop_tvocLearningOffset_default 12 +#define jprop_noxLearningOffset_default 12 +#define jprop_mqttBrokerUrl_default "" +#define jprop_temperatureUnit_default "c" +#define jprop_configurationControl_default String(CONFIGURATION_CONTROL_NAME[ConfigurationControl::ConfigurationControlBoth]) +#define jprop_postDataToAirGradient_default true +#define jprop_ledBarBrightness_default 100 +#define jprop_displayBrightness_default 100 +#define jprop_offlineMode_default false +#define jprop_monitorDisplayCompensatedValues_default false JSONVar jconfig; @@ -167,6 +169,7 @@ void Configuration::defaultConfig(void) { jconfig[jprop_abcDays] = jprop_abcDays_default; jconfig[jprop_model] = jprop_model_default; jconfig[jprop_offlineMode] = jprop_offlineMode_default; + jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default; saveConfig(); } @@ -628,6 +631,27 @@ bool Configuration::parse(String data, bool isLocal) { } } + if (JSON.typeof_(root[jprop_monitorDisplayCompensatedValues]) == "boolean") { + bool value = root[jprop_monitorDisplayCompensatedValues]; + bool oldValue = jconfig[jprop_monitorDisplayCompensatedValues]; + if (value != oldValue) { + changed = true; + jconfig[jprop_monitorDisplayCompensatedValues] = value; + + configLogInfo(String(jprop_monitorDisplayCompensatedValues), + String(oldValue ? "true" : "false"), + String(value ? "true" : "false")); + } + } else { + if (jsonTypeInvalid(root[jprop_monitorDisplayCompensatedValues], + "boolean")) { + failedMessage = jsonTypeInvalidMessage( + String(jprop_monitorDisplayCompensatedValues), "boolean"); + jsonInvalid(); + return false; + } + } + if (ag->getBoardType() == ONE_INDOOR || ag->getBoardType() == OPEN_AIR_OUTDOOR) { if (JSON.typeof_(root["targetFirmware"]) == "string") { @@ -1082,12 +1106,14 @@ void Configuration::toConfig(const char *buf) { } if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") { - isInvalid = true; - } else { - isInvalid = false; + jconfig[jprop_offlineMode] = jprop_offlineMode_default; } - if (isInvalid) { - jconfig[jprop_offlineMode] = false; + + /** Validate monitorDisplayCompensatedValues */ + if (JSON.typeof_(jconfig[jprop_monitorDisplayCompensatedValues]) != + "boolean") { + jconfig[jprop_monitorDisplayCompensatedValues] = + jprop_monitorDisplayCompensatedValues_default; } if (changed) { @@ -1173,6 +1199,10 @@ bool Configuration::isLedBarModeChanged(void) { return changed; } +bool Configuration::isMonitorDisplayCompensatedValues(void) { + return jconfig[jprop_monitorDisplayCompensatedValues]; +} + bool Configuration::isDisplayBrightnessChanged(void) { bool changed = displayBrightnessChanged; displayBrightnessChanged = false; diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 24473ec..a899b64 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -82,6 +82,7 @@ public: void setOfflineMode(bool offline); void setOfflineModeWithoutSave(bool offline); bool isLedBarModeChanged(void); + bool isMonitorDisplayCompensatedValues(void); }; #endif /** _AG_CONFIG_H_ */ diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 8250407..2a18d59 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -10,37 +10,49 @@ * * @param hasStatus */ -void OledDisplay::showTempHum(bool hasStatus) { - char buf[16]; +void OledDisplay::showTempHum(bool hasStatus, char* buf, int buf_size) { if (utils::isValidTemperature(value.Temperature)) { + float t = 0.0f; + if(ag->isOpenAir() && config.isMonitorDisplayCompensatedValues()) { + if(config.isTemperatureUnitInF()) { + /** Compensate temperature */ + t = ag->pms5003t_1.compensateTemp(value.Temperature); + /** Convert C to F */ + t = (t * 9)/5.0f + 32.0f; + } + } else { + + } + + if (config.isTemperatureUnitInF()) { float tempF = (value.Temperature * 9) / 5 + 32; if (hasStatus) { - snprintf(buf, sizeof(buf), "%0.1f", tempF); + snprintf(buf, buf_size, "%0.1f", tempF); } else { - snprintf(buf, sizeof(buf), "%0.1f°F", tempF); + snprintf(buf, buf_size, "%0.1f°F", tempF); } } else { if (hasStatus) { - snprintf(buf, sizeof(buf), "%.1f", value.Temperature); + snprintf(buf, buf_size, "%.1f", value.Temperature); } else { - snprintf(buf, sizeof(buf), "%.1f°C", value.Temperature); + snprintf(buf, buf_size, "%.1f°C", value.Temperature); } } } else { if (config.isTemperatureUnitInF()) { - snprintf(buf, sizeof(buf), "-°F"); + snprintf(buf, buf_size, "-°F"); } else { - snprintf(buf, sizeof(buf), "-°C"); + snprintf(buf, buf_size, "-°C"); } } DISP()->drawUTF8(1, 10, buf); /** Show humidty */ if (utils::isValidHumidity(value.Humidity)) { - snprintf(buf, sizeof(buf), "%d%%", value.Humidity); + snprintf(buf, buf_size, "%d%%", value.Humidity); } else { - snprintf(buf, sizeof(buf), "-%%"); + snprintf(buf, buf_size, "-%%"); } if (value.Humidity > 99) { @@ -261,7 +273,7 @@ void OledDisplay::showDashboard(const char *status) { do { DISP()->setFont(u8g2_font_t0_16_tf); if ((status == NULL) || (strlen(status) == 0)) { - showTempHum(false); + showTempHum(false, strBuf, sizeof(strBuf)); } else { String strStatus = "Show status: " + String(status); logInfo(strStatus); @@ -272,7 +284,7 @@ void OledDisplay::showDashboard(const char *status) { /** Show WiFi NA*/ if (strcmp(status, "WiFi N/A") == 0) { DISP()->setFont(u8g2_font_t0_12_tf); - showTempHum(true); + showTempHum(true, strBuf, sizeof(strBuf)); } } @@ -304,29 +316,27 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(55, 27, "PM2.5"); /** Draw PM2.5 value */ - int pm25 = value.pm25_1; - if (config.hasSensorSHT) { - pm25 = ag->pms5003.compensate(pm25, value.Humidity); - logInfo("PM2.5:" + String(value.pm25_1) + String("Compensated:") + String(pm25)); - } - DISP()->setFont(u8g2_font_t0_22b_tf); - if (config.isPmStandardInUSAQI()) { - if (utils::isValidPm(pm25)) { + if (utils::isValidPm(value.pm25_1)) { + int pm25 = value.pm25_1; + if (config.isPmStandardInUSAQI() && + config.isMonitorDisplayCompensatedValues()) { + if (config.hasSensorSHT) { + pm25 = ag->pms5003.compensate(pm25, value.Humidity); + } sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); } else { - sprintf(strBuf, "%s", "-"); + sprintf(strBuf, "%d", pm25); } - DISP()->drawStr(55, 48, strBuf); - DISP()->setFont(u8g2_font_t0_12_tf); + } else { + sprintf(strBuf, "%s", "-"); + } + DISP()->setFont(u8g2_font_t0_22b_tf); + DISP()->drawStr(55, 48, strBuf); + + DISP()->setFont(u8g2_font_t0_12_tf); + if (config.isPmStandardInUSAQI()) { DISP()->drawUTF8(55, 61, "AQI"); } else { - if (utils::isValidPm(pm25)) { - sprintf(strBuf, "%d", pm25); - } else { - sprintf(strBuf, "%s", "-"); - } - DISP()->drawStr(55, 48, strBuf); - DISP()->setFont(u8g2_font_t0_12_tf); DISP()->drawUTF8(55, 61, "ug/m³"); } @@ -355,18 +365,18 @@ void OledDisplay::showDashboard(const char *status) { ag->display.clear(); /** Set CO2 */ - if(utils::isValidCO2(value.CO2)) { + if (utils::isValidCO2(value.CO2)) { snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2); } else { snprintf(strBuf, sizeof(strBuf), "CO2:-"); } - + ag->display.setCursor(0, 0); ag->display.setText(strBuf); /** Set PM */ int pm25 = value.pm25_1; - if(config.hasSensorSHT) { + if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity); } ag->display.setCursor(0, 12); diff --git a/src/AgOledDisplay.h b/src/AgOledDisplay.h index 28a0cba..435ff4c 100644 --- a/src/AgOledDisplay.h +++ b/src/AgOledDisplay.h @@ -16,7 +16,7 @@ private: Measurements &value; bool isDisplayOff = false; - void showTempHum(bool hasStatus); + void showTempHum(bool hasStatus, char* buf, int buf_size); void setCentralText(int y, String text); void setCentralText(int y, const char *text); diff --git a/src/Main/utils.cpp b/src/Main/utils.cpp index 42b06a5..33f8618 100644 --- a/src/Main/utils.cpp +++ b/src/Main/utils.cpp @@ -87,3 +87,8 @@ int utils::getInvalidPmValue(void) { return INVALID_PMS; } int utils::getInvalidNOx(void) { return INVALID_NOX; } int utils::getInvalidVOC(void) { return INVALID_VOC; } + +float utils::degreeC_To_F(float t) { + /** (t * 9)/5 + 32 */ + return t * 1.8f + 32.0f; +} diff --git a/src/Main/utils.h b/src/Main/utils.h index b51ac98..6b8452b 100644 --- a/src/Main/utils.h +++ b/src/Main/utils.h @@ -24,6 +24,7 @@ public: static int getInvalidPmValue(void); static int getInvalidNOx(void); static int getInvalidVOC(void); + static float degreeC_To_F(float t); }; From 9d014794061803c41c3ee8ead9470f67e25a0a80 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sat, 21 Sep 2024 14:08:42 +0700 Subject: [PATCH 18/25] Update show PM compensate value on display and documents --- docs/local-server.md | 2 +- src/AgOledDisplay.cpp | 49 +++++++++++++++++++++--------------------- src/AgStateMachine.cpp | 6 +++++- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 25cab52..68582b9 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -130,4 +130,4 @@ If the monitor is set up on the AirGradient dashboard, it will also receive conf | `noxLearningOffset` | Set NOx learning gain offset. | Number | 0-720 (default 12) | `{"noxLearningOffset": 12}` | | `tvocLearningOffset` | Set VOC learning gain offset. | Number | 0-720 (default 12) | `{"tvocLearningOffset": 12}` | | `offlineMode` | Set monitor to run without WiFi. | Boolean | `false`: Disabled (default)
`true`: Enabled | `{"offlineMode": true}` | -| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (From [3.1.8]()) | Boolean | `false`: Without compensate (default)
`true`: with compensate | `{"monitorDisplayCompensatedValues": false }` | +| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (From [3.1.9]()) | Boolean | `false`: Without compensate (default)
`true`: with compensate | `{"monitorDisplayCompensatedValues": false }` | diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 2a18d59..451da32 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -10,36 +10,30 @@ * * @param hasStatus */ -void OledDisplay::showTempHum(bool hasStatus, char* buf, int buf_size) { +void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { + /** Temperature */ if (utils::isValidTemperature(value.Temperature)) { float t = 0.0f; - if(ag->isOpenAir() && config.isMonitorDisplayCompensatedValues()) { - if(config.isTemperatureUnitInF()) { - /** Compensate temperature */ - t = ag->pms5003t_1.compensateTemp(value.Temperature); - /** Convert C to F */ - t = (t * 9)/5.0f + 32.0f; - } + if (config.isTemperatureUnitInF()) { + t = utils::degreeC_To_F(value.Temperature); } else { - + t = value.Temperature; } - if (config.isTemperatureUnitInF()) { - float tempF = (value.Temperature * 9) / 5 + 32; if (hasStatus) { - snprintf(buf, buf_size, "%0.1f", tempF); + snprintf(buf, buf_size, "%0.1f", t); } else { - snprintf(buf, buf_size, "%0.1f°F", tempF); + snprintf(buf, buf_size, "%0.1f°F", t); } } else { if (hasStatus) { - snprintf(buf, buf_size, "%.1f", value.Temperature); + snprintf(buf, buf_size, "%.1f", t); } else { - snprintf(buf, buf_size, "%.1f°C", value.Temperature); + snprintf(buf, buf_size, "%.1f°C", t); } } - } else { + } else { /** Show invalid value */ if (config.isTemperatureUnitInF()) { snprintf(buf, buf_size, "-°F"); } else { @@ -48,7 +42,7 @@ void OledDisplay::showTempHum(bool hasStatus, char* buf, int buf_size) { } DISP()->drawUTF8(1, 10, buf); - /** Show humidty */ + /** Show humidity */ if (utils::isValidHumidity(value.Humidity)) { snprintf(buf, buf_size, "%d%%", value.Humidity); } else { @@ -318,21 +312,25 @@ void OledDisplay::showDashboard(const char *status) { /** Draw PM2.5 value */ if (utils::isValidPm(value.pm25_1)) { int pm25 = value.pm25_1; - if (config.isPmStandardInUSAQI() && - config.isMonitorDisplayCompensatedValues()) { - if (config.hasSensorSHT) { - pm25 = ag->pms5003.compensate(pm25, value.Humidity); - } + + /** Compensate PM2.5 value. */ + if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { + pm25 = ag->pms5003.compensate(pm25, value.Humidity); + logInfo("PM2.5 compensate: " + String(pm25)); + } + + if (config.isPmStandardInUSAQI()) { sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); } else { sprintf(strBuf, "%d", pm25); } - } else { + } else { /** Show invalid value. */ sprintf(strBuf, "%s", "-"); } DISP()->setFont(u8g2_font_t0_22b_tf); DISP()->drawStr(55, 48, strBuf); + /** Draw PM2.5 unit */ DISP()->setFont(u8g2_font_t0_12_tf); if (config.isPmStandardInUSAQI()) { DISP()->drawUTF8(55, 61, "AQI"); @@ -379,6 +377,7 @@ void OledDisplay::showDashboard(const char *status) { if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity); } + ag->display.setCursor(0, 12); if (utils::isValidPm(pm25)) { snprintf(strBuf, sizeof(strBuf), "PM2.5:%d", pm25); @@ -390,8 +389,8 @@ void OledDisplay::showDashboard(const char *status) { /** Set temperature and humidity */ if (utils::isValidTemperature(value.Temperature)) { if (config.isTemperatureUnitInF()) { - float tempF = (value.Temperature * 9) / 5 + 32; - snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", tempF); + snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", + utils::degreeC_To_F(value.Temperature)); } else { snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature); } diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index f2e6105..8cc0372 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -141,7 +141,11 @@ void StateMachine::co2handleLeds(void) { * */ void StateMachine::pm25handleLeds(void) { - int pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity); + int pm25Value = value.pm25_1; + if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) { + pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity); + } + if (pm25Value < 5) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); From 866684eb30d28da42a8acdebddfd4544c1f65464 Mon Sep 17 00:00:00 2001 From: Phat Nguyen Date: Sat, 21 Sep 2024 14:26:06 +0700 Subject: [PATCH 19/25] fix load configuration value changed --- src/AgConfigure.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index f3abd7c..3f3005f 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -1106,12 +1106,14 @@ void Configuration::toConfig(const char *buf) { } if (JSON.typeof_(jconfig[jprop_offlineMode]) != "boolean") { + changed = true; jconfig[jprop_offlineMode] = jprop_offlineMode_default; } /** Validate monitorDisplayCompensatedValues */ if (JSON.typeof_(jconfig[jprop_monitorDisplayCompensatedValues]) != "boolean") { + changed = true; jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default; } From 6e212714fc5f75484cbbf351e20e3c2789f639f2 Mon Sep 17 00:00:00 2001 From: Samuel Siburian Date: Sat, 21 Sep 2024 14:57:05 +0700 Subject: [PATCH 20/25] Fix/mqtt-log (#235) Ignore init mqtt when it's not configured --- examples/BASIC/BASIC.ino | 13 ++++++++++--- examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 13 ++++++++++--- examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 13 ++++++++++--- examples/OneOpenAir/OneOpenAir.ino | 13 ++++++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 2a574e0..562761c 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -266,10 +266,17 @@ static void mdnsInit(void) { } static void initMqtt(void) { - if (mqttClient.begin(configuration.getMqttBrokerUri())) { - Serial.println("Setup connect to MQTT broker successful"); + String mqttUri = configuration.getMqttBrokerUri(); + if (mqttUri.isEmpty()) { + Serial.println( + "MQTT is not configured, skipping initialization of MQTT client"); + return; + } + + if (mqttClient.begin(mqttUri)) { + Serial.println("Successfully connected to MQTT broker"); } else { - Serial.println("setup Connect to MQTT broker failed"); + Serial.println("Connection to MQTT broker failed"); } } diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index 743aafc..af55b77 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -264,10 +264,17 @@ static void mdnsInit(void) { } static void initMqtt(void) { - if (mqttClient.begin(configuration.getMqttBrokerUri())) { - Serial.println("Setup connect to MQTT broker successful"); + String mqttUri = configuration.getMqttBrokerUri(); + if (mqttUri.isEmpty()) { + Serial.println( + "MQTT is not configured, skipping initialization of MQTT client"); + return; + } + + if (mqttClient.begin(mqttUri)) { + Serial.println("Successfully connected to MQTT broker"); } else { - Serial.println("setup Connect to MQTT broker failed"); + Serial.println("Connection to MQTT broker failed"); } } diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index 5394f82..10cb314 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -291,10 +291,17 @@ static void mdnsInit(void) { } static void initMqtt(void) { - if (mqttClient.begin(configuration.getMqttBrokerUri())) { - Serial.println("Setup connect to MQTT broker successful"); + String mqttUri = configuration.getMqttBrokerUri(); + if (mqttUri.isEmpty()) { + Serial.println( + "MQTT is not configured, skipping initialization of MQTT client"); + return; + } + + if (mqttClient.begin(mqttUri)) { + Serial.println("Successfully connected to MQTT broker"); } else { - Serial.println("setup Connect to MQTT broker failed"); + Serial.println("Connection to MQTT broker failed"); } } diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index b5c9518..29fdc81 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -382,11 +382,18 @@ static void createMqttTask(void) { } static void initMqtt(void) { - if (mqttClient.begin(configuration.getMqttBrokerUri())) { - Serial.println("Connect to MQTT broker successful"); + String mqttUri = configuration.getMqttBrokerUri(); + if (mqttUri.isEmpty()) { + Serial.println( + "MQTT is not configured, skipping initialization of MQTT client"); + return; + } + + if (mqttClient.begin(mqttUri)) { + Serial.println("Successfully connected to MQTT broker"); createMqttTask(); } else { - Serial.println("Connect to MQTT broker failed"); + Serial.println("Connection to MQTT broker failed"); } } From e10c9ff854eeba965b72f9c2be272b0a2a1c9305 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 22 Sep 2024 13:18:15 +0700 Subject: [PATCH 21/25] Update status notification in 1 function call --- examples/OneOpenAir/OneOpenAir.ino | 84 ++++++++++++------------------ 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 29fdc81..1a60f70 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -98,9 +98,7 @@ static String fwNewVersion; static void boardInit(void); static void failedHandler(String msg); static void configurationUpdateSchedule(void); -static void appLedHandler(void); -static void appDispHandler(void); -static void oledDisplayLedBarSchedule(void); +static void updateDisplayAndLedBar(void); static void updateTvoc(void); static void updatePm(void); static void sendDataToServer(void); @@ -118,7 +116,7 @@ static void otaHandlerCallback(OtaState state, String mesasge); static void displayExecuteOta(OtaState state, String msg, int processing); -AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplayLedBarSchedule); +AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, configurationUpdateSchedule); AgSchedule agApiPostSchedule(SERVER_SYNC_INTERVAL, sendDataToServer); @@ -261,8 +259,8 @@ void setup() { oledDisplay.setBrightness(configuration.getDisplayBrightness()); } - appLedHandler(); - appDispHandler(); + // Update display and led bar after finishing setup to show dashboard + updateDisplayAndLedBar(); } void loop() { @@ -449,7 +447,7 @@ static void factoryConfigReset(void) { /** Show current content cause reset ignore */ factoryBtnPressTime = 0; if (ag->isOne()) { - appDispHandler(); + updateDisplayAndLedBar(); } } } @@ -457,7 +455,7 @@ static void factoryConfigReset(void) { if (factoryBtnPressTime != 0) { if (ag->isOne()) { /** Restore last display content */ - appDispHandler(); + updateDisplayAndLedBar(); } } factoryBtnPressTime = 0; @@ -949,57 +947,41 @@ static void configUpdateHandle() { stateMachine.executeLedBarTest(); } - appDispHandler(); - appLedHandler(); + // Update display and led bar notification based on updated configuration + updateDisplayAndLedBar(); } -static void appLedHandler(void) { +static void updateDisplayAndLedBar(void) { + if (factoryBtnPressTime != 0) { + // Do not distrub factory reset sequence countdown + return; + } + + if (configuration.isOfflineMode()) { + // Ignore network related status when in offline mode + stateMachine.displayHandle(AgStateMachineNormal); + stateMachine.handleLeds(AgStateMachineNormal); + return; + } + AgStateMachineState state = AgStateMachineNormal; - if (configuration.isOfflineMode() == false) { - if (wifiConnector.isConnected() == false) { - state = AgStateMachineWiFiLost; - } else if (apiClient.isFetchConfigureFailed()) { - state = AgStateMachineSensorConfigFailed; - } else if (apiClient.isPostToServerFailed()) { - state = AgStateMachineServerLost; + if (wifiConnector.isConnected() == false) { + state = AgStateMachineWiFiLost; + } else if (apiClient.isFetchConfigureFailed()) { + state = AgStateMachineSensorConfigFailed; + if (apiClient.isNotAvailableOnDashboard()) { + stateMachine.displaySetAddToDashBoard(); + } else { + stateMachine.displayClearAddToDashBoard(); } + } else if (apiClient.isPostToServerFailed() && configuration.isPostDataToAirGradient()) { + state = AgStateMachineServerLost; } + stateMachine.displayHandle(state); stateMachine.handleLeds(state); } -static void appDispHandler(void) { - if (ag->isOne()) { - AgStateMachineState state = AgStateMachineNormal; - - /** Only show display status on online mode. */ - if (configuration.isOfflineMode() == false) { - if (wifiConnector.isConnected() == false) { - state = AgStateMachineWiFiLost; - } else if (apiClient.isFetchConfigureFailed()) { - state = AgStateMachineSensorConfigFailed; - if (apiClient.isNotAvailableOnDashboard()) { - stateMachine.displaySetAddToDashBoard(); - } else { - stateMachine.displayClearAddToDashBoard(); - } - } else if (apiClient.isPostToServerFailed()) { - state = AgStateMachineServerLost; - } - } - stateMachine.displayHandle(state); - } -} - -static void oledDisplayLedBarSchedule(void) { - if (ag->isOne()) { - if (factoryBtnPressTime == 0) { - appDispHandler(); - } - } - appLedHandler(); -} - static void updateTvoc(void) { measurements.TVOC = ag->sgp41.getTvocIndex(); measurements.TVOCRaw = ag->sgp41.getTvocRaw(); @@ -1280,4 +1262,4 @@ static void tempHumUpdate(void) { measurements.Humidity = utils::getInvalidHumidity(); Serial.println("SHT read failed"); } -} +} \ No newline at end of file From 3adf58537a3f84360ee3119c705134ac9b723903 Mon Sep 17 00:00:00 2001 From: Achim Date: Sun, 22 Sep 2024 14:13:24 +0700 Subject: [PATCH 22/25] Changed error message --- src/AgStateMachine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index f2e6105..62334d0 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -495,7 +495,7 @@ void StateMachine::displayHandle(AgStateMachineState state) { break; } case AgStateMachineServerLost: { - disp.showDashboard("Server N/A"); + disp.showDashboard("AG Server N/A"); break; } case AgStateMachineSensorConfigFailed: { @@ -504,7 +504,7 @@ void StateMachine::displayHandle(AgStateMachineState state) { if (ms >= 5000) { addToDashboardTime = millis(); if (addToDashBoardToggle) { - disp.showDashboard("Add to Dashboard"); + disp.showDashboard("Add to AG Dashb."); } else { disp.showDashboard(ag->deviceId().c_str()); } From 612317d9761ffe1333ea99252c7d4377492aa82e Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:46:18 +0700 Subject: [PATCH 23/25] Update local-server config example --- docs/local-server.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 68582b9..307ad8c 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -77,17 +77,21 @@ Compensated values apply correction algorithms to make the sensor values more ac With the path "/config" you can get the current configuration. ```json { - "country": "US", + "country": "TH", "pmStandard": "ugm3", "ledBarMode": "pm", - "displayMode": "on", - "abcDays": 30, + "abcDays": 7, "tvocLearningOffset": 12, "noxLearningOffset": 12, "mqttBrokerUrl": "", - "temperatureUnit": "f", - "configurationControl": "both", - "postDataToAirGradient": true + "temperatureUnit": "c", + "configurationControl": "local", + "postDataToAirGradient": true, + "ledBarBrightness": 100, + "displayBrightness": 100, + "offlineMode": false, + "model": "I-9PSL", + "monitorDisplayCompensatedValues": true } ``` From 863a37132ab0ff80ce8303216d3c284f239b90b7 Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:04:28 +0700 Subject: [PATCH 24/25] Update library.properties to 3.1.9 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 0a9ed39..51de43b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AirGradient Air Quality Sensor -version=3.1.8 +version=3.1.9 author=AirGradient maintainer=AirGradient sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display. From 0370a8aa15ffaf776f9055f84b5d7c221046b9be Mon Sep 17 00:00:00 2001 From: AirGradient <68736665+airgradienthq@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:05:04 +0700 Subject: [PATCH 25/25] Update AirGradient.h to 3.1.9 --- src/AirGradient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AirGradient.h b/src/AirGradient.h index 31d296f..cc187f7 100644 --- a/src/AirGradient.h +++ b/src/AirGradient.h @@ -15,7 +15,7 @@ #include "Main/utils.h" #ifndef GIT_VERSION -#define GIT_VERSION "3.1.8-snap" +#define GIT_VERSION "3.1.9-snap" #endif /**