Add dynamic measurement interval for SEN5X

This commit is contained in:
oscgonfer 2025-07-17 21:14:18 +02:00
parent e712200509
commit 2c72d94325
3 changed files with 58 additions and 16 deletions

View File

@ -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);
}

View File

@ -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");

View File

@ -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();
};