diff --git a/examples/BASIC/BASIC.ino b/examples/BASIC/BASIC.ino index 5d50c4e..78da232 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()); } } @@ -543,8 +526,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( @@ -554,26 +536,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 19659ea..04430e3 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()); } } @@ -595,8 +578,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( @@ -606,26 +588,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 7d203ae..8bfb466 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()); } } @@ -636,8 +619,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( @@ -647,26 +629,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/LocalServer.cpp b/examples/OneOpenAir/LocalServer.cpp index 1b88e8e..e0b5c98 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.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/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index bf2cbd1..b202929 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 */ @@ -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; @@ -115,6 +114,8 @@ 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); +static void setMeasurementMaxPeriod(); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar); AgSchedule configSchedule(SERVER_CONFIG_SYNC_INTERVAL, @@ -165,6 +166,10 @@ void setup() { /** Init sensor */ boardInit(); + setMeasurementMaxPeriod(); + + // Uncomment below line to print every measurements reading update + // measurements.setDebug(true); /** Connecting wifi */ bool connectToWifi = false; @@ -317,17 +322,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()); } } @@ -360,8 +364,8 @@ static void createMqttTask(void) { /** Send data */ if (mqttClient.isConnected()) { - String payload = measurements.toString( - true, fwMode, wifiConnector.RSSI(), ag, &configuration); + String payload = + measurements.toString(true, fwMode, wifiConnector.RSSI(), *ag, configuration); String topic = "airgradient/readings/" + ag->deviceId(); if (mqttClient.publish(topic.c_str(), payload.c_str(), @@ -982,240 +986,110 @@ 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.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 updatePMS5003() { + if (ag->pms5003.connected()) { + 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 { + 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()); + } } 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.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 { - 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.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); } } - 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.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.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); + } + } + + if (configuration.hasSensorSGP) { + float temp, hum; + if (newPMS1Value && newPMS2Value) { + // Both PMS has new valid value + temp = (measurements.getFloat(Measurements::Temperature, 1) + + measurements.getFloat(Measurements::Temperature, 2)) / + 2.0f; + 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, 1); + hum = measurements.getFloat(Measurements::Humidity, 1); + } else { + // Only PMS2 has new valid value + temp = measurements.getFloat(Measurements::Temperature, 2); + hum = measurements.getFloat(Measurements::Humidity, 2); + } + + // Update compensation temperature and humidity for SGP41 + ag->sgp41.setCompensationTemperatureHumidity(temp, hum); } } @@ -1228,8 +1102,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( @@ -1241,24 +1114,53 @@ 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.Temperature = utils::getInvalidTemperature(); - measurements.Humidity = utils::getInvalidHumidity(); + measurements.update(Measurements::Temperature, utils::getInvalidTemperature()); + measurements.update(Measurements::Humidity, utils::getInvalidHumidity()); Serial.println("SHT read failed"); } +} + +/* 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/OneOpenAir/OpenMetrics.cpp b/examples/OneOpenAir/OpenMetrics.cpp index a7dfe70..23b2dd0 100644 --- a/examples/OneOpenAir/OpenMetrics.cpp +++ b/examples/OneOpenAir/OpenMetrics.cpp @@ -74,41 +74,46 @@ 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, 1) + + measure.getFloat(Measurements::Temperature, 2)) / + 2.0f; + _hum = (measure.getFloat(Measurements::Humidity, 1) + + measure.getFloat(Measurements::Humidity, 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.Temperature; - _hum = measure.Humidity; + _temp = measure.getFloat(Measurements::Temperature); + _hum = measure.getFloat(Measurements::Humidity); } 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); } } 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, 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.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, 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 050782a..5661f21 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); + 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:- %%"); } diff --git a/src/AgStateMachine.cpp b/src/AgStateMachine.cpp index 85f87d5..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.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) { diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 867ee42..7965b68 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -1,414 +1,746 @@ #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" -/** - * @brief Get PMS5003 firmware version string - * - * @param fwCode - * @return String - */ +void Measurements::maxPeriod(MeasurementType type, int max) { + switch (type) { + case Temperature: + _temperature[0].update.max = max; + _temperature[1].update.max = max; + break; + case Humidity: + _humidity[0].update.max = max; + _humidity[1].update.max = max; + break; + case CO2: + _co2.update.max = max; + break; + case TVOC: + _tvoc.update.max = max; + break; + case TVOCRaw: + _tvoc_raw.update.max = max; + break; + case NOx: + _nox.update.max = max; + break; + case NOxRaw: + _nox_raw.update.max = max; + break; + case PM25: + _pm_25[0].update.max = max; + _pm_25[1].update.max = max; + break; + case PM01: + _pm_01[0].update.max = max; + _pm_01[1].update.max = max; + break; + case PM10: + _pm_10[0].update.max = max; + _pm_10[1].update.max = max; + break; + case PM03_PC: + _pm_03_pc[0].update.max = max; + _pm_03_pc[1].update.max = max; + break; + }; +} + +bool Measurements::update(MeasurementType type, int val, 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 + IntegerValue *temporary = nullptr; + int invalidValue = 0; + switch (type) { + case CO2: + temporary = &_co2; + invalidValue = utils::getInvalidCO2(); + break; + case TVOC: + temporary = &_tvoc; + invalidValue = utils::getInvalidVOC(); + break; + case TVOCRaw: + temporary = &_tvoc_raw; + invalidValue = utils::getInvalidVOC(); + break; + case NOx: + temporary = &_nox; + invalidValue = utils::getInvalidNOx(); + break; + case NOxRaw: + temporary = &_nox_raw; + invalidValue = utils::getInvalidNOx(); + break; + case PM25: + temporary = &_pm_25[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM01: + temporary = &_pm_01[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM10: + temporary = &_pm_10[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + case PM03_PC: + temporary = &_pm_03_pc[ch]; + invalidValue = utils::getInvalidPmValue(); + break; + default: + break; + }; + + // 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", measurementTypeStr(type)); + // TODO: Just assert? + return false; + } + + // Restore channel value for debugging purpose + ch = ch + 1; + + if (val == invalidValue) { + 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); + temporary->update.avg = invalidValue; + return false; + } + + // Still consider updating value to valid + return true; + } + + // Reset invalid counter when update new valid value + temporary->update.invalidCounter = 0; + + // 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 + } + + // Calculate average based on how many elements on the list + temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); + if (_debug) { + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + } + + return true; +} + +bool Measurements::update(MeasurementType type, float val, 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; + float invalidValue = 0; + switch (type) { + case Temperature: + temporary = &_temperature[ch]; + invalidValue = utils::getInvalidTemperature(); + break; + case Humidity: + temporary = &_humidity[ch]; + invalidValue = utils::getInvalidHumidity(); + break; + default: + break; + } + + // 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", measurementTypeStr(type)); + // TODO: Just assert? + return false; + } + + // Restore channel value for debugging purpose + ch = ch + 1; + + if (val == invalidValue) { + 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); + temporary->update.avg = invalidValue; + return false; + } + + // Still consider updating value to valid + return true; + } + + // Reset invalid counter when update new valid value + temporary->update.invalidCounter = 0; + + // 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 + } + + // Calculate average based on how many elements on the list + temporary->update.avg = temporary->sumValues / (float)temporary->listValues.size(); + if (_debug) { + Serial.printf("%s{%d}: %.2f\n", measurementTypeStr(type), ch, temporary->update.avg); + } + + return true; +} + +int Measurements::get(MeasurementType type, 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 + IntegerValue *temporary = nullptr; + switch (type) { + case CO2: + temporary = &_co2; + break; + case TVOC: + temporary = &_tvoc; + break; + case TVOCRaw: + temporary = &_tvoc_raw; + break; + case NOx: + temporary = &_nox; + break; + case NOxRaw: + temporary = &_nox_raw; + break; + case PM25: + temporary = &_pm_25[ch]; + break; + case PM01: + temporary = &_pm_01[ch]; + break; + case PM10: + temporary = &_pm_10[ch]; + break; + case PM03_PC: + temporary = &_pm_03_pc[ch]; + break; + default: + break; + }; + + // 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", measurementTypeStr(type)); + // TODO: Just assert? + return false; + } + + if (temporary->listValues.empty()) { + // Values still empty, return 0 + return 0; + } + + return temporary->listValues.back(); +} + +float Measurements::getFloat(MeasurementType type, 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 Temperature: + temporary = &_temperature[ch]; + break; + case Humidity: + temporary = &_humidity[ch]; + break; + default: + break; + } + + // 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", measurementTypeStr(type)); + // TODO: Just assert? + return false; + } + + if (temporary->listValues.empty()) { + // Values still empty, return 0 + return 0; + } + + return temporary->listValues.back(); +} + 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::toString(bool localServer, AgFirmwareMode fwMode, int rssi, - void *_ag, void *_config) { - AirGradient *ag = (AirGradient *)_ag; - Configuration *config = (Configuration *)_config; +String Measurements::measurementTypeStr(MeasurementType type) { + String str; + switch (type) { + case Temperature: + str = "Temperature"; + break; + case Humidity: + str = "Humidity"; + break; + case CO2: + str = "CO2"; + break; + case TVOC: + str = "TVOC"; + break; + case TVOCRaw: + str = "TVOCRaw"; + break; + case NOx: + str = "NOx"; + break; + case NOxRaw: + str = "NOxRaw"; + break; + case PM25: + str = "PM25"; + break; + case PM01: + str = "PM01"; + break; + case PM10: + str = "PM10"; + break; + case PM03_PC: + str = "PM03"; + break; + default: + break; + }; + 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); + } +} + +String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi, AirGradient &ag, + 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; - } - } + if (ag.isOne() || (ag.isPro4_2()) || ag.isPro3_3() || ag.isBasic()) { + root = buildIndoor(localServer, ag, config); } 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); - } + root = buildOutdoor(localServer, fwMode, ag, config); + } - 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; - } - } - } + // CO2 + if (config.hasSensorS8 && utils::isValidCO2(_co2.update.avg)) { + root["rco2"] = ag.round2(_co2.update.avg); + } - 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; + /// TVOx and NOx + if (config.hasSensorSGP) { + if (utils::isValidVOC(_tvoc.update.avg)) { + root["tvocIndex"] = ag.round2(_tvoc.update.avg); } - - 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 (utils::isValidVOC(_tvoc_raw.update.avg)) { + root["tvocRaw"] = ag.round2(_tvoc_raw.update.avg); + } + if (utils::isValidNOx(_nox.update.avg)) { + root["noxIndex"] = ag.round2(_nox.update.avg); + } + if (utils::isValidNOx(_nox_raw.update.avg)) { + root["noxRaw"] = ag.round2(_nox_raw.update.avg); } } - 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; + root["wifi"] = rssi; if (localServer) { - if (ag->isOne()) { - root["ledMode"] = config->getLedBarModeName(); + if (ag.isOne()) { + root["ledMode"] = config.getLedBarModeName(); } - root["firmware"] = ag->getVersion(); + root["serialno"] = ag.deviceId(); + root["firmware"] = ag.getVersion(); root["model"] = AgFirmwareModeName(fwMode); } - return JSON.stringify(root); + String result = JSON.stringify(root); + Serial.printf("\n---- PAYLOAD\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].update.avg)) { + indoor["atmp"] = ag.round2(_temperature[0].update.avg); + if (localServer) { + indoor["atmpCompensated"] = ag.round2(_temperature[0].update.avg); + } + } + // Add humidity + if (utils::isValidHumidity(_humidity[0].update.avg)) { + indoor["rhum"] = ag.round2(_humidity[0].update.avg); + if (localServer) { + 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].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["pm02Compensated"] = ag.round2(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].update.avg)) { + pms["pm01"] = ag.round2(_pm_01[ch].update.avg); + } + if (utils::isValidPm(_pm_25[ch].update.avg)) { + pms["pm02"] = ag.round2(_pm_25[ch].update.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].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].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].update.avg); + if (utils::isValidTemperature(_vc)) { + pms["atmpCompensated"] = ag.round2(_vc); + } + } + } + // Set humidity if valid + 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].update.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].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 + float pm25 = ag.pms5003t_1.compensate(_pm_25[ch].update.avg, _humidity[ch].update.avg); + if (utils::isValidPm(pm25)) { + pms["pm02Compensated"] = ag.round2(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].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); + } 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].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); + } 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].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); + } 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].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); + } 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].update.avg) && + 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); + + 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); + } + + } 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].update.avg); + pms["atmpCompensated"] = ag.round2(temp1); + pms["channels"]["1"]["atmpCompensated"] = 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); + + 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); + } + } + + /// Relative humidity + 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); + + 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); + } + + } 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].update.avg); + pms["rhumCompensated"] = ag.round2(hum1); + pms["channels"]["1"]["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); + + 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); + } + } + + if (compensate) { + // Add pm25 compensated value + /// First get both channel compensated value + 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"] = 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); + } + + /// 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"] = ag.round2(pm25_comp1); + } else if (utils::isValidPm(pm25_comp2)) { + pms["pm02Compensated"] = ag.round2(pm25_comp2); + } + } + } + + return pms; +} + +void Measurements::setDebug(bool debug) { _debug = debug; } \ No newline at end of file diff --git a/src/AgValue.h b/src/AgValue.h index 1957828..8c26bf2 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -1,79 +1,179 @@ #ifndef _AG_VALUE_H_ #define _AG_VALUE_H_ -#include +#include "AgConfigure.h" +#include "AirGradient.h" #include "App/AppDef.h" +#include "Libraries/Arduino_JSON/src/Arduino_JSON.h" +#include "Main/utils.h" +#include +#include 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 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 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_ */ diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 6bacd7d..45c16c0 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -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; } /** 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);