diff --git a/src/AgValue.cpp b/src/AgValue.cpp index 0bed140..5a1ed5d 100644 --- a/src/AgValue.cpp +++ b/src/AgValue.cpp @@ -686,6 +686,340 @@ float Measurements::getCorrectedPM25(bool useAvg, int ch, bool forceCorrection) return corrected; } +Measurements::MeasurementCycle Measurements::getMeasurementCycle() { + MeasurementCycle mc; + mc.bootCount = _bootCount; + mc.freeHeap = ESP.getFreeHeap(); + // co2, tvoc, nox + mc.co2 = _co2.update.avg; + mc.tvoc = _tvoc.update.avg; + mc.tvoc_raw = _tvoc_raw.update.avg; + mc.nox = _nox.update.avg; + mc.nox_raw = _nox_raw.update.avg; + // Temperature & Humidity + mc.temperature[0] = _temperature[0].update.avg; + mc.humidity[0] = _humidity[0].update.avg; + mc.temperature[1] = _temperature[1].update.avg; + mc.humidity[1] = _humidity[1].update.avg; + // PM atmospheric + mc.pm_01[0] = _pm_01[0].update.avg; + mc.pm_25[0] = _pm_25[0].update.avg; + mc.pm_10[0] = _pm_10[0].update.avg; + mc.pm_01[1] = _pm_01[1].update.avg; + mc.pm_25[1] = _pm_25[1].update.avg; + mc.pm_10[1] = _pm_10[1].update.avg; + // PM standard particle + mc.pm_01_sp[0] = _pm_01_sp[0].update.avg; + mc.pm_25_sp[0] = _pm_25_sp[0].update.avg; + mc.pm_10_sp[0] = _pm_10_sp[0].update.avg; + mc.pm_01_sp[1] = _pm_01_sp[1].update.avg; + mc.pm_25_sp[1] = _pm_25_sp[1].update.avg; + mc.pm_10_sp[1] = _pm_10_sp[1].update.avg; + // Particle Count + mc.pm_03_pc[0] = _pm_03_pc[0].update.avg; + mc.pm_05_pc[0] = _pm_05_pc[0].update.avg; + mc.pm_01_pc[0] = _pm_01_pc[0].update.avg; + mc.pm_25_pc[0] = _pm_25_pc[0].update.avg; + mc.pm_5_pc[0] = _pm_5_pc[0].update.avg; + mc.pm_10_pc[0] = _pm_10_pc[0].update.avg; + mc.pm_03_pc[1] = _pm_03_pc[1].update.avg; + mc.pm_05_pc[1] = _pm_05_pc[1].update.avg; + mc.pm_01_pc[1] = _pm_01_pc[1].update.avg; + mc.pm_25_pc[1] = _pm_25_pc[1].update.avg; + mc.pm_5_pc[1] = _pm_5_pc[1].update.avg; + mc.pm_10_pc[1] = _pm_10_pc[1].update.avg; + + return mc; +} + +String Measurements::buildMeasurementPayload(MeasurementCycle &mc, AgFirmwareMode fwMode) { + JSONVar root; + + /// TVOx and NOx + if (utils::isValidVOC(mc.tvoc)) { + root[json_prop_tvoc] = ag->round2(mc.tvoc); + } + if (utils::isValidVOC(mc.tvoc_raw)) { + root[json_prop_tvocRaw] = ag->round2(mc.tvoc_raw); + } + if (utils::isValidNOx(mc.nox)) { + root[json_prop_nox] = ag->round2(mc.nox); + } + if (utils::isValidNOx(mc.nox_raw)) { + root[json_prop_noxRaw] = ag->round2(mc.nox_raw); + } + + // CO2 + if (utils::isValidCO2(mc.co2)) { + root[json_prop_co2] = ag->round2(mc.co2); + } + + if (fwMode == FW_MODE_O_1PP || fwMode == FW_MODE_O_1PPT) { + // Have 2 PMS sensor using PM5003T + root["channels"]["1"][json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); + root["channels"]["2"][json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); + + /// PM1.0 atmospheric environment + if (utils::isValidPm(mc.pm_01[0]) && utils::isValidPm(mc.pm_01[1])) { + float avg = (mc.pm_01[0] + mc.pm_01[1]) / 2.0f; + root[json_prop_pm01Ae] = ag->round2(avg); + root["channels"]["1"][json_prop_pm01Ae] = ag->round2(mc.pm_01[0]); + root["channels"]["2"][json_prop_pm01Ae] = ag->round2(mc.pm_01[1]); + } else if (utils::isValidPm(mc.pm_01[0])) { + root[json_prop_pm01Ae] = ag->round2(mc.pm_01[0]); + root["channels"]["1"][json_prop_pm01Ae] = ag->round2(mc.pm_01[0]); + } else if (utils::isValidPm(mc.pm_01[1])) { + root[json_prop_pm01Ae] = ag->round2(mc.pm_01[1]); + root["channels"]["2"][json_prop_pm01Ae] = ag->round2(mc.pm_01[1]); + } + + /// PM2.5 atmospheric environment + if (utils::isValidPm(mc.pm_25[0]) && utils::isValidPm(mc.pm_25[1])) { + float avg = (mc.pm_25[0] + mc.pm_25[1]) / 2.0f; + root[json_prop_pm25Ae] = ag->round2(avg); + root["channels"]["1"][json_prop_pm25Ae] = ag->round2(mc.pm_25[0]); + root["channels"]["2"][json_prop_pm25Ae] = ag->round2(mc.pm_25[1]); + } else if (utils::isValidPm(mc.pm_25[0])) { + root[json_prop_pm25Ae] = ag->round2(mc.pm_25[0]); + root["channels"]["1"][json_prop_pm25Ae] = ag->round2(mc.pm_25[0]); + } else if (utils::isValidPm(mc.pm_25[1])) { + root[json_prop_pm25Ae] = ag->round2(mc.pm_25[1]); + root["channels"]["2"][json_prop_pm25Ae] = ag->round2(mc.pm_25[1]); + } + + /// PM10 atmospheric environment + if (utils::isValidPm(mc.pm_10[0]) && utils::isValidPm(mc.pm_10[1])) { + float avg = (mc.pm_10[0] + mc.pm_10[1]) / 2.0f; + root[json_prop_pm10Ae] = ag->round2(avg); + root["channels"]["1"][json_prop_pm10Ae] = ag->round2(mc.pm_10[0]); + root["channels"]["2"][json_prop_pm10Ae] = ag->round2(mc.pm_10[1]); + } else if (utils::isValidPm(mc.pm_10[0])) { + root[json_prop_pm10Ae] = ag->round2(mc.pm_10[0]); + root["channels"]["1"][json_prop_pm10Ae] = ag->round2(mc.pm_10[0]); + } else if (utils::isValidPm(mc.pm_10[1])) { + root[json_prop_pm10Ae] = ag->round2(mc.pm_10[1]); + root["channels"]["2"][json_prop_pm10Ae] = ag->round2(mc.pm_10[1]); + } + + /// PM1.0 standard particle + if (utils::isValidPm(mc.pm_01_sp[0]) && utils::isValidPm(mc.pm_01_sp[1])) { + float avg = (mc.pm_01_sp[0] + mc.pm_01_sp[1]) / 2.0f; + root[json_prop_pm01Sp] = ag->round2(avg); + root["channels"]["1"][json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[0]); + root["channels"]["2"][json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[1]); + } else if (utils::isValidPm(mc.pm_01_sp[0])) { + root[json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[0]); + root["channels"]["1"][json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[0]); + } else if (utils::isValidPm(mc.pm_01_sp[1])) { + root[json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[1]); + root["channels"]["2"][json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[1]); + } + + /// PM2.5 standard particle + if (utils::isValidPm(mc.pm_25_sp[0]) && utils::isValidPm(mc.pm_25_sp[1])) { + float avg = (mc.pm_25_sp[0] + mc.pm_25_sp[1]) / 2.0f; + root[json_prop_pm25Sp] = ag->round2(avg); + root["channels"]["1"][json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[0]); + root["channels"]["2"][json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[1]); + } else if (utils::isValidPm(mc.pm_25_sp[0])) { + root[json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[0]); + root["channels"]["1"][json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[0]); + } else if (utils::isValidPm(mc.pm_25_sp[1])) { + root[json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[1]); + root["channels"]["2"][json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[1]); + } + + /// PM10 standard particle + if (utils::isValidPm(mc.pm_10_sp[0]) && utils::isValidPm(mc.pm_10_sp[1])) { + float avg = (mc.pm_10_sp[0] + mc.pm_10_sp[1]) / 2.0f; + root[json_prop_pm10Sp] = ag->round2(avg); + root["channels"]["1"][json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[0]); + root["channels"]["2"][json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[1]); + } else if (utils::isValidPm(mc.pm_10_sp[0])) { + root[json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[0]); + root["channels"]["1"][json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[0]); + } else if (utils::isValidPm(mc.pm_10_sp[1])) { + root[json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[1]); + root["channels"]["2"][json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[1]); + } + + /// PM003 particle count + if (utils::isValidPm03Count(mc.pm_03_pc[0]) && + utils::isValidPm03Count(mc.pm_03_pc[1])) { + float avg = (mc.pm_03_pc[0] + mc.pm_03_pc[1]) / 2.0f; + root[json_prop_pm03Count] = ag->round2(avg); + root["channels"]["1"][json_prop_pm03Count] = ag->round2(mc.pm_03_pc[0]); + root["channels"]["2"][json_prop_pm03Count] = ag->round2(mc.pm_03_pc[1]); + } else if (utils::isValidPm03Count(mc.pm_03_pc[0])) { + root[json_prop_pm03Count] = ag->round2(mc.pm_03_pc[0]); + root["channels"]["1"][json_prop_pm03Count] = ag->round2(mc.pm_03_pc[0]); + } else if (utils::isValidPm03Count(mc.pm_03_pc[1])) { + root[json_prop_pm03Count] = ag->round2(mc.pm_03_pc[1]); + root["channels"]["2"][json_prop_pm03Count] = ag->round2(mc.pm_03_pc[1]); + } + + /// PM0.5 particle count + if (utils::isValidPm03Count(mc.pm_05_pc[0]) && + utils::isValidPm03Count(mc.pm_05_pc[1])) { + float avg = (mc.pm_05_pc[0] + mc.pm_05_pc[1]) / 2.0f; + root[json_prop_pm05Count] = ag->round2(avg); + root["channels"]["1"][json_prop_pm05Count] = ag->round2(mc.pm_05_pc[0]); + root["channels"]["2"][json_prop_pm05Count] = ag->round2(mc.pm_05_pc[1]); + } else if (utils::isValidPm03Count(mc.pm_05_pc[0])) { + root[json_prop_pm05Count] = ag->round2(mc.pm_05_pc[0]); + root["channels"]["1"][json_prop_pm05Count] = ag->round2(mc.pm_05_pc[0]); + } else if (utils::isValidPm03Count(mc.pm_05_pc[1])) { + root[json_prop_pm05Count] = ag->round2(mc.pm_05_pc[1]); + root["channels"]["2"][json_prop_pm05Count] = ag->round2(mc.pm_05_pc[1]); + } + /// PM1.0 particle count + if (utils::isValidPm03Count(mc.pm_01_pc[0]) && + utils::isValidPm03Count(mc.pm_01_pc[1])) { + float avg = (mc.pm_01_pc[0] + mc.pm_01_pc[1]) / 2.0f; + root[json_prop_pm1Count] = ag->round2(avg); + root["channels"]["1"][json_prop_pm1Count] = ag->round2(mc.pm_01_pc[0]); + root["channels"]["2"][json_prop_pm1Count] = ag->round2(mc.pm_01_pc[1]); + } else if (utils::isValidPm03Count(mc.pm_01_pc[0])) { + root[json_prop_pm1Count] = ag->round2(mc.pm_01_pc[0]); + root["channels"]["1"][json_prop_pm1Count] = ag->round2(mc.pm_01_pc[0]); + } else if (utils::isValidPm03Count(mc.pm_01_pc[1])) { + root[json_prop_pm1Count] = ag->round2(mc.pm_01_pc[1]); + root["channels"]["2"][json_prop_pm1Count] = ag->round2(mc.pm_01_pc[1]); + } + + /// PM2.5 particle count + if (utils::isValidPm03Count(mc.pm_25_pc[0]) && + utils::isValidPm03Count(mc.pm_25_pc[1])) { + float avg = (mc.pm_25_pc[0] + mc.pm_25_pc[1]) / 2.0f; + root[json_prop_pm25Count] = ag->round2(avg); + root["channels"]["1"][json_prop_pm25Count] = ag->round2(mc.pm_25_pc[0]); + root["channels"]["2"][json_prop_pm25Count] = ag->round2(mc.pm_25_pc[1]); + } else if (utils::isValidPm03Count(mc.pm_25_pc[0])) { + root[json_prop_pm25Count] = ag->round2(mc.pm_25_pc[0]); + root["channels"]["1"][json_prop_pm25Count] = ag->round2(mc.pm_25_pc[0]); + } else if (utils::isValidPm03Count(mc.pm_25_pc[1])) { + root[json_prop_pm25Count] = ag->round2(mc.pm_25_pc[1]); + root["channels"]["2"][json_prop_pm25Count] = ag->round2(mc.pm_25_pc[1]); + } + + /// Temperature + if (utils::isValidTemperature(mc.temperature[0]) && + utils::isValidTemperature(mc.temperature[1])) { + float temperature = (mc.temperature[0] + mc.temperature[1]) / 2.0f; + root[json_prop_temp] = ag->round2(temperature); + root["channels"]["1"][json_prop_temp] = ag->round2(mc.temperature[0]); + root["channels"]["2"][json_prop_temp] = ag->round2(mc.temperature[1]); + } else if (utils::isValidTemperature(mc.temperature[0])) { + root[json_prop_temp] = ag->round2(mc.temperature[0]); + root["channels"]["1"][json_prop_temp] = ag->round2(mc.temperature[0]); + } else if (utils::isValidTemperature(mc.temperature[1])) { + root[json_prop_temp] = ag->round2(mc.temperature[1]); + root["channels"]["2"][json_prop_temp] = ag->round2(mc.temperature[1]); + } + + /// Relative humidity + if (utils::isValidHumidity(mc.humidity[0]) && + utils::isValidHumidity(mc.humidity[1])) { + float humidity = (mc.humidity[0] + mc.humidity[1]) / 2.0f; + root[json_prop_rhum] = ag->round2(humidity); + root["channels"]["1"][json_prop_rhum] = ag->round2(mc.humidity[0]); + root["channels"]["2"][json_prop_rhum] = ag->round2(mc.humidity[1]); + } else if (utils::isValidHumidity(mc.humidity[0])) { + root[json_prop_rhum] = ag->round2(mc.humidity[0]); + root["channels"]["1"][json_prop_rhum] = ag->round2(mc.humidity[0]); + } else if (utils::isValidHumidity(mc.humidity[1])) { + root[json_prop_rhum] = ag->round2(mc.humidity[1]); + root["channels"]["2"][json_prop_rhum] = ag->round2(mc.humidity[1]); + } + + } else { + // Define PMS channel and add PMS firmware version + int chIndex; + if (config.hasSensorPMS1) { + chIndex = 0; // PMS connected to channel 1 + if (ag->isOne() || (ag->isPro4_2()) || ag->isPro3_3() || ag->isBasic()) { + root[json_prop_pmFirmware] = + pms5003FirmwareVersion(ag->pms5003.getFirmwareVersion()); + } else { + root[json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_1.getFirmwareVersion()); + } + } else { + chIndex = 1; // PMS connected to channel 2 + root[json_prop_pmFirmware] = + pms5003TFirmwareVersion(ag->pms5003t_2.getFirmwareVersion()); + } + // Have 1 PMS sensor + if (utils::isValidPm(mc.pm_01[chIndex])) { + root[json_prop_pm01Ae] = ag->round2(mc.pm_01[chIndex]); + } + if (utils::isValidPm(mc.pm_25[chIndex])) { + root[json_prop_pm25Ae] = ag->round2(mc.pm_25[chIndex]); + } + if (utils::isValidPm(mc.pm_10[chIndex])) { + root[json_prop_pm10Ae] = ag->round2(mc.pm_10[chIndex]); + } + if (utils::isValidPm(mc.pm_01_sp[chIndex])) { + root[json_prop_pm01Sp] = ag->round2(mc.pm_01_sp[chIndex]); + } + if (utils::isValidPm(mc.pm_25_sp[chIndex])) { + root[json_prop_pm25Sp] = ag->round2(mc.pm_25_sp[chIndex]); + } + if (utils::isValidPm(mc.pm_10_sp[chIndex])) { + root[json_prop_pm10Sp] = ag->round2(mc.pm_10_sp[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_03_pc[chIndex])) { + root[json_prop_pm03Count] = ag->round2(mc.pm_03_pc[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_05_pc[chIndex])) { + root[json_prop_pm05Count] = ag->round2(mc.pm_05_pc[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_01_pc[chIndex])) { + root[json_prop_pm1Count] = ag->round2(mc.pm_01_pc[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_25_pc[chIndex])) { + root[json_prop_pm25Count] = ag->round2(mc.pm_25_pc[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_5_pc[chIndex])) { + root[json_prop_pm5Count] = ag->round2(mc.pm_5_pc[chIndex]); + } + if (utils::isValidPm03Count(mc.pm_10_pc[chIndex])) { + root[json_prop_pm10Count] = ag->round2(mc.pm_10_pc[chIndex]); + } + // Temperature and humidity + if (config.hasSensorSHT) { + // If SHT exists, it always on channel 1 + if (utils::isValidTemperature(mc.temperature[0])) { + root[json_prop_temp] = ag->round2(mc.temperature[0]); + } + if (utils::isValidHumidity(mc.humidity[0])) { + root[json_prop_rhum] = ag->round2(mc.humidity[0]); + } + } else { + if (utils::isValidTemperature(mc.temperature[chIndex])) { + root[json_prop_temp] = ag->round2(mc.temperature[chIndex]); + } + if (utils::isValidHumidity(mc.humidity[chIndex])) { + root[json_prop_rhum] = ag->round2(mc.humidity[chIndex]); + } + } + } + + // Others + root["boot"] = mc.bootCount; + root["bootCount"] = mc.bootCount; + root["wifi"] = mc.wifi; + root["freeHeap"] = mc.freeHeap; + root["resetReason"] = _resetReason; + + String result = JSON.stringify(root); + Serial.printf("\n---- TRANSMISSION PAYLOAD\n %s \n-----\n", result.c_str()); + return result; +} + + String Measurements::toString(bool localServer, AgFirmwareMode fwMode, int rssi) { JSONVar root; @@ -1219,4 +1553,4 @@ void Measurements::setResetReason(esp_reset_reason_t reason) { _resetReason = (int)reason; } -#endif \ No newline at end of file +#endif diff --git a/src/AgValue.h b/src/AgValue.h index 9a3c446..de7ee94 100644 --- a/src/AgValue.h +++ b/src/AgValue.h @@ -7,6 +7,7 @@ #include "Libraries/Arduino_JSON/src/Arduino_JSON.h" #include "Main/utils.h" #include +#include #include class Measurements { @@ -37,6 +38,31 @@ public: Measurements(Configuration &config); ~Measurements() {} + struct MeasurementCycle { + float temperature[2]; + float humidity[2]; + float co2; + float tvoc; // Index value + float tvoc_raw; + float nox; // Index value + float nox_raw; + float pm_01[2]; // pm 1.0 atmospheric environment + float pm_25[2]; // pm 2.5 atmospheric environment + float pm_10[2]; // pm 10 atmospheric environment + float pm_01_sp[2]; // pm 1.0 standard particle + float pm_25_sp[2]; // pm 2.5 standard particle + float pm_10_sp[2]; // pm 10 standard particle + float pm_03_pc[2]; // particle count 0.3 + float pm_05_pc[2]; // particle count 0.5 + float pm_01_pc[2]; // particle count 1.0 + float pm_25_pc[2]; // particle count 2.5 + float pm_5_pc[2]; // particle count 5.0 + float pm_10_pc[2]; // particle count 10 + int bootCount; + int wifi; + uint32_t freeHeap; + }; + void setAirGradient(AirGradient *ag); // Enumeration for every AG measurements @@ -154,6 +180,10 @@ public: */ String toString(bool localServer, AgFirmwareMode fwMode, int rssi); + MeasurementCycle getMeasurementCycle(); + + String buildMeasurementPayload(MeasurementCycle &mc, AgFirmwareMode fwMode); + /** * Set to true if want to debug every update value */