diff --git a/platformio.ini b/platformio.ini
index b1f89e5b4..fe9b18ccc 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -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
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
index cddc7ba7a..97138d475 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -195,6 +195,7 @@ along with this program. If not, see .
#define LTR390UV_ADDR 0x53
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
#define PCT2075_ADDR 0x37
+#define SEN5X_ADDR 0x69
// -----------------------------------------------------------------------------
// ACCELEROMETER
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index dd290db98..e25d7ce55 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -75,6 +75,7 @@ class ScanI2C
TCA8418KB,
PCT2075,
BMM150,
+ SEN5X
} DeviceType;
// typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 9e9441123..87079cb14 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -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;
diff --git a/src/main.cpp b/src/main.cpp
index 640f0b1fe..0d23e6cd8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index 7689802ea..9190850d0 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -24,6 +24,13 @@ PMSA003ISensor pmsa003iSensor;
NullSensor pmsa003iSensor;
#endif
+#if __has_include()
+#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;
diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.cpp b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp
new file mode 100644
index 000000000..b65b3e76d
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SEN5XSensor.cpp
@@ -0,0 +1,107 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include()
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "SEN5XSensor.h"
+#include "TelemetrySensor.h"
+#include
+
+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
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/SEN5XSensor.h b/src/modules/Telemetry/Sensor/SEN5XSensor.h
new file mode 100644
index 000000000..f2b8321ee
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SEN5XSensor.h
@@ -0,0 +1,43 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include()
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+#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
\ No newline at end of file