diff --git a/platformio.ini b/platformio.ini
index 5c3c4e421..7935eb457 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -149,6 +149,7 @@ lib_deps =
ClosedCube OPT3001@^1.1.2
emotibit/EmotiBit MLX90632@^1.0.8
dfrobot/DFRobot_RTU@^1.0.3
+ sensirion/Sensirion I2C SCD4x@^0.4.0
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
diff --git a/src/configuration.h b/src/configuration.h
index 2e0efffd4..79f24453d 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -139,6 +139,7 @@ along with this program. If not, see .
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
#define NAU7802_ADDR 0x2A
+#define SCD4X_ADDR 0x62
// -----------------------------------------------------------------------------
// ACCELEROMETER
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 711e8bee5..de5d2dc34 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -51,7 +51,8 @@ class ScanI2C
AHT10,
BMX160,
DFROBOT_LARK,
- NAU7802
+ NAU7802,
+ SCD4X
} DeviceType;
// typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 1183d0ddc..85b0001b1 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -367,6 +367,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n");
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n");
+ case SCD4X_ADDR:
+ type = SCD4X;
+ LOG_INFO("SCD4X CO2 sensor found\n");
+ break;
+
default:
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
}
@@ -404,4 +409,4 @@ size_t ScanI2CTwoWire::countDevices() const
{
return foundDevices.size();
}
-#endif
+#endif
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index b73d9803b..9a5e24b43 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -583,6 +583,7 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X)
i2cScanner.reset();
#endif
@@ -1155,4 +1156,4 @@ void loop()
}
// if (didWake) LOG_DEBUG("wake!\n");
}
-#endif
+#endif
\ No newline at end of file
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index cedc2867e..bfe4f3fcf 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -71,7 +71,9 @@ typedef enum _meshtastic_TelemetrySensorType {
/* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */
meshtastic_TelemetrySensorType_MAX17048 = 28,
/* Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor */
- meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29
+ meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29,
+ /* SCD40/SCD41 CO2 sensor */
+ meshtastic_TelemetrySensorType_SCD4X = 30
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -114,8 +116,8 @@ typedef struct _meshtastic_EnvironmentMetrics {
/* Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */
bool has_current;
float current;
- /* relative scale IAQ value as measured by Bosch BME680 . value 0-500.
- Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */
+ /* relative scale IAQ value as measured by Bosch BME680. value 0-500.
+ Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */
bool has_iaq;
uint16_t iaq;
/* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */
@@ -134,7 +136,7 @@ typedef struct _meshtastic_EnvironmentMetrics {
bool has_uv_lux;
float uv_lux;
/* Wind direction in degrees
- 0 degrees = North, 90 = East, etc... */
+ 0 degrees = North, 90 = East, etc... */
bool has_wind_direction;
uint16_t wind_direction;
/* Wind speed in m/s */
@@ -149,6 +151,9 @@ typedef struct _meshtastic_EnvironmentMetrics {
/* Wind lull in m/s */
bool has_wind_lull;
float wind_lull;
+ /* CO2 measured in ppm */
+ bool has_co2;
+ uint16_t co2;
} meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */
@@ -267,8 +272,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
-#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_CUSTOM_SENSOR
-#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_CUSTOM_SENSOR+1))
+#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SCD4X
+#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SCD4X+1))
@@ -280,14 +285,14 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
+#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
+#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
@@ -317,6 +322,7 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_weight_tag 15
#define meshtastic_EnvironmentMetrics_wind_gust_tag 16
#define meshtastic_EnvironmentMetrics_wind_lull_tag 17
+#define meshtastic_EnvironmentMetrics_co2_tag 18
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
#define meshtastic_PowerMetrics_ch1_current_tag 2
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
@@ -379,7 +385,8 @@ X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \
X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \
X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \
X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \
-X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17)
+X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) \
+X(a, STATIC, OPTIONAL, UINT32, co2, 18)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
@@ -473,4 +480,4 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
} /* extern "C" */
#endif
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 4755a5be5..1a6233370 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -30,6 +30,7 @@
#include "Sensor/NAU7802Sensor.h"
#include "Sensor/OPT3001Sensor.h"
#include "Sensor/RCWL9620Sensor.h"
+#include "Sensor/SCD4XSensor.h"
#include "Sensor/SHT31Sensor.h"
#include "Sensor/SHT4XSensor.h"
#include "Sensor/SHTC3Sensor.h"
@@ -54,6 +55,7 @@ AHT10Sensor aht10Sensor;
MLX90632Sensor mlx90632Sensor;
DFRobotLarkSensor dfRobotLarkSensor;
NAU7802Sensor nau7802Sensor;
+SCD4XSensor scd4xSensor;
#ifdef T1000X_SENSOR_EN
T1000xSensor t1000xSensor;
#endif
@@ -139,6 +141,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = mlx90632Sensor.runOnce();
if (nau7802Sensor.hasSensor())
result = nau7802Sensor.runOnce();
+ if (scd4xSensor.hasSensor())
+ result = scd4xSensor.runOnce();
#endif
}
return result;
@@ -252,6 +256,10 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
if (lastMeasurement.variant.environment_metrics.weight != 0)
display->drawString(x, y += _fontHeight(FONT_SMALL),
"Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
+
+ if (lastMeasurement.variant.environment_metrics.co2 != 0)
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
+ "CO2: " + String(lastMeasurement.variant.environment_metrics.co2) + " ppm");
}
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
@@ -268,9 +276,9 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
- LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", sender,
+ LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg, co2=%d ppm\n", sender,
t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction,
- t->variant.environment_metrics.weight);
+ t->variant.environment_metrics.weight, t->variant.environment_metrics.co2);
#endif
// release previous packet before occupying a new spot
@@ -383,6 +391,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
}
}
+ if (scd4xSensor.hasSensor()) {
+ valid = valid && scd4xSensor.getMetrics(m);
+ hasSensor = true;
+ }
#endif
return valid && hasSensor;
@@ -433,8 +445,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
- LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", m.variant.environment_metrics.wind_speed,
- m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight);
+ LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg, co2=%d ppm\n", m.variant.environment_metrics.wind_speed,
+ m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight, m.variant.environment_metrics.co2);
sensor_read_error_count = 0;
@@ -568,7 +580,12 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
+ if (scd4xSensor.hasSensor()) {
+ result = scd4xSensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
return result;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/Telemetry/Sensor/SCD4XSensor.cpp b/src/modules/Telemetry/Sensor/SCD4XSensor.cpp
new file mode 100644
index 000000000..a958de3fa
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SCD4XSensor.cpp
@@ -0,0 +1,57 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "SCD4XSensor.h"
+#include "TelemetrySensor.h"
+#include
+
+SCD4XSensor::SCD4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD4X, "SCD4X") {}
+
+int32_t SCD4XSensor::runOnce()
+{
+ LOG_INFO("Init sensor: %s\n", sensorName);
+ if (!hasSensor()) {
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+ }
+ // scd4x = SensirionI2CScd4x(nodeTelemetrySensorsMap[sensorType].second);
+ // status = scd4x.begin(nodeTelemetrySensorsMap[sensorType].first);
+ scd4x.begin(*nodeTelemetrySensorsMap[sensorType].second);
+ scd4x.stopPeriodicMeasurement();
+ status = scd4x.startLowPowerPeriodicMeasurement();
+ if (status == 0) {
+ status = 1;
+ } else {
+ status = 0;
+ }
+ return initI2CSensor();
+}
+
+void SCD4XSensor::setup()
+{
+
+}
+
+bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ uint16_t co2, error;
+ float temperature, humidity;
+ error = scd4x.readMeasurement(
+ co2, temperature, humidity
+ );
+ if (error || co2 == 0) {
+ LOG_DEBUG("Skipping invalid SCD4X measurement.\n");
+ return false;
+ } else {
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+ measurement->variant.environment_metrics.has_co2 = true;
+ measurement->variant.environment_metrics.temperature = temperature;
+ measurement->variant.environment_metrics.relative_humidity = humidity;
+ measurement->variant.environment_metrics.co2 = co2;
+ return true;
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/SCD4XSensor.h b/src/modules/Telemetry/Sensor/SCD4XSensor.h
new file mode 100644
index 000000000..0be607e1b
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/SCD4XSensor.h
@@ -0,0 +1,23 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+class SCD4XSensor : public TelemetrySensor
+{
+ private:
+ SensirionI2CScd4x scd4x = SensirionI2CScd4x();
+
+ protected:
+ virtual void setup() override;
+
+ public:
+ SCD4XSensor();
+ virtual int32_t runOnce() override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+};
+
+#endif