From e9b27185b42f99bf0ce436c9f904415d6695f3fd Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 22 Jan 2025 01:46:08 +0700 Subject: [PATCH 01/11] Fix spaces --- src/App/AppDef.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/App/AppDef.h b/src/App/AppDef.h index 1e114e7..df5707e 100644 --- a/src/App/AppDef.h +++ b/src/App/AppDef.h @@ -107,14 +107,14 @@ enum PMCorrectionAlgorithm { }; enum AgFirmwareMode { - FW_MODE_I_9PSL, /** ONE_INDOOR */ - FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */ - FW_MODE_O_1PPT, /** PMS5003T_1, PMS5003T_2, SGP41 */ - FW_MODE_O_1PP, /** PMS5003T_1, PMS5003T_2 */ - FW_MODE_O_1PS, /** PMS5003T, S8 */ - FW_MODE_O_1P, /** PMS5003T */ - FW_MODE_I_42PS, /** DIY_PRO 4.2 */ - FW_MODE_I_33PS, /** DIY_PRO 3.3 */ + FW_MODE_I_9PSL, /** ONE_INDOOR */ + FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */ + FW_MODE_O_1PPT, /** PMS5003T_1, PMS5003T_2, SGP41 */ + FW_MODE_O_1PP, /** PMS5003T_1, PMS5003T_2 */ + FW_MODE_O_1PS, /** PMS5003T, S8 */ + FW_MODE_O_1P, /** PMS5003T */ + FW_MODE_I_42PS, /** DIY_PRO 4.2 */ + FW_MODE_I_33PS, /** DIY_PRO 3.3 */ FW_MODE_I_BASIC_40PS, /** DIY_BASIC 4.0 */ }; const char *AgFirmwareModeName(AgFirmwareMode mode); From 88c2437907e86624b77a7f73a637f8e65c96e015 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 22 Jan 2025 01:53:55 +0700 Subject: [PATCH 02/11] Handle correction configuration for atmp and rhum From local and cloud --- src/AgConfigure.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++-- src/AgConfigure.h | 14 ++++- src/App/AppDef.h | 8 +++ 3 files changed, 158 insertions(+), 5 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 57e864b..8a247d2 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -33,6 +33,13 @@ const char *PM_CORRECTION_ALGORITHM_NAMES[] = { [SLR_PMS5003_20240104] = "slr_PMS5003_20240104", }; +const char *TEMP_HUM_CORRECTION_ALGORITHM_NAMES[] = { + [CA_TH_UNKNOWN] = "-", // This is only to pass "non-trivial designated initializers" error + [CA_TH_NONE] = "none", + [CA_TH_AG_PMS5003T_2024] = "ag_pms5003t_2024", + [CA_TH_SLR_CUSTOM] = "custom", +}; + #define JSON_PROP_NAME(name) jprop_##name #define JSON_PROP_DEF(name) const char *JSON_PROP_NAME(name) = #name @@ -54,6 +61,8 @@ JSON_PROP_DEF(ledBarTestRequested); JSON_PROP_DEF(offlineMode); JSON_PROP_DEF(monitorDisplayCompensatedValues); JSON_PROP_DEF(corrections); +JSON_PROP_DEF(atmp); +JSON_PROP_DEF(rhum); #define jprop_model_default "" #define jprop_country_default "TH" @@ -117,6 +126,21 @@ PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) { return result; } +TempHumCorrectionAlgorithm Configuration::matchTempHumAlgorithm(String algorithm) { + // Get the actual size of the enum + const int enumSize = static_cast(CA_TH_SLR_CUSTOM); + TempHumCorrectionAlgorithm result = CA_TH_UNKNOWN; + + // Loop through enum values + for (size_t enumVal = 0; enumVal <= enumSize; enumVal++) { + if (algorithm == TEMP_HUM_CORRECTION_ALGORITHM_NAMES[enumVal]) { + result = static_cast(enumVal); + } + } + + return result; +} + bool Configuration::updatePmCorrection(JSONVar &json) { if (!json.hasOwnProperty("corrections")) { // TODO: need to response message? @@ -205,6 +229,89 @@ bool Configuration::updatePmCorrection(JSONVar &json) { return true; } +bool Configuration::updateTempHumCorrection(JSONVar &json, TempHumCorrection &target, + const char *correctionName) { + if (!json.hasOwnProperty(jprop_corrections)) { + // TODO: need to response message? + return false; + } + + JSONVar corrections = json[jprop_corrections]; + if (!corrections.hasOwnProperty(correctionName)) { + Serial.println("pm02 not found"); + logWarning(String(correctionName) + " correction field not found on configuration"); + return false; + } + + JSONVar correctionTarget = corrections[correctionName]; + if (!correctionTarget.hasOwnProperty("correctionAlgorithm")) { + Serial.println("correctionAlgorithm not found"); + return false; + } + + String algorithm = correctionTarget["correctionAlgorithm"]; + TempHumCorrectionAlgorithm algo = matchTempHumAlgorithm(algorithm); + if (algo == CA_TH_UNKNOWN) { + logInfo("Uknown temp/hum algorithm"); + return false; + } + logInfo(String(correctionName) + " correction algorithm: " + algorithm); + + // If algo is None or Standard, then no need to check slr + // But first check if target correction different from algo + if (algo == CA_TH_NONE || algo == CA_TH_AG_PMS5003T_2024) { + if (target.algorithm != algo) { + // Deep copy corrections from root to jconfig, so it will be saved later + jconfig[jprop_corrections][correctionName]["correctionAlgorithm"] = algorithm; + jconfig[jprop_corrections][correctionName]["slr"] = JSON.parse("{}"); // Clear slr + // Update pmCorrection with new values + target.algorithm = algo; + target.changed = true; + logInfo(String(correctionName) + " correction updated"); + return true; + } + + return false; + } + + // Check if correction.target (atmp or rhum) has slr object + if (!correctionTarget.hasOwnProperty("slr")) { + logWarning(String(correctionName) + " slr not found"); + return false; + } + + JSONVar slr = correctionTarget["slr"]; + + // Validate required slr properties exist + if (!slr.hasOwnProperty("intercept") || !slr.hasOwnProperty("scalingFactor")) { + Serial.println("Missing required slr properties"); + return false; + } + + // arduino_json doesn't support float type, need to cast to double first + float intercept = (float)((double)slr["intercept"]); + float scalingFactor = (float)((double)slr["scalingFactor"]); + + // Compare with current target correciont + if (target.algorithm == algo && target.intercept == intercept && + target.scalingFactor == scalingFactor) { + return false; // No changes needed + } + + // Deep copy corrections from root to jconfig, so it will be saved later + jconfig[jprop_corrections] = corrections; + + // Update target with new values + target.algorithm = algo; + target.intercept = intercept; + target.scalingFactor = scalingFactor; + target.changed = true; + + // Correction values were updated + logInfo(String(correctionName) + " correction updated"); + return true; +} + /** * @brief Save configure to device storage (EEPROM) * @@ -792,11 +899,21 @@ bool Configuration::parse(String data, bool isLocal) { } } - // Corrections + // PM2.5 Corrections if (updatePmCorrection(root)) { changed = true; } + // Temperature correction + if (updateTempHumCorrection(root, tempCorrection, jprop_atmp)) { + changed = true; + } + + // Relative humidity correction + if (updateTempHumCorrection(root, rhumCorrection, jprop_rhum)) { + changed = true; + } + if (changed) { updated = true; saveConfig(); @@ -1248,15 +1365,31 @@ void Configuration::toConfig(const char *buf) { jprop_monitorDisplayCompensatedValues_default; } - - // Set default first before parsing local config + // PM2.5 correction + /// Set default first before parsing local config pmCorrection.algorithm = PMCorrectionAlgorithm::None; pmCorrection.intercept = 0; pmCorrection.scalingFactor = 0; pmCorrection.useEPA = false; - // Load correction from saved config + /// Load correction from saved config updatePmCorrection(jconfig); + // Temperature correction + /// Set default first before parsing local config + tempCorrection.algorithm = CA_TH_NONE; + tempCorrection.intercept = 0; + tempCorrection.scalingFactor = 0; + /// Load correction from saved config + updateTempHumCorrection(jconfig, tempCorrection, jprop_atmp); + + // Relative humidity correction + /// Set default first before parsing local config + rhumCorrection.algorithm = CA_TH_NONE; + rhumCorrection.intercept = 0; + rhumCorrection.scalingFactor = 0; + /// Load correction from saved config + updateTempHumCorrection(jconfig, rhumCorrection, jprop_rhum); + if (changed) { saveConfig(); } diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 1cca028..e80a7fa 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -17,6 +17,13 @@ public: bool changed; }; + struct TempHumCorrection { + TempHumCorrectionAlgorithm algorithm; + float intercept; + float scalingFactor; + bool changed; + }; + private: bool co2CalibrationRequested; bool ledBarTestRequested; @@ -30,12 +37,17 @@ private: bool _offlineMode = false; bool _ledBarModeChanged = false; PMCorrection pmCorrection; + TempHumCorrection tempCorrection; + TempHumCorrection rhumCorrection; AirGradient* ag; String getLedBarModeName(LedBarMode mode); PMCorrectionAlgorithm matchPmAlgorithm(String algorithm); + TempHumCorrectionAlgorithm matchTempHumAlgorithm(String algorithm); bool updatePmCorrection(JSONVar &json); + bool updateTempHumCorrection(JSONVar &json, TempHumCorrection &target, + const char *correctionName); void saveConfig(void); void loadConfig(void); void defaultConfig(void); @@ -46,7 +58,7 @@ private: void configLogInfo(String name, String fromValue, String toValue); String getPMStandardString(bool usaqi); String getAbcDayString(int value); - void toConfig(const char* buf); + void toConfig(const char *buf); public: Configuration(Stream &debugLog); diff --git a/src/App/AppDef.h b/src/App/AppDef.h index df5707e..9a03a71 100644 --- a/src/App/AppDef.h +++ b/src/App/AppDef.h @@ -106,6 +106,14 @@ enum PMCorrectionAlgorithm { SLR_PMS5003_20240104, }; +// Don't change the order of the enum +enum TempHumCorrectionAlgorithm { + CA_TH_UNKNOWN, // Unknown algorithm + CA_TH_NONE, // No PM correction + CA_TH_AG_PMS5003T_2024, + CA_TH_SLR_CUSTOM +}; + enum AgFirmwareMode { FW_MODE_I_9PSL, /** ONE_INDOOR */ FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */ From 20db9d699b19de7149ebdeeaeafbff6d0b3d45ac Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 23 Jan 2025 01:16:49 +0700 Subject: [PATCH 03/11] Retrieve temp hum correction object --- src/AgConfigure.cpp | 8 +++++--- src/AgConfigure.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 8a247d2..63e5e8f 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -1510,6 +1510,8 @@ bool Configuration::isPMCorrectionEnabled(void) { return true; } -Configuration::PMCorrection Configuration::getPMCorrection(void) { - return pmCorrection; -} +Configuration::PMCorrection Configuration::getPMCorrection(void) { return pmCorrection; } + +Configuration::TempHumCorrection Configuration::getTempCorrection(void) { return tempCorrection; } + +Configuration::TempHumCorrection Configuration::getHumCorrection(void) { return rhumCorrection; } diff --git a/src/AgConfigure.h b/src/AgConfigure.h index e80a7fa..863d876 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -111,6 +111,8 @@ public: bool isPMCorrectionChanged(void); bool isPMCorrectionEnabled(void); PMCorrection getPMCorrection(void); + TempHumCorrection getTempCorrection(void); + TempHumCorrection getHumCorrection(void); }; #endif /** _AG_CONFIG_H_ */ From 89475ddf95c5c6a5b963b2368e001054e78972a2 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 23 Jan 2025 01:21:27 +0700 Subject: [PATCH 04/11] Get correction of temp and hum based on configuration --- src/AgValue.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ src/AgValue.h | 3 +++ 2 files changed, 60 insertions(+) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 1a46ece..f724d9e 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -535,6 +535,63 @@ void Measurements::validateChannel(int ch) { } } +float Measurements::getCorrectedTempHum(AirGradient &ag, Configuration &config, + MeasurementType type, int ch, bool forceCorrection) { + // Sanity check to validate channel, assert if invalid + validateChannel(ch); + + // Follow array indexing just for get address of the value type + ch = ch - 1; + bool undefined = false; + + float rawValue; + Configuration::TempHumCorrection correction; + + switch (type) { + case Temperature: { + rawValue = _temperature[ch].update.avg; + + Configuration::TempHumCorrection tmp = config.getTempCorrection(); + if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { + return ag.pms5003t_1.compensateTemp(rawValue); + } + + correction.algorithm = tmp.algorithm; + correction.intercept = tmp.intercept; + correction.scalingFactor = tmp.scalingFactor; + break; + } + case Humidity: { + rawValue = _humidity[ch].update.avg; + + Configuration::TempHumCorrection tmp = config.getHumCorrection(); + if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { + return ag.pms5003t_1.compensateHum(rawValue); + } + + correction.algorithm = tmp.algorithm; + correction.intercept = tmp.intercept; + correction.scalingFactor = tmp.scalingFactor; + break; + } + default: + // Should not be called for other measurements + delay(1000); + assert(0); + } + + // Use raw if correction not defined + if (correction.algorithm == TempHumCorrectionAlgorithm::CA_TH_NONE || + correction.algorithm == TempHumCorrectionAlgorithm::CA_TH_UNKNOWN) { + return rawValue; + } + + // Custom correction constants + float corrected = (rawValue * correction.scalingFactor) + correction.intercept; + + return corrected; +} + float Measurements::getCorrectedPM25(AirGradient &ag, Configuration &config, bool useAvg, int ch) { float pm25; float corrected; diff --git a/src/AgValue.h b/src/AgValue.h index 6cc7a05..7402e1d 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -123,6 +123,9 @@ public: */ float getAverage(MeasurementType type, int ch = 1); + float getCorrectedTempHum(AirGradient &ag, Configuration &config, MeasurementType type, + int ch = 1, bool forceCorrection = false); + /** * @brief Get the Corrected PM25 object based on the correction algorithm from configuration * From 1666923ab31cc81508b3580ac9164a34e495e213 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 23 Jan 2025 03:33:47 +0700 Subject: [PATCH 05/11] Add AirGradient and Configuration object to AgValue --- examples/OneOpenAir/LocalServer.cpp | 2 +- examples/OneOpenAir/OneOpenAir.ino | 8 +- examples/OneOpenAir/OpenMetrics.cpp | 10 +- src/AgOledDisplay.cpp | 4 +- src/AgStateMachine.cpp | 2 +- src/AgValue.cpp | 356 ++++++++++++++-------------- src/AgValue.h | 26 +- 7 files changed, 204 insertions(+), 204 deletions(-) diff --git a/examples/OneOpenAir/LocalServer.cpp b/examples/OneOpenAir/LocalServer.cpp index e0b5c98..69434e2 100644 --- a/examples/OneOpenAir/LocalServer.cpp +++ b/examples/OneOpenAir/LocalServer.cpp @@ -64,7 +64,7 @@ void LocalServer::_GET_metrics(void) { } void LocalServer::_GET_measure(void) { - String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config); + String toSend = measure.toString(true, fwMode, wifiConnector.RSSI()); server.send(200, "application/json", toSend); } diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index ee4b680..5c374a3 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -76,7 +76,7 @@ static MqttClient mqttClient(Serial); static TaskHandle_t mqttTask = NULL; static Configuration configuration(Serial); static AgApiClient apiClient(Serial, configuration); -static Measurements measurements; +static Measurements measurements(configuration); static AirGradient *ag; static OledDisplay oledDisplay(configuration, measurements, Serial); static StateMachine stateMachine(oledDisplay, Serial, measurements, @@ -164,6 +164,7 @@ void setup() { apiClient.setAirGradient(ag); openMetrics.setAirGradient(ag); localServer.setAirGraident(ag); + measurements.setAirGradient(ag); /** Example set custom API root URL */ // apiClient.setApiRoot("https://example.custom.api"); @@ -368,8 +369,7 @@ static void createMqttTask(void) { /** Send data */ if (mqttClient.isConnected()) { - String payload = - measurements.toString(true, fwMode, wifiConnector.RSSI(), *ag, configuration); + String payload = measurements.toString(true, fwMode, wifiConnector.RSSI()); String topic = "airgradient/readings/" + ag->deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), @@ -1145,7 +1145,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), *ag, configuration); + String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI()); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( diff --git a/examples/OneOpenAir/OpenMetrics.cpp b/examples/OneOpenAir/OpenMetrics.cpp index fa59444..753f9c7 100644 --- a/examples/OneOpenAir/OpenMetrics.cpp +++ b/examples/OneOpenAir/OpenMetrics.cpp @@ -81,8 +81,8 @@ String OpenMetrics::getPayload(void) { measure.getFloat(Measurements::Humidity, 2)) / 2.0f; pm01 = (measure.get(Measurements::PM01, 1) + measure.get(Measurements::PM01, 2)) / 2.0f; - float correctedPm25_1 = measure.getCorrectedPM25(*ag, config, false, 1); - float correctedPm25_2 = measure.getCorrectedPM25(*ag, config, false, 2); + float correctedPm25_1 = measure.getCorrectedPM25(false, 1); + float correctedPm25_2 = measure.getCorrectedPM25(false, 2); float correctedPm25 = (correctedPm25_1 + correctedPm25_2) / 2.0f; pm25 = round(correctedPm25); pm10 = (measure.get(Measurements::PM10, 1) + measure.get(Measurements::PM10, 2)) / 2.0f; @@ -97,7 +97,7 @@ String OpenMetrics::getPayload(void) { if (config.hasSensorPMS1) { pm01 = measure.get(Measurements::PM01); - float correctedPm = measure.getCorrectedPM25(*ag, config, false, 1); + float correctedPm = measure.getCorrectedPM25(false, 1); pm25 = round(correctedPm); pm10 = measure.get(Measurements::PM10); pm03PCount = measure.get(Measurements::PM03_PC); @@ -107,7 +107,7 @@ String OpenMetrics::getPayload(void) { _temp = measure.getFloat(Measurements::Temperature, 1); _hum = measure.getFloat(Measurements::Humidity, 1); pm01 = measure.get(Measurements::PM01, 1); - float correctedPm = measure.getCorrectedPM25(*ag, config, false, 1); + float correctedPm = measure.getCorrectedPM25(false, 1); pm25 = round(correctedPm); pm10 = measure.get(Measurements::PM10, 1); pm03PCount = measure.get(Measurements::PM03_PC, 1); @@ -116,7 +116,7 @@ String OpenMetrics::getPayload(void) { _temp = measure.getFloat(Measurements::Temperature, 2); _hum = measure.getFloat(Measurements::Humidity, 2); pm01 = measure.get(Measurements::PM01, 2); - float correctedPm = measure.getCorrectedPM25(*ag, config, false, 2); + float correctedPm = measure.getCorrectedPM25(false, 2); pm25 = round(correctedPm); pm10 = measure.get(Measurements::PM10, 2); pm03PCount = measure.get(Measurements::PM03_PC, 2); diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 0682038..bccab18 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -316,7 +316,7 @@ void OledDisplay::showDashboard(const char *status) { int pm25 = round(value.getAverage(Measurements::PM25)); if (utils::isValidPm(pm25)) { if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { - pm25 = round(value.getCorrectedPM25(*ag, config, true)); + pm25 = round(value.getCorrectedPM25(true)); } if (config.isPmStandardInUSAQI()) { sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); @@ -377,7 +377,7 @@ void OledDisplay::showDashboard(const char *status) { /** Set PM */ int pm25 = round(value.getAverage(Measurements::PM25)); if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { - pm25 = round(value.getCorrectedPM25(*ag, config, true)); + pm25 = round(value.getCorrectedPM25(true)); } ag->display.setCursor(0, 12); diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 3656bde..8d1f45d 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -174,7 +174,7 @@ int StateMachine::pm25handleLeds(void) { int pm25Value = round(value.getAverage(Measurements::PM25)); if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { - pm25Value = round(value.getCorrectedPM25(*ag, config, true)); + pm25Value = round(value.getCorrectedPM25(true)); } if (pm25Value <= 5) { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index f724d9e..e265c89 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -27,12 +27,14 @@ #define json_prop_noxRaw "noxRaw" #define json_prop_co2 "rco2" -Measurements::Measurements() { +Measurements::Measurements(Configuration &config) : config(config) { #ifndef ESP8266 _resetReason = (int)ESP_RST_UNKNOWN; #endif } +void Measurements::setAirGradient(AirGradient *ag) { this->ag = ag; } + void Measurements::maxPeriod(MeasurementType type, int max) { switch (type) { case Temperature: @@ -535,8 +537,7 @@ void Measurements::validateChannel(int ch) { } } -float Measurements::getCorrectedTempHum(AirGradient &ag, Configuration &config, - MeasurementType type, int ch, bool forceCorrection) { +float Measurements::getCorrectedTempHum(MeasurementType type, int ch, bool forceCorrection) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -553,7 +554,7 @@ float Measurements::getCorrectedTempHum(AirGradient &ag, Configuration &config, Configuration::TempHumCorrection tmp = config.getTempCorrection(); if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { - return ag.pms5003t_1.compensateTemp(rawValue); + return ag->pms5003t_1.compensateTemp(rawValue); } correction.algorithm = tmp.algorithm; @@ -566,7 +567,7 @@ float Measurements::getCorrectedTempHum(AirGradient &ag, Configuration &config, Configuration::TempHumCorrection tmp = config.getHumCorrection(); if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { - return ag.pms5003t_1.compensateHum(rawValue); + return ag->pms5003t_1.compensateHum(rawValue); } correction.algorithm = tmp.algorithm; @@ -592,7 +593,7 @@ float Measurements::getCorrectedTempHum(AirGradient &ag, Configuration &config, return corrected; } -float Measurements::getCorrectedPM25(AirGradient &ag, Configuration &config, bool useAvg, int ch) { +float Measurements::getCorrectedPM25(bool useAvg, int ch) { float pm25; float corrected; float humidity; @@ -617,15 +618,15 @@ float Measurements::getCorrectedPM25(AirGradient &ag, Configuration &config, boo corrected = pm25; break; case PMCorrectionAlgorithm::EPA_2021: - corrected = ag.pms5003.compensate(pm25, humidity); + corrected = ag->pms5003.compensate(pm25, humidity); break; default: { // All SLR correction using the same flow, hence default condition - corrected = ag.pms5003.slrCorrection(pm25, pm003Count, pmCorrection.scalingFactor, - pmCorrection.intercept); + corrected = ag->pms5003.slrCorrection(pm25, pm003Count, pmCorrection.scalingFactor, + pmCorrection.intercept); if (pmCorrection.useEPA) { // Add EPA compensation on top of SLR - corrected = ag.pms5003.compensate(corrected, humidity); + corrected = ag->pms5003.compensate(corrected, humidity); } } } @@ -633,34 +634,33 @@ float Measurements::getCorrectedPM25(AirGradient &ag, Configuration &config, boo return corrected; } -String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, - Configuration &config) { +String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi) { JSONVar root; - if (ag.isOne() || (ag.isPro4_2()) || ag.isPro3_3() || ag.isBasic()) { - root = buildIndoor(localServer, ag, config); + if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { + root = buildIndoor(localServer); } else { - root = buildOutdoor(localServer, fwMode, ag, config); + root = buildOutdoor(localServer, fwMode); } // CO2 if (config.hasSensorS8 && utils::isValidCO2(_co2.update.avg)) { - root[json_prop_co2] = ag.round2(_co2.update.avg); + root[json_prop_co2] = ag->round2(_co2.update.avg); } /// TVOx and NOx if (config.hasSensorSGP) { if (utils::isValidVOC(_tvoc.update.avg)) { - root[json_prop_tvoc] = ag.round2(_tvoc.update.avg); + root[json_prop_tvoc] = ag->round2(_tvoc.update.avg); } if (utils::isValidVOC(_tvoc_raw.update.avg)) { - root[json_prop_tvocRaw] = ag.round2(_tvoc_raw.update.avg); + root[json_prop_tvocRaw] = ag->round2(_tvoc_raw.update.avg); } if (utils::isValidNOx(_nox.update.avg)) { - root[json_prop_nox] = ag.round2(_nox.update.avg); + root[json_prop_nox] = ag->round2(_nox.update.avg); } if (utils::isValidNOx(_nox_raw.update.avg)) { - root[json_prop_noxRaw] = ag.round2(_nox_raw.update.avg); + root[json_prop_noxRaw] = ag->round2(_nox_raw.update.avg); } } @@ -669,11 +669,11 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, root["wifi"] = rssi; if (localServer) { - if (ag.isOne()) { + if (ag->isOne()) { root["ledMode"] = config.getLedBarModeName(); } - root["serialno"] = ag.deviceId(); - root["firmware"] = ag.getVersion(); + root["serialno"] = ag->deviceId(); + root["firmware"] = ag->getVersion(); root["model"] = AgFirmwareModeName(fwMode); } else { #ifndef ESP8266 @@ -687,8 +687,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, return result; } -JSONVar Measurements::buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirGradient &ag, - Configuration &config) { +JSONVar Measurements::buildOutdoor(bool localServer, AgFirmwareMode fwMode) { JSONVar outdoor; if (fwMode == FW_MODE_O_1P || fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) { // buildPMS params: @@ -697,14 +696,16 @@ JSONVar Measurements::buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirG /// compensated values if requested by local server /// Set ch based on hasSensorPMSx if (config.hasSensorPMS1) { - outdoor = buildPMS(ag, 1, false, true, localServer); + outdoor = buildPMS(1, false, true, localServer); if (!localServer) { - outdoor[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag.pms5003t_1.getFirmwareVersion()); + outdoor[json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); } } else { - outdoor = buildPMS(ag, 2, false, true, localServer); + outdoor = buildPMS(2, false, true, localServer); if (!localServer) { - outdoor[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag.pms5003t_2.getFirmwareVersion()); + outdoor[json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); } } } else { @@ -713,20 +714,20 @@ JSONVar Measurements::buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirG /// Have 2 PMS sensor, allCh is set to true (ch params ignored) /// Enable temp hum from PMS /// compensated values if requested by local server - outdoor = buildPMS(ag, 1, true, true, localServer); + outdoor = buildPMS(1, true, true, localServer); // PMS5003T version if (!localServer) { outdoor["channels"]["1"][json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag.pms5003t_1.getFirmwareVersion()); + pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); outdoor["channels"]["2"][json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag.pms5003t_2.getFirmwareVersion()); + pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); } } return outdoor; } -JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configuration &config) { +JSONVar Measurements::buildIndoor(bool localServer) { JSONVar indoor; if (config.hasSensorPMS1) { @@ -734,26 +735,26 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati /// PMS channel 1 (indoor only have 1 PMS; hence allCh false) /// Not include temperature and humidity from PMS sensor /// Not include compensated calculation - indoor = buildPMS(ag, 1, false, false, false); + indoor = buildPMS(1, false, false, false); if (!localServer) { // Indoor is using PMS5003 - indoor[json_prop_pmFirmware] = this->pms5003FirmwareVersion(ag.pms5003.getFirmwareVersion()); + indoor[json_prop_pmFirmware] = this->pms5003FirmwareVersion(ag->pms5003.getFirmwareVersion()); } } if (config.hasSensorSHT) { // Add temperature if (utils::isValidTemperature(_temperature[0].update.avg)) { - indoor[json_prop_temp] = ag.round2(_temperature[0].update.avg); + indoor[json_prop_temp] = ag->round2(_temperature[0].update.avg); if (localServer) { - indoor[json_prop_tempCompensated] = ag.round2(_temperature[0].update.avg); + indoor[json_prop_tempCompensated] = ag->round2(_temperature[0].update.avg); } } // Add humidity if (utils::isValidHumidity(_humidity[0].update.avg)) { - indoor[json_prop_rhum] = ag.round2(_humidity[0].update.avg); + indoor[json_prop_rhum] = ag->round2(_humidity[0].update.avg); if (localServer) { - indoor[json_prop_rhumCompensated] = ag.round2(_humidity[0].update.avg); + indoor[json_prop_rhumCompensated] = ag->round2(_humidity[0].update.avg); } } } @@ -762,16 +763,15 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].update.avg)) { if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].update.avg)) { // Correction using moving average value - float tmp = getCorrectedPM25(ag, config, true); - indoor[json_prop_pm25Compensated] = ag.round2(tmp); + float tmp = getCorrectedPM25(true); + indoor[json_prop_pm25Compensated] = ag->round2(tmp); } } return indoor; } -JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTempHum, - bool compensate) { +JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compensate) { JSONVar pms; // When only one of the channel @@ -783,47 +783,47 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem ch = ch - 1; if (utils::isValidPm(_pm_01[ch].update.avg)) { - pms[json_prop_pm01Ae] = ag.round2(_pm_01[ch].update.avg); + pms[json_prop_pm01Ae] = ag->round2(_pm_01[ch].update.avg); } if (utils::isValidPm(_pm_25[ch].update.avg)) { - pms[json_prop_pm25Ae] = ag.round2(_pm_25[ch].update.avg); + pms[json_prop_pm25Ae] = ag->round2(_pm_25[ch].update.avg); } if (utils::isValidPm(_pm_10[ch].update.avg)) { - pms[json_prop_pm10Ae] = ag.round2(_pm_10[ch].update.avg); + pms[json_prop_pm10Ae] = ag->round2(_pm_10[ch].update.avg); } if (utils::isValidPm(_pm_01_sp[ch].update.avg)) { - pms[json_prop_pm01Sp] = ag.round2(_pm_01_sp[ch].update.avg); + pms[json_prop_pm01Sp] = ag->round2(_pm_01_sp[ch].update.avg); } if (utils::isValidPm(_pm_25_sp[ch].update.avg)) { - pms[json_prop_pm25Sp] = ag.round2(_pm_25_sp[ch].update.avg); + pms[json_prop_pm25Sp] = ag->round2(_pm_25_sp[ch].update.avg); } if (utils::isValidPm(_pm_10_sp[ch].update.avg)) { - pms[json_prop_pm10Sp] = ag.round2(_pm_10_sp[ch].update.avg); + pms[json_prop_pm10Sp] = ag->round2(_pm_10_sp[ch].update.avg); } if (utils::isValidPm03Count(_pm_03_pc[ch].update.avg)) { - pms[json_prop_pm03Count] = ag.round2(_pm_03_pc[ch].update.avg); + pms[json_prop_pm03Count] = ag->round2(_pm_03_pc[ch].update.avg); } if (utils::isValidPm03Count(_pm_05_pc[ch].update.avg)) { - pms[json_prop_pm05Count] = ag.round2(_pm_05_pc[ch].update.avg); + pms[json_prop_pm05Count] = ag->round2(_pm_05_pc[ch].update.avg); } if (utils::isValidPm03Count(_pm_01_pc[ch].update.avg)) { - pms[json_prop_pm1Count] = ag.round2(_pm_01_pc[ch].update.avg); + pms[json_prop_pm1Count] = ag->round2(_pm_01_pc[ch].update.avg); } if (utils::isValidPm03Count(_pm_25_pc[ch].update.avg)) { - pms[json_prop_pm25Count] = ag.round2(_pm_25_pc[ch].update.avg); + pms[json_prop_pm25Count] = ag->round2(_pm_25_pc[ch].update.avg); } if (_pm_5_pc[ch].listValues.empty() == false) { // Only include pm5.0 count when values available on its list // If not, means no pm5_pc available from the sensor if (utils::isValidPm03Count(_pm_5_pc[ch].update.avg)) { - pms[json_prop_pm5Count] = ag.round2(_pm_5_pc[ch].update.avg); + pms[json_prop_pm5Count] = ag->round2(_pm_5_pc[ch].update.avg); } } if (_pm_10_pc[ch].listValues.empty() == false) { // Only include pm10 count when values available on its list // If not, means no pm10_pc available from the sensor if (utils::isValidPm03Count(_pm_10_pc[ch].update.avg)) { - pms[json_prop_pm10Count] = ag.round2(_pm_10_pc[ch].update.avg); + pms[json_prop_pm10Count] = ag->round2(_pm_10_pc[ch].update.avg); } } @@ -831,23 +831,23 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem float _vc; // Set temperature if valid if (utils::isValidTemperature(_temperature[ch].update.avg)) { - pms[json_prop_temp] = ag.round2(_temperature[ch].update.avg); + pms[json_prop_temp] = ag->round2(_temperature[ch].update.avg); // Compensate temperature when flag is set if (compensate) { - _vc = ag.pms5003t_1.compensateTemp(_temperature[ch].update.avg); + _vc = ag->pms5003t_1.compensateTemp(_temperature[ch].update.avg); if (utils::isValidTemperature(_vc)) { - pms[json_prop_tempCompensated] = ag.round2(_vc); + pms[json_prop_tempCompensated] = ag->round2(_vc); } } } // Set humidity if valid if (utils::isValidHumidity(_humidity[ch].update.avg)) { - pms[json_prop_rhum] = ag.round2(_humidity[ch].update.avg); + pms[json_prop_rhum] = ag->round2(_humidity[ch].update.avg); // Compensate relative humidity when flag is set if (compensate) { - _vc = ag.pms5003t_1.compensateHum(_humidity[ch].update.avg); + _vc = ag->pms5003t_1.compensateHum(_humidity[ch].update.avg); if (utils::isValidTemperature(_vc)) { - pms[json_prop_rhumCompensated] = ag.round2(_vc); + pms[json_prop_rhumCompensated] = ag->round2(_vc); } } } @@ -858,9 +858,9 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem utils::isValidHumidity(_humidity[ch].update.avg)) { // Note: the pms5003t object is not matter either for channel 1 or 2, compensate points to // the same base function - float pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); + float pm25 = ag->pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); if (utils::isValidPm(pm25)) { - pms[json_prop_pm25Compensated] = ag.round2(pm25); + pms[json_prop_pm25Compensated] = ag->round2(pm25); } } } @@ -876,144 +876,144 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem /// PM1.0 atmospheric environment if (utils::isValidPm(_pm_01[0].update.avg) && utils::isValidPm(_pm_01[1].update.avg)) { float avg = (_pm_01[0].update.avg + _pm_01[1].update.avg) / 2.0f; - pms[json_prop_pm01Ae] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm01Ae] = ag.round2(_pm_01[0].update.avg); - pms["channels"]["2"][json_prop_pm01Ae] = ag.round2(_pm_01[1].update.avg); + pms[json_prop_pm01Ae] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm01Ae] = ag->round2(_pm_01[0].update.avg); + pms["channels"]["2"][json_prop_pm01Ae] = ag->round2(_pm_01[1].update.avg); } else if (utils::isValidPm(_pm_01[0].update.avg)) { - pms[json_prop_pm01Ae] = ag.round2(_pm_01[0].update.avg); - pms["channels"]["1"][json_prop_pm01Ae] = ag.round2(_pm_01[0].update.avg); + pms[json_prop_pm01Ae] = ag->round2(_pm_01[0].update.avg); + pms["channels"]["1"][json_prop_pm01Ae] = ag->round2(_pm_01[0].update.avg); } else if (utils::isValidPm(_pm_01[1].update.avg)) { - pms[json_prop_pm01Ae] = ag.round2(_pm_01[1].update.avg); - pms["channels"]["2"][json_prop_pm01Ae] = ag.round2(_pm_01[1].update.avg); + pms[json_prop_pm01Ae] = ag->round2(_pm_01[1].update.avg); + pms["channels"]["2"][json_prop_pm01Ae] = ag->round2(_pm_01[1].update.avg); } /// PM2.5 atmospheric environment if (utils::isValidPm(_pm_25[0].update.avg) && utils::isValidPm(_pm_25[1].update.avg)) { float avg = (_pm_25[0].update.avg + _pm_25[1].update.avg) / 2.0f; - pms[json_prop_pm25Ae] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm25Ae] = ag.round2(_pm_25[0].update.avg); - pms["channels"]["2"][json_prop_pm25Ae] = ag.round2(_pm_25[1].update.avg); + pms[json_prop_pm25Ae] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm25Ae] = ag->round2(_pm_25[0].update.avg); + pms["channels"]["2"][json_prop_pm25Ae] = ag->round2(_pm_25[1].update.avg); } else if (utils::isValidPm(_pm_25[0].update.avg)) { - pms[json_prop_pm25Ae] = ag.round2(_pm_25[0].update.avg); - pms["channels"]["1"][json_prop_pm25Ae] = ag.round2(_pm_25[0].update.avg); + pms[json_prop_pm25Ae] = ag->round2(_pm_25[0].update.avg); + pms["channels"]["1"][json_prop_pm25Ae] = ag->round2(_pm_25[0].update.avg); } else if (utils::isValidPm(_pm_25[1].update.avg)) { - pms[json_prop_pm25Ae] = ag.round2(_pm_25[1].update.avg); - pms["channels"]["2"][json_prop_pm25Ae] = ag.round2(_pm_25[1].update.avg); + pms[json_prop_pm25Ae] = ag->round2(_pm_25[1].update.avg); + pms["channels"]["2"][json_prop_pm25Ae] = ag->round2(_pm_25[1].update.avg); } /// PM10 atmospheric environment if (utils::isValidPm(_pm_10[0].update.avg) && utils::isValidPm(_pm_10[1].update.avg)) { float avg = (_pm_10[0].update.avg + _pm_10[1].update.avg) / 2.0f; - pms[json_prop_pm10Ae] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm10Ae] = ag.round2(_pm_10[0].update.avg); - pms["channels"]["2"][json_prop_pm10Ae] = ag.round2(_pm_10[1].update.avg); + pms[json_prop_pm10Ae] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm10Ae] = ag->round2(_pm_10[0].update.avg); + pms["channels"]["2"][json_prop_pm10Ae] = ag->round2(_pm_10[1].update.avg); } else if (utils::isValidPm(_pm_10[0].update.avg)) { - pms[json_prop_pm10Ae] = ag.round2(_pm_10[0].update.avg); - pms["channels"]["1"][json_prop_pm10Ae] = ag.round2(_pm_10[0].update.avg); + pms[json_prop_pm10Ae] = ag->round2(_pm_10[0].update.avg); + pms["channels"]["1"][json_prop_pm10Ae] = ag->round2(_pm_10[0].update.avg); } else if (utils::isValidPm(_pm_10[1].update.avg)) { - pms[json_prop_pm10Ae] = ag.round2(_pm_10[1].update.avg); - pms["channels"]["2"][json_prop_pm10Ae] = ag.round2(_pm_10[1].update.avg); + pms[json_prop_pm10Ae] = ag->round2(_pm_10[1].update.avg); + pms["channels"]["2"][json_prop_pm10Ae] = ag->round2(_pm_10[1].update.avg); } /// PM1.0 standard particle if (utils::isValidPm(_pm_01_sp[0].update.avg) && utils::isValidPm(_pm_01_sp[1].update.avg)) { float avg = (_pm_01_sp[0].update.avg + _pm_01_sp[1].update.avg) / 2.0f; - pms[json_prop_pm01Sp] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm01Sp] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["2"][json_prop_pm01Sp] = ag.round2(_pm_01_sp[1].update.avg); + pms[json_prop_pm01Sp] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm01Sp] = ag->round2(_pm_01_sp[0].update.avg); + pms["channels"]["2"][json_prop_pm01Sp] = ag->round2(_pm_01_sp[1].update.avg); } else if (utils::isValidPm(_pm_01_sp[0].update.avg)) { - pms[json_prop_pm01Sp] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["1"][json_prop_pm01Sp] = ag.round2(_pm_01_sp[0].update.avg); + pms[json_prop_pm01Sp] = ag->round2(_pm_01_sp[0].update.avg); + pms["channels"]["1"][json_prop_pm01Sp] = ag->round2(_pm_01_sp[0].update.avg); } else if (utils::isValidPm(_pm_01_sp[1].update.avg)) { - pms[json_prop_pm01Sp] = ag.round2(_pm_01_sp[1].update.avg); - pms["channels"]["2"][json_prop_pm01Sp] = ag.round2(_pm_01_sp[1].update.avg); + pms[json_prop_pm01Sp] = ag->round2(_pm_01_sp[1].update.avg); + pms["channels"]["2"][json_prop_pm01Sp] = ag->round2(_pm_01_sp[1].update.avg); } /// PM2.5 standard particle if (utils::isValidPm(_pm_25_sp[0].update.avg) && utils::isValidPm(_pm_25_sp[1].update.avg)) { float avg = (_pm_25_sp[0].update.avg + _pm_25_sp[1].update.avg) / 2.0f; - pms[json_prop_pm25Sp] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm25Sp] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["2"][json_prop_pm25Sp] = ag.round2(_pm_25_sp[1].update.avg); + pms[json_prop_pm25Sp] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm25Sp] = ag->round2(_pm_25_sp[0].update.avg); + pms["channels"]["2"][json_prop_pm25Sp] = ag->round2(_pm_25_sp[1].update.avg); } else if (utils::isValidPm(_pm_25_sp[0].update.avg)) { - pms[json_prop_pm25Sp] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["1"][json_prop_pm25Sp] = ag.round2(_pm_25_sp[0].update.avg); + pms[json_prop_pm25Sp] = ag->round2(_pm_25_sp[0].update.avg); + pms["channels"]["1"][json_prop_pm25Sp] = ag->round2(_pm_25_sp[0].update.avg); } else if (utils::isValidPm(_pm_25_sp[1].update.avg)) { - pms[json_prop_pm25Sp] = ag.round2(_pm_25_sp[1].update.avg); - pms["channels"]["2"][json_prop_pm25Sp] = ag.round2(_pm_25_sp[1].update.avg); + pms[json_prop_pm25Sp] = ag->round2(_pm_25_sp[1].update.avg); + pms["channels"]["2"][json_prop_pm25Sp] = ag->round2(_pm_25_sp[1].update.avg); } /// PM10 standard particle if (utils::isValidPm(_pm_10_sp[0].update.avg) && utils::isValidPm(_pm_10_sp[1].update.avg)) { float avg = (_pm_10_sp[0].update.avg + _pm_10_sp[1].update.avg) / 2.0f; - pms[json_prop_pm10Sp] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm10Sp] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["2"][json_prop_pm10Sp] = ag.round2(_pm_10_sp[1].update.avg); + pms[json_prop_pm10Sp] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm10Sp] = ag->round2(_pm_10_sp[0].update.avg); + pms["channels"]["2"][json_prop_pm10Sp] = ag->round2(_pm_10_sp[1].update.avg); } else if (utils::isValidPm(_pm_10_sp[0].update.avg)) { - pms[json_prop_pm10Sp] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["1"][json_prop_pm10Sp] = ag.round2(_pm_10_sp[0].update.avg); + pms[json_prop_pm10Sp] = ag->round2(_pm_10_sp[0].update.avg); + pms["channels"]["1"][json_prop_pm10Sp] = ag->round2(_pm_10_sp[0].update.avg); } else if (utils::isValidPm(_pm_10_sp[1].update.avg)) { - pms[json_prop_pm10Sp] = ag.round2(_pm_10_sp[1].update.avg); - pms["channels"]["2"][json_prop_pm10Sp] = ag.round2(_pm_10_sp[1].update.avg); + pms[json_prop_pm10Sp] = ag->round2(_pm_10_sp[1].update.avg); + pms["channels"]["2"][json_prop_pm10Sp] = ag->round2(_pm_10_sp[1].update.avg); } /// PM003 particle count if (utils::isValidPm03Count(_pm_03_pc[0].update.avg) && utils::isValidPm03Count(_pm_03_pc[1].update.avg)) { float avg = (_pm_03_pc[0].update.avg + _pm_03_pc[1].update.avg) / 2.0f; - pms[json_prop_pm03Count] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm03Count] = ag.round2(_pm_03_pc[0].update.avg); - pms["channels"]["2"][json_prop_pm03Count] = ag.round2(_pm_03_pc[1].update.avg); + pms[json_prop_pm03Count] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm03Count] = ag->round2(_pm_03_pc[0].update.avg); + pms["channels"]["2"][json_prop_pm03Count] = ag->round2(_pm_03_pc[1].update.avg); } else if (utils::isValidPm03Count(_pm_03_pc[0].update.avg)) { - pms[json_prop_pm03Count] = ag.round2(_pm_03_pc[0].update.avg); - pms["channels"]["1"][json_prop_pm03Count] = ag.round2(_pm_03_pc[0].update.avg); + pms[json_prop_pm03Count] = ag->round2(_pm_03_pc[0].update.avg); + pms["channels"]["1"][json_prop_pm03Count] = ag->round2(_pm_03_pc[0].update.avg); } else if (utils::isValidPm03Count(_pm_03_pc[1].update.avg)) { - pms[json_prop_pm03Count] = ag.round2(_pm_03_pc[1].update.avg); - pms["channels"]["2"][json_prop_pm03Count] = ag.round2(_pm_03_pc[1].update.avg); + pms[json_prop_pm03Count] = ag->round2(_pm_03_pc[1].update.avg); + pms["channels"]["2"][json_prop_pm03Count] = ag->round2(_pm_03_pc[1].update.avg); } /// PM0.5 particle count if (utils::isValidPm03Count(_pm_05_pc[0].update.avg) && utils::isValidPm03Count(_pm_05_pc[1].update.avg)) { float avg = (_pm_05_pc[0].update.avg + _pm_05_pc[1].update.avg) / 2.0f; - pms[json_prop_pm05Count] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm05Count] = ag.round2(_pm_05_pc[0].update.avg); - pms["channels"]["2"][json_prop_pm05Count] = ag.round2(_pm_05_pc[1].update.avg); + pms[json_prop_pm05Count] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm05Count] = ag->round2(_pm_05_pc[0].update.avg); + pms["channels"]["2"][json_prop_pm05Count] = ag->round2(_pm_05_pc[1].update.avg); } else if (utils::isValidPm03Count(_pm_05_pc[0].update.avg)) { - pms[json_prop_pm05Count] = ag.round2(_pm_05_pc[0].update.avg); - pms["channels"]["1"][json_prop_pm05Count] = ag.round2(_pm_05_pc[0].update.avg); + pms[json_prop_pm05Count] = ag->round2(_pm_05_pc[0].update.avg); + pms["channels"]["1"][json_prop_pm05Count] = ag->round2(_pm_05_pc[0].update.avg); } else if (utils::isValidPm03Count(_pm_05_pc[1].update.avg)) { - pms[json_prop_pm05Count] = ag.round2(_pm_05_pc[1].update.avg); - pms["channels"]["2"][json_prop_pm05Count] = ag.round2(_pm_05_pc[1].update.avg); + pms[json_prop_pm05Count] = ag->round2(_pm_05_pc[1].update.avg); + pms["channels"]["2"][json_prop_pm05Count] = ag->round2(_pm_05_pc[1].update.avg); } /// PM1.0 particle count if (utils::isValidPm03Count(_pm_01_pc[0].update.avg) && utils::isValidPm03Count(_pm_01_pc[1].update.avg)) { float avg = (_pm_01_pc[0].update.avg + _pm_01_pc[1].update.avg) / 2.0f; - pms[json_prop_pm1Count] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm1Count] = ag.round2(_pm_01_pc[0].update.avg); - pms["channels"]["2"][json_prop_pm1Count] = ag.round2(_pm_01_pc[1].update.avg); + pms[json_prop_pm1Count] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm1Count] = ag->round2(_pm_01_pc[0].update.avg); + pms["channels"]["2"][json_prop_pm1Count] = ag->round2(_pm_01_pc[1].update.avg); } else if (utils::isValidPm03Count(_pm_01_pc[0].update.avg)) { - pms[json_prop_pm1Count] = ag.round2(_pm_01_pc[0].update.avg); - pms["channels"]["1"][json_prop_pm1Count] = ag.round2(_pm_01_pc[0].update.avg); + pms[json_prop_pm1Count] = ag->round2(_pm_01_pc[0].update.avg); + pms["channels"]["1"][json_prop_pm1Count] = ag->round2(_pm_01_pc[0].update.avg); } else if (utils::isValidPm03Count(_pm_01_pc[1].update.avg)) { - pms[json_prop_pm1Count] = ag.round2(_pm_01_pc[1].update.avg); - pms["channels"]["2"][json_prop_pm1Count] = ag.round2(_pm_01_pc[1].update.avg); + pms[json_prop_pm1Count] = ag->round2(_pm_01_pc[1].update.avg); + pms["channels"]["2"][json_prop_pm1Count] = ag->round2(_pm_01_pc[1].update.avg); } /// PM2.5 particle count if (utils::isValidPm03Count(_pm_25_pc[0].update.avg) && utils::isValidPm03Count(_pm_25_pc[1].update.avg)) { float avg = (_pm_25_pc[0].update.avg + _pm_25_pc[1].update.avg) / 2.0f; - pms[json_prop_pm25Count] = ag.round2(avg); - pms["channels"]["1"][json_prop_pm25Count] = ag.round2(_pm_25_pc[0].update.avg); - pms["channels"]["2"][json_prop_pm25Count] = ag.round2(_pm_25_pc[1].update.avg); + pms[json_prop_pm25Count] = ag->round2(avg); + pms["channels"]["1"][json_prop_pm25Count] = ag->round2(_pm_25_pc[0].update.avg); + pms["channels"]["2"][json_prop_pm25Count] = ag->round2(_pm_25_pc[1].update.avg); } else if (utils::isValidPm03Count(_pm_25_pc[0].update.avg)) { - pms[json_prop_pm25Count] = ag.round2(_pm_25_pc[0].update.avg); - pms["channels"]["1"][json_prop_pm25Count] = ag.round2(_pm_25_pc[0].update.avg); + pms[json_prop_pm25Count] = ag->round2(_pm_25_pc[0].update.avg); + pms["channels"]["1"][json_prop_pm25Count] = ag->round2(_pm_25_pc[0].update.avg); } else if (utils::isValidPm03Count(_pm_25_pc[1].update.avg)) { - pms[json_prop_pm25Count] = ag.round2(_pm_25_pc[1].update.avg); - pms["channels"]["2"][json_prop_pm25Count] = ag.round2(_pm_25_pc[1].update.avg); + pms[json_prop_pm25Count] = ag->round2(_pm_25_pc[1].update.avg); + pms["channels"]["2"][json_prop_pm25Count] = ag->round2(_pm_25_pc[1].update.avg); } // NOTE: No need for particle count 5.0 and 10. When allCh is true, basically monitor using @@ -1025,40 +1025,40 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem utils::isValidTemperature(_temperature[1].update.avg)) { float temperature = (_temperature[0].update.avg + _temperature[1].update.avg) / 2.0f; - pms[json_prop_temp] = ag.round2(temperature); - pms["channels"]["1"][json_prop_temp] = ag.round2(_temperature[0].update.avg); - pms["channels"]["2"][json_prop_temp] = ag.round2(_temperature[1].update.avg); + pms[json_prop_temp] = ag->round2(temperature); + pms["channels"]["1"][json_prop_temp] = ag->round2(_temperature[0].update.avg); + pms["channels"]["2"][json_prop_temp] = ag->round2(_temperature[1].update.avg); if (compensate) { // Compensate both temperature channel - float temp = ag.pms5003t_1.compensateTemp(temperature); - float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].update.avg); - float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].update.avg); - pms[json_prop_tempCompensated] = ag.round2(temp); - pms["channels"]["1"][json_prop_tempCompensated] = ag.round2(temp1); - pms["channels"]["2"][json_prop_tempCompensated] = ag.round2(temp2); + float temp = ag->pms5003t_1.compensateTemp(temperature); + float temp1 = ag->pms5003t_1.compensateTemp(_temperature[0].update.avg); + float temp2 = ag->pms5003t_2.compensateTemp(_temperature[1].update.avg); + pms[json_prop_tempCompensated] = ag->round2(temp); + pms["channels"]["1"][json_prop_tempCompensated] = ag->round2(temp1); + pms["channels"]["2"][json_prop_tempCompensated] = ag->round2(temp2); } } else if (utils::isValidTemperature(_temperature[0].update.avg)) { - pms[json_prop_temp] = ag.round2(_temperature[0].update.avg); - pms["channels"]["1"][json_prop_temp] = ag.round2(_temperature[0].update.avg); + pms[json_prop_temp] = ag->round2(_temperature[0].update.avg); + pms["channels"]["1"][json_prop_temp] = ag->round2(_temperature[0].update.avg); if (compensate) { // Compensate channel 1 - float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].update.avg); - pms[json_prop_tempCompensated] = ag.round2(temp1); - pms["channels"]["1"][json_prop_tempCompensated] = ag.round2(temp1); + float temp1 = ag->pms5003t_1.compensateTemp(_temperature[0].update.avg); + pms[json_prop_tempCompensated] = ag->round2(temp1); + pms["channels"]["1"][json_prop_tempCompensated] = ag->round2(temp1); } } else if (utils::isValidTemperature(_temperature[1].update.avg)) { - pms[json_prop_temp] = ag.round2(_temperature[1].update.avg); - pms["channels"]["2"][json_prop_temp] = ag.round2(_temperature[1].update.avg); + pms[json_prop_temp] = ag->round2(_temperature[1].update.avg); + pms["channels"]["2"][json_prop_temp] = ag->round2(_temperature[1].update.avg); if (compensate) { // Compensate channel 2 - float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].update.avg); - pms[json_prop_tempCompensated] = ag.round2(temp2); - pms["channels"]["2"][json_prop_tempCompensated] = ag.round2(temp2); + float temp2 = ag->pms5003t_2.compensateTemp(_temperature[1].update.avg); + pms[json_prop_tempCompensated] = ag->round2(temp2); + pms["channels"]["2"][json_prop_tempCompensated] = ag->round2(temp2); } } @@ -1066,40 +1066,40 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem if (utils::isValidHumidity(_humidity[0].update.avg) && utils::isValidHumidity(_humidity[1].update.avg)) { float humidity = (_humidity[0].update.avg + _humidity[1].update.avg) / 2.0f; - pms[json_prop_rhum] = ag.round2(humidity); - pms["channels"]["1"][json_prop_rhum] = ag.round2(_humidity[0].update.avg); - pms["channels"]["2"][json_prop_rhum] = ag.round2(_humidity[1].update.avg); + pms[json_prop_rhum] = ag->round2(humidity); + pms["channels"]["1"][json_prop_rhum] = ag->round2(_humidity[0].update.avg); + pms["channels"]["2"][json_prop_rhum] = ag->round2(_humidity[1].update.avg); if (compensate) { // Compensate both humidity channel - float hum = ag.pms5003t_1.compensateHum(humidity); - float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].update.avg); - float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].update.avg); - pms[json_prop_rhumCompensated] = ag.round2(hum); - pms["channels"]["1"][json_prop_rhumCompensated] = ag.round2(hum1); - pms["channels"]["2"][json_prop_rhumCompensated] = ag.round2(hum2); + float hum = ag->pms5003t_1.compensateHum(humidity); + float hum1 = ag->pms5003t_1.compensateHum(_humidity[0].update.avg); + float hum2 = ag->pms5003t_2.compensateHum(_humidity[1].update.avg); + pms[json_prop_rhumCompensated] = ag->round2(hum); + pms["channels"]["1"][json_prop_rhumCompensated] = ag->round2(hum1); + pms["channels"]["2"][json_prop_rhumCompensated] = ag->round2(hum2); } } else if (utils::isValidHumidity(_humidity[0].update.avg)) { - pms[json_prop_rhum] = ag.round2(_humidity[0].update.avg); - pms["channels"]["1"][json_prop_rhum] = ag.round2(_humidity[0].update.avg); + pms[json_prop_rhum] = ag->round2(_humidity[0].update.avg); + pms["channels"]["1"][json_prop_rhum] = ag->round2(_humidity[0].update.avg); if (compensate) { // Compensate humidity channel 1 - float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].update.avg); - pms[json_prop_rhumCompensated] = ag.round2(hum1); - pms["channels"]["1"][json_prop_rhumCompensated] = ag.round2(hum1); + float hum1 = ag->pms5003t_1.compensateHum(_humidity[0].update.avg); + pms[json_prop_rhumCompensated] = ag->round2(hum1); + pms["channels"]["1"][json_prop_rhumCompensated] = ag->round2(hum1); } } else if (utils::isValidHumidity(_humidity[1].update.avg)) { - pms[json_prop_rhum] = ag.round2(_humidity[1].update.avg); - pms["channels"]["2"][json_prop_rhum] = ag.round2(_humidity[1].update.avg); + pms[json_prop_rhum] = ag->round2(_humidity[1].update.avg); + pms["channels"]["2"][json_prop_rhum] = ag->round2(_humidity[1].update.avg); if (compensate) { // Compensate humidity channel 2 - float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].update.avg); - pms[json_prop_rhumCompensated] = ag.round2(hum2); - pms["channels"]["2"][json_prop_rhumCompensated] = ag.round2(hum2); + float hum2 = ag->pms5003t_2.compensateHum(_humidity[1].update.avg); + pms[json_prop_rhumCompensated] = ag->round2(hum2); + pms["channels"]["2"][json_prop_rhumCompensated] = ag->round2(hum2); } } @@ -1110,22 +1110,22 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem float pm25_comp2 = utils::getInvalidPmValue(); if (utils::isValidPm(_pm_25[0].update.avg) && utils::isValidHumidity(_humidity[0].update.avg)) { - pm25_comp1 = ag.pms5003t_1.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); - pms["channels"]["1"][json_prop_pm25Compensated] = ag.round2(pm25_comp1); + pm25_comp1 = ag->pms5003t_1.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); + pms["channels"]["1"][json_prop_pm25Compensated] = ag->round2(pm25_comp1); } if (utils::isValidPm(_pm_25[1].update.avg) && utils::isValidHumidity(_humidity[1].update.avg)) { - pm25_comp2 = ag.pms5003t_2.compensate(_pm_25[1].update.avg, _humidity[1].update.avg); - pms["channels"]["2"][json_prop_pm25Compensated] = ag.round2(pm25_comp2); + pm25_comp2 = ag->pms5003t_2.compensate(_pm_25[1].update.avg, _humidity[1].update.avg); + pms["channels"]["2"][json_prop_pm25Compensated] = ag->round2(pm25_comp2); } /// Get average or one of the channel compensated value if only one channel is valid if (utils::isValidPm(pm25_comp1) && utils::isValidPm(pm25_comp2)) { - pms[json_prop_pm25Compensated] = ag.round2((pm25_comp1 + pm25_comp2) / 2.0f); + pms[json_prop_pm25Compensated] = ag->round2((pm25_comp1 + pm25_comp2) / 2.0f); } else if (utils::isValidPm(pm25_comp1)) { - pms[json_prop_pm25Compensated] = ag.round2(pm25_comp1); + pms[json_prop_pm25Compensated] = ag->round2(pm25_comp1); } else if (utils::isValidPm(pm25_comp2)) { - pms[json_prop_pm25Compensated] = ag.round2(pm25_comp2); + pms[json_prop_pm25Compensated] = ag->round2(pm25_comp2); } } } diff --git a/src/AgValue.h b/src/AgValue.h index 7402e1d..c5bd5e0 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -34,9 +34,11 @@ private: }; public: - Measurements(); + Measurements(Configuration &config); ~Measurements() {} + void setAirGradient(AirGradient *ag); + // Enumeration for every AG measurements enum MeasurementType { Temperature, @@ -123,27 +125,23 @@ public: */ float getAverage(MeasurementType type, int ch = 1); - float getCorrectedTempHum(AirGradient &ag, Configuration &config, MeasurementType type, - int ch = 1, bool forceCorrection = false); + float getCorrectedTempHum(MeasurementType type, int ch = 1, bool forceCorrection = false); /** * @brief Get the Corrected PM25 object based on the correction algorithm from configuration - * + * * If correction is not enabled, then will return the raw value (either average or last value) * - * @param ag AirGradient instance - * @param config Configuration instance * @param useAvg Use moving average value if true, otherwise use latest value * @param ch MeasurementType channel * @return float Corrected PM2.5 value */ - float getCorrectedPM25(AirGradient &ag, Configuration &config, bool useAvg = false, int ch = 1); + float getCorrectedPM25(bool useAvg = false, int ch = 1); /** * build json payload for every measurements */ - String toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, - Configuration &config); + String toString(bool localServer, AgFirmwareMode fwMode, int rssi); /** * Set to true if want to debug every update value @@ -158,6 +156,9 @@ public: #endif private: + Configuration &config; + AirGradient *ag; + // Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T FloatValue _temperature[2]; FloatValue _humidity[2]; @@ -216,10 +217,9 @@ private: */ void validateChannel(int ch); - JSONVar buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirGradient &ag, - Configuration &config); - JSONVar buildIndoor(bool localServer, AirGradient &ag, Configuration &config); - JSONVar buildPMS(AirGradient &ag, int ch, bool allCh, bool withTempHum, bool compensate); + JSONVar buildOutdoor(bool localServer, AgFirmwareMode fwMode); + JSONVar buildIndoor(bool localServer); + JSONVar buildPMS(int ch, bool allCh, bool withTempHum, bool compensate); }; #endif /** _AG_VALUE_H_ */ From af16c1c06033bb4a196f17b83ff1f55c1629cdbb Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 24 Jan 2025 09:56:01 +0700 Subject: [PATCH 06/11] Apply temphum correction --- examples/OneOpenAir/OpenMetrics.cpp | 12 ++++--- src/AgOledDisplay.cpp | 8 ++--- src/AgValue.cpp | 49 ++++++++++++++++------------- src/AgValue.h | 10 ++++++ 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/examples/OneOpenAir/OpenMetrics.cpp b/examples/OneOpenAir/OpenMetrics.cpp index 753f9c7..393025c 100644 --- a/examples/OneOpenAir/OpenMetrics.cpp +++ b/examples/OneOpenAir/OpenMetrics.cpp @@ -137,11 +137,15 @@ String OpenMetrics::getPayload(void) { /** Get temperature and humidity compensated */ if (ag->isOne()) { - atmpCompensated = _temp; - ahumCompensated = _hum; + atmpCompensated = round(measure.getCorrectedTempHum(Measurements::Temperature)); + ahumCompensated = round(measure.getCorrectedTempHum(Measurements::Humidity)); } else { - atmpCompensated = ag->pms5003t_1.compensateTemp(_temp); - ahumCompensated = ag->pms5003t_1.compensateHum(_hum); + atmpCompensated = round((measure.getCorrectedTempHum(Measurements::Temperature, 1) + + measure.getCorrectedTempHum(Measurements::Temperature, 2)) / + 2.0f); + ahumCompensated = round((measure.getCorrectedTempHum(Measurements::Humidity, 1) + + measure.getCorrectedTempHum(Measurements::Humidity, 2)) / + 2.0f); } // Add measurements that valid to the metrics diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index bccab18..840d24f 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -12,7 +12,7 @@ */ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { /** Temperature */ - float temp = value.getAverage(Measurements::Temperature); + float temp = value.getCorrectedTempHum(Measurements::Temperature, 1); if (utils::isValidTemperature(temp)) { float t = 0.0f; if (config.isTemperatureUnitInF()) { @@ -44,7 +44,7 @@ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { DISP()->drawUTF8(1, 10, buf); /** Show humidity */ - int rhum = round(value.getAverage(Measurements::Humidity)); + int rhum = round(value.getCorrectedTempHum(Measurements::Humidity, 1)); if (utils::isValidHumidity(rhum)) { snprintf(buf, buf_size, "%d%%", rhum); } else { @@ -389,7 +389,7 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set temperature and humidity */ - float temp = value.getAverage(Measurements::Temperature); + float temp = value.getCorrectedTempHum(Measurements::Temperature, 1); if (utils::isValidTemperature(temp)) { if (config.isTemperatureUnitInF()) { snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", utils::degreeC_To_F(temp)); @@ -407,7 +407,7 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setCursor(0, 24); ag->display.setText(strBuf); - int rhum = round(value.getAverage(Measurements::Humidity)); + int rhum = round(value.getCorrectedTempHum(Measurements::Humidity, 1)); if (utils::isValidHumidity(rhum)) { snprintf(strBuf, sizeof(strBuf), "H:%d %%", rhum); } else { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index e265c89..c6be2e8 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -551,9 +551,12 @@ float Measurements::getCorrectedTempHum(MeasurementType type, int ch, bool force switch (type) { case Temperature: { rawValue = _temperature[ch].update.avg; - Configuration::TempHumCorrection tmp = config.getTempCorrection(); - if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { + + // Apply 'standard' correction if its defined or correction forced + if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024) { + return ag->pms5003t_1.compensateTemp(rawValue); + } else if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_NONE && forceCorrection) { return ag->pms5003t_1.compensateTemp(rawValue); } @@ -564,9 +567,12 @@ float Measurements::getCorrectedTempHum(MeasurementType type, int ch, bool force } case Humidity: { rawValue = _humidity[ch].update.avg; - Configuration::TempHumCorrection tmp = config.getHumCorrection(); - if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024 || forceCorrection) { + + // Apply 'standard' correction if its defined or correction forced + if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_AG_PMS5003T_2024) { + return ag->pms5003t_1.compensateHum(rawValue); + } else if (tmp.algorithm == TempHumCorrectionAlgorithm::CA_TH_NONE && forceCorrection) { return ag->pms5003t_1.compensateHum(rawValue); } @@ -589,6 +595,7 @@ float Measurements::getCorrectedTempHum(MeasurementType type, int ch, bool force // Custom correction constants float corrected = (rawValue * correction.scalingFactor) + correction.intercept; + Serial.println("Custom correction applied"); return corrected; } @@ -747,14 +754,14 @@ JSONVar Measurements::buildIndoor(bool localServer) { if (utils::isValidTemperature(_temperature[0].update.avg)) { indoor[json_prop_temp] = ag->round2(_temperature[0].update.avg); if (localServer) { - indoor[json_prop_tempCompensated] = ag->round2(_temperature[0].update.avg); + indoor[json_prop_tempCompensated] = ag->round2(getCorrectedTempHum(Temperature)); } } // Add humidity if (utils::isValidHumidity(_humidity[0].update.avg)) { indoor[json_prop_rhum] = ag->round2(_humidity[0].update.avg); if (localServer) { - indoor[json_prop_rhumCompensated] = ag->round2(_humidity[0].update.avg); + indoor[json_prop_rhumCompensated] = ag->round2(getCorrectedTempHum(Humidity)); } } } @@ -834,7 +841,7 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen pms[json_prop_temp] = ag->round2(_temperature[ch].update.avg); // Compensate temperature when flag is set if (compensate) { - _vc = ag->pms5003t_1.compensateTemp(_temperature[ch].update.avg); + _vc = getCorrectedTempHum(Temperature, ch, true); if (utils::isValidTemperature(_vc)) { pms[json_prop_tempCompensated] = ag->round2(_vc); } @@ -845,8 +852,8 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen pms[json_prop_rhum] = ag->round2(_humidity[ch].update.avg); // Compensate relative humidity when flag is set if (compensate) { - _vc = ag->pms5003t_1.compensateHum(_humidity[ch].update.avg); - if (utils::isValidTemperature(_vc)) { + _vc = getCorrectedTempHum(Humidity, ch, true); + if (utils::isValidHumidity(_vc)) { pms[json_prop_rhumCompensated] = ag->round2(_vc); } } @@ -1031,10 +1038,10 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate both temperature channel - float temp = ag->pms5003t_1.compensateTemp(temperature); - float temp1 = ag->pms5003t_1.compensateTemp(_temperature[0].update.avg); - float temp2 = ag->pms5003t_2.compensateTemp(_temperature[1].update.avg); - pms[json_prop_tempCompensated] = ag->round2(temp); + float temp1 = getCorrectedTempHum(Temperature, 1, true); + float temp2 = getCorrectedTempHum(Temperature, 2, true); + float tempAverage = (temp1 + temp2) / 2.0f; + pms[json_prop_tempCompensated] = ag->round2(tempAverage); pms["channels"]["1"][json_prop_tempCompensated] = ag->round2(temp1); pms["channels"]["2"][json_prop_tempCompensated] = ag->round2(temp2); } @@ -1045,7 +1052,7 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate channel 1 - float temp1 = ag->pms5003t_1.compensateTemp(_temperature[0].update.avg); + float temp1 = getCorrectedTempHum(Temperature, 1, true); pms[json_prop_tempCompensated] = ag->round2(temp1); pms["channels"]["1"][json_prop_tempCompensated] = ag->round2(temp1); } @@ -1056,7 +1063,7 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate channel 2 - float temp2 = ag->pms5003t_2.compensateTemp(_temperature[1].update.avg); + float temp2 = getCorrectedTempHum(Temperature, 2, true); pms[json_prop_tempCompensated] = ag->round2(temp2); pms["channels"]["2"][json_prop_tempCompensated] = ag->round2(temp2); } @@ -1072,10 +1079,10 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate both humidity channel - float hum = ag->pms5003t_1.compensateHum(humidity); - float hum1 = ag->pms5003t_1.compensateHum(_humidity[0].update.avg); - float hum2 = ag->pms5003t_2.compensateHum(_humidity[1].update.avg); - pms[json_prop_rhumCompensated] = ag->round2(hum); + float hum1 = getCorrectedTempHum(Humidity, 1, true); + float hum2 = getCorrectedTempHum(Humidity, 2, true); + float humAverage = (hum1 + hum2) / 2.0f; + pms[json_prop_rhumCompensated] = ag->round2(humAverage); pms["channels"]["1"][json_prop_rhumCompensated] = ag->round2(hum1); pms["channels"]["2"][json_prop_rhumCompensated] = ag->round2(hum2); } @@ -1086,7 +1093,7 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate humidity channel 1 - float hum1 = ag->pms5003t_1.compensateHum(_humidity[0].update.avg); + float hum1 = getCorrectedTempHum(Humidity, 1, true); pms[json_prop_rhumCompensated] = ag->round2(hum1); pms["channels"]["1"][json_prop_rhumCompensated] = ag->round2(hum1); } @@ -1097,7 +1104,7 @@ JSONVar Measurements::buildPMS(int ch, bool allCh, bool withTempHum, bool compen if (compensate) { // Compensate humidity channel 2 - float hum2 = ag->pms5003t_2.compensateHum(_humidity[1].update.avg); + float hum2 = getCorrectedTempHum(Humidity, 2, true); pms[json_prop_rhumCompensated] = ag->round2(hum2); pms["channels"]["2"][json_prop_rhumCompensated] = ag->round2(hum2); } diff --git a/src/AgValue.h b/src/AgValue.h index c5bd5e0..db5508e 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -125,6 +125,16 @@ public: */ float getAverage(MeasurementType type, int ch = 1); + /** + * @brief Get Temperature or Humidity correction value + * Only if correction is applied from configuration or forceCorrection is True + * + * @param type measurement type either Temperature or Humidity + * @param ch target type value channel + * @param forceCorrection force using correction even though config correction is not applied, but + * not for CUSTOM + * @return correction value + */ float getCorrectedTempHum(MeasurementType type, int ch = 1, bool forceCorrection = false); /** From a28931493aa4f73d8cf336fea30a57d0140e47aa Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 24 Jan 2025 18:03:13 +0700 Subject: [PATCH 07/11] Add docs about how to set correction from local server --- docs/local-server.md | 56 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index f56d7f1..06dcdf1 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -2,7 +2,7 @@ From [firmware version 3.0.10](firmwares) onwards, the AirGradient ONE and Open Air monitors have below API available. -#### Discovery +### Discovery The monitors run a mDNS discovery. So within the same network, the monitor can be accessed through: @@ -11,7 +11,7 @@ http://airgradient_{{serialnumber}}.local The following requests are possible: -#### Get Current Air Quality (GET) +### Get Current Air Quality (GET) With the path "/measures/current" you can get the current air quality data. @@ -80,7 +80,7 @@ You get the following response: 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. -#### Get Configuration Parameters (GET) +### Get Configuration Parameters (GET) "/config" path returns the current configuration of the monitor. @@ -111,7 +111,7 @@ Compensated values apply correction algorithms to make the sensor values more ac } ``` -#### Set Configuration Parameters (PUT) +### Set Configuration Parameters (PUT) Configuration parameters can be changed with a PUT request to the monitor, e.g. @@ -131,11 +131,11 @@ Example to set monitor to Celsius ``` -d "{\"param\":\"value\"}" ``` -#### Avoiding Conflicts with Configuration on AirGradient Server +### Avoiding Conflicts with Configuration on AirGradient Server If the monitor is set up on the AirGradient dashboard, it will also receive the configuration parameters from there. In case you do not want this, please set `configurationControl` to `local`. In case you set it to `cloud` and want to change it to `local`, you need to make a factory reset. -#### Configuration Parameters (GET/PUT) +### Configuration Parameters (GET/PUT) | Properties | Description | Type | Accepted Values | Example | |-----------------------------------|:-----------------------------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------| @@ -160,9 +160,9 @@ If the monitor is set up on the AirGradient dashboard, it will also receive the -#### Corrections +### Corrections -The `corrections` object allows configuring PM2.5 correction algorithms and parameters locally. This affects both the display and local server response values. +The `corrections` object allows configuring PM2.5, Temperature and Humidity correction algorithms and parameters locally. This affects both the display, local server response and open metrics values. Example correction configuration: @@ -176,11 +176,29 @@ Example correction configuration: "scalingFactor": 0, "useEpa2021": false } - } + }, + "atmp": { + "correctionAlgorithm": "