Merge pull request #256 from airgradienthq/restructure-agvalue

Restructure Measurements class
This commit is contained in:
Samuel Siburian authored and GitHub committed 2024-10-22 13:40:50 +07:00
commit eb76eff403
22 files changed
+1421 -1010

No files matched your search

+32 -26
View File
@@ -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);
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);
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);
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);
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));
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);
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);
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);
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);
if (config.hasSensorSHT && config.isMonitorDisplayCompensatedValues()) {
pm25 = (int)ag->pms5003.compensate(pm25, value.Humidity);
pm25 = (int)ag->pms5003.compensate(pm25, value.getFloat(Measurements::Humidity));
}
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);
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);
if (utils::isValidHumidity(rhum)) {
snprintf(strBuf, sizeof(strBuf), "H:%d %%", rhum);
} else {
snprintf(strBuf, sizeof(strBuf), "H:- %%");
}
+3 -3
View File
@@ -69,7 +69,7 @@ void StateMachine::sensorhandleLeds(void) {
*
*/
void StateMachine::co2handleLeds(void) {
int co2Value = value.CO2;
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.pm25_1;
int pm25Value = value.get(Measurements::PM25);
if (config.isMonitorDisplayCompensatedValues() && config.hasSensorSHT) {
pm25Value = ag->pms5003.compensate(value.pm25_1, value.Humidity);
pm25Value = ag->pms5003.compensate(pm25Value, value.getFloat(Measurements::Humidity));
}
if (pm25Value < 5) {
+709 -377
View File
File diff suppressed because it is too large. Load diff
+161 -61
View File
@@ -1,79 +1,179 @@
#ifndef _AG_VALUE_H_
#define _AG_VALUE_H_
#include <Arduino.h>
#include "AgConfigure.h"
#include "AirGradient.h"
#include "App/AppDef.h"
#include "Libraries/Arduino_JSON/src/Arduino_JSON.h"
#include "Main/utils.h"
#include <Arduino.h>
#include <vector>
class Measurements {
private:
String pms5003FirmwareVersion(int fwCode);
String pms5003TFirmwareVersion(int fwCode);
String pms5003FirmwareVersionBase(String prefix, int fwCode);
// 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 length of the period of the moving average
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<float> listValues; // List of update value that are kept
Update update;
};
// Reading type for sensor value that outputs integer
struct IntegerValue {
unsigned long sumValues; // Total value from each update; unsigned long to accomodate TVOx and
// NOx raw data
std::vector<int> listValues; // List of update value that are kept
Update update;
};
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() {}
float Temperature;
int Humidity;
int CO2;
int TVOC;
int TVOCRaw;
int NOx;
int NOxRaw;
// Enumeration for every AG measurements
enum MeasurementType {
Temperature,
Humidity,
CO2,
TVOC, // index value
TVOCRaw,
NOx, // index value
NOxRaw,
PM25,
PM01,
PM10,
PM03_PC, // Particle count
};
int pm25_1;
int pm01_1;
int pm10_1;
int pm03PCount_1;
float temp_1;
int hum_1;
/**
* @brief Set each MeasurementType maximum period length for moving average
*
* @param type the target measurement type to set
* @param max the maximum period length
*/
void maxPeriod(MeasurementType, int max);
int pm25_2;
int pm01_2;
int pm10_2;
int pm03PCount_2;
float temp_2;
int hum_2;
/**
* @brief update target measurement type with new value.
* 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 false if new value invalid consecutively reach threshold (max period)
* @return true otherwise
*/
bool update(MeasurementType type, int val, int ch = 1);
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;
/**
* @brief update target measurement type with new value.
* 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 false if new value invalid consecutively reach threshold (max period)
* @return true otherwise
*/
bool update(MeasurementType type, float val, int ch = 1);
/**
* @brief Get the target measurement latest value
*
* @param type measurement type that will be retrieve
* @param ch target type value channel
* @return int measurement type value
*/
int get(MeasurementType type, int ch = 1);
/**
* @brief Get the target measurement latest value
*
* @param type measurement type that will be retrieve
* @param ch target type value channel
* @return float measurement type value
*/
float getFloat(MeasurementType type, int ch = 1);
/**
* 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;
String toString(bool isLocal, AgFirmwareMode fwMode, int rssi, void* _ag, void* _config);
private:
// Some declared as an array (channel), because FW_MODE_O_1PPx has two PMS5003T
FloatValue _temperature[2];
FloatValue _humidity[2];
IntegerValue _co2;
IntegerValue _tvoc; // Index value
IntegerValue _tvoc_raw;
IntegerValue _nox; // Index value
IntegerValue _nox_raw;
IntegerValue _pm_25[2];
IntegerValue _pm_01[2];
IntegerValue _pm_10[2];
IntegerValue _pm_03_pc[2]; // particle count 0.3
bool _debug = false;
/**
* @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 measurementTypeStr(MeasurementType type);
/**
* @brief check if provided channel is a valid channel or not
* 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_ */
+25 -14
View File
@@ -320,11 +320,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;
}
@@ -332,23 +333,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;
}
/**
+1 -1
View File
@@ -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;
+4 -6
View File
@@ -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
+1 -1
View File
@@ -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);
+4 -6
View File
@@ -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
+1 -1
View File
@@ -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);