From 210f0a5ff9f5115ec2385c6e965f746302474c25 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 30 Sep 2024 22:27:45 +0700 Subject: [PATCH 01/69] Update CO2 level ledbar indicator --- src/AgStateMachine.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 85f87d5..9155938 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -70,32 +70,32 @@ void StateMachine::sensorhandleLeds(void) { */ void StateMachine::co2handleLeds(void) { int co2Value = value.CO2; - if (co2Value <= 600) { + if (co2Value <= 700) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); - } else if (co2Value <= 800) { + } else if (co2Value <= 1000) { /** GG; 2 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2); - } else if (co2Value <= 1000) { + } else if (co2Value <= 1333) { /** YYY; 3 */ ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); - } else if (co2Value <= 1250) { + } else if (co2Value <= 1666) { /** OOOO; 4 */ ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); - } else if (co2Value <= 1500) { + } else if (co2Value <= 2000) { /** OOOOO; 5 */ ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5); - } else if (co2Value <= 1750) { + } else if (co2Value <= 2666) { /** RRRRRR; 6 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); @@ -103,7 +103,7 @@ void StateMachine::co2handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 4); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); - } else if (co2Value <= 2000) { + } else if (co2Value <= 3333) { /** RRRRRRR; 7 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); @@ -112,7 +112,7 @@ void StateMachine::co2handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); - } else if (co2Value <= 3000) { + } else if (co2Value <= 4000) { /** PPPPPPPP; 8 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2); @@ -122,7 +122,7 @@ void StateMachine::co2handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8); - } else { /** > 3000 */ + } else { /** > 4000 */ /* PRPRPRPRP; 9 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); From b86f0d45e325ec8aabce69fc8315745a650ac64c Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 30 Sep 2024 22:33:52 +0700 Subject: [PATCH 02/69] Update PM2 level ledbar indicator --- src/AgStateMachine.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 9155938..8ade805 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -146,32 +146,32 @@ void StateMachine::pm25handleLeds(void) { pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity); } - if (pm25Value < 5) { + if (pm25Value <= 5) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); - } else if (pm25Value < 10) { + } else if (pm25Value <= 10) { /** GG; 2 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2); - } else if (pm25Value < 20) { + } else if (pm25Value <= 20) { /** YYY; 3 */ ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); - } else if (pm25Value < 35) { + } else if (pm25Value <= 35) { /** YYYY; 4 */ ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4); - } else if (pm25Value < 45) { + } else if (pm25Value <= 45) { /** OOOOO; 5 */ ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5); - } else if (pm25Value < 55) { + } else if (pm25Value <= 55) { /** OOOOOO; 6 */ ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); @@ -179,7 +179,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5); ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 6); - } else if (pm25Value < 100) { + } else if (pm25Value <= 100) { /** RRRRRRR; 7 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); @@ -188,7 +188,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); - } else if (pm25Value < 200) { + } else if (pm25Value <= 200) { /** RRRRRRRR; 8 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); @@ -198,7 +198,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8); - } else if (pm25Value < 250) { + } else if (pm25Value <= 250) { /** PPPPPPPPP; 9 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2); From 9325830fad6baed25b0f6f78ea73232fa9f80f7d Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 30 Sep 2024 22:38:52 +0700 Subject: [PATCH 03/69] Replace orange to yellow for ledbar co2 indicator --- src/AgStateMachine.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 8ade805..a4be479 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -84,17 +84,17 @@ void StateMachine::co2handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); } else if (co2Value <= 1666) { /** OOOO; 4 */ - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4); } else if (co2Value <= 2000) { /** OOOOO; 5 */ - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 2); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 3); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 4); - ag->ledBar.setColor(RGB_COLOR_O, ag->ledBar.getNumberOfLeds() - 5); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); } else if (co2Value <= 2666) { /** RRRRRR; 6 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); From fe4389bff4308230d20cf92e88084adb725c48d8 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 30 Sep 2024 22:43:08 +0700 Subject: [PATCH 04/69] Fix color under <4000 for co2 --- src/AgStateMachine.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index a4be479..0fe03c9 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -91,10 +91,10 @@ void StateMachine::co2handleLeds(void) { } else if (co2Value <= 2000) { /** OOOOO; 5 */ ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 2); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 3); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 4); + ag->ledBar.setColor(RGB_COLOR_Y, ag->ledBar.getNumberOfLeds() - 5); } else if (co2Value <= 2666) { /** RRRRRR; 6 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); @@ -113,15 +113,15 @@ void StateMachine::co2handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); } else if (co2Value <= 4000) { - /** PPPPPPPP; 8 */ - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 3); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 4); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 5); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 6); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7); - ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8); + /** RRRRRRRR; 8 */ + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 3); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 4); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); + ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8); } else { /** > 4000 */ /* PRPRPRPRP; 9 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); From 4e41fd5d712c8c72cf0d0692791c23e33c91a315 Mon Sep 17 00:00:00 2001 From: Kelly Campbell Date: Mon, 7 Oct 2024 10:31:24 -0400 Subject: [PATCH 05/69] Fix default for ledBarMode (#252) --- src/AgConfigure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 3f3005f..1446d1a 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -162,7 +162,7 @@ void Configuration::defaultConfig(void) { jconfig[jprop_displayBrightness] = jprop_displayBrightness_default; } if (ag->isOne()) { - jconfig[jprop_ledBarMode] = jprop_ledBarBrightness_default; + jconfig[jprop_ledBarMode] = jprop_ledBarMode_default; } jconfig[jprop_tvocLearningOffset] = jprop_tvocLearningOffset_default; jconfig[jprop_noxLearningOffset] = jprop_noxLearningOffset_default; From e7a91c53bc6b84124333b377e0be9a37d924753e Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 8 Oct 2024 00:34:29 +0700 Subject: [PATCH 06/69] Update PM ledbar threshold --- src/AgStateMachine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 0fe03c9..9b74f96 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -149,7 +149,7 @@ void StateMachine::pm25handleLeds(void) { if (pm25Value <= 5) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); - } else if (pm25Value <= 10) { + } else if (pm25Value <= 9) { /** GG; 2 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 2); @@ -188,7 +188,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 5); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); - } else if (pm25Value <= 200) { + } else if (pm25Value <= 125) { /** RRRRRRRR; 8 */ ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); @@ -198,7 +198,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 6); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 7); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 8); - } else if (pm25Value <= 250) { + } else if (pm25Value <= 225) { /** PPPPPPPPP; 9 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 2); @@ -209,7 +209,7 @@ void StateMachine::pm25handleLeds(void) { ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 7); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 8); ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 9); - } else { /** > 250 */ + } else { /** > 225 */ /* PRPRPRPRP; 9 */ ag->ledBar.setColor(RGB_COLOR_P, ag->ledBar.getNumberOfLeds() - 1); ag->ledBar.setColor(RGB_COLOR_R, ag->ledBar.getNumberOfLeds() - 2); From 60d01c0d9438c09bb9ed71397f4761b088fe7989 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 10 Oct 2024 22:51:37 +0700 Subject: [PATCH 07/69] First init the data structure --- src/AgValue.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/AgValue.h b/src/AgValue.h index 1957828..c3432b3 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -35,6 +35,41 @@ public: } ~Measurements() {} + // Generic struct for reading indication for respective value + struct Reading { + int counter; // How many reading attempts done + int success; // How many reading that success from each attempts + int max; // Maximum reading attempt + }; + + // Reading type for sensor value that outputs float + struct FloatValue { + float lastValue; // Last reading value + float sumValues; // Total value of each reading + float avg; // The last average calculation after maximum reading attempt reached + Reading read; + }; + + // Reading type for sensor value that outputs integer + struct IntegerValue { + int lastValue; // Last reading value + unsigned long sumValues; // Total value of each reading + int avg; // The last average calculation after maximum reading attempt reached + Reading read; + }; + + FloatValue temperature; + FloatValue humidity; + IntegerValue co2; + IntegerValue tvoc; + IntegerValue tvoc_raw; + IntegerValue nox; + IntegerValue nox_raw; + IntegerValue pm_25; + IntegerValue pm_01; + IntegerValue pm_10; + IntegerValue pm_03_pc; + float Temperature; int Humidity; int CO2; From 9c09b82efdd59082d0ae3f936d56d98dcd3622da Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 11 Oct 2024 20:54:05 +0700 Subject: [PATCH 08/69] The data structure and update value function --- src/AgValue.cpp | 178 +++++++++++++++++++++++++++++++++++++++++++++++- src/AgValue.h | 96 ++++++++++++++++---------- 2 files changed, 236 insertions(+), 38 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 867ee42..0b3cfbc 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -1,7 +1,6 @@ #include "AgValue.h" #include "AgConfigure.h" #include "AirGradient.h" -#include "Main/utils.h" #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #define json_prop_pmFirmware "firmware" @@ -37,6 +36,183 @@ String Measurements::pms5003FirmwareVersionBase(String prefix, int fwCode) { return prefix + String("-") + String(fwCode); } +String Measurements::agValueTypeStr(AgValueType type) { + String str; + switch (type) { + case AgValueType::Temperature: + str = "Temperature"; + break; + case AgValueType::Humidity: + str = "Humidity"; + break; + case AgValueType::CO2: + str = "CO2"; + break; + case AgValueType::TVOC: + str = "TVOC"; + break; + case AgValueType::TVOCRaw: + str = "TVOCRaw"; + break; + case AgValueType::NOx: + str = "NOx"; + break; + case AgValueType::NOxRaw: + str = "NOxRaw"; + break; + case AgValueType::PM25: + str = "PM25"; + break; + case AgValueType::PM01: + str = "PM01"; + break; + case AgValueType::PM10: + str = "PM10"; + break; + case AgValueType::PM03: + str = "PM03"; + break; + default: + break; + }; + + return str; +} + +void Measurements::updateValue(AgValueType type, int val) { + // Define data point source + IntegerValue *temporary = nullptr; + float invalidValue = 0; + switch (type) { + case AgValueType::CO2: + temporary = &_co2; + invalidValue = utils::getInvalidCO2(); + break; + case AgValueType::TVOC: + temporary = &_tvoc; + invalidValue = utils::getInvalidVOC(); + break; + case AgValueType::TVOCRaw: + temporary = &_tvoc_raw; + invalidValue = utils::getInvalidVOC(); + break; + case AgValueType::NOx: + temporary = &_nox; + invalidValue = utils::getInvalidNOx(); + break; + case AgValueType::NOxRaw: + temporary = &_nox_raw; + invalidValue = utils::getInvalidNOx(); + break; + case AgValueType::PM25: + temporary = &_pm_25; + invalidValue = utils::getInvalidPmValue(); + break; + case AgValueType::PM01: + temporary = &_pm_01; + invalidValue = utils::getInvalidPmValue(); + break; + case AgValueType::PM10: + temporary = &_pm_10; + invalidValue = utils::getInvalidPmValue(); + break; + case AgValueType::PM03: + temporary = &_pm_03_pc; + invalidValue = utils::getInvalidPmValue(); + break; + default: + break; + }; + + // Sanity check if agvaluetype is defined for integer data type or not + if (temporary == nullptr) { + Serial.printf("%s is not defined for integer data type\n", agValueTypeStr(type)); + // TODO: Just assert? + return; + } + + // Update new value when value provided is not the invalid one + if (val != invalidValue) { + temporary->lastValue = val; + temporary->sumValues = temporary->sumValues + val; + temporary->read.success = temporary->read.success + 1; + } + + // Increment read counter + temporary->read.counter = temporary->read.counter + 1; + + // Calculate value average when maximum set is reached + if (temporary->read.counter >= temporary->read.max) { + // Calculate the average + temporary->avg = temporary->sumValues / temporary->read.success; + + // This is just for notifying + int miss = temporary->read.max - temporary->read.success; + if (miss != 0) { + Serial.printf("%s reading miss %d out of %d update\n", agValueTypeStr(type), miss, + temporary->read.max); + } + + // Resets the sum data and read variables + temporary->sumValues = 0; + temporary->read.counter = 0; + temporary->read.success = 0; + } +} + +void Measurements::updateValue(AgValueType type, float val) { + // Define data point source + FloatValue *temporary = nullptr; + float invalidValue = 0; + switch (type) { + case AgValueType::Temperature: + temporary = &_temperature; + invalidValue = utils::getInvalidTemperature(); + break; + case AgValueType::Humidity: + temporary = &_humidity; + invalidValue = utils::getInvalidHumidity(); + break; + default: + break; + } + + // Sanity check if agvaluetype is defined for float data type or not + if (temporary == nullptr) { + Serial.printf("%s is not defined for float data type\n", agValueTypeStr(type)); + // TODO: Just assert? + return; + } + + // Update new value when value provided is not the invalid one + if (val != invalidValue) { + temporary->lastValue = val; + temporary->sumValues = temporary->sumValues + val; + temporary->read.success = temporary->read.success + 1; + } + + // Increment read counter + temporary->read.counter = temporary->read.counter + 1; + + // Calculate value average when maximum set is reached + if (temporary->read.counter >= temporary->read.max) { + // Calculate the average + temporary->avg = temporary->sumValues / temporary->read.success; + + // This is just for notifying + int miss = temporary->read.max - temporary->read.success; + if (miss != 0) { + Serial.printf("%s reading miss %d out of %d update\n", agValueTypeStr(type), miss, + temporary->read.max); + } + + // Resets the sum data and read variables + temporary->sumValues = 0; + temporary->read.counter = 0; + temporary->read.success = 0; + } +} + String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, void *_ag, void *_config) { AirGradient *ag = (AirGradient *)_ag; diff --git a/src/AgValue.h b/src/AgValue.h index c3432b3..b8476f4 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -1,14 +1,36 @@ #ifndef _AG_VALUE_H_ #define _AG_VALUE_H_ -#include #include "App/AppDef.h" +#include "Main/utils.h" +#include class Measurements { private: - String pms5003FirmwareVersion(int fwCode); - String pms5003TFirmwareVersion(int fwCode); - String pms5003FirmwareVersionBase(String prefix, int fwCode); + // Generic struct for reading indication for respective value + // TODO: Reading naming is confusing, because its not actually reading but updating with new value + struct Reading { + int counter; // How many reading attempts done + int success; // How many reading that success from each attempts + int max; // Maximum reading attempt + }; + + // Reading type for sensor value that outputs float + struct FloatValue { + float lastValue; // Last reading value + float sumValues; // Total value of each reading + float avg; // The last average calculation after maximum reading attempt reached + Reading read; + }; + + // Reading type for sensor value that outputs integer + struct IntegerValue { + int lastValue; // Last reading value + unsigned long sumValues; // Total value of each reading // TODO: explain why unsigned long + int avg; // The last average calculation after maximum reading attempt reached + Reading read; + }; + public: Measurements() { pm25_1 = -1; @@ -35,40 +57,22 @@ public: } ~Measurements() {} - // Generic struct for reading indication for respective value - struct Reading { - int counter; // How many reading attempts done - int success; // How many reading that success from each attempts - int max; // Maximum reading attempt + enum class AgValueType { + Temperature, + Humidity, + CO2, + TVOC, + TVOCRaw, + NOx, + NOxRaw, + PM25, + PM01, + PM10, + PM03, }; - // Reading type for sensor value that outputs float - struct FloatValue { - float lastValue; // Last reading value - float sumValues; // Total value of each reading - float avg; // The last average calculation after maximum reading attempt reached - Reading read; - }; - - // Reading type for sensor value that outputs integer - struct IntegerValue { - int lastValue; // Last reading value - unsigned long sumValues; // Total value of each reading - int avg; // The last average calculation after maximum reading attempt reached - Reading read; - }; - - FloatValue temperature; - FloatValue humidity; - IntegerValue co2; - IntegerValue tvoc; - IntegerValue tvoc_raw; - IntegerValue nox; - IntegerValue nox_raw; - IntegerValue pm_25; - IntegerValue pm_01; - IntegerValue pm_10; - IntegerValue pm_03_pc; + void updateValue(AgValueType type, int val); + void updateValue(AgValueType type, float val); float Temperature; int Humidity; @@ -108,7 +112,25 @@ public: const int targetCount = 20; int bootCount; - String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void* _ag, void* _config); + String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void *_ag, void *_config); + +private: + FloatValue _temperature; + FloatValue _humidity; + IntegerValue _co2; + IntegerValue _tvoc; + IntegerValue _tvoc_raw; + IntegerValue _nox; + IntegerValue _nox_raw; + IntegerValue _pm_25; + IntegerValue _pm_01; + IntegerValue _pm_10; + IntegerValue _pm_03_pc; // particle count 0.3 + + String pms5003FirmwareVersion(int fwCode); + String pms5003TFirmwareVersion(int fwCode); + String pms5003FirmwareVersionBase(String prefix, int fwCode); + String agValueTypeStr(AgValueType type); }; #endif /** _AG_VALUE_H_ */ From 2a5cf78b6848dc1345f1fe4b56bce23350d03e4f Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 11 Oct 2024 22:05:03 +0700 Subject: [PATCH 09/69] updateValue return bool to indicate max average is set or not Add more comments Update naming --- src/AgValue.cpp | 54 +++++++++++++++++++++++++++---------------------- src/AgValue.h | 53 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0b3cfbc..5bb322b 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -79,7 +79,7 @@ String Measurements::agValueTypeStr(AgValueType type) { return str; } -void Measurements::updateValue(AgValueType type, int val) { +bool Measurements::updateValue(AgValueType type, int val) { // Define data point source IntegerValue *temporary = nullptr; float invalidValue = 0; @@ -135,32 +135,35 @@ void Measurements::updateValue(AgValueType type, int val) { if (val != invalidValue) { temporary->lastValue = val; temporary->sumValues = temporary->sumValues + val; - temporary->read.success = temporary->read.success + 1; + temporary->update.success = temporary->update.success + 1; } - // Increment read counter - temporary->read.counter = temporary->read.counter + 1; + // Increment update.counter + temporary->update.counter = temporary->update.counter + 1; // Calculate value average when maximum set is reached - if (temporary->read.counter >= temporary->read.max) { + if (temporary->update.counter >= temporary->update.max) { // Calculate the average - temporary->avg = temporary->sumValues / temporary->read.success; + temporary->avg = temporary->sumValues / temporary->update.success; // This is just for notifying - int miss = temporary->read.max - temporary->read.success; + int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s reading miss %d out of %d update\n", agValueTypeStr(type), miss, - temporary->read.max); + Serial.printf("%s update.ng miss %d out of %d update\n", agValueTypeStr(type), miss, + temporary->update.max); } - // Resets the sum data and read variables + // Resets average related variable calculation temporary->sumValues = 0; - temporary->read.counter = 0; - temporary->read.success = 0; + temporary->update.counter = 0; + temporary->update.success = 0; + return true; } + + return false; } -void Measurements::updateValue(AgValueType type, float val) { +bool Measurements::updateValue(AgValueType type, float val) { // Define data point source FloatValue *temporary = nullptr; float invalidValue = 0; @@ -188,29 +191,32 @@ void Measurements::updateValue(AgValueType type, float val) { if (val != invalidValue) { temporary->lastValue = val; temporary->sumValues = temporary->sumValues + val; - temporary->read.success = temporary->read.success + 1; + temporary->update.success = temporary->update.success + 1; } - // Increment read counter - temporary->read.counter = temporary->read.counter + 1; + // Increment update.counter + temporary->update.counter = temporary->update.counter + 1; // Calculate value average when maximum set is reached - if (temporary->read.counter >= temporary->read.max) { + if (temporary->update.counter >= temporary->update.max) { // Calculate the average - temporary->avg = temporary->sumValues / temporary->read.success; + temporary->avg = temporary->sumValues / temporary->update.success; // This is just for notifying - int miss = temporary->read.max - temporary->read.success; + int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s reading miss %d out of %d update\n", agValueTypeStr(type), miss, - temporary->read.max); + Serial.printf("%s update.ng miss %d out of %d update\n", agValueTypeStr(type), miss, + temporary->update.max); } - // Resets the sum data and read variables + // Resets average related variable calculation temporary->sumValues = 0; - temporary->read.counter = 0; - temporary->read.success = 0; + temporary->update.counter = 0; + temporary->update.success = 0; + return true; } + + return false; } String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, diff --git a/src/AgValue.h b/src/AgValue.h index b8476f4..c39174c 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -7,28 +7,28 @@ class Measurements { private: - // Generic struct for reading indication for respective value - // TODO: Reading naming is confusing, because its not actually reading but updating with new value - struct Reading { - int counter; // How many reading attempts done - int success; // How many reading that success from each attempts - int max; // Maximum reading attempt + // Generic struct for update indication for respective value + struct Update { + int counter; // How many update attempts done + int success; // How many update value that actually give valid value + int max; // Maximum update before calculating average }; // Reading type for sensor value that outputs float struct FloatValue { - float lastValue; // Last reading value - float sumValues; // Total value of each reading - float avg; // The last average calculation after maximum reading attempt reached - Reading read; + float lastValue; // Last update value + float sumValues; // Total value from each update + float avg; // The last average calculation after maximum update attempt reached + Update update; }; // Reading type for sensor value that outputs integer struct IntegerValue { - int lastValue; // Last reading value - unsigned long sumValues; // Total value of each reading // TODO: explain why unsigned long - int avg; // The last average calculation after maximum reading attempt reached - Reading read; + int lastValue; // Last update value + unsigned long sumValues; // Total value from each update; unsigned long to accomodate TVOx and + // NOx raw data + int avg; // The last average calculation after maximum update attempt reached + Update update; }; public: @@ -71,8 +71,29 @@ public: PM03, }; - void updateValue(AgValueType type, int val); - void updateValue(AgValueType type, float val); + /** + * @brief update target type value with new value. + * Each AgValueType has last raw value and last average that are calculated based on max number of + * set This function is for AgValueType that use INT as the data type + * + * @param type (AgValueType) value type that will be updated + * @param val (int) the new value + * @return true if update counter reached and new average value is calculated + * @return false otherwise + */ + bool updateValue(AgValueType type, int val); + + /** + * @brief update target type value with new value. + * Each AgValueType has last raw value and last average that are calculated based on max number of + * set This function is for AgValueType that use FLOAT as the data type + * + * @param type (AgValueType) value type that will be updated + * @param val (float) the new value + * @return true if update counter reached and new average value is calculated + * @return false otherwise + */ + bool updateValue(AgValueType type, float val); float Temperature; int Humidity; From 467b3e86370344f1eb5bb42e3b86c367900002ff Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 11 Oct 2024 22:11:26 +0700 Subject: [PATCH 10/69] Tidy up --- src/AgValue.cpp | 129 +++++++++++++++++++++--------------------------- src/AgValue.h | 25 ++++++++++ 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 5bb322b..961c65b 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -5,79 +5,7 @@ #define json_prop_pmFirmware "firmware" -/** - * @brief Get PMS5003 firmware version string - * - * @param fwCode - * @return String - */ -String Measurements::pms5003FirmwareVersion(int fwCode) { - return pms5003FirmwareVersionBase("PMS5003x", fwCode); -} - -/** - * @brief Get PMS5003T firmware version string - * - * @param fwCode - * @return String - */ -String Measurements::pms5003TFirmwareVersion(int fwCode) { - return pms5003FirmwareVersionBase("PMS5003x", fwCode); -} - -/** - * @brief Get firmware version string - * - * @param prefix Prefix firmware string - * @param fwCode Version code - * @return string - */ -String Measurements::pms5003FirmwareVersionBase(String prefix, int fwCode) { - return prefix + String("-") + String(fwCode); -} - -String Measurements::agValueTypeStr(AgValueType type) { - String str; - switch (type) { - case AgValueType::Temperature: - str = "Temperature"; - break; - case AgValueType::Humidity: - str = "Humidity"; - break; - case AgValueType::CO2: - str = "CO2"; - break; - case AgValueType::TVOC: - str = "TVOC"; - break; - case AgValueType::TVOCRaw: - str = "TVOCRaw"; - break; - case AgValueType::NOx: - str = "NOx"; - break; - case AgValueType::NOxRaw: - str = "NOxRaw"; - break; - case AgValueType::PM25: - str = "PM25"; - break; - case AgValueType::PM01: - str = "PM01"; - break; - case AgValueType::PM10: - str = "PM10"; - break; - case AgValueType::PM03: - str = "PM03"; - break; - default: - break; - }; - - return str; -} +void Measurements::init() {} bool Measurements::updateValue(AgValueType type, int val) { // Define data point source @@ -594,3 +522,58 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, return JSON.stringify(root); } + +String Measurements::pms5003FirmwareVersion(int fwCode) { + return pms5003FirmwareVersionBase("PMS5003x", fwCode); +} + +String Measurements::pms5003TFirmwareVersion(int fwCode) { + return pms5003FirmwareVersionBase("PMS5003x", fwCode); +} + +String Measurements::pms5003FirmwareVersionBase(String prefix, int fwCode) { + return prefix + String("-") + String(fwCode); +} + +String Measurements::agValueTypeStr(AgValueType type) { + String str; + switch (type) { + case AgValueType::Temperature: + str = "Temperature"; + break; + case AgValueType::Humidity: + str = "Humidity"; + break; + case AgValueType::CO2: + str = "CO2"; + break; + case AgValueType::TVOC: + str = "TVOC"; + break; + case AgValueType::TVOCRaw: + str = "TVOCRaw"; + break; + case AgValueType::NOx: + str = "NOx"; + break; + case AgValueType::NOxRaw: + str = "NOxRaw"; + break; + case AgValueType::PM25: + str = "PM25"; + break; + case AgValueType::PM01: + str = "PM01"; + break; + case AgValueType::PM10: + str = "PM10"; + break; + case AgValueType::PM03: + str = "PM03"; + break; + default: + break; + }; + + return str; +} \ No newline at end of file diff --git a/src/AgValue.h b/src/AgValue.h index c39174c..56fc38a 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -71,6 +71,8 @@ public: PM03, }; + void init(); + /** * @brief update target type value with new value. * Each AgValueType has last raw value and last average that are calculated based on max number of @@ -148,9 +150,32 @@ private: IntegerValue _pm_10; IntegerValue _pm_03_pc; // particle count 0.3 + /** + * @brief Get PMS5003 firmware version string + * + * @param fwCode + * @return String + */ String pms5003FirmwareVersion(int fwCode); + /** + * @brief Get PMS5003T firmware version string + * + * @param fwCode + * @return String + */ String pms5003TFirmwareVersion(int fwCode); + /** + * @brief Get firmware version string + * + * @param prefix Prefix firmware string + * @param fwCode Version code + * @return string + */ String pms5003FirmwareVersionBase(String prefix, int fwCode); + + /** + * Convert AgValue Type to string representation of the value + */ String agValueTypeStr(AgValueType type); }; From ea91cf9b6cf029537247e37a3cb9b15d346bd031 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 11 Oct 2024 22:47:43 +0700 Subject: [PATCH 11/69] New function to set max update before averaging Rename enum member --- src/AgValue.cpp | 42 +++++++++++++++++++++++++++++++++++++++--- src/AgValue.h | 16 +++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 961c65b..0190852 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -5,7 +5,43 @@ #define json_prop_pmFirmware "firmware" -void Measurements::init() {} +void Measurements::maxUpdate(AgValueType type, int max) { + switch (type) { + case AgValueType::Temperature: + _temperature.update.max = max; + break; + case AgValueType::Humidity: + _humidity.update.max = max; + break; + case AgValueType::CO2: + _co2.update.max = max; + break; + case AgValueType::TVOC: + _tvoc.update.max = max; + break; + case AgValueType::TVOCRaw: + _tvoc_raw.update.max = max; + break; + case AgValueType::NOx: + _nox.update.max = max; + break; + case AgValueType::NOxRaw: + _nox_raw.update.max = max; + break; + case AgValueType::PM25: + _pm_25.update.max = max; + break; + case AgValueType::PM01: + _pm_01.update.max = max; + break; + case AgValueType::PM10: + _pm_10.update.max = max; + break; + case AgValueType::PM03_PC: + _pm_03_pc.update.max = max; + break; + }; +} bool Measurements::updateValue(AgValueType type, int val) { // Define data point source @@ -44,7 +80,7 @@ bool Measurements::updateValue(AgValueType type, int val) { temporary = &_pm_10; invalidValue = utils::getInvalidPmValue(); break; - case AgValueType::PM03: + case AgValueType::PM03_PC: temporary = &_pm_03_pc; invalidValue = utils::getInvalidPmValue(); break; @@ -568,7 +604,7 @@ String Measurements::agValueTypeStr(AgValueType type) { case AgValueType::PM10: str = "PM10"; break; - case AgValueType::PM03: + case AgValueType::PM03_PC: str = "PM03"; break; default: diff --git a/src/AgValue.h b/src/AgValue.h index 56fc38a..225c041 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -11,7 +11,7 @@ private: struct Update { int counter; // How many update attempts done int success; // How many update value that actually give valid value - int max; // Maximum update before calculating average + int max; // Maximum update counter before calculating average }; // Reading type for sensor value that outputs float @@ -68,15 +68,21 @@ public: PM25, PM01, PM10, - PM03, + PM03_PC, }; - void init(); + /** + * @brief Set each AgValueType maximum update for a value type before calculate the average + * + * @param type the target value type to set + * @param max the maximum counter + */ + void maxUpdate(AgValueType type, int max); /** * @brief update target type value with new value. * Each AgValueType has last raw value and last average that are calculated based on max number of - * set This function is for AgValueType that use INT as the data type + * update. This function is for AgValueType that use INT as the data type * * @param type (AgValueType) value type that will be updated * @param val (int) the new value @@ -88,7 +94,7 @@ public: /** * @brief update target type value with new value. * Each AgValueType has last raw value and last average that are calculated based on max number of - * set This function is for AgValueType that use FLOAT as the data type + * update. This function is for AgValueType that use FLOAT as the data type * * @param type (AgValueType) value type that will be updated * @param val (float) the new value From 77a23b4202bc5458ed51f80a5957be491d176631 Mon Sep 17 00:00:00 2001 From: Samuel Siburian Date: Sun, 13 Oct 2024 15:46:41 +0700 Subject: [PATCH 12/69] Always increment bootcount when send measurements data is scheduled (#255) --- 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, 12 insertions(+), 4 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index b3be046..5d50c4e 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -534,6 +534,9 @@ static void updatePm(void) { } static void sendDataToServer(void) { + /** Increment bootcount when send measurements data is scheduled */ + measurements.bootCount++; + /** Ignore send data to server if postToAirGradient disabled */ if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) { @@ -548,7 +551,6 @@ static void sendDataToServer(void) { "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } static void tempHumUpdate(void) { diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index b67e262..19659ea 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -586,6 +586,9 @@ static void updatePm(void) { } static void sendDataToServer(void) { + /** Increment bootcount when send measurements data is scheduled */ + measurements.bootCount++; + /** Ignore send data to server if postToAirGradient disabled */ if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) { @@ -600,7 +603,6 @@ static void sendDataToServer(void) { "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } static void tempHumUpdate(void) { diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index bac20a5..7d203ae 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -627,6 +627,9 @@ static void updatePm(void) { } static void sendDataToServer(void) { + /** Increment bootcount when send measurements data is scheduled */ + measurements.bootCount++; + /** Ignore send data to server if postToAirGradient disabled */ if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) { @@ -641,7 +644,6 @@ static void sendDataToServer(void) { "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } static void tempHumUpdate(void) { diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index f4bf37b..bf2cbd1 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1220,6 +1220,9 @@ static void updatePm(void) { } static void sendDataToServer(void) { + /** Increment bootcount when send measurements data is scheduled */ + measurements.bootCount++; + /** Ignore send data to server if postToAirGradient disabled */ if (configuration.isPostDataToAirGradient() == false || configuration.isOfflineMode()) { return; @@ -1233,7 +1236,6 @@ static void sendDataToServer(void) { "Online mode and isPostToAirGradient = true: watchdog reset"); Serial.println(); } - measurements.bootCount++; } static void tempHumUpdate(void) { From 6925b1ac9ae65c79bcb19254becd8774c02555f3 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 13 Oct 2024 23:30:03 +0700 Subject: [PATCH 13/69] Provide channel for neccessary ValueType To support OA that have 2 PMS sensor --- src/AgValue.cpp | 80 ++++++++++++++++++++++++++++++++++++------------- src/AgValue.h | 21 ++++++++----- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0190852..eae3166 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -8,10 +8,12 @@ void Measurements::maxUpdate(AgValueType type, int max) { switch (type) { case AgValueType::Temperature: - _temperature.update.max = max; + _temperature[0].update.max = max; + _temperature[1].update.max = max; break; case AgValueType::Humidity: - _humidity.update.max = max; + _humidity[0].update.max = max; + _humidity[1].update.max = max; break; case AgValueType::CO2: _co2.update.max = max; @@ -29,21 +31,35 @@ void Measurements::maxUpdate(AgValueType type, int max) { _nox_raw.update.max = max; break; case AgValueType::PM25: - _pm_25.update.max = max; + _pm_25[0].update.max = max; + _pm_25[1].update.max = max; break; case AgValueType::PM01: - _pm_01.update.max = max; + _pm_01[0].update.max = max; + _pm_01[1].update.max = max; break; case AgValueType::PM10: - _pm_10.update.max = max; + _pm_10[0].update.max = max; + _pm_10[1].update.max = max; break; case AgValueType::PM03_PC: - _pm_03_pc.update.max = max; + _pm_03_pc[0].update.max = max; + _pm_03_pc[1].update.max = max; break; }; } -bool Measurements::updateValue(AgValueType type, int val) { +bool Measurements::updateValue(AgValueType type, int val, int ch) { + // Validate channel + if (ch != 1 || ch != 2) { + Serial.printf(" Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch); + // TODO: Perhaps just do assert + return false; + } + + // Follow array indexing just for get address of the value type + ch = ch - 1; + // Define data point source IntegerValue *temporary = nullptr; float invalidValue = 0; @@ -69,19 +85,19 @@ bool Measurements::updateValue(AgValueType type, int val) { invalidValue = utils::getInvalidNOx(); break; case AgValueType::PM25: - temporary = &_pm_25; + temporary = &_pm_25[ch]; invalidValue = utils::getInvalidPmValue(); break; case AgValueType::PM01: - temporary = &_pm_01; + temporary = &_pm_01[ch]; invalidValue = utils::getInvalidPmValue(); break; case AgValueType::PM10: - temporary = &_pm_10; + temporary = &_pm_10[ch]; invalidValue = utils::getInvalidPmValue(); break; case AgValueType::PM03_PC: - temporary = &_pm_03_pc; + temporary = &_pm_03_pc[ch]; invalidValue = utils::getInvalidPmValue(); break; default: @@ -92,9 +108,12 @@ bool Measurements::updateValue(AgValueType type, int val) { if (temporary == nullptr) { Serial.printf("%s is not defined for integer data type\n", agValueTypeStr(type)); // TODO: Just assert? - return; + return false; } + // Restore channel value for debugging purpose + ch = ch + 1; + // Update new value when value provided is not the invalid one if (val != invalidValue) { temporary->lastValue = val; @@ -107,14 +126,17 @@ bool Measurements::updateValue(AgValueType type, int val) { // Calculate value average when maximum set is reached if (temporary->update.counter >= temporary->update.max) { + // TODO: Need to check if SUCCESS == 0, what should we do? // Calculate the average temporary->avg = temporary->sumValues / temporary->update.success; + Serial.printf("%s{%d} count reached! Average value %d\n", agValueTypeStr(type), ch, + temporary->avg); - // This is just for notifying + // Notify if there's are invalid value when updating int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s update.ng miss %d out of %d update\n", agValueTypeStr(type), miss, - temporary->update.max); + Serial.printf("%s{%d} has %d invalid value out of %d update\n", agValueTypeStr(type), ch, + miss, temporary->update.max); } // Resets average related variable calculation @@ -127,17 +149,27 @@ bool Measurements::updateValue(AgValueType type, int val) { return false; } -bool Measurements::updateValue(AgValueType type, float val) { +bool Measurements::updateValue(AgValueType type, float val, int ch) { + // Validate channel + if (ch != 1 || ch != 2) { + Serial.printf(" Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch); + // TODO: Perhaps just do assert + return false; + } + + // Follow array indexing just for get address of the value type + ch = ch - 1; + // Define data point source FloatValue *temporary = nullptr; float invalidValue = 0; switch (type) { case AgValueType::Temperature: - temporary = &_temperature; + temporary = &_temperature[ch]; invalidValue = utils::getInvalidTemperature(); break; case AgValueType::Humidity: - temporary = &_humidity; + temporary = &_humidity[ch]; invalidValue = utils::getInvalidHumidity(); break; default: @@ -148,9 +180,12 @@ bool Measurements::updateValue(AgValueType type, float val) { if (temporary == nullptr) { Serial.printf("%s is not defined for float data type\n", agValueTypeStr(type)); // TODO: Just assert? - return; + return false; } + // Restore channel value for debugging purpose + ch = ch + 1; + // Update new value when value provided is not the invalid one if (val != invalidValue) { temporary->lastValue = val; @@ -163,14 +198,17 @@ bool Measurements::updateValue(AgValueType type, float val) { // Calculate value average when maximum set is reached if (temporary->update.counter >= temporary->update.max) { + // TODO: Need to check if SUCCESS == 0 // Calculate the average temporary->avg = temporary->sumValues / temporary->update.success; + Serial.printf("%s{%d} count reached! Average value %0.2f\n", agValueTypeStr(type), + temporary->avg); // This is just for notifying int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s update.ng miss %d out of %d update\n", agValueTypeStr(type), miss, - temporary->update.max); + Serial.printf("%s{%d} has %d invalid value out of %d update\n", agValueTypeStr(type), ch, + miss, temporary->update.max); } // Resets average related variable calculation diff --git a/src/AgValue.h b/src/AgValue.h index 225c041..247497d 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -86,10 +86,12 @@ public: * * @param type (AgValueType) value type that will be updated * @param val (int) the new value + * @param ch (int) the AgValueType channel, not every AgValueType has more than 1 channel. + * Currently maximum channel is 2. Default: 1 (channel 1) * @return true if update counter reached and new average value is calculated * @return false otherwise */ - bool updateValue(AgValueType type, int val); + bool updateValue(AgValueType type, int val, int ch = 1); /** * @brief update target type value with new value. @@ -98,10 +100,12 @@ public: * * @param type (AgValueType) value type that will be updated * @param val (float) the new value + * @param ch (int) the AgValueType channel, not every AgValueType has more than 1 channel. + * Currently maximum channel is 2. Default: 1 (channel 1) * @return true if update counter reached and new average value is calculated * @return false otherwise */ - bool updateValue(AgValueType type, float val); + bool updateValue(AgValueType type, float val, int ch = 1); float Temperature; int Humidity; @@ -144,17 +148,18 @@ public: String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void *_ag, void *_config); private: - FloatValue _temperature; - FloatValue _humidity; + // Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T + FloatValue _temperature[2]; + FloatValue _humidity[2]; IntegerValue _co2; IntegerValue _tvoc; IntegerValue _tvoc_raw; IntegerValue _nox; IntegerValue _nox_raw; - IntegerValue _pm_25; - IntegerValue _pm_01; - IntegerValue _pm_10; - IntegerValue _pm_03_pc; // particle count 0.3 + IntegerValue _pm_25[2]; + IntegerValue _pm_01[2]; + IntegerValue _pm_10[2]; + IntegerValue _pm_03_pc[2]; // particle count 0.3 /** * @brief Get PMS5003 firmware version string From 751d4e83809f12c793a4aff15ed0635b330fff84 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 14 Oct 2024 01:22:44 +0700 Subject: [PATCH 14/69] Get value from each data type --- src/AgValue.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++++++-- src/AgValue.h | 20 ++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index eae3166..17ee6fa 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -221,8 +221,87 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { return false; } -String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, - void *_ag, void *_config) { +int Measurements::getValue(AgValueType type, bool average, int ch) { + // Follow array indexing just for get address of the value type + ch = ch - 1; + + // Define data point source + IntegerValue *temporary = nullptr; + float invalidValue = 0; + switch (type) { + case AgValueType::CO2: + temporary = &_co2; + break; + case AgValueType::TVOC: + temporary = &_tvoc; + break; + case AgValueType::TVOCRaw: + temporary = &_tvoc_raw; + break; + case AgValueType::NOx: + temporary = &_nox; + break; + case AgValueType::NOxRaw: + temporary = &_nox_raw; + break; + case AgValueType::PM25: + temporary = &_pm_25[ch]; + break; + case AgValueType::PM01: + temporary = &_pm_01[ch]; + break; + case AgValueType::PM10: + temporary = &_pm_10[ch]; + break; + case AgValueType::PM03_PC: + temporary = &_pm_03_pc[ch]; + break; + default: + break; + }; + + // Sanity check if agvaluetype is defined for integer data type or not + if (temporary == nullptr) { + Serial.printf("%s is not defined for integer data type\n", agValueTypeStr(type)); + // TODO: Just assert? + return false; + } + + if (average) { + return temporary->avg; + } + + return temporary->lastValue; +} + +float Measurements::getValueFloat(AgValueType type, bool average, int ch) { + // Define data point source + FloatValue *temporary = nullptr; + switch (type) { + case AgValueType::Temperature: + temporary = &_temperature[ch]; + break; + case AgValueType::Humidity: + temporary = &_humidity[ch]; + break; + default: + break; + } + + // Sanity check if agvaluetype is defined for float data type or not + if (temporary == nullptr) { + Serial.printf("%s is not defined for float data type\n", agValueTypeStr(type)); + // TODO: Just assert? + return false; + } + + if (average) { + return temporary->avg; + } + + return temporary->lastValue; +} + AirGradient *ag = (AirGradient *)_ag; Configuration *config = (Configuration *)_config; diff --git a/src/AgValue.h b/src/AgValue.h index 247497d..54a35fd 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -107,6 +107,26 @@ public: */ bool updateValue(AgValueType type, float val, int ch = 1); + /** + * @brief Get the target measurement type value + * + * @param type measurement type that will be retrieve + * @param average true if expect last average value, false if expect last update value + * @param ch target type value channel + * @return int measurement type value + */ + int getValue(AgValueType type, bool average = true, int ch = 1); + + /** + * @brief Get the target measurement type value + * + * @param type measurement type that will be retrieve + * @param average true if expect last average value, false if expect last update value + * @param ch target type value channel + * @return float measurement type value + */ + float getValueFloat(AgValueType type, bool average = true, int ch = 1); + float Temperature; int Humidity; int CO2; From ac838efdb584391f31eeaf129b21d5817db09159 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 14 Oct 2024 01:25:35 +0700 Subject: [PATCH 15/69] validate measurement type channel --- src/AgValue.cpp | 22 ++++++++++------------ src/AgValue.h | 6 ++++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 17ee6fa..541edc1 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -50,12 +50,8 @@ void Measurements::maxUpdate(AgValueType type, int max) { } bool Measurements::updateValue(AgValueType type, int val, int ch) { - // Validate channel - if (ch != 1 || ch != 2) { - Serial.printf(" Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch); - // TODO: Perhaps just do assert - return false; - } + // Sanity check to validate channel, assert if invalid + validateChannel(ch); // Follow array indexing just for get address of the value type ch = ch - 1; @@ -150,12 +146,8 @@ bool Measurements::updateValue(AgValueType type, int val, int ch) { } bool Measurements::updateValue(AgValueType type, float val, int ch) { - // Validate channel - if (ch != 1 || ch != 2) { - Serial.printf(" Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch); - // TODO: Perhaps just do assert - return false; - } + // Sanity check to validate channel, assert if invalid + validateChannel(ch); // Follow array indexing just for get address of the value type ch = ch - 1; @@ -222,6 +214,9 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { } int Measurements::getValue(AgValueType type, bool average, int ch) { + // Sanity check to validate channel, assert if invalid + validateChannel(ch); + // Follow array indexing just for get address of the value type ch = ch - 1; @@ -275,6 +270,9 @@ int Measurements::getValue(AgValueType type, bool average, int ch) { } float Measurements::getValueFloat(AgValueType type, bool average, int ch) { + // Sanity check to validate channel, assert if invalid + validateChannel(ch); + // Define data point source FloatValue *temporary = nullptr; switch (type) { diff --git a/src/AgValue.h b/src/AgValue.h index 54a35fd..8244653 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -208,6 +208,12 @@ private: * Convert AgValue Type to string representation of the value */ String agValueTypeStr(AgValueType type); + + /** + * @brief check if provided channel is a valid channel or not + * abort program if invalid + */ + void validateChannel(int ch); }; #endif /** _AG_VALUE_H_ */ From a2c19438c0e82a61ef7468b916583173f587c928 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 14 Oct 2024 01:54:55 +0700 Subject: [PATCH 16/69] validateChannel implementation --- src/AgValue.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 541edc1..38c29d1 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -727,4 +727,12 @@ String Measurements::agValueTypeStr(AgValueType type) { }; return str; +} + +void Measurements::validateChannel(int ch) { + if (ch != 1 && ch != 2) { + Serial.printf("ERROR! Channel %d is undefined. Only channel 1 or 2 is the optional value!", ch); + delay(1000); + assert(0); + } } \ No newline at end of file From e145d327142567500bada11601cc04ba226901d0 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 14 Oct 2024 02:05:30 +0700 Subject: [PATCH 17/69] First test, console Working average from main only --- examples/OneOpenAir/OneOpenAir.ino | 410 ++++++++++++----------------- src/AgValue.cpp | 2 +- 2 files changed, 167 insertions(+), 245 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index f4bf37b..da6ad61 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -261,6 +261,19 @@ void setup() { // Update display and led bar after finishing setup to show dashboard updateDisplayAndLedBar(); + + // NOTE: This is just a temporary, will do a proper set maximum value based on schedule interval + measurements.maxUpdate(Measurements::AgValueType::Temperature, 26); + measurements.maxUpdate(Measurements::AgValueType::Humidity, 26); + measurements.maxUpdate(Measurements::AgValueType::CO2, 13); + measurements.maxUpdate(Measurements::AgValueType::TVOC, 53); + measurements.maxUpdate(Measurements::AgValueType::TVOCRaw, 53); + measurements.maxUpdate(Measurements::AgValueType::NOx, 53); + measurements.maxUpdate(Measurements::AgValueType::NOxRaw, 53); + measurements.maxUpdate(Measurements::AgValueType::PM25, 26); + measurements.maxUpdate(Measurements::AgValueType::PM01, 26); + measurements.maxUpdate(Measurements::AgValueType::PM10, 26); + measurements.maxUpdate(Measurements::AgValueType::PM03_PC, 26); } void loop() { @@ -317,17 +330,17 @@ void loop() { } static void co2Update(void) { + if (!configuration.hasSensorS8) { + // Device don't have SHT sensor + return; + } + int value = ag->s8.getCo2(); if (utils::isValidCO2(value)) { - measurements.CO2 = value; - getCO2FailCount = 0; - Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); + measurements.updateValue(Measurements::AgValueType::CO2, value); + // Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); } else { - getCO2FailCount++; - Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount); - if (getCO2FailCount >= 3) { - measurements.CO2 = utils::getInvalidCO2(); - } + measurements.updateValue(Measurements::AgValueType::CO2, utils::getInvalidCO2()); } } @@ -982,240 +995,148 @@ static void updateDisplayAndLedBar(void) { } static void updateTvoc(void) { - measurements.TVOC = ag->sgp41.getTvocIndex(); - measurements.TVOCRaw = ag->sgp41.getTvocRaw(); - measurements.NOx = ag->sgp41.getNoxIndex(); - measurements.NOxRaw = ag->sgp41.getNoxRaw(); + if (!configuration.hasSensorSGP) { + return; + } - Serial.println(); - Serial.printf("TVOC index: %d\r\n", measurements.TVOC); - Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); - Serial.printf("NOx index: %d\r\n", measurements.NOx); - Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); + measurements.updateValue(Measurements::AgValueType::TVOC, ag->sgp41.getTvocIndex()); + measurements.updateValue(Measurements::AgValueType::TVOCRaw, ag->sgp41.getTvocRaw()); + measurements.updateValue(Measurements::AgValueType::NOx, ag->sgp41.getNoxIndex()); + measurements.updateValue(Measurements::AgValueType::NOxRaw, ag->sgp41.getNoxRaw()); + + // Serial.println(); + // Serial.printf("TVOC index: %d\r\n", measurements.TVOC); + // Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); + // Serial.printf("NOx index: %d\r\n", measurements.NOx); + // Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); +} + +static void updatePMS5003() { + if (ag->pms5003.connected()) { + measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003.getPm01Ae()); + measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003.getPm25Ae()); + measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003.getPm10Ae()); + measurements.updateValue(Measurements::AgValueType::PM03_PC, + ag->pms5003.getPm03ParticleCount()); + + // Serial.println(); + // Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); + // 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()); + } else { + measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue()); + measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue()); + measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue()); + measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue()); + } } static void updatePm(void) { - bool restart = false; if (ag->isOne()) { - if (ag->pms5003.connected()) { - measurements.pm01_1 = ag->pms5003.getPm01Ae(); - measurements.pm25_1 = ag->pms5003.getPm25Ae(); - measurements.pm10_1 = ag->pms5003.getPm10Ae(); - measurements.pm03PCount_1 = ag->pms5003.getPm03ParticleCount(); + updatePMS5003(); + return; + } - Serial.println(); - Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); - 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(); + // Open Air Monitor series, can have two PMS5003T sensor + bool newPMS1Value = false; + bool newPMS2Value = false; + + // Read PMS channel 1 if available + int channel = 1; + if (configuration.hasSensorPMS1) { + if (ag->pms5003t_1.connected()) { + measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003t_1.getPm01Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003t_1.getPm25Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003t_1.getPm10Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM03_PC, + ag->pms5003t_1.getPm03ParticleCount(), channel); + measurements.updateValue(Measurements::AgValueType::Temperature, + ag->pms5003t_1.getTemperature(), channel); + measurements.updateValue(Measurements::AgValueType::Humidity, + ag->pms5003t_1.getRelativeHumidity(), channel); + + // flag that new valid PMS value exists + newPMS2Value = true; } else { - ag->pms5003.updateFailCount(); - 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(); - measurements.pm10_1 = utils::getInvalidPmValue(); - measurements.pm03PCount_1 = utils::getInvalidPmValue(); - } - - if (ag->pms5003.getFailCount() >= ag->pms5003.getFailCountMax()) { - restart = true; - } - } - } else { - bool pmsResult_1 = false; - bool pmsResult_2 = false; - if (configuration.hasSensorPMS1 && ag->pms5003t_1.connected()) { - measurements.pm01_1 = ag->pms5003t_1.getPm01Ae(); - measurements.pm25_1 = ag->pms5003t_1.getPm25Ae(); - measurements.pm10_1 = ag->pms5003t_1.getPm10Ae(); - measurements.pm03PCount_1 = ag->pms5003t_1.getPm03ParticleCount(); - measurements.temp_1 = ag->pms5003t_1.getTemperature(); - measurements.hum_1 = ag->pms5003t_1.getRelativeHumidity(); - - pmsResult_1 = true; - - Serial.println(); - Serial.printf("[1] PM1 ug/m3: %d\r\n", measurements.pm01_1); - Serial.printf("[1] PM2.5 ug/m3: %d\r\n", measurements.pm25_1); - Serial.printf("[1] PM10 ug/m3: %d\r\n", measurements.pm10_1); - Serial.printf("[1] PM3.0 Count: %d\r\n", measurements.pm03PCount_1); - Serial.printf("[1] Temperature in C: %0.2f\r\n", measurements.temp_1); - Serial.printf("[1] Relative Humidity: %d\r\n", measurements.hum_1); - Serial.printf("[1] Temperature compensated in C: %0.2f\r\n", - 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 { - if (configuration.hasSensorPMS1) { - ag->pms5003t_1.updateFailCount(); - Serial.printf("[1] PMS read failed %d times\r\n", ag->pms5003t_1.getFailCount()); - - if (ag->pms5003t_1.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) { - measurements.pm01_1 = utils::getInvalidPmValue(); - measurements.pm25_1 = utils::getInvalidPmValue(); - measurements.pm10_1 = utils::getInvalidPmValue(); - measurements.pm03PCount_1 = utils::getInvalidPmValue(); - measurements.temp_1 = utils::getInvalidTemperature(); - measurements.hum_1 = utils::getInvalidHumidity(); - } - - if (ag->pms5003t_1.getFailCount() >= ag->pms5003t_1.getFailCountMax()) { - restart = true; - } - } - } - - if (configuration.hasSensorPMS2 && ag->pms5003t_2.connected()) { - measurements.pm01_2 = ag->pms5003t_2.getPm01Ae(); - measurements.pm25_2 = ag->pms5003t_2.getPm25Ae(); - measurements.pm10_2 = ag->pms5003t_2.getPm10Ae(); - measurements.pm03PCount_2 = ag->pms5003t_2.getPm03ParticleCount(); - measurements.temp_2 = ag->pms5003t_2.getTemperature(); - measurements.hum_2 = ag->pms5003t_2.getRelativeHumidity(); - - pmsResult_2 = true; - - Serial.println(); - Serial.printf("[2] PM1 ug/m3: %d\r\n", measurements.pm01_2); - Serial.printf("[2] PM2.5 ug/m3: %d\r\n", measurements.pm25_2); - Serial.printf("[2] PM10 ug/m3: %d\r\n", measurements.pm10_2); - Serial.printf("[2] PM3.0 Count: %d\r\n", measurements.pm03PCount_2); - Serial.printf("[2] Temperature in C: %0.2f\r\n", measurements.temp_2); - Serial.printf("[2] Relative Humidity: %d\r\n", measurements.hum_2); - Serial.printf("[2] Temperature compensated in C: %0.2f\r\n", - 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 { - if (configuration.hasSensorPMS2) { - ag->pms5003t_2.updateFailCount(); - Serial.printf("[2] PMS read failed %d times\r\n", ag->pms5003t_2.getFailCount()); - - if (ag->pms5003t_2.getFailCount() >= PMS_FAIL_COUNT_SET_INVALID) { - measurements.pm01_2 = utils::getInvalidPmValue(); - measurements.pm25_2 = utils::getInvalidPmValue(); - measurements.pm10_2 = utils::getInvalidPmValue(); - measurements.pm03PCount_2 = utils::getInvalidPmValue(); - measurements.temp_2 = utils::getInvalidTemperature(); - measurements.hum_2 = utils::getInvalidHumidity(); - } - - if (ag->pms5003t_2.getFailCount() >= ag->pms5003t_2.getFailCountMax()) { - restart = true; - } - } - } - - if (configuration.hasSensorPMS1 && configuration.hasSensorPMS2 && - pmsResult_1 && pmsResult_2) { - /** Get total of PMS1*/ - measurements.pm1Value01 = measurements.pm1Value01 + measurements.pm01_1; - measurements.pm1Value25 = measurements.pm1Value25 + measurements.pm25_1; - measurements.pm1Value10 = measurements.pm1Value10 + measurements.pm10_1; - measurements.pm1PCount = - measurements.pm1PCount + measurements.pm03PCount_1; - measurements.pm1temp = measurements.pm1temp + measurements.temp_1; - measurements.pm1hum = measurements.pm1hum + measurements.hum_1; - - /** Get total of PMS2 */ - measurements.pm2Value01 = measurements.pm2Value01 + measurements.pm01_2; - measurements.pm2Value25 = measurements.pm2Value25 + measurements.pm25_2; - measurements.pm2Value10 = measurements.pm2Value10 + measurements.pm10_2; - measurements.pm2PCount = - measurements.pm2PCount + measurements.pm03PCount_2; - measurements.pm2temp = measurements.pm2temp + measurements.temp_2; - measurements.pm2hum = measurements.pm2hum + measurements.hum_2; - - measurements.countPosition++; - - /** Get average */ - if (measurements.countPosition == measurements.targetCount) { - measurements.pm01_1 = - measurements.pm1Value01 / measurements.targetCount; - measurements.pm25_1 = - measurements.pm1Value25 / measurements.targetCount; - measurements.pm10_1 = - measurements.pm1Value10 / measurements.targetCount; - measurements.pm03PCount_1 = - measurements.pm1PCount / measurements.targetCount; - measurements.temp_1 = measurements.pm1temp / measurements.targetCount; - measurements.hum_1 = measurements.pm1hum / measurements.targetCount; - - measurements.pm01_2 = - measurements.pm2Value01 / measurements.targetCount; - measurements.pm25_2 = - measurements.pm2Value25 / measurements.targetCount; - measurements.pm10_2 = - measurements.pm2Value10 / measurements.targetCount; - measurements.pm03PCount_2 = - measurements.pm2PCount / measurements.targetCount; - measurements.temp_2 = measurements.pm2temp / measurements.targetCount; - measurements.hum_2 = measurements.pm2hum / measurements.targetCount; - - measurements.countPosition = 0; - - measurements.pm1Value01 = 0; - measurements.pm1Value25 = 0; - measurements.pm1Value10 = 0; - measurements.pm1PCount = 0; - measurements.pm1temp = 0; - measurements.pm1hum = 0; - measurements.pm2Value01 = 0; - measurements.pm2Value25 = 0; - measurements.pm2Value10 = 0; - measurements.pm2PCount = 0; - measurements.pm2temp = 0; - measurements.pm2hum = 0; - } - } - - if (pmsResult_1 && pmsResult_2) { - measurements.Temperature = - (measurements.temp_1 + measurements.temp_2) / 2; - measurements.Humidity = (measurements.hum_1 + measurements.hum_2) / 2; - } else { - if (pmsResult_1) { - measurements.Temperature = measurements.temp_1; - measurements.Humidity = measurements.hum_1; - } - if (pmsResult_2) { - measurements.Temperature = measurements.temp_2; - measurements.Humidity = measurements.hum_2; - } - } - - if (configuration.hasSensorSGP) { - float temp; - float hum; - if (pmsResult_1 && pmsResult_2) { - temp = (measurements.temp_1 + measurements.temp_2) / 2.0f; - hum = (measurements.hum_1 + measurements.hum_2) / 2.0f; - } else { - if (pmsResult_1) { - temp = measurements.temp_1; - hum = measurements.hum_1; - } - if (pmsResult_2) { - temp = measurements.temp_2; - hum = measurements.hum_2; - } - } - ag->sgp41.setCompensationTemperatureHumidity(temp, hum); + // PMS channel 1 now is not connected, update using invalid value + measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::Temperature, + utils::getInvalidTemperature(), channel); + measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity(), + channel); } } - if (restart) { - Serial.printf("PMS failure count reach to max set %d, restarting...", ag->pms5003.getFailCountMax()); - ESP.restart(); + // Read PMS channel 2 if available + channel = 2; + if (configuration.hasSensorPMS2) { + if (ag->pms5003t_2.connected()) { + measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003t_2.getPm01Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003t_2.getPm25Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003t_2.getPm10Ae(), + channel); + measurements.updateValue(Measurements::AgValueType::PM03_PC, + ag->pms5003t_2.getPm03ParticleCount(), channel); + measurements.updateValue(Measurements::AgValueType::Temperature, + ag->pms5003t_2.getTemperature(), channel); + measurements.updateValue(Measurements::AgValueType::Humidity, + ag->pms5003t_2.getRelativeHumidity(), channel); + + // flag that new valid PMS value exists + newPMS2Value = true; + } else { + // PMS channel channel now is not connected, update using invalid value + measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue(), + channel); + measurements.updateValue(Measurements::AgValueType::Temperature, + utils::getInvalidTemperature(), channel); + measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity(), + channel); + } + } + + if (configuration.hasSensorSGP) { + float temp, hum; + if (newPMS1Value && newPMS2Value) { + // Both PMS has new valid value + temp = (measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 1) + + measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 2)) / + 2.0f; + hum = (measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 1) + + measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 2)) / + 2.0f; + } else if (newPMS1Value) { + // Only PMS1 has new valid value + temp = measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 1); + hum = measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 1); + } else { + // Only PMS2 has new valid value + temp = measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 2); + hum = measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 2); + } + + // Update compensation temperature and humidity for SGP41 + ag->sgp41.setCompensationTemperatureHumidity(temp, hum); } } @@ -1239,24 +1160,25 @@ static void sendDataToServer(void) { static void tempHumUpdate(void) { delay(100); if (ag->sht.measure()) { - measurements.Temperature = ag->sht.getTemperature(); - measurements.Humidity = ag->sht.getRelativeHumidity(); + float temp = ag->sht.getTemperature(); + float rhum = ag->sht.getRelativeHumidity(); - Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature); - Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity); - Serial.printf("Temperature compensated in C: %0.2f\r\n", - measurements.Temperature); - Serial.printf("Relative Humidity compensated: %d\r\n", - measurements.Humidity); + measurements.updateValue(Measurements::AgValueType::Temperature, temp); + measurements.updateValue(Measurements::AgValueType::Humidity, rhum); + + // Serial.printf("Temperature in C: %0.2f\n", temp); + // Serial.printf("Relative Humidity: %d\n", rhum); + // Serial.printf("Temperature compensated in C: %0.2f\n", temp); + // Serial.printf("Relative Humidity compensated: %0.2f\n", rhum); // Update compensation temperature and humidity for SGP41 if (configuration.hasSensorSGP) { - ag->sgp41.setCompensationTemperatureHumidity(measurements.Temperature, - measurements.Humidity); + ag->sgp41.setCompensationTemperatureHumidity(temp, rhum); } } else { - measurements.Temperature = utils::getInvalidTemperature(); - measurements.Humidity = utils::getInvalidHumidity(); + measurements.updateValue(Measurements::AgValueType::Temperature, + utils::getInvalidTemperature()); + measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); } } \ No newline at end of file diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 38c29d1..02f453f 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -193,7 +193,7 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { // TODO: Need to check if SUCCESS == 0 // Calculate the average temporary->avg = temporary->sumValues / temporary->update.success; - Serial.printf("%s{%d} count reached! Average value %0.2f\n", agValueTypeStr(type), + Serial.printf("%s{%d} count reached! Average value %0.2f\n", agValueTypeStr(type), ch, temporary->avg); // This is just for notifying From 2082a2fa932906360184cba8e8e8c46cb9e369bd Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 14 Oct 2024 02:14:10 +0700 Subject: [PATCH 18/69] Fix missing line when commit --- src/AgValue.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 02f453f..88c5ab7 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -300,6 +300,8 @@ float Measurements::getValueFloat(AgValueType type, bool average, int ch) { return temporary->lastValue; } +String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, void *_ag, + void *_config) { AirGradient *ag = (AirGradient *)_ag; Configuration *config = (Configuration *)_config; From ed344d3e1a4f03627150bce22f19a4f035139a83 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 17 Oct 2024 00:53:49 +0700 Subject: [PATCH 19/69] measurement toString Tested on I-9PSL monitor Update OneOpenAir post and mqtt to use new measurement toString --- examples/OneOpenAir/LocalServer.cpp | 5 +- examples/OneOpenAir/OneOpenAir.ino | 7 +- src/AgValue.cpp | 368 ++++++++++++++++++++++++++++ src/AgValue.h | 10 + 4 files changed, 383 insertions(+), 7 deletions(-) diff --git a/examples/OneOpenAir/LocalServer.cpp b/examples/OneOpenAir/LocalServer.cpp index 1b88e8e..b9989a1 100644 --- a/examples/OneOpenAir/LocalServer.cpp +++ b/examples/OneOpenAir/LocalServer.cpp @@ -64,9 +64,8 @@ void LocalServer::_GET_metrics(void) { } void LocalServer::_GET_measure(void) { - server.send( - 200, "application/json", - measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config)); + String toSend = measure.toStringX(true, fwMode, wifiConnector.RSSI(), *ag, config); + server.send(200, "application/json", toSend); } void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; } diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index da6ad61..c9e014f 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -373,8 +373,8 @@ static void createMqttTask(void) { /** Send data */ if (mqttClient.isConnected()) { - String payload = measurements.toString( - true, fwMode, wifiConnector.RSSI(), ag, &configuration); + String payload = + measurements.toStringX(true, fwMode, wifiConnector.RSSI(), *ag, configuration); String topic = "airgradient/readings/" + ag->deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), @@ -1146,8 +1146,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), - ag, &configuration); + String syncData = measurements.toStringX(false, fwMode, wifiConnector.RSSI(), *ag, configuration); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 88c5ab7..0f778d6 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -737,4 +737,372 @@ void Measurements::validateChannel(int ch) { delay(1000); assert(0); } +} + +String Measurements::toStringX(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, + Configuration &config) { + JSONVar root; + + if (ag.isOne() || (ag.isPro4_2()) || ag.isPro3_3() || ag.isBasic()) { + root = buildIndoor(localServer, ag, config); + } else { + root = buildOutdoor(localServer, fwMode, ag, config); + } + + // CO2 + if (config.hasSensorS8 && utils::isValidCO2(_co2.avg)) { + root["rco2"] = _co2.avg; + } + + /// TVOx and NOx + if (config.hasSensorSGP) { + if (utils::isValidVOC(_tvoc.avg)) { + root["tvocIndex"] = _tvoc.avg; + } + if (utils::isValidVOC(_tvoc_raw.avg)) { + root["tvocRaw"] = _tvoc_raw.avg; + } + if (utils::isValidNOx(_nox.avg)) { + root["noxIndex"] = _nox.avg; + } + if (utils::isValidNOx(_nox_raw.avg)) { + root["noxRaw"] = _nox_raw.avg; + } + } + + root["boot"] = bootCount; + root["bootCount"] = bootCount; + root["wifi"] = rssi; + + if (localServer) { + if (ag.isOne()) { + root["ledMode"] = config.getLedBarModeName(); + } + root["serialno"] = ag.deviceId(); + root["firmware"] = ag.getVersion(); + root["model"] = AgFirmwareModeName(fwMode); + } + + String result = JSON.stringify(root); + Serial.printf("\n----\n %s \n-----\n", result.c_str()); + return result; +} + +JSONVar Measurements::buildOutdoor(bool localServer, AgFirmwareMode fwMode, AirGradient &ag, + Configuration &config) { + JSONVar outdoor; + if (fwMode == FW_MODE_O_1P || fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) { + // buildPMS params: + /// Because only have 1 PMS, allCh is set to false + /// But enable temp hum from PMS + /// compensated values if requested by local server + /// Set ch based on hasSensorPMSx + if (config.hasSensorPMS1) { + outdoor = buildPMS(ag, 1, false, true, localServer); + if (!localServer) { + outdoor[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag.pms5003t_1.getFirmwareVersion()); + } + } else { + outdoor = buildPMS(ag, 2, false, true, localServer); + if (!localServer) { + outdoor[json_prop_pmFirmware] = pms5003TFirmwareVersion(ag.pms5003t_2.getFirmwareVersion()); + } + } + } else { + // FW_MODE_O_1PPT && FW_MODE_O_1PP: Outdoor monitor that have 2 PMS sensor + // buildPMS params: + /// 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); + // PMS5003T version + if (!localServer) { + outdoor["channels"]["1"][json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag.pms5003t_1.getFirmwareVersion()); + outdoor["channels"]["2"][json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag.pms5003t_2.getFirmwareVersion()); + } + } + + return outdoor; +} + +JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configuration &config) { + JSONVar indoor; + + if (config.hasSensorPMS1) { + // buildPMS params: + /// 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); + if (!localServer) { + // Indoor is using PMS5003 + indoor[json_prop_pmFirmware] = this->pms5003FirmwareVersion(ag.pms5003.getFirmwareVersion()); + } + } + + if (config.hasSensorSHT) { + // Add temperature + if (utils::isValidTemperature(_temperature[0].avg)) { + indoor["atmp"] = ag.round2(_temperature[0].avg); + if (localServer) { + indoor["atmpCompensated"] = ag.round2(_temperature[0].avg); + } + } + // Add humidity + if (utils::isValidHumidity(_humidity[0].avg)) { + indoor["rhum"] = _humidity[0].avg; + if (localServer) { + indoor["rhumCompensated"] = ag.round2(_humidity[0].avg); + } + } + } + + // Add pm25 compensated value only if PM2.5 and humidity value is valid + if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].avg)) { + if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].avg)) { + int pm25 = ag.pms5003.compensate(_pm_25[0].avg, _humidity[0].avg); + if (utils::isValidPm(pm25)) { + indoor["pm02Compensated"] = pm25; + } + } + } + + return indoor; +} + +JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTempHum, + bool compensate) { + JSONVar pms; + + // When only one of the channel + if (allCh == false) { + // Sanity check to validate channel, assert if invalid + validateChannel(ch); + + // Follow array indexing just for get address of the value type + ch = ch - 1; + + if (utils::isValidPm(_pm_01[ch].avg)) { + pms["pm01"] = _pm_01[ch].avg; + } + if (utils::isValidPm(_pm_25[ch].avg)) { + pms["pm02"] = _pm_25[ch].avg; + } + if (utils::isValidPm(_pm_10[ch].avg)) { + pms["pm10"] = _pm_10[ch].avg; + } + if (utils::isValidPm03Count(_pm_03_pc[ch].avg)) { + pms["pm003Count"] = _pm_03_pc[ch].avg; + } + + if (withTempHum) { + float _vc; + // Set temperature if valid + if (utils::isValidTemperature(_temperature[ch].avg)) { + pms["atmp"] = ag.round2(_temperature[ch].avg); + // Compensate temperature when flag is set + if (compensate) { + _vc = ag.pms5003t_1.compensateTemp(_temperature[ch].avg); + if (utils::isValidTemperature(_vc)) { + pms["atmpCompensated"] = ag.round2(_vc); + } + } + } + // Set humidity if valid + if (utils::isValidHumidity(_humidity[ch].avg)) { + pms["rhum"] = ag.round2(_humidity[ch].avg); + // Compensate relative humidity when flag is set + if (compensate) { + _vc = ag.pms5003t_1.compensateHum(_humidity[ch].avg); + if (utils::isValidTemperature(_vc)) { + pms["rhumCompensated"] = ag.round2(_vc); + } + } + } + + // Add pm25 compensated value only if PM2.5 and humidity value is valid + if (compensate) { + if (utils::isValidPm(_pm_25[ch].avg) && utils::isValidHumidity(_humidity[ch].avg)) { + // Note: the pms5003t object is not matter either for channel 1 or 2, compensate points to + // the same base function + int pm25 = ag.pms5003t_1.compensate(_pm_25[ch].avg, _humidity[ch].avg); + if (utils::isValidPm(pm25)) { + pms["pm02Compensated"] = pm25; + } + } + } + } + + // Directly return the json object + return pms; + }; + + // Handle both channel with average, if one of the channel not valid, use another one + /// PM01 + if (utils::isValidPm(_pm_01[0].avg) && utils::isValidPm(_pm_01[1].avg)) { + float avg = (_pm_01[0].avg + _pm_01[1].avg) / 2; + pms["pm01"] = ag.round2(avg); + pms["channels"]["1"]["pm01"] = _pm_01[0].avg; + pms["channels"]["2"]["pm01"] = _pm_01[1].avg; + } else if (utils::isValidPm(_pm_01[0].avg)) { + pms["pm01"] = _pm_01[0].avg; + pms["channels"]["1"]["pm01"] = _pm_01[0].avg; + } else if (utils::isValidPm(_pm_01[1].avg)) { + pms["pm01"] = _pm_01[1].avg; + pms["channels"]["2"]["pm01"] = _pm_01[1].avg; + } + + /// PM2.5 + if (utils::isValidPm(_pm_25[0].avg) && utils::isValidPm(_pm_25[1].avg)) { + float avg = (_pm_25[0].avg + _pm_25[1].avg) / 2.0f; + pms["pm02"] = ag.round2(avg); + pms["channels"]["1"]["pm02"] = _pm_25[0].avg; + pms["channels"]["2"]["pm02"] = _pm_25[1].avg; + } else if (utils::isValidPm(_pm_25[0].avg)) { + pms["pm02"] = _pm_25[0].avg; + pms["channels"]["1"]["pm02"] = _pm_25[0].avg; + } else if (utils::isValidPm(_pm_25[1].avg)) { + pms["pm02"] = _pm_25[1].avg; + pms["channels"]["2"]["pm02"] = _pm_25[1].avg; + } + + /// PM10 + if (utils::isValidPm(_pm_10[0].avg) && utils::isValidPm(_pm_10[1].avg)) { + float avg = (_pm_10[0].avg + _pm_10[1].avg) / 2.0f; + pms["pm10"] = ag.round2(avg); + pms["channels"]["1"]["pm10"] = _pm_10[0].avg; + pms["channels"]["2"]["pm10"] = _pm_10[1].avg; + } else if (utils::isValidPm(_pm_10[0].avg)) { + pms["pm10"] = _pm_10[0].avg; + pms["channels"]["1"]["pm10"] = _pm_10[0].avg; + } else if (utils::isValidPm(_pm_10[1].avg)) { + pms["pm10"] = _pm_10[1].avg; + pms["channels"]["2"]["pm10"] = _pm_10[1].avg; + } + + /// PM03 particle count + if (utils::isValidPm03Count(_pm_03_pc[0].avg) && utils::isValidPm03Count(_pm_03_pc[1].avg)) { + float avg = (_pm_03_pc[0].avg + _pm_03_pc[1].avg) / 2.0f; + pms["pm003Count"] = ag.round2(avg); + pms["channels"]["1"]["pm003Count"] = _pm_03_pc[0].avg; + pms["channels"]["2"]["pm003Count"] = _pm_03_pc[1].avg; + } else if (utils::isValidPm(_pm_03_pc[0].avg)) { + pms["pm003Count"] = _pm_03_pc[0].avg; + pms["channels"]["1"]["pm003Count"] = _pm_03_pc[0].avg; + } else if (utils::isValidPm(_pm_03_pc[1].avg)) { + pms["pm003Count"] = _pm_03_pc[1].avg; + pms["channels"]["2"]["pm003Count"] = _pm_03_pc[1].avg; + } + + if (withTempHum) { + /// Temperature + if (utils::isValidTemperature(_temperature[0].avg) && + utils::isValidTemperature(_temperature[1].avg)) { + + float temperature = (_temperature[0].avg + _temperature[1].avg) / 2.0f; + pms["atmp"] = ag.round2(temperature); + pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].avg); + pms["channels"]["2"]["atmp"] = ag.round2(_temperature[1].avg); + + if (compensate) { + // Compensate both temperature channel + float temp = ag.pms5003t_1.compensateTemp(temperature); + float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].avg); + float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].avg); + pms["atmpCompensated"] = ag.round2(temp); + pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); + pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); + } + + } else if (utils::isValidTemperature(_temperature[0].avg)) { + pms["atmp"] = ag.round2(_temperature[0].avg); + pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].avg); + + if (compensate) { + // Compensate channel 1 + float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].avg); + pms["atmpCompensated"] = ag.round2(temp1); + pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); + } + + } else if (utils::isValidTemperature(_temperature[1].avg)) { + pms["atmp"] = ag.round2(_temperature[1].avg); + pms["channels"]["2"]["atmp"] = ag.round2(_temperature[1].avg); + + if (compensate) { + // Compensate channel 2 + float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].avg); + pms["atmpCompensated"] = ag.round2(temp2); + pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); + } + } + + /// Relative humidity + if (utils::isValidHumidity(_humidity[0].avg) && utils::isValidHumidity(_humidity[1].avg)) { + float humidity = (_humidity[0].avg + _humidity[1].avg) / 2.0f; + pms["rhum"] = ag.round2(humidity); + pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].avg); + pms["channels"]["2"]["rhum"] = ag.round2(_humidity[1].avg); + + if (compensate) { + // Compensate both humidity channel + float hum = ag.pms5003t_1.compensateHum(humidity); + float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].avg); + float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].avg); + pms["rhumCompensated"] = ag.round2(hum); + pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); + pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); + } + + } else if (utils::isValidHumidity(_humidity[0].avg)) { + pms["rhum"] = ag.round2(_humidity[0].avg); + pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].avg); + + if (compensate) { + // Compensate humidity channel 1 + float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].avg); + pms["rhumCompensated"] = ag.round2(hum1); + pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); + } + + } else if (utils::isValidHumidity(_humidity[1].avg)) { + pms["rhum"] = ag.round2(_humidity[1].avg); + pms["channels"]["2"]["rhum"] = ag.round2(_humidity[1].avg); + + if (compensate) { + // Compensate humidity channel 2 + float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].avg); + pms["rhumCompensated"] = ag.round2(hum2); + pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); + } + } + + if (compensate) { + // Add pm25 compensated value + /// First get both channel compensated value + int pm25_comp1 = utils::getInvalidPmValue(); + int pm25_comp2 = utils::getInvalidPmValue(); + if (utils::isValidPm(_pm_25[0].avg) && utils::isValidHumidity(_humidity[0].avg)) { + pm25_comp1 = ag.pms5003t_1.compensate(_pm_25[0].avg, _humidity[0].avg); + pms["channels"]["1"]["pm02Compensated"] = pm25_comp1; + } + if (utils::isValidPm(_pm_25[1].avg) && utils::isValidHumidity(_humidity[1].avg)) { + pm25_comp2 = ag.pms5003t_2.compensate(_pm_25[1].avg, _humidity[1].avg); + pms["channels"]["2"]["pm02Compensated"] = 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["pm02Compensated"] = (int)((pm25_comp1 / pm25_comp2) / 2); + } else if (utils::isValidPm(pm25_comp1)) { + pms["pm02Compensated"] = pm25_comp1; + } else if (utils::isValidPm(pm25_comp2)) { + pms["pm02Compensated"] = pm25_comp2; + } + } + } + + return pms; } \ No newline at end of file diff --git a/src/AgValue.h b/src/AgValue.h index 8244653..752bf59 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -1,7 +1,10 @@ #ifndef _AG_VALUE_H_ #define _AG_VALUE_H_ +#include "AgConfigure.h" +#include "AirGradient.h" #include "App/AppDef.h" +#include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #include "Main/utils.h" #include @@ -166,6 +169,8 @@ public: int bootCount; String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void *_ag, void *_config); + String toStringX(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, + Configuration &config); private: // Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T @@ -214,6 +219,11 @@ private: * abort program if invalid */ 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); }; #endif /** _AG_VALUE_H_ */ From 76a2f332d7c0c62026669acc1bee38fffaf347f5 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 17 Oct 2024 12:08:00 +0700 Subject: [PATCH 20/69] Fix rhum precission on buildIndoor measurements --- src/AgValue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0f778d6..214b072 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -852,7 +852,7 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati } // Add humidity if (utils::isValidHumidity(_humidity[0].avg)) { - indoor["rhum"] = _humidity[0].avg; + indoor["rhum"] = ag.round2(_humidity[0].avg); if (localServer) { indoor["rhumCompensated"] = ag.round2(_humidity[0].avg); } From 0ccf46c2194a4b345cdbb201b34acf1e29053fca Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 19 Oct 2024 01:32:41 +0700 Subject: [PATCH 21/69] Rename AgValueType to MeasurementType Just use plain enum instead of enum class Remove unecessary legacy variables and function --- examples/OneOpenAir/OneOpenAir.ino | 130 +++----- src/AgValue.cpp | 508 ++++------------------------- src/AgValue.h | 105 ++---- 3 files changed, 143 insertions(+), 600 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index c9e014f..d50bf3f 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -337,10 +337,10 @@ static void co2Update(void) { int value = ag->s8.getCo2(); if (utils::isValidCO2(value)) { - measurements.updateValue(Measurements::AgValueType::CO2, value); + measurements.update(Measurements::CO2, value); // Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); } else { - measurements.updateValue(Measurements::AgValueType::CO2, utils::getInvalidCO2()); + measurements.update(Measurements::CO2, utils::getInvalidCO2()); } } @@ -374,7 +374,7 @@ static void createMqttTask(void) { /** Send data */ if (mqttClient.isConnected()) { String payload = - measurements.toStringX(true, fwMode, wifiConnector.RSSI(), *ag, configuration); + measurements.toString(true, fwMode, wifiConnector.RSSI(), *ag, configuration); String topic = "airgradient/readings/" + ag->deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), @@ -999,10 +999,10 @@ static void updateTvoc(void) { return; } - measurements.updateValue(Measurements::AgValueType::TVOC, ag->sgp41.getTvocIndex()); - measurements.updateValue(Measurements::AgValueType::TVOCRaw, ag->sgp41.getTvocRaw()); - measurements.updateValue(Measurements::AgValueType::NOx, ag->sgp41.getNoxIndex()); - measurements.updateValue(Measurements::AgValueType::NOxRaw, ag->sgp41.getNoxRaw()); + measurements.update(Measurements::TVOC, ag->sgp41.getTvocIndex()); + measurements.update(Measurements::TVOCRaw, ag->sgp41.getTvocRaw()); + measurements.update(Measurements::NOx, ag->sgp41.getNoxIndex()); + measurements.update(Measurements::NOxRaw, ag->sgp41.getNoxRaw()); // Serial.println(); // Serial.printf("TVOC index: %d\r\n", measurements.TVOC); @@ -1013,11 +1013,10 @@ static void updateTvoc(void) { static void updatePMS5003() { if (ag->pms5003.connected()) { - measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003.getPm01Ae()); - measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003.getPm25Ae()); - measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003.getPm10Ae()); - measurements.updateValue(Measurements::AgValueType::PM03_PC, - ag->pms5003.getPm03ParticleCount()); + measurements.update(Measurements::PM01, ag->pms5003.getPm01Ae()); + measurements.update(Measurements::PM25, ag->pms5003.getPm25Ae()); + measurements.update(Measurements::PM10, ag->pms5003.getPm10Ae()); + measurements.update(Measurements::PM03_PC, ag->pms5003.getPm03ParticleCount()); // Serial.println(); // Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); @@ -1026,10 +1025,10 @@ static void updatePMS5003() { // Serial.printf("PM0.3 Count: %d\r\n", measurements.pm03PCount_1); // Serial.printf("PM firmware version: %d\r\n", ag->pms5003.getFirmwareVersion()); } else { - measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue()); - measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue()); - measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue()); - measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM01, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10, utils::getInvalidPmValue()); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); } } @@ -1047,35 +1046,23 @@ static void updatePm(void) { int channel = 1; if (configuration.hasSensorPMS1) { if (ag->pms5003t_1.connected()) { - measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003t_1.getPm01Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003t_1.getPm25Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003t_1.getPm10Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM03_PC, - ag->pms5003t_1.getPm03ParticleCount(), channel); - measurements.updateValue(Measurements::AgValueType::Temperature, - ag->pms5003t_1.getTemperature(), channel); - measurements.updateValue(Measurements::AgValueType::Humidity, - ag->pms5003t_1.getRelativeHumidity(), channel); + measurements.update(Measurements::PM01, ag->pms5003t_1.getPm01Ae(), channel); + measurements.update(Measurements::PM25, ag->pms5003t_1.getPm25Ae(), channel); + measurements.update(Measurements::PM10, ag->pms5003t_1.getPm10Ae(), channel); + measurements.update(Measurements::PM03_PC, ag->pms5003t_1.getPm03ParticleCount(), channel); + measurements.update(Measurements::Temperature, ag->pms5003t_1.getTemperature(), channel); + measurements.update(Measurements::Humidity, ag->pms5003t_1.getRelativeHumidity(), channel); // flag that new valid PMS value exists newPMS2Value = true; } else { // PMS channel 1 now is not connected, update using invalid value - measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::Temperature, - utils::getInvalidTemperature(), channel); - measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity(), - channel); + measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel); } } @@ -1083,35 +1070,23 @@ static void updatePm(void) { channel = 2; if (configuration.hasSensorPMS2) { if (ag->pms5003t_2.connected()) { - measurements.updateValue(Measurements::AgValueType::PM01, ag->pms5003t_2.getPm01Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM25, ag->pms5003t_2.getPm25Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM10, ag->pms5003t_2.getPm10Ae(), - channel); - measurements.updateValue(Measurements::AgValueType::PM03_PC, - ag->pms5003t_2.getPm03ParticleCount(), channel); - measurements.updateValue(Measurements::AgValueType::Temperature, - ag->pms5003t_2.getTemperature(), channel); - measurements.updateValue(Measurements::AgValueType::Humidity, - ag->pms5003t_2.getRelativeHumidity(), channel); + measurements.update(Measurements::PM01, ag->pms5003t_2.getPm01Ae(), channel); + measurements.update(Measurements::PM25, ag->pms5003t_2.getPm25Ae(), channel); + measurements.update(Measurements::PM10, ag->pms5003t_2.getPm10Ae(), channel); + measurements.update(Measurements::PM03_PC, ag->pms5003t_2.getPm03ParticleCount(), channel); + measurements.update(Measurements::Temperature, ag->pms5003t_2.getTemperature(), channel); + measurements.update(Measurements::Humidity, ag->pms5003t_2.getRelativeHumidity(), channel); // flag that new valid PMS value exists newPMS2Value = true; } else { // PMS channel channel now is not connected, update using invalid value - measurements.updateValue(Measurements::AgValueType::PM01, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM25, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM10, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::PM03_PC, utils::getInvalidPmValue(), - channel); - measurements.updateValue(Measurements::AgValueType::Temperature, - utils::getInvalidTemperature(), channel); - measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity(), - channel); + measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel); } } @@ -1119,20 +1094,20 @@ static void updatePm(void) { float temp, hum; if (newPMS1Value && newPMS2Value) { // Both PMS has new valid value - temp = (measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 1) + - measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 2)) / + temp = (measurements.getFloat(Measurements::Temperature, false, 1) + + measurements.getFloat(Measurements::Temperature, false, 2)) / 2.0f; - hum = (measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 1) + - measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 2)) / + hum = (measurements.getFloat(Measurements::Humidity, false, 1) + + measurements.getFloat(Measurements::Humidity, false, 2)) / 2.0f; } else if (newPMS1Value) { // Only PMS1 has new valid value - temp = measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 1); - hum = measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 1); + temp = measurements.getFloat(Measurements::Temperature, false, 1); + hum = measurements.getFloat(Measurements::Humidity, false, 1); } else { // Only PMS2 has new valid value - temp = measurements.getValueFloat(Measurements::AgValueType::Temperature, false, 2); - hum = measurements.getValueFloat(Measurements::AgValueType::Humidity, false, 2); + temp = measurements.getFloat(Measurements::Temperature, false, 2); + hum = measurements.getFloat(Measurements::Humidity, false, 2); } // Update compensation temperature and humidity for SGP41 @@ -1146,7 +1121,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toStringX(false, fwMode, wifiConnector.RSSI(), *ag, configuration); + String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), *ag, configuration); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( @@ -1162,8 +1137,8 @@ static void tempHumUpdate(void) { float temp = ag->sht.getTemperature(); float rhum = ag->sht.getRelativeHumidity(); - measurements.updateValue(Measurements::AgValueType::Temperature, temp); - measurements.updateValue(Measurements::AgValueType::Humidity, rhum); + measurements.update(Measurements::Temperature, temp); + measurements.update(Measurements::Humidity, rhum); // Serial.printf("Temperature in C: %0.2f\n", temp); // Serial.printf("Relative Humidity: %d\n", rhum); @@ -1175,9 +1150,8 @@ static void tempHumUpdate(void) { ag->sgp41.setCompensationTemperatureHumidity(temp, rhum); } } else { - measurements.updateValue(Measurements::AgValueType::Temperature, - utils::getInvalidTemperature()); - measurements.updateValue(Measurements::AgValueType::Humidity, utils::getInvalidHumidity()); + measurements.update(Measurements::Temperature, utils::getInvalidTemperature()); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); } } \ No newline at end of file diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 214b072..578f289 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -1,55 +1,54 @@ #include "AgValue.h" #include "AgConfigure.h" #include "AirGradient.h" -#include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #define json_prop_pmFirmware "firmware" -void Measurements::maxUpdate(AgValueType type, int max) { +void Measurements::maxUpdate(MeasurementType type, int max) { switch (type) { - case AgValueType::Temperature: + case Temperature: _temperature[0].update.max = max; _temperature[1].update.max = max; break; - case AgValueType::Humidity: + case Humidity: _humidity[0].update.max = max; _humidity[1].update.max = max; break; - case AgValueType::CO2: + case CO2: _co2.update.max = max; break; - case AgValueType::TVOC: + case TVOC: _tvoc.update.max = max; break; - case AgValueType::TVOCRaw: + case TVOCRaw: _tvoc_raw.update.max = max; break; - case AgValueType::NOx: + case NOx: _nox.update.max = max; break; - case AgValueType::NOxRaw: + case NOxRaw: _nox_raw.update.max = max; break; - case AgValueType::PM25: + case PM25: _pm_25[0].update.max = max; _pm_25[1].update.max = max; break; - case AgValueType::PM01: + case PM01: _pm_01[0].update.max = max; _pm_01[1].update.max = max; break; - case AgValueType::PM10: + case PM10: _pm_10[0].update.max = max; _pm_10[1].update.max = max; break; - case AgValueType::PM03_PC: + case PM03_PC: _pm_03_pc[0].update.max = max; _pm_03_pc[1].update.max = max; break; }; } -bool Measurements::updateValue(AgValueType type, int val, int ch) { +bool Measurements::update(MeasurementType type, int val, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -60,39 +59,39 @@ bool Measurements::updateValue(AgValueType type, int val, int ch) { IntegerValue *temporary = nullptr; float invalidValue = 0; switch (type) { - case AgValueType::CO2: + case CO2: temporary = &_co2; invalidValue = utils::getInvalidCO2(); break; - case AgValueType::TVOC: + case TVOC: temporary = &_tvoc; invalidValue = utils::getInvalidVOC(); break; - case AgValueType::TVOCRaw: + case TVOCRaw: temporary = &_tvoc_raw; invalidValue = utils::getInvalidVOC(); break; - case AgValueType::NOx: + case NOx: temporary = &_nox; invalidValue = utils::getInvalidNOx(); break; - case AgValueType::NOxRaw: + case NOxRaw: temporary = &_nox_raw; invalidValue = utils::getInvalidNOx(); break; - case AgValueType::PM25: + case PM25: temporary = &_pm_25[ch]; invalidValue = utils::getInvalidPmValue(); break; - case AgValueType::PM01: + case PM01: temporary = &_pm_01[ch]; invalidValue = utils::getInvalidPmValue(); break; - case AgValueType::PM10: + case PM10: temporary = &_pm_10[ch]; invalidValue = utils::getInvalidPmValue(); break; - case AgValueType::PM03_PC: + case PM03_PC: temporary = &_pm_03_pc[ch]; invalidValue = utils::getInvalidPmValue(); break; @@ -100,9 +99,9 @@ bool Measurements::updateValue(AgValueType type, int val, int ch) { break; }; - // Sanity check if agvaluetype is defined for integer data type or not + // Sanity check if measurement type is defined for integer data type or not if (temporary == nullptr) { - Serial.printf("%s is not defined for integer data type\n", agValueTypeStr(type)); + Serial.printf("%s is not defined for integer data type\n", measurementTypeStr(type)); // TODO: Just assert? return false; } @@ -125,13 +124,13 @@ bool Measurements::updateValue(AgValueType type, int val, int ch) { // TODO: Need to check if SUCCESS == 0, what should we do? // Calculate the average temporary->avg = temporary->sumValues / temporary->update.success; - Serial.printf("%s{%d} count reached! Average value %d\n", agValueTypeStr(type), ch, + Serial.printf("%s{%d} count reached! Average value %d\n", measurementTypeStr(type), ch, temporary->avg); // Notify if there's are invalid value when updating int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s{%d} has %d invalid value out of %d update\n", agValueTypeStr(type), ch, + Serial.printf("%s{%d} has %d invalid value out of %d update\n", measurementTypeStr(type), ch, miss, temporary->update.max); } @@ -145,7 +144,7 @@ bool Measurements::updateValue(AgValueType type, int val, int ch) { return false; } -bool Measurements::updateValue(AgValueType type, float val, int ch) { +bool Measurements::update(MeasurementType type, float val, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -156,11 +155,11 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { FloatValue *temporary = nullptr; float invalidValue = 0; switch (type) { - case AgValueType::Temperature: + case Temperature: temporary = &_temperature[ch]; invalidValue = utils::getInvalidTemperature(); break; - case AgValueType::Humidity: + case Humidity: temporary = &_humidity[ch]; invalidValue = utils::getInvalidHumidity(); break; @@ -168,9 +167,9 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { break; } - // Sanity check if agvaluetype is defined for float data type or not + // Sanity check if measurement type is defined for float data type or not if (temporary == nullptr) { - Serial.printf("%s is not defined for float data type\n", agValueTypeStr(type)); + Serial.printf("%s is not defined for float data type\n", measurementTypeStr(type)); // TODO: Just assert? return false; } @@ -193,13 +192,13 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { // TODO: Need to check if SUCCESS == 0 // Calculate the average temporary->avg = temporary->sumValues / temporary->update.success; - Serial.printf("%s{%d} count reached! Average value %0.2f\n", agValueTypeStr(type), ch, + Serial.printf("%s{%d} count reached! Average value %0.2f\n", measurementTypeStr(type), ch, temporary->avg); // This is just for notifying int miss = temporary->update.max - temporary->update.success; if (miss != 0) { - Serial.printf("%s{%d} has %d invalid value out of %d update\n", agValueTypeStr(type), ch, + Serial.printf("%s{%d} has %d invalid value out of %d update\n", measurementTypeStr(type), ch, miss, temporary->update.max); } @@ -213,7 +212,7 @@ bool Measurements::updateValue(AgValueType type, float val, int ch) { return false; } -int Measurements::getValue(AgValueType type, bool average, int ch) { +int Measurements::get(MeasurementType type, bool average, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -224,40 +223,40 @@ int Measurements::getValue(AgValueType type, bool average, int ch) { IntegerValue *temporary = nullptr; float invalidValue = 0; switch (type) { - case AgValueType::CO2: + case CO2: temporary = &_co2; break; - case AgValueType::TVOC: + case TVOC: temporary = &_tvoc; break; - case AgValueType::TVOCRaw: + case TVOCRaw: temporary = &_tvoc_raw; break; - case AgValueType::NOx: + case NOx: temporary = &_nox; break; - case AgValueType::NOxRaw: + case NOxRaw: temporary = &_nox_raw; break; - case AgValueType::PM25: + case PM25: temporary = &_pm_25[ch]; break; - case AgValueType::PM01: + case PM01: temporary = &_pm_01[ch]; break; - case AgValueType::PM10: + case PM10: temporary = &_pm_10[ch]; break; - case AgValueType::PM03_PC: + case PM03_PC: temporary = &_pm_03_pc[ch]; break; default: break; }; - // Sanity check if agvaluetype is defined for integer data type or not + // Sanity check if measurement type is defined for integer data type or not if (temporary == nullptr) { - Serial.printf("%s is not defined for integer data type\n", agValueTypeStr(type)); + Serial.printf("%s is not defined for integer data type\n", measurementTypeStr(type)); // TODO: Just assert? return false; } @@ -269,26 +268,29 @@ int Measurements::getValue(AgValueType type, bool average, int ch) { return temporary->lastValue; } -float Measurements::getValueFloat(AgValueType type, bool average, int ch) { +float Measurements::getFloat(MeasurementType type, bool average, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); + // Follow array indexing just for get address of the value type + ch = ch - 1; + // Define data point source FloatValue *temporary = nullptr; switch (type) { - case AgValueType::Temperature: + case Temperature: temporary = &_temperature[ch]; break; - case AgValueType::Humidity: + case Humidity: temporary = &_humidity[ch]; break; default: break; } - // Sanity check if agvaluetype is defined for float data type or not + // Sanity check if measurement type is defined for float data type or not if (temporary == nullptr) { - Serial.printf("%s is not defined for float data type\n", agValueTypeStr(type)); + Serial.printf("%s is not defined for float data type\n", measurementTypeStr(type)); // TODO: Just assert? return false; } @@ -300,382 +302,6 @@ float Measurements::getValueFloat(AgValueType type, bool average, int ch) { return temporary->lastValue; } -String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, void *_ag, - void *_config) { - AirGradient *ag = (AirGradient *)_ag; - Configuration *config = (Configuration *)_config; - - JSONVar root; - root["wifi"] = rssi; - if (localServer) { - root["serialno"] = ag->deviceId(); - } - - if (config->hasSensorS8 && utils::isValidCO2(this->CO2)) { - root["rco2"] = this->CO2; - } - - if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { - if (config->hasSensorPMS1) { - if (utils::isValidPm(this->pm01_1)) { - root["pm01"] = this->pm01_1; - } - if (utils::isValidPm(this->pm25_1)) { - root["pm02"] = this->pm25_1; - } - if (utils::isValidPm(this->pm10_1)) { - root["pm10"] = this->pm10_1; - } - if (utils::isValidPm03Count(this->pm03PCount_1)) { - root["pm003Count"] = this->pm03PCount_1; - } - if (!localServer) { - - root[json_prop_pmFirmware] = - this->pms5003FirmwareVersion(ag->pms5003.getFirmwareVersion()); - } - } - - if (config->hasSensorSHT) { - if (utils::isValidTemperature(this->Temperature)) { - root["atmp"] = ag->round2(this->Temperature); - if (localServer) { - root["atmpCompensated"] = ag->round2(this->Temperature); - } - } - if (utils::isValidHumidity(this->Humidity)) { - root["rhum"] = this->Humidity; - if (localServer) { - root["rhumCompensated"] = this->Humidity; - } - } - } - - if (config->hasSensorSHT && config->hasSensorPMS1) { - int pm25 = ag->pms5003.compensate(this->pm25_1, this->Humidity); - if (pm25 >= 0) { - root["pm02Compensated"] = pm25; - } - } - - } else { - if (config->hasSensorPMS1 && config->hasSensorPMS2) { - if (utils::isValidPm(this->pm01_1) && utils::isValidPm(this->pm01_2)) { - root["pm01"] = ag->round2((this->pm01_1 + this->pm01_2) / 2.0f); - } - if (utils::isValidPm(this->pm25_1) && utils::isValidPm(this->pm25_2)) { - root["pm02"] = ag->round2((this->pm25_1 + this->pm25_2) / 2.0f); - } - if (utils::isValidPm(this->pm10_1) && utils::isValidPm(this->pm10_2)) { - root["pm10"] = ag->round2((this->pm10_1 + this->pm10_2) / 2.0f); - } - if (utils::isValidPm(this->pm03PCount_1) && utils::isValidPm(this->pm03PCount_2)) { - root["pm003Count"] = ag->round2((this->pm03PCount_1 + this->pm03PCount_2) / 2.0f); - } - - float val; - if (utils::isValidTemperature(this->temp_1) && utils::isValidTemperature(this->temp_1)) { - root["atmp"] = ag->round2((this->temp_1 + this->temp_2) / 2.0f); - if (localServer) { - val = ag->pms5003t_2.compensateTemp((this->temp_1 + this->temp_2) / 2.0f); - if (utils::isValidTemperature(val)) { - root["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_1) && utils::isValidHumidity(this->hum_1)) { - root["rhum"] = ag->round2((this->hum_1 + this->hum_2) / 2.0f); - if (localServer) { - val = ag->pms5003t_2.compensateHum((this->hum_1 + this->hum_2) / 2.0f); - if (utils::isValidHumidity(val)) { - root["rhumCompensated"] = (int)val; - } - } - } - - 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; - } - - if (fwMode == FW_MODE_O_1PS || fwMode == FW_MODE_O_1PST) { - float val; - if (config->hasSensorPMS1) { - if (utils::isValidPm(this->pm01_1)) { - root["pm01"] = this->pm01_1; - } - if (utils::isValidPm(this->pm25_1)) { - root["pm02"] = this->pm25_1; - } - if (utils::isValidPm(this->pm10_1)) { - root["pm10"] = this->pm10_1; - } - if (utils::isValidPm03Count(this->pm03PCount_1)) { - root["pm003Count"] = this->pm03PCount_1; - } - if (utils::isValidTemperature(this->temp_1)) { - root["atmp"] = ag->round2(this->temp_1); - - if (localServer) { - val = ag->pms5003t_1.compensateTemp(this->temp_1); - if (utils::isValidTemperature(val)) { - root["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_1)) { - root["rhum"] = this->hum_1; - - if (localServer) { - val = ag->pms5003t_1.compensateHum(this->hum_1); - if (utils::isValidHumidity(val)) { - root["rhumCompensated"] = (int)val; - } - } - } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); - if (!localServer) { - root[json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); - } - } - if (config->hasSensorPMS2) { - if(utils::isValidPm(this->pm01_2)) { - root["pm01"] = this->pm01_2; - } - if(utils::isValidPm(this->pm25_2)) { - root["pm02"] = this->pm25_2; - } - if(utils::isValidPm(this->pm10_2)) { - root["pm10"] = this->pm10_2; - } - if(utils::isValidPm03Count(this->pm03PCount_2)) { - root["pm003Count"] = this->pm03PCount_2; - } - - float val; - if (utils::isValidTemperature(this->temp_2)) { - root["atmp"] = ag->round2(this->temp_2); - - if (localServer) { - val = ag->pms5003t_2.compensateTemp(this->temp_2); - if (utils::isValidTemperature(val)) { - root["atmpCompensated"] = ag->round2(val); - } - } - } - if(utils::isValidHumidity(this->hum_2)) { - root["rhum"] = this->hum_2; - - if (localServer) { - val = ag->pms5003t_2.compensateHum(this->hum_2); - if (utils::isValidHumidity(val)) { - root["rhumCompensated"] = (int)val; - } - } - } - root["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2); - if(!localServer) { - root[json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); - } - } - } else { - if (fwMode == FW_MODE_O_1P) { - float val; - if (config->hasSensorPMS1) { - if (utils::isValidPm(this->pm01_1)) { - root["pm01"] = this->pm01_1; - } - if (utils::isValidPm(this->pm25_1)) { - root["pm02"] = this->pm25_1; - } - if (utils::isValidPm(this->pm10_1)) { - root["pm10"] = this->pm10_1; - } - if (utils::isValidPm03Count(this->pm03PCount_1)) { - root["pm003Count"] = this->pm03PCount_1; - } - if (utils::isValidTemperature(this->temp_1)) { - root["atmp"] = ag->round2(this->temp_1); - - if (localServer) { - val = ag->pms5003t_1.compensateTemp(this->temp_1); - if (utils::isValidTemperature(val)) { - root["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_1)) { - root["rhum"] = this->hum_1; - if(localServer) { - val = ag->pms5003t_1.compensateHum(this->hum_1); - if(utils::isValidHumidity(val)) { - root["rhumCompensated"] = (int)val; - } - } - } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); - if(!localServer) { - root[json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); - } - } else if (config->hasSensorPMS2) { - if(utils::isValidPm(this->pm01_2)) { - root["pm01"] = this->pm01_2; - } - if(utils::isValidPm(this->pm25_2)) { - root["pm02"] = this->pm25_2; - } - if(utils::isValidPm(this->pm10_2)) { - root["pm10"] = this->pm10_2; - } - if(utils::isValidPm03Count(this->pm03PCount_2)) { - root["pm003Count"] = this->pm03PCount_2; - } - if (utils::isValidTemperature(this->temp_2)) { - root["atmp"] = ag->round2(this->temp_2); - if (localServer) { - - val = ag->pms5003t_1.compensateTemp(this->temp_2); - if (utils::isValidTemperature(val)) { - root["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_2)) { - root["rhum"] = this->hum_2; - - if(localServer) { - val = ag->pms5003t_1.compensateHum(this->hum_2); - if(utils::isValidHumidity(val)) { - root["rhumCompensated"] = (int)val; - } - } - } - root["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); - if(!localServer) { - root[json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); - } - } - } else { - float val; - if (config->hasSensorPMS1) { - if(utils::isValidPm(this->pm01_1)) { - root["channels"]["1"]["pm01"] = this->pm01_1; - } - if(utils::isValidPm(this->pm25_1)) { - root["channels"]["1"]["pm02"] = this->pm25_1; - } - if(utils::isValidPm(this->pm10_1)) { - root["channels"]["1"]["pm10"] = this->pm10_1; - } - if (utils::isValidPm03Count(this->pm03PCount_1)) { - root["channels"]["1"]["pm003Count"] = this->pm03PCount_1; - } - if(utils::isValidTemperature(this->temp_1)) { - root["channels"]["1"]["atmp"] = ag->round2(this->temp_1); - - if (localServer) { - val = ag->pms5003t_1.compensateTemp(this->temp_1); - if (utils::isValidTemperature(val)) { - root["channels"]["1"]["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_1)) { - root["channels"]["1"]["rhum"] = this->hum_1; - - if (localServer) { - val = ag->pms5003t_1.compensateHum(this->hum_1); - if (utils::isValidHumidity(val)) { - root["channels"]["1"]["rhumCompensated"] = (int)val; - } - } - } - root["channels"]["1"]["pm02Compensated"] = ag->pms5003t_1.compensate(this->pm25_1, this->hum_1); - - // PMS5003T version - if(!localServer) { - root["channels"]["1"][json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); - } - } - if (config->hasSensorPMS2) { - float val; - if (utils::isValidPm(this->pm01_2)) { - root["channels"]["2"]["pm01"] = this->pm01_2; - } - if (utils::isValidPm(this->pm25_2)) { - root["channels"]["2"]["pm02"] = this->pm25_2; - } - if (utils::isValidPm(this->pm10_2)) { - root["channels"]["2"]["pm10"] = this->pm10_2; - } - if (utils::isValidPm03Count(this->pm03PCount_2)) { - root["channels"]["2"]["pm003Count"] = this->pm03PCount_2; - } - if (utils::isValidTemperature(this->temp_2)) { - root["channels"]["2"]["atmp"] = ag->round2(this->temp_2); - - if (localServer) { - val = ag->pms5003t_1.compensateTemp(this->temp_2); - if (utils::isValidTemperature(val)) { - root["channels"]["2"]["atmpCompensated"] = ag->round2(val); - } - } - } - if (utils::isValidHumidity(this->hum_2)) { - root["channels"]["2"]["rhum"] = this->hum_2; - - if (localServer) { - val = ag->pms5003t_1.compensateHum(this->hum_2); - if (utils::isValidHumidity(val)) { - root["channels"]["2"]["rhumCompensated"] = (int)val; - } - } - } - root["channels"]["2"]["pm02Compensated"] = ag->pms5003t_2.compensate(this->pm25_2, this->hum_2); - // PMS5003T version - if(!localServer) { - root["channels"]["2"][json_prop_pmFirmware] = - pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); - } - } - } - } - } - - if (config->hasSensorSGP) { - if (utils::isValidVOC(this->TVOC)) { - root["tvocIndex"] = this->TVOC; - } - if (utils::isValidVOC(this->TVOCRaw)) { - root["tvocRaw"] = this->TVOCRaw; - } - if (utils::isValidNOx(this->NOx)) { - root["noxIndex"] = this->NOx; - } - if (utils::isValidNOx(this->NOxRaw)) { - root["noxRaw"] = this->NOxRaw; - } - } - root["boot"] = bootCount; - root["bootCount"] = bootCount; - - if (localServer) { - if (ag->isOne()) { - root["ledMode"] = config->getLedBarModeName(); - } - root["firmware"] = ag->getVersion(); - root["model"] = AgFirmwareModeName(fwMode); - } - - return JSON.stringify(root); -} - String Measurements::pms5003FirmwareVersion(int fwCode) { return pms5003FirmwareVersionBase("PMS5003x", fwCode); } @@ -688,40 +314,40 @@ String Measurements::pms5003FirmwareVersionBase(String prefix, int fwCode) { return prefix + String("-") + String(fwCode); } -String Measurements::agValueTypeStr(AgValueType type) { +String Measurements::measurementTypeStr(MeasurementType type) { String str; switch (type) { - case AgValueType::Temperature: + case Temperature: str = "Temperature"; break; - case AgValueType::Humidity: + case Humidity: str = "Humidity"; break; - case AgValueType::CO2: + case CO2: str = "CO2"; break; - case AgValueType::TVOC: + case TVOC: str = "TVOC"; break; - case AgValueType::TVOCRaw: + case TVOCRaw: str = "TVOCRaw"; break; - case AgValueType::NOx: + case NOx: str = "NOx"; break; - case AgValueType::NOxRaw: + case NOxRaw: str = "NOxRaw"; break; - case AgValueType::PM25: + case PM25: str = "PM25"; break; - case AgValueType::PM01: + case PM01: str = "PM01"; break; - case AgValueType::PM10: + case PM10: str = "PM10"; break; - case AgValueType::PM03_PC: + case PM03_PC: str = "PM03"; break; default: @@ -739,8 +365,8 @@ void Measurements::validateChannel(int ch) { } } -String Measurements::toStringX(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, - Configuration &config) { +String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, + Configuration &config) { JSONVar root; if (ag.isOne() || (ag.isPro4_2()) || ag.isPro3_3() || ag.isBasic()) { diff --git a/src/AgValue.h b/src/AgValue.h index 752bf59..3f6f46e 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -35,32 +35,11 @@ private: }; public: - Measurements() { - pm25_1 = -1; - pm01_1 = -1; - pm10_1 = -1; - pm03PCount_1 = -1; - temp_1 = -1001; - hum_1 = -1; - - pm25_2 = -1; - pm01_2 = -1; - pm10_2 = -1; - pm03PCount_2 = -1; - temp_2 = -1001; - hum_2 = -1; - - Temperature = -1001; - Humidity = -1; - CO2 = -1; - TVOC = -1; - TVOCRaw = -1; - NOx = -1; - NOxRaw = -1; - } + Measurements() {} ~Measurements() {} - enum class AgValueType { + // Enumeration for every AG measurements + enum MeasurementType { Temperature, Humidity, CO2, @@ -75,40 +54,40 @@ public: }; /** - * @brief Set each AgValueType maximum update for a value type before calculate the average + * @brief Set each MeasurementType maximum update before calculate the average * - * @param type the target value type to set + * @param type the target measurement type to set * @param max the maximum counter */ - void maxUpdate(AgValueType type, int max); + void maxUpdate(MeasurementType, int max); /** - * @brief update target type value with new value. - * Each AgValueType has last raw value and last average that are calculated based on max number of - * update. This function is for AgValueType that use INT as the data type + * @brief update target measurement type with new value. + * Each MeasurementType has last raw value and last average that are calculated based on max + * number of update. This function is for MeasurementType that use INT as the data type * - * @param type (AgValueType) value type that will be updated + * @param type measurement type that will be updated * @param val (int) the new value - * @param ch (int) the AgValueType channel, not every AgValueType has more than 1 channel. + * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) * @return true if update counter reached and new average value is calculated * @return false otherwise */ - bool updateValue(AgValueType type, int val, int ch = 1); + bool update(MeasurementType type, int val, int ch = 1); /** - * @brief update target type value with new value. - * Each AgValueType has last raw value and last average that are calculated based on max number of - * update. This function is for AgValueType that use FLOAT as the data type + * @brief update target measurement type with new value. + * Each MeasurementType has last raw value and last average that are calculated based on max + * number of update. This function is for MeasurementType that use FLOAT as the data type * - * @param type (AgValueType) value type that will be updated + * @param type measurement type that will be updated * @param val (float) the new value - * @param ch (int) the AgValueType channel, not every AgValueType has more than 1 channel. + * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) * @return true if update counter reached and new average value is calculated * @return false otherwise */ - bool updateValue(AgValueType type, float val, int ch = 1); + bool update(MeasurementType type, float val, int ch = 1); /** * @brief Get the target measurement type value @@ -118,7 +97,7 @@ public: * @param ch target type value channel * @return int measurement type value */ - int getValue(AgValueType type, bool average = true, int ch = 1); + int get(MeasurementType type, bool average = true, int ch = 1); /** * @brief Get the target measurement type value @@ -128,49 +107,13 @@ public: * @param ch target type value channel * @return float measurement type value */ - float getValueFloat(AgValueType type, bool average = true, int ch = 1); + float getFloat(MeasurementType type, bool average = true, int ch = 1); - float Temperature; - int Humidity; - int CO2; - int TVOC; - int TVOCRaw; - int NOx; - int NOxRaw; - - int pm25_1; - int pm01_1; - int pm10_1; - int pm03PCount_1; - float temp_1; - int hum_1; - - int pm25_2; - int pm01_2; - int pm10_2; - int pm03PCount_2; - float temp_2; - int hum_2; - - int pm1Value01; - int pm1Value25; - int pm1Value10; - int pm1PCount; - int pm1temp; - int pm1hum; - int pm2Value01; - int pm2Value25; - int pm2Value10; - int pm2PCount; - int pm2temp; - int pm2hum; - int countPosition; - const int targetCount = 20; + // TODO: update this to using setter int bootCount; - String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void *_ag, void *_config); - String toStringX(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, - Configuration &config); + String toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, + Configuration &config); private: // Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T @@ -212,7 +155,7 @@ private: /** * Convert AgValue Type to string representation of the value */ - String agValueTypeStr(AgValueType type); + String measurementTypeStr(MeasurementType type); /** * @brief check if provided channel is a valid channel or not From 2e4f4643fa9e0851851baeba03addab98d2e9834 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 19 Oct 2024 01:33:00 +0700 Subject: [PATCH 22/69] maxUpdate --- examples/OneOpenAir/OneOpenAir.ino | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index d50bf3f..f98f14a 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -263,17 +263,17 @@ void setup() { updateDisplayAndLedBar(); // NOTE: This is just a temporary, will do a proper set maximum value based on schedule interval - measurements.maxUpdate(Measurements::AgValueType::Temperature, 26); - measurements.maxUpdate(Measurements::AgValueType::Humidity, 26); - measurements.maxUpdate(Measurements::AgValueType::CO2, 13); - measurements.maxUpdate(Measurements::AgValueType::TVOC, 53); - measurements.maxUpdate(Measurements::AgValueType::TVOCRaw, 53); - measurements.maxUpdate(Measurements::AgValueType::NOx, 53); - measurements.maxUpdate(Measurements::AgValueType::NOxRaw, 53); - measurements.maxUpdate(Measurements::AgValueType::PM25, 26); - measurements.maxUpdate(Measurements::AgValueType::PM01, 26); - measurements.maxUpdate(Measurements::AgValueType::PM10, 26); - measurements.maxUpdate(Measurements::AgValueType::PM03_PC, 26); + measurements.maxUpdate(Measurements::Temperature, 26); + measurements.maxUpdate(Measurements::Humidity, 26); + measurements.maxUpdate(Measurements::CO2, 13); + measurements.maxUpdate(Measurements::TVOC, 53); + measurements.maxUpdate(Measurements::TVOCRaw, 53); + measurements.maxUpdate(Measurements::NOx, 53); + measurements.maxUpdate(Measurements::NOxRaw, 53); + measurements.maxUpdate(Measurements::PM25, 26); + measurements.maxUpdate(Measurements::PM01, 26); + measurements.maxUpdate(Measurements::PM10, 26); + measurements.maxUpdate(Measurements::PM03_PC, 26); } void loop() { From 399b4ca1dcc734cab98aae189e1ee2d33941ddc8 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 19 Oct 2024 01:35:36 +0700 Subject: [PATCH 23/69] Other related class use new AgValue structure --- examples/OneOpenAir/LocalServer.cpp | 2 +- examples/OneOpenAir/OpenMetrics.cpp | 57 ++++++++++++++++------------ src/AgOledDisplay.cpp | 58 ++++++++++++++++------------- src/AgStateMachine.cpp | 6 +-- 4 files changed, 69 insertions(+), 54 deletions(-) diff --git a/examples/OneOpenAir/LocalServer.cpp b/examples/OneOpenAir/LocalServer.cpp index b9989a1..e0b5c98 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.toStringX(true, fwMode, wifiConnector.RSSI(), *ag, config); + String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config); server.send(200, "application/json", toSend); } diff --git a/examples/OneOpenAir/OpenMetrics.cpp b/examples/OneOpenAir/OpenMetrics.cpp index a7dfe70..b1bfcc5 100644 --- a/examples/OneOpenAir/OpenMetrics.cpp +++ b/examples/OneOpenAir/OpenMetrics.cpp @@ -74,41 +74,50 @@ String OpenMetrics::getPayload(void) { int atmpCompensated = utils::getInvalidTemperature(); int ahumCompensated = utils::getInvalidHumidity(); if (config.hasSensorPMS1 && config.hasSensorPMS2) { - _temp = (measure.temp_1 + measure.temp_2) / 2.0f; - _hum = (measure.hum_1 + measure.hum_2) / 2.0f; - pm01 = (measure.pm01_1 + measure.pm01_2) / 2; - pm25 = (measure.pm25_1 + measure.pm25_2) / 2; - pm10 = (measure.pm10_1 + measure.pm10_2) / 2; - pm03PCount = (measure.pm03PCount_1 + measure.pm03PCount_2) / 2; + _temp = (measure.getFloat(Measurements::Temperature, false, 1) + + measure.getFloat(Measurements::Temperature, false, 2)) / + 2.0f; + _hum = (measure.getFloat(Measurements::Humidity, false, 1) + + measure.getFloat(Measurements::Humidity, false, 2)) / + 2.0f; + pm01 = (measure.get(Measurements::PM01, false, 1) + measure.get(Measurements::PM01, false, 2)) / + 2.0f; + pm25 = (measure.get(Measurements::PM25, false, 1) + measure.get(Measurements::PM25, false, 2)) / + 2.0f; + pm10 = (measure.get(Measurements::PM10, false, 1) + measure.get(Measurements::PM10, false, 2)) / + 2.0f; + pm03PCount = (measure.get(Measurements::PM03_PC, false, 1) + + measure.get(Measurements::PM03_PC, false, 2)) / + 2.0f; } else { if (ag->isOne()) { if (config.hasSensorSHT) { - _temp = measure.Temperature; - _hum = measure.Humidity; + _temp = measure.getFloat(Measurements::Temperature, false); + _hum = measure.getFloat(Measurements::Humidity, false); } if (config.hasSensorPMS1) { - pm01 = measure.pm01_1; - pm25 = measure.pm25_1; - pm10 = measure.pm10_1; - pm03PCount = measure.pm03PCount_1; + pm01 = measure.get(Measurements::PM01, false); + pm25 = measure.get(Measurements::PM25, false); + pm10 = measure.get(Measurements::PM10, false); + pm03PCount = measure.get(Measurements::PM03_PC, false); } } else { if (config.hasSensorPMS1) { - _temp = measure.temp_1; - _hum = measure.hum_1; - pm01 = measure.pm01_1; - pm25 = measure.pm25_1; - pm10 = measure.pm10_1; - pm03PCount = measure.pm03PCount_1; + _temp = measure.getFloat(Measurements::Temperature, false); + _hum = measure.getFloat(Measurements::Humidity, false); + pm01 = measure.get(Measurements::PM01, false); + pm25 = measure.get(Measurements::PM25, false); + pm10 = measure.get(Measurements::PM10, false); + pm03PCount = measure.get(Measurements::PM03_PC, false); } if (config.hasSensorPMS2) { - _temp = measure.temp_2; - _hum = measure.hum_2; - pm01 = measure.pm01_2; - pm25 = measure.pm25_2; - pm10 = measure.pm10_2; - pm03PCount = measure.pm03PCount_2; + _temp = measure.getFloat(Measurements::Temperature, false, 2); + _hum = measure.getFloat(Measurements::Humidity, false, 2); + pm01 = measure.get(Measurements::PM01, false, 2); + pm25 = measure.get(Measurements::PM25, false, 2); + pm10 = measure.get(Measurements::PM10, false, 2); + pm03PCount = measure.get(Measurements::PM03_PC, false, 2); } } } diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 050782a..1228a66 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -12,12 +12,13 @@ */ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { /** Temperature */ - if (utils::isValidTemperature(value.Temperature)) { + float temp = value.getFloat(Measurements::Temperature, false); + if (utils::isValidTemperature(temp)) { float t = 0.0f; if (config.isTemperatureUnitInF()) { - t = utils::degreeC_To_F(value.Temperature); + t = utils::degreeC_To_F(temp); } else { - t = value.Temperature; + t = temp; } if (config.isTemperatureUnitInF()) { @@ -43,13 +44,14 @@ void OledDisplay::showTempHum(bool hasStatus, char *buf, int buf_size) { DISP()->drawUTF8(1, 10, buf); /** Show humidity */ - if (utils::isValidHumidity(value.Humidity)) { - snprintf(buf, buf_size, "%d%%", value.Humidity); + int rhum = (int)value.getFloat(Measurements::Humidity, false); + if (utils::isValidHumidity(rhum)) { + snprintf(buf, buf_size, "%d%%", rhum); } else { snprintf(buf, buf_size, "-%%"); } - if (value.Humidity > 99) { + if (rhum > 99.0) { DISP()->drawStr(97, 10, buf); } else { DISP()->drawStr(105, 10, buf); @@ -290,8 +292,9 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawUTF8(1, 27, "CO2"); DISP()->setFont(u8g2_font_t0_22b_tf); - if (utils::isValidCO2(value.CO2)) { - sprintf(strBuf, "%d", value.CO2); + int co2 = value.get(Measurements::CO2, false); + if (utils::isValidCO2(co2)) { + sprintf(strBuf, "%d", co2); } else { sprintf(strBuf, "%s", "-"); } @@ -310,12 +313,11 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(55, 27, "PM2.5"); /** Draw PM2.5 value */ - if (utils::isValidPm(value.pm25_1)) { - int pm25 = value.pm25_1; - + int pm25 = value.get(Measurements::PM25, false); + if (utils::isValidPm(pm25)) { /** Compensate PM2.5 value. */ if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { - pm25 = ag->pms5003.compensate(pm25, value.Humidity); + pm25 = ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity, false)); logInfo("PM2.5 compensate: " + String(pm25)); } @@ -343,17 +345,19 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(100, 27, "VOC:"); /** Draw tvocIndexvalue */ - if (utils::isValidVOC(value.TVOC)) { - sprintf(strBuf, "%d", value.TVOC); + int tvoc = value.get(Measurements::TVOC, false); + if (utils::isValidVOC(tvoc)) { + sprintf(strBuf, "%d", tvoc); } else { sprintf(strBuf, "%s", "-"); } DISP()->drawStr(100, 39, strBuf); /** Draw NOx label */ + int nox = value.get(Measurements::NOx, false); DISP()->drawStr(100, 53, "NOx:"); - if (utils::isValidNOx(value.NOx)) { - sprintf(strBuf, "%d", value.NOx); + if (utils::isValidNOx(nox)) { + sprintf(strBuf, "%d", nox); } else { sprintf(strBuf, "%s", "-"); } @@ -363,8 +367,9 @@ void OledDisplay::showDashboard(const char *status) { ag->display.clear(); /** Set CO2 */ - if (utils::isValidCO2(value.CO2)) { - snprintf(strBuf, sizeof(strBuf), "CO2:%d", value.CO2); + int co2 = value.get(Measurements::CO2, false); + if (utils::isValidCO2(co2)) { + snprintf(strBuf, sizeof(strBuf), "CO2:%d", co2); } else { snprintf(strBuf, sizeof(strBuf), "CO2:-"); } @@ -373,9 +378,9 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set PM */ - int pm25 = value.pm25_1; + int pm25 = value.get(Measurements::PM25, false); if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { - pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity); + pm25 = (int)ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity, false)); } ag->display.setCursor(0, 12); @@ -387,12 +392,12 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set temperature and humidity */ - if (utils::isValidTemperature(value.Temperature)) { + float temp = value.getFloat(Measurements::Temperature, false); + if (utils::isValidTemperature(temp)) { if (config.isTemperatureUnitInF()) { - snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", - utils::degreeC_To_F(value.Temperature)); + snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", utils::degreeC_To_F(temp)); } else { - snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", value.Temperature); + snprintf(strBuf, sizeof(strBuf), "T:%0.f1 C", temp); } } else { if (config.isTemperatureUnitInF()) { @@ -405,8 +410,9 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setCursor(0, 24); ag->display.setText(strBuf); - if (utils::isValidHumidity(value.Humidity)) { - snprintf(strBuf, sizeof(strBuf), "H:%d %%", (int)value.Humidity); + int rhum = (int)value.getFloat(Measurements::Humidity, false); + if (utils::isValidHumidity(rhum)) { + snprintf(strBuf, sizeof(strBuf), "H:%d %%", rhum); } else { snprintf(strBuf, sizeof(strBuf), "H:- %%"); } diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 85f87d5..85dd76b 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -69,7 +69,7 @@ void StateMachine::sensorhandleLeds(void) { * */ void StateMachine::co2handleLeds(void) { - int co2Value = value.CO2; + int co2Value = value.get(Measurements::CO2, false); if (co2Value <= 600) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); @@ -141,9 +141,9 @@ void StateMachine::co2handleLeds(void) { * */ void StateMachine::pm25handleLeds(void) { - int pm25Value = value.pm25_1; + int pm25Value = value.get(Measurements::PM25, false); if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) { - pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity); + pm25Value = ag->pms5003.compensate(pm25Value, value.getFloat(Measurements::Humidity, false)); } if (pm25Value < 5) { From e47a9057eabc6b7a85ded8de3ade3514a06af280 Mon Sep 17 00:00:00 2001 From: MallocArray Date: Sat, 19 Oct 2024 02:55:08 -0500 Subject: [PATCH 24/69] Update AQI breakpoints to 2024 values (#208) * Update breakpoints to 2024 standard * Update formula to match Wikipedia https://en.wikipedia.org/wiki/Air_quality_index#Computing_the_AQI --------- Co-authored-by: Samuel Siburian --- src/PMS/PMS.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 4bf4da6..6bacd7d 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -297,20 +297,18 @@ uint8_t PMSBase::getErrorCode(void) { return pms_errorCode; } * @return int */ int PMSBase::pm25ToAQI(int pm02) { - if (pm02 <= 12.0) - return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0); + if (pm02 <= 9.0) + return ((50 - 0) / (9.0 - .0) * (pm02 - .0) + 0); else if (pm02 <= 35.4) - return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50); + return ((100 - 51) / (35.4 - 9.1) * (pm02 - 9.0) + 51); else if (pm02 <= 55.4) - return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100); - else if (pm02 <= 150.4) - return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150); - else if (pm02 <= 250.4) - return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200); - else if (pm02 <= 350.4) - return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300); - else if (pm02 <= 500.4) - return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400); + return ((150 - 101) / (55.4 - 35.5) * (pm02 - 35.5) + 101); + else if (pm02 <= 125.4) + return ((200 - 151) / (125.4 - 55.5) * (pm02 - 55.5) + 151); + else if (pm02 <= 225.4) + return ((300 - 201) / (225.4 - 125.5) * (pm02 - 125.5) + 201); + else if (pm02 <= 325.4) + return ((500 - 301) / (325.4 - 225.5) * (pm02 - 225.5) + 301); else return 500; } From f36f860c2e921c87c7b85475e4fe937bbaddb58c Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 19:01:41 +0700 Subject: [PATCH 25/69] Switch to moving average for sensor data average value to floating points --- src/AgValue.cpp | 183 ++++++++++++++++++++++++------------------------ src/AgValue.h | 14 ++-- 2 files changed, 97 insertions(+), 100 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 578f289..7248ff3 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -109,39 +109,32 @@ bool Measurements::update(MeasurementType type, int val, int ch) { // Restore channel value for debugging purpose ch = ch + 1; - // Update new value when value provided is not the invalid one - if (val != invalidValue) { - temporary->lastValue = val; - temporary->sumValues = temporary->sumValues + val; - temporary->update.success = temporary->update.success + 1; + if (val == invalidValue) { + temporary->update.invalidCounter++; + // TODO: Need to check if its more than threshold, to create some indication. Maybe reference to + // max element? + return false; } - // Increment update.counter - temporary->update.counter = temporary->update.counter + 1; + // Reset invalid counter when update new valid value + temporary->update.invalidCounter = 0; - // Calculate value average when maximum set is reached - if (temporary->update.counter >= temporary->update.max) { - // TODO: Need to check if SUCCESS == 0, what should we do? - // Calculate the average - temporary->avg = temporary->sumValues / temporary->update.success; - Serial.printf("%s{%d} count reached! Average value %d\n", measurementTypeStr(type), ch, - temporary->avg); - - // Notify if there's are invalid value when updating - int miss = temporary->update.max - temporary->update.success; - if (miss != 0) { - Serial.printf("%s{%d} has %d invalid value out of %d update\n", measurementTypeStr(type), ch, - miss, temporary->update.max); - } - - // Resets average related variable calculation - temporary->sumValues = 0; - temporary->update.counter = 0; - temporary->update.success = 0; - return true; + // Add new value to the end of the list + temporary->listValues.push_back(val); + // Sum the new value + temporary->sumValues = temporary->sumValues + val; + // Remove the oldest value on the list when the list exceed max elements + if (temporary->listValues.size() > temporary->update.max) { + auto it = temporary->listValues.begin(); + temporary->sumValues = temporary->sumValues - *it; // subtract the oldest value from sum + temporary->listValues.erase(it); // And remove it from the list } - return false; + // Calculate average based on how many elements on the list + temporary->avg = temporary->sumValues / (float)temporary->listValues.size(); + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->avg); + + return true; } bool Measurements::update(MeasurementType type, float val, int ch) { @@ -177,39 +170,32 @@ bool Measurements::update(MeasurementType type, float val, int ch) { // Restore channel value for debugging purpose ch = ch + 1; - // Update new value when value provided is not the invalid one - if (val != invalidValue) { - temporary->lastValue = val; - temporary->sumValues = temporary->sumValues + val; - temporary->update.success = temporary->update.success + 1; + if (val == invalidValue) { + temporary->update.invalidCounter++; + // TODO: Need to check if its more than threshold, to create some indication. Maybe reference to + // max element? + return false; } - // Increment update.counter - temporary->update.counter = temporary->update.counter + 1; + // Reset invalid counter when update new valid value + temporary->update.invalidCounter = 0; - // Calculate value average when maximum set is reached - if (temporary->update.counter >= temporary->update.max) { - // TODO: Need to check if SUCCESS == 0 - // Calculate the average - temporary->avg = temporary->sumValues / temporary->update.success; - Serial.printf("%s{%d} count reached! Average value %0.2f\n", measurementTypeStr(type), ch, - temporary->avg); - - // This is just for notifying - int miss = temporary->update.max - temporary->update.success; - if (miss != 0) { - Serial.printf("%s{%d} has %d invalid value out of %d update\n", measurementTypeStr(type), ch, - miss, temporary->update.max); - } - - // Resets average related variable calculation - temporary->sumValues = 0; - temporary->update.counter = 0; - temporary->update.success = 0; - return true; + // Add new value to the end of the list + temporary->listValues.push_back(val); + // Sum the new value + temporary->sumValues = temporary->sumValues + val; + // Remove the oldest value on the list when the list exceed max elements + if (temporary->listValues.size() > temporary->update.max) { + auto it = temporary->listValues.begin(); + temporary->sumValues = temporary->sumValues - *it; // subtract the oldest value from sum + temporary->listValues.erase(it); // And remove it from the list } - return false; + // Calculate average based on how many elements on the list + temporary->avg = temporary->sumValues / (float)temporary->listValues.size(); + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->avg); + + return true; } int Measurements::get(MeasurementType type, bool average, int ch) { @@ -261,11 +247,17 @@ int Measurements::get(MeasurementType type, bool average, int ch) { return false; } + if (temporary->listValues.empty()) { + // Values still empty, return 0 + return 0; + } + if (average) { + // TODO: This now is average value, need to update this return temporary->avg; } - return temporary->lastValue; + return temporary->listValues.back(); } float Measurements::getFloat(MeasurementType type, bool average, int ch) { @@ -295,11 +287,16 @@ float Measurements::getFloat(MeasurementType type, bool average, int ch) { return false; } + if (temporary->listValues.empty()) { + // Values still empty, return 0 + return 0; + } + if (average) { return temporary->avg; } - return temporary->lastValue; + return temporary->listValues.back(); } String Measurements::pms5003FirmwareVersion(int fwCode) { @@ -377,22 +374,22 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, // CO2 if (config.hasSensorS8 && utils::isValidCO2(_co2.avg)) { - root["rco2"] = _co2.avg; + root["rco2"] = ag.round2(_co2.avg); } /// TVOx and NOx if (config.hasSensorSGP) { if (utils::isValidVOC(_tvoc.avg)) { - root["tvocIndex"] = _tvoc.avg; + root["tvocIndex"] = ag.round2(_tvoc.avg); } if (utils::isValidVOC(_tvoc_raw.avg)) { - root["tvocRaw"] = _tvoc_raw.avg; + root["tvocRaw"] = ag.round2(_tvoc_raw.avg); } if (utils::isValidNOx(_nox.avg)) { - root["noxIndex"] = _nox.avg; + root["noxIndex"] = ag.round2(_nox.avg); } if (utils::isValidNOx(_nox_raw.avg)) { - root["noxRaw"] = _nox_raw.avg; + root["noxRaw"] = ag.round2(_nox_raw.avg); } } @@ -511,16 +508,16 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem ch = ch - 1; if (utils::isValidPm(_pm_01[ch].avg)) { - pms["pm01"] = _pm_01[ch].avg; + pms["pm01"] = ag.round2(_pm_01[ch].avg); } if (utils::isValidPm(_pm_25[ch].avg)) { - pms["pm02"] = _pm_25[ch].avg; + pms["pm02"] = ag.round2(_pm_25[ch].avg); } if (utils::isValidPm(_pm_10[ch].avg)) { - pms["pm10"] = _pm_10[ch].avg; + pms["pm10"] = ag.round2(_pm_10[ch].avg); } if (utils::isValidPm03Count(_pm_03_pc[ch].avg)) { - pms["pm003Count"] = _pm_03_pc[ch].avg; + pms["pm003Count"] = ag.round2(_pm_03_pc[ch].avg); } if (withTempHum) { @@ -568,58 +565,58 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem // Handle both channel with average, if one of the channel not valid, use another one /// PM01 if (utils::isValidPm(_pm_01[0].avg) && utils::isValidPm(_pm_01[1].avg)) { - float avg = (_pm_01[0].avg + _pm_01[1].avg) / 2; + float avg = (_pm_01[0].avg + _pm_01[1].avg) / 2.0f; pms["pm01"] = ag.round2(avg); - pms["channels"]["1"]["pm01"] = _pm_01[0].avg; - pms["channels"]["2"]["pm01"] = _pm_01[1].avg; + pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].avg); + pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].avg); } else if (utils::isValidPm(_pm_01[0].avg)) { - pms["pm01"] = _pm_01[0].avg; - pms["channels"]["1"]["pm01"] = _pm_01[0].avg; + pms["pm01"] = ag.round2(_pm_01[0].avg); + pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].avg); } else if (utils::isValidPm(_pm_01[1].avg)) { - pms["pm01"] = _pm_01[1].avg; - pms["channels"]["2"]["pm01"] = _pm_01[1].avg; + pms["pm01"] = ag.round2(_pm_01[1].avg); + pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].avg); } /// PM2.5 if (utils::isValidPm(_pm_25[0].avg) && utils::isValidPm(_pm_25[1].avg)) { float avg = (_pm_25[0].avg + _pm_25[1].avg) / 2.0f; pms["pm02"] = ag.round2(avg); - pms["channels"]["1"]["pm02"] = _pm_25[0].avg; - pms["channels"]["2"]["pm02"] = _pm_25[1].avg; + pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].avg); + pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].avg); } else if (utils::isValidPm(_pm_25[0].avg)) { - pms["pm02"] = _pm_25[0].avg; - pms["channels"]["1"]["pm02"] = _pm_25[0].avg; + pms["pm02"] = ag.round2(_pm_25[0].avg); + pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].avg); } else if (utils::isValidPm(_pm_25[1].avg)) { - pms["pm02"] = _pm_25[1].avg; - pms["channels"]["2"]["pm02"] = _pm_25[1].avg; + pms["pm02"] = ag.round2(_pm_25[1].avg); + pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].avg); } /// PM10 if (utils::isValidPm(_pm_10[0].avg) && utils::isValidPm(_pm_10[1].avg)) { float avg = (_pm_10[0].avg + _pm_10[1].avg) / 2.0f; pms["pm10"] = ag.round2(avg); - pms["channels"]["1"]["pm10"] = _pm_10[0].avg; - pms["channels"]["2"]["pm10"] = _pm_10[1].avg; + pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].avg); + pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].avg); } else if (utils::isValidPm(_pm_10[0].avg)) { - pms["pm10"] = _pm_10[0].avg; - pms["channels"]["1"]["pm10"] = _pm_10[0].avg; + pms["pm10"] = ag.round2(_pm_10[0].avg); + pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].avg); } else if (utils::isValidPm(_pm_10[1].avg)) { - pms["pm10"] = _pm_10[1].avg; - pms["channels"]["2"]["pm10"] = _pm_10[1].avg; + pms["pm10"] = ag.round2(_pm_10[1].avg); + pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].avg); } /// PM03 particle count if (utils::isValidPm03Count(_pm_03_pc[0].avg) && utils::isValidPm03Count(_pm_03_pc[1].avg)) { float avg = (_pm_03_pc[0].avg + _pm_03_pc[1].avg) / 2.0f; pms["pm003Count"] = ag.round2(avg); - pms["channels"]["1"]["pm003Count"] = _pm_03_pc[0].avg; - pms["channels"]["2"]["pm003Count"] = _pm_03_pc[1].avg; + pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].avg); + pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].avg); } else if (utils::isValidPm(_pm_03_pc[0].avg)) { - pms["pm003Count"] = _pm_03_pc[0].avg; - pms["channels"]["1"]["pm003Count"] = _pm_03_pc[0].avg; + pms["pm003Count"] = ag.round2(_pm_03_pc[0].avg); + pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].avg); } else if (utils::isValidPm(_pm_03_pc[1].avg)) { - pms["pm003Count"] = _pm_03_pc[1].avg; - pms["channels"]["2"]["pm003Count"] = _pm_03_pc[1].avg; + pms["pm003Count"] = ag.round2(_pm_03_pc[1].avg); + pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].avg); } if (withTempHum) { @@ -721,7 +718,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem /// 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["pm02Compensated"] = (int)((pm25_comp1 / pm25_comp2) / 2); + pms["pm02Compensated"] = ag.round2((pm25_comp1 / pm25_comp2) / 2.0f); } else if (utils::isValidPm(pm25_comp1)) { pms["pm02Compensated"] = pm25_comp1; } else if (utils::isValidPm(pm25_comp2)) { diff --git a/src/AgValue.h b/src/AgValue.h index 3f6f46e..797f3f0 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -7,30 +7,30 @@ #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #include "Main/utils.h" #include +#include class Measurements { private: // Generic struct for update indication for respective value struct Update { - int counter; // How many update attempts done - int success; // How many update value that actually give valid value - int max; // Maximum update counter before calculating average + int invalidCounter; // Counting on how many invalid value that are passed to update function + int max; // Maximum elements on the list }; // Reading type for sensor value that outputs float struct FloatValue { - float lastValue; // Last update value float sumValues; // Total value from each update - float avg; // The last average calculation after maximum update attempt reached + std::vector listValues; // List of update value that are kept + float avg; // Moving average value, updated every update function called Update update; }; // Reading type for sensor value that outputs integer struct IntegerValue { - int lastValue; // Last update value unsigned long sumValues; // Total value from each update; unsigned long to accomodate TVOx and // NOx raw data - int avg; // The last average calculation after maximum update attempt reached + std::vector listValues; // List of update value that are kept + float avg; // Moving average value, updated every update function called Update update; }; From 84884d0c15ab634657273b8f1acc8e8c452b8448 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 19:20:17 +0700 Subject: [PATCH 26/69] Move average member value to update struct --- src/AgValue.cpp | 233 +++++++++++++++++++++++++----------------------- src/AgValue.h | 3 +- 2 files changed, 120 insertions(+), 116 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 7248ff3..473163c 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -131,8 +131,8 @@ bool Measurements::update(MeasurementType type, int val, int ch) { } // Calculate average based on how many elements on the list - temporary->avg = temporary->sumValues / (float)temporary->listValues.size(); - Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->avg); + temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); return true; } @@ -192,8 +192,8 @@ bool Measurements::update(MeasurementType type, float val, int ch) { } // Calculate average based on how many elements on the list - temporary->avg = temporary->sumValues / (float)temporary->listValues.size(); - Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->avg); + temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); return true; } @@ -254,7 +254,7 @@ int Measurements::get(MeasurementType type, bool average, int ch) { if (average) { // TODO: This now is average value, need to update this - return temporary->avg; + return temporary->update.avg; } return temporary->listValues.back(); @@ -293,7 +293,7 @@ float Measurements::getFloat(MeasurementType type, bool average, int ch) { } if (average) { - return temporary->avg; + return temporary->update.avg; } return temporary->listValues.back(); @@ -373,23 +373,23 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } // CO2 - if (config.hasSensorS8 && utils::isValidCO2(_co2.avg)) { - root["rco2"] = ag.round2(_co2.avg); + if (config.hasSensorS8 && utils::isValidCO2(_co2.update.avg)) { + root["rco2"] = ag.round2(_co2.update.avg); } /// TVOx and NOx if (config.hasSensorSGP) { - if (utils::isValidVOC(_tvoc.avg)) { - root["tvocIndex"] = ag.round2(_tvoc.avg); + if (utils::isValidVOC(_tvoc.update.avg)) { + root["tvocIndex"] = ag.round2(_tvoc.update.avg); } - if (utils::isValidVOC(_tvoc_raw.avg)) { - root["tvocRaw"] = ag.round2(_tvoc_raw.avg); + if (utils::isValidVOC(_tvoc_raw.update.avg)) { + root["tvocRaw"] = ag.round2(_tvoc_raw.update.avg); } - if (utils::isValidNOx(_nox.avg)) { - root["noxIndex"] = ag.round2(_nox.avg); + if (utils::isValidNOx(_nox.update.avg)) { + root["noxIndex"] = ag.round2(_nox.update.avg); } - if (utils::isValidNOx(_nox_raw.avg)) { - root["noxRaw"] = ag.round2(_nox_raw.avg); + if (utils::isValidNOx(_nox_raw.update.avg)) { + root["noxRaw"] = ag.round2(_nox_raw.update.avg); } } @@ -467,25 +467,25 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati if (config.hasSensorSHT) { // Add temperature - if (utils::isValidTemperature(_temperature[0].avg)) { - indoor["atmp"] = ag.round2(_temperature[0].avg); + if (utils::isValidTemperature(_temperature[0].update.avg)) { + indoor["atmp"] = ag.round2(_temperature[0].update.avg); if (localServer) { - indoor["atmpCompensated"] = ag.round2(_temperature[0].avg); + indoor["atmpCompensated"] = ag.round2(_temperature[0].update.avg); } } // Add humidity - if (utils::isValidHumidity(_humidity[0].avg)) { - indoor["rhum"] = ag.round2(_humidity[0].avg); + if (utils::isValidHumidity(_humidity[0].update.avg)) { + indoor["rhum"] = ag.round2(_humidity[0].update.avg); if (localServer) { - indoor["rhumCompensated"] = ag.round2(_humidity[0].avg); + indoor["rhumCompensated"] = ag.round2(_humidity[0].update.avg); } } } // Add pm25 compensated value only if PM2.5 and humidity value is valid - if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].avg)) { - if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].avg)) { - int pm25 = ag.pms5003.compensate(_pm_25[0].avg, _humidity[0].avg); + if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].update.avg)) { + if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].update.avg)) { + int pm25 = ag.pms5003.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); if (utils::isValidPm(pm25)) { indoor["pm02Compensated"] = pm25; } @@ -507,38 +507,38 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem // Follow array indexing just for get address of the value type ch = ch - 1; - if (utils::isValidPm(_pm_01[ch].avg)) { - pms["pm01"] = ag.round2(_pm_01[ch].avg); + if (utils::isValidPm(_pm_01[ch].update.avg)) { + pms["pm01"] = ag.round2(_pm_01[ch].update.avg); } - if (utils::isValidPm(_pm_25[ch].avg)) { - pms["pm02"] = ag.round2(_pm_25[ch].avg); + if (utils::isValidPm(_pm_25[ch].update.avg)) { + pms["pm02"] = ag.round2(_pm_25[ch].update.avg); } - if (utils::isValidPm(_pm_10[ch].avg)) { - pms["pm10"] = ag.round2(_pm_10[ch].avg); + if (utils::isValidPm(_pm_10[ch].update.avg)) { + pms["pm10"] = ag.round2(_pm_10[ch].update.avg); } - if (utils::isValidPm03Count(_pm_03_pc[ch].avg)) { - pms["pm003Count"] = ag.round2(_pm_03_pc[ch].avg); + if (utils::isValidPm03Count(_pm_03_pc[ch].update.avg)) { + pms["pm003Count"] = ag.round2(_pm_03_pc[ch].update.avg); } if (withTempHum) { float _vc; // Set temperature if valid - if (utils::isValidTemperature(_temperature[ch].avg)) { - pms["atmp"] = ag.round2(_temperature[ch].avg); + if (utils::isValidTemperature(_temperature[ch].update.avg)) { + pms["atmp"] = ag.round2(_temperature[ch].update.avg); // Compensate temperature when flag is set if (compensate) { - _vc = ag.pms5003t_1.compensateTemp(_temperature[ch].avg); + _vc = ag.pms5003t_1.compensateTemp(_temperature[ch].update.avg); if (utils::isValidTemperature(_vc)) { pms["atmpCompensated"] = ag.round2(_vc); } } } // Set humidity if valid - if (utils::isValidHumidity(_humidity[ch].avg)) { - pms["rhum"] = ag.round2(_humidity[ch].avg); + if (utils::isValidHumidity(_humidity[ch].update.avg)) { + pms["rhum"] = ag.round2(_humidity[ch].update.avg); // Compensate relative humidity when flag is set if (compensate) { - _vc = ag.pms5003t_1.compensateHum(_humidity[ch].avg); + _vc = ag.pms5003t_1.compensateHum(_humidity[ch].update.avg); if (utils::isValidTemperature(_vc)) { pms["rhumCompensated"] = ag.round2(_vc); } @@ -547,10 +547,11 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem // Add pm25 compensated value only if PM2.5 and humidity value is valid if (compensate) { - if (utils::isValidPm(_pm_25[ch].avg) && utils::isValidHumidity(_humidity[ch].avg)) { + if (utils::isValidPm(_pm_25[ch].update.avg) && + 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 - int pm25 = ag.pms5003t_1.compensate(_pm_25[ch].avg, _humidity[ch].avg); + int pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); if (utils::isValidPm(pm25)) { pms["pm02Compensated"] = pm25; } @@ -564,139 +565,141 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem // Handle both channel with average, if one of the channel not valid, use another one /// PM01 - if (utils::isValidPm(_pm_01[0].avg) && utils::isValidPm(_pm_01[1].avg)) { - float avg = (_pm_01[0].avg + _pm_01[1].avg) / 2.0f; + 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["pm01"] = ag.round2(avg); - pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].avg); - pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].avg); - } else if (utils::isValidPm(_pm_01[0].avg)) { - pms["pm01"] = ag.round2(_pm_01[0].avg); - pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].avg); - } else if (utils::isValidPm(_pm_01[1].avg)) { - pms["pm01"] = ag.round2(_pm_01[1].avg); - pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].avg); + pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].update.avg); + pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].update.avg); + } else if (utils::isValidPm(_pm_01[0].update.avg)) { + pms["pm01"] = ag.round2(_pm_01[0].update.avg); + pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].update.avg); + } else if (utils::isValidPm(_pm_01[1].update.avg)) { + pms["pm01"] = ag.round2(_pm_01[1].update.avg); + pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].update.avg); } /// PM2.5 - if (utils::isValidPm(_pm_25[0].avg) && utils::isValidPm(_pm_25[1].avg)) { - float avg = (_pm_25[0].avg + _pm_25[1].avg) / 2.0f; + 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["pm02"] = ag.round2(avg); - pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].avg); - pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].avg); - } else if (utils::isValidPm(_pm_25[0].avg)) { - pms["pm02"] = ag.round2(_pm_25[0].avg); - pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].avg); - } else if (utils::isValidPm(_pm_25[1].avg)) { - pms["pm02"] = ag.round2(_pm_25[1].avg); - pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].avg); + pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].update.avg); + pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].update.avg); + } else if (utils::isValidPm(_pm_25[0].update.avg)) { + pms["pm02"] = ag.round2(_pm_25[0].update.avg); + pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].update.avg); + } else if (utils::isValidPm(_pm_25[1].update.avg)) { + pms["pm02"] = ag.round2(_pm_25[1].update.avg); + pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].update.avg); } /// PM10 - if (utils::isValidPm(_pm_10[0].avg) && utils::isValidPm(_pm_10[1].avg)) { - float avg = (_pm_10[0].avg + _pm_10[1].avg) / 2.0f; + 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["pm10"] = ag.round2(avg); - pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].avg); - pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].avg); - } else if (utils::isValidPm(_pm_10[0].avg)) { - pms["pm10"] = ag.round2(_pm_10[0].avg); - pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].avg); - } else if (utils::isValidPm(_pm_10[1].avg)) { - pms["pm10"] = ag.round2(_pm_10[1].avg); - pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].avg); + pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].update.avg); + pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].update.avg); + } else if (utils::isValidPm(_pm_10[0].update.avg)) { + pms["pm10"] = ag.round2(_pm_10[0].update.avg); + pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].update.avg); + } else if (utils::isValidPm(_pm_10[1].update.avg)) { + pms["pm10"] = ag.round2(_pm_10[1].update.avg); + pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].update.avg); } /// PM03 particle count - if (utils::isValidPm03Count(_pm_03_pc[0].avg) && utils::isValidPm03Count(_pm_03_pc[1].avg)) { - float avg = (_pm_03_pc[0].avg + _pm_03_pc[1].avg) / 2.0f; + 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["pm003Count"] = ag.round2(avg); - pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].avg); - pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].avg); - } else if (utils::isValidPm(_pm_03_pc[0].avg)) { - pms["pm003Count"] = ag.round2(_pm_03_pc[0].avg); - pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].avg); - } else if (utils::isValidPm(_pm_03_pc[1].avg)) { - pms["pm003Count"] = ag.round2(_pm_03_pc[1].avg); - pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].avg); + pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].update.avg); + pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].update.avg); + } else if (utils::isValidPm(_pm_03_pc[0].update.avg)) { + pms["pm003Count"] = ag.round2(_pm_03_pc[0].update.avg); + pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].update.avg); + } else if (utils::isValidPm(_pm_03_pc[1].update.avg)) { + pms["pm003Count"] = ag.round2(_pm_03_pc[1].update.avg); + pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_pc[1].update.avg); } if (withTempHum) { /// Temperature - if (utils::isValidTemperature(_temperature[0].avg) && - utils::isValidTemperature(_temperature[1].avg)) { + if (utils::isValidTemperature(_temperature[0].update.avg) && + utils::isValidTemperature(_temperature[1].update.avg)) { - float temperature = (_temperature[0].avg + _temperature[1].avg) / 2.0f; + float temperature = (_temperature[0].update.avg + _temperature[1].update.avg) / 2.0f; pms["atmp"] = ag.round2(temperature); - pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].avg); - pms["channels"]["2"]["atmp"] = ag.round2(_temperature[1].avg); + pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].update.avg); + pms["channels"]["2"]["atmp"] = 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].avg); - float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].avg); + float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].update.avg); + float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].update.avg); pms["atmpCompensated"] = ag.round2(temp); pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); } - } else if (utils::isValidTemperature(_temperature[0].avg)) { - pms["atmp"] = ag.round2(_temperature[0].avg); - pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].avg); + } else if (utils::isValidTemperature(_temperature[0].update.avg)) { + pms["atmp"] = ag.round2(_temperature[0].update.avg); + pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].update.avg); if (compensate) { // Compensate channel 1 - float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].avg); + float temp1 = ag.pms5003t_1.compensateTemp(_temperature[0].update.avg); pms["atmpCompensated"] = ag.round2(temp1); pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); } - } else if (utils::isValidTemperature(_temperature[1].avg)) { - pms["atmp"] = ag.round2(_temperature[1].avg); - pms["channels"]["2"]["atmp"] = ag.round2(_temperature[1].avg); + } else if (utils::isValidTemperature(_temperature[1].update.avg)) { + pms["atmp"] = ag.round2(_temperature[1].update.avg); + pms["channels"]["2"]["atmp"] = ag.round2(_temperature[1].update.avg); if (compensate) { // Compensate channel 2 - float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].avg); + float temp2 = ag.pms5003t_2.compensateTemp(_temperature[1].update.avg); pms["atmpCompensated"] = ag.round2(temp2); pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); } } /// Relative humidity - if (utils::isValidHumidity(_humidity[0].avg) && utils::isValidHumidity(_humidity[1].avg)) { - float humidity = (_humidity[0].avg + _humidity[1].avg) / 2.0f; + 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["rhum"] = ag.round2(humidity); - pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].avg); - pms["channels"]["2"]["rhum"] = ag.round2(_humidity[1].avg); + pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].update.avg); + pms["channels"]["2"]["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].avg); - float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].avg); + float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].update.avg); + float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].update.avg); pms["rhumCompensated"] = ag.round2(hum); pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); } - } else if (utils::isValidHumidity(_humidity[0].avg)) { - pms["rhum"] = ag.round2(_humidity[0].avg); - pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].avg); + } else if (utils::isValidHumidity(_humidity[0].update.avg)) { + pms["rhum"] = ag.round2(_humidity[0].update.avg); + pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].update.avg); if (compensate) { // Compensate humidity channel 1 - float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].avg); + float hum1 = ag.pms5003t_1.compensateHum(_humidity[0].update.avg); pms["rhumCompensated"] = ag.round2(hum1); pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); } - } else if (utils::isValidHumidity(_humidity[1].avg)) { - pms["rhum"] = ag.round2(_humidity[1].avg); - pms["channels"]["2"]["rhum"] = ag.round2(_humidity[1].avg); + } else if (utils::isValidHumidity(_humidity[1].update.avg)) { + pms["rhum"] = ag.round2(_humidity[1].update.avg); + pms["channels"]["2"]["rhum"] = ag.round2(_humidity[1].update.avg); if (compensate) { // Compensate humidity channel 2 - float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].avg); + float hum2 = ag.pms5003t_2.compensateHum(_humidity[1].update.avg); pms["rhumCompensated"] = ag.round2(hum2); pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); } @@ -707,12 +710,14 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem /// First get both channel compensated value int pm25_comp1 = utils::getInvalidPmValue(); int pm25_comp2 = utils::getInvalidPmValue(); - if (utils::isValidPm(_pm_25[0].avg) && utils::isValidHumidity(_humidity[0].avg)) { - pm25_comp1 = ag.pms5003t_1.compensate(_pm_25[0].avg, _humidity[0].avg); + 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"]["pm02Compensated"] = pm25_comp1; } - if (utils::isValidPm(_pm_25[1].avg) && utils::isValidHumidity(_humidity[1].avg)) { - pm25_comp2 = ag.pms5003t_2.compensate(_pm_25[1].avg, _humidity[1].avg); + 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"]["pm02Compensated"] = pm25_comp2; } diff --git a/src/AgValue.h b/src/AgValue.h index 797f3f0..fd85e3d 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -15,13 +15,13 @@ private: struct Update { int invalidCounter; // Counting on how many invalid value that are passed to update function int max; // Maximum elements on the list + float avg; // Moving average value, updated every update function called }; // Reading type for sensor value that outputs float struct FloatValue { float sumValues; // Total value from each update std::vector listValues; // List of update value that are kept - float avg; // Moving average value, updated every update function called Update update; }; @@ -30,7 +30,6 @@ private: unsigned long sumValues; // Total value from each update; unsigned long to accomodate TVOx and // NOx raw data std::vector listValues; // List of update value that are kept - float avg; // Moving average value, updated every update function called Update update; }; From 43ca0a2c2e641bff637498502d3640855d0eacbe Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 20:04:07 +0700 Subject: [PATCH 27/69] get and getFloat function specific for latest value Update functions comments --- src/AgValue.cpp | 13 ++----------- src/AgValue.h | 32 +++++++++++++++----------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 473163c..f22c943 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -198,7 +198,7 @@ bool Measurements::update(MeasurementType type, float val, int ch) { return true; } -int Measurements::get(MeasurementType type, bool average, int ch) { +int Measurements::get(MeasurementType type, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -252,15 +252,10 @@ int Measurements::get(MeasurementType type, bool average, int ch) { return 0; } - if (average) { - // TODO: This now is average value, need to update this - return temporary->update.avg; - } - return temporary->listValues.back(); } -float Measurements::getFloat(MeasurementType type, bool average, int ch) { +float Measurements::getFloat(MeasurementType type, int ch) { // Sanity check to validate channel, assert if invalid validateChannel(ch); @@ -292,10 +287,6 @@ float Measurements::getFloat(MeasurementType type, bool average, int ch) { return 0; } - if (average) { - return temporary->update.avg; - } - return temporary->listValues.back(); } diff --git a/src/AgValue.h b/src/AgValue.h index fd85e3d..adaa6db 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -14,7 +14,7 @@ private: // Generic struct for update indication for respective value struct Update { int invalidCounter; // Counting on how many invalid value that are passed to update function - int max; // Maximum elements on the list + int max; // Maximum length of the period of the moving average float avg; // Moving average value, updated every update function called }; @@ -53,60 +53,58 @@ public: }; /** - * @brief Set each MeasurementType maximum update before calculate the average + * @brief Set each MeasurementType maximum period length for moving average * * @param type the target measurement type to set - * @param max the maximum counter + * @param max the maximum period length */ void maxUpdate(MeasurementType, int max); /** * @brief update target measurement type with new value. - * Each MeasurementType has last raw value and last average that are calculated based on max - * number of update. This function is for MeasurementType that use INT as the data type + * Each MeasurementType has last raw value and moving average value based on max period + * This function is for MeasurementType that use INT as the data type * * @param type measurement type that will be updated * @param val (int) the new value * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) - * @return true if update counter reached and new average value is calculated - * @return false otherwise + * @return false if new value invalid consecutively reach threshold + * @return true otherwise */ bool update(MeasurementType type, int val, int ch = 1); /** * @brief update target measurement type with new value. - * Each MeasurementType has last raw value and last average that are calculated based on max - * number of update. This function is for MeasurementType that use FLOAT as the data type + * Each MeasurementType has last raw value and moving average value based on max period + * This function is for MeasurementType that use FLOAT as the data type * * @param type measurement type that will be updated * @param val (float) the new value * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) - * @return true if update counter reached and new average value is calculated - * @return false otherwise + * @return false if new value invalid consecutively reach threshold + * @return true otherwise */ bool update(MeasurementType type, float val, int ch = 1); /** - * @brief Get the target measurement type value + * @brief Get the target measurement latest value * * @param type measurement type that will be retrieve - * @param average true if expect last average value, false if expect last update value * @param ch target type value channel * @return int measurement type value */ - int get(MeasurementType type, bool average = true, int ch = 1); + int get(MeasurementType type, int ch = 1); /** - * @brief Get the target measurement type value + * @brief Get the target measurement latest value * * @param type measurement type that will be retrieve - * @param average true if expect last average value, false if expect last update value * @param ch target type value channel * @return float measurement type value */ - float getFloat(MeasurementType type, bool average = true, int ch = 1); + float getFloat(MeasurementType type, int ch = 1); // TODO: update this to using setter int bootCount; From f478dd16c8b8cbe26bce0f58e779c7cf88e5563c Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 22:30:49 +0700 Subject: [PATCH 28/69] get value function consume --- examples/OneOpenAir/OneOpenAir.ino | 16 ++++---- examples/OneOpenAir/OpenMetrics.cpp | 58 ++++++++++++++--------------- src/AgOledDisplay.cpp | 24 ++++++------ src/AgStateMachine.cpp | 6 +-- 4 files changed, 50 insertions(+), 54 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index f98f14a..90e138b 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1094,20 +1094,20 @@ static void updatePm(void) { float temp, hum; if (newPMS1Value && newPMS2Value) { // Both PMS has new valid value - temp = (measurements.getFloat(Measurements::Temperature, false, 1) + - measurements.getFloat(Measurements::Temperature, false, 2)) / + temp = (measurements.getFloat(Measurements::Temperature, 1) + + measurements.getFloat(Measurements::Temperature, 2)) / 2.0f; - hum = (measurements.getFloat(Measurements::Humidity, false, 1) + - measurements.getFloat(Measurements::Humidity, false, 2)) / + hum = (measurements.getFloat(Measurements::Humidity, 1) + + measurements.getFloat(Measurements::Humidity, 2)) / 2.0f; } else if (newPMS1Value) { // Only PMS1 has new valid value - temp = measurements.getFloat(Measurements::Temperature, false, 1); - hum = measurements.getFloat(Measurements::Humidity, false, 1); + temp = measurements.getFloat(Measurements::Temperature, 1); + hum = measurements.getFloat(Measurements::Humidity, 1); } else { // Only PMS2 has new valid value - temp = measurements.getFloat(Measurements::Temperature, false, 2); - hum = measurements.getFloat(Measurements::Humidity, false, 2); + temp = measurements.getFloat(Measurements::Temperature, 2); + hum = measurements.getFloat(Measurements::Humidity, 2); } // Update compensation temperature and humidity for SGP41 diff --git a/examples/OneOpenAir/OpenMetrics.cpp b/examples/OneOpenAir/OpenMetrics.cpp index b1bfcc5..23b2dd0 100644 --- a/examples/OneOpenAir/OpenMetrics.cpp +++ b/examples/OneOpenAir/OpenMetrics.cpp @@ -74,50 +74,46 @@ String OpenMetrics::getPayload(void) { int atmpCompensated = utils::getInvalidTemperature(); int ahumCompensated = utils::getInvalidHumidity(); if (config.hasSensorPMS1 && config.hasSensorPMS2) { - _temp = (measure.getFloat(Measurements::Temperature, false, 1) + - measure.getFloat(Measurements::Temperature, false, 2)) / + _temp = (measure.getFloat(Measurements::Temperature, 1) + + measure.getFloat(Measurements::Temperature, 2)) / 2.0f; - _hum = (measure.getFloat(Measurements::Humidity, false, 1) + - measure.getFloat(Measurements::Humidity, false, 2)) / + _hum = (measure.getFloat(Measurements::Humidity, 1) + + measure.getFloat(Measurements::Humidity, 2)) / 2.0f; - pm01 = (measure.get(Measurements::PM01, false, 1) + measure.get(Measurements::PM01, false, 2)) / - 2.0f; - pm25 = (measure.get(Measurements::PM25, false, 1) + measure.get(Measurements::PM25, false, 2)) / - 2.0f; - pm10 = (measure.get(Measurements::PM10, false, 1) + measure.get(Measurements::PM10, false, 2)) / - 2.0f; - pm03PCount = (measure.get(Measurements::PM03_PC, false, 1) + - measure.get(Measurements::PM03_PC, false, 2)) / - 2.0f; + pm01 = (measure.get(Measurements::PM01, 1) + measure.get(Measurements::PM01, 2)) / 2.0f; + pm25 = (measure.get(Measurements::PM25, 1) + measure.get(Measurements::PM25, 2)) / 2.0f; + pm10 = (measure.get(Measurements::PM10, 1) + measure.get(Measurements::PM10, 2)) / 2.0f; + pm03PCount = + (measure.get(Measurements::PM03_PC, 1) + measure.get(Measurements::PM03_PC, 2)) / 2.0f; } else { if (ag->isOne()) { if (config.hasSensorSHT) { - _temp = measure.getFloat(Measurements::Temperature, false); - _hum = measure.getFloat(Measurements::Humidity, false); + _temp = measure.getFloat(Measurements::Temperature); + _hum = measure.getFloat(Measurements::Humidity); } if (config.hasSensorPMS1) { - pm01 = measure.get(Measurements::PM01, false); - pm25 = measure.get(Measurements::PM25, false); - pm10 = measure.get(Measurements::PM10, false); - pm03PCount = measure.get(Measurements::PM03_PC, false); + pm01 = measure.get(Measurements::PM01); + pm25 = measure.get(Measurements::PM25); + pm10 = measure.get(Measurements::PM10); + pm03PCount = measure.get(Measurements::PM03_PC); } } else { if (config.hasSensorPMS1) { - _temp = measure.getFloat(Measurements::Temperature, false); - _hum = measure.getFloat(Measurements::Humidity, false); - pm01 = measure.get(Measurements::PM01, false); - pm25 = measure.get(Measurements::PM25, false); - pm10 = measure.get(Measurements::PM10, false); - pm03PCount = measure.get(Measurements::PM03_PC, false); + _temp = measure.getFloat(Measurements::Temperature, 1); + _hum = measure.getFloat(Measurements::Humidity, 1); + pm01 = measure.get(Measurements::PM01, 1); + pm25 = measure.get(Measurements::PM25, 1); + pm10 = measure.get(Measurements::PM10, 1); + pm03PCount = measure.get(Measurements::PM03_PC, 1); } if (config.hasSensorPMS2) { - _temp = measure.getFloat(Measurements::Temperature, false, 2); - _hum = measure.getFloat(Measurements::Humidity, false, 2); - pm01 = measure.get(Measurements::PM01, false, 2); - pm25 = measure.get(Measurements::PM25, false, 2); - pm10 = measure.get(Measurements::PM10, false, 2); - pm03PCount = measure.get(Measurements::PM03_PC, false, 2); + _temp = measure.getFloat(Measurements::Temperature, 2); + _hum = measure.getFloat(Measurements::Humidity, 2); + pm01 = measure.get(Measurements::PM01, 2); + pm25 = measure.get(Measurements::PM25, 2); + pm10 = measure.get(Measurements::PM10, 2); + pm03PCount = measure.get(Measurements::PM03_PC, 2); } } } diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 1228a66..5661f21 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.getFloat(Measurements::Temperature, false); + float temp = value.getFloat(Measurements::Temperature); 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 = (int)value.getFloat(Measurements::Humidity, false); + int rhum = (int)value.getFloat(Measurements::Humidity); if (utils::isValidHumidity(rhum)) { snprintf(buf, buf_size, "%d%%", rhum); } else { @@ -292,7 +292,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawUTF8(1, 27, "CO2"); DISP()->setFont(u8g2_font_t0_22b_tf); - int co2 = value.get(Measurements::CO2, false); + int co2 = value.get(Measurements::CO2); if (utils::isValidCO2(co2)) { sprintf(strBuf, "%d", co2); } else { @@ -313,11 +313,11 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(55, 27, "PM2.5"); /** Draw PM2.5 value */ - int pm25 = value.get(Measurements::PM25, false); + int pm25 = value.get(Measurements::PM25); if (utils::isValidPm(pm25)) { /** Compensate PM2.5 value. */ if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { - pm25 = ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity, false)); + pm25 = ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity)); logInfo("PM2.5 compensate: " + String(pm25)); } @@ -345,7 +345,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(100, 27, "VOC:"); /** Draw tvocIndexvalue */ - int tvoc = value.get(Measurements::TVOC, false); + int tvoc = value.get(Measurements::TVOC); if (utils::isValidVOC(tvoc)) { sprintf(strBuf, "%d", tvoc); } else { @@ -354,7 +354,7 @@ void OledDisplay::showDashboard(const char *status) { DISP()->drawStr(100, 39, strBuf); /** Draw NOx label */ - int nox = value.get(Measurements::NOx, false); + int nox = value.get(Measurements::NOx); DISP()->drawStr(100, 53, "NOx:"); if (utils::isValidNOx(nox)) { sprintf(strBuf, "%d", nox); @@ -367,7 +367,7 @@ void OledDisplay::showDashboard(const char *status) { ag->display.clear(); /** Set CO2 */ - int co2 = value.get(Measurements::CO2, false); + int co2 = value.get(Measurements::CO2); if (utils::isValidCO2(co2)) { snprintf(strBuf, sizeof(strBuf), "CO2:%d", co2); } else { @@ -378,9 +378,9 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set PM */ - int pm25 = value.get(Measurements::PM25, false); + int pm25 = value.get(Measurements::PM25); if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { - pm25 = (int)ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity, false)); + pm25 = (int)ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity)); } ag->display.setCursor(0, 12); @@ -392,7 +392,7 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setText(strBuf); /** Set temperature and humidity */ - float temp = value.getFloat(Measurements::Temperature, false); + float temp = value.getFloat(Measurements::Temperature); if (utils::isValidTemperature(temp)) { if (config.isTemperatureUnitInF()) { snprintf(strBuf, sizeof(strBuf), "T:%0.1f F", utils::degreeC_To_F(temp)); @@ -410,7 +410,7 @@ void OledDisplay::showDashboard(const char *status) { ag->display.setCursor(0, 24); ag->display.setText(strBuf); - int rhum = (int)value.getFloat(Measurements::Humidity, false); + int rhum = (int)value.getFloat(Measurements::Humidity); if (utils::isValidHumidity(rhum)) { snprintf(strBuf, sizeof(strBuf), "H:%d %%", rhum); } else { diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 85dd76b..9cbe068 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -69,7 +69,7 @@ void StateMachine::sensorhandleLeds(void) { * */ void StateMachine::co2handleLeds(void) { - int co2Value = value.get(Measurements::CO2, false); + int co2Value = value.get(Measurements::CO2); if (co2Value <= 600) { /** G; 1 */ ag->ledBar.setColor(RGB_COLOR_G, ag->ledBar.getNumberOfLeds() - 1); @@ -141,9 +141,9 @@ void StateMachine::co2handleLeds(void) { * */ void StateMachine::pm25handleLeds(void) { - int pm25Value = value.get(Measurements::PM25, false); + int pm25Value = value.get(Measurements::PM25); if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) { - pm25Value = ag->pms5003.compensate(pm25Value, value.getFloat(Measurements::Humidity, false)); + pm25Value = ag->pms5003.compensate(pm25Value, value.getFloat(Measurements::Humidity)); } if (pm25Value < 5) { From 7de2d0cc3017bf28bb1abc5abde571ecb1e39591 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 23:20:16 +0700 Subject: [PATCH 29/69] Set proper max period for moving average based on update interval SHT read set to 6s --- examples/OneOpenAir/OneOpenAir.ino | 46 +++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 90e138b..dca35c9 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -62,7 +62,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */ #define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */ #define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */ -#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */ +#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */ #define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */ #define FIRMWARE_CHECK_FOR_UPDATE_MS (60*60*1000) /** ms */ @@ -115,6 +115,7 @@ static void firmwareCheckForUpdate(void); static void otaHandlerCallback(OtaState state, String mesasge); static void displayExecuteOta(OtaState state, String msg, int processing); +static int calculateMaxPeriod(int updateInterval); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -166,6 +167,32 @@ void setup() { /** Init sensor */ boardInit(); + /* Set max period for each measurement type based on sensor update interval*/ + if (configuration.hasSensorSHT) { + /// Max period for SHT sensors measurements + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + } else { + /// Temp and hum data retrieved from PMS5003T sensor + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + } + /// Max period for S8 sensors measurements + measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements + measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + /// Max period for PMS sensors measurements + measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + /** Connecting wifi */ bool connectToWifi = false; if (ag->isOne()) { @@ -261,19 +288,6 @@ void setup() { // Update display and led bar after finishing setup to show dashboard updateDisplayAndLedBar(); - - // NOTE: This is just a temporary, will do a proper set maximum value based on schedule interval - measurements.maxUpdate(Measurements::Temperature, 26); - measurements.maxUpdate(Measurements::Humidity, 26); - measurements.maxUpdate(Measurements::CO2, 13); - measurements.maxUpdate(Measurements::TVOC, 53); - measurements.maxUpdate(Measurements::TVOCRaw, 53); - measurements.maxUpdate(Measurements::NOx, 53); - measurements.maxUpdate(Measurements::NOxRaw, 53); - measurements.maxUpdate(Measurements::PM25, 26); - measurements.maxUpdate(Measurements::PM01, 26); - measurements.maxUpdate(Measurements::PM10, 26); - measurements.maxUpdate(Measurements::PM03_PC, 26); } void loop() { @@ -1154,4 +1168,8 @@ static void tempHumUpdate(void) { measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); } +} + +int calculateMaxPeriod(int updateInterval) { + return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.1)) / updateInterval; } \ No newline at end of file From 2cda36ed0d0e6c4535951f0cf3a21cedbe1be724 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sun, 20 Oct 2024 23:27:27 +0700 Subject: [PATCH 30/69] set measurement max period as function --- examples/OneOpenAir/OneOpenAir.ino | 57 ++++++++++++++++-------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index dca35c9..a6bfd04 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -116,6 +116,7 @@ static void otaHandlerCallback(OtaState state, String mesasge); static void displayExecuteOta(OtaState state, String msg, int processing); static int calculateMaxPeriod(int updateInterval); +static void setMeasurementMaxPeriod(); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -166,32 +167,7 @@ void setup() { /** Init sensor */ boardInit(); - - /* Set max period for each measurement type based on sensor update interval*/ - if (configuration.hasSensorSHT) { - /// Max period for SHT sensors measurements - measurements.maxPeriod(Measurements::Temperature, - calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::Humidity, - calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); - } else { - /// Temp and hum data retrieved from PMS5003T sensor - measurements.maxPeriod(Measurements::Temperature, - calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - } - /// Max period for S8 sensors measurements - measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); - /// Max period for SGP sensors measurements - measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - /// Max period for PMS sensors measurements - measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + setMeasurementMaxPeriod(); /** Connecting wifi */ bool connectToWifi = false; @@ -1170,6 +1146,35 @@ static void tempHumUpdate(void) { } } +/* Set max period for each measurement type based on sensor update interval*/ +void setMeasurementMaxPeriod() { + /// Max period for S8 sensors measurements + measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements + measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + /// Max period for PMS sensors measurements + measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + // Temperature and Humidity + if (configuration.hasSensorSHT) { + /// Max period for SHT sensors measurements + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + } else { + /// Temp and hum data retrieved from PMS5003T sensor + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + } +} + int calculateMaxPeriod(int updateInterval) { return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.1)) / updateInterval; } \ No newline at end of file From 2ffe0a62aa151609ea6db2993f2be25a3bb452bc Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 00:15:59 +0700 Subject: [PATCH 31/69] Reduce update interval for max period to 50% --- examples/OneOpenAir/OneOpenAir.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index a6bfd04..5244aec 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1176,5 +1176,6 @@ void setMeasurementMaxPeriod() { } int calculateMaxPeriod(int updateInterval) { - return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.1)) / updateInterval; + // 0.5 is 50% reduced interval for max period + return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval; } \ No newline at end of file From f7e1363da916df6316428608a0f7e753317f08ec Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 00:22:50 +0700 Subject: [PATCH 32/69] Rename function --- src/AgValue.cpp | 2 +- src/AgValue.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index f22c943..ef89944 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -4,7 +4,7 @@ #define json_prop_pmFirmware "firmware" -void Measurements::maxUpdate(MeasurementType type, int max) { +void Measurements::maxPeriod(MeasurementType type, int max) { switch (type) { case Temperature: _temperature[0].update.max = max; diff --git a/src/AgValue.h b/src/AgValue.h index adaa6db..3dd8f95 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -58,7 +58,7 @@ public: * @param type the target measurement type to set * @param max the maximum period length */ - void maxUpdate(MeasurementType, int max); + void maxPeriod(MeasurementType, int max); /** * @brief update target measurement type with new value. From 8548d3e9f47857b97e1d1df0d3d65a8ecca8a6b5 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 00:43:04 +0700 Subject: [PATCH 33/69] Optional to debug every measurement update value --- src/AgValue.cpp | 14 ++++++++++---- src/AgValue.h | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index ef89944..0b003c6 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -132,7 +132,9 @@ bool Measurements::update(MeasurementType type, int val, int ch) { // Calculate average based on how many elements on the list temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); - Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + if (_debug) { + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + } return true; } @@ -193,7 +195,9 @@ bool Measurements::update(MeasurementType type, float val, int ch) { // Calculate average based on how many elements on the list temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); - Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + if (_debug) { + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + } return true; } @@ -398,7 +402,7 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, } String result = JSON.stringify(root); - Serial.printf("\n----\n %s \n-----\n", result.c_str()); + Serial.printf("\n---- PAYLOAD\n %s \n-----\n", result.c_str()); return result; } @@ -724,4 +728,6 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem } return pms; -} \ No newline at end of file +} + +void Measurements::setDebug(bool debug) { _debug = debug; } \ No newline at end of file diff --git a/src/AgValue.h b/src/AgValue.h index 3dd8f95..27d98b9 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -106,12 +106,20 @@ public: */ float getFloat(MeasurementType type, int ch = 1); - // TODO: update this to using setter - int bootCount; - + /** + * build json payload for every measurements + */ String toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, Configuration &config); + /** + * Set to true if want to debug every update value + */ + void setDebug(bool debug); + + // TODO: update this to use setter + int bootCount; + private: // Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T FloatValue _temperature[2]; @@ -126,6 +134,8 @@ private: IntegerValue _pm_10[2]; IntegerValue _pm_03_pc[2]; // particle count 0.3 + bool _debug = false; + /** * @brief Get PMS5003 firmware version string * From 63bb5f8ddbcf02234b56a0200c137a438fa60ba2 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 01:49:01 +0700 Subject: [PATCH 34/69] Compensate function use float data type --- examples/OneOpenAir/OneOpenAir.ino | 3 +++ src/AgValue.cpp | 21 ++++++++-------- src/PMS/PMS.cpp | 39 +++++++++++++++++++----------- src/PMS/PMS.h | 2 +- src/PMS/PMS5003.cpp | 10 +++----- src/PMS/PMS5003.h | 2 +- src/PMS/PMS5003T.cpp | 10 +++----- src/PMS/PMS5003T.h | 2 +- 8 files changed, 50 insertions(+), 39 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 5244aec..4db7022 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -169,6 +169,9 @@ void setup() { boardInit(); setMeasurementMaxPeriod(); + // Uncomment below line to print every measurements reading update + // measurements.setDebug(true); + /** Connecting wifi */ bool connectToWifi = false; if (ag->isOne()) { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0b003c6..6e9584e 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -480,9 +480,9 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati // Add pm25 compensated value only if PM2.5 and humidity value is valid if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].update.avg)) { if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].update.avg)) { - int pm25 = ag.pms5003.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); + float pm25 = ag.pms5003.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); if (utils::isValidPm(pm25)) { - indoor["pm02Compensated"] = pm25; + indoor["pm02Compensated"] = ag.round2(pm25); } } } @@ -546,9 +546,10 @@ 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 - int pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); + // TODO: Need to use compensated humidity? + float pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); if (utils::isValidPm(pm25)) { - pms["pm02Compensated"] = pm25; + pms["pm02Compensated"] = ag.round2(pm25); } } } @@ -703,26 +704,26 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem if (compensate) { // Add pm25 compensated value /// First get both channel compensated value - int pm25_comp1 = utils::getInvalidPmValue(); - int pm25_comp2 = utils::getInvalidPmValue(); + float pm25_comp1 = utils::getInvalidPmValue(); + 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"]["pm02Compensated"] = pm25_comp1; + pms["channels"]["1"]["pm02Compensated"] = 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"]["pm02Compensated"] = pm25_comp2; + pms["channels"]["2"]["pm02Compensated"] = 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["pm02Compensated"] = ag.round2((pm25_comp1 / pm25_comp2) / 2.0f); } else if (utils::isValidPm(pm25_comp1)) { - pms["pm02Compensated"] = pm25_comp1; + pms["pm02Compensated"] = ag.round2(pm25_comp1); } else if (utils::isValidPm(pm25_comp2)) { - pms["pm02Compensated"] = pm25_comp2; + pms["pm02Compensated"] = ag.round2(pm25_comp2); } } } diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 4bf4da6..b788dae 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -322,11 +322,12 @@ int PMSBase::pm25ToAQI(int pm02) { * * @param pm25 Raw PM2.5 value * @param humidity Humidity value (%) - * @return int + * @return compensated pm25 value */ -int PMSBase::compensate(int pm25, float humidity) { +float PMSBase::compensate(float pm25, float humidity) { float value; - float fpm25 = pm25; + + // Correct invalid humidity value if (humidity < 0) { humidity = 0; } @@ -334,23 +335,33 @@ int PMSBase::compensate(int pm25, float humidity) { humidity = 100.0f; } - if(pm25 < 30) { /** pm2.5 < 30 */ - value = (fpm25 * 0.524f) - (humidity * 0.0862f) + 5.75f; - } else if(pm25 < 50) { /** 30 <= pm2.5 < 50 */ - value = (0.786f * (fpm25 * 0.05f - 1.5f) + 0.524f * (1.0f - (fpm25 * 0.05f - 1.5f))) * fpm25 - (0.0862f * humidity) + 5.75f; - } else if(pm25 < 210) { /** 50 <= pm2.5 < 210 */ - value = (0.786f * fpm25) - (0.0862f * humidity) + 5.75f; - } else if(pm25 < 260) { /** 210 <= pm2.5 < 260 */ - value = (0.69f * (fpm25 * 0.02f - 4.2f) + 0.786f * (1.0f - (fpm25 * 0.02f - 4.2f))) * fpm25 - (0.0862f * humidity * (1.0f - (fpm25 * 0.02f - 4.2f))) + (2.966f * (fpm25 * 0.02f - 4.2f)) + (5.75f * (1.0f - (fpm25 * 0.02f - 4.2f))) + (8.84f * (1.e-4) * fpm25 * fpm25 * (fpm25 * 0.02f - 4.2f)); + // If its already 0, do not proceed + if (pm25 == 0) { + return 0.0; + } + + if (pm25 < 30) { /** pm2.5 < 30 */ + value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f; + } else if (pm25 < 50) { /** 30 <= pm2.5 < 50 */ + value = (0.786f * (pm25 * 0.05f - 1.5f) + 0.524f * (1.0f - (pm25 * 0.05f - 1.5f))) * pm25 - + (0.0862f * humidity) + 5.75f; + } else if (pm25 < 210) { /** 50 <= pm2.5 < 210 */ + value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f; + } else if (pm25 < 260) { /** 210 <= pm2.5 < 260 */ + value = (0.69f * (pm25 * 0.02f - 4.2f) + 0.786f * (1.0f - (pm25 * 0.02f - 4.2f))) * pm25 - + (0.0862f * humidity * (1.0f - (pm25 * 0.02f - 4.2f))) + + (2.966f * (pm25 * 0.02f - 4.2f)) + (5.75f * (1.0f - (pm25 * 0.02f - 4.2f))) + + (8.84f * (1.e-4) * pm25 * pm25 * (pm25 * 0.02f - 4.2f)); } else { /** 260 <= pm2.5 */ - value = 2.966f + (0.69f * fpm25) + (8.84f * (1.e-4) * fpm25 * fpm25); + value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25 * pm25); } + // No negative value for pm2.5 if (value < 0) { - value = 0; + return 0.0; } - return (int)value; + return value; } /** diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index d381d69..460a87a 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -39,7 +39,7 @@ public: uint8_t getErrorCode(void); int pm25ToAQI(int pm02); - int compensate(int pm25, float humidity); + float compensate(float pm25, float humidity); private: static const uint8_t package_size = 32; diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 7488bd6..7b021bf 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -118,16 +118,14 @@ int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); } /** * @brief Correct PM2.5 - * + * * Reference formula: https://www.airgradient.com/documentation/correction-algorithms/ - * + * * @param pm25 PM2.5 raw value * @param humidity Humidity value - * @return int + * @return compensated value in float */ -int PMS5003::compensate(int pm25, float humidity) { - return pms.compensate(pm25, humidity); -} +float PMS5003::compensate(float pm25, float humidity) { return pms.compensate(pm25, humidity); } /** * @brief Get sensor firmware version diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index 5bfadde..d8fc862 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -30,7 +30,7 @@ public: int getPm10Ae(void); int getPm03ParticleCount(void); int convertPm25ToUsAqi(int pm25); - int compensate(int pm25, float humidity); + float compensate(float pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); bool connected(void); diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index 4294ec5..38ce259 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -165,16 +165,14 @@ float PMS5003T::getRelativeHumidity(void) { /** * @brief Correct PM2.5 - * + * * Reference formula: https://www.airgradient.com/documentation/correction-algorithms/ - * + * * @param pm25 PM2.5 raw value * @param humidity Humidity value - * @return int + * @return compensated value */ -int PMS5003T::compensate(int pm25, float humidity) { - return pms.compensate(pm25, humidity); -} +float PMS5003T::compensate(float pm25, float humidity) { return pms.compensate(pm25, humidity); } /** * @brief Get module(s) firmware version diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 7c3e0df..eac4a8d 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -35,7 +35,7 @@ public: int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); - int compensate(int pm25, float humidity); + float compensate(float pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); bool connected(void); From c3068be6e90a833d74e660f18ec26c07c8e01bc5 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 22:00:47 +0700 Subject: [PATCH 35/69] Fix calculation PPT compensated PM2.5 --- src/AgValue.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 6e9584e..3a47d3e 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -546,7 +546,6 @@ 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 - // TODO: Need to use compensated humidity? float pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); if (utils::isValidPm(pm25)) { pms["pm02Compensated"] = ag.round2(pm25); @@ -719,7 +718,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem /// 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["pm02Compensated"] = ag.round2((pm25_comp1 / pm25_comp2) / 2.0f); + pms["pm02Compensated"] = ag.round2((pm25_comp1 + pm25_comp2) / 2.0f); } else if (utils::isValidPm(pm25_comp1)) { pms["pm02Compensated"] = ag.round2(pm25_comp1); } else if (utils::isValidPm(pm25_comp2)) { From 8a87b865e66c7281837e63649767eec6f1e61366 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Mon, 21 Oct 2024 22:37:44 +0700 Subject: [PATCH 36/69] Handle consecutive invalid value update Set measurements type average value to invalid when invalidCounter reached max period --- src/AgValue.cpp | 26 ++++++++++++++++++++------ src/AgValue.h | 4 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 3a47d3e..d728f56 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -111,9 +111,16 @@ bool Measurements::update(MeasurementType type, int val, int ch) { if (val == invalidValue) { temporary->update.invalidCounter++; - // TODO: Need to check if its more than threshold, to create some indication. Maybe reference to - // max element? - return false; + if (temporary->update.invalidCounter >= temporary->update.max) { + Serial.printf("%s{%d} invalid value update counter reached (%dx)! Setting its average value " + "to invalid!", + measurementTypeStr(type), ch, temporary->update.max); + temporary->update.avg = invalidValue; + return false; + } + + // Still consider updating value to valid + return true; } // Reset invalid counter when update new valid value @@ -174,9 +181,16 @@ bool Measurements::update(MeasurementType type, float val, int ch) { if (val == invalidValue) { temporary->update.invalidCounter++; - // TODO: Need to check if its more than threshold, to create some indication. Maybe reference to - // max element? - return false; + if (temporary->update.invalidCounter >= temporary->update.max) { + Serial.printf("%s{%d} invalid value update counter reached (%dx)! Setting its average value " + "to invalid!", + measurementTypeStr(type), ch, temporary->update.max); + temporary->update.avg = invalidValue; + return false; + } + + // Still consider updating value to valid + return true; } // Reset invalid counter when update new valid value diff --git a/src/AgValue.h b/src/AgValue.h index 27d98b9..236589a 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -69,7 +69,7 @@ public: * @param val (int) the new value * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) - * @return false if new value invalid consecutively reach threshold + * @return false if new value invalid consecutively reach threshold (max period) * @return true otherwise */ bool update(MeasurementType type, int val, int ch = 1); @@ -83,7 +83,7 @@ public: * @param val (float) the new value * @param ch (int) the MeasurementType channel, not every MeasurementType has more than 1 channel. * Currently maximum channel is 2. Default: 1 (channel 1) - * @return false if new value invalid consecutively reach threshold + * @return false if new value invalid consecutively reach threshold (max period) * @return true otherwise */ bool update(MeasurementType type, float val, int ch = 1); From 83aa6a450227282aeca67f5fcfe1282a283ff204 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 00:11:58 +0700 Subject: [PATCH 37/69] Apply for other monitor series --- examples/BASIC/BASIC.ino | 134 ++++++++++-------- examples/BASIC/LocalServer.cpp | 5 +- examples/BASIC/OpenMetrics.cpp | 39 +++-- .../DiyProIndoorV3_3/DiyProIndoorV3_3.ino | 134 ++++++++++-------- examples/DiyProIndoorV3_3/LocalServer.cpp | 5 +- examples/DiyProIndoorV3_3/OpenMetrics.cpp | 39 +++-- .../DiyProIndoorV4_2/DiyProIndoorV4_2.ino | 134 ++++++++++-------- examples/DiyProIndoorV4_2/LocalServer.cpp | 5 +- examples/DiyProIndoorV4_2/OpenMetrics.cpp | 39 +++-- examples/OneOpenAir/OneOpenAir.ino | 22 +-- src/AgValue.h | 10 +- 11 files changed, 303 insertions(+), 263 deletions(-) diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index b3be046..66daa1c 100644 --- a/examples/BASIC/BASIC.ino +++ b/examples/BASIC/BASIC.ino @@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */ #define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */ #define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */ -#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */ +#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */ #define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */ -#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */ static AirGradient ag(DIY_BASIC); static Configuration configuration(Serial); @@ -68,7 +67,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector); static MqttClient mqttClient(Serial); -static int getCO2FailCount = 0; static AgFirmwareMode fwMode = FW_MODE_I_BASIC_40PS; static String fwNewVersion; @@ -90,6 +88,8 @@ static void wdgFeedUpdate(void); static bool sgp41Init(void); static void wifiFactoryConfigure(void); static void mqttHandle(void); +static int calculateMaxPeriod(int updateInterval); +static void setMeasurementMaxPeriod(); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -130,6 +130,10 @@ void setup() { /** Init sensor */ boardInit(); + setMeasurementMaxPeriod(); + + // Uncomment below line to print every measurements reading update + // measurements.setDebug(true); /** Connecting wifi */ bool connectToWifi = false; @@ -230,17 +234,16 @@ void loop() { } static void co2Update(void) { + if (!configuration.hasSensorS8) { + // Device don't have S8 sensor + return; + } + int value = ag.s8.getCo2(); if (utils::isValidCO2(value)) { - measurements.CO2 = value; - getCO2FailCount = 0; - Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); + measurements.update(Measurements::CO2, value); } else { - getCO2FailCount++; - Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount); - if (getCO2FailCount >= 3) { - measurements.CO2 = utils::getInvalidCO2(); - } + measurements.update(Measurements::CO2, utils::getInvalidCO2()); } } @@ -313,8 +316,7 @@ static void mqttHandle(void) { } if (mqttClient.isConnected()) { - String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration); String topic = "airgradient/readings/" + ag.deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) { Serial.println("MQTT sync success"); @@ -490,46 +492,27 @@ static void oledDisplaySchedule(void) { } static void updateTvoc(void) { - measurements.TVOC = ag.sgp41.getTvocIndex(); - measurements.TVOCRaw = ag.sgp41.getTvocRaw(); - measurements.NOx = ag.sgp41.getNoxIndex(); - measurements.NOxRaw = ag.sgp41.getNoxRaw(); + if (!configuration.hasSensorSGP) { + return; + } - Serial.println(); - Serial.printf("TVOC index: %d\r\n", measurements.TVOC); - Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); - Serial.printf("NOx index: %d\r\n", measurements.NOx); - Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); + measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex()); + measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw()); + measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex()); + measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw()); } static void updatePm(void) { if (ag.pms5003.connected()) { - measurements.pm01_1 = ag.pms5003.getPm01Ae(); - measurements.pm25_1 = ag.pms5003.getPm25Ae(); - measurements.pm10_1 = ag.pms5003.getPm10Ae(); - measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount(); - - Serial.println(); - Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); - 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(); + measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae()); + measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae()); + measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae()); + measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount()); } else { - ag.pms5003.updateFailCount(); - 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(); - measurements.pm10_1 = utils::getInvalidPmValue(); - measurements.pm03PCount_1 = utils::getInvalidPmValue(); - } - - if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) { - Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax()); - ESP.restart(); - } + measurements.update(Measurements::PM01, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10, utils::getInvalidPmValue()); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); } } @@ -540,8 +523,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( @@ -552,26 +534,54 @@ static void sendDataToServer(void) { } static void tempHumUpdate(void) { - delay(100); if (ag.sht.measure()) { - measurements.Temperature = ag.sht.getTemperature(); - measurements.Humidity = ag.sht.getRelativeHumidity(); + float temp = ag.sht.getTemperature(); + float rhum = ag.sht.getRelativeHumidity(); - Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature); - Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity); - Serial.printf("Temperature compensated in C: %0.2f\r\n", - measurements.Temperature); - Serial.printf("Relative Humidity compensated: %d\r\n", - measurements.Humidity); + measurements.update(Measurements::Temperature, temp); + measurements.update(Measurements::Humidity, rhum); // Update compensation temperature and humidity for SGP41 if (configuration.hasSensorSGP) { - ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature, - measurements.Humidity); + ag.sgp41.setCompensationTemperatureHumidity(temp, rhum); } } else { + measurements.update(Measurements::Temperature, utils::getInvalidTemperature()); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); - measurements.Temperature = utils::getInvalidTemperature(); - measurements.Humidity = utils::getInvalidHumidity(); } } + +/* Set max period for each measurement type based on sensor update interval*/ +void setMeasurementMaxPeriod() { + /// Max period for S8 sensors measurements + measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements + measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + /// Max period for PMS sensors measurements + measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + // Temperature and Humidity + if (configuration.hasSensorSHT) { + /// Max period for SHT sensors measurements + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + } else { + /// Temp and hum data retrieved from PMS5003T sensor + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + } +} + +int calculateMaxPeriod(int updateInterval) { + // 0.5 is 50% reduced interval for max period + return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval; +} \ No newline at end of file diff --git a/examples/BASIC/LocalServer.cpp b/examples/BASIC/LocalServer.cpp index 8970ece..61a6dfb 100644 --- a/examples/BASIC/LocalServer.cpp +++ b/examples/BASIC/LocalServer.cpp @@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) { } void LocalServer::_GET_measure(void) { - server.send( - 200, "application/json", - measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config)); + String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config); + server.send(200, "application/json", toSend); } void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; } diff --git a/examples/BASIC/OpenMetrics.cpp b/examples/BASIC/OpenMetrics.cpp index cffd2c6..ddf2d4c 100644 --- a/examples/BASIC/OpenMetrics.cpp +++ b/examples/BASIC/OpenMetrics.cpp @@ -73,19 +73,30 @@ String OpenMetrics::getPayload(void) { int pm03PCount = utils::getInvalidPmValue(); int atmpCompensated = utils::getInvalidTemperature(); int ahumCompensated = utils::getInvalidHumidity(); + int tvoc = utils::getInvalidVOC(); + int tvoc_raw = utils::getInvalidVOC(); + int nox = utils::getInvalidNOx(); + int nox_raw = utils::getInvalidNOx(); if (config.hasSensorSHT) { - _temp = measure.Temperature; - _hum = measure.Humidity; + _temp = measure.getFloat(Measurements::Temperature); + _hum = measure.getFloat(Measurements::Humidity); atmpCompensated = _temp; ahumCompensated = _hum; } if (config.hasSensorPMS1) { - pm01 = measure.pm01_1; - pm25 = measure.pm25_1; - pm10 = measure.pm10_1; - pm03PCount = measure.pm03PCount_1; + pm01 = measure.get(Measurements::PM01); + pm25 = measure.get(Measurements::PM25); + pm10 = measure.get(Measurements::PM10); + pm03PCount = measure.get(Measurements::PM03_PC); + } + + if (config.hasSensorSGP) { + tvoc = measure.get(Measurements::TVOC); + tvoc_raw = measure.get(Measurements::TVOCRaw); + nox = measure.get(Measurements::NOx); + nox_raw = measure.get(Measurements::NOxRaw); } if (config.hasSensorPMS1) { @@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) { } if (config.hasSensorSGP) { - if (utils::isValidVOC(measure.TVOC)) { + if (utils::isValidVOC(tvoc)) { add_metric("tvoc_index", "The processed Total Volatile Organic Compounds (TVOC) index " "as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOC)); + add_metric_point("", String(tvoc)); } - if (utils::isValidVOC(measure.TVOCRaw)) { + if (utils::isValidVOC(tvoc_raw)) { add_metric("tvoc_raw", "The raw input value to the Total Volatile Organic Compounds " "(TVOC) index as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOCRaw)); + add_metric_point("", String(tvoc_raw)); } - if (utils::isValidNOx(measure.NOx)) { + if (utils::isValidNOx(nox)) { add_metric("nox_index", "The processed Nitrous Oxide (NOx) index as measured by the " "AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOx)); + add_metric_point("", String(nox)); } - if (utils::isValidNOx(measure.NOxRaw)) { + if (utils::isValidNOx(nox_raw)) { add_metric("nox_raw", "The raw input value to the Nitrous Oxide (NOx) index as " "measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOxRaw)); + add_metric_point("", String(nox_raw)); } } diff --git a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino index b67e262..dd87cb4 100644 --- a/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino +++ b/examples/DiyProIndoorV3_3/DiyProIndoorV3_3.ino @@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */ #define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */ #define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */ -#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */ +#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */ #define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */ -#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */ static AirGradient ag(DIY_PRO_INDOOR_V3_3); static Configuration configuration(Serial); @@ -68,7 +67,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector); static MqttClient mqttClient(Serial); -static int getCO2FailCount = 0; static AgFirmwareMode fwMode = FW_MODE_I_33PS; static String fwNewVersion; @@ -90,6 +88,8 @@ static void wdgFeedUpdate(void); static bool sgp41Init(void); static void wifiFactoryConfigure(void); static void mqttHandle(void); +static int calculateMaxPeriod(int updateInterval); +static void setMeasurementMaxPeriod(); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -130,6 +130,10 @@ void setup() { /** Init sensor */ boardInit(); + setMeasurementMaxPeriod(); + + // Uncomment below line to print every measurements reading update + // measurements.setDebug(true); /** Connecting wifi */ bool connectToWifi = false; @@ -228,17 +232,16 @@ void loop() { } static void co2Update(void) { + if (!configuration.hasSensorS8) { + // Device don't have S8 sensor + return; + } + int value = ag.s8.getCo2(); if (utils::isValidCO2(value)) { - measurements.CO2 = value; - getCO2FailCount = 0; - Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); + measurements.update(Measurements::CO2, value); } else { - getCO2FailCount++; - Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount); - if (getCO2FailCount >= 3) { - measurements.CO2 = utils::getInvalidCO2(); - } + measurements.update(Measurements::CO2, utils::getInvalidCO2()); } } @@ -370,8 +373,7 @@ static void mqttHandle(void) { } if (mqttClient.isConnected()) { - String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration); String topic = "airgradient/readings/" + ag.deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) { Serial.println("MQTT sync success"); @@ -542,46 +544,27 @@ static void oledDisplaySchedule(void) { } static void updateTvoc(void) { - measurements.TVOC = ag.sgp41.getTvocIndex(); - measurements.TVOCRaw = ag.sgp41.getTvocRaw(); - measurements.NOx = ag.sgp41.getNoxIndex(); - measurements.NOxRaw = ag.sgp41.getNoxRaw(); + if (!configuration.hasSensorSGP) { + return; + } - Serial.println(); - Serial.printf("TVOC index: %d\r\n", measurements.TVOC); - Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); - Serial.printf("NOx index: %d\r\n", measurements.NOx); - Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); + measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex()); + measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw()); + measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex()); + measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw()); } static void updatePm(void) { if (ag.pms5003.connected()) { - measurements.pm01_1 = ag.pms5003.getPm01Ae(); - measurements.pm25_1 = ag.pms5003.getPm25Ae(); - measurements.pm10_1 = ag.pms5003.getPm10Ae(); - measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount(); - - Serial.println(); - Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); - 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(); + measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae()); + measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae()); + measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae()); + measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount()); } else { - ag.pms5003.updateFailCount(); - 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(); - measurements.pm10_1 = utils::getInvalidPmValue(); - measurements.pm03PCount_1 = utils::getInvalidPmValue(); - } - - if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) { - Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax()); - ESP.restart(); - } + measurements.update(Measurements::PM01, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10, utils::getInvalidPmValue()); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); } } @@ -592,8 +575,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( @@ -604,26 +586,54 @@ static void sendDataToServer(void) { } static void tempHumUpdate(void) { - delay(100); if (ag.sht.measure()) { - measurements.Temperature = ag.sht.getTemperature(); - measurements.Humidity = ag.sht.getRelativeHumidity(); + float temp = ag.sht.getTemperature(); + float rhum = ag.sht.getRelativeHumidity(); - Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature); - Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity); - Serial.printf("Temperature compensated in C: %0.2f\r\n", - measurements.Temperature); - Serial.printf("Relative Humidity compensated: %d\r\n", - measurements.Humidity); + measurements.update(Measurements::Temperature, temp); + measurements.update(Measurements::Humidity, rhum); // Update compensation temperature and humidity for SGP41 if (configuration.hasSensorSGP) { - ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature, - measurements.Humidity); + ag.sgp41.setCompensationTemperatureHumidity(temp, rhum); } } else { + measurements.update(Measurements::Temperature, utils::getInvalidTemperature()); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); - measurements.Temperature = utils::getInvalidTemperature(); - measurements.Humidity = utils::getInvalidHumidity(); } } + +/* Set max period for each measurement type based on sensor update interval*/ +void setMeasurementMaxPeriod() { + /// Max period for S8 sensors measurements + measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements + measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + /// Max period for PMS sensors measurements + measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + // Temperature and Humidity + if (configuration.hasSensorSHT) { + /// Max period for SHT sensors measurements + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + } else { + /// Temp and hum data retrieved from PMS5003T sensor + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + } +} + +int calculateMaxPeriod(int updateInterval) { + // 0.5 is 50% reduced interval for max period + return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval; +} \ No newline at end of file diff --git a/examples/DiyProIndoorV3_3/LocalServer.cpp b/examples/DiyProIndoorV3_3/LocalServer.cpp index 8970ece..61a6dfb 100644 --- a/examples/DiyProIndoorV3_3/LocalServer.cpp +++ b/examples/DiyProIndoorV3_3/LocalServer.cpp @@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) { } void LocalServer::_GET_measure(void) { - server.send( - 200, "application/json", - measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config)); + String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config); + server.send(200, "application/json", toSend); } void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; } diff --git a/examples/DiyProIndoorV3_3/OpenMetrics.cpp b/examples/DiyProIndoorV3_3/OpenMetrics.cpp index cffd2c6..ddf2d4c 100644 --- a/examples/DiyProIndoorV3_3/OpenMetrics.cpp +++ b/examples/DiyProIndoorV3_3/OpenMetrics.cpp @@ -73,19 +73,30 @@ String OpenMetrics::getPayload(void) { int pm03PCount = utils::getInvalidPmValue(); int atmpCompensated = utils::getInvalidTemperature(); int ahumCompensated = utils::getInvalidHumidity(); + int tvoc = utils::getInvalidVOC(); + int tvoc_raw = utils::getInvalidVOC(); + int nox = utils::getInvalidNOx(); + int nox_raw = utils::getInvalidNOx(); if (config.hasSensorSHT) { - _temp = measure.Temperature; - _hum = measure.Humidity; + _temp = measure.getFloat(Measurements::Temperature); + _hum = measure.getFloat(Measurements::Humidity); atmpCompensated = _temp; ahumCompensated = _hum; } if (config.hasSensorPMS1) { - pm01 = measure.pm01_1; - pm25 = measure.pm25_1; - pm10 = measure.pm10_1; - pm03PCount = measure.pm03PCount_1; + pm01 = measure.get(Measurements::PM01); + pm25 = measure.get(Measurements::PM25); + pm10 = measure.get(Measurements::PM10); + pm03PCount = measure.get(Measurements::PM03_PC); + } + + if (config.hasSensorSGP) { + tvoc = measure.get(Measurements::TVOC); + tvoc_raw = measure.get(Measurements::TVOCRaw); + nox = measure.get(Measurements::NOx); + nox_raw = measure.get(Measurements::NOxRaw); } if (config.hasSensorPMS1) { @@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) { } if (config.hasSensorSGP) { - if (utils::isValidVOC(measure.TVOC)) { + if (utils::isValidVOC(tvoc)) { add_metric("tvoc_index", "The processed Total Volatile Organic Compounds (TVOC) index " "as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOC)); + add_metric_point("", String(tvoc)); } - if (utils::isValidVOC(measure.TVOCRaw)) { + if (utils::isValidVOC(tvoc_raw)) { add_metric("tvoc_raw", "The raw input value to the Total Volatile Organic Compounds " "(TVOC) index as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOCRaw)); + add_metric_point("", String(tvoc_raw)); } - if (utils::isValidNOx(measure.NOx)) { + if (utils::isValidNOx(nox)) { add_metric("nox_index", "The processed Nitrous Oxide (NOx) index as measured by the " "AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOx)); + add_metric_point("", String(nox)); } - if (utils::isValidNOx(measure.NOxRaw)) { + if (utils::isValidNOx(nox_raw)) { add_metric("nox_raw", "The raw input value to the Nitrous Oxide (NOx) index as " "measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOxRaw)); + add_metric_point("", String(nox_raw)); } } diff --git a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino index bac20a5..7a2a800 100644 --- a/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino +++ b/examples/DiyProIndoorV4_2/DiyProIndoorV4_2.ino @@ -49,9 +49,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License #define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */ #define SENSOR_CO2_UPDATE_INTERVAL 4000 /** ms */ #define SENSOR_PM_UPDATE_INTERVAL 2000 /** ms */ -#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 2000 /** ms */ +#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 6000 /** ms */ #define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */ -#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */ static AirGradient ag(DIY_PRO_INDOOR_V4_2); static Configuration configuration(Serial); @@ -69,7 +68,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration, static MqttClient mqttClient(Serial); static uint32_t factoryBtnPressTime = 0; -static int getCO2FailCount = 0; static AgFirmwareMode fwMode = FW_MODE_I_42PS; static String fwNewVersion; @@ -91,6 +89,8 @@ static void wdgFeedUpdate(void); static bool sgp41Init(void); static void wifiFactoryConfigure(void); static void mqttHandle(void); +static int calculateMaxPeriod(int updateInterval); +static void setMeasurementMaxPeriod(); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, oledDisplaySchedule); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -131,6 +131,10 @@ void setup() { /** Init sensor */ boardInit(); + setMeasurementMaxPeriod(); + + // Uncomment below line to print every measurements reading update + // measurements.setDebug(true); /** Connecting wifi */ bool connectToWifi = false; @@ -255,17 +259,16 @@ void loop() { } static void co2Update(void) { + if (!configuration.hasSensorS8) { + // Device don't have S8 sensor + return; + } + int value = ag.s8.getCo2(); if (utils::isValidCO2(value)) { - measurements.CO2 = value; - getCO2FailCount = 0; - Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); + measurements.update(Measurements::CO2, value); } else { - getCO2FailCount++; - Serial.printf("Get CO2 failed: %d\r\n", getCO2FailCount); - if (getCO2FailCount >= 3) { - measurements.CO2 = utils::getInvalidCO2(); - } + measurements.update(Measurements::CO2, utils::getInvalidCO2()); } } @@ -393,8 +396,7 @@ static void mqttHandle(void) { } if (mqttClient.isConnected()) { - String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String payload = measurements.toString(true, fwMode, wifiConnector.RSSI(), ag, configuration); String topic = "airgradient/readings/" + ag.deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), payload.length())) { Serial.println("MQTT sync success"); @@ -583,46 +585,27 @@ static void oledDisplaySchedule(void) { } static void updateTvoc(void) { - measurements.TVOC = ag.sgp41.getTvocIndex(); - measurements.TVOCRaw = ag.sgp41.getTvocRaw(); - measurements.NOx = ag.sgp41.getNoxIndex(); - measurements.NOxRaw = ag.sgp41.getNoxRaw(); + if (!configuration.hasSensorSGP) { + return; + } - Serial.println(); - Serial.printf("TVOC index: %d\r\n", measurements.TVOC); - Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); - Serial.printf("NOx index: %d\r\n", measurements.NOx); - Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); + measurements.update(Measurements::TVOC, ag.sgp41.getTvocIndex()); + measurements.update(Measurements::TVOCRaw, ag.sgp41.getTvocRaw()); + measurements.update(Measurements::NOx, ag.sgp41.getNoxIndex()); + measurements.update(Measurements::NOxRaw, ag.sgp41.getNoxRaw()); } static void updatePm(void) { if (ag.pms5003.connected()) { - measurements.pm01_1 = ag.pms5003.getPm01Ae(); - measurements.pm25_1 = ag.pms5003.getPm25Ae(); - measurements.pm10_1 = ag.pms5003.getPm10Ae(); - measurements.pm03PCount_1 = ag.pms5003.getPm03ParticleCount(); - - Serial.println(); - Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); - 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(); + measurements.update(Measurements::PM01, ag.pms5003.getPm01Ae()); + measurements.update(Measurements::PM25, ag.pms5003.getPm25Ae()); + measurements.update(Measurements::PM10, ag.pms5003.getPm10Ae()); + measurements.update(Measurements::PM03_PC, ag.pms5003.getPm03ParticleCount()); } else { - ag.pms5003.updateFailCount(); - 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(); - measurements.pm10_1 = utils::getInvalidPmValue(); - measurements.pm03PCount_1 = utils::getInvalidPmValue(); - } - - if(ag.pms5003.getFailCount() >= ag.pms5003.getFailCountMax()) { - Serial.printf("PMS failure count reach to max set %d, restarting...", ag.pms5003.getFailCountMax()); - ESP.restart(); - } + measurements.update(Measurements::PM01, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10, utils::getInvalidPmValue()); + measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); } } @@ -633,8 +616,7 @@ static void sendDataToServer(void) { return; } - String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), - &ag, &configuration); + String syncData = measurements.toString(false, fwMode, wifiConnector.RSSI(), ag, configuration); if (apiClient.postToServer(syncData)) { Serial.println(); Serial.println( @@ -645,26 +627,54 @@ static void sendDataToServer(void) { } static void tempHumUpdate(void) { - delay(100); if (ag.sht.measure()) { - measurements.Temperature = ag.sht.getTemperature(); - measurements.Humidity = ag.sht.getRelativeHumidity(); + float temp = ag.sht.getTemperature(); + float rhum = ag.sht.getRelativeHumidity(); - Serial.printf("Temperature in C: %0.2f\r\n", measurements.Temperature); - Serial.printf("Relative Humidity: %d\r\n", measurements.Humidity); - Serial.printf("Temperature compensated in C: %0.2f\r\n", - measurements.Temperature); - Serial.printf("Relative Humidity compensated: %d\r\n", - measurements.Humidity); + measurements.update(Measurements::Temperature, temp); + measurements.update(Measurements::Humidity, rhum); // Update compensation temperature and humidity for SGP41 if (configuration.hasSensorSGP) { - ag.sgp41.setCompensationTemperatureHumidity(measurements.Temperature, - measurements.Humidity); + ag.sgp41.setCompensationTemperatureHumidity(temp, rhum); } } else { + measurements.update(Measurements::Temperature, utils::getInvalidTemperature()); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); - measurements.Temperature = utils::getInvalidTemperature(); - measurements.Humidity = utils::getInvalidHumidity(); } } + +/* Set max period for each measurement type based on sensor update interval*/ +void setMeasurementMaxPeriod() { + /// Max period for S8 sensors measurements + measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements + measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + /// Max period for PMS sensors measurements + measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + // Temperature and Humidity + if (configuration.hasSensorSHT) { + /// Max period for SHT sensors measurements + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, + calculateMaxPeriod(SENSOR_TEMP_HUM_UPDATE_INTERVAL)); + } else { + /// Temp and hum data retrieved from PMS5003T sensor + measurements.maxPeriod(Measurements::Temperature, + calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + measurements.maxPeriod(Measurements::Humidity, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + } +} + +int calculateMaxPeriod(int updateInterval) { + // 0.5 is 50% reduced interval for max period + return (SERVER_SYNC_INTERVAL - (SERVER_SYNC_INTERVAL * 0.5)) / updateInterval; +} \ No newline at end of file diff --git a/examples/DiyProIndoorV4_2/LocalServer.cpp b/examples/DiyProIndoorV4_2/LocalServer.cpp index 8970ece..61a6dfb 100644 --- a/examples/DiyProIndoorV4_2/LocalServer.cpp +++ b/examples/DiyProIndoorV4_2/LocalServer.cpp @@ -53,9 +53,8 @@ void LocalServer::_GET_metrics(void) { } void LocalServer::_GET_measure(void) { - server.send( - 200, "application/json", - measure.toString(true, fwMode, wifiConnector.RSSI(), ag, &config)); + String toSend = measure.toString(true, fwMode, wifiConnector.RSSI(), *ag, config); + server.send(200, "application/json", toSend); } void LocalServer::setFwMode(AgFirmwareMode fwMode) { this->fwMode = fwMode; } diff --git a/examples/DiyProIndoorV4_2/OpenMetrics.cpp b/examples/DiyProIndoorV4_2/OpenMetrics.cpp index cffd2c6..ddf2d4c 100644 --- a/examples/DiyProIndoorV4_2/OpenMetrics.cpp +++ b/examples/DiyProIndoorV4_2/OpenMetrics.cpp @@ -73,19 +73,30 @@ String OpenMetrics::getPayload(void) { int pm03PCount = utils::getInvalidPmValue(); int atmpCompensated = utils::getInvalidTemperature(); int ahumCompensated = utils::getInvalidHumidity(); + int tvoc = utils::getInvalidVOC(); + int tvoc_raw = utils::getInvalidVOC(); + int nox = utils::getInvalidNOx(); + int nox_raw = utils::getInvalidNOx(); if (config.hasSensorSHT) { - _temp = measure.Temperature; - _hum = measure.Humidity; + _temp = measure.getFloat(Measurements::Temperature); + _hum = measure.getFloat(Measurements::Humidity); atmpCompensated = _temp; ahumCompensated = _hum; } if (config.hasSensorPMS1) { - pm01 = measure.pm01_1; - pm25 = measure.pm25_1; - pm10 = measure.pm10_1; - pm03PCount = measure.pm03PCount_1; + pm01 = measure.get(Measurements::PM01); + pm25 = measure.get(Measurements::PM25); + pm10 = measure.get(Measurements::PM10); + pm03PCount = measure.get(Measurements::PM03_PC); + } + + if (config.hasSensorSGP) { + tvoc = measure.get(Measurements::TVOC); + tvoc_raw = measure.get(Measurements::TVOCRaw); + nox = measure.get(Measurements::NOx); + nox_raw = measure.get(Measurements::NOxRaw); } if (config.hasSensorPMS1) { @@ -120,33 +131,33 @@ String OpenMetrics::getPayload(void) { } if (config.hasSensorSGP) { - if (utils::isValidVOC(measure.TVOC)) { + if (utils::isValidVOC(tvoc)) { add_metric("tvoc_index", "The processed Total Volatile Organic Compounds (TVOC) index " "as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOC)); + add_metric_point("", String(tvoc)); } - if (utils::isValidVOC(measure.TVOCRaw)) { + if (utils::isValidVOC(tvoc_raw)) { add_metric("tvoc_raw", "The raw input value to the Total Volatile Organic Compounds " "(TVOC) index as measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.TVOCRaw)); + add_metric_point("", String(tvoc_raw)); } - if (utils::isValidNOx(measure.NOx)) { + if (utils::isValidNOx(nox)) { add_metric("nox_index", "The processed Nitrous Oxide (NOx) index as measured by the " "AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOx)); + add_metric_point("", String(nox)); } - if (utils::isValidNOx(measure.NOxRaw)) { + if (utils::isValidNOx(nox_raw)) { add_metric("nox_raw", "The raw input value to the Nitrous Oxide (NOx) index as " "measured by the AirGradient SGP sensor", "gauge"); - add_metric_point("", String(measure.NOxRaw)); + add_metric_point("", String(nox_raw)); } } diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 4db7022..ee34f76 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -89,7 +89,6 @@ static LocalServer localServer(Serial, openMetrics, measurements, configuration, wifiConnector); static uint32_t factoryBtnPressTime = 0; -static int getCO2FailCount = 0; static AgFirmwareMode fwMode = FW_MODE_I_9PSL; static bool ledBarButtonTest = false; @@ -324,14 +323,13 @@ void loop() { static void co2Update(void) { if (!configuration.hasSensorS8) { - // Device don't have SHT sensor + // Device don't have S8 sensor return; } int value = ag->s8.getCo2(); if (utils::isValidCO2(value)) { measurements.update(Measurements::CO2, value); - // Serial.printf("CO2 (ppm): %d\r\n", measurements.CO2); } else { measurements.update(Measurements::CO2, utils::getInvalidCO2()); } @@ -996,12 +994,6 @@ static void updateTvoc(void) { measurements.update(Measurements::TVOCRaw, ag->sgp41.getTvocRaw()); measurements.update(Measurements::NOx, ag->sgp41.getNoxIndex()); measurements.update(Measurements::NOxRaw, ag->sgp41.getNoxRaw()); - - // Serial.println(); - // Serial.printf("TVOC index: %d\r\n", measurements.TVOC); - // Serial.printf("TVOC raw: %d\r\n", measurements.TVOCRaw); - // Serial.printf("NOx index: %d\r\n", measurements.NOx); - // Serial.printf("NOx raw: %d\r\n", measurements.NOxRaw); } static void updatePMS5003() { @@ -1010,13 +1002,6 @@ static void updatePMS5003() { measurements.update(Measurements::PM25, ag->pms5003.getPm25Ae()); measurements.update(Measurements::PM10, ag->pms5003.getPm10Ae()); measurements.update(Measurements::PM03_PC, ag->pms5003.getPm03ParticleCount()); - - // Serial.println(); - // Serial.printf("PM1 ug/m3: %d\r\n", measurements.pm01_1); - // 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()); } else { measurements.update(Measurements::PM01, utils::getInvalidPmValue()); measurements.update(Measurements::PM25, utils::getInvalidPmValue()); @@ -1133,11 +1118,6 @@ static void tempHumUpdate(void) { measurements.update(Measurements::Temperature, temp); measurements.update(Measurements::Humidity, rhum); - // Serial.printf("Temperature in C: %0.2f\n", temp); - // Serial.printf("Relative Humidity: %d\n", rhum); - // Serial.printf("Temperature compensated in C: %0.2f\n", temp); - // Serial.printf("Relative Humidity compensated: %0.2f\n", rhum); - // Update compensation temperature and humidity for SGP41 if (configuration.hasSensorSGP) { ag->sgp41.setCompensationTemperatureHumidity(temp, rhum); diff --git a/src/AgValue.h b/src/AgValue.h index 236589a..8c26bf2 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -42,14 +42,14 @@ public: Temperature, Humidity, CO2, - TVOC, + TVOC, // index value TVOCRaw, - NOx, + NOx, // index value NOxRaw, PM25, PM01, PM10, - PM03_PC, + PM03_PC, // Particle count }; /** @@ -125,9 +125,9 @@ private: FloatValue _temperature[2]; FloatValue _humidity[2]; IntegerValue _co2; - IntegerValue _tvoc; + IntegerValue _tvoc; // Index value IntegerValue _tvoc_raw; - IntegerValue _nox; + IntegerValue _nox; // Index value IntegerValue _nox_raw; IntegerValue _pm_25[2]; IntegerValue _pm_01[2]; From 4673999dda847d615ed640d5841d4dbbcf0bafb4 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 12:50:43 +0700 Subject: [PATCH 38/69] Fix var type --- src/AgValue.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index d728f56..7965b68 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -57,7 +57,7 @@ bool Measurements::update(MeasurementType type, int val, int ch) { // Define data point source IntegerValue *temporary = nullptr; - float invalidValue = 0; + int invalidValue = 0; switch (type) { case CO2: temporary = &_co2; @@ -225,7 +225,6 @@ int Measurements::get(MeasurementType type, int ch) { // Define data point source IntegerValue *temporary = nullptr; - float invalidValue = 0; switch (type) { case CO2: temporary = &_co2; From fd1f35f6d8af2ec74756d16411fc6beeb62de1dc Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 15:28:58 +0700 Subject: [PATCH 39/69] Getter to get other PMS sensor data --- src/PMS/PMS.cpp | 14 +++++++++---- src/PMS/PMS5003.cpp | 50 ++++++++++++++++++++++++++++++++++++++++---- src/PMS/PMS5003.h | 10 +++++++++ src/PMS/PMS5003T.cpp | 45 ++++++++++++++++++++++++++++++++++----- src/PMS/PMS5003T.h | 9 ++++++++ 5 files changed, 115 insertions(+), 13 deletions(-) diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 45c16c0..68df585 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -399,20 +399,26 @@ bool PMSBase::validate(const uint8_t *buf) { } void PMSBase::parse(const uint8_t *buf) { + // Standard particle pms_raw0_1 = toU16(&buf[4]); pms_raw2_5 = toU16(&buf[6]); pms_raw10 = toU16(&buf[8]); + // atmospheric pms_pm0_1 = toU16(&buf[10]); pms_pm2_5 = toU16(&buf[12]); pms_pm10 = toU16(&buf[14]); + + // particle count pms_count0_3 = toU16(&buf[16]); pms_count0_5 = toU16(&buf[18]); pms_count1_0 = toU16(&buf[20]); pms_count2_5 = toU16(&buf[22]); - pms_count5_0 = toU16(&buf[24]); - pms_count10 = toU16(&buf[26]); - pms_temp = toU16(&buf[24]); - pms_hum = toU16(&buf[26]); + pms_count5_0 = toU16(&buf[24]); // PMS5003 only + pms_count10 = toU16(&buf[26]); // PMS5003 only + + // Others + pms_temp = toU16(&buf[24]); // PMS5003T only + pms_hum = toU16(&buf[26]); // PMS5003T only pms_firmwareVersion = buf[28]; pms_errorCode = buf[29]; } diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 7b021bf..1605375 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -81,26 +81,47 @@ bool PMS5003::begin(void) { /** * @brief Read PM1.0 must call this function after @ref readData success * - * @return int PM1.0 index + * @return int PM1.0 index (atmospheric environment) */ int PMS5003::getPm01Ae(void) { return pms.getPM0_1(); } /** * @brief Read PM2.5 must call this function after @ref readData success * - * @return int PM2.5 index + * @return int PM2.5 index (atmospheric environment) */ int PMS5003::getPm25Ae(void) { return pms.getPM2_5(); } /** * @brief Read PM10.0 must call this function after @ref readData success * - * @return int PM10.0 index + * @return int PM10.0 index (atmospheric environment) */ int PMS5003::getPm10Ae(void) { return pms.getPM10(); } /** - * @brief Read PM0.3 must call this function after @ref readData success + * @brief Read PM1.0 must call this function after @ref readData success + * + * @return int PM1.0 index (standard particle) + */ +int PMS5003::getPm01Sp(void) { return pms.getRaw0_1(); } + +/** + * @brief Read PM2.5 must call this function after @ref readData success + * + * @return int PM2.5 index (standard particle) + */ +int PMS5003::getPm25Sp(void) { return pms.getRaw2_5(); } + +/** + * @brief Read PM10 must call this function after @ref readData success + * + * @return int PM10 index (standard particle) + */ +int PMS5003::getPm10Sp(void) { return pms.getRaw10(); } + +/** + * @brief Read particle 0.3 count must call this function after @ref readData success * * @return int PM0.3 index */ @@ -108,6 +129,27 @@ int PMS5003::getPm03ParticleCount(void) { return pms.getCount0_3(); } +/** + * @brief Read particle 1.0 count must call this function after @ref readData success + * + * @return int particle 1.0 count index + */ +int PMS5003::getPm01ParticleCount(void) { return pms.getCount1_0(); } + +/** + * @brief Read particle 2.5 count must call this function after @ref readData success + * + * @return int particle 2.5 count index + */ +int PMS5003::getPm25ParticleCount(void) { return pms.getCount2_5(); } + +/** + * @brief Read particle 10 count must call this function after @ref readData success + * + * @return int particle 10 count index + */ +int PMS5003::getPm10ParticleCount(void) { return pms.getCount10(); } + /** * @brief Convert PM2.5 to US AQI * diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index d8fc862..0e3ca13 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -25,10 +25,20 @@ public: void resetFailCount(void); int getFailCount(void); int getFailCountMax(void); + // Atmospheric environment int getPm01Ae(void); int getPm25Ae(void); int getPm10Ae(void); + // Standard particle + int getPm01Sp(void); + int getPm25Sp(void); + int getPm10Sp(void); + // Particle count int getPm03ParticleCount(void); + int getPm01ParticleCount(void); + int getPm25ParticleCount(void); + int getPm10ParticleCount(void); + int convertPm25ToUsAqi(int pm25); float compensate(float pm25, float humidity); int getFirmwareVersion(void); diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index 38ce259..494bf2f 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -110,33 +110,68 @@ bool PMS5003T::begin(void) { /** * @brief Read PM1.0 must call this function after @ref readData success * - * @return int PM1.0 index + * @return int PM1.0 index (atmospheric environment) */ int PMS5003T::getPm01Ae(void) { return pms.getPM0_1(); } /** * @brief Read PM2.5 must call this function after @ref readData success * - * @return int PM2.5 index + * @return int PM2.5 index (atmospheric environment) */ int PMS5003T::getPm25Ae(void) { return pms.getPM2_5(); } /** * @brief Read PM10.0 must call this function after @ref readData success * - * @return int PM10.0 index + * @return int PM10.0 index (atmospheric environment) */ int PMS5003T::getPm10Ae(void) { return pms.getPM10(); } /** - * @brief Read PM 0.3 Count must call this function after @ref readData success + * @brief Read PM1.0 must call this function after @ref readData success * - * @return int PM 0.3 Count index + * @return int PM1.0 index (standard particle) + */ +int PMS5003T::getPm01Sp(void) { return pms.getRaw0_1(); } + +/** + * @brief Read PM2.5 must call this function after @ref readData success + * + * @return int PM2.5 index (standard particle) + */ +int PMS5003T::getPm25Sp(void) { return pms.getRaw2_5(); } + +/** + * @brief Read PM10 must call this function after @ref readData success + * + * @return int PM10 index (standard particle) + */ +int PMS5003T::getPm10Sp(void) { return pms.getRaw10(); } + +/** + * @brief Read particle 0.3 count must call this function after @ref readData success + * + * @return int particle 0.3 count index */ int PMS5003T::getPm03ParticleCount(void) { return pms.getCount0_3(); } +/** + * @brief Read particle 1.0 count must call this function after @ref readData success + * + * @return int particle 1.0 count index + */ +int PMS5003T::getPm01ParticleCount(void) { return pms.getCount1_0(); } + +/** + * @brief Read particle 2.5 count must call this function after @ref readData success + * + * @return int particle 2.5 count index + */ +int PMS5003T::getPm25ParticleCount(void) { return pms.getCount2_5(); } + /** * @brief Convert PM2.5 to US AQI * diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index eac4a8d..695439b 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -28,10 +28,19 @@ public: void resetFailCount(void); int getFailCount(void); int getFailCountMax(void); + // Atmospheric environment int getPm01Ae(void); int getPm25Ae(void); int getPm10Ae(void); + // Standard particle + int getPm01Sp(void); + int getPm25Sp(void); + int getPm10Sp(void); + // Particle count int getPm03ParticleCount(void); + int getPm01ParticleCount(void); + int getPm25ParticleCount(void); + int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); From eeba41f497feafd244a8a4920e8262a6a556beb7 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 17:13:15 +0700 Subject: [PATCH 40/69] Include other PMS data to measurements --- examples/OneOpenAir/OneOpenAir.ino | 65 ++++++++++++++++++++++---- src/AgValue.cpp | 74 ++++++++++++++++++++++++++++-- src/AgValue.h | 26 ++++++++--- 3 files changed, 144 insertions(+), 21 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index b202929..8627583 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1001,12 +1001,24 @@ static void updatePMS5003() { measurements.update(Measurements::PM01, ag->pms5003.getPm01Ae()); measurements.update(Measurements::PM25, ag->pms5003.getPm25Ae()); measurements.update(Measurements::PM10, ag->pms5003.getPm10Ae()); + measurements.update(Measurements::PM01_SP, ag->pms5003.getPm01Sp()); + measurements.update(Measurements::PM25_SP, ag->pms5003.getPm25Sp()); + measurements.update(Measurements::PM10_SP, ag->pms5003.getPm10Sp()); measurements.update(Measurements::PM03_PC, ag->pms5003.getPm03ParticleCount()); + measurements.update(Measurements::PM01_PC, ag->pms5003.getPm01ParticleCount()); + measurements.update(Measurements::PM25_PC, ag->pms5003.getPm25ParticleCount()); + measurements.update(Measurements::PM10_PC, ag->pms5003.getPm10ParticleCount()); } else { measurements.update(Measurements::PM01, utils::getInvalidPmValue()); measurements.update(Measurements::PM25, utils::getInvalidPmValue()); measurements.update(Measurements::PM10, utils::getInvalidPmValue()); + measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue()); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM10_PC, utils::getInvalidPmValue()); } } @@ -1027,18 +1039,28 @@ static void updatePm(void) { measurements.update(Measurements::PM01, ag->pms5003t_1.getPm01Ae(), channel); measurements.update(Measurements::PM25, ag->pms5003t_1.getPm25Ae(), channel); measurements.update(Measurements::PM10, ag->pms5003t_1.getPm10Ae(), channel); + measurements.update(Measurements::PM01_SP, ag->pms5003t_1.getPm01Sp(), channel); + measurements.update(Measurements::PM25_SP, ag->pms5003t_1.getPm25Sp(), channel); + measurements.update(Measurements::PM10_SP, ag->pms5003t_1.getPm10Sp(), channel); measurements.update(Measurements::PM03_PC, ag->pms5003t_1.getPm03ParticleCount(), channel); + measurements.update(Measurements::PM01_PC, ag->pms5003t_1.getPm01ParticleCount(), channel); + measurements.update(Measurements::PM25_PC, ag->pms5003t_1.getPm25ParticleCount(), channel); measurements.update(Measurements::Temperature, ag->pms5003t_1.getTemperature(), channel); measurements.update(Measurements::Humidity, ag->pms5003t_1.getRelativeHumidity(), channel); // flag that new valid PMS value exists - newPMS2Value = true; + newPMS1Value = true; } else { // PMS channel 1 now is not connected, update using invalid value measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel); } @@ -1051,18 +1073,28 @@ static void updatePm(void) { measurements.update(Measurements::PM01, ag->pms5003t_2.getPm01Ae(), channel); measurements.update(Measurements::PM25, ag->pms5003t_2.getPm25Ae(), channel); measurements.update(Measurements::PM10, ag->pms5003t_2.getPm10Ae(), channel); + measurements.update(Measurements::PM01_SP, ag->pms5003t_2.getPm01Sp(), channel); + measurements.update(Measurements::PM25_SP, ag->pms5003t_2.getPm25Sp(), channel); + measurements.update(Measurements::PM10_SP, ag->pms5003t_2.getPm10Sp(), channel); measurements.update(Measurements::PM03_PC, ag->pms5003t_2.getPm03ParticleCount(), channel); + measurements.update(Measurements::PM01_PC, ag->pms5003t_2.getPm01ParticleCount(), channel); + measurements.update(Measurements::PM25_PC, ag->pms5003t_2.getPm25ParticleCount(), channel); measurements.update(Measurements::Temperature, ag->pms5003t_2.getTemperature(), channel); measurements.update(Measurements::Humidity, ag->pms5003t_2.getRelativeHumidity(), channel); // flag that new valid PMS value exists newPMS2Value = true; } else { - // PMS channel channel now is not connected, update using invalid value + // PMS channel 2 now is not connected, update using invalid value measurements.update(Measurements::PM01, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM25, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM10, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM01_SP, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); measurements.update(Measurements::Humidity, utils::getInvalidHumidity(), channel); } @@ -1133,18 +1165,31 @@ static void tempHumUpdate(void) { /* Set max period for each measurement type based on sensor update interval*/ void setMeasurementMaxPeriod() { + int max; + /// Max period for S8 sensors measurements measurements.maxPeriod(Measurements::CO2, calculateMaxPeriod(SENSOR_CO2_UPDATE_INTERVAL)); + /// Max period for SGP sensors measurements - measurements.maxPeriod(Measurements::TVOC, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::TVOCRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::NOx, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::NOxRaw, calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL)); + max = calculateMaxPeriod(SENSOR_TVOC_UPDATE_INTERVAL); + measurements.maxPeriod(Measurements::TVOC, max); + measurements.maxPeriod(Measurements::TVOCRaw, max); + measurements.maxPeriod(Measurements::NOx, max); + measurements.maxPeriod(Measurements::NOxRaw, max); + /// Max period for PMS sensors measurements - measurements.maxPeriod(Measurements::PM25, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM01, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM10, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); - measurements.maxPeriod(Measurements::PM03_PC, calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL)); + max = calculateMaxPeriod(SENSOR_PM_UPDATE_INTERVAL); + measurements.maxPeriod(Measurements::PM25, max); + measurements.maxPeriod(Measurements::PM01, max); + measurements.maxPeriod(Measurements::PM10, max); + measurements.maxPeriod(Measurements::PM25_SP, max); + measurements.maxPeriod(Measurements::PM01_SP, max); + measurements.maxPeriod(Measurements::PM10_SP, max); + measurements.maxPeriod(Measurements::PM03_PC, max); + measurements.maxPeriod(Measurements::PM01_PC, max); + measurements.maxPeriod(Measurements::PM25_PC, max); + measurements.maxPeriod(Measurements::PM10_PC, max); + // Temperature and Humidity if (configuration.hasSensorSHT) { /// Max period for SHT sensors measurements diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 7965b68..748303d 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -41,10 +41,34 @@ void Measurements::maxPeriod(MeasurementType type, int max) { _pm_10[0].update.max = max; _pm_10[1].update.max = max; break; + case PM01_SP: + _pm_01_sp[0].update.max = max; + _pm_01_sp[1].update.max = max; + break; + case PM25_SP: + _pm_25_sp[0].update.max = max; + _pm_25_sp[1].update.max = max; + break; + case PM10_SP: + _pm_10_sp[0].update.max = max; + _pm_10_sp[1].update.max = max; + break; case PM03_PC: _pm_03_pc[0].update.max = max; _pm_03_pc[1].update.max = max; break; + case PM01_PC: + _pm_01_pc[0].update.max = max; + _pm_01_pc[1].update.max = max; + break; + case PM25_PC: + _pm_25_pc[0].update.max = max; + _pm_25_pc[1].update.max = max; + break; + case PM10_PC: + _pm_10_pc[0].update.max = max; + _pm_10_pc[1].update.max = max; + break; }; } @@ -91,10 +115,34 @@ bool Measurements::update(MeasurementType type, int val, int ch) { temporary = &_pm_10[ch]; invalidValue = utils::getInvalidPmValue(); break; + case PM01_SP: + temporary = &_pm_01_sp[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM25_SP: + temporary = &_pm_25_sp[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM10_SP: + temporary = &_pm_10_sp[ch]; + invalidValue = utils::getInvalidPmValue(); + break; case PM03_PC: temporary = &_pm_03_pc[ch]; invalidValue = utils::getInvalidPmValue(); break; + case PM01_PC: + temporary = &_pm_01_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM25_PC: + temporary = &_pm_25_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM10_PC: + temporary = &_pm_10_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; default: break; }; @@ -344,16 +392,34 @@ String Measurements::measurementTypeStr(MeasurementType type) { str = "NOxRaw"; break; case PM25: - str = "PM25"; + str = "PM25_AE"; break; case PM01: - str = "PM01"; + str = "PM1_AE"; break; case PM10: - str = "PM10"; + str = "PM10_AE"; + break; + case PM25_SP: + str = "PM25_SP"; + break; + case PM01_SP: + str = "PM1_SP"; + break; + case PM10_SP: + str = "PM10_SP"; break; case PM03_PC: - str = "PM03"; + str = "PM003_PC"; + break; + case PM01_PC: + str = "PM01_PC"; + break; + case PM25_PC: + str = "PM25_PC"; + break; + case PM10_PC: + str = "PM10_PC"; break; default: break; diff --git a/src/AgValue.h b/src/AgValue.h index 8c26bf2..cb723a0 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -46,10 +46,16 @@ public: TVOCRaw, NOx, // index value NOxRaw, - PM25, - PM01, - PM10, - PM03_PC, // Particle count + PM01, // PM1.0 under atmospheric environment + PM25, // PM2.5 under athompheric environment + PM10, // PM10 under atmospheric environment + PM01_SP, // PM1.0 standard particle + PM25_SP, // PM2.5 standard particle + PM10_SP, // PM10 standard particle + PM03_PC, // Particle 0.3 count + PM01_PC, // Particle 1.0 count + PM25_PC, // Particle 2.5 count + PM10_PC, // Particle 10 count }; /** @@ -129,10 +135,16 @@ private: IntegerValue _tvoc_raw; IntegerValue _nox; // Index value IntegerValue _nox_raw; - IntegerValue _pm_25[2]; - IntegerValue _pm_01[2]; - IntegerValue _pm_10[2]; + IntegerValue _pm_01[2]; // pm 1.0 atmospheric environment + IntegerValue _pm_25[2]; // pm 2.5 atmospheric environment + IntegerValue _pm_10[2]; // pm 10 atmospheric environment + IntegerValue _pm_01_sp[2]; // pm 1.0 standard particle + IntegerValue _pm_25_sp[2]; // pm 2.5 standard particle + IntegerValue _pm_10_sp[2]; // pm 10 standard particle IntegerValue _pm_03_pc[2]; // particle count 0.3 + IntegerValue _pm_01_pc[2]; // particle count 1.0 + IntegerValue _pm_25_pc[2]; // particle count 2.5 + IntegerValue _pm_10_pc[2]; // particle count 10 bool _debug = false; From 3b0c77ca4df00ea5f8723d63a09911752dc215c7 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 18:28:56 +0700 Subject: [PATCH 41/69] New measurements add to transmission payload --- src/AgValue.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 748303d..3a93c02 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -590,9 +590,31 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem if (utils::isValidPm(_pm_10[ch].update.avg)) { pms["pm10"] = ag.round2(_pm_10[ch].update.avg); } + if (utils::isValidPm(_pm_01_sp[ch].update.avg)) { + pms["pm01_sp"] = ag.round2(_pm_01_sp[ch].update.avg); + } + if (utils::isValidPm(_pm_25_sp[ch].update.avg)) { + pms["pm02_sp"] = ag.round2(_pm_25_sp[ch].update.avg); + } + if (utils::isValidPm(_pm_10_sp[ch].update.avg)) { + pms["pm10_sp"] = ag.round2(_pm_10_sp[ch].update.avg); + } if (utils::isValidPm03Count(_pm_03_pc[ch].update.avg)) { pms["pm003Count"] = ag.round2(_pm_03_pc[ch].update.avg); } + if (utils::isValidPm03Count(_pm_01_pc[ch].update.avg)) { + pms["pm01Count"] = ag.round2(_pm_01_pc[ch].update.avg); + } + if (utils::isValidPm03Count(_pm_25_pc[ch].update.avg)) { + pms["pm02Count"] = ag.round2(_pm_25_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["pm10Count"] = ag.round2(_pm_10_pc[ch].update.avg); + } + } if (withTempHum) { float _vc; @@ -637,8 +659,9 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem return pms; }; - // Handle both channel with average, if one of the channel not valid, use another one - /// PM01 + /** Handle both channel with average, if one of the channel not valid, use another one */ + + /// 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["pm01"] = ag.round2(avg); @@ -652,7 +675,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm01"] = ag.round2(_pm_01[1].update.avg); } - /// PM2.5 + /// 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["pm02"] = ag.round2(avg); @@ -666,7 +689,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm02"] = ag.round2(_pm_25[1].update.avg); } - /// PM10 + /// 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["pm10"] = ag.round2(avg); @@ -680,7 +703,49 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm10"] = ag.round2(_pm_10[1].update.avg); } - /// PM03 particle count + /// 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["pm01_sp"] = ag.round2(avg); + pms["channels"]["1"]["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); + pms["channels"]["2"]["pm01_sp"] = ag.round2(_pm_01_sp[1].update.avg); + } else if (utils::isValidPm(_pm_01_sp[0].update.avg)) { + pms["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); + pms["channels"]["1"]["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); + } else if (utils::isValidPm(_pm_01_sp[1].update.avg)) { + pms["pm01_sp"] = ag.round2(_pm_01_sp[1].update.avg); + pms["channels"]["2"]["pm01_sp"] = 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["pm02_sp"] = ag.round2(avg); + pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); + pms["channels"]["2"]["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); + } else if (utils::isValidPm(_pm_25_sp[0].update.avg)) { + pms["pm01_sp"] = ag.round2(_pm_25_sp[0].update.avg); + pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); + } else if (utils::isValidPm(_pm_25_sp[1].update.avg)) { + pms["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); + pms["channels"]["2"]["pm02_sp"] = 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["pm10_sp"] = ag.round2(avg); + pms["channels"]["1"]["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); + pms["channels"]["2"]["pm10_sp"] = ag.round2(_pm_10_sp[1].update.avg); + } else if (utils::isValidPm(_pm_10_sp[0].update.avg)) { + pms["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); + pms["channels"]["1"]["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); + } else if (utils::isValidPm(_pm_10_sp[1].update.avg)) { + pms["pm10_sp"] = ag.round2(_pm_10_sp[1].update.avg); + pms["channels"]["2"]["pm10_sp"] = 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; @@ -695,6 +760,53 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm003Count"] = ag.round2(_pm_03_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["pm01Count"] = ag.round2(avg); + pms["channels"]["1"]["pm01Count"] = ag.round2(_pm_01_pc[0].update.avg); + pms["channels"]["2"]["pm01Count"] = ag.round2(_pm_01_pc[1].update.avg); + } else if (utils::isValidPm(_pm_01_pc[0].update.avg)) { + pms["pm01Count"] = ag.round2(_pm_01_pc[0].update.avg); + pms["channels"]["1"]["pm01Count"] = ag.round2(_pm_01_pc[0].update.avg); + } else if (utils::isValidPm(_pm_01_pc[1].update.avg)) { + pms["pm01Count"] = ag.round2(_pm_01_pc[1].update.avg); + pms["channels"]["2"]["pm01Count"] = 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["pm25Count"] = ag.round2(avg); + pms["channels"]["1"]["pm25Count"] = ag.round2(_pm_25_pc[0].update.avg); + pms["channels"]["2"]["pm25Count"] = ag.round2(_pm_25_pc[1].update.avg); + } else if (utils::isValidPm(_pm_25_pc[0].update.avg)) { + pms["pm25Count"] = ag.round2(_pm_25_pc[0].update.avg); + pms["channels"]["1"]["pm25Count"] = ag.round2(_pm_25_pc[0].update.avg); + } else if (utils::isValidPm(_pm_25_pc[1].update.avg)) { + pms["pm25Count"] = ag.round2(_pm_25_pc[1].update.avg); + pms["channels"]["2"]["pm25Count"] = ag.round2(_pm_25_pc[1].update.avg); + } + + // NOTE: No need for particle count 10. When allCh is true, basically monitor using PM5003T, which + // don't have PC 10 + // /// PM10 particle count + // if (utils::isValidPm03Count(_pm_10_pc[0].update.avg) && + // utils::isValidPm03Count(_pm_10_pc[1].update.avg)) { + // float avg = (_pm_10_pc[0].update.avg + _pm_10_pc[1].update.avg) / 2.0f; + // pms["pm10Count"] = ag.round2(avg); + // pms["channels"]["1"]["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); + // pms["channels"]["2"]["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); + // } else if (utils::isValidPm(_pm_10_pc[0].update.avg)) { + // pms["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); + // pms["channels"]["1"]["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); + // } else if (utils::isValidPm(_pm_10_pc[1].update.avg)) { + // pms["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); + // pms["channels"]["2"]["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); + // } + if (withTempHum) { /// Temperature if (utils::isValidTemperature(_temperature[0].update.avg) && From 4783684443aa9585e2990992cd6d461e31eb3cc7 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 22 Oct 2024 18:37:56 +0700 Subject: [PATCH 42/69] Update local server payloads --- docs/local-server.md | 14 ++++++++++---- src/AgValue.cpp | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 307ad8c..fe0d496 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -50,12 +50,18 @@ You get the following response: |-----------------------------------|---------|----------------------------------------------------------------------------------------| | `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 | +| `pm01` | Number | PM1.0 in ug/m3 (atmospheric environment) | +| `pm02` | Number | PM2.5 in ug/m3 (atmospheric environment) | +| `pm10` | Number | PM10 in ug/m3 (atmospheric environment) | | `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) | +| `pm01_sp` | Number | PM1.0 in ug/m3 (standard particle) | +| `pm02_sp` | Number | PM2.5 in ug/m3 (standard particle) | +| `pm10_sp` | Number | PM10 in ug/m3 (standard particle) | | `rco2` | Number | CO2 in ppm | -| `pm003Count` | Number | Particle count per dL | +| `pm003Count` | Number | Particle count 0.3um per dL | +| `pm01Count` | Number | Particle count 1.0um per dL | +| `pm25Count` | Number | Particle count 2.5um per dL | +| `pm10Count` | Number | Particle count 10um per dL | | `atmp` | Number | Temperature in Degrees Celsius | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | | `rhum` | Number | Relative Humidity | diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 3a93c02..638bee1 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -724,7 +724,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); pms["channels"]["2"]["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); } else if (utils::isValidPm(_pm_25_sp[0].update.avg)) { - pms["pm01_sp"] = ag.round2(_pm_25_sp[0].update.avg); + pms["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); } else if (utils::isValidPm(_pm_25_sp[1].update.avg)) { pms["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); From 85e779cfc2b70fcadaa2674ad1e006bdf5f6915a Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 23 Oct 2024 11:05:16 +0700 Subject: [PATCH 43/69] Use camel case for transmission payload --- docs/local-server.md | 8 ++++---- src/AgValue.cpp | 48 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index fe0d496..d65aee1 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -54,14 +54,14 @@ You get the following response: | `pm02` | Number | PM2.5 in ug/m3 (atmospheric environment) | | `pm10` | Number | PM10 in ug/m3 (atmospheric environment) | | `pm02Compensated` | Number | PM2.5 in ug/m3 with correction applied (from fw version 3.1.4 onwards) | -| `pm01_sp` | Number | PM1.0 in ug/m3 (standard particle) | -| `pm02_sp` | Number | PM2.5 in ug/m3 (standard particle) | -| `pm10_sp` | Number | PM10 in ug/m3 (standard particle) | +| `pm01Standard` | Number | PM1.0 in ug/m3 (standard particle) | +| `pm02Standard` | Number | PM2.5 in ug/m3 (standard particle) | +| `pm10Standard` | Number | PM10 in ug/m3 (standard particle) | | `rco2` | Number | CO2 in ppm | | `pm003Count` | Number | Particle count 0.3um per dL | | `pm01Count` | Number | Particle count 1.0um per dL | | `pm25Count` | Number | Particle count 2.5um per dL | -| `pm10Count` | Number | Particle count 10um per dL | +| `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) | | `atmp` | Number | Temperature in Degrees Celsius | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | | `rhum` | Number | Relative Humidity | diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 638bee1..aa21145 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -591,13 +591,13 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["pm10"] = ag.round2(_pm_10[ch].update.avg); } if (utils::isValidPm(_pm_01_sp[ch].update.avg)) { - pms["pm01_sp"] = ag.round2(_pm_01_sp[ch].update.avg); + pms["pm01Standard"] = ag.round2(_pm_01_sp[ch].update.avg); } if (utils::isValidPm(_pm_25_sp[ch].update.avg)) { - pms["pm02_sp"] = ag.round2(_pm_25_sp[ch].update.avg); + pms["pm02Standard"] = ag.round2(_pm_25_sp[ch].update.avg); } if (utils::isValidPm(_pm_10_sp[ch].update.avg)) { - pms["pm10_sp"] = ag.round2(_pm_10_sp[ch].update.avg); + pms["pm10Standard"] = ag.round2(_pm_10_sp[ch].update.avg); } if (utils::isValidPm03Count(_pm_03_pc[ch].update.avg)) { pms["pm003Count"] = ag.round2(_pm_03_pc[ch].update.avg); @@ -706,43 +706,43 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem /// 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["pm01_sp"] = ag.round2(avg); - pms["channels"]["1"]["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["2"]["pm01_sp"] = ag.round2(_pm_01_sp[1].update.avg); + pms["pm01Standard"] = ag.round2(avg); + pms["channels"]["1"]["pm01Standard"] = ag.round2(_pm_01_sp[0].update.avg); + pms["channels"]["2"]["pm01Standard"] = ag.round2(_pm_01_sp[1].update.avg); } else if (utils::isValidPm(_pm_01_sp[0].update.avg)) { - pms["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["1"]["pm01_sp"] = ag.round2(_pm_01_sp[0].update.avg); + pms["pm01Standard"] = ag.round2(_pm_01_sp[0].update.avg); + pms["channels"]["1"]["pm01Standard"] = ag.round2(_pm_01_sp[0].update.avg); } else if (utils::isValidPm(_pm_01_sp[1].update.avg)) { - pms["pm01_sp"] = ag.round2(_pm_01_sp[1].update.avg); - pms["channels"]["2"]["pm01_sp"] = ag.round2(_pm_01_sp[1].update.avg); + pms["pm01Standard"] = ag.round2(_pm_01_sp[1].update.avg); + pms["channels"]["2"]["pm01Standard"] = 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["pm02_sp"] = ag.round2(avg); - pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["2"]["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); + pms["pm02Standard"] = ag.round2(avg); + pms["channels"]["1"]["pm02Standard"] = ag.round2(_pm_25_sp[0].update.avg); + pms["channels"]["2"]["pm02Standard"] = ag.round2(_pm_25_sp[1].update.avg); } else if (utils::isValidPm(_pm_25_sp[0].update.avg)) { - pms["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["1"]["pm02_sp"] = ag.round2(_pm_25_sp[0].update.avg); + pms["pm02Standard"] = ag.round2(_pm_25_sp[0].update.avg); + pms["channels"]["1"]["pm02Standard"] = ag.round2(_pm_25_sp[0].update.avg); } else if (utils::isValidPm(_pm_25_sp[1].update.avg)) { - pms["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); - pms["channels"]["2"]["pm02_sp"] = ag.round2(_pm_25_sp[1].update.avg); + pms["pm02Standard"] = ag.round2(_pm_25_sp[1].update.avg); + pms["channels"]["2"]["pm02Standard"] = 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["pm10_sp"] = ag.round2(avg); - pms["channels"]["1"]["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["2"]["pm10_sp"] = ag.round2(_pm_10_sp[1].update.avg); + pms["pm10Standard"] = ag.round2(avg); + pms["channels"]["1"]["pm10Standard"] = ag.round2(_pm_10_sp[0].update.avg); + pms["channels"]["2"]["pm10Standard"] = ag.round2(_pm_10_sp[1].update.avg); } else if (utils::isValidPm(_pm_10_sp[0].update.avg)) { - pms["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["1"]["pm10_sp"] = ag.round2(_pm_10_sp[0].update.avg); + pms["pm10Standard"] = ag.round2(_pm_10_sp[0].update.avg); + pms["channels"]["1"]["pm10Standard"] = ag.round2(_pm_10_sp[0].update.avg); } else if (utils::isValidPm(_pm_10_sp[1].update.avg)) { - pms["pm10_sp"] = ag.round2(_pm_10_sp[1].update.avg); - pms["channels"]["2"]["pm10_sp"] = ag.round2(_pm_10_sp[1].update.avg); + pms["pm10Standard"] = ag.round2(_pm_10_sp[1].update.avg); + pms["channels"]["2"]["pm10Standard"] = ag.round2(_pm_10_sp[1].update.avg); } /// PM003 particle count From 33e2977eb46b249ca7adf61d4c790deeec3cd9ee Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 23 Oct 2024 11:18:18 +0700 Subject: [PATCH 44/69] Fix comments --- src/AgValue.cpp | 3 ++- src/PMS/PMS5003.cpp | 20 ++++++++++---------- src/PMS/PMS5003T.cpp | 18 +++++++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index aa21145..639ab53 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -659,7 +659,8 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem return pms; }; - /** Handle both channel with average, if one of the channel not valid, use another one */ + /** Handle both channels by averaging their values; if one channel's value is not valid, skip + * averaging and use the valid value from the other channel */ /// PM1.0 atmospheric environment if (utils::isValidPm(_pm_01[0].update.avg) && utils::isValidPm(_pm_01[1].update.avg)) { diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 1605375..5b8cdfd 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -79,49 +79,49 @@ bool PMS5003::begin(void) { } /** - * @brief Read PM1.0 must call this function after @ref readData success + * @brief Read PM1.0 * * @return int PM1.0 index (atmospheric environment) */ int PMS5003::getPm01Ae(void) { return pms.getPM0_1(); } /** - * @brief Read PM2.5 must call this function after @ref readData success + * @brief Read PM2.5 * * @return int PM2.5 index (atmospheric environment) */ int PMS5003::getPm25Ae(void) { return pms.getPM2_5(); } /** - * @brief Read PM10.0 must call this function after @ref readData success + * @brief Read PM10.0 * * @return int PM10.0 index (atmospheric environment) */ int PMS5003::getPm10Ae(void) { return pms.getPM10(); } /** - * @brief Read PM1.0 must call this function after @ref readData success + * @brief Read PM1.0 * * @return int PM1.0 index (standard particle) */ int PMS5003::getPm01Sp(void) { return pms.getRaw0_1(); } /** - * @brief Read PM2.5 must call this function after @ref readData success + * @brief Read PM2.5 * * @return int PM2.5 index (standard particle) */ int PMS5003::getPm25Sp(void) { return pms.getRaw2_5(); } /** - * @brief Read PM10 must call this function after @ref readData success + * @brief Read PM10 * * @return int PM10 index (standard particle) */ int PMS5003::getPm10Sp(void) { return pms.getRaw10(); } /** - * @brief Read particle 0.3 count must call this function after @ref readData success + * @brief Read particle 0.3 count * * @return int PM0.3 index */ @@ -130,21 +130,21 @@ int PMS5003::getPm03ParticleCount(void) { } /** - * @brief Read particle 1.0 count must call this function after @ref readData success + * @brief Read particle 1.0 count * * @return int particle 1.0 count index */ int PMS5003::getPm01ParticleCount(void) { return pms.getCount1_0(); } /** - * @brief Read particle 2.5 count must call this function after @ref readData success + * @brief Read particle 2.5 count * * @return int particle 2.5 count index */ int PMS5003::getPm25ParticleCount(void) { return pms.getCount2_5(); } /** - * @brief Read particle 10 count must call this function after @ref readData success + * @brief Read particle 10 count * * @return int particle 10 count index */ diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index 494bf2f..e43ea91 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -108,49 +108,49 @@ bool PMS5003T::begin(void) { } /** - * @brief Read PM1.0 must call this function after @ref readData success + * @brief Read PM1.0 * * @return int PM1.0 index (atmospheric environment) */ int PMS5003T::getPm01Ae(void) { return pms.getPM0_1(); } /** - * @brief Read PM2.5 must call this function after @ref readData success + * @brief Read PM2.5 * * @return int PM2.5 index (atmospheric environment) */ int PMS5003T::getPm25Ae(void) { return pms.getPM2_5(); } /** - * @brief Read PM10.0 must call this function after @ref readData success + * @brief Read PM10.0 * * @return int PM10.0 index (atmospheric environment) */ int PMS5003T::getPm10Ae(void) { return pms.getPM10(); } /** - * @brief Read PM1.0 must call this function after @ref readData success + * @brief Read PM1.0 * * @return int PM1.0 index (standard particle) */ int PMS5003T::getPm01Sp(void) { return pms.getRaw0_1(); } /** - * @brief Read PM2.5 must call this function after @ref readData success + * @brief Read PM2.5 * * @return int PM2.5 index (standard particle) */ int PMS5003T::getPm25Sp(void) { return pms.getRaw2_5(); } /** - * @brief Read PM10 must call this function after @ref readData success + * @brief Read PM10 * * @return int PM10 index (standard particle) */ int PMS5003T::getPm10Sp(void) { return pms.getRaw10(); } /** - * @brief Read particle 0.3 count must call this function after @ref readData success + * @brief Read particle 0.3 count * * @return int particle 0.3 count index */ @@ -159,14 +159,14 @@ int PMS5003T::getPm03ParticleCount(void) { } /** - * @brief Read particle 1.0 count must call this function after @ref readData success + * @brief Read particle 1.0 count * * @return int particle 1.0 count index */ int PMS5003T::getPm01ParticleCount(void) { return pms.getCount1_0(); } /** - * @brief Read particle 2.5 count must call this function after @ref readData success + * @brief Read particle 2.5 count * * @return int particle 2.5 count index */ From bf0768c7da57029e1a6a7a3cd264f9252d3b51cd Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Wed, 23 Oct 2024 20:55:45 +0700 Subject: [PATCH 45/69] Comment description to invalidValue variable --- src/AgValue.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 639ab53..00343cc 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -81,6 +81,7 @@ bool Measurements::update(MeasurementType type, int val, int ch) { // Define data point source IntegerValue *temporary = nullptr; + // Act as reference invalid value respective to target measurements int invalidValue = 0; switch (type) { case CO2: @@ -203,6 +204,7 @@ bool Measurements::update(MeasurementType type, float val, int ch) { // Define data point source FloatValue *temporary = nullptr; + // Act as reference invalid value respective to target measurements float invalidValue = 0; switch (type) { case Temperature: From e6696f3d4157a901e74cb588a5800a90c6f3c90f Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Tue, 29 Oct 2024 23:51:19 +0700 Subject: [PATCH 46/69] New particle count 0.5 and 5.0 --- examples/OneOpenAir/OneOpenAir.ino | 10 +++++ src/AgValue.cpp | 72 +++++++++++++++++++++--------- src/AgValue.h | 4 ++ src/PMS/PMS5003.cpp | 14 ++++++ src/PMS/PMS5003.h | 2 + src/PMS/PMS5003T.cpp | 7 +++ src/PMS/PMS5003T.h | 1 + 7 files changed, 90 insertions(+), 20 deletions(-) diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index 8627583..1824ee7 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -1005,8 +1005,10 @@ static void updatePMS5003() { measurements.update(Measurements::PM25_SP, ag->pms5003.getPm25Sp()); measurements.update(Measurements::PM10_SP, ag->pms5003.getPm10Sp()); measurements.update(Measurements::PM03_PC, ag->pms5003.getPm03ParticleCount()); + measurements.update(Measurements::PM05_PC, ag->pms5003.getPm05ParticleCount()); measurements.update(Measurements::PM01_PC, ag->pms5003.getPm01ParticleCount()); measurements.update(Measurements::PM25_PC, ag->pms5003.getPm25ParticleCount()); + measurements.update(Measurements::PM5_PC, ag->pms5003.getPm5ParticleCount()); measurements.update(Measurements::PM10_PC, ag->pms5003.getPm10ParticleCount()); } else { measurements.update(Measurements::PM01, utils::getInvalidPmValue()); @@ -1016,8 +1018,10 @@ static void updatePMS5003() { measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue()); measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue()); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue()); measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue()); measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue()); + measurements.update(Measurements::PM5_PC, utils::getInvalidPmValue()); measurements.update(Measurements::PM10_PC, utils::getInvalidPmValue()); } } @@ -1043,6 +1047,7 @@ static void updatePm(void) { measurements.update(Measurements::PM25_SP, ag->pms5003t_1.getPm25Sp(), channel); measurements.update(Measurements::PM10_SP, ag->pms5003t_1.getPm10Sp(), channel); measurements.update(Measurements::PM03_PC, ag->pms5003t_1.getPm03ParticleCount(), channel); + measurements.update(Measurements::PM05_PC, ag->pms5003t_1.getPm05ParticleCount(), channel); measurements.update(Measurements::PM01_PC, ag->pms5003t_1.getPm01ParticleCount(), channel); measurements.update(Measurements::PM25_PC, ag->pms5003t_1.getPm25ParticleCount(), channel); measurements.update(Measurements::Temperature, ag->pms5003t_1.getTemperature(), channel); @@ -1059,6 +1064,7 @@ static void updatePm(void) { measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); @@ -1077,6 +1083,7 @@ static void updatePm(void) { measurements.update(Measurements::PM25_SP, ag->pms5003t_2.getPm25Sp(), channel); measurements.update(Measurements::PM10_SP, ag->pms5003t_2.getPm10Sp(), channel); measurements.update(Measurements::PM03_PC, ag->pms5003t_2.getPm03ParticleCount(), channel); + measurements.update(Measurements::PM05_PC, ag->pms5003t_2.getPm05ParticleCount(), channel); measurements.update(Measurements::PM01_PC, ag->pms5003t_2.getPm01ParticleCount(), channel); measurements.update(Measurements::PM25_PC, ag->pms5003t_2.getPm25ParticleCount(), channel); measurements.update(Measurements::Temperature, ag->pms5003t_2.getTemperature(), channel); @@ -1093,6 +1100,7 @@ static void updatePm(void) { measurements.update(Measurements::PM25_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM10_SP, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM03_PC, utils::getInvalidPmValue(), channel); + measurements.update(Measurements::PM05_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM01_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::PM25_PC, utils::getInvalidPmValue(), channel); measurements.update(Measurements::Temperature, utils::getInvalidTemperature(), channel); @@ -1186,8 +1194,10 @@ void setMeasurementMaxPeriod() { measurements.maxPeriod(Measurements::PM01_SP, max); measurements.maxPeriod(Measurements::PM10_SP, max); measurements.maxPeriod(Measurements::PM03_PC, max); + measurements.maxPeriod(Measurements::PM05_PC, max); measurements.maxPeriod(Measurements::PM01_PC, max); measurements.maxPeriod(Measurements::PM25_PC, max); + measurements.maxPeriod(Measurements::PM5_PC, max); measurements.maxPeriod(Measurements::PM10_PC, max); // Temperature and Humidity diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 00343cc..969d468 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -57,6 +57,10 @@ void Measurements::maxPeriod(MeasurementType type, int max) { _pm_03_pc[0].update.max = max; _pm_03_pc[1].update.max = max; break; + case PM05_PC: + _pm_05_pc[0].update.max = max; + _pm_05_pc[1].update.max = max; + break; case PM01_PC: _pm_01_pc[0].update.max = max; _pm_01_pc[1].update.max = max; @@ -65,6 +69,10 @@ void Measurements::maxPeriod(MeasurementType type, int max) { _pm_25_pc[0].update.max = max; _pm_25_pc[1].update.max = max; break; + case PM5_PC: + _pm_5_pc[0].update.max = max; + _pm_5_pc[1].update.max = max; + break; case PM10_PC: _pm_10_pc[0].update.max = max; _pm_10_pc[1].update.max = max; @@ -132,6 +140,10 @@ bool Measurements::update(MeasurementType type, int val, int ch) { temporary = &_pm_03_pc[ch]; invalidValue = utils::getInvalidPmValue(); break; + case PM05_PC: + temporary = &_pm_05_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; case PM01_PC: temporary = &_pm_01_pc[ch]; invalidValue = utils::getInvalidPmValue(); @@ -140,6 +152,10 @@ bool Measurements::update(MeasurementType type, int val, int ch) { temporary = &_pm_25_pc[ch]; invalidValue = utils::getInvalidPmValue(); break; + case PM5_PC: + temporary = &_pm_5_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; case PM10_PC: temporary = &_pm_10_pc[ch]; invalidValue = utils::getInvalidPmValue(); @@ -162,8 +178,8 @@ bool Measurements::update(MeasurementType type, int val, int ch) { temporary->update.invalidCounter++; if (temporary->update.invalidCounter >= temporary->update.max) { Serial.printf("%s{%d} invalid value update counter reached (%dx)! Setting its average value " - "to invalid!", - measurementTypeStr(type), ch, temporary->update.max); + "to invalid!\n", + measurementTypeStr(type).c_str(), ch, temporary->update.max); temporary->update.avg = invalidValue; return false; } @@ -233,8 +249,8 @@ bool Measurements::update(MeasurementType type, float val, int ch) { temporary->update.invalidCounter++; if (temporary->update.invalidCounter >= temporary->update.max) { Serial.printf("%s{%d} invalid value update counter reached (%dx)! Setting its average value " - "to invalid!", - measurementTypeStr(type), ch, temporary->update.max); + "to invalid!\n", + measurementTypeStr(type).c_str(), ch, temporary->update.max); temporary->update.avg = invalidValue; return false; } @@ -414,12 +430,18 @@ String Measurements::measurementTypeStr(MeasurementType type) { case PM03_PC: str = "PM003_PC"; break; + case PM05_PC: + str = "PM005_PC"; + break; case PM01_PC: str = "PM01_PC"; break; case PM25_PC: str = "PM25_PC"; break; + case PM5_PC: + str = "PM05_PC"; + break; case PM10_PC: str = "PM10_PC"; break; @@ -604,12 +626,22 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem if (utils::isValidPm03Count(_pm_03_pc[ch].update.avg)) { pms["pm003Count"] = ag.round2(_pm_03_pc[ch].update.avg); } + if (utils::isValidPm03Count(_pm_05_pc[ch].update.avg)) { + pms["pm005Count"] = ag.round2(_pm_05_pc[ch].update.avg); + } if (utils::isValidPm03Count(_pm_01_pc[ch].update.avg)) { pms["pm01Count"] = ag.round2(_pm_01_pc[ch].update.avg); } if (utils::isValidPm03Count(_pm_25_pc[ch].update.avg)) { pms["pm02Count"] = 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["pm05Count"] = 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 @@ -763,6 +795,20 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm003Count"] = 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["pm005Count"] = ag.round2(avg); + pms["channels"]["1"]["pm005Count"] = ag.round2(_pm_05_pc[0].update.avg); + pms["channels"]["2"]["pm005Count"] = ag.round2(_pm_05_pc[1].update.avg); + } else if (utils::isValidPm(_pm_05_pc[0].update.avg)) { + pms["pm005Count"] = ag.round2(_pm_05_pc[0].update.avg); + pms["channels"]["1"]["pm005Count"] = ag.round2(_pm_05_pc[0].update.avg); + } else if (utils::isValidPm(_pm_05_pc[1].update.avg)) { + pms["pm005Count"] = ag.round2(_pm_05_pc[1].update.avg); + pms["channels"]["2"]["pm005Count"] = 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)) { @@ -793,22 +839,8 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem pms["channels"]["2"]["pm25Count"] = ag.round2(_pm_25_pc[1].update.avg); } - // NOTE: No need for particle count 10. When allCh is true, basically monitor using PM5003T, which - // don't have PC 10 - // /// PM10 particle count - // if (utils::isValidPm03Count(_pm_10_pc[0].update.avg) && - // utils::isValidPm03Count(_pm_10_pc[1].update.avg)) { - // float avg = (_pm_10_pc[0].update.avg + _pm_10_pc[1].update.avg) / 2.0f; - // pms["pm10Count"] = ag.round2(avg); - // pms["channels"]["1"]["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); - // pms["channels"]["2"]["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); - // } else if (utils::isValidPm(_pm_10_pc[0].update.avg)) { - // pms["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); - // pms["channels"]["1"]["pm10Count"] = ag.round2(_pm_10_pc[0].update.avg); - // } else if (utils::isValidPm(_pm_10_pc[1].update.avg)) { - // pms["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); - // pms["channels"]["2"]["pm10Count"] = ag.round2(_pm_10_pc[1].update.avg); - // } + // NOTE: No need for particle count 5.0 and 10. When allCh is true, basically monitor using + // PM5003T, which don't have PC 5.0 and 10 if (withTempHum) { /// Temperature diff --git a/src/AgValue.h b/src/AgValue.h index cb723a0..5efea18 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -53,8 +53,10 @@ public: PM25_SP, // PM2.5 standard particle PM10_SP, // PM10 standard particle PM03_PC, // Particle 0.3 count + PM05_PC, // Particle 0.5 count PM01_PC, // Particle 1.0 count PM25_PC, // Particle 2.5 count + PM5_PC, // Particle 5.0 count PM10_PC, // Particle 10 count }; @@ -142,8 +144,10 @@ private: IntegerValue _pm_25_sp[2]; // pm 2.5 standard particle IntegerValue _pm_10_sp[2]; // pm 10 standard particle IntegerValue _pm_03_pc[2]; // particle count 0.3 + IntegerValue _pm_05_pc[2]; // particle count 0.5 IntegerValue _pm_01_pc[2]; // particle count 1.0 IntegerValue _pm_25_pc[2]; // particle count 2.5 + IntegerValue _pm_5_pc[2]; // particle count 5.0 IntegerValue _pm_10_pc[2]; // particle count 10 bool _debug = false; diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 5b8cdfd..974bb18 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -136,6 +136,13 @@ int PMS5003::getPm03ParticleCount(void) { */ int PMS5003::getPm01ParticleCount(void) { return pms.getCount1_0(); } +/** + * @brief Read particle 0.5 count + * + * @return int particle 0.5 count index + */ +int PMS5003::getPm05ParticleCount(void) { return pms.getCount0_5(); } + /** * @brief Read particle 2.5 count * @@ -143,6 +150,13 @@ int PMS5003::getPm01ParticleCount(void) { return pms.getCount1_0(); } */ int PMS5003::getPm25ParticleCount(void) { return pms.getCount2_5(); } +/** + * @brief Read particle 5.0 count + * + * @return int particle 5.0 count index + */ +int PMS5003::getPm5ParticleCount(void) { return pms.getCount5_0(); } + /** * @brief Read particle 10 count * diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index 0e3ca13..594964e 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -35,8 +35,10 @@ public: int getPm10Sp(void); // Particle count int getPm03ParticleCount(void); + int getPm05ParticleCount(void); int getPm01ParticleCount(void); int getPm25ParticleCount(void); + int getPm5ParticleCount(void); int getPm10ParticleCount(void); int convertPm25ToUsAqi(int pm25); diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index e43ea91..27d6313 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -158,6 +158,13 @@ int PMS5003T::getPm03ParticleCount(void) { return pms.getCount0_3(); } +/** + * @brief Read particle 0.5 count + * + * @return int particle 0.5 count index + */ +int PMS5003T::getPm05ParticleCount(void) { return pms.getCount0_5(); } + /** * @brief Read particle 1.0 count * diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 695439b..73017e7 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -38,6 +38,7 @@ public: int getPm10Sp(void); // Particle count int getPm03ParticleCount(void); + int getPm05ParticleCount(void); int getPm01ParticleCount(void); int getPm25ParticleCount(void); From 4af5ca26655dfae4abf21ef6612072d601b2e17a Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 31 Oct 2024 21:12:29 +0700 Subject: [PATCH 47/69] Update local server docs --- docs/local-server.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/local-server.md b/docs/local-server.md index d65aee1..b6460a8 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -59,8 +59,10 @@ You get the following response: | `pm10Standard` | Number | PM10 in ug/m3 (standard particle) | | `rco2` | Number | CO2 in ppm | | `pm003Count` | Number | Particle count 0.3um per dL | +| `pm005Count` | Number | Particle count 0.5um per dL | | `pm01Count` | Number | Particle count 1.0um per dL | | `pm25Count` | Number | Particle count 2.5um per dL | +| `pm05Count` | Number | Particle count 5.0um per dL | | `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) | | `atmp` | Number | Temperature in Degrees Celsius | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | From 3004a82e7e22a516ca04f4d43b4f8cf9b75bdc76 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 31 Oct 2024 21:16:29 +0700 Subject: [PATCH 48/69] pms disconnected log --- src/PMS/PMS.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 68df585..88f17bb 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -151,6 +151,7 @@ void PMSBase::readPackage(Stream *serial) { if (ms >= READ_PACKGE_TIMEOUT) { lastPackage = 0; _connected = false; + Serial.println("PMS disconnected"); } } } From 4d40ae421c8eae627157d9a86c7a8863b45ce53d Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 31 Oct 2024 21:17:28 +0700 Subject: [PATCH 49/69] Comment data to post Payload already print out on toString() measurement --- src/AgApiClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgApiClient.cpp b/src/AgApiClient.cpp index 5933c30..8a72a67 100644 --- a/src/AgApiClient.cpp +++ b/src/AgApiClient.cpp @@ -130,7 +130,7 @@ bool AgApiClient::postToServer(String data) { client.end(); logInfo(String("POST: ") + uri); - logInfo(String("DATA: ") + data); + // logInfo(String("DATA: ") + data); logInfo(String("Return code: ") + String(retCode)); if ((retCode == 200) || (retCode == 429)) { From 3ebcc584a4dab37417e2764d10da24b1b966f16e Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Thu, 31 Oct 2024 21:19:46 +0700 Subject: [PATCH 50/69] Update localserver docs --- docs/local-server.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index b6460a8..ad04f5e 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -62,8 +62,8 @@ You get the following response: | `pm005Count` | Number | Particle count 0.5um per dL | | `pm01Count` | Number | Particle count 1.0um per dL | | `pm25Count` | Number | Particle count 2.5um per dL | -| `pm05Count` | Number | Particle count 5.0um per dL | -| `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) | +| `pm05Count` | Number | Particle count 5.0um per dL (only for indoor monitor) | +| `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) | | `atmp` | Number | Temperature in Degrees Celsius | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | | `rhum` | Number | Relative Humidity | From 1d991b1004b920dc714ab1ad6154b99db28451e7 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 1 Nov 2024 17:26:18 +0700 Subject: [PATCH 51/69] json key field from constants --- src/AgValue.cpp | 267 +++++++++++++++++++++++++----------------------- 1 file changed, 142 insertions(+), 125 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 969d468..a54dddc 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -3,6 +3,23 @@ #include "AirGradient.h" #define json_prop_pmFirmware "firmware" +#define json_prop_pm01Ae "pm01" +#define json_prop_pm25Ae "pm02" +#define json_prop_pm10Ae "pm10" +#define json_prop_pm01Sp "pm01Standard" +#define json_prop_pm25Sp "pm02Standard" +#define json_prop_pm10Sp "pm10Standard" +#define json_prop_pm25Compensated "pm02Compensated" +#define json_prop_pm03Count "pm003Count" +#define json_prop_pm05Count "pm005Count" +#define json_prop_pm1Count "pm01Count" +#define json_prop_pm25Count "pm25Count" +#define json_prop_pm5Count "pm05Count" +#define json_prop_pm10Count "pm10Count" +#define json_prop_temp "atmp" +#define json_prop_tempCompensated "atmpCompensated" +#define json_prop_rhum "rhum" +#define json_prop_rhumCompensated "rhumCompensated" void Measurements::maxPeriod(MeasurementType type, int max) { switch (type) { @@ -566,16 +583,16 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati if (config.hasSensorSHT) { // Add temperature if (utils::isValidTemperature(_temperature[0].update.avg)) { - indoor["atmp"] = ag.round2(_temperature[0].update.avg); + indoor[json_prop_temp] = ag.round2(_temperature[0].update.avg); if (localServer) { - indoor["atmpCompensated"] = 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["rhum"] = ag.round2(_humidity[0].update.avg); + indoor[json_prop_rhum] = ag.round2(_humidity[0].update.avg); if (localServer) { - indoor["rhumCompensated"] = ag.round2(_humidity[0].update.avg); + indoor[json_prop_rhumCompensated] = ag.round2(_humidity[0].update.avg); } } } @@ -585,7 +602,7 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].update.avg)) { float pm25 = ag.pms5003.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); if (utils::isValidPm(pm25)) { - indoor["pm02Compensated"] = ag.round2(pm25); + indoor[json_prop_pm25Compensated] = ag.round2(pm25); } } } @@ -606,47 +623,47 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem ch = ch - 1; if (utils::isValidPm(_pm_01[ch].update.avg)) { - pms["pm01"] = 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["pm02"] = 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["pm10"] = 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["pm01Standard"] = 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["pm02Standard"] = 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["pm10Standard"] = 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["pm003Count"] = 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["pm005Count"] = 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["pm01Count"] = 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["pm02Count"] = 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["pm05Count"] = 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["pm10Count"] = ag.round2(_pm_10_pc[ch].update.avg); + pms[json_prop_pm10Count] = ag.round2(_pm_10_pc[ch].update.avg); } } @@ -654,23 +671,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["atmp"] = 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); if (utils::isValidTemperature(_vc)) { - pms["atmpCompensated"] = ag.round2(_vc); + pms[json_prop_tempCompensated] = ag.round2(_vc); } } } // Set humidity if valid if (utils::isValidHumidity(_humidity[ch].update.avg)) { - pms["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); if (utils::isValidTemperature(_vc)) { - pms["rhumCompensated"] = ag.round2(_vc); + pms[json_prop_rhumCompensated] = ag.round2(_vc); } } } @@ -683,7 +700,7 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem // the same base function float pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); if (utils::isValidPm(pm25)) { - pms["pm02Compensated"] = ag.round2(pm25); + pms[json_prop_pm25Compensated] = ag.round2(pm25); } } } @@ -699,144 +716,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["pm01"] = ag.round2(avg); - pms["channels"]["1"]["pm01"] = ag.round2(_pm_01[0].update.avg); - pms["channels"]["2"]["pm01"] = 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["pm01"] = ag.round2(_pm_01[0].update.avg); - pms["channels"]["1"]["pm01"] = 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["pm01"] = ag.round2(_pm_01[1].update.avg); - pms["channels"]["2"]["pm01"] = 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["pm02"] = ag.round2(avg); - pms["channels"]["1"]["pm02"] = ag.round2(_pm_25[0].update.avg); - pms["channels"]["2"]["pm02"] = 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["pm02"] = ag.round2(_pm_25[0].update.avg); - pms["channels"]["1"]["pm02"] = 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["pm02"] = ag.round2(_pm_25[1].update.avg); - pms["channels"]["2"]["pm02"] = 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["pm10"] = ag.round2(avg); - pms["channels"]["1"]["pm10"] = ag.round2(_pm_10[0].update.avg); - pms["channels"]["2"]["pm10"] = 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["pm10"] = ag.round2(_pm_10[0].update.avg); - pms["channels"]["1"]["pm10"] = 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["pm10"] = ag.round2(_pm_10[1].update.avg); - pms["channels"]["2"]["pm10"] = 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["pm01Standard"] = ag.round2(avg); - pms["channels"]["1"]["pm01Standard"] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["2"]["pm01Standard"] = 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["pm01Standard"] = ag.round2(_pm_01_sp[0].update.avg); - pms["channels"]["1"]["pm01Standard"] = 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["pm01Standard"] = ag.round2(_pm_01_sp[1].update.avg); - pms["channels"]["2"]["pm01Standard"] = 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["pm02Standard"] = ag.round2(avg); - pms["channels"]["1"]["pm02Standard"] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["2"]["pm02Standard"] = 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["pm02Standard"] = ag.round2(_pm_25_sp[0].update.avg); - pms["channels"]["1"]["pm02Standard"] = 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["pm02Standard"] = ag.round2(_pm_25_sp[1].update.avg); - pms["channels"]["2"]["pm02Standard"] = 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["pm10Standard"] = ag.round2(avg); - pms["channels"]["1"]["pm10Standard"] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["2"]["pm10Standard"] = 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["pm10Standard"] = ag.round2(_pm_10_sp[0].update.avg); - pms["channels"]["1"]["pm10Standard"] = 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["pm10Standard"] = ag.round2(_pm_10_sp[1].update.avg); - pms["channels"]["2"]["pm10Standard"] = 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["pm003Count"] = ag.round2(avg); - pms["channels"]["1"]["pm003Count"] = ag.round2(_pm_03_pc[0].update.avg); - pms["channels"]["2"]["pm003Count"] = 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::isValidPm(_pm_03_pc[0].update.avg)) { - pms["pm003Count"] = ag.round2(_pm_03_pc[0].update.avg); - pms["channels"]["1"]["pm003Count"] = 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::isValidPm(_pm_03_pc[1].update.avg)) { - pms["pm003Count"] = ag.round2(_pm_03_pc[1].update.avg); - pms["channels"]["2"]["pm003Count"] = 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["pm005Count"] = ag.round2(avg); - pms["channels"]["1"]["pm005Count"] = ag.round2(_pm_05_pc[0].update.avg); - pms["channels"]["2"]["pm005Count"] = 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::isValidPm(_pm_05_pc[0].update.avg)) { - pms["pm005Count"] = ag.round2(_pm_05_pc[0].update.avg); - pms["channels"]["1"]["pm005Count"] = 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::isValidPm(_pm_05_pc[1].update.avg)) { - pms["pm005Count"] = ag.round2(_pm_05_pc[1].update.avg); - pms["channels"]["2"]["pm005Count"] = 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["pm01Count"] = ag.round2(avg); - pms["channels"]["1"]["pm01Count"] = ag.round2(_pm_01_pc[0].update.avg); - pms["channels"]["2"]["pm01Count"] = 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::isValidPm(_pm_01_pc[0].update.avg)) { - pms["pm01Count"] = ag.round2(_pm_01_pc[0].update.avg); - pms["channels"]["1"]["pm01Count"] = 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::isValidPm(_pm_01_pc[1].update.avg)) { - pms["pm01Count"] = ag.round2(_pm_01_pc[1].update.avg); - pms["channels"]["2"]["pm01Count"] = 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["pm25Count"] = ag.round2(avg); - pms["channels"]["1"]["pm25Count"] = ag.round2(_pm_25_pc[0].update.avg); - pms["channels"]["2"]["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::isValidPm(_pm_25_pc[0].update.avg)) { - pms["pm25Count"] = ag.round2(_pm_25_pc[0].update.avg); - pms["channels"]["1"]["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::isValidPm(_pm_25_pc[1].update.avg)) { - pms["pm25Count"] = ag.round2(_pm_25_pc[1].update.avg); - pms["channels"]["2"]["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 @@ -848,40 +865,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["atmp"] = ag.round2(temperature); - pms["channels"]["1"]["atmp"] = ag.round2(_temperature[0].update.avg); - pms["channels"]["2"]["atmp"] = 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["atmpCompensated"] = ag.round2(temp); - pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); - pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); + 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["atmp"] = ag.round2(_temperature[0].update.avg); - pms["channels"]["1"]["atmp"] = 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["atmpCompensated"] = ag.round2(temp1); - pms["channels"]["1"]["atmpCompensated"] = ag.round2(temp1); + 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["atmp"] = ag.round2(_temperature[1].update.avg); - pms["channels"]["2"]["atmp"] = 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["atmpCompensated"] = ag.round2(temp2); - pms["channels"]["2"]["atmpCompensated"] = ag.round2(temp2); + pms[json_prop_tempCompensated] = ag.round2(temp2); + pms["channels"]["2"][json_prop_tempCompensated] = ag.round2(temp2); } } @@ -889,40 +906,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["rhum"] = ag.round2(humidity); - pms["channels"]["1"]["rhum"] = ag.round2(_humidity[0].update.avg); - pms["channels"]["2"]["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["rhumCompensated"] = ag.round2(hum); - pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); - pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); + 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["rhum"] = ag.round2(_humidity[0].update.avg); - pms["channels"]["1"]["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["rhumCompensated"] = ag.round2(hum1); - pms["channels"]["1"]["rhumCompensated"] = ag.round2(hum1); + 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["rhum"] = ag.round2(_humidity[1].update.avg); - pms["channels"]["2"]["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["rhumCompensated"] = ag.round2(hum2); - pms["channels"]["2"]["rhumCompensated"] = ag.round2(hum2); + pms[json_prop_rhumCompensated] = ag.round2(hum2); + pms["channels"]["2"][json_prop_rhumCompensated] = ag.round2(hum2); } } @@ -934,21 +951,21 @@ JSONVar Measurements::buildPMS(AirGradient &ag, int ch, bool allCh, bool withTem 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"]["pm02Compensated"] = ag.round2(pm25_comp1); + 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"]["pm02Compensated"] = ag.round2(pm25_comp2); + 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["pm02Compensated"] = 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["pm02Compensated"] = ag.round2(pm25_comp1); + pms[json_prop_pm25Compensated] = ag.round2(pm25_comp1); } else if (utils::isValidPm(pm25_comp2)) { - pms["pm02Compensated"] = ag.round2(pm25_comp2); + pms[json_prop_pm25Compensated] = ag.round2(pm25_comp2); } } } From bb754edc515c78a519b5d7546b0232f2c22acd29 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 1 Nov 2024 17:44:11 +0700 Subject: [PATCH 52/69] Add other sensor json key field as const --- src/AgValue.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index a54dddc..d1ea117 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -20,6 +20,11 @@ #define json_prop_tempCompensated "atmpCompensated" #define json_prop_rhum "rhum" #define json_prop_rhumCompensated "rhumCompensated" +#define json_prop_tvoc "tvocIndex" +#define json_prop_tvocRaw "tvocRaw" +#define json_prop_nox "noxIndex" +#define json_prop_noxRaw "noxRaw" +#define json_prop_co2 "rco2" void Measurements::maxPeriod(MeasurementType type, int max) { switch (type) { @@ -489,22 +494,22 @@ String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, // CO2 if (config.hasSensorS8 && utils::isValidCO2(_co2.update.avg)) { - root["rco2"] = 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["tvocIndex"] = ag.round2(_tvoc.update.avg); + root[json_prop_tvoc] = ag.round2(_tvoc.update.avg); } if (utils::isValidVOC(_tvoc_raw.update.avg)) { - root["tvocRaw"] = ag.round2(_tvoc_raw.update.avg); + root[json_prop_tvocRaw] = ag.round2(_tvoc_raw.update.avg); } if (utils::isValidNOx(_nox.update.avg)) { - root["noxIndex"] = ag.round2(_nox.update.avg); + root[json_prop_nox] = ag.round2(_nox.update.avg); } if (utils::isValidNOx(_nox_raw.update.avg)) { - root["noxRaw"] = ag.round2(_nox_raw.update.avg); + root[json_prop_noxRaw] = ag.round2(_nox_raw.update.avg); } } From 3a9bb16c0933dc494615942715f81c8df443c2e6 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Fri, 1 Nov 2024 17:46:58 +0700 Subject: [PATCH 53/69] Change json key name for particle count 2.5 and 5.0 --- docs/local-server.md | 4 ++-- src/AgValue.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index ad04f5e..247ec06 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -61,8 +61,8 @@ You get the following response: | `pm003Count` | Number | Particle count 0.3um per dL | | `pm005Count` | Number | Particle count 0.5um per dL | | `pm01Count` | Number | Particle count 1.0um per dL | -| `pm25Count` | Number | Particle count 2.5um per dL | -| `pm05Count` | Number | Particle count 5.0um per dL (only for indoor monitor) | +| `pm02Count` | Number | Particle count 2.5um per dL | +| `pm50Count` | Number | Particle count 5.0um per dL (only for indoor monitor) | | `pm10Count` | Number | Particle count 10um per dL (only for indoor monitor) | | `atmp` | Number | Temperature in Degrees Celsius | | `atmpCompensated` | Number | Temperature in Degrees Celsius with correction applied | diff --git a/src/AgValue.cpp b/src/AgValue.cpp index d1ea117..fb7135a 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -13,8 +13,8 @@ #define json_prop_pm03Count "pm003Count" #define json_prop_pm05Count "pm005Count" #define json_prop_pm1Count "pm01Count" -#define json_prop_pm25Count "pm25Count" -#define json_prop_pm5Count "pm05Count" +#define json_prop_pm25Count "pm02Count" +#define json_prop_pm5Count "pm50Count" #define json_prop_pm10Count "pm10Count" #define json_prop_temp "atmp" #define json_prop_tempCompensated "atmpCompensated" From 16c932962a60c72296af04c1b9642d304359cc9c Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 00:10:08 +0700 Subject: [PATCH 54/69] Handle pm correction algorithm from ag server config --- src/AgConfigure.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++ src/AgConfigure.h | 13 +++++ src/App/AppDef.h | 12 +++++ 3 files changed, 139 insertions(+) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 1446d1a..5e438af 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -22,6 +22,18 @@ const char *LED_BAR_MODE_NAMES[] = { [LedBarModeCO2] = "co2", }; +const char *PM_CORRECTION_ALGORITHM_NAMES[] = { + [Unknown] = "-", // This is only to pass "non-trivial designated initializers" error + [None] = "none", + [EPA_2021] = "epa_2021", + [SLR_PMS5003_20220802] = "slr_PMS5003_20220802", + [SLR_PMS5003_20220803] = "slr_PMS5003_20220803", + [SLR_PMS5003_20220824] = "slr_PMS5003_20220824", + [SLR_PMS5003_20231030] = "slr_PMS5003_20231030", + [SLR_PMS5003_20231218] = "slr_PMS5003_20231218", + [SLR_PMS5003_20240104] = "slr_PMS5003_20240104", +}; + #define JSON_PROP_NAME(name) jprop_##name #define JSON_PROP_DEF(name) const char *JSON_PROP_NAME(name) = #name @@ -87,6 +99,96 @@ String Configuration::getLedBarModeName(LedBarMode mode) { return String("unknown"); } +PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) { + // Loop through all algorithm names in the PM_CORRECTION_ALGORITHM_NAMES array + // If the input string matches an algorithm name, return the corresponding enum value + // Else return Unknown + for (int idx = 0; + idx < sizeof(PM_CORRECTION_ALGORITHM_NAMES) / sizeof(PM_CORRECTION_ALGORITHM_NAMES[0]); + idx++) { + if (algorithm == PM_CORRECTION_ALGORITHM_NAMES[idx]) { + return (PMCorrectionAlgorithm)idx; + } + } + + return Unknown; +} + +bool Configuration::parsePmCorrection(JSONVar &json) { + if (!json.hasOwnProperty("corrections")) { + // TODO: need to response message? + Serial.println("corrections not found"); + return false; + } + + JSONVar corrections = json["corrections"]; + if (!corrections.hasOwnProperty("pm02")) { + Serial.println("pm02 not found"); + return false; + } + + JSONVar pm02 = corrections["pm02"]; + if (!pm02.hasOwnProperty("correctionAlgorithm")) { + Serial.println("correctionAlgorithm not found"); + return false; + } + + // Check algorithm + const char *algorithm = (const char *)pm02["correctionAlgorithm"]; + PMCorrectionAlgorithm algo = matchPmAlgorithm(algorithm); + if (algo == Unknown) { + Serial.println("Unknown algorithm"); + return false; + } + Serial.printf("Correction algorithm: %s\n", algorithm); + + // If algo is None or EPA_2021, no need to check slr + // But first check if pmCorrection different from algo + if (algo == None || algo == EPA_2021) { + if (pmCorrection.algorithm != algo) { + pmCorrection.algorithm = algo; + pmCorrection.changed = true; + Serial.println("PM2.5 correction updated"); + return true; + } + + return false; + } + + // Check if pm02 has slr object + if (!pm02.hasOwnProperty("slr")) { + Serial.println("slr not found"); + return false; + } + + JSONVar slr = pm02["slr"]; + + // Validate required slr properties exist + if (!slr.hasOwnProperty("intercept") || !slr.hasOwnProperty("scalingFactor") || + !slr.hasOwnProperty("useEpa2021")) { + Serial.println("Missing required slr properties"); + return false; + } + + // Compare with current pmCorrection + if (pmCorrection.algorithm == algo && pmCorrection.intercept == (double)slr["intercept"] && + pmCorrection.scalingFactor == (double)slr["scalingFactor"] && + pmCorrection.useEPA == (bool)slr["useEpa2021"]) { + return false; // No changes needed + } + + // Update pmCorrection with new values + pmCorrection.algorithm = algo; + pmCorrection.intercept = (double)slr["intercept"]; + pmCorrection.scalingFactor = (double)slr["scalingFactor"]; + pmCorrection.useEPA = (bool)slr["useEpa2021"]; + pmCorrection.changed = true; + + // Correction values were updated + Serial.println("PM2.5 correction updated"); + return true; +} + /** * @brief Save configure to device storage (EEPROM) * @@ -171,6 +273,13 @@ void Configuration::defaultConfig(void) { jconfig[jprop_offlineMode] = jprop_offlineMode_default; jconfig[jprop_monitorDisplayCompensatedValues] = jprop_monitorDisplayCompensatedValues_default; + // PM2.5 correction + pmCorrection.algorithm = None; + pmCorrection.changed = false; + pmCorrection.intercept = -1; + pmCorrection.scalingFactor = -1; + pmCorrection.useEPA = false; + saveConfig(); } @@ -667,6 +776,11 @@ bool Configuration::parse(String data, bool isLocal) { } } + // Corrections + if (parsePmCorrection(root)) { + changed = true; + } + if (changed) { udpated = true; saveConfig(); diff --git a/src/AgConfigure.h b/src/AgConfigure.h index a899b64..3a7be13 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -5,8 +5,18 @@ #include "Main/PrintLog.h" #include "AirGradient.h" #include +#include "Libraries/Arduino_JSON/src/Arduino_JSON.h" class Configuration : public PrintLog { +public: + struct PMCorrection { + PMCorrectionAlgorithm algorithm; + int intercept; + int scalingFactor; + bool useEPA; // EPA 2021 + bool changed; + }; + private: bool co2CalibrationRequested; bool ledBarTestRequested; @@ -19,10 +29,13 @@ private: String otaNewFirmwareVersion; bool _offlineMode = false; bool _ledBarModeChanged = false; + PMCorrection pmCorrection; AirGradient* ag; String getLedBarModeName(LedBarMode mode); + PMCorrectionAlgorithm matchPmAlgorithm(String algorithm); + bool parsePmCorrection(JSONVar &json); void saveConfig(void); void loadConfig(void); void defaultConfig(void); diff --git a/src/App/AppDef.h b/src/App/AppDef.h index 3bb9320..1e114e7 100644 --- a/src/App/AppDef.h +++ b/src/App/AppDef.h @@ -94,6 +94,18 @@ enum ConfigurationControl { ConfigurationControlBoth }; +enum PMCorrectionAlgorithm { + Unknown, // Unknown algorithm + None, // No PM correction + EPA_2021, + SLR_PMS5003_20220802, + SLR_PMS5003_20220803, + SLR_PMS5003_20220824, + SLR_PMS5003_20231030, + SLR_PMS5003_20231218, + SLR_PMS5003_20240104, +}; + enum AgFirmwareMode { FW_MODE_I_9PSL, /** ONE_INDOOR */ FW_MODE_O_1PST, /** PMS5003T, S8 and SGP41 */ From ea46b812c13403d7c6bfa47173deee718849ab27 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 00:53:33 +0700 Subject: [PATCH 55/69] Handle saving back to eeprom rename the function --- src/AgConfigure.cpp | 17 ++++++++++++++--- src/AgConfigure.h | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 5e438af..16f91b0 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -1,5 +1,4 @@ #include "AgConfigure.h" -#include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #if ESP32 #include "FS.h" #include "SPIFFS.h" @@ -54,6 +53,7 @@ JSON_PROP_DEF(co2CalibrationRequested); JSON_PROP_DEF(ledBarTestRequested); JSON_PROP_DEF(offlineMode); JSON_PROP_DEF(monitorDisplayCompensatedValues); +JSON_PROP_DEF(corrections); #define jprop_model_default "" #define jprop_country_default "TH" @@ -114,7 +114,7 @@ PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) { return Unknown; } -bool Configuration::parsePmCorrection(JSONVar &json) { +bool Configuration::updatePmCorrection(JSONVar &json) { if (!json.hasOwnProperty("corrections")) { // TODO: need to response message? Serial.println("corrections not found"); @@ -777,7 +777,9 @@ bool Configuration::parse(String data, bool isLocal) { } // Corrections - if (parsePmCorrection(root)) { + if (updatePmCorrection(root)) { + // Deep copy corrections from root to jconfig, so it will be saved later + jconfig[jprop_corrections] = JSON.parse(JSON.stringify(root["corrections"])); changed = true; } @@ -1232,6 +1234,15 @@ void Configuration::toConfig(const char *buf) { jprop_monitorDisplayCompensatedValues_default; } + + // 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 + updatePmCorrection(jconfig); + if (changed) { saveConfig(); } diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 3a7be13..58d0ee8 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -35,7 +35,7 @@ private: String getLedBarModeName(LedBarMode mode); PMCorrectionAlgorithm matchPmAlgorithm(String algorithm); - bool parsePmCorrection(JSONVar &json); + bool updatePmCorrection(JSONVar &json); void saveConfig(void); void loadConfig(void); void defaultConfig(void); From 0275aee370b69624398dc7005c334eee21b8035f Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 10:34:35 +0700 Subject: [PATCH 56/69] Copy correction object to jconfig --- src/AgConfigure.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 16f91b0..7d311e1 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -146,6 +146,7 @@ bool Configuration::updatePmCorrection(JSONVar &json) { // But first check if pmCorrection different from algo if (algo == None || algo == EPA_2021) { if (pmCorrection.algorithm != algo) { + jconfig[jprop_corrections]["pm02"]["correctionAlgorithm"] = algorithm; pmCorrection.algorithm = algo; pmCorrection.changed = true; Serial.println("PM2.5 correction updated"); @@ -177,6 +178,10 @@ bool Configuration::updatePmCorrection(JSONVar &json) { return false; // No changes needed } + // Deep copy corrections from root to jconfig, so it will be saved later + jconfig[jprop_corrections] = corrections; + Serial.println("Correction copied"); + // Update pmCorrection with new values pmCorrection.algorithm = algo; pmCorrection.intercept = (double)slr["intercept"]; @@ -778,8 +783,6 @@ bool Configuration::parse(String data, bool isLocal) { // Corrections if (updatePmCorrection(root)) { - // Deep copy corrections from root to jconfig, so it will be saved later - jconfig[jprop_corrections] = JSON.parse(JSON.stringify(root["corrections"])); changed = true; } From 641003f9d148b94b9010d694403d9dec58e991df Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 10:41:01 +0700 Subject: [PATCH 57/69] Get pm config function --- src/AgConfigure.cpp | 10 ++++++++++ src/AgConfigure.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 7d311e1..0a6a2cf 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -1344,3 +1344,13 @@ String Configuration::newFirmwareVersion(void) { otaNewFirmwareVersion = String(""); return newFw; } + +bool Configuration::isPMCorrectionChanged(void) { + bool changed = pmCorrection.changed; + pmCorrection.changed = false; + return changed; +} + +PMCorrection Configuration::getPMCorrection(void) { + return pmCorrection; +} diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 58d0ee8..94c2606 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -96,6 +96,8 @@ public: void setOfflineModeWithoutSave(bool offline); bool isLedBarModeChanged(void); bool isMonitorDisplayCompensatedValues(void); + bool isPMCorrectionChanged(void); + PMCorrection getPMCorrection(void); }; #endif /** _AG_CONFIG_H_ */ From a98d77e0c3694197bf8cfc57cd4dd28a56b353bc Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 11:02:36 +0700 Subject: [PATCH 58/69] slr pm2.5 correction implementation --- src/PMS/PMS.cpp | 30 ++++++++++++++++++++++++++++++ src/PMS/PMS.h | 1 + src/PMS/PMS5003.cpp | 4 ++++ src/PMS/PMS5003.h | 1 + src/PMS/PMS5003T.cpp | 4 ++++ src/PMS/PMS5003T.h | 1 + 6 files changed, 41 insertions(+) diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 88f17bb..feeb4b4 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -314,6 +314,36 @@ int PMSBase::pm25ToAQI(int pm02) { return 500; } + +/** + * @brief SLR correction for PM2.5 + * + * Reference: https://www.airgradient.com/blog/low-readings-from-pms5003/ + * + * @param pm25 PM2.5 raw value + * @param pm003Count PM0.3 count + * @param scalingFactor Scaling factor + * @param intercept Intercept + * @return float Calibrated PM2.5 value + */ +float PMSBase::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) { + float calibrated; + + float lowCalibrated = (scalingFactor * pm003Count) + intercept; + if (lowCalibrated < 31) { + calibrated = lowCalibrated; + } else { + calibrated = pm25; + } + + // No negative value for pm2.5 + if (calibrated < 0) { + return 0.0; + } + + return calibrated; +} + /** * @brief Correction PM2.5 * diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index 460a87a..c363c69 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -39,6 +39,7 @@ public: uint8_t getErrorCode(void); int pm25ToAQI(int pm02); + float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept); float compensate(float pm25, float humidity); private: diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index 974bb18..d20c4e9 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -172,6 +172,10 @@ int PMS5003::getPm10ParticleCount(void) { return pms.getCount10(); } */ int PMS5003::convertPm25ToUsAqi(int pm25) { return pms.pm25ToAQI(pm25); } +float PMS5003::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) { + return pms.slrCorrection(pm25, pm003Count, scalingFactor, intercept); +} + /** * @brief Correct PM2.5 * diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index 594964e..bd2cf7b 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -42,6 +42,7 @@ public: int getPm10ParticleCount(void); int convertPm25ToUsAqi(int pm25); + float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept); float compensate(float pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index 27d6313..e50d58a 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -205,6 +205,10 @@ float PMS5003T::getRelativeHumidity(void) { return pms.getHum() / 10.0f; } +float PMS5003T::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) { + return pms.slrCorrection(pm25, pm003Count, scalingFactor, intercept); +} + /** * @brief Correct PM2.5 * diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 73017e7..4f09e8b 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -45,6 +45,7 @@ public: int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); + float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept); float compensate(float pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); From 5867d0f1d5498b4e46f380ef3e29b20d7b59c57d Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 14:40:35 +0700 Subject: [PATCH 59/69] Fix pmcorrection member datatype Log using printlog Function to check if correction is not none --- src/AgConfigure.cpp | 23 ++++++++++++++++------- src/AgConfigure.h | 5 +++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 0a6a2cf..68f62ea 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -134,13 +134,13 @@ bool Configuration::updatePmCorrection(JSONVar &json) { } // Check algorithm - const char *algorithm = (const char *)pm02["correctionAlgorithm"]; + String algorithm = pm02["correctionAlgorithm"]; PMCorrectionAlgorithm algo = matchPmAlgorithm(algorithm); if (algo == Unknown) { - Serial.println("Unknown algorithm"); + logInfo("Unknown algorithm"); return false; } - Serial.printf("Correction algorithm: %s\n", algorithm); + logInfo("Correction algorithm: " + algorithm); // If algo is None or EPA_2021, no need to check slr // But first check if pmCorrection different from algo @@ -149,7 +149,7 @@ bool Configuration::updatePmCorrection(JSONVar &json) { jconfig[jprop_corrections]["pm02"]["correctionAlgorithm"] = algorithm; pmCorrection.algorithm = algo; pmCorrection.changed = true; - Serial.println("PM2.5 correction updated"); + logInfo("PM2.5 correction updated"); return true; } @@ -180,7 +180,6 @@ bool Configuration::updatePmCorrection(JSONVar &json) { // Deep copy corrections from root to jconfig, so it will be saved later jconfig[jprop_corrections] = corrections; - Serial.println("Correction copied"); // Update pmCorrection with new values pmCorrection.algorithm = algo; @@ -190,7 +189,7 @@ bool Configuration::updatePmCorrection(JSONVar &json) { pmCorrection.changed = true; // Correction values were updated - Serial.println("PM2.5 correction updated"); + logInfo("PM2.5 correction updated"); return true; } @@ -1351,6 +1350,16 @@ bool Configuration::isPMCorrectionChanged(void) { return changed; } -PMCorrection Configuration::getPMCorrection(void) { +/** + * @brief Check if PM correction is enabled + * + * @return true if PM correction algorithm is not None, otherwise false + */ +bool Configuration::isPMCorrectionEnabled(void) { + PMCorrection pmCorrection = getPMCorrection(); + return pmCorrection.algorithm != PMCorrectionAlgorithm::None; +} + +Configuration::PMCorrection Configuration::getPMCorrection(void) { return pmCorrection; } diff --git a/src/AgConfigure.h b/src/AgConfigure.h index 94c2606..f351fab 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -11,8 +11,8 @@ class Configuration : public PrintLog { public: struct PMCorrection { PMCorrectionAlgorithm algorithm; - int intercept; - int scalingFactor; + float intercept; + float scalingFactor; bool useEPA; // EPA 2021 bool changed; }; @@ -97,6 +97,7 @@ public: bool isLedBarModeChanged(void); bool isMonitorDisplayCompensatedValues(void); bool isPMCorrectionChanged(void); + bool isPMCorrectionEnabled(void); PMCorrection getPMCorrection(void); }; From 7b0381dea315e9bdbfce45b203e6e3cbf37d92bf Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 14:44:32 +0700 Subject: [PATCH 60/69] Apply pm correction to display and led bar --- src/AgOledDisplay.cpp | 10 ++++------ src/AgStateMachine.cpp | 4 ++-- src/AgValue.cpp | 33 +++++++++++++++++++++++++++++++++ src/AgValue.h | 12 ++++++++++++ 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 5661f21..7f11bb5 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -314,13 +314,11 @@ void OledDisplay::showDashboard(const char *status) { /** Draw PM2.5 value */ int pm25 = value.get(Measurements::PM25); - if (utils::isValidPm(pm25)) { - /** Compensate PM2.5 value. */ - if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) { - pm25 = ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity)); - logInfo("PM2.5 compensate: " + String(pm25)); - } + if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { + pm25 = (int)value.getCorrectedPM25(*ag, config); + } + if (utils::isValidPm(pm25)) { if (config.isPmStandardInUSAQI()) { sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); } else { diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 9cbe068..8e0be6a 100644 --- a/src/AgStateMachine.cpp +++ b/src/AgStateMachine.cpp @@ -142,8 +142,8 @@ void StateMachine::co2handleLeds(void) { */ void StateMachine::pm25handleLeds(void) { int pm25Value = value.get(Measurements::PM25); - if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) { - pm25Value = ag->pms5003.compensate(pm25Value, value.getFloat(Measurements::Humidity)); + if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { + pm25Value = (int)value.getCorrectedPM25(*ag, config); } if (pm25Value < 5) { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index fb7135a..bcf617b 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -1,6 +1,7 @@ #include "AgValue.h" #include "AgConfigure.h" #include "AirGradient.h" +#include "App/AppDef.h" #define json_prop_pmFirmware "firmware" #define json_prop_pm01Ae "pm01" @@ -482,6 +483,38 @@ void Measurements::validateChannel(int ch) { } } +float Measurements::getCorrectedPM25(AirGradient &ag, Configuration &config, bool useAvg, int ch) { + float pm25; + float humidity; + float pm003Count; + int channel = ch - 1; // Array index + if (useAvg) { + // Directly call from the index + pm25 = _pm_25[channel].update.avg; + humidity = _humidity[channel].update.avg; + pm003Count = _pm_03_pc[channel].update.avg; + } else { + pm25 = get(PM25, ch); + humidity = getFloat(Humidity, ch); + pm003Count = get(PM03_PC, ch); + } + + Configuration::PMCorrection pmCorrection = config.getPMCorrection(); + if (pmCorrection.algorithm == PMCorrectionAlgorithm::EPA_2021) { + // EPA correction directly applied + pm25 = ag.pms5003.compensate(pm25, humidity); + } else { + // SLR correction, this is assumes before calling this function, correction algorithm is not None + pm25 = ag.pms5003.slrCorrection(pm25, pm003Count, pmCorrection.scalingFactor, pmCorrection.intercept); + if (pmCorrection.useEPA) { + // Add EPA compensation on top of SLR + pm25 = ag.pms5003.compensate(pm25, humidity); + } + } + + return pm25; +} + String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, Configuration &config) { JSONVar root; diff --git a/src/AgValue.h b/src/AgValue.h index 5efea18..3b129b3 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -114,6 +114,18 @@ public: */ float getFloat(MeasurementType type, int ch = 1); + + /** + * @brief Get the Corrected PM25 object based on the correction algorithm from configuration + * + * @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); + /** * build json payload for every measurements */ From 9fbbea22ffa5ff34497a8ae287aa0ca7d0194701 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 14:52:58 +0700 Subject: [PATCH 61/69] Fix typo --- src/AgConfigure.cpp | 10 +++++----- src/AgConfigure.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 68f62ea..43c97a4 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -773,7 +773,7 @@ bool Configuration::parse(String data, bool isLocal) { if (curVer != newVer) { logInfo("Detected new firmware version: " + newVer); otaNewFirmwareVersion = newVer; - udpated = true; + updated = true; } else { otaNewFirmwareVersion = String(""); } @@ -786,12 +786,12 @@ bool Configuration::parse(String data, bool isLocal) { } if (changed) { - udpated = true; + updated = true; saveConfig(); printConfig(); } else { if (ledBarTestRequested || co2CalibrationRequested) { - udpated = true; + updated = true; } } return true; @@ -978,8 +978,8 @@ String Configuration::getModel(void) { } bool Configuration::isUpdated(void) { - bool updated = this->udpated; - this->udpated = false; + bool updated = this->updated; + this->updated = false; return updated; } diff --git a/src/AgConfigure.h b/src/AgConfigure.h index f351fab..1cca028 100644 --- a/src/AgConfigure.h +++ b/src/AgConfigure.h @@ -20,7 +20,7 @@ public: private: bool co2CalibrationRequested; bool ledBarTestRequested; - bool udpated; + bool updated; String failedMessage; bool _noxLearnOffsetChanged; bool _tvocLearningOffsetChanged; From ade72ff3b8183d04aa7eefca34610ed93ed90ef9 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 16:11:00 +0700 Subject: [PATCH 62/69] Apply correction to transmission payload Only for indoor --- src/AgValue.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/AgValue.cpp b/src/AgValue.cpp index bcf617b..ca148ca 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -638,10 +638,9 @@ JSONVar Measurements::buildIndoor(bool localServer, AirGradient &ag, Configurati // Add pm25 compensated value only if PM2.5 and humidity value is valid if (config.hasSensorPMS1 && utils::isValidPm(_pm_25[0].update.avg)) { if (config.hasSensorSHT && utils::isValidHumidity(_humidity[0].update.avg)) { - float pm25 = ag.pms5003.compensate(_pm_25[0].update.avg, _humidity[0].update.avg); - if (utils::isValidPm(pm25)) { - indoor[json_prop_pm25Compensated] = ag.round2(pm25); - } + // Correction using moving average value + float tmp = getCorrectedPM25(ag, config, true); + indoor[json_prop_pm25Compensated] = ag.round2(tmp); } } From c6961b3ca8ea4da3e1fc0cd51b8c8997dd3f6202 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 16:11:47 +0700 Subject: [PATCH 63/69] Validate raw pm before correction --- src/AgOledDisplay.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/AgOledDisplay.cpp b/src/AgOledDisplay.cpp index 7f11bb5..94dd5e6 100644 --- a/src/AgOledDisplay.cpp +++ b/src/AgOledDisplay.cpp @@ -314,11 +314,10 @@ void OledDisplay::showDashboard(const char *status) { /** Draw PM2.5 value */ int pm25 = value.get(Measurements::PM25); - if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { - pm25 = (int)value.getCorrectedPM25(*ag, config); - } - if (utils::isValidPm(pm25)) { + if (config.hasSensorSHT && config.isPMCorrectionEnabled()) { + pm25 = (int)value.getCorrectedPM25(*ag, config); + } if (config.isPmStandardInUSAQI()) { sprintf(strBuf, "%d", ag->pms5003.convertPm25ToUsAqi(pm25)); } else { From 75f88b00092ba20c70d77e69e48e5cc5730b97cf Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 16:15:46 +0700 Subject: [PATCH 64/69] Remove slr correction for pms5003t --- src/PMS/PMS5003T.cpp | 4 ---- src/PMS/PMS5003T.h | 1 - 2 files changed, 5 deletions(-) diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index e50d58a..27d6313 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -205,10 +205,6 @@ float PMS5003T::getRelativeHumidity(void) { return pms.getHum() / 10.0f; } -float PMS5003T::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) { - return pms.slrCorrection(pm25, pm003Count, scalingFactor, intercept); -} - /** * @brief Correct PM2.5 * diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index 4f09e8b..73017e7 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -45,7 +45,6 @@ public: int convertPm25ToUsAqi(int pm25); float getTemperature(void); float getRelativeHumidity(void); - float slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept); float compensate(float pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); From f49e4a4b370348c51d1d10e42aabe18c2132a13c Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 17:23:15 +0700 Subject: [PATCH 65/69] Fix casting enum issue Previously if algo is slr, it's always consider new update --- src/AgConfigure.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 43c97a4..04efa76 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -103,15 +103,18 @@ PMCorrectionAlgorithm Configuration::matchPmAlgorithm(String algorithm) { // Loop through all algorithm names in the PM_CORRECTION_ALGORITHM_NAMES array // If the input string matches an algorithm name, return the corresponding enum value // Else return Unknown - for (int idx = 0; - idx < sizeof(PM_CORRECTION_ALGORITHM_NAMES) / sizeof(PM_CORRECTION_ALGORITHM_NAMES[0]); - idx++) { - if (algorithm == PM_CORRECTION_ALGORITHM_NAMES[idx]) { - return (PMCorrectionAlgorithm)idx; + + const size_t enumSize = SLR_PMS5003_20240104 + 1; // Get the actual size of the enum + PMCorrectionAlgorithm result = PMCorrectionAlgorithm::Unknown; + + // Loop through enum values + for (size_t enumVal = 0; enumVal < enumSize; enumVal++) { + if (algorithm == PM_CORRECTION_ALGORITHM_NAMES[enumVal]) { + result = static_cast(enumVal); } } - return Unknown; + return result; } bool Configuration::updatePmCorrection(JSONVar &json) { @@ -171,9 +174,13 @@ bool Configuration::updatePmCorrection(JSONVar &json) { 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 pmCorrection - if (pmCorrection.algorithm == algo && pmCorrection.intercept == (double)slr["intercept"] && - pmCorrection.scalingFactor == (double)slr["scalingFactor"] && + if (pmCorrection.algorithm == algo && pmCorrection.intercept == intercept && + pmCorrection.scalingFactor == scalingFactor && pmCorrection.useEPA == (bool)slr["useEpa2021"]) { return false; // No changes needed } @@ -183,8 +190,8 @@ bool Configuration::updatePmCorrection(JSONVar &json) { // Update pmCorrection with new values pmCorrection.algorithm = algo; - pmCorrection.intercept = (double)slr["intercept"]; - pmCorrection.scalingFactor = (double)slr["scalingFactor"]; + pmCorrection.intercept = intercept; + pmCorrection.scalingFactor = scalingFactor; pmCorrection.useEPA = (bool)slr["useEpa2021"]; pmCorrection.changed = true; From d850d27dc15e75276eee1aa8cc365874d3ddbf5d Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 17:31:05 +0700 Subject: [PATCH 66/69] Clear slr when not avail --- src/AgConfigure.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AgConfigure.cpp b/src/AgConfigure.cpp index 04efa76..266886e 100644 --- a/src/AgConfigure.cpp +++ b/src/AgConfigure.cpp @@ -136,6 +136,8 @@ bool Configuration::updatePmCorrection(JSONVar &json) { return false; } + // TODO: Need to have data type check, with error message response if invalid + // Check algorithm String algorithm = pm02["correctionAlgorithm"]; PMCorrectionAlgorithm algo = matchPmAlgorithm(algorithm); @@ -149,7 +151,10 @@ bool Configuration::updatePmCorrection(JSONVar &json) { // But first check if pmCorrection different from algo if (algo == None || algo == EPA_2021) { if (pmCorrection.algorithm != algo) { + // Deep copy corrections from root to jconfig, so it will be saved later jconfig[jprop_corrections]["pm02"]["correctionAlgorithm"] = algorithm; + jconfig[jprop_corrections]["pm02"]["slr"] = JSON.parse("{}"); // Clear slr + // Update pmCorrection with new values pmCorrection.algorithm = algo; pmCorrection.changed = true; logInfo("PM2.5 correction updated"); From 1db8fbefe98a3ab402c457df8e92731b1e046458 Mon Sep 17 00:00:00 2001 From: samuelbles07 Date: Sat, 2 Nov 2024 18:44:44 +0700 Subject: [PATCH 67/69] Corrections from local server Tidy some things --- docs/local-server.md | 72 ++++++++++++++++++++++++++++++++++++++----- src/AgOledDisplay.cpp | 5 +-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/docs/local-server.md b/docs/local-server.md index 247ec06..dfbbd07 100644 --- a/docs/local-server.md +++ b/docs/local-server.md @@ -73,16 +73,17 @@ You get the following response: | `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. | +| `bootCount` | Number | Same as boot property. Required for Home Assistant compatability. (deprecated soon!) | | `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. #### Get Configuration Parameters (GET) -With the path "/config" you can get the current configuration. + +With the path "/config" you can get monitor current configurations. + ```json { "country": "TH", @@ -99,27 +100,39 @@ With the path "/config" you can get the current configuration. "displayBrightness": 100, "offlineMode": false, "model": "I-9PSL", - "monitorDisplayCompensatedValues": true + "monitorDisplayCompensatedValues": true, + "corrections": { + "pm02": { + "correctionAlgorithm": "epa_2021", + "slr": {} + } + } + } } ``` #### Set Configuration Parameters (PUT) -Configuration parameters can be changed with a put request to the monitor, e.g. +Configuration parameters can be changed with a PUT request to the monitor, e.g. Example to force CO2 calibration - ```curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config ``` + ```bash + curl -X PUT -H "Content-Type: application/json" -d '{"co2CalibrationRequested":true}' http://airgradient_84fce612eff4.local/config + ``` Example to set monitor to Celsius - ```curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config ``` + ```bash + curl -X PUT -H "Content-Type: application/json" -d '{"temperatureUnit":"c"}' http://airgradient_84fce612eff4.local/config + ``` If you use command prompt on Windows, you need to escape the quotes: ``` -d "{\"param\":\"value\"}" ``` #### Avoiding Conflicts with Configuration on AirGradient Server + If the monitor is set up on the AirGradient dashboard, it will also receive configurations 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) @@ -142,4 +155,47 @@ 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.9]()) | Boolean | `false`: Without compensate (default)
`true`: with compensate | `{"monitorDisplayCompensatedValues": false }` | +| `monitorDisplayCompensatedValues` | Set the display show the PM value with/without compensate value (only on [3.1.9]()) | Boolean | `false`: Without compensate (default)
`true`: with compensate | `{"monitorDisplayCompensatedValues": false }` | +| `corrections` | Sets correction options to display and measurement values on local server response. | Object | _see corretions section_ | _see corretions section_ | + + + +#### Corrections + +The `corrections` object allows configuring PM2.5 correction algorithms and parameters. This affects both the display and local server response values. + +Example correction configuration: + +```json +{ + "corrections": { + "pm02": { + "correctionAlgorithm": "