From 178c3114d90a9058c0d286b0e8af7d343504a02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 17 Feb 2026 22:16:56 +0100 Subject: [PATCH] Fake IAQ values on Non-BSEC2 platforms like Platformio and the original ESP32 (#9663) * BSEC2 Replacement - add approximation for IAQ to non-BSEC2 platforms. - Re-add this sensor to ESP32 targets, and refactor env_extra includes. - Fix C++ 11 compatibility * Check for gas resistance 0 --- platformio.ini | 50 ++++++------------- src/modules/Telemetry/Sensor/BME680Sensor.cpp | 26 +++++++++- src/modules/Telemetry/Sensor/BME680Sensor.h | 2 +- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1d18b4573..ccc5c9755 100644 --- a/platformio.ini +++ b/platformio.ini @@ -184,8 +184,8 @@ lib_deps = # renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE wollewald/BH1750_WE@1.1.10 -; (not included in native / portduino) -[environmental_extra] +; Common environmental sensor libraries (not included in native / portduino) +[environmental_extra_common] lib_deps = # renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library adafruit/Adafruit BMP3XX Library@2.1.6 @@ -205,10 +205,6 @@ lib_deps = sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6 # renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001 closedcube/ClosedCube OPT3001@1.1.2 - # renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2 - boschsensortec/bsec2@1.10.2610 - # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library - boschsensortec/BME68x Sensor Library@1.3.40408 # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip # renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core @@ -220,34 +216,18 @@ lib_deps = # renovate: datasource=custom.pio depName=Sensirion I2C SCD30 packageName=sensirion/library/Sensirion I2C SCD30 sensirion/Sensirion I2C SCD30@1.0.0 -; Same as environmental_extra but without BSEC (saves ~3.5KB DRAM for original ESP32 targets) +; Environmental sensors with BSEC2 (Bosch proprietary IAQ) +[environmental_extra] +lib_deps = + ${environmental_extra_common.lib_deps} + # renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2 + boschsensortec/bsec2@1.10.2610 + # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library + boschsensortec/BME68x Sensor Library@1.3.40408 + +; Environmental sensors without BSEC (saves ~3.5KB DRAM for original ESP32 targets) [environmental_extra_no_bsec] lib_deps = - # renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library - adafruit/Adafruit BMP3XX Library@2.1.6 - # renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X - adafruit/Adafruit MAX1704X@1.0.3 - # renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library - adafruit/Adafruit SHTC3 Library@1.0.2 - # renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X - adafruit/Adafruit LPS2X@2.0.6 - # renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library - adafruit/Adafruit SHT31 Library@2.2.2 - # renovate: datasource=custom.pio depName=Adafruit VEML7700 packageName=adafruit/library/Adafruit VEML7700 Library - adafruit/Adafruit VEML7700 Library@2.1.6 - # renovate: datasource=custom.pio depName=Adafruit SHT4x packageName=adafruit/library/Adafruit SHT4x Library - adafruit/Adafruit SHT4x Library@1.0.5 - # renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library - sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6 - # renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001 - closedcube/ClosedCube OPT3001@1.1.2 - # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master - https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip - # renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core - sensirion/Sensirion Core@0.7.3 - # 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 SFA3x packageName=sensirion/library/Sensirion I2C SFA3x - sensirion/Sensirion I2C SFA3x@1.0.0 - # renovate: datasource=custom.pio depName=Sensirion I2C SCD30 packageName=sensirion/library/Sensirion I2C SCD30 - sensirion/Sensirion I2C SCD30@1.0.0 \ No newline at end of file + ${environmental_extra_common.lib_deps} + # renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680 + adafruit/Adafruit BME680 Library@^2.0.5 diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp index 3a1eb9532..c202028e1 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp @@ -8,6 +8,10 @@ #include "SPILock.h" #include "TelemetrySensor.h" +#if __has_include() +#include +#endif + BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {} #if __has_include() @@ -96,8 +100,28 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.temperature = bme680->readTemperature(); measurement->variant.environment_metrics.relative_humidity = bme680->readHumidity(); measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F; - measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0; + float gasRaw = bme680->readGas(); + measurement->variant.environment_metrics.gas_resistance = gasRaw / 1000.0; + + // IAQ approximation: humidity-compensated logarithmic mapping of gas resistance + // Gas sensor resistance drops with humidity; compensate to a 40% RH reference baseline + // Map compensated gas resistance (Ohms) to IAQ 0-500 using log-linear interpolation + // Clean air reference ~400 kOhm, polluted reference ~5 kOhm + if (gasRaw > 0.0f && !isfinite(gasRaw)) { + + static constexpr float LOG_UPPER = 12.899219f; // log(400k) + static constexpr float LOG_RANGE_INV = 1.0f / (12.899219f - 8.517193f); // 1 / (log(400k) - log(5k)) + measurement->variant.environment_metrics.has_iaq = true; + measurement->variant.environment_metrics.iaq = (uint16_t)(fminf( + fmaxf(((LOG_UPPER - + logf(fmaxf(gasRaw * expf(0.035f * (measurement->variant.environment_metrics.relative_humidity - 40.0f)), + 1.0f))) * + LOG_RANGE_INV) * + 500.0f, + 0.0f), + 500.0f)); + } #endif return true; } diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.h b/src/modules/Telemetry/Sensor/BME680Sensor.h index eaeceb848..1134f04d9 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.h +++ b/src/modules/Telemetry/Sensor/BME680Sensor.h @@ -28,7 +28,7 @@ class BME680Sensor : public TelemetrySensor #else using BME680Ptr = std::unique_ptr; - static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique(bus); } + static BME680Ptr makeBME680(TwoWire *bus) { return BME680Ptr(new Adafruit_BME680(bus)); } BME680Ptr bme680; #endif