esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
404 stars 26 forks source link

Support for BSEC 2.0.6 and BSEC_OUTPUT_GAS_ESTIMATE_1/2/3/4 #1332

Open mjdyson opened 2 years ago

mjdyson commented 2 years ago

Describe the problem you have/What new integration you would like

Support for the customised BME688 algorithm that is learned from the dev-board and then put through the Bosch ML software tool. For Arduino sketches, I believe additional config files need to be included in the main program, which allow a customised data model to be referenced. BSEC 2.0.6 is the latest version, which is only available from the BoschSensorTec website after signing a form. The BoschSensorTec github is not currently at this version.

Please describe your use case for this integration and alternatives you've tried:

By using the ML output from a sensor board, the BME688 claims to be able to distinguish between different types of gases. A trained model should be able to tell the difference between coffee and tea for example. The use case I am looking to achieve is to be able to detect 3d printer fumes from various filament types, including ABS and PLA.

Additional context

I dont believe the latest API is in the ESPHome code base. Specifically there are outputs returned from the sensor called "BSEC_OUTPUT_GAS_ESTIMATE_1" (and 2/3/4), which show "Gas_Estimate [probability 0.00-1]. Outputs corresponding to different target groups(Classes) with the probability of occurrence". These target groups (classes) are specified during the ML training and are for the different types of gas that you want to test for.

There is a lot of information in the zip file that is available from the Bosch website, after signing up for the download file, including test data.

mjdyson commented 2 years ago

https://www.bosch-sensortec.com/software-tools/software/bme688-software/

michaelm-github commented 2 years ago

I am interested in BME688 support also. I found this Arduino library that does not include BSEC, but might at least allow readings from the BME688 VOC sensor.

https://github.com/BoschSensortec/BME68x-Sensor-API

neffs commented 2 years ago

I just updated the existing component to the BSEC2 library: https://github.com/neffs/esphome/tree/bsec2

Before creating a PR we should figure out if it should be an additional component (e.g. bme68x_bsec) or replace the old version. In theory this should also work with BME680, but I don't have one to check. Help would be appreciated.

Adding the additional GAS_ESTIMATE values will be easy, but we also need to provide a way to configure the model.

nagyrobi commented 1 year ago

Please submit a PR with your work.

Schluggi commented 1 year ago

Before creating a PR we should figure out if it should be an additional component (e.g. bme68x_bsec) or replace the old version. In theory this should also work with BME680, but I don't have one to check. Help would be appreciated.

Thank you @neffs <3 ! I would love to see your work inside the current bme680_bsec component. Moreover I can approve that it does work totally fine on an ESP32-C3 with an BME680 (#2072).

bootmagic commented 1 year ago

It also works on a Pico W with the following build flags modified from #3917

esphome:
  name: <device-name>
  platformio_options:
    build_flags: 
      - -I /data/<device-name>/.piolibdeps/<device-name>/BSEC2\ Software\ Library/src/inc
      - -L /data/<device-name>/.piolibdeps/<device-name>/BSEC2\ Software\ Library/src/cortex-m0plus
      - -lalgobsec

Thanks @neffs.

Before creating a PR we should figure out if it should be an additional component (e.g. bme68x_bsec) or replace the old version. In theory this should also work with BME680, but I don't have one to check. Help would be appreciated.

Could the bme680_bsec component be depreciated and replaced with a bme68x_bsec2 component? This would cover the newer bme688 sensor.

X4V1 commented 1 year ago

Hello,

First, thank you @neffs for your contributions, it's really cool to have the support for bme688 :)

I'm trying to test the code from you pull request and I can't compile it. I'm getting the error "Platform not found: 'sensor.bme68x_bsec'." Any idea what I'm doing wrong ? I see that your PR passed firsts compile tests so I suppose the problem is between the chair and the keyboard ^^

This is my configuration:

external_components:
  - source: github://neffs/esphome@bsec2_bme68x
    components:
      - bme68x_bsec

substitutions:
  devicename: esp32-5

packages:
  device_base: !include common/boards/arduino/d1-mini32.yaml

i2c:
  sda: 21
  scl: 22
  scan: true
  id: bus_i2c

bme68x_bsec:
  address: 0x77
  state_save_interval: 30min
  bsec_configuration: 0,0,4,2,189,... (it is complete in my yaml, but let's not polluate the topic) # bme688_sel_33v_300s_4d

sensor:
  - platform: bme68x_bsec
    temperature:
      name: "BME688 Temperature"
      filters:
        - median
    pressure:
      name: "BME688 Pressure"
      filters:
        - median
    humidity:
      name: "BME688 Humidity"
      filters:
        - median
    iaq:
      name: "BME688 IAQ"
      filters:
        - median
    iaq_static:
      name: "BME688 Static IAQ"
      filters:
        - median
    co2_equivalent:
      name: "BME688 CO2 Equivalent"
      filters:
        - median
    breath_voc_equivalent:
      name: "BME688 Breath VOC Equivalent"
      filters:
        - median
    gas_resistance:
      # Gas resistance in Ω
      name: "BME688 Gas Resistance"
      filters:
        - median
TCB13 commented 5 months ago

Here's my config for an ESP32-S2 Mini board (lolin_s2_mini) + ### BME680:

esphome:
  name: esp-device-name

external_components:
  - source: github://neffs/esphome@bsec2_bme68x
    components:
     - bme68x_bsec

esp32:
  board: lolin_s2_mini
  variant: ESP32S2
  framework:
    type: arduino

# Enable logging
logger:

api:
  encryption:
    key: "xxxxxxxxxxxxx"

ota:

wifi:
  ssid: "xxxxxxxxx"
  password: "xxxxxxxxxx"

captive_portal:

i2c:
  sda: 11
  scl: 12

bme68x_bsec:
  # Default: 0x76 or 0x77
  address: 0x77
  temperature_offset: 0
  # Default: static or mobile
  # iaq_mode: static # currently not working
  # Default: lp or ulp
  sample_rate: lp
  # Default: 6h
  state_save_interval: 6h
  # Download from Bosch website: https://www.bosch-sensortec.com/software-tools/software/bme688-software/
  # File: BSEC 2.x
  # bsec2-4-0-0_generic_release_23012023.zip\BSEC2.4.0.0_Generic_Release_23012023\config\bme680\bme680_iaq_33v_3s_28d
  bsec_configuration: 0,0,4,2,189,1,0,0,(...copy from the file above...)

sensor:
  - platform: bme68x_bsec
    temperature:
      name: "Temperature"
      id: "temperature"
    humidity:
      name: "Humidity"
      id: "humidity"
    pressure:
      name: "Pressure"
      id: "pressure"
      icon: "mdi:gauge"
    co2_equivalent:
      name: "CO2 Equivalent"
      icon: "mdi:molecule-co2"
    breath_voc_equivalent:
      name: "VOC Equivalent"
      icon: "mdi:molecule"
    iaq:
      name: "IAQ"
      id: iaq
      icon: "mdi:approximately-equal"
    gas_resistance:
      name: "Gas Resistance"
      icon: "mdi:omega"

  - platform: absolute_humidity
    name: Absolute Humidity
    icon: "mdi:water"
    temperature: temperature
    humidity: humidity

text_sensor:
  - platform: bme68x_bsec
    iaq_accuracy:
      name: "IAQ Accuracy"

  - platform: template
    name: "IAQ Classification"
    icon: "mdi:checkbox-marked-circle-outline"
    lambda: |-
      if ( int(id(iaq).state) <= 50) {
        return {"Excellent"};
      }
      else if (int(id(iaq).state) >= 51 && int(id(iaq).state) <= 100) {
        return {"Good"};
      }
      else if (int(id(iaq).state) >= 101 && int(id(iaq).state) <= 150) {
        return {"Lightly polluted"};
      }
      else if (int(id(iaq).state) >= 151 && int(id(iaq).state) <= 200) {
        return {"Moderately polluted"};
      }
      else if (int(id(iaq).state) >= 201 && int(id(iaq).state) <= 250) {
        return {"Heavily polluted"};
      }
      else if (int(id(iaq).state) >= 251 && int(id(iaq).state) <= 350) {
        return {"Severely polluted"};
      }
      else if (int(id(iaq).state) >= 351) {
        return {"Extremely polluted"};
      }
      else {
        return {"error"};
      }

This config should work for both the BME680 and BME688.

Thank you @neffs for your efforts.

TCB13 commented 1 week ago

Update: this seems to be fixed by updating ESPHome to 2024.6.4.


@neffs github://neffs/esphome@bsec2_bme68x seems to be broken with Esphome 2024.6.1 if the safe_mode is set...

esphome:
  name: "esp-xxxxxx"
  friendly_name: esp-xxxxx

external_components:
  - source: github://neffs/esphome@bsec2_bme68x
    components:
      - bme68x_bsec

esp32:
  board: lolin_s2_mini
  variant: ESP32S2
  framework:
    type: arduino

# Enable logging
logger:
  level: ERROR

api:
  encryption:
    key: "x"xxxxxxxxxxx

safe_mode:   <--- this was introduced, before those options lived under OTA
  disabled: true

ota:
  - platform: esphome
    password: "`xxxxxxxxxx"

wifi:

The rest of my config is what I posted above.

The safe_mode was previously at ota but now lives on a component.

While trying to compile the code I get this:

INFO ESPHome 2024.6.1
INFO Reading configuration /config/esphome/esp-xxxxx.yaml...
INFO Generating C++ source...
ERROR Circular dependency detected! Please run with -v option to see what functions failed to complete.
seven7seven commented 4 days ago

This breaks for me on a C3, the sensor reads nothing and:

[14:16:15][E][component:164]: Component bme68x_bsec set Error flag: unspecified

This is my yaml:

# 
# BME680 Sensors
# 

i2c:
  id: i2c_bus
  sda: GPIO05 # Orange
  scl: GPIO04 # Yellow
  scan: True

external_components:
  - source: github://neffs/esphome@bsec2_bme68x
    components:
     - bme68x_bsec 

bme68x_bsec:
  id: bme680
  address: 0x77
  temperature_offset: 0
  sample_rate: lp
  i2c_id: i2c_bus
  bsec_configuration: 0,0,4,2,189,1,0,0,0, _(copied from bme680_iaq_33v_3s_4d)_

sensor:
  - platform: bme68x_bsec
    bme68x_bsec_id: bme680
    temperature:
      name: "Temperature"
      accuracy_decimals: 1
      id: temperature
    pressure:
      name: "Pressure"
      accuracy_decimals: 5
      id: pressure
    humidity:
      name: "Humidity"
      id: humidity
      accuracy_decimals: 1
    iaq:
      name: "IAQ"
      id: iaq
    co2_equivalent:
      name: "CO2 Equivalent"
    gas_resistance:
      name: "Gas Resistance"
      id: gas
      accuracy_decimals: 5
    breath_voc_equivalent:
      name: "Intake Breath VOC Equivalent"

text_sensor:
  - platform: bme68x_bsec
    bme68x_bsec_id: bme680
    iaq_accuracy:
      name: "IAQ Accuracy"
  - platform: template
    name: "Air Quality"
    icon: "mdi:checkbox-marked-circle-outline"
    lambda: |-
      if (int(id(iaq).state) <= 50) {
        return {"Excellent"};
      }
      else if (int(id(iaq).state) <= 100) {
        return {"Good"};
      }
      else if (int(id(iaq).state) <= 150) {
        return {"Lightly polluted"};
      }
      else if (int(id(iaq).state) <= 200) {
        return {"Moderately polluted"};
      }
      else if (int(id(iaq).state) <= 250) {
        return {"Heavily polluted"};
      }
      else if (int(id(iaq).state) <= 350) {
        return {"Severely polluted"};
      }
      else if (int(id(iaq).state) <= 500) {
        return {"Extremely polluted"};
      }
      else {
        return {"unknown"};
      }
TCB13 commented 4 days ago

This breaks for me on a C3, the sensor reads nothing and:

[14:16:15][E][component:164]: Component bme68x_bsec set Error flag: unspecified

Can you try the following config?

There seems to be some awkward things in yours like that bme68x_bsec_id and your i2c setup. To be fair I would use GPIO 8 and 9 as they seem to be the preferred / native i2c ones:

image

I've it working on 2024.6.4 (update if you are in an earlier 2024.6 version) with this C3 board.

esphome:
  name: esp-storage
  friendly_name: esp-storage

external_components:
  - source: github://neffs/esphome@bsec2_bme68x
    components:
      - bme68x_bsec
    refresh: 0s

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

# Enable logging
logger:
  level: ERROR

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxx"

ota:
  - platform: esphome
    password: "xxxxxxxxxxxxxxxxxxxxx"

safe_mode:
  disabled: true

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

i2c:
  sda: 8
  scl: 9

bme68x_bsec:
  # Default: 0x76 or 0x77
  address: 0x77
  temperature_offset: 4.9 # 4.9
  # Default: static or mobile
  #iaq_mode: static
  # Default: lp or ulp
  sample_rate: lp
  # Default: 6h
  state_save_interval: 6h
  # bsec2-4-0-0_generic_release_23012023.zip\BSEC2.4.0.0_Generic_Release_23012023\config\bme680\bme680_iaq_33v_3s_28d\
  bsec_configuration: 0,0,4,2,189,1,0,0,0,0,0,0,158,7,0,0,(.........)

sensor:
  - platform: bme68x_bsec
    temperature:
      name: "Temperature"
      id: "temperature"
    humidity:
      name: "Humidity"
      id: "humidity"
    pressure:
      name: "Pressure"
      id: "pressure"
      icon: "mdi:gauge"
    co2_equivalent:
      name: "CO2 Equivalent"
      id: "co2_equivalent"
      icon: "mdi:molecule-co2"
    breath_voc_equivalent:
      name: "VOC Equivalent"
      icon: "mdi:molecule"
    iaq:
      name: "IAQ"
      id: iaq
      icon: "mdi:approximately-equal"
    gas_resistance:
      name: "Gas Resistance"
      icon: "mdi:omega"

  - platform: absolute_humidity
    name: Absolute Humidity
    icon: "mdi:water"
    temperature: temperature
    humidity: humidity

text_sensor:
  - platform: bme68x_bsec
    iaq_accuracy:
      name: "IAQ Accuracy"

  - platform: template
    name: "IAQ Classification"
    icon: "mdi:checkbox-marked-circle-outline"
    lambda: |-
      if ( int(id(iaq).state) <= 50) {
        return {"Excellent"};
      }
      else if (int(id(iaq).state) >= 51 && int(id(iaq).state) <= 100) {
        return {"Good"};
      }
      else if (int(id(iaq).state) >= 101 && int(id(iaq).state) <= 150) {
        return {"Lightly polluted"};
      }
      else if (int(id(iaq).state) >= 151 && int(id(iaq).state) <= 200) {
        return {"Moderately polluted"};
      }
      else if (int(id(iaq).state) >= 201 && int(id(iaq).state) <= 250) {
        return {"Heavily polluted"};
      }
      else if (int(id(iaq).state) >= 251 && int(id(iaq).state) <= 350) {
        return {"Severely polluted"};
      }
      else if (int(id(iaq).state) >= 351) {
        return {"Extremely polluted"};
      }
      else {
        return {"error"};
      }

  - platform: template
    name: "CO2 Classification"
    icon: "mdi:checkbox-marked-circle-outline"
    lambda: |-
      if ( int(id(co2_equivalent).state) <= 400) {
        return {"Excellent"};
      }
      else if (int(id(co2_equivalent).state) >= 401 && int(id(co2_equivalent).state) <= 1000) {
        return {"Normal"};
      }
      else if (int(id(co2_equivalent).state) >= 1001 && int(id(co2_equivalent).state) <= 2000) {
        return {"Unhealty"};
      }
      else if (int(id(co2_equivalent).state) >= 2001 && int(id(co2_equivalent).state) <= 5000) {
        return {"Health Risk"};
      }
      else if (int(id(co2_equivalent).state) >= 5001) {
        return {"DANGER!"};
      }
      else {
        return {"error"};
      }

switch:
  - platform: restart
    name: "Restart"
kbx81 commented 4 days ago

I've updated https://github.com/esphome/esphome/pull/4585 so it can now be used as an external component: see https://github.com/esphome/esphome/pull/4585#issuecomment-2199450256 You'll need to be using ESPHome dev as https://github.com/esphome/esphome/pull/7023 was just merged and it contains some necessary changes. Some feedback on the PR would be great. 🙂

TCB13 commented 4 days ago

I've updated esphome/esphome#4585 so it can now be used as an external component: see esphome/esphome#4585 (comment) You'll need to be using ESPHome dev as esphome/esphome#7023 was just merged and it contains some necessary changes. Some feedback on the PR would be great. 🙂

I don't get it... the code from @neffs was already working as an external component. It just broke for a specific version of ESPHome (2024.6.x) but the fix was to update ESPHome to 2024.6.4..

kbx81 commented 4 days ago

You can of course still use it as an external component directly from @neffs's repo. The PR is using that branch, so I believe it should give you the same thing either way.