Add functional SCD4X

This commit is contained in:
oscgonfer 2025-07-02 11:57:34 +02:00
parent ec5a752078
commit 7305f1cccc
3 changed files with 143 additions and 3 deletions

View File

@ -22,6 +22,14 @@
// Sensors
PMSA003ISensor pmsa003iSensor;
#if __has_include(<SensirionI2cScd4x.h>)
#include "Sensor/SCD4XSensor.h"
SCD4XSensor scd4xSensor;
#else
NullSensor scd4xSensor;
#endif
#include "graphics/ScreenFonts.h"
int32_t AirQualityTelemetryModule::runOnce()
@ -61,6 +69,9 @@ int32_t AirQualityTelemetryModule::runOnce()
if (pmsa003iSensor.hasSensor())
result = pmsa003iSensor.runOnce();
if (scd4xSensor.hasSensor())
result = scd4xSensor.runOnce();
}
// it's possible to have this module enabled, only for displaying values on the screen.
@ -143,7 +154,7 @@ void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSta
// Check if any telemetry field has valid data
bool hasAny = m.has_pm10_standard || m.has_pm25_standard || m.has_pm100_standard || m.has_pm10_environmental || m.has_pm25_environmental ||
m.has_pm100_environmental;
m.has_pm100_environmental || m.has_co2;
if (!hasAny) {
display->drawString(x, currentY, "No Telemetry");
@ -170,6 +181,9 @@ void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSta
entries.push_back("PM2.5: " + String(m.pm25_standard) + "ug/m3");
if (m.has_pm100_standard)
entries.push_back("PM10.0: " + String(m.pm100_standard) + "ug/m3");
if (m.has_co2)
entries.push_back("CO2: " + String(m.co2, 0) + "ppm");
// === Show first available metric on top-right of first line ===
if (!entries.empty()) {
@ -210,6 +224,10 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental,
t->variant.air_quality_metrics.pm100_environmental);
LOG_INFO(" | CO2=%i, CO2_T=%f, CO2_H=%f",
t->variant.air_quality_metrics.co2, t->variant.air_quality_metrics.co2_temperature,
t->variant.air_quality_metrics.co2_humidity);
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
@ -236,6 +254,11 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
hasSensor = true;
}
if (scd4xSensor.hasSensor()) {
valid = valid && scd4xSensor.getMetrics(m);
hasSensor = true;
}
return valid && hasSensor;
}
@ -273,11 +296,25 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m.time = getTime();
if (getAirQualityTelemetry(&m)) {
LOG_INFO("Send: pm10_standard=%u, pm25_standard=%u, pm100_standard=%u, \
bool hasAnyPM = m.variant.air_quality_metrics.has_pm10_standard || m.variant.air_quality_metrics.has_pm25_standard || m.variant.air_quality_metrics.has_pm100_standard || m.variant.air_quality_metrics.has_pm10_environmental || m.variant.air_quality_metrics.has_pm25_environmental ||
m.variant.air_quality_metrics.has_pm100_environmental;
if (hasAnyPM) {
LOG_INFO("Send: pm10_standard=%u, pm25_standard=%u, pm100_standard=%u, \
pm10_environmental=%u, pm25_environmental=%u, pm100_environmental=%u", \
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard, \
m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.pm10_environmental, \
m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental);
m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental);
}
bool hasAnyCO2 = m.variant.air_quality_metrics.has_co2 || m.variant.air_quality_metrics.has_co2_temperature || m.variant.air_quality_metrics.has_co2_humidity;
if (hasAnyCO2) {
LOG_INFO("Send: co2=%i, co2_t=%f, co2_rh=%f",
m.variant.air_quality_metrics.co2, m.variant.air_quality_metrics.co2_temperature,
m.variant.air_quality_metrics.co2_humidity);
}
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
@ -333,6 +370,11 @@ AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(
return result;
}
if (scd4xSensor.hasSensor()) {
result = scd4xSensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#endif
return result;

View File

@ -0,0 +1,75 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD4XSensor.h"
#include "TelemetrySensor.h"
#include <SensirionI2cScd4x.h>
#define SCD4X_NO_ERROR 0
SCD4XSensor::SCD4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD4X, "SCD4X") {}
int32_t SCD4XSensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
uint16_t error;
scd4x.begin(*nodeTelemetrySensorsMap[sensorType].second,
nodeTelemetrySensorsMap[sensorType].first);
delay(30);
// Ensure sensor is in clean state
error = scd4x.wakeUp();
if (error != SCD4X_NO_ERROR) {
LOG_INFO("Error trying to execute wakeUp()");
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// Stop periodic measurement
error = scd4x.stopPeriodicMeasurement();
if (error != SCD4X_NO_ERROR) {
LOG_INFO("Error trying to stopPeriodicMeasurement()");
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// TODO - Decide if using Periodic mesaurement or singleshot
// status = scd4x.startLowPowerPeriodicMeasurement();
if (!scd4x.startLowPowerPeriodicMeasurement()) {
status = 1;
} else {
status = 0;
}
return initI2CSensor();
}
void SCD4XSensor::setup() {}
bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
uint16_t co2, error;
float temperature;
float humidity;
error = scd4x.readMeasurement(co2, temperature, humidity);
if (error != SCD4X_NO_ERROR || co2 == 0) {
LOG_DEBUG("Skipping invalid SCD4X measurement.");
return false;
} else {
measurement->variant.air_quality_metrics.has_co2_temperature = true;
measurement->variant.air_quality_metrics.has_co2_humidity = true;
measurement->variant.air_quality_metrics.has_co2 = true;
measurement->variant.air_quality_metrics.co2_temperature = temperature;
measurement->variant.air_quality_metrics.co2_humidity = humidity;
measurement->variant.air_quality_metrics.co2 = co2;
return true;
}
}
#endif

View File

@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <SensirionI2cScd4x.h>
class SCD4XSensor : public TelemetrySensor
{
private:
SensirionI2cScd4x scd4x;
protected:
virtual void setup() override;
public:
SCD4XSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif