build CE payload include tvoc and nox index

If measures value invalid, set it to empty
New schedule to print network signal
This commit is contained in:
samuelbles07
2025-03-26 16:18:48 +07:00
parent ef87cde9d6
commit 070a103234
3 changed files with 90 additions and 50 deletions

View File

@ -38,7 +38,6 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include "Arduino.h" #include "Arduino.h"
#include "EEPROM.h" #include "EEPROM.h"
#include "ESPmDNS.h" #include "ESPmDNS.h"
#include "Libraries/airgradient-ota/src/airgradientOta.h"
#include "LocalServer.h" #include "LocalServer.h"
#include "MqttClient.h" #include "MqttClient.h"
#include "OpenMetrics.h" #include "OpenMetrics.h"
@ -54,6 +53,7 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include "Libraries/airgradient-client/src/cellularModuleA7672xx.h" #include "Libraries/airgradient-client/src/cellularModuleA7672xx.h"
#include "Libraries/airgradient-client/src/airgradientCellularClient.h" #include "Libraries/airgradient-client/src/airgradientCellularClient.h"
#include "Libraries/airgradient-client/src/airgradientWifiClient.h" #include "Libraries/airgradient-client/src/airgradientWifiClient.h"
#include "Libraries/airgradient-ota/src/airgradientOta.h"
#include "Libraries/airgradient-ota/src/airgradientOtaWifi.h" #include "Libraries/airgradient-ota/src/airgradientOtaWifi.h"
#include "Libraries/airgradient-ota/src/airgradientOtaCellular.h" #include "Libraries/airgradient-ota/src/airgradientOtaCellular.h"
#include "esp_system.h" #include "esp_system.h"
@ -147,6 +147,7 @@ static void displayExecuteOta(AirgradientOTA::OtaResult result, String msg, int
static int calculateMaxPeriod(int updateInterval); static int calculateMaxPeriod(int updateInterval);
static void setMeasurementMaxPeriod(); static void setMeasurementMaxPeriod();
static void newMeasurementCycle(); static void newMeasurementCycle();
static void networkSignalCheck();
static void networkingTask(void *args); static void networkingTask(void *args);
AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar); AgSchedule dispLedSchedule(DISP_UPDATE_INTERVAL, updateDisplayAndLedBar);
@ -160,6 +161,7 @@ AgSchedule tempHumSchedule(SENSOR_TEMP_HUM_UPDATE_INTERVAL, tempHumUpdate);
AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc); AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, updateTvoc);
AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate); AgSchedule watchdogFeedSchedule(60000, wdgFeedUpdate);
AgSchedule checkForUpdateSchedule(FIRMWARE_CHECK_FOR_UPDATE_MS, checkForFirmwareUpdate); AgSchedule checkForUpdateSchedule(FIRMWARE_CHECK_FOR_UPDATE_MS, checkForFirmwareUpdate);
AgSchedule networkSignalCheckSchedule(10000, networkSignalCheck);
void setup() { void setup() {
/** Serial for print debug message */ /** Serial for print debug message */
@ -575,6 +577,7 @@ void otaHandlerCallback(AirgradientOTA::OtaResult result, const char *msg) {
displayExecuteOta(result, fwNewVersion, 0); displayExecuteOta(result, fwNewVersion, 0);
break; break;
case AirgradientOTA::InProgress: case AirgradientOTA::InProgress:
Serial.printf("OTA progress: %s", msg);
displayExecuteOta(result, "", std::stoi(msg)); displayExecuteOta(result, "", std::stoi(msg));
break; break;
case AirgradientOTA::Failed: case AirgradientOTA::Failed:
@ -1329,8 +1332,8 @@ void postUsingCellular() {
} }
// Build payload include all measurements from queue // Build payload include all measurements from queue
String payload; std::string payload;
payload += String(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds payload += std::to_string(CELLULAR_MEASUREMENT_INTERVAL / 1000); // Convert to seconds
for (int i = 0; i < queueSize; i++) { for (int i = 0; i < queueSize; i++) {
auto mc = measurementCycleQueue.at(i); auto mc = measurementCycleQueue.at(i);
payload += ","; payload += ",";
@ -1341,8 +1344,7 @@ void postUsingCellular() {
xSemaphoreGive(mutexMeasurementCycleQueue); xSemaphoreGive(mutexMeasurementCycleQueue);
// Attempt to send // Attempt to send
Serial.println(payload); if (agClient->httpPostMeasures(payload) == false) {
if (agClient->httpPostMeasures(payload.c_str()) == false) {
// Consider network has a problem, retry in next schedule // Consider network has a problem, retry in next schedule
Serial.println("Post measures failed, retry in next schedule"); Serial.println("Post measures failed, retry in next schedule");
return; return;
@ -1364,7 +1366,7 @@ void sendDataToServer(void) {
if (networkOption == UseWifi) { if (networkOption == UseWifi) {
postUsingWifi(); postUsingWifi();
} else { } else if (networkOption == UseCellular) {
postUsingCellular(); postUsingCellular();
} }
} }
@ -1443,6 +1445,20 @@ int calculateMaxPeriod(int updateInterval) {
} }
} }
void networkSignalCheck() {
if (networkOption == UseWifi) {
Serial.printf("WiFi RSSI %d\n", wifiConnector.RSSI());
} else if (networkOption == UseCellular) {
auto result = cell->retrieveSignal();
if (result.status != CellReturnStatus::Ok) {
// TODO: Need to do something when get signal failed
return;
}
Serial.printf("Cellular signal strength %d\n", result.data);
}
}
void networkingTask(void *args) { void networkingTask(void *args) {
// OTA check on boot // OTA check on boot
#ifdef ESP8266 #ifdef ESP8266
@ -1453,7 +1469,15 @@ void networkingTask(void *args) {
checkForUpdateSchedule.update(); checkForUpdateSchedule.update();
#endif #endif
// TODO: Need to better define delay value // Because cellular interval is longer, needs to send first measures cycle on
// boot to indicate that its online
if (networkOption == UseCellular) {
Serial.println("Prepare first measures cycle to send on boot for 20s");
delay(20000);
newMeasurementCycle();
sendDataToServer();
measurementSchedule.update();
}
// Reset scheduler // Reset scheduler
configSchedule.update(); configSchedule.update();
@ -1489,6 +1513,7 @@ void networkingTask(void *args) {
} }
// Run scheduler // Run scheduler
networkSignalCheckSchedule.run();
configSchedule.run(); configSchedule.run();
transmissionSchedule.run(); transmissionSchedule.run();
checkForUpdateSchedule.run(); checkForUpdateSchedule.run();

View File

@ -3,6 +3,7 @@
#include "AirGradient.h" #include "AirGradient.h"
#include "App/AppDef.h" #include "App/AppDef.h"
#include <cmath> #include <cmath>
#include <sstream>
#define json_prop_pmFirmware "firmware" #define json_prop_pmFirmware "firmware"
#define json_prop_pm01Ae "pm01" #define json_prop_pm01Ae "pm01"
@ -733,90 +734,104 @@ Measurements::MeasurementCycle Measurements::getMeasurementCycle() {
return mc; return mc;
} }
String Measurements::buildMeasurementPayload(MeasurementCycle &mc) { std::string Measurements::buildMeasurementPayload(MeasurementCycle &mc) {
String result; std::ostringstream oss;
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 // CO2
if (utils::isValidCO2(mc.co2)) { if (utils::isValidCO2(mc.co2)) {
co2 = std::round(mc.co2); oss << std::round(mc.co2);
} }
// NOx oss << ",";
if (utils::isValidNOx(mc.nox)) {
nox = std::round(mc.nox);
}
/// Temperature // Temperature
if (utils::isValidTemperature(mc.temperature[0]) && utils::isValidTemperature(mc.temperature[1])) { if (utils::isValidTemperature(mc.temperature[0]) && utils::isValidTemperature(mc.temperature[1])) {
temp = ag->round2((mc.temperature[0] + mc.temperature[1]) / 2.0f); float temp = (mc.temperature[0] + mc.temperature[1]) / 2.0f;
oss << std::round(temp * 10);
} else if (utils::isValidTemperature(mc.temperature[0])) { } else if (utils::isValidTemperature(mc.temperature[0])) {
temp = ag->round2(mc.temperature[0]); oss << std::round(mc.temperature[0] * 10);
} else if (utils::isValidTemperature(mc.temperature[1])) { } else if (utils::isValidTemperature(mc.temperature[1])) {
temp = ag->round2(mc.temperature[1]); oss << std::round(mc.temperature[1] * 10);
} }
oss << ",";
// Humidity // Humidity
if (utils::isValidHumidity(mc.humidity[0]) && utils::isValidHumidity(mc.humidity[1])) { if (utils::isValidHumidity(mc.humidity[0]) && utils::isValidHumidity(mc.humidity[1])) {
hum = ag->round2((mc.humidity[0] + mc.humidity[1]) / 2.0f); float hum = (mc.humidity[0] + mc.humidity[1]) / 2.0f;
oss << std::round(hum * 10);
} else if (utils::isValidHumidity(mc.humidity[0])) { } else if (utils::isValidHumidity(mc.humidity[0])) {
hum = ag->round2(mc.humidity[0]); oss << std::round(mc.humidity[0] * 10);
} else if (utils::isValidHumidity(mc.humidity[1])) { } else if (utils::isValidHumidity(mc.humidity[1])) {
hum = ag->round2(mc.humidity[1]); oss << std::round(mc.humidity[1] * 10);
} }
oss << ",";
/// PM1.0 atmospheric environment /// PM1.0 atmospheric environment
if (utils::isValidPm(mc.pm_01[0]) && utils::isValidPm(mc.pm_01[1])) { 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); float pm01 = (mc.pm_01[0] + mc.pm_01[1]) / 2.0f;
oss << std::round(pm01 * 10);
} else if (utils::isValidPm(mc.pm_01[0])) { } else if (utils::isValidPm(mc.pm_01[0])) {
pm01 = ag->round2(mc.pm_01[0]); oss << std::round(mc.pm_01[0] * 10);
} else if (utils::isValidPm(mc.pm_01[1])) { } else if (utils::isValidPm(mc.pm_01[1])) {
pm01 = ag->round2(mc.pm_01[1]); oss << std::round(mc.pm_01[1] * 10);
} }
oss << ",";
/// PM2.5 atmospheric environment /// PM2.5 atmospheric environment
if (utils::isValidPm(mc.pm_25[0]) && utils::isValidPm(mc.pm_25[1])) { 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); float pm25 = (mc.pm_25[0] + mc.pm_25[1]) / 2.0f;
oss << std::round(pm25 * 10);
} else if (utils::isValidPm(mc.pm_25[0])) { } else if (utils::isValidPm(mc.pm_25[0])) {
pm25 = ag->round2(mc.pm_25[0]); oss << std::round(mc.pm_25[0] * 10);
} else if (utils::isValidPm(mc.pm_25[1])) { } else if (utils::isValidPm(mc.pm_25[1])) {
pm25 = ag->round2(mc.pm_25[1]); oss << std::round(mc.pm_25[1] * 10);
} }
oss << ",";
/// PM10 atmospheric environment /// PM10 atmospheric environment
if (utils::isValidPm(mc.pm_10[0]) && utils::isValidPm(mc.pm_10[1])) { 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); float pm10 = (mc.pm_10[0] + mc.pm_10[1]) / 2.0f;
oss << std::round(pm10 * 10);
} else if (utils::isValidPm(mc.pm_10[0])) { } else if (utils::isValidPm(mc.pm_10[0])) {
pm10 = ag->round2(mc.pm_10[0]); oss << std::round(mc.pm_10[0] * 10);
} else if (utils::isValidPm(mc.pm_10[1])) { } else if (utils::isValidPm(mc.pm_10[1])) {
pm10 = ag->round2(mc.pm_10[1]); oss << std::round(mc.pm_10[1] * 10);
} }
oss << ",";
// NOx
if (utils::isValidNOx(mc.nox)) {
oss << std::round(mc.nox);
}
oss << ",";
// TVOC
if (utils::isValidVOC(mc.tvoc)) {
oss << std::round(mc.tvoc);
}
oss << ",";
/// PM 0.3 particle count /// PM 0.3 particle count
if (utils::isValidPm03Count(mc.pm_03_pc[0]) && utils::isValidPm03Count(mc.pm_03_pc[1])) { if (utils::isValidPm03Count(mc.pm_03_pc[0]) && utils::isValidPm03Count(mc.pm_03_pc[1])) {
pm003Count = std::round((mc.pm_03_pc[0] + mc.pm_03_pc[1]) / 2.0f); oss << std::round((mc.pm_03_pc[0] + mc.pm_03_pc[1]) / 2.0f);
} else if (utils::isValidPm03Count(mc.pm_03_pc[0])) { } else if (utils::isValidPm03Count(mc.pm_03_pc[0])) {
pm003Count = std::round(mc.pm_03_pc[0]); oss << std::round(mc.pm_03_pc[0]);
} else if (utils::isValidPm03Count(mc.pm_03_pc[1])) { } else if (utils::isValidPm03Count(mc.pm_03_pc[1])) {
pm003Count = std::round(mc.pm_03_pc[1]); oss << std::round(mc.pm_03_pc[1]);
} }
char datapoint[128] = {0}; // char datapoint[128] = {0};
Serial.printf(datapoint, 128, "%d,%.0f,%.0f,%.0f,%.0f,%.0f,%d,%d\n", co2, // snprintf(datapoint, 128, "%d,%.0f,%.0f,%.0f,%.0f,%.0f,%d,%d,%d", co2, temp * 10,
temp * 10, hum * 10, pm01 * 10, pm25 * 10, pm10 * 10, nox, // hum * 10, pm01 * 10, pm25 * 10, pm10 * 10, tvoc, nox, pm003Count);
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); return oss.str();
} }

View File

@ -182,7 +182,7 @@ public:
MeasurementCycle getMeasurementCycle(); MeasurementCycle getMeasurementCycle();
String buildMeasurementPayload(MeasurementCycle &mc); std::string buildMeasurementPayload(MeasurementCycle &mc);
/** /**
* Set to true if want to debug every update value * Set to true if want to debug every update value