add CO2 sensing with SCD4X

This commit is contained in:
Riley Nielsen 2024-08-31 10:30:33 -07:00
parent 33eb073535
commit 8fa513ff1f
9 changed files with 131 additions and 18 deletions

View File

@ -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

View File

@ -139,6 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
#define NAU7802_ADDR 0x2A
#define SCD4X_ADDR 0x62
// -----------------------------------------------------------------------------
// ACCELEROMETER

View File

@ -51,7 +51,8 @@ class ScanI2C
AHT10,
BMX160,
DFROBOT_LARK,
NAU7802
NAU7802,
SCD4X
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
#endif

View File

@ -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 <SensirionI2CScd4x.h>
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

View File

@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <SensirionI2CScd4x.h>
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