浏览代码

Merge pull request #2634 from NickDunklee/bme680-fixes-consolidation

feat: Integrate BME680 Bosch BSEC support for RAK4631
ripplebiz 2 月之前
父节点
当前提交
bb6cf8396d

+ 114 - 5
src/helpers/sensors/EnvironmentSensorManager.cpp

@@ -12,6 +12,31 @@
 // Sensor library includes and static driver instances
 // ============================================================
 
+#if ENV_INCLUDE_BME680_BSEC
+#ifndef TELEM_BME680_ADDRESS
+#define TELEM_BME680_ADDRESS 0x76
+#endif
+#define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25)
+#include <bsec.h>
+#include <Adafruit_LittleFS.h>
+#include <InternalFileSystem.h>
+static const uint8_t bsec_config_iaq[] = {
+#include "config/generic_33v_3s_28d/bsec_iaq.txt" // 3.3v, LP, 28 day background calibration window
+};
+static Bsec     bsec_iaq;
+static float    bsec_temperature     = 0;
+static float    bsec_humidity        = 0;
+static float    bsec_pressure_hpa    = 0;
+static float    bsec_iaq_val         = 0;
+static uint8_t  bsec_accuracy        = 0;
+static bool     bsec_active          = false;
+static bool     bsec_data_ready      = false;
+static bool     bsec_first_save_done = false;
+static uint32_t bsec_last_save_ms    = 0;
+#define BSEC_STATE_FILE "/bsec_state.bin"
+#define BSEC_SAVE_INTERVAL_MS (8UL * 60 * 60 * 1000) // 8 hour state-save interval
+#endif
+
 #ifdef ENV_INCLUDE_BME680
 #ifndef TELEM_BME680_ADDRESS
 #define TELEM_BME680_ADDRESS 0x76
@@ -235,9 +260,10 @@ static void query_bme680(uint8_t ch, uint8_t, CayenneLPP& lpp) {
   if (BME680.performReading()) {
     lpp.addTemperature(ch, BME680.temperature);
     lpp.addRelativeHumidity(ch, BME680.humidity);
-    lpp.addBarometricPressure(ch, BME680.pressure / 100);
-    lpp.addAltitude(ch, 44330.0 * (1.0 - pow((BME680.pressure / 100) / TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903)));
-    lpp.addAnalogInput(ch, BME680.gas_resistance);
+    const float pressure_hpa = BME680.pressure / 100.0f;
+    lpp.addBarometricPressure(ch, pressure_hpa);
+    lpp.addAltitude(ch, 44330.0f * (1.0f - powf(pressure_hpa / (float)TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903f)));
+    lpp.addGenericSensor(ch, BME680.gas_resistance);
   }
 }
 #endif
@@ -449,6 +475,62 @@ static void query_rak12035(uint8_t ch, uint8_t sub_ch, CayenneLPP& lpp) {
 }
 #endif
 
+#if ENV_INCLUDE_BME680_BSEC
+static void bsec_load_state() {
+  using namespace Adafruit_LittleFS_Namespace;
+  File f = InternalFS.open(BSEC_STATE_FILE, FILE_O_READ);
+  if (!f) return;
+  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
+  f.read(state, BSEC_MAX_STATE_BLOB_SIZE);
+  f.close();
+  bsec_iaq.setState(state);
+}
+
+static void bsec_save_state() {
+  using namespace Adafruit_LittleFS_Namespace;
+  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
+  bsec_iaq.getState(state);
+  InternalFS.remove(BSEC_STATE_FILE);
+  File f = InternalFS.open(BSEC_STATE_FILE, FILE_O_WRITE);
+  if (!f) return;
+  f.write(state, BSEC_MAX_STATE_BLOB_SIZE);
+  f.close();
+}
+
+static uint8_t init_bme680_bsec(TwoWire* wire, uint8_t addr) {
+  bsec_iaq.begin(addr, *wire);
+  if (bsec_iaq.bsecStatus != BSEC_OK) return 0;
+
+  bsec_iaq.setConfig(bsec_config_iaq);
+  if (bsec_iaq.bsecStatus != BSEC_OK) return 0;
+
+  bsec_virtual_sensor_t outputs[] = {
+    BSEC_OUTPUT_IAQ,
+    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
+    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
+    BSEC_OUTPUT_RAW_PRESSURE,
+    BSEC_OUTPUT_STABILIZATION_STATUS,
+    BSEC_OUTPUT_RUN_IN_STATUS,
+  };
+  bsec_iaq.updateSubscription(outputs, 6, BSEC_SAMPLE_RATE_LP);
+  if (bsec_iaq.bsecStatus != BSEC_OK) return 0;
+
+  bsec_load_state();
+  bsec_active = true;
+  return 1;
+}
+
+static void query_bme680_bsec(uint8_t ch, uint8_t, CayenneLPP& lpp) {
+  if (!bsec_data_ready) return;
+  lpp.addTemperature(ch, bsec_temperature);
+  lpp.addRelativeHumidity(ch, bsec_humidity);
+  lpp.addBarometricPressure(ch, bsec_pressure_hpa);
+  lpp.addAltitude(ch, 44330.0f * (1.0f - powf(bsec_pressure_hpa / (float)TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903f)));
+  lpp.addGenericSensor(ch, (uint16_t)bsec_iaq_val);
+  lpp.addAnalogInput(ch, (float)bsec_accuracy);
+}
+#endif
+
 // ============================================================
 // Sensor descriptor table
 //
@@ -476,6 +558,9 @@ static const SensorDef SENSOR_TABLE[] = {
 #ifdef ENV_INCLUDE_BME680
   { TELEM_BME680_ADDRESS,  "BME680",       init_bme680,   query_bme680   },
 #endif
+#if ENV_INCLUDE_BME680_BSEC
+  { TELEM_BME680_ADDRESS,  "BME680+BSEC",   init_bme680_bsec, query_bme680_bsec },
+#endif
 #if ENV_INCLUDE_BME280
   { TELEM_BME280_ADDRESS,  "BME280",       init_bme280,   query_bme280   },
 #endif
@@ -798,11 +883,13 @@ void EnvironmentSensorManager::stop_gps() {
   MESH_DEBUG_PRINTLN("Stop GPS is N/A on this board. Actual GPS state unchanged");
   #endif
 }
+#endif // ENV_INCLUDE_GPS
 
+#if ENV_INCLUDE_GPS || defined(ENV_INCLUDE_BME680_BSEC)
 void EnvironmentSensorManager::loop() {
-  static long next_gps_update = 0;
 
   #if ENV_INCLUDE_GPS
+  static long next_gps_update = 0;
   if (gps_active) {
     _location->loop();
   }
@@ -830,5 +917,27 @@ void EnvironmentSensorManager::loop() {
     next_gps_update = millis() + (gps_update_interval_sec * 1000);
   }
   #endif
+  #if ENV_INCLUDE_BME680_BSEC
+  if (bsec_active && bsec_iaq.run()) {
+    uint8_t prev_accuracy = bsec_accuracy;
+    bsec_temperature  = bsec_iaq.temperature;
+    bsec_humidity     = bsec_iaq.humidity;
+    bsec_pressure_hpa = bsec_iaq.pressure / 100.0f;
+    bsec_iaq_val      = bsec_iaq.iaq;
+    bsec_accuracy     = bsec_iaq.iaqAccuracy;
+    bsec_data_ready   = true;
+
+    if (bsec_accuracy == 3) {
+      if (!bsec_first_save_done) {
+        bsec_save_state();
+        bsec_last_save_ms = millis();
+        bsec_first_save_done = true;
+      } else if ((millis() - bsec_last_save_ms) >= BSEC_SAVE_INTERVAL_MS) {
+        bsec_save_state();
+        bsec_last_save_ms = millis();
+      }
+    }
+  }
+  #endif  // ENV_INCLUDE_BME680_BSEC
 }
-#endif
+#endif // ENV_INCLUDE_GPS || ENV_INCLUDE_BME680_BSEC

+ 1 - 1
src/helpers/sensors/EnvironmentSensorManager.h

@@ -43,7 +43,7 @@ public:
   #endif
   bool begin() override;
   bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
-  #if ENV_INCLUDE_GPS
+  #if ENV_INCLUDE_GPS || defined(ENV_INCLUDE_BME680_BSEC)
   void loop() override;
   #endif
   int getNumSettings() const override;

+ 15 - 0
variants/rak4631/fix_bsec_lib.py

@@ -0,0 +1,15 @@
+Import('env')
+import os
+
+# Bosch has a goof in their PlatformIO packaging making linking fail.
+# The BSEC library's extra_script.py selects cortex-m4/libalgobsec.a (soft-float ABI).
+# nRF52840 compiles with -mfloat-abi=hard, requiring the fpv4-sp-d16-hard blob.
+# Workaround to prepend the hard-float path so the linker finds it before the 
+# soft-float one.
+bsec_hard = os.path.join(
+    env.subst('$PROJECT_DIR'),
+    '.pio', 'libdeps', env.subst('$PIOENV'),
+    'BSEC Software Library', 'src',
+    'cortex-m4', 'fpv4-sp-d16-hard'
+)
+env.Prepend(LIBPATH=[bsec_hard])

+ 5 - 0
variants/rak4631/platformio.ini

@@ -2,6 +2,8 @@
 extends = nrf52_base
 board = rak4631
 board_check = true
+extra_scripts = ${nrf52_base.extra_scripts}
+  post:variants/rak4631/fix_bsec_lib.py
 build_flags = ${nrf52_base.build_flags}
   ${sensor_base.build_flags}
   -I variants/rak4631
@@ -21,6 +23,8 @@ build_flags = ${nrf52_base.build_flags}
   -D SX126X_CURRENT_LIMIT=140
   -D SX126X_RX_BOOSTED_GAIN=1
   -D ENV_INCLUDE_RAK12035=1
+  -UENV_INCLUDE_BME680
+  -D ENV_INCLUDE_BME680_BSEC=1
 build_src_filter = ${nrf52_base.build_src_filter}
   +<../variants/rak4631>
   +<helpers/sensors>
@@ -31,6 +35,7 @@ lib_deps =
   ${sensor_base.lib_deps}
   adafruit/Adafruit SSD1306 @ ^2.5.13
   sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27
+  boschsensortec/BSEC Software Library @ ^1.8.1492
 
 [env:RAK_4631_repeater]
 extends = rak4631