PMSA003I: add support for driving SET pin low while not actively taking a telemetry reading (#6569)

* support manually shutting off power to the PMSA003I sensor when we aren't doing a telemetry reading

* add comment about PMSA003I_WARMUP_MS to AirQualityTelemetry.cpp

* fix typos, use arduino gpio defines instead of magic numbers

* support manually shutting off power to the PMSA003I sensor when we aren't doing a telemetry reading

* add comment about PMSA003I_WARMUP_MS to AirQualityTelemetry.cpp

* fix typos, use arduino gpio defines instead of magic numbers

* RAK4631: add PMSA003I_ENABLE_PIN define

* fix indentation

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
Colin 2025-04-29 04:31:01 -07:00 committed by GitHub
parent b4e8f7dbb6
commit 72eae42b81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 71 additions and 13 deletions

View File

@ -14,6 +14,13 @@
#include "main.h"
#include <Throttle.h>
#ifndef PMSA003I_WARMUP_MS
// from the PMSA003I datasheet:
// "Stable data should be got at least 30 seconds after the sensor wakeup
// from the sleep mode because of the fans performance."
#define PMSA003I_WARMUP_MS 30000
#endif
int32_t AirQualityTelemetryModule::runOnce()
{
/*
@ -34,6 +41,13 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init");
#ifdef PMSA003I_ENABLE_PIN
// put the sensor to sleep on startup
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
#endif /* PMSA003I_ENABLE_PIN */
if (!aqi.begin_I2C()) {
#ifndef I2C_NO_RESCAN
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan");
@ -63,21 +77,45 @@ int32_t AirQualityTelemetryModule::runOnce()
if (!moduleConfig.telemetry.air_quality_enabled)
return disable();
if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (service->isToPhoneQueueEmpty()) {
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true);
switch (state) {
#ifdef PMSA003I_ENABLE_PIN
case State::IDLE:
// sensor is in standby; fire it up and sleep
LOG_DEBUG("runOnce(): state = idle");
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
state = State::ACTIVE;
return PMSA003I_WARMUP_MS;
#endif /* PMSA003I_ENABLE_PIN */
case State::ACTIVE:
// sensor is already warmed up; grab telemetry and send it
LOG_DEBUG("runOnce(): state = active");
if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (service->isToPhoneQueueEmpty()) {
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true);
}
#ifdef PMSA003I_ENABLE_PIN
// put sensor back to sleep
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
state = State::IDLE;
#endif /* PMSA003I_ENABLE_PIN */
return sendToPhoneIntervalMs;
default:
return disable();
}
}
return sendToPhoneIntervalMs;
}
bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)

View File

@ -23,6 +23,14 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
setIntervalFromNow(10 * 1000);
aqi = Adafruit_PM25AQI();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
#ifdef PMSA003I_ENABLE_PIN
// the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking
// a reading
state = State::IDLE;
#else
state = State::ACTIVE;
#endif
}
protected:
@ -42,6 +50,12 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
enum State {
IDLE = 0,
ACTIVE = 1,
};
State state;
Adafruit_PM25AQI aqi;
PM25_AQI_Data data = {0};
bool firstTime = true;

View File

@ -197,6 +197,12 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
*/
// configure the SET pin on the RAK12039 sensor board to disable the sensor while not reading
// air quality telemetry. PIN_NFC2 doesn't seem to be used anywhere else in the codebase, but if
// you're having problems with your node behaving weirdly when a RAK12039 board isn't connected,
// try disabling this.
#define PMSA003I_ENABLE_PIN PIN_NFC2
#define DETECTION_SENSOR_EN 4
#define USE_SX1262