Skip to content

Commit 6237b2e

Browse files
committed
Add functional SCD4X
1 parent c545a9f commit 6237b2e

File tree

3 files changed

+143
-3
lines changed

3 files changed

+143
-3
lines changed

src/modules/Telemetry/AirQualityTelemetry.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ PMSA003ISensor pmsa003iSensor;
2424
NullSensor pmsa003iSensor;
2525
#endif
2626

27+
#if __has_include(<SensirionI2cScd4x.h>)
28+
#include "Sensor/SCD4XSensor.h"
29+
SCD4XSensor scd4xSensor;
30+
#else
31+
NullSensor scd4xSensor;
32+
#endif
33+
34+
2735
int32_t AirQualityTelemetryModule::runOnce()
2836
{
2937
if (sleepOnNextExecution == true) {
@@ -61,6 +69,9 @@ int32_t AirQualityTelemetryModule::runOnce()
6169

6270
if (pmsa003iSensor.hasSensor())
6371
result = pmsa003iSensor.runOnce();
72+
73+
if (scd4xSensor.hasSensor())
74+
result = scd4xSensor.runOnce();
6475
}
6576

6677
// 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
143154

144155
// Check if any telemetry field has valid data
145156
bool hasAny = m.has_pm10_standard || m.has_pm25_standard || m.has_pm100_standard || m.has_pm10_environmental || m.has_pm25_environmental ||
146-
m.has_pm100_environmental;
157+
m.has_pm100_environmental || m.has_co2;
147158

148159
if (!hasAny) {
149160
display->drawString(x, currentY, "No Telemetry");
@@ -170,6 +181,9 @@ void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSta
170181
entries.push_back("PM2.5: " + String(m.pm25_standard, 0) + "ug/m3");
171182
if (m.has_pm100_standard)
172183
entries.push_back("PM10.0: " + String(m.pm100_standard, 0) + "ug/m3");
184+
if (m.has_co2)
185+
entries.push_back("CO2: " + String(m.co2, 0) + "ppm");
186+
173187

174188
// === Show first available metric on top-right of first line ===
175189
if (!entries.empty()) {
@@ -210,6 +224,10 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
210224
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
211225
t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental,
212226
t->variant.air_quality_metrics.pm100_environmental);
227+
228+
LOG_INFO(" | CO2=%i, CO2_T=%f, CO2_H=%f",
229+
t->variant.air_quality_metrics.co2, t->variant.air_quality_metrics.co2_temperature,
230+
t->variant.air_quality_metrics.co2_humidity);
213231
#endif
214232
// release previous packet before occupying a new spot
215233
if (lastMeasurementPacket != nullptr)
@@ -236,6 +254,11 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
236254
hasSensor = true;
237255
}
238256

257+
if (scd4xSensor.hasSensor()) {
258+
valid = valid && scd4xSensor.getMetrics(m);
259+
hasSensor = true;
260+
}
261+
239262
return valid && hasSensor;
240263
}
241264

@@ -273,10 +296,24 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
273296
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
274297
m.time = getTime();
275298
if (getAirQualityTelemetry(&m)) {
276-
LOG_INFO("Send: pm10_standard=%f, pm25_standard=%f, pm100_standard=%f, pm10_environmental=%f, pm100_environmental=%f",
299+
300+
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 ||
301+
m.variant.air_quality_metrics.has_pm100_environmental;
302+
303+
if (hasAnyPM) {
304+
LOG_INFO("Send: pm10_standard=%f, pm25_standard=%f, pm100_standard=%f, pm10_environmental=%f, pm100_environmental=%f",
277305
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
278306
m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.pm10_environmental,
279-
m.variant.air_quality_metrics.pm100_environmental);
307+
m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental);
308+
}
309+
310+
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;
311+
312+
if (hasAnyCO2) {
313+
LOG_INFO("Send: co2=%i, co2_t=%f, co2_rh=%f",
314+
m.variant.air_quality_metrics.co2, m.variant.air_quality_metrics.co2_temperature,
315+
m.variant.air_quality_metrics.co2_humidity);
316+
}
280317

281318
meshtastic_MeshPacket *p = allocDataProtobuf(m);
282319
p->to = dest;
@@ -329,6 +366,11 @@ AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(
329366
return result;
330367
}
331368

369+
if (scd4xSensor.hasSensor()) {
370+
result = scd4xSensor.handleAdminMessage(mp, request, response);
371+
if (result != AdminMessageHandleResult::NOT_HANDLED)
372+
return result;
373+
}
332374

333375
#endif
334376
return result;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "configuration.h"
2+
3+
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2cScd4x.h>)
4+
5+
#include "../mesh/generated/meshtastic/telemetry.pb.h"
6+
#include "SCD4XSensor.h"
7+
#include "TelemetrySensor.h"
8+
#include <SensirionI2cScd4x.h>
9+
10+
#define SCD4X_NO_ERROR 0
11+
12+
SCD4XSensor::SCD4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD4X, "SCD4X") {}
13+
14+
int32_t SCD4XSensor::runOnce()
15+
{
16+
LOG_INFO("Init sensor: %s", sensorName);
17+
if (!hasSensor()) {
18+
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
19+
}
20+
21+
uint16_t error;
22+
23+
scd4x.begin(*nodeTelemetrySensorsMap[sensorType].second,
24+
nodeTelemetrySensorsMap[sensorType].first);
25+
26+
delay(30);
27+
// Ensure sensor is in clean state
28+
error = scd4x.wakeUp();
29+
if (error != SCD4X_NO_ERROR) {
30+
LOG_INFO("Error trying to execute wakeUp()");
31+
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
32+
}
33+
34+
// Stop periodic measurement
35+
error = scd4x.stopPeriodicMeasurement();
36+
if (error != SCD4X_NO_ERROR) {
37+
LOG_INFO("Error trying to stopPeriodicMeasurement()");
38+
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
39+
}
40+
41+
// TODO - Decide if using Periodic mesaurement or singleshot
42+
// status = scd4x.startLowPowerPeriodicMeasurement();
43+
44+
if (!scd4x.startLowPowerPeriodicMeasurement()) {
45+
status = 1;
46+
} else {
47+
status = 0;
48+
}
49+
return initI2CSensor();
50+
}
51+
52+
void SCD4XSensor::setup() {}
53+
54+
bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
55+
{
56+
uint16_t co2, error;
57+
float temperature;
58+
float humidity;
59+
60+
error = scd4x.readMeasurement(co2, temperature, humidity);
61+
if (error != SCD4X_NO_ERROR || co2 == 0) {
62+
LOG_DEBUG("Skipping invalid SCD4X measurement.");
63+
return false;
64+
} else {
65+
measurement->variant.air_quality_metrics.has_co2_temperature = true;
66+
measurement->variant.air_quality_metrics.has_co2_humidity = true;
67+
measurement->variant.air_quality_metrics.has_co2 = true;
68+
measurement->variant.air_quality_metrics.co2_temperature = temperature;
69+
measurement->variant.air_quality_metrics.co2_humidity = humidity;
70+
measurement->variant.air_quality_metrics.co2 = co2;
71+
return true;
72+
}
73+
}
74+
75+
#endif
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "configuration.h"
2+
3+
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<SensirionI2cScd4x.h>)
4+
5+
#include "../mesh/generated/meshtastic/telemetry.pb.h"
6+
#include "TelemetrySensor.h"
7+
#include <SensirionI2cScd4x.h>
8+
9+
class SCD4XSensor : public TelemetrySensor
10+
{
11+
private:
12+
SensirionI2cScd4x scd4x;
13+
14+
protected:
15+
virtual void setup() override;
16+
17+
public:
18+
SCD4XSensor();
19+
virtual int32_t runOnce() override;
20+
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
21+
};
22+
23+
#endif

0 commit comments

Comments
 (0)