mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-01 19:35:42 +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
|
||||
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
||||
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 XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
|
||||
#define PCT2075_ADDR 0x37
|
||||
#define SEN5X_ADDR 0x69
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ACCELEROMETER
|
||||
|
@ -75,6 +75,7 @@ class ScanI2C
|
||||
TCA8418KB,
|
||||
PCT2075,
|
||||
BMM150,
|
||||
SEN5X
|
||||
} DeviceType;
|
||||
|
||||
// typedef uint8_t DeviceAddress;
|
||||
|
@ -8,6 +8,11 @@
|
||||
#endif
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
#include "meshUtils.h" // vformat
|
||||
|
||||
#define SEN50_NAME 48
|
||||
#define SEN54_NAME 52
|
||||
#define SEN55_NAME 53
|
||||
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
// ICM20948 Register check
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
||||
if (registerValue == 0xEA) {
|
||||
type = ICM20948;
|
||||
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
||||
break;
|
||||
} else if (addr.address == BMX160_ADDR) {
|
||||
type = BMX160;
|
||||
logFoundDevice("BMX160", (uint8_t)addr.address);
|
||||
break;
|
||||
} else {
|
||||
type = MPU6050;
|
||||
logFoundDevice("MPU6050", (uint8_t)addr.address);
|
||||
break;
|
||||
// TODO refurbish to find the model
|
||||
// Just a hack for the hackathon
|
||||
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;
|
||||
|
||||
|
@ -696,7 +696,7 @@ void setup()
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
|
||||
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SEN5X, meshtastic_TelemetrySensorType_SEN5X);
|
||||
i2cScanner.reset();
|
||||
#endif
|
||||
|
||||
|
@ -24,6 +24,13 @@ PMSA003ISensor pmsa003iSensor;
|
||||
NullSensor pmsa003iSensor;
|
||||
#endif
|
||||
|
||||
#if __has_include(<SensirionI2CSen5x.h>)
|
||||
#include "Sensor/SEN5XSensor.h"
|
||||
SEN5XSensor sen5xSensor;
|
||||
#else
|
||||
NullSensor sen5xSensor;
|
||||
#endif
|
||||
|
||||
int32_t AirQualityTelemetryModule::runOnce()
|
||||
{
|
||||
if (sleepOnNextExecution == true) {
|
||||
@ -61,6 +68,9 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
|
||||
if (pmsa003iSensor.hasSensor())
|
||||
result = pmsa003iSensor.runOnce();
|
||||
|
||||
if (sen5xSensor.hasSensor())
|
||||
result = sen5xSensor.runOnce();
|
||||
}
|
||||
|
||||
// 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();
|
||||
#endif /* PMSA003I_ENABLE_PIN */
|
||||
|
||||
#ifdef SEN5X_ENABLE_PIN
|
||||
if (sen5xSensor.hasSensor() && !sen5xSensor.isActive())
|
||||
return sen5xSensor.wakeUp();
|
||||
#endif /* SEN5X_ENABLE_PIN */
|
||||
|
||||
if (((lastSentToMesh == 0) ||
|
||||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
|
||||
moduleConfig.telemetry.air_quality_interval,
|
||||
@ -98,6 +113,10 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
pmsa003iSensor.sleep();
|
||||
#endif /* PMSA003I_ENABLE_PIN */
|
||||
|
||||
#ifdef SEN5X_ENABLE_PIN
|
||||
sen5xSensor.sleep();
|
||||
#endif /* SEN5X_ENABLE_PIN */
|
||||
|
||||
}
|
||||
return min(sendToPhoneIntervalMs, result);
|
||||
}
|
||||
@ -236,6 +255,13 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -329,6 +355,11 @@ AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sen5xSensor.hasSensor()) {
|
||||
result = sen5xSensor.handleAdminMessage(mp, request, response);
|
||||
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
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