* Make ReClockI2C API class * Use new API in AirQualityTelemetry module * Minor changes on some logs
212 lines
6.8 KiB
C++
212 lines
6.8 KiB
C++
#include "configuration.h"
|
|
|
|
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
|
|
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
|
#include "PMSA003ISensor.h"
|
|
#include "TelemetrySensor.h"
|
|
|
|
#include <Wire.h>
|
|
|
|
PMSA003ISensor::PMSA003ISensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA003I") {}
|
|
|
|
bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
|
{
|
|
LOG_INFO("%s: Init sensor", sensorName);
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
|
|
#endif
|
|
|
|
// TODO PMS5003I sometimes get late to the party...
|
|
|
|
_bus = bus;
|
|
_address = dev->address.address;
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
_port = dev->address.port;
|
|
reClockI2C.setup(_bus, _port);
|
|
|
|
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED);
|
|
reClockI2C.setClock(PMSA003I_I2C_CLOCK_SPEED);
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
|
|
_bus->beginTransmission(_address);
|
|
if (_bus->endTransmission() != 0) {
|
|
LOG_WARN("%s not found on I2C at 0x12", sensorName);
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
LOG_INFO("%s: restoring clock speed", sensorName);
|
|
reClockI2C.restoreClock();
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
return false;
|
|
}
|
|
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
LOG_INFO("%s: restoring clock speed", sensorName);
|
|
reClockI2C.restoreClock();
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
|
|
status = 1;
|
|
LOG_INFO("%s Enabled", sensorName);
|
|
|
|
initI2CSensor();
|
|
return true;
|
|
}
|
|
|
|
bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
|
|
{
|
|
if (!isActive()) {
|
|
LOG_WARN("Can't get metrics. %s is not active", sensorName);
|
|
return false;
|
|
}
|
|
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED);
|
|
reClockI2C.setClock(PMSA003I_I2C_CLOCK_SPEED);
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
|
|
_bus->requestFrom(_address, (uint8_t)PMSA003I_FRAME_LENGTH);
|
|
if (_bus->available() < PMSA003I_FRAME_LENGTH) {
|
|
LOG_WARN("%s: read failed: incomplete data (%d bytes)", sensorName, _bus->available());
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
LOG_INFO("%s: restoring clock speed", sensorName);
|
|
reClockI2C.restoreClock();
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
return false;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < PMSA003I_FRAME_LENGTH; i++) {
|
|
buffer[i] = _bus->read();
|
|
}
|
|
|
|
#ifdef PMSA003I_I2C_CLOCK_SPEED
|
|
LOG_INFO("%s: restoring clock speed", sensorName);
|
|
reClockI2C.restoreClock();
|
|
#endif /* PMSA003I_I2C_CLOCK_SPEED */
|
|
|
|
if (buffer[0] != 0x42 || buffer[1] != 0x4D) {
|
|
LOG_WARN("%s: frame header invalid: 0x%02X 0x%02X", sensorName, buffer[0], buffer[1]);
|
|
return false;
|
|
}
|
|
|
|
auto read16 = [](const uint8_t *data, uint8_t idx) -> uint16_t { return (data[idx] << 8) | data[idx + 1]; };
|
|
|
|
computedChecksum = 0;
|
|
|
|
for (uint8_t i = 0; i < PMSA003I_FRAME_LENGTH - 2; i++) {
|
|
computedChecksum += buffer[i];
|
|
}
|
|
receivedChecksum = read16(buffer, PMSA003I_FRAME_LENGTH - 2);
|
|
|
|
if (computedChecksum != receivedChecksum) {
|
|
LOG_WARN("%s checksum failed: computed 0x%04X, received 0x%04X", sensorName, computedChecksum, receivedChecksum);
|
|
return false;
|
|
}
|
|
|
|
measurement->variant.air_quality_metrics.has_pm10_standard = true;
|
|
measurement->variant.air_quality_metrics.pm10_standard = read16(buffer, 4);
|
|
|
|
measurement->variant.air_quality_metrics.has_pm25_standard = true;
|
|
measurement->variant.air_quality_metrics.pm25_standard = read16(buffer, 6);
|
|
|
|
measurement->variant.air_quality_metrics.has_pm100_standard = true;
|
|
measurement->variant.air_quality_metrics.pm100_standard = read16(buffer, 8);
|
|
|
|
// TODO - Add admin command to remove environmental metrics to save protobuf space
|
|
measurement->variant.air_quality_metrics.has_pm10_environmental = true;
|
|
measurement->variant.air_quality_metrics.pm10_environmental = read16(buffer, 10);
|
|
|
|
measurement->variant.air_quality_metrics.has_pm25_environmental = true;
|
|
measurement->variant.air_quality_metrics.pm25_environmental = read16(buffer, 12);
|
|
|
|
measurement->variant.air_quality_metrics.has_pm100_environmental = true;
|
|
measurement->variant.air_quality_metrics.pm100_environmental = read16(buffer, 14);
|
|
|
|
// TODO - Add admin command to remove PN to save protobuf space
|
|
measurement->variant.air_quality_metrics.has_particles_03um = true;
|
|
measurement->variant.air_quality_metrics.particles_03um = read16(buffer, 16);
|
|
|
|
measurement->variant.air_quality_metrics.has_particles_05um = true;
|
|
measurement->variant.air_quality_metrics.particles_05um = read16(buffer, 18);
|
|
|
|
measurement->variant.air_quality_metrics.has_particles_10um = true;
|
|
measurement->variant.air_quality_metrics.particles_10um = read16(buffer, 20);
|
|
|
|
measurement->variant.air_quality_metrics.has_particles_25um = true;
|
|
measurement->variant.air_quality_metrics.particles_25um = read16(buffer, 22);
|
|
|
|
measurement->variant.air_quality_metrics.has_particles_50um = true;
|
|
measurement->variant.air_quality_metrics.particles_50um = read16(buffer, 24);
|
|
|
|
measurement->variant.air_quality_metrics.has_particles_100um = true;
|
|
measurement->variant.air_quality_metrics.particles_100um = read16(buffer, 26);
|
|
|
|
LOG_DEBUG("%s: Got readings: pM1p0_standard=%u, pM2p5_standard=%u, pM10p0_standard=%u", sensorName,
|
|
measurement->variant.air_quality_metrics.pm10_standard, measurement->variant.air_quality_metrics.pm25_standard,
|
|
measurement->variant.air_quality_metrics.pm100_standard);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PMSA003ISensor::isActive()
|
|
{
|
|
return state == State::ACTIVE;
|
|
}
|
|
|
|
int32_t PMSA003ISensor::wakeUpTimeMs()
|
|
{
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
return PMSA003I_WARMUP_MS;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int32_t PMSA003ISensor::pendingForReadyMs()
|
|
{
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
|
|
uint32_t now;
|
|
now = getTime();
|
|
uint32_t sincePmMeasureStarted = (now - pmMeasureStarted) * 1000;
|
|
LOG_DEBUG("%s: Since measure started: %ums", sensorName, sincePmMeasureStarted);
|
|
|
|
if (sincePmMeasureStarted < PMSA003I_WARMUP_MS) {
|
|
LOG_INFO("%s: not enough time passed since starting measurement", sensorName);
|
|
return PMSA003I_WARMUP_MS - sincePmMeasureStarted;
|
|
}
|
|
return 0;
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
bool PMSA003ISensor::canSleep()
|
|
{
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void PMSA003ISensor::sleep()
|
|
{
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
|
|
state = State::IDLE;
|
|
pmMeasureStarted = 0;
|
|
#endif
|
|
}
|
|
|
|
uint32_t PMSA003ISensor::wakeUp()
|
|
{
|
|
#ifdef PMSA003I_ENABLE_PIN
|
|
LOG_INFO("%s: Waking up", sensorName);
|
|
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
|
|
state = State::ACTIVE;
|
|
pmMeasureStarted = getTime();
|
|
|
|
return PMSA003I_WARMUP_MS;
|
|
#endif
|
|
// No need to wait for warmup if already active
|
|
return 0;
|
|
}
|
|
#endif
|