ESPHome 2026, Home Assistant

Indoor Climate Sensor with BME680 and ESPHome

· 5 steps · ca. 1 h, dazu 1–2 Tage Kalibrierung

Indoor Climate Sensor with BME680 and ESPHome

A BME680 on an ESP32 measures temperature, humidity and air pressure – and also notices how stale the air in the room is. Bosch’s BSEC2 library turns this into an air quality index that Home Assistant can use to remind you to ventilate. The build takes an hour; after that, the sensor needs one to two days to get to know its room.

Overview

Parts: ESP32 board, BME680 breakout, four wires, Home Assistant with ESPHome.

Result: seven entities in Home Assistant – temperature, humidity, pressure, air quality (IAQ) plus a rating in words, estimated CO₂ and VOC, and how reliable the IAQ value currently is.

1 WiringAufbau

Goal: sensor on the ESP32’s I²C bus.

BME680ESP32
VCC3V3
GNDGND
SDAGPIO 21
SCLGPIO 22

The remaining pins (CS, SDO) are already wired for I²C on common breakout boards.

2 Flash the configurationAufbau

Goal: ESPHome firmware with BSEC2 on the ESP32.

WhyWhy BSEC2 instead of a custom formula?

The sensor itself only provides a gas resistance: high in clean air, low in polluted air. What counts as “clean” depends on the room. BSEC2 learns this baseline and scales the index to it – a fixed formula, by contrast, stays at around 20 and barely reacts.

First create a secrets.yaml with wifi_ssid, wifi_password, ap_password and api_key. Then the device configuration:

room-climate.yaml
esphome:
  name: room-climate
  friendly_name: Room Climate

esp32:
  variant: esp32
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: !secret api_key

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Room Climate Fallback Hotspot"
    password: !secret ap_password

captive_portal:

i2c:
  - id: i2c_1
    sda: 21
    scl: 22
    scan: true                # shows the detected address in the log

bme68x_bsec2_i2c:
  i2c_id: i2c_1
  address: 0x77               # SDO open: 0x77, SDO to GND: 0x76
  model: bme680
  operating_age: 28d          # for the first calibration: 4d
  sample_rate: LP             # every 3 s; ULP (5 min) hardly calibrates
  supply_voltage: 3.3V
  temperature_offset: 0       # determine in step 5
  state_save_interval: 6h     # save calibration to flash

sensor:
  - platform: bme68x_bsec2
    temperature:
      name: "Temperature"
    pressure:
      name: "Pressure"
    humidity:
      name: "Humidity"
    iaq:
      name: "Air Quality"
      on_value:
        - text_sensor.template.publish:
            id: iaq_rating
            state: !lambda |-
              if (isnan(x))  return "unknown";
              if (x <= 50)   return "excellent";
              if (x <= 100)  return "good";
              if (x <= 150)  return "lightly polluted";
              if (x <= 200)  return "moderately polluted";
              if (x <= 250)  return "heavily polluted";
              if (x <= 350)  return "severely polluted";
              return "extremely polluted";
    co2_equivalent:
      name: "CO2 (estimated)"
    breath_voc_equivalent:
      name: "VOC (estimated)"

text_sensor:
  - platform: bme68x_bsec2
    iaq_accuracy:
      name: "Calibration"
  - platform: template
    id: iaq_rating
    name: "Air Quality Rating"
    update_interval: never

Do the first flash via USB; all further updates run over the air (OTA).

3 Add to Home AssistantAufbau

Goal: readings as entities in Home Assistant.

The device is usually discovered automatically; otherwise add it under Settings → Devices & services → ESPHome.

4 Let it calibrateEinregeln

Goal: Calibration shows 3 – Calibrated.

WhyWhy does it take so long?

BSEC2 needs to experience both clean and polluted air to set the baseline. Only from level 3 onwards is the state saved to flash – a restart before that starts over.

Let the sensor run openly in the room without reflashing or restarting it. A brief scent stimulus helps: hold a felt-tip pen or a little disinfectant nearby for a few seconds, then air the room thoroughly. A brand-new sensor additionally needs up to 48 hours of burn-in.

CalibrationMeaning
0 – Stabilizingstart-up phase
1 – Uncertainbaseline not yet determined
2 – Calibratingcalibration in progress
3 – Calibratedvalues reliable

5 Enclosure and temperature correctionEinregeln

Goal: readings not skewed by the ESP32’s heat.

WhyWhy a separate compartment?

The ESP32 warms up by several degrees during operation. If the sensor sits near it or in its exhaust air, it reads too warm and too dry. Room air also reaches the sensor only by diffusion – in still air it reacts sluggishly and calibrates poorly.

  • Put the sensor in its own compartment, separated from the ESP32 by a wall, below or beside it, never above.
  • Give the sensor compartment generous openings on at least two sides, with the opening in the metal lid facing the room air.
  • Let printed enclosures air out for a few days before the sensor moves in – outgassing distorts the baseline.
  • Avoid sunlight, radiators and stoves.

After 1–2 hours in the enclosure, compare the temperature with a reference thermometer and enter the difference as temperature_offset (e.g. -2.5). The saved calibration state is kept when reflashing.

Reading the values

IAQRating
0–50excellent
51–100good
101–150lightly polluted
151–200moderately polluted
201–250heavily polluted
251–350severely polluted
above 350extremely polluted

The trend says more than a single value: a rise reliably indicates that it is time to ventilate.

Troubleshooting

ObservationCauseRemedy
IAQ stays at around 50calibration still runningkeep it running (step 4)
Calibration stays at Uncertainuniform air, restarts or ULPscent stimulus, no restarts, LP
Temperature several degrees too highheat from the ESP32separate, temperature_offset (step 5)
Sensor not foundaddress or wiringI²C scan in the log, 0x76/0x77, SDA/SCL
IAQ implausible after fitting the enclosureoutgassing of the print materialair it out, let it recalibrate