From 2c72d94325175ec9d079f6919f2676442d2d024c Mon Sep 17 00:00:00 2001 From: oscgonfer Date: Thu, 17 Jul 2025 21:14:18 +0200 Subject: [PATCH] Add dynamic measurement interval for SEN5X --- src/modules/Telemetry/AirQualityTelemetry.cpp | 14 +++++-- src/modules/Telemetry/Sensor/SEN5XSensor.cpp | 38 +++++++++++++++++++ src/modules/Telemetry/Sensor/SEN5XSensor.h | 22 +++++------ 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index a3a3ecb85..192285d9c 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -47,6 +47,7 @@ int32_t AirQualityTelemetryModule::runOnce() } uint32_t result = UINT32_MAX; + uint32_t sen5xPendingForReady; /* Uncomment the preferences below if you want to use the module @@ -102,6 +103,14 @@ int32_t AirQualityTelemetryModule::runOnce() return sen5xSensor.wakeUp(); } + // Check if sen5x is ready to return data, or if it needs more time because of the low concentration threshold + if (sen5xSensor.hasSensor() && sen5xSensor.isActive()) { + sen5xPendingForReady = sen5xSensor.pendingForReady(); + if (sen5xPendingForReady) { + return sen5xPendingForReady; + } + } + if (((lastSentToMesh == 0) || !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( moduleConfig.telemetry.air_quality_interval, @@ -118,14 +127,13 @@ int32_t AirQualityTelemetryModule::runOnce() lastSentToPhone = millis(); } - // TODO - Add logic here to send the sensor to idle ONLY if there is enough time to wake it up before the next reading cycle #ifdef PMSA003I_ENABLE_PIN pmsa003iSensor.sleep(); #endif /* PMSA003I_ENABLE_PIN */ - if (sen5xSensor.hasSensor() && sen5xSensor.isActive()) - sen5xSensor.idle(); + if (sen5xSensor.hasSensor() && sen5xSensor.isActive() ) + sen5xSensor.idle(); } return min(sendToPhoneIntervalMs, result); } diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp index 7da2e1086..0083d20d2 100644 --- a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp +++ b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp @@ -570,6 +570,44 @@ uint8_t SEN5XSensor::getMeasurements() return 0; } +int32_t SEN5XSensor::pendingForReady(){ + uint32_t now; + now = getTime(); + uint32_t sinceMeasureStarted = (now - measureStarted)*1000; + LOG_INFO("Since measure started: %u", sinceMeasureStarted); + switch (state) { + case SEN5X_MEASUREMENT: { + + if (sinceMeasureStarted < SEN5X_WARMUP_MS_1) { + LOG_INFO("SEN5X: not enough time passed since starting measurement"); + return SEN5X_WARMUP_MS_1 - sinceMeasureStarted; + } + + // Get PN values to check if we are above or below threshold + readPnValues(); + + // If the reading is low (the tyhreshold is in #/cm3) and second warmUp hasn't passed we return to come back later + if ((sen5xmeasurement.pN4p0 / 100) < SEN5X_PN4P0_CONC_THD && sinceMeasureStarted < SEN5X_WARMUP_MS_2) { + LOG_INFO("SEN5X: Concentration is low, we will ask again in the second warm up period"); + state = SEN5X_MEASUREMENT_2; + // Report how many seconds are pending to cover the first warm up period + return SEN5X_WARMUP_MS_2 - sinceMeasureStarted; + } + return 0; + } + case SEN5X_MEASUREMENT_2: { + if (sinceMeasureStarted < SEN5X_WARMUP_MS_2) { + // Report how many seconds are pending to cover the first warm up period + return SEN5X_WARMUP_MS_2 - sinceMeasureStarted; + } + return 0; + } + default: { + return -1; + } + } +} + bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement) { LOG_INFO("SEN5X: Attempting to get metrics"); diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.h b/src/modules/Telemetry/Sensor/SEN5XSensor.h index b8e0a0ac9..71b32a6f6 100644 --- a/src/modules/Telemetry/Sensor/SEN5XSensor.h +++ b/src/modules/Telemetry/Sensor/SEN5XSensor.h @@ -7,15 +7,12 @@ #include "Wire.h" #include "RTC.h" +// Warm up times for SEN5X from the datasheet #ifndef SEN5X_WARMUP_MS_1 -// from the SEN5X datasheet -// #define SEN5X_WARMUP_MS_1 15000 - Change to this -#define SEN5X_WARMUP_MS_1 30000 +#define SEN5X_WARMUP_MS_1 15000 #endif -// TODO - For now, we ignore this threshold, and we only use the MS_1 (to 30000) #ifndef SEN5X_WARMUP_MS_2 -// from the SEN5X datasheet #define SEN5X_WARMUP_MS_2 30000 #endif @@ -76,17 +73,10 @@ class SEN5XSensor : public TelemetrySensor enum SEN5XState { SEN5X_OFF, SEN5X_IDLE, SEN5X_MEASUREMENT, SEN5X_MEASUREMENT_2, SEN5X_CLEANING, SEN5X_NOT_DETECTED }; SEN5XState state = SEN5X_OFF; - + // TODO - Remove bool continousMode = false; bool forcedContinousMode = false; - // TODO - // Sensirion recommends taking a reading after 16 seconds, if the Perticle number reading is over 100#/cm3 the reading is OK, but if it is lower wait until 30 seconds and take it again. - // https://sensirion.com/resource/application_note/low_power_mode/sen5x - // TODO Implement logic for this concentrationThreshold - // This can reduce battery consumption by a lot - // uint16_t concentrationThreshold = 100; - bool sendCommand(uint16_t wichCommand); bool sendCommand(uint16_t wichCommand, uint8_t* buffer, uint8_t byteNumber=0); uint8_t readBuffer(uint8_t* buffer, uint8_t byteNumber); // Return number of bytes received @@ -131,6 +121,12 @@ class SEN5XSensor : public TelemetrySensor bool idle(); virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + + // Sensirion recommends taking a reading after 15 seconds, if the Particle number reading is over 100#/cm3 the reading is OK, but if it is lower wait until 30 seconds and take it again. + // https://sensirion.com/resource/application_note/low_power_mode/sen5x + #define SEN5X_PN4P0_CONC_THD 100 + // This value represents the time needed for pending data + int32_t pendingForReady(); };