mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-03 12:20:43 +00:00
SEN5X first pass
This commit is contained in:
parent
9604dadc45
commit
94df0cb636
@ -200,3 +200,5 @@ lib_deps =
|
|||||||
sensirion/Sensirion Core@0.7.1
|
sensirion/Sensirion Core@0.7.1
|
||||||
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
||||||
sensirion/Sensirion I2C SCD4x@1.1.0
|
sensirion/Sensirion I2C SCD4x@1.1.0
|
||||||
|
# renovate: datasource=custom.pio depName=Sensirion I2C SEN5X packageName=sensirion/library/Sensirion I2C SEN5X
|
||||||
|
sensirion/Sensirion I2C SEN5X
|
@ -195,6 +195,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define LTR390UV_ADDR 0x53
|
#define LTR390UV_ADDR 0x53
|
||||||
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
|
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
|
||||||
#define PCT2075_ADDR 0x37
|
#define PCT2075_ADDR 0x37
|
||||||
|
#define SEN5X_ADDR 0x69
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ACCELEROMETER
|
// ACCELEROMETER
|
||||||
|
@ -75,6 +75,7 @@ class ScanI2C
|
|||||||
TCA8418KB,
|
TCA8418KB,
|
||||||
PCT2075,
|
PCT2075,
|
||||||
BMM150,
|
BMM150,
|
||||||
|
SEN5X
|
||||||
} DeviceType;
|
} DeviceType;
|
||||||
|
|
||||||
// typedef uint8_t DeviceAddress;
|
// typedef uint8_t DeviceAddress;
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
#endif
|
#endif
|
||||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||||
#include "meshUtils.h" // vformat
|
#include "meshUtils.h" // vformat
|
||||||
|
|
||||||
|
#define SEN50_NAME 48
|
||||||
|
#define SEN54_NAME 52
|
||||||
|
#define SEN55_NAME 53
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool in_array(uint8_t *array, int size, uint8_t lookfor)
|
bool in_array(uint8_t *array, int size, uint8_t lookfor)
|
||||||
@ -464,21 +469,53 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ICM20948_ADDR: // same as BMX160_ADDR
|
case ICM20948_ADDR: // same as BMX160_ADDR and SEN5X_ADDR
|
||||||
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
|
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
|
||||||
|
// ICM20948 Register check
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
||||||
if (registerValue == 0xEA) {
|
if (registerValue == 0xEA) {
|
||||||
type = ICM20948;
|
type = ICM20948;
|
||||||
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
||||||
break;
|
break;
|
||||||
} else if (addr.address == BMX160_ADDR) {
|
|
||||||
type = BMX160;
|
|
||||||
logFoundDevice("BMX160", (uint8_t)addr.address);
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
type = MPU6050;
|
// TODO refurbish to find the model
|
||||||
logFoundDevice("MPU6050", (uint8_t)addr.address);
|
// Just a hack for the hackathon
|
||||||
break;
|
if (addr.address == SEN5X_ADDR) {
|
||||||
|
type = SEN5X;
|
||||||
|
logFoundDevice("SEN5X", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can get the 0xD014 register to find the model. This is not a simple task
|
||||||
|
// There is a buffer returned - getRegisterValue is not enough (maybe)
|
||||||
|
// registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xD014), 6);
|
||||||
|
// Important to leave delay
|
||||||
|
// delay(50);
|
||||||
|
|
||||||
|
// const uint8_t nameSize = 48;
|
||||||
|
// uint8_t name[nameSize] = ®isterValue;
|
||||||
|
|
||||||
|
// switch(name[4]){
|
||||||
|
// case SEN50_NAME:
|
||||||
|
// type = SEN50;
|
||||||
|
// break;
|
||||||
|
// case SEN54_NAME:
|
||||||
|
// type = SEN54;
|
||||||
|
// break;
|
||||||
|
// case SEN55_NAME:
|
||||||
|
// type = SEN55;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (addr.address == BMX160_ADDR) {
|
||||||
|
type = BMX160;
|
||||||
|
logFoundDevice("BMX160", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
type = MPU6050;
|
||||||
|
logFoundDevice("MPU6050", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ void setup()
|
|||||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
|
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
|
||||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
|
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
|
||||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
|
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
|
||||||
|
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SEN5X, meshtastic_TelemetrySensorType_SEN5X);
|
||||||
i2cScanner.reset();
|
i2cScanner.reset();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -24,6 +24,13 @@ PMSA003ISensor pmsa003iSensor;
|
|||||||
NullSensor pmsa003iSensor;
|
NullSensor pmsa003iSensor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<SensirionI2CSen5x.h>)
|
||||||
|
#include "Sensor/SEN5XSensor.h"
|
||||||
|
SEN5XSensor sen5xSensor;
|
||||||
|
#else
|
||||||
|
NullSensor sen5xSensor;
|
||||||
|
#endif
|
||||||
|
|
||||||
int32_t AirQualityTelemetryModule::runOnce()
|
int32_t AirQualityTelemetryModule::runOnce()
|
||||||
{
|
{
|
||||||
if (sleepOnNextExecution == true) {
|
if (sleepOnNextExecution == true) {
|
||||||
@ -61,6 +68,9 @@ int32_t AirQualityTelemetryModule::runOnce()
|
|||||||
|
|
||||||
if (pmsa003iSensor.hasSensor())
|
if (pmsa003iSensor.hasSensor())
|
||||||
result = pmsa003iSensor.runOnce();
|
result = pmsa003iSensor.runOnce();
|
||||||
|
|
||||||
|
if (sen5xSensor.hasSensor())
|
||||||
|
result = sen5xSensor.runOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's possible to have this module enabled, only for displaying values on the screen.
|
// it's possible to have this module enabled, only for displaying values on the screen.
|
||||||
@ -78,6 +88,11 @@ int32_t AirQualityTelemetryModule::runOnce()
|
|||||||
return pmsa003iSensor.wakeUp();
|
return pmsa003iSensor.wakeUp();
|
||||||
#endif /* PMSA003I_ENABLE_PIN */
|
#endif /* PMSA003I_ENABLE_PIN */
|
||||||
|
|
||||||
|
#ifdef SEN5X_ENABLE_PIN
|
||||||
|
if (sen5xSensor.hasSensor() && !sen5xSensor.isActive())
|
||||||
|
return sen5xSensor.wakeUp();
|
||||||
|
#endif /* SEN5X_ENABLE_PIN */
|
||||||
|
|
||||||
if (((lastSentToMesh == 0) ||
|
if (((lastSentToMesh == 0) ||
|
||||||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
|
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
|
||||||
moduleConfig.telemetry.air_quality_interval,
|
moduleConfig.telemetry.air_quality_interval,
|
||||||
@ -98,6 +113,10 @@ int32_t AirQualityTelemetryModule::runOnce()
|
|||||||
pmsa003iSensor.sleep();
|
pmsa003iSensor.sleep();
|
||||||
#endif /* PMSA003I_ENABLE_PIN */
|
#endif /* PMSA003I_ENABLE_PIN */
|
||||||
|
|
||||||
|
#ifdef SEN5X_ENABLE_PIN
|
||||||
|
sen5xSensor.sleep();
|
||||||
|
#endif /* SEN5X_ENABLE_PIN */
|
||||||
|
|
||||||
}
|
}
|
||||||
return min(sendToPhoneIntervalMs, result);
|
return min(sendToPhoneIntervalMs, result);
|
||||||
}
|
}
|
||||||
@ -236,6 +255,13 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
|||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sen5xSensor.hasSensor()) {
|
||||||
|
// TODO - Should we check for sensor state here?
|
||||||
|
// If a sensor is sleeping, we should know and check to wake it up
|
||||||
|
valid = valid && sen5xSensor.getMetrics(m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
|
|
||||||
return valid && hasSensor;
|
return valid && hasSensor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +355,11 @@ AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sen5xSensor.hasSensor()) {
|
||||||
|
result = sen5xSensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
|
107
src/modules/Telemetry/Sensor/SEN5XSensor.cpp
Normal file
107
src/modules/Telemetry/Sensor/SEN5XSensor.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2CSen5x.h>)
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "SEN5XSensor.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <SensirionI2CSen5x.h>
|
||||||
|
|
||||||
|
SEN5XSensor::SEN5XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SEN5X, "SEN5X") {}
|
||||||
|
|
||||||
|
int32_t SEN5XSensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sen5x.begin(*nodeTelemetrySensorsMap[sensorType].second);
|
||||||
|
|
||||||
|
delay(25); // without this there is an error on the deviceReset function (NOT WORKING)
|
||||||
|
|
||||||
|
uint16_t error;
|
||||||
|
char errorMessage[256];
|
||||||
|
error = sen5x.deviceReset();
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("Error trying to execute deviceReset(): ");
|
||||||
|
errorToString(error, errorMessage, 256);
|
||||||
|
LOG_INFO(errorMessage);
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = sen5x.startMeasurement();
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("Error trying to execute startMeasurement(): ");
|
||||||
|
errorToString(error, errorMessage, 256);
|
||||||
|
LOG_INFO(errorMessage);
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
} else {
|
||||||
|
status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEN5XSensor::setup()
|
||||||
|
{
|
||||||
|
#ifdef SEN5X_ENABLE_PIN
|
||||||
|
pinMode(SEN5X_ENABLE_PIN, OUTPUT);
|
||||||
|
#endif /* SEN5X_ENABLE_PIN */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SEN5X_ENABLE_PIN
|
||||||
|
void SEN5XSensor::sleep() {
|
||||||
|
digitalWrite(SEN5X_ENABLE_PIN, LOW);
|
||||||
|
state = State::IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SEN5XSensor::wakeUp() {
|
||||||
|
digitalWrite(SEN5X_ENABLE_PIN, HIGH);
|
||||||
|
state = State::ACTIVE;
|
||||||
|
return SEN5X_WARMUP_MS;
|
||||||
|
}
|
||||||
|
#endif /* SEN5X_ENABLE_PIN */
|
||||||
|
|
||||||
|
bool SEN5XSensor::isActive() {
|
||||||
|
return state == State::ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
uint16_t error;
|
||||||
|
char errorMessage[256];
|
||||||
|
|
||||||
|
// Read Measurement
|
||||||
|
float massConcentrationPm1p0;
|
||||||
|
float massConcentrationPm2p5;
|
||||||
|
float massConcentrationPm4p0;
|
||||||
|
float massConcentrationPm10p0;
|
||||||
|
float ambientHumidity;
|
||||||
|
float ambientTemperature;
|
||||||
|
float vocIndex;
|
||||||
|
float noxIndex;
|
||||||
|
|
||||||
|
error = sen5x.readMeasuredValues(
|
||||||
|
massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
|
||||||
|
massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
|
||||||
|
noxIndex);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("Error trying to execute readMeasuredValues(): ");
|
||||||
|
errorToString(error, errorMessage, 256);
|
||||||
|
LOG_INFO(errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
measurement->variant.air_quality_metrics.has_pm10_standard = true;
|
||||||
|
measurement->variant.air_quality_metrics.pm10_standard = massConcentrationPm1p0;
|
||||||
|
measurement->variant.air_quality_metrics.has_pm25_standard = true;
|
||||||
|
measurement->variant.air_quality_metrics.pm25_standard = massConcentrationPm2p5;
|
||||||
|
measurement->variant.air_quality_metrics.has_pm100_standard = true;
|
||||||
|
measurement->variant.air_quality_metrics.pm100_standard = massConcentrationPm10p0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
src/modules/Telemetry/Sensor/SEN5XSensor.h
Normal file
43
src/modules/Telemetry/Sensor/SEN5XSensor.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2CSen5x.h>)
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <SensirionI2CSen5x.h>
|
||||||
|
|
||||||
|
#ifndef SEN5X_WARMUP_MS
|
||||||
|
// from the SEN5X datasheet
|
||||||
|
#define SEN5X_WARMUP_MS_SMALL 30000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SEN5XSensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SensirionI2CSen5x sen5x;
|
||||||
|
// PM25_AQI_Data pmsa003iData = {0};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
IDLE = 0,
|
||||||
|
ACTIVE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef SEN5X_ENABLE_PIN
|
||||||
|
void sleep();
|
||||||
|
uint32_t wakeUp();
|
||||||
|
State state = State::IDLE;
|
||||||
|
#else
|
||||||
|
State state = State::ACTIVE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SEN5XSensor();
|
||||||
|
bool isActive();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user