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
| Output | Measured or calculated? | How to interpret it |
|---|---|---|
| Temperature | Measured | Local sensor/PCB temperature; check enclosure heating. |
| Relative humidity | Measured | Room humidity at the sensor’s temperature. |
| Barometric pressure | Measured | Useful for local pressure trend, not an air-quality score. |
| Gas resistance (Ω) | Measured | Response of a heated metal-oxide element to a mixture of gases and humidity. |
| IAQ index (0–500) | Calculated by BSEC2 | Relative air-quality indicator informed by history and environmental compensation. |
| CO₂ equivalent / eCO₂ | Calculated estimate | Not a measurement of the actual CO₂ concentration. |
| Breath-VOC equivalent | Calculated estimate | Algorithmic 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 breakout | Classic ESP32 | Notes |
|---|---|---|
| VIN / VCC | 3V3 | Use 3.3 V for the simplest compatible module. |
| GND | GND | Common electrical ground. |
| SDA / SDI | GPIO21 | I²C data; check 3.3 V pull-ups. |
| SCL / SCK | GPIO22 | I²C clock; check 3.3 V pull-ups. |
| SDO / ADDR | Board-dependent | Determines 0x76 or 0x77 on common breakouts. |
| CS | Board-dependent | I²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
| Requirement | Raw ESPHome bme680 | Bosch BSEC2 ESPHome |
|---|---|---|
| Temperature, humidity, pressure | Yes | Yes |
| Gas resistance | Yes | Yes |
| IAQ index with history | Not inherently | Yes |
| Algorithm calibration status | No | Yes |
| CO₂ equivalent estimate | No | Yes |
| Third-party binary licence | Not needed for basic raw component | Bosch BSEC2 licence applies |
| Control of sampling interval/heater | Direct ESPHome sensor settings | BSEC2 owns its sampling schedule |
| Best for | Transparent trends and experimentation | Relative 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 index | Bosch category |
|---|---|
| 0–50 | Excellent |
| 51–100 | Good |
| 101–150 | Lightly polluted |
| 151–200 | Moderately polluted |
| 201–250 | Heavily polluted |
| 251–350 | Severely polluted |
| 351–500 | Extremely 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_offsetafter 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
| Entity | Suggested display | Why |
|---|---|---|
| Room IAQ | Current number plus history graph | Show trends, not a safety clearance. |
| IAQ calibration status | Small diagnostic badge | Explain unreliable readings after restart. |
| Gas resistance | History graph in kΩ/Ω | See direct sensor response and baseline shifts. |
| Temperature and humidity | Current + 24-hour trend | Context for changes and condensation. |
| Pressure | Optional long-term history | Weather context, not pollution. |
| eCO₂ and b-VOC equivalent | Optional diagnostic graph labelled “estimate” | Avoid confusion with a real CO₂ or TVOC analyser. |
| ESPHome uptime/RSSI | Diagnostic section | Separate 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
| Symptom | Likely cause / first check |
|---|---|
| Nothing found at 0x76 | Try 0x77; confirm supply, I²C wiring and pull-ups. |
| I²C device found but setup fails | Confirm actual chip: BME280/BMP280 can share addresses. |
| Gas resistance missing | Use the BME680 component with gas output and heater enabled; check library logs. |
| BSEC2 firmware does not build | Verify current ESPHome/framework compatibility and review Bosch library licence. |
| IAQ unavailable at startup | Allow stabilisation and monitor IAQ accuracy. |
| IAQ differs from another module | Different baseline, placement, history and humidity; compare trends. |
| Temperature high / humidity low | Heat from enclosure, regulator, ESP32 or gas-measurement duty. |
| Air-quality graph swings after cleaning | VOC-sensitive gas response is expected; it is not direct CO₂. |
| Every reboot seems to reset baseline | Check state-saving settings, flash/power integrity and uptime. |
| Graph flat despite cooking | Check heater, airflow, installed sensor placement and calibration status. |
| Node unavailable in Home Assistant | Check 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
- BME280 vs BME680 vs BME688: Which Is Best? — choose the correct Bosch environmental sensor.
- BME688 vs BME690 — newer gas sensor architecture and condensation considerations.
- BMP280 vs BME280 — pressure/humidity alternatives without gas sensing.
- AHT20 with ESP32 and ESPHome — a lower-cost temperature/humidity-only node.
- ESPHome Native API vs MQTT — how to publish the measurements to Home Assistant.
Official Documentation and References
- ESPHome BME680 raw component — direct sensor readings and heater settings.
- ESPHome BME68x BSEC2 component — recommended Bosch IAQ integration for BME680/BME688.
- ESPHome legacy BME680 BSEC component — migration context and former interface.
- Bosch Sensortec BME680 product information — specifications and official datasheet.