BME680 with ESPHome: Gas Resistance, IAQ and Reliable Indoor-Air Monitoring

BME680 ESPHome guide: ESP32 wiring, raw gas resistance versus BSEC2 IAQ, calibrated readings, Home Assistant dashboards, temperature offsets, limitations and fixes.

The BME680 can show how indoor air changes during cooking, cleaning or poor ventilation—but it is not a CO₂ meter or a universal gas detector. Its heated metal-oxide sensor reports gas resistance, while Bosch’s BSEC2 software can turn a history of sensor readings into a relative Indoor Air Quality (IAQ) index. These are different outputs, and understanding that distinction is the difference between a useful Home Assistant dashboard and a misleading one.

This guide shows both supported ESPHome approaches: a raw BME680 configuration that does not require Bosch’s proprietary algorithm, and an alternative BSEC2 configuration that supplies IAQ, estimated CO₂-equivalent, gas resistance and calibration status. Use one approach per physical sensor; do not declare both platforms against the same I²C address.

What the BME680 Actually Measures

OutputMeasured or calculated?How to interpret it
TemperatureMeasuredLocal sensor/PCB temperature; check enclosure heating.
Relative humidityMeasuredRoom humidity at the sensor’s temperature.
Barometric pressureMeasuredUseful for local pressure trend, not an air-quality score.
Gas resistance (Ω)MeasuredResponse of a heated metal-oxide element to a mixture of gases and humidity.
IAQ index (0–500)Calculated by BSEC2Relative air-quality indicator informed by history and environmental compensation.
CO₂ equivalent / eCO₂Calculated estimateNot a measurement of the actual CO₂ concentration.
Breath-VOC equivalentCalculated estimateAlgorithmic breath-VOC-style output, not a direct lab TVOC measurement.

Bosch describes the BME680 as a four-in-one temperature, pressure, humidity and gas sensor. It cannot identify individual VOCs from one resistance reading and does not measure particulate matter (PM2.5), radon, carbon monoxide, oxygen or carbon dioxide directly. Never rely on it as a safety alarm or a substitute for a certified CO alarm.

Gas Resistance Is Not an IAQ Percentage

The raw gas output is electrical resistance, commonly shown in ohms or kilo-ohms. On many everyday VOC exposures, resistance falls as reducing gas concentration rises, but humidity, temperature, heater settings, ageing and the particular vapour all influence that relationship. Higher resistance is often associated with cleaner air under consistent conditions; it is not a universal calibration.

Example raw readings:
Clean-air period:  120 kΩ
After cleaning:     55 kΩ

Do NOT conclude: 55 kΩ = 55% air quality
Do NOT conclude: 55 kΩ = 550 ppm CO₂

The response is most useful when compared with that same sensor, in the same room, after its heater has stabilised. Comparing “80 kΩ” between two inexpensive modules is much less informative than comparing each module with its own recent baseline.

BME680 vs BME280, BME688 and BME690

BME280 measures temperature, humidity and pressure but has no gas sensor. BME680 adds a metal-oxide gas measurement and supports Bosch’s IAQ calculation. BME688 extends the family with more advanced gas-scanning and classification workflows, while BME690 is a newer evolution with improvements for some low-power and high-condensation use cases. The ordinary BME680 remains useful for room-condition trends when advanced BME688 AI gas scanning is not needed.

For model selection, read BME280 vs BME680 vs BME688 and BME688 vs BME690.

ESP32 to BME680 Wiring

The following is a typical 3.3 V I²C breakout setup with a classic ESP32 DevKit. The bare Bosch sensor is not a 5 V logic device; some modules accept 5 V at VIN only because they add a regulator and sometimes level shifting. Power and logic compatibility must be checked for the particular breakout.

BME680 breakoutClassic ESP32Notes
VIN / VCC3V3Use 3.3 V for the simplest compatible module.
GNDGNDCommon electrical ground.
SDA / SDIGPIO21I²C data; check 3.3 V pull-ups.
SCL / SCKGPIO22I²C clock; check 3.3 V pull-ups.
SDO / ADDRBoard-dependentDetermines 0x76 or 0x77 on common breakouts.
CSBoard-dependentI²C mode requires the correct chip-select connection; many breakouts handle it.

Keep the I²C lines short initially. The sensor addresses are 0x76 or 0x77, not 0x38 (that address belongs to AHT20). If an I²C scan finds 0x76 but the BME680 component refuses to initialise, it could be another Bosch device at that address; an address scan alone does not identify the actual chip.

Choose One of Two ESPHome Paths

RequirementRaw ESPHome bme680Bosch BSEC2 ESPHome
Temperature, humidity, pressureYesYes
Gas resistanceYesYes
IAQ index with historyNot inherentlyYes
Algorithm calibration statusNoYes
CO₂ equivalent estimateNoYes
Third-party binary licenceNot needed for basic raw componentBosch BSEC2 licence applies
Control of sampling interval/heaterDirect ESPHome sensor settingsBSEC2 owns its sampling schedule
Best forTransparent trends and experimentationRelative indoor air-quality dashboard

Do not combine the two YAML examples below. They are alternative drivers for the same sensor; both would attempt to manage the BME680 heater and measurements if pointed at one physical device.

Raw Gas Resistance: Working ESPHome Example

Start here if you want simple, reproducible measurements and do not need the proprietary IAQ estimate. This complete example uses a classic ESP32, I²C at GPIO21/22 and the ESPHome native API for Home Assistant.

esphome:
  name: bme680-room-raw
  friendly_name: BME680 Room Raw

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: !secret api_encryption_key

ota:
  - platform: esphome

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

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

sensor:
  - platform: bme680
    address: 0x76
    temperature:
      name: "Room Temperature"
      oversampling: 16x
    humidity:
      name: "Room Humidity"
    pressure:
      name: "Room Pressure"
    gas_resistance:
      name: "Room Gas Resistance"
    heater:
      temperature: 320
      duration: 150ms
    update_interval: 60s

Check the I²C scan and change address: 0x76 to 0x77 if necessary. ESPHome documents the raw component heater at 320°C for 150 ms by default; this temperature applies to the tiny internal metal-oxide sensing element, not the ambient air or external PCB.

What to Expect from the Raw Graph

After the board starts, gas resistance may drift substantially as the heater and sensor settle. Exposing the board to alcohol vapour, cleaning products or cooking emissions can create noticeable changes. Humidity changes can also shift resistance without a comparable change in perceived air quality; therefore plot gas resistance alongside temperature and humidity rather than looking at the gas line in isolation.

Do not label a simple raw-resistance graph as “CO₂ ppm”, “TVOC ppm” or Bosch IAQ. You can create a custom relative indicator for your own device, but its thresholds and units must not be confused with Bosch’s BSEC2 0–500 scale.

BSEC2: ESPHome Configuration for Bosch IAQ

ESPHome’s current recommended alternative for calculated IAQ is bme68x_bsec2_i2c. It supports BME680 and BME688. For BME680, set model: bme680; do not copy the BME688-only gas-classification or regression settings from another guide.

The configuration below replaces, rather than extends, the raw platform: bme680 example. ESPHome enables BSEC2 when this component is added; Bosch’s proprietary library requires acceptance of its licence. ESPHome notes that redistribution of compiled firmware binaries containing the component is prohibited under that licence—build for your own device and review the licence before distributing firmware.

esphome:
  name: bme680-room-iaq
  friendly_name: BME680 Room IAQ

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: !secret api_encryption_key

ota:
  - platform: esphome

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

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

bme68x_bsec2_i2c:
  address: 0x76
  model: bme680
  sample_rate: LP
  operating_age: 28d
  state_save_interval: 6h
  temperature_offset: 0

sensor:
  - platform: bme68x_bsec2
    temperature:
      name: "Room Temperature"
    humidity:
      name: "Room Humidity"
    pressure:
      name: "Room Pressure"
    gas_resistance:
      name: "Room Gas Resistance"
    iaq:
      name: "Room IAQ"
    iaq_accuracy:
      name: "Room IAQ Accuracy Level"
      entity_category: diagnostic
    co2_equivalent:
      name: "Room CO2 Equivalent (Estimate)"
      entity_category: diagnostic
    breath_voc_equivalent:
      name: "Room Breath VOC Equivalent (Estimate)"
      entity_category: diagnostic

text_sensor:
  - platform: bme68x_bsec2
    iaq_accuracy:
      name: "Room IAQ Calibration Status"
      entity_category: diagnostic

Leave temperature_offset: 0 until you have compared the finished, ventilated enclosure against a trusted room-temperature reference. Do not add update_interval: to the BSEC2 gas sensor as though it were the raw component: the BSEC2 hub controls its sampling schedule.

What BSEC2 IAQ 0–500 Means

Bosch’s BSEC2 IAQ runs from 0 (better) toward 500 (worse) and is a relative indoor air-quality indicator derived from the heated gas-sensor signal, environmental measurements and a rolling background baseline. It is not the US EPA Air Quality Index (AQI), an absolute chemical-exposure limit or an independently measured VOC concentration.

BSEC IAQ indexBosch category
0–50Excellent
51–100Good
101–150Lightly polluted
151–200Moderately polluted
201–250Heavily polluted
251–350Severely polluted
351–500Extremely polluted

Use these as the vendor’s descriptive categories, not a medical or regulatory interpretation. One product using its own historical baseline can show different scores from another device in the same room, especially during commissioning.

IAQ Accuracy: Why a New Sensor Can Look Wrong

BSEC2 maintains an adaptive baseline. Its accuracy indicator has four text states: Stabilizing, Uncertain, Calibrating and Calibrated. ESPHome says initial stabilisation typically lasts around five minutes, but a credible background baseline may take longer because the sensor needs a representative history of its installed environment.

  • Stabilizing: newly started sensor is warming and stabilising; do not automate from its IAQ value yet.
  • Uncertain: there is insufficient or uninformative recent variation for a confident baseline.
  • Calibrating: the algorithm is collecting/adjusting its reference.
  • Calibrated: the algorithm reports a successful calibration state; this is not a certificate of pollutant-concentration accuracy.

For the first days, compare trends with known events such as cooking or opening windows, and watch the calibration-status entity. It is normal for the baseline to adapt after moving a sensor to a different room or changing heater/sampling settings.

LP vs ULP Sampling and Flash-State Saving

ESPHome’s BSEC2 component offers LP (typically one gas-dependent sample every three seconds) and ULP (approximately one sample every five minutes). Sampling also controls how frequently the internal gas heater is driven. For a mains-powered room monitor that should show the effect of cooking or cleaning, LP gives a more responsive trace; for a battery budget or slow monitoring, ULP may fit better.

By default, ESPHome saves the BSEC2 algorithm state to flash at a minimum interval of six hours, or when full calibration is reached as described by the component. This reduces the need to rebuild the entire baseline after a normal reboot. Setting an unnecessarily short interval can increase flash writes; setting it extremely long means you may lose more recent calibration history on restart.

Important for battery projects: putting the ESP32 itself into deep sleep for most of the day is not equivalent to operating BSEC2 in its own ULP mode. The algorithm needs a suitable schedule and history; frequent full power loss or long sleeps can compromise the value of continuous IAQ tracking.

BME680 eCO₂ Is Not Real CO₂

BSEC2 can publish a co2_equivalent sensor, typically with a ppm-style unit. Bosch describes this as an estimated equivalent derived from correlations between VOCs and exhaled CO₂; the BME680 contains no nondispersive-infrared (NDIR) or other direct CO₂ measurement channel.

Examples of misleading situations include alcohol-based cleaning elevating eCO₂ without a matching rise in actual CO₂, or a poorly ventilated room with elevated human-generated CO₂ but few VOC changes. Do not use eCO₂ as the only input to a ventilation strategy whose purpose is to control actual CO₂ concentration. Use a proper NDIR CO₂ sensor for that requirement.

The same caution applies to breath_voc_equivalent: it is a Bosch algorithm output, not an independent laboratory analysis of every VOC present. The BME680 is also not a certified CO, gas-leak or smoke detector.

Avoid the Fake IAQ Template Trap

You may find older examples that take log(gas_resistance), add a humidity term and then compare that value directly with thresholds 50/100/150/200. The logarithmic expression is not the same as Bosch BSEC2’s calibrated 0–500 IAQ index. Those thresholds only make sense for the correctly produced Bosch index, not an arbitrary raw-resistance formula.

For an honest Home Assistant dashboard, either show raw gas resistance with its physical unit and a clearly labelled “relative trend”, or install the BSEC2 component and expose its official IAQ output with calibration status. Do not relabel one as the other.

Temperature and Humidity: Fix the Enclosure First

The BME680’s heater operates inside the sensor, and the nearby ESP32 and regulator can warm the whole PCB. If the sensor is enclosed with the processor in a sealed plastic box, the reported temperature may be too high and relative humidity correspondingly too low. A constant software correction may appear to fix temperature while leaving behaviour wrong during Wi-Fi or CPU load changes.

  • Place the sensor on a small ventilated extension board or near the enclosure’s vent.
  • Keep it away from the ESP32, USB supply/regulator and direct sunlight.
  • Do not place it directly in a duct with water droplets or inside a sealed box if you want room-air humidity.
  • Allow airflow while protecting against insects, dust and liquid water.
  • Only apply temperature_offset after measuring a stable bias in the final installation.

BSEC2 specifically provides temperature_offset so its algorithm can correct the reported temperature and compensate the resulting relative humidity calculation. That is preferable to arbitrarily editing only the Home Assistant display entity, which would leave the algorithm working with a different internal temperature assumption.

Where to Install an Indoor-Air-Quality Node

For a general room reading, position it away from the direct jet of an air purifier, open window, cooking hood or air-conditioner. A kitchen-adjacent location makes cooking spikes visible, but directly above the hob may expose it to grease, heat and condensation and make it unrepresentative of the occupied room.

For a bathroom, greenhouse, aggressive solvent environment or any installation that regularly condenses water, choose enclosure and sensor hardware intended for those conditions. Bosch’s newer BME690 specifically targets improved high-condensation robustness, but even that does not make a bare breakout waterproof.

Home Assistant Dashboard: What to Display

EntitySuggested displayWhy
Room IAQCurrent number plus history graphShow trends, not a safety clearance.
IAQ calibration statusSmall diagnostic badgeExplain unreliable readings after restart.
Gas resistanceHistory graph in kΩ/ΩSee direct sensor response and baseline shifts.
Temperature and humidityCurrent + 24-hour trendContext for changes and condensation.
PressureOptional long-term historyWeather context, not pollution.
eCO₂ and b-VOC equivalentOptional diagnostic graph labelled “estimate”Avoid confusion with a real CO₂ or TVOC analyser.
ESPHome uptime/RSSIDiagnostic sectionSeparate network failures from sensor errors.

Use the actual entity IDs generated by the ESPHome integration, typically similar to sensor.bme680_room_iaq and sensor.bme680_room_iaq_calibration_status. Verify the IDs in Home Assistant Developer Tools before copying them into an automation.

Gentle Ventilation Reminder Based on IAQ

The example below is an optional non-safety reminder when IAQ stays above an illustrative threshold for ten minutes and the reported calibration state is good. Replace the entity IDs with the ones that Home Assistant created on your installation:

alias: Room air quality reminder
triggers:
  - trigger: numeric_state
    entity_id: sensor.bme680_room_iaq
    above: 150
    for: "00:10:00"
conditions:
  - condition: state
    entity_id: sensor.bme680_room_iaq_calibration_status
    state: "Calibrated"
actions:
  - action: persistent_notification.create
    data:
      title: "Room air-quality trend"
      message: "BME680 IAQ has remained elevated. Consider ventilation and check the room conditions."
mode: single

Treat 150 here as an illustrative choice tied to Bosch’s relative scale, not a health-based threshold. Do not create an automatic “all clear” simply because the score falls; neither the sensor nor the algorithm can prove that the room is free from hazardous gases.

Troubleshooting BME680 in ESPHome

SymptomLikely cause / first check
Nothing found at 0x76Try 0x77; confirm supply, I²C wiring and pull-ups.
I²C device found but setup failsConfirm actual chip: BME280/BMP280 can share addresses.
Gas resistance missingUse the BME680 component with gas output and heater enabled; check library logs.
BSEC2 firmware does not buildVerify current ESPHome/framework compatibility and review Bosch library licence.
IAQ unavailable at startupAllow stabilisation and monitor IAQ accuracy.
IAQ differs from another moduleDifferent baseline, placement, history and humidity; compare trends.
Temperature high / humidity lowHeat from enclosure, regulator, ESP32 or gas-measurement duty.
Air-quality graph swings after cleaningVOC-sensitive gas response is expected; it is not direct CO₂.
Every reboot seems to reset baselineCheck state-saving settings, flash/power integrity and uptime.
Graph flat despite cookingCheck heater, airflow, installed sensor placement and calibration status.
Node unavailable in Home AssistantCheck ESPHome API/Wi-Fi independently of the I²C sensor.

If the entire ESP32 goes offline, follow ESPHome Wi-Fi disconnect and reboot troubleshooting rather than repeatedly adjusting the gas heater. If only BME680 reads fail, temporarily disconnect other I²C devices and run scan: true on a short cable.

Should You Use BME680 for a New Project?

The BME680 is still a solid inexpensive choice for temperature, humidity, pressure and relative VOC-responsive gas trends. ESPHome’s raw component is easy to configure; BSEC2 adds a vendor-defined IAQ index and calibration status if you accept the proprietary-library constraints. A BME280 remains simpler when gas sensing is not required; BME688 is worth considering if you specifically need modern gas-scanning workflows.

For a general room monitor, the most useful arrangement is often BME680/BSEC2 for environmental trends plus a dedicated NDIR CO₂ sensor if ventilation control genuinely depends on carbon dioxide. The two sensors answer different questions, and neither should be presented as a certified life-safety detector.

Related ESP32 Guides

Official Documentation and References

Share your love