From d5839912483eaa5afb9209a45812d29c641c4d0a Mon Sep 17 00:00:00 2001 From: oscgonfer Date: Tue, 26 Aug 2025 19:02:12 +0200 Subject: [PATCH] Add nan checks on sensor data from SEN5X --- src/modules/Telemetry/Sensor/SEN5XSensor.cpp | 130 +++++++++++-------- src/modules/Telemetry/Sensor/SEN5XSensor.h | 6 +- 2 files changed, 79 insertions(+), 57 deletions(-) diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp index f3096d82f..2c31d3f1d 100644 --- a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp +++ b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp @@ -10,6 +10,7 @@ #include "SafeFile.h" #include #include +#include // FLT_MAX SEN5XSensor::SEN5XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SEN5X, "SEN5X") {} @@ -620,7 +621,7 @@ bool SEN5XSensor::readValues() return false; } - // First get the integers + // Get the integers uint16_t uint_pM1p0 = static_cast((dataBuffer[0] << 8) | dataBuffer[1]); uint16_t uint_pM2p5 = static_cast((dataBuffer[2] << 8) | dataBuffer[3]); uint16_t uint_pM4p0 = static_cast((dataBuffer[4] << 8) | dataBuffer[5]); @@ -630,17 +631,15 @@ bool SEN5XSensor::readValues() int16_t int_vocIndex = static_cast((dataBuffer[12] << 8) | dataBuffer[13]); int16_t int_noxIndex = static_cast((dataBuffer[14] << 8) | dataBuffer[15]); - // TODO we should check if values are NAN before converting them - // Convert values based on Sensirion Arduino lib - sen5xmeasurement.pM1p0 = uint_pM1p0 / 10; - sen5xmeasurement.pM2p5 = uint_pM2p5 / 10; - sen5xmeasurement.pM4p0 = uint_pM4p0 / 10; - sen5xmeasurement.pM10p0 = uint_pM10p0 / 10; - sen5xmeasurement.humidity = int_humidity / 100.0f; - sen5xmeasurement.temperature = int_temperature / 200.0f; - sen5xmeasurement.vocIndex = int_vocIndex / 10.0f; - sen5xmeasurement.noxIndex = int_noxIndex / 10.0f; + sen5xmeasurement.pM1p0 = !isnan(uint_pM1p0) ? uint_pM1p0 / 10 : UINT16_MAX; + sen5xmeasurement.pM2p5 = !isnan(uint_pM2p5) ? uint_pM2p5 / 10 : UINT16_MAX; + sen5xmeasurement.pM4p0 = !isnan(uint_pM4p0) ? uint_pM4p0 / 10 : UINT16_MAX; + sen5xmeasurement.pM10p0 = !isnan(uint_pM10p0) ? uint_pM10p0 / 10 : UINT16_MAX; + sen5xmeasurement.humidity = !isnan(int_humidity) ? int_humidity / 100.0f : FLT_MAX; + sen5xmeasurement.temperature = !isnan(int_temperature) ? int_temperature / 200.0f : FLT_MAX; + sen5xmeasurement.vocIndex = !isnan(int_vocIndex) ? int_vocIndex / 10.0f : FLT_MAX; + sen5xmeasurement.noxIndex = !isnan(int_noxIndex) ? int_noxIndex / 10.0f : FLT_MAX; LOG_DEBUG("Got: pM1p0=%u, pM2p5=%u, pM4p0=%u, pM10p0=%u", sen5xmeasurement.pM1p0, sen5xmeasurement.pM2p5, @@ -666,7 +665,7 @@ bool SEN5XSensor::readPnValues(bool cumulative) return false; } - // First get the integers + // Get the integers // uint16_t uint_pM1p0 = static_cast((dataBuffer[0] << 8) | dataBuffer[1]); // uint16_t uint_pM2p5 = static_cast((dataBuffer[2] << 8) | dataBuffer[3]); // uint16_t uint_pM4p0 = static_cast((dataBuffer[4] << 8) | dataBuffer[5]); @@ -679,20 +678,13 @@ bool SEN5XSensor::readPnValues(bool cumulative) uint16_t uint_tSize = static_cast((dataBuffer[18] << 8) | dataBuffer[19]); // Convert values based on Sensirion Arduino lib - sen5xmeasurement.pN0p5 = uint_pN0p5 / 10; - sen5xmeasurement.pN1p0 = uint_pN1p0 / 10; - sen5xmeasurement.pN2p5 = uint_pN2p5 / 10; - sen5xmeasurement.pN4p0 = uint_pN4p0 / 10; - sen5xmeasurement.pN10p0 = uint_pN10p0 / 10; - sen5xmeasurement.tSize = uint_tSize / 1000.0f; - - // Convert PN readings from #/cm3 to #/0.1l - sen5xmeasurement.pN0p5 *= 100; - sen5xmeasurement.pN1p0 *= 100; - sen5xmeasurement.pN2p5 *= 100; - sen5xmeasurement.pN4p0 *= 100; - sen5xmeasurement.pN10p0 *= 100; - sen5xmeasurement.tSize *= 100; + // Multiply by 100 for converting from #/cm3 to #/0.1l for PN values + sen5xmeasurement.pN0p5 = !isnan(uint_pN0p5) ? uint_pN0p5 / 10 * 100: UINT32_MAX; + sen5xmeasurement.pN1p0 = !isnan(uint_pN1p0) ? uint_pN1p0 / 10 * 100: UINT32_MAX; + sen5xmeasurement.pN2p5 = !isnan(uint_pN2p5) ? uint_pN2p5 / 10 * 100: UINT32_MAX; + sen5xmeasurement.pN4p0 = !isnan(uint_pN4p0) ? uint_pN4p0 / 10 * 100: UINT32_MAX; + sen5xmeasurement.pN10p0 = !isnan(uint_pN10p0) ? uint_pN10p0 / 10 * 100: UINT32_MAX; + sen5xmeasurement.tSize = !isnan(uint_tSize) ? uint_tSize / 1000.0f : FLT_MAX; // Remove accumuluative values: // https://github.com/fablabbcn/smartcitizen-kit-2x/issues/85 @@ -766,7 +758,7 @@ uint8_t SEN5XSensor::getMeasurements() } if(!readPnValues(false)) { - LOG_ERROR("SEN5X: Error getting PM readings"); + LOG_ERROR("SEN5X: Error getting PN readings"); return 2; } @@ -828,39 +820,69 @@ bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement) response = getMeasurements(); if (response == 0) { - measurement->variant.air_quality_metrics.has_pm10_standard = true; - measurement->variant.air_quality_metrics.pm10_standard = sen5xmeasurement.pM1p0; - measurement->variant.air_quality_metrics.has_pm25_standard = true; - measurement->variant.air_quality_metrics.pm25_standard = sen5xmeasurement.pM2p5; - measurement->variant.air_quality_metrics.has_pm40_standard = true; - measurement->variant.air_quality_metrics.pm40_standard = sen5xmeasurement.pM4p0; - measurement->variant.air_quality_metrics.has_pm100_standard = true; - measurement->variant.air_quality_metrics.pm100_standard = sen5xmeasurement.pM10p0; - - measurement->variant.air_quality_metrics.has_particles_05um = true; - measurement->variant.air_quality_metrics.particles_05um = sen5xmeasurement.pN0p5; - measurement->variant.air_quality_metrics.has_particles_10um = true; - measurement->variant.air_quality_metrics.particles_10um = sen5xmeasurement.pN1p0; - measurement->variant.air_quality_metrics.has_particles_25um = true; - measurement->variant.air_quality_metrics.particles_25um = sen5xmeasurement.pN2p5; - measurement->variant.air_quality_metrics.has_particles_40um = true; - measurement->variant.air_quality_metrics.particles_40um = sen5xmeasurement.pN4p0; - measurement->variant.air_quality_metrics.has_particles_100um = true; - measurement->variant.air_quality_metrics.particles_100um = sen5xmeasurement.pN10p0; + if (sen5xmeasurement.pM1p0 != UINT16_MAX) { + measurement->variant.air_quality_metrics.has_pm10_standard = true; + measurement->variant.air_quality_metrics.pm10_standard = sen5xmeasurement.pM1p0; + } + if (sen5xmeasurement.pM2p5 != UINT16_MAX) { + measurement->variant.air_quality_metrics.has_pm25_standard = true; + measurement->variant.air_quality_metrics.pm25_standard = sen5xmeasurement.pM2p5; + } + if (sen5xmeasurement.pM4p0 != UINT16_MAX) { + measurement->variant.air_quality_metrics.has_pm40_standard = true; + measurement->variant.air_quality_metrics.pm40_standard = sen5xmeasurement.pM4p0; + } + if (sen5xmeasurement.pM10p0 != UINT16_MAX) { + measurement->variant.air_quality_metrics.has_pm100_standard = true; + measurement->variant.air_quality_metrics.pm100_standard = sen5xmeasurement.pM10p0; + } + if (sen5xmeasurement.pN0p5 != UINT32_MAX) { + measurement->variant.air_quality_metrics.has_particles_05um = true; + measurement->variant.air_quality_metrics.particles_05um = sen5xmeasurement.pN0p5; + } + if (sen5xmeasurement.pN1p0 != UINT32_MAX) { + measurement->variant.air_quality_metrics.has_particles_10um = true; + measurement->variant.air_quality_metrics.particles_10um = sen5xmeasurement.pN1p0; + } + if (sen5xmeasurement.pN2p5 != UINT32_MAX) { + measurement->variant.air_quality_metrics.has_particles_25um = true; + measurement->variant.air_quality_metrics.particles_25um = sen5xmeasurement.pN2p5; + } + if (sen5xmeasurement.pN4p0 != UINT32_MAX) { + measurement->variant.air_quality_metrics.has_particles_40um = true; + measurement->variant.air_quality_metrics.particles_40um = sen5xmeasurement.pN4p0; + } + if (sen5xmeasurement.pN10p0 != UINT32_MAX) { + measurement->variant.air_quality_metrics.has_particles_100um = true; + measurement->variant.air_quality_metrics.particles_100um = sen5xmeasurement.pN10p0; + } + if (sen5xmeasurement.tSize != FLT_MAX) { + measurement->variant.air_quality_metrics.has_particles_tps = true; + measurement->variant.air_quality_metrics.particles_tps = sen5xmeasurement.tSize; + } if (model == SEN54 || model == SEN55) { - measurement->variant.air_quality_metrics.has_pm_humidity = true; - measurement->variant.air_quality_metrics.pm_humidity = sen5xmeasurement.humidity; - measurement->variant.air_quality_metrics.has_pm_temperature = true; - measurement->variant.air_quality_metrics.pm_temperature = sen5xmeasurement.temperature; - measurement->variant.air_quality_metrics.has_pm_nox_idx = true; - measurement->variant.air_quality_metrics.pm_nox_idx = sen5xmeasurement.noxIndex; + if (sen5xmeasurement.humidity!= FLT_MAX) { + measurement->variant.air_quality_metrics.has_pm_humidity = true; + measurement->variant.air_quality_metrics.pm_humidity = sen5xmeasurement.humidity; + } + if (sen5xmeasurement.temperature!= FLT_MAX) { + measurement->variant.air_quality_metrics.has_pm_temperature = true; + measurement->variant.air_quality_metrics.pm_temperature = sen5xmeasurement.temperature; + } + if (sen5xmeasurement.noxIndex!= FLT_MAX) { + measurement->variant.air_quality_metrics.has_pm_nox_idx = true; + measurement->variant.air_quality_metrics.pm_nox_idx = sen5xmeasurement.noxIndex; + } } if (model == SEN55) { - measurement->variant.air_quality_metrics.has_pm_voc_idx = true; - measurement->variant.air_quality_metrics.pm_voc_idx = sen5xmeasurement.vocIndex; + if (sen5xmeasurement.noxIndex!= FLT_MAX) { + measurement->variant.air_quality_metrics.has_pm_voc_idx = true; + measurement->variant.air_quality_metrics.pm_voc_idx = sen5xmeasurement.vocIndex; + } } + return true; } else if (response == 1) { // TODO return because data was not ready yet diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.h b/src/modules/Telemetry/Sensor/SEN5XSensor.h index e89470a51..1f4694a35 100644 --- a/src/modules/Telemetry/Sensor/SEN5XSensor.h +++ b/src/modules/Telemetry/Sensor/SEN5XSensor.h @@ -78,15 +78,15 @@ class SEN5XSensor : public TelemetrySensor bool oneShotMode = true; void setMode(bool setOneShot); - bool sendCommand(uint16_t wichCommand); - bool sendCommand(uint16_t wichCommand, uint8_t* buffer, uint8_t byteNumber=0); + bool sendCommand(uint16_t command); + bool sendCommand(uint16_t command, uint8_t* buffer, uint8_t byteNumber=0); uint8_t readBuffer(uint8_t* buffer, uint8_t byteNumber); // Return number of bytes received uint8_t sen5xCRC(uint8_t* buffer); bool I2Cdetect(TwoWire *_Wire, uint8_t address); bool restoreClock(uint32_t); bool startCleaning(); uint8_t getMeasurements(); - bool readRawValues(); + // bool readRawValues(); bool readPnValues(bool cumulative); bool readValues();