MeasurementCycle queue only applied for cellular

Cellular post measures payload different with wifi
Update submodule to support different cellular post endpoint
This commit is contained in:
samuelbles07
2025-03-21 04:40:27 +07:00
parent 7c2aa35e4f
commit 30622fca99
6 changed files with 1748 additions and 334 deletions

1592
compile_commands.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -63,9 +63,9 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#define WIFI_SERVER_CONFIG_SYNC_INTERVAL 1 * 60000 /** ms */
#define WIFI_MEASUREMENT_INTERVAL 1 * 60000 /** ms */
#define WIFI_TRANSMISSION_INTERVAL 1 * 60000 /** ms */
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 15 * 60000 /** ms */
#define CELLULAR_SERVER_CONFIG_SYNC_INTERVAL 30 * 60000 /** ms */
#define CELLULAR_MEASUREMENT_INTERVAL 3 * 60000 /** ms */
#define CELLULAR_TRANSMISSION_INTERVAL 3 * 60000 /** ms */
#define CELLULAR_TRANSMISSION_INTERVAL 9 * 60000 /** ms */
#define MQTT_SYNC_INTERVAL 60000 /** ms */
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
@ -75,6 +75,8 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
#define FIRMWARE_CHECK_FOR_UPDATE_MS (60 * 60 * 1000) /** ms */
#define MAXIMUM_MEASUREMENT_CYCLE_QUEUE 80
/** I2C define */
#define I2C_SDA_PIN 7
#define I2C_SCL_PIN 6
@ -259,10 +261,19 @@ void setup() {
oledDisplay.setBrightness(configuration.getDisplayBrightness());
}
// Allocate queue memory to avoid always reallocation
measurementCycleQueue.reserve(10);
// Initialize mutex to access mesurementCycleQueue
mutexMeasurementCycleQueue = xSemaphoreCreateMutex();
if (networkOption == UseCellular) {
// If using cellular re-set scheduler interval
configSchedule.setPeriod(CELLULAR_SERVER_CONFIG_SYNC_INTERVAL);
transmissionSchedule.setPeriod(CELLULAR_TRANSMISSION_INTERVAL);
measurementSchedule.setPeriod(CELLULAR_MEASUREMENT_INTERVAL);
measurementSchedule.update();
// Queue now only applied for cellular
// Allocate queue memory to avoid always reallocation
measurementCycleQueue.reserve(10);
// Initialize mutex to access mesurementCycleQueue
mutexMeasurementCycleQueue = xSemaphoreCreateMutex();
}
// Only run network task if monitor is not in offline mode
if (configuration.isOfflineMode() == false) {
@ -283,12 +294,6 @@ void setup() {
Serial.println("Running monitor without connection to AirGradient server");
}
// If using cellular re-set scheduler interval
if (networkOption == UseCellular) {
configSchedule.setPeriod(CELLULAR_SERVER_CONFIG_SYNC_INTERVAL);
transmissionSchedule.setPeriod(CELLULAR_TRANSMISSION_INTERVAL);
measurementSchedule.setPeriod(CELLULAR_MEASUREMENT_INTERVAL);
}
}
void loop() {
@ -304,10 +309,8 @@ void loop() {
// Schedule to update display and led
dispLedSchedule.run();
// No need to run measurement cycle schedule when mode is offline or connection to AG disabled
if (configuration.isOfflineMode() == false ||
configuration.isCloudConnectionDisabled() == false) {
// Schedule to take new measurement cycle
if (networkOption == UseCellular) {
// Queue now only applied for cellular
measurementSchedule.run();
}
@ -975,10 +978,10 @@ void initializeNetwork() {
if (configuration.isCloudConnectionDisabled()) {
return;
}
}
// Send data for the first time to AG server at boot
sendDataToAg();
// Send data for the first time to AG server at boot
sendDataToAg();
}
std::string config = agClient->httpFetchConfig(ag->getDeviceId());
@ -1287,13 +1290,23 @@ static void updatePm(void) {
}
}
void sendDataToServer(void) {
if (configuration.isPostDataToAirGradient() == false) {
Serial.println("Skipping transmission of data to AG server, post data to server disabled");
agClient->resetPostMeasuresStatus();
return;
void postUsingWifi() {
// Increment bootcount when send measurements data is scheduled
int bootCount = measurements.bootCount() + 1;
measurements.setBootCount(bootCount);
String payload = measurements.toString(false, fwMode, wifiConnector.RSSI());
if (agClient->httpPostMeasures(ag->getDeviceId(), payload.c_str()) == false) {
Serial.println();
Serial.println("Online mode and isPostToAirGradient = true");
Serial.println();
}
// Log current free heap size
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
}
void postUsingCellular() {
// Aquire queue mutex to get queue size
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
@ -1305,36 +1318,44 @@ void sendDataToServer(void) {
return;
}
Serial.printf("Measurement cycle queue size %d\n", queueSize);
// Build payload include all measurements from queue
String payload;
payload += String(CELLULAR_MEASUREMENT_INTERVAL);
for (int i = 0; i < queueSize; i++) {
auto mc = measurementCycleQueue.at(i);
payload += ",";
payload += measurements.buildMeasurementPayload(mc);
}
// Release before actually post measures that might takes too long
xSemaphoreGive(mutexMeasurementCycleQueue);
delay(10); // Wait for a moment in case new measurement schedule wait for it
for (int i = 1; i <= queueSize; i++) {
Serial.printf("Attempt post measurement cycle from queue %d\n", i);
// Aquire queue mutex to get oldest in queue
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
// Attempt to send
Serial.println(payload);
if (agClient->httpPostMeasures(ag->getDeviceId(), payload.c_str()) == false) {
// Consider network has a problem, retry in next schedule
Serial.println("Post measures failed, retry in next schedule");
return;
}
// Get the oldest queue
auto mc = measurementCycleQueue.front();
// Post success, remove the data that previously sent from queue
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
measurementCycleQueue.erase(measurementCycleQueue.begin(),
measurementCycleQueue.begin() + queueSize);
xSemaphoreGive(mutexMeasurementCycleQueue);
}
// Release before actually post measures that might takes too long
xSemaphoreGive(mutexMeasurementCycleQueue);
void sendDataToServer(void) {
if (configuration.isPostDataToAirGradient() == false) {
Serial.println("Skipping transmission of data to AG server, post data to server disabled");
agClient->resetPostMeasuresStatus();
return;
}
String payload = measurements.buildMeasurementPayload(mc, fwMode);
if (agClient->httpPostMeasures(ag->getDeviceId(), payload.c_str()) == false) {
// Consider network has a problem, retry in next schedule
Serial.println("Post measures failed, retry in next schedule");
break;
}
Serial.println();
// Post success, remove the oldest queue
xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY);
measurementCycleQueue.erase(measurementCycleQueue.begin());
xSemaphoreGive(mutexMeasurementCycleQueue);
// Wait a moment before post next in queue
delay(2000);
if (networkOption == UseWifi) {
postUsingWifi();
} else {
postUsingCellular();
}
}
@ -1469,17 +1490,18 @@ void networkingTask(void *args) {
}
void newMeasurementCycle() {
// TODO: Need to check max queue
if (xSemaphoreTake(mutexMeasurementCycleQueue, portMAX_DELAY) == pdTRUE) {
// Make sure queue not overflow
if (measurementCycleQueue.size() >= MAXIMUM_MEASUREMENT_CYCLE_QUEUE) {
// Remove the oldest data from queue if queue reach max
measurementCycleQueue.erase(measurementCycleQueue.begin());
}
Measurements::MeasurementCycle mc = measurements.getMeasurementCycle();
mc.wifi = wifiConnector.RSSI();
measurementCycleQueue.push_back(mc);
Serial.println("New measurement cycle added to queue");
// Release mutex
xSemaphoreGive(mutexMeasurementCycleQueue);
// Increment bootcount for the next measurement cycle
int bootCount = measurements.bootCount() + 1;
measurements.setBootCount(bootCount);
// Log current free heap size
Serial.printf("Free heap: %u\n", ESP.getFreeHeap());
}

View File

@ -2,6 +2,7 @@
#include "AgConfigure.h"
#include "AirGradient.h"
#include "App/AppDef.h"
#include <cmath>
#define json_prop_pmFirmware "firmware"
#define json_prop_pm01Ae "pm01"
@ -732,291 +733,90 @@ Measurements::MeasurementCycle Measurements::getMeasurementCycle() {
return mc;
}
String Measurements::buildMeasurementPayload(MeasurementCycle &mc, AgFirmwareMode fwMode) {
JSONVar root;
String Measurements::buildMeasurementPayload(MeasurementCycle &mc) {
String result;
/// 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);
}
int co2 = utils::getInvalidCO2();
float temp = utils::getInvalidTemperature();
float hum = utils::getInvalidHumidity();
float pm01 = utils::getInvalidPmValue();
float pm25 = utils::getInvalidPmValue();
float pm10 = utils::getInvalidPmValue();
int nox = utils::getInvalidNOx();
int pm003Count = utils::getInvalidPmValue();
// CO2
if (utils::isValidCO2(mc.co2)) {
root[json_prop_co2] = ag->round2(mc.co2);
co2 = std::round(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]);
}
}
// NOx
if (utils::isValidNOx(mc.nox)) {
nox = std::round(mc.nox);
}
// Others
root["boot"] = mc.bootCount;
root["bootCount"] = mc.bootCount;
root["wifi"] = mc.wifi;
root["freeHeap"] = mc.freeHeap;
root["resetReason"] = _resetReason;
/// Temperature
if (utils::isValidTemperature(mc.temperature[0]) && utils::isValidTemperature(mc.temperature[1])) {
temp = ag->round2((mc.temperature[0] + mc.temperature[1]) / 2.0f);
} else if (utils::isValidTemperature(mc.temperature[0])) {
temp = ag->round2(mc.temperature[0]);
} else if (utils::isValidTemperature(mc.temperature[1])) {
temp = ag->round2(mc.temperature[1]);
}
String result = JSON.stringify(root);
Serial.printf("\n---- TRANSMISSION PAYLOAD\n %s \n-----\n", result.c_str());
return result;
// Humidity
if (utils::isValidHumidity(mc.humidity[0]) && utils::isValidHumidity(mc.humidity[1])) {
hum = ag->round2((mc.humidity[0] + mc.humidity[1]) / 2.0f);
} else if (utils::isValidHumidity(mc.humidity[0])) {
hum = ag->round2(mc.humidity[0]);
} else if (utils::isValidHumidity(mc.humidity[1])) {
hum = ag->round2(mc.humidity[1]);
}
/// PM1.0 atmospheric environment
if (utils::isValidPm(mc.pm_01[0]) && utils::isValidPm(mc.pm_01[1])) {
pm01 = ag->round2((mc.pm_01[0] + mc.pm_01[1]) / 2.0f);
} else if (utils::isValidPm(mc.pm_01[0])) {
pm01 = ag->round2(mc.pm_01[0]);
} else if (utils::isValidPm(mc.pm_01[1])) {
pm01 = ag->round2(mc.pm_01[1]);
}
/// PM2.5 atmospheric environment
if (utils::isValidPm(mc.pm_25[0]) && utils::isValidPm(mc.pm_25[1])) {
pm25 = ag->round2((mc.pm_25[0] + mc.pm_25[1]) / 2.0f);
} else if (utils::isValidPm(mc.pm_25[0])) {
pm25 = ag->round2(mc.pm_25[0]);
} else if (utils::isValidPm(mc.pm_25[1])) {
pm25 = ag->round2(mc.pm_25[1]);
}
/// PM10 atmospheric environment
if (utils::isValidPm(mc.pm_10[0]) && utils::isValidPm(mc.pm_10[1])) {
pm10 = ag->round2((mc.pm_10[0] + mc.pm_10[1]) / 2.0f);
} else if (utils::isValidPm(mc.pm_10[0])) {
pm10 = ag->round2(mc.pm_10[0]);
} else if (utils::isValidPm(mc.pm_10[1])) {
pm10 = ag->round2(mc.pm_10[1]);
}
/// PM 0.3 particle count
if (utils::isValidPm(mc.pm_03_pc[0]) && utils::isValidPm(mc.pm_03_pc[1])) {
pm003Count = std::round((mc.pm_03_pc[0] + mc.pm_03_pc[1]) / 2.0f);
} else if (utils::isValidPm(mc.pm_03_pc[0])) {
pm003Count = std::round(mc.pm_03_pc[0]);
} else if (utils::isValidPm(mc.pm_03_pc[1])) {
pm003Count = std::round(mc.pm_03_pc[1]);
}
char datapoint[128] = {0};
Serial.printf(datapoint, 128, "%d,%.0f,%.0f,%.0f,%.0f,%.0f,%d,%d\n", co2,
temp * 10, hum * 10, pm01 * 10, pm25 * 10, pm10 * 10, nox,
pm003Count);
snprintf(datapoint, 128, "%d,%.0f,%.0f,%.0f,%.0f,%.0f,%d,%d", co2, temp * 10,
hum * 10, pm01 * 10, pm25 * 10, pm10 * 10, nox, pm003Count);
return String(datapoint);
}

View File

@ -59,7 +59,7 @@ public:
float pm_5_pc[2]; // particle count 5.0
float pm_10_pc[2]; // particle count 10
int bootCount;
int wifi;
int signal;
uint32_t freeHeap;
};
@ -182,7 +182,7 @@ public:
MeasurementCycle getMeasurementCycle();
String buildMeasurementPayload(MeasurementCycle &mc, AgFirmwareMode fwMode);
String buildMeasurementPayload(MeasurementCycle &mc);
/**
* Set to true if want to debug every update value