mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-25 03:07:44 +00:00
Move PMSA003I to separate class and update AQ telemetry (#7190)
This commit is contained in:
34
src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
Normal file
34
src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
||||
|
||||
#include <forward_list>
|
||||
#include "TelemetrySensor.h"
|
||||
#include "detect/ScanI2C.h"
|
||||
#include "detect/ScanI2CTwoWire.h"
|
||||
#include <Wire.h>
|
||||
|
||||
static std::forward_list<TelemetrySensor *> sensors;
|
||||
|
||||
template <typename T> void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type)
|
||||
{
|
||||
ScanI2C::FoundDevice dev = i2cScanner->find(type);
|
||||
if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) {
|
||||
TelemetrySensor *sensor = new T();
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
TwoWire *bus = ScanI2CTwoWire::fetchI2CBus(dev.address);
|
||||
if (dev.address.port != ScanI2C::I2CPort::WIRE1 && sensor->onlyWire1()) {
|
||||
// This sensor only works on Wire (Wire1 is not supported)
|
||||
delete sensor;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
TwoWire *bus = &Wire;
|
||||
#endif
|
||||
if (sensor->initDevice(bus, &dev)) {
|
||||
sensors.push_front(sensor);
|
||||
return;
|
||||
}
|
||||
// destroy sensor
|
||||
delete sensor;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
164
src/modules/Telemetry/Sensor/PMSA003ISensor.cpp
Normal file
164
src/modules/Telemetry/Sensor/PMSA003ISensor.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "PMSA003ISensor.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include "../detect/reClockI2C.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
PMSA003ISensor::PMSA003ISensor()
|
||||
: TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA003I")
|
||||
{
|
||||
}
|
||||
|
||||
bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
||||
{
|
||||
LOG_INFO("Init sensor: %s", sensorName);
|
||||
#ifdef PMSA003I_ENABLE_PIN
|
||||
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
|
||||
#endif
|
||||
|
||||
_bus = bus;
|
||||
_address = dev->address.address;
|
||||
|
||||
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
|
||||
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus);
|
||||
if (!currentClock){
|
||||
LOG_WARN("PMSA003I can't be used at this clock speed");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
_bus->beginTransmission(_address);
|
||||
if (_bus->endTransmission() != 0) {
|
||||
LOG_WARN("PMSA003I not found on I2C at 0x12");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
|
||||
reClockI2C(currentClock, _bus);
|
||||
#endif
|
||||
|
||||
status = 1;
|
||||
LOG_INFO("PMSA003I Enabled");
|
||||
|
||||
initI2CSensor();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
if(!isActive()){
|
||||
LOG_WARN("PMSA003I is not active");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
|
||||
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus);
|
||||
#endif
|
||||
|
||||
_bus->requestFrom(_address, PMSA003I_FRAME_LENGTH);
|
||||
if (_bus->available() < PMSA003I_FRAME_LENGTH) {
|
||||
LOG_WARN("PMSA003I read failed: incomplete data (%d bytes)", _bus->available());
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
|
||||
reClockI2C(currentClock, _bus);
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < PMSA003I_FRAME_LENGTH; i++) {
|
||||
buffer[i] = _bus->read();
|
||||
}
|
||||
|
||||
if (buffer[0] != 0x42 || buffer[1] != 0x4D) {
|
||||
LOG_WARN("PMSA003I frame header invalid: 0x%02X 0x%02X", buffer[0], buffer[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read16 = [](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("PMSA003I checksum failed: computed 0x%04X, received 0x%04X", 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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PMSA003ISensor::isActive()
|
||||
{
|
||||
return state == State::ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void PMSA003ISensor::sleep()
|
||||
{
|
||||
#ifdef PMSA003I_ENABLE_PIN
|
||||
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
|
||||
state = State::IDLE;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t PMSA003ISensor::wakeUp()
|
||||
{
|
||||
#ifdef PMSA003I_ENABLE_PIN
|
||||
LOG_INFO("Waking up PMSA003I");
|
||||
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
|
||||
state = State::ACTIVE;
|
||||
return PMSA003I_WARMUP_MS;
|
||||
#endif
|
||||
// No need to wait for warmup if already active
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
35
src/modules/Telemetry/Sensor/PMSA003ISensor.h
Normal file
35
src/modules/Telemetry/Sensor/PMSA003ISensor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
|
||||
#define PMSA003I_I2C_CLOCK_SPEED 100000
|
||||
#define PMSA003I_FRAME_LENGTH 32
|
||||
#define PMSA003I_WARMUP_MS 30000
|
||||
|
||||
class PMSA003ISensor : public TelemetrySensor
|
||||
{
|
||||
public:
|
||||
PMSA003ISensor();
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
|
||||
|
||||
virtual bool isActive() override;
|
||||
virtual void sleep() override;
|
||||
virtual uint32_t wakeUp() override;
|
||||
|
||||
private:
|
||||
enum class State { IDLE, ACTIVE };
|
||||
State state = State::ACTIVE;
|
||||
|
||||
uint16_t computedChecksum = 0;
|
||||
uint16_t receivedChecksum = 0;
|
||||
|
||||
uint8_t buffer[PMSA003I_FRAME_LENGTH]{};
|
||||
TwoWire * _bus{};
|
||||
uint8_t _address{};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "NodeDB.h"
|
||||
|
||||
@@ -58,6 +58,11 @@ class TelemetrySensor
|
||||
|
||||
// TODO: delete after migration
|
||||
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
|
||||
// Functions to sleep / wakeup sensors that support it
|
||||
virtual void sleep() {};
|
||||
virtual uint32_t wakeUp() { return 0; }
|
||||
// Return active by default, override per sensor
|
||||
virtual bool isActive() { return true; }
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
// Set to true if Implementation only works first I2C port (Wire)
|
||||
@@ -65,6 +70,7 @@ class TelemetrySensor
|
||||
#endif
|
||||
virtual int32_t runOnce() { return INT32_MAX; }
|
||||
virtual bool isInitialized() { return initialized; }
|
||||
// TODO: is this used?
|
||||
virtual bool isRunning() { return status > 0; }
|
||||
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0;
|
||||
|
||||
Reference in New Issue
Block a user