The ENS160 is an inexpensive digital gas sensor used on ESP32 air-quality projects, particularly the small ENS160 + AHT20 breakout boards sold for DIY monitoring. It makes a useful VOC trend monitor in ESPHome and Home Assistant, but one of its advertised outputs is easily misunderstood: the chip does not measure carbon dioxide. Its “eCO₂” number is an algorithmic estimate inferred from a gas-sensor signal. Treating it as a true CO₂ reading can lead to poor ventilation decisions.
This guide shows the actual ENS160 wiring, what its three air-quality outputs mean, a minimal ESPHome setup, a complete temperature/humidity-compensated ENS160 + AHT20 configuration, useful Home Assistant automations, and ways to diagnose unchanging, implausible or missing readings. The example uses an original ESP32 DevKit; change the pins for an ESP32-C3, S3 or another board.
What the ENS160 Measures—and What It Does Not
ENS160 is a metal-oxide (MOX) gas sensor. Changes in its sensing elements are processed into estimates and an indoor-air-quality classification. The outputs are useful for noticing changes in some indoor gas mixtures, not for identifying a specific chemical, establishing that a room is safe, or measuring every airborne contaminant. Source: ESPHome ENS160 integration.
| ESPHome output | Meaning | How to interpret it |
|---|---|---|
tvoc | Estimated total volatile organic compounds, expressed in ppb | A gas-related trend. It does not identify a chemical or replace a dedicated gas analyser. |
eco2 | Estimated CO₂ equivalent in ppm | Calculated from gas-sensor behaviour; not a CO₂ measurement. Do not use as the sole input for occupancy or CO₂ ventilation thresholds. |
aqi | ENS160 AQI-UBA classification from 1 to 5 | This is the device’s VOC-related indoor index, not the outdoor PM2.5/ozone air-quality index commonly shown by weather apps. |
Two monitors on your dashboard can therefore both show a number with “ppm” in the label yet measure different things. A real NDIR or photoacoustic CO₂ sensor observes CO₂ itself; ENS160 reports an equivalent concentration estimated from its gas response. A window opening may affect both, but matching trends are not proof that eCO₂ is accurate. See our indoor air quality station with genuine SCD41 CO₂ for a proper CO₂ measurement path.
The ENS160 AQI scale contains five integer categories. It is not a 0–500 outdoor AQI, and “AQI 3” must not be displayed as if it were a particulate exposure metric. Keep the sensor name explicit—ENS160 VOC AQI—if your dashboard also includes particulate and CO₂ entities.
| ENS160 AQI-UBA | Manufacturer classification | Practical use |
|---|---|---|
| 1 | Excellent | Useful reference for a steady indoor baseline. |
| 2 | Good | Normal low gas-related index; keep observing trends. |
| 3 | Moderate | Check recent cleaning products, cooking or ventilation changes. |
| 4 | Poor | Investigate sources and consider additional ventilation if appropriate. |
| 5 | Unhealthy | Investigate the situation; do not regard this index as a safety alarm. |
These labels belong to the sensor’s own classification and should be presented as ENS160-specific categories, not as a diagnosis of the room or a universal health threshold. A gas sensor can miss hazards outside its sensing capabilities. Use certified alarms for smoke or carbon monoxide, never an ENS160 automation.
Which ENS160 Board Do You Have?
There are bare ENS160 modules, ENS160-only breakouts and combined boards labelled ENS160 + AHT20, ENS160 + AHT21 or similar. The paired temperature/humidity sensor is important because ESPHome can provide ambient compensation to the ENS160. Do not assume every combined PCB has the same pin order, voltage regulator, address-strap position or pull-up voltage: verify your board’s silkscreen and schematic.
- Original ESP32 DevKit or compatible board with a spare I²C bus.
- ENS160 breakout, optionally with a fitted AHT20 temperature/humidity sensor.
- A 3.3 V-safe I²C wiring arrangement and a stable USB supply.
- ESPHome Device Builder and Home Assistant for the optional dashboard and automation.
- Short jumpers for prototyping and a ventilated enclosure for a permanent installation.
Some breakout boards accept 5 V at a pin marked VIN because they have their own regulator; others expose the raw sensor’s low-voltage supply pin. Never infer the permissible voltage solely from the chip name or an online photograph. For the 3.3 V-compatible breakout assumed below, connect it to the ESP32’s 3V3 rail and make sure neither SDA nor SCL is pulled above 3.3 V. If your module explicitly requires another supply, follow its board documentation and use appropriate level shifting as necessary.
Wire ENS160 to the ESP32
| ENS160 breakout | ESP32 DevKit | Notes |
|---|---|---|
| VCC / VIN (3.3 V-compatible board) | 3V3 | Check breakout electrical specification first. |
| GND | GND | All attached sensor boards share ground. |
| SDA | GPIO21 | Original ESP32 example; choose an available GPIO on your actual board. |
| SCL | GPIO22 | Keep wiring short and avoid noisy power wiring. |
ESP32 DevKit 3.3 V-compatible ENS160 board
3V3 -------------------- VCC / VIN
GND -------------------- GND
GPIO21 -------------------- SDA
GPIO22 -------------------- SCL
Optional combined AHT20 is already connected to the same
SDA/SCL lines on a correctly designed dual-sensor breakout.
I²C is a shared bus: the ENS160 and AHT20 can coexist on the same SDA/SCL pair because their addresses differ. AHT20 is normally at 0x38; the ENS160 is at 0x52 or 0x53. For a combined PCB, the sensors are usually connected internally. Do not add another long pair of wires just to connect the AHT20 pins if the PCB already does that.
On bare ENS160 designs, interface-select and address-strap pins need to be wired correctly. The sensor’s I²C addresses are selected by hardware at power-up; whether and how you can change them on a purchased breakout is board-specific. On an I²C board, select the address indicated by the ESPHome scan before manually forcing an address in YAML.
Check the I²C Scan Before Adding the ENS160 Component
Flash a minimal ESPHome node with I²C scanning enabled. A healthy combined board normally appears as 0x38 plus one of 0x52 or 0x53. If it does not, check wiring and power first rather than guessing a new sensor platform.
esphome:
name: ens160-air-node
friendly_name: ENS160 Air Node
esp32:
board: esp32dev
logger:
api:
encryption:
key: !secret ens160_api_key
ota:
- platform: esphome
encryption:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
scan: true
Add a device-specific API encryption key to your secrets.yaml, or use the existing key if Home Assistant already provisioned the device. On a new USB-flashed node, the encrypted OTA block above is appropriate for ESPHome 2026.9 or newer. If you are updating an older password-protected ESPHome node over the air, keep its old OTA configuration until you complete the documented staged encryption migration; replacing it immediately can prevent the upload. Details: ESPHome encrypted OTA and migration.
The original ESP32 commonly uses GPIO21/GPIO22 for I²C, but they are not universal defaults across the entire ESP32 family. Also inspect existing modules for pull-up resistors: adding multiple breakouts in parallel can make the effective pull-up too strong, while a 5 V pull-up can damage an ESP32 input even if the sensor itself survives.
Minimal ESPHome ENS160 Configuration
If you have an ENS160-only board, add this sensor: section to the base configuration above. The example uses 0x52; change it to 0x53 if that is what your I²C scanner finds. Current ESPHome uses ens160_i2c; examples using the older generic ens160 platform should not be copied uncritically.
sensor:
- platform: ens160_i2c
address: 0x52
update_interval: 60s
tvoc:
name: "ENS160 TVOC"
eco2:
name: "ENS160 Estimated CO2 Equivalent"
aqi:
name: "ENS160 VOC AQI"
ESPHome’s ENS160 update interval defaults to 60 seconds, which is a sensible starting point for a room trend. Updating the dashboard more often does not create new physical measurements faster than the device can produce them. For quick troubleshooting, watch the ESPHome logs to separate sensor-bus problems from Home Assistant API problems.
Recommended Complete Build: ENS160 + AHT20 with Ambient Compensation
On a combined ENS160 + AHT20 breakout, ESPHome should read the temperature and humidity sensor and pass those values to the ENS160. Without a compensation input, the ENS160 assumes approximately 25 °C and 50% relative humidity internally, according to ESPHome’s documentation. That can distort trends when the room’s actual conditions differ significantly.
The following is a standalone YAML example for a new original ESP32 DevKit and a confirmed 3.3 V-compatible combined board. Edit the ENS160 address after scanning and ensure the secret names exist. Do not paste a second top-level sensor: block into a YAML file that already has one: merge its entries instead.
esphome:
name: ens160-room-air
friendly_name: ENS160 Room Air
esp32:
board: esp32dev
logger:
api:
encryption:
key: !secret ens160_api_key
ota:
- platform: esphome
encryption:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "ENS160 Air Fallback"
password: !secret ens160_fallback_password
captive_portal:
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
scan: true
sensor:
- platform: aht10
variant: AHT20
address: 0x38
update_interval: 30s
temperature:
name: "Room Temperature"
id: room_temperature
humidity:
name: "Room Humidity"
id: room_humidity
- platform: ens160_i2c
address: 0x52 # Use 0x53 if shown by the I2C scan
update_interval: 60s
tvoc:
name: "ENS160 TVOC"
id: room_tvoc
eco2:
name: "ENS160 Estimated CO2 Equivalent"
id: room_eco2
aqi:
name: "ENS160 VOC AQI"
id: room_voc_aqi
compensation:
temperature: room_temperature
humidity: room_humidity
Why platform: aht10 with variant: AHT20? That is ESPHome’s supported component family for AHT10, AHT20 and AHT30. The variant tells the driver how to communicate with an AHT20-class device. An AHT21-labelled combined board may use a compatible underlying chip, but check the module documentation and identify which sensor ESPHome actually detects. Reference: ESPHome AHT10/AHT20 integration.
Temperature compensation should use air temperature, not the ESP32’s internal silicon temperature or a sensor attached to a hot regulator. A compact enclosure next to the ESP32 Wi-Fi antenna can run warmer than the room. For a reliable trend, keep the AHT20 away from board hot spots and allow gentle airflow over both chips.
What to Expect at First Start: Warm-Up Is Not a Fault
According to ESPHome’s current ENS160 documentation, allow approximately one hour at first power-up before expecting normal readings, and about three minutes after subsequent starts or reboots. Initial values, unavailable entities, and apparent flatlines during that period should not be diagnosed immediately as broken hardware. The gas-sensing algorithm also needs time to adapt to its surroundings; a single first-hour snapshot is not a meaningful indoor-air baseline.
For an initial check, put the assembled node in a normally occupied room, avoid placing solvents or cleaners next to it, and record a baseline after the sensor has warmed up. Then observe routine activities such as cooking or cleaning from a sensible distance. You are looking for an identifiable change and recovery, not for an exact TVOC concentration you can certify against a household reference.
Do not run a “calibrate outdoors to 400 ppm” step on the ENS160: that idea belongs to certain true CO₂ sensors and is meaningless for gas-derived eCO₂. Rebooting or cutting power repeatedly in an attempt to clear a high reading usually makes interpretation worse because it restarts warm-up and baseline behaviour.
How to Read TVOC, eCO₂ and AQI Together
A cleaning spray may produce a rapid ENS160 TVOC response and push its related AQI to a higher category. The same event can move eCO₂ even without a proportional increase in actual CO₂. Conversely, a crowded meeting room can accumulate genuine CO₂ while the VOC mixture changes differently. Neither case is a contradiction; the sensors are reporting different physical quantities or algorithms.
When a graph spikes, check the timeline against activities, room humidity, fan operation and sensor availability. Avoid aggregating tvoc, eco2 and aqi into one “air quality percentage”: their scales are unrelated. The AQI is a discrete five-level classification; TVOC and eCO₂ are separately published numerical estimates. A higher eCO₂ number should not be shown with the same label as a genuine SCD40/SCD41 reading.
| Question | ENS160 helps answer | Use a different sensor when you need |
|---|---|---|
| Did this room’s gas environment change? | Yes, ENS160 TVOC/AQI trends can be useful. | Specific compound identification or legally defensible concentration. |
| Is indoor CO₂ high from occupants? | Only indirect and potentially misleading eCO₂. | A true CO₂ sensor such as SCD40/SCD41. |
| Is PM2.5 elevated after cooking? | No particle measurement. | PMS5003, SPS30 or SEN55 particulate sensor. |
| Is there carbon monoxide or a fire? | No; not an alarm instrument. | Appropriate certified CO/smoke alarms. |
For complementary instrumentation see our SEN55 particulate and gas-index guide and BME280 vs BME680 vs BME688 guide. Neither VOC index, estimated eCO₂ nor PM2.5 stands in for the other.
Home Assistant Dashboard: Keep the Labels Honest
After uploading, add the ESPHome device through the Home Assistant ESPHome integration. Create a dashboard section with Room Temperature, Room Humidity, ENS160 TVOC and ENS160 VOC AQI. Put Estimated CO₂ Equivalent in a separate card or mark it clearly as diagnostic so it is not confused with a genuine CO₂ monitor elsewhere in the home.
- Use a history graph for TVOC to see deviations from its normal baseline and how quickly levels return.
- Display VOC AQI with a five-category legend; never reuse the 0–500 outdoor AQI colour scale.
- Keep both temperature and humidity next to gas readings to identify moisture- or heat-related changes.
- Keep a real CO₂ reading, if installed, as its own entity and chart; never average it with ENS160 eCO₂.
- Show unavailable or warming-up readings as unknown, not as “0 VOC” or automatically “excellent”.
If you want a friendly text label, use a Home Assistant template sensor with explicit values 1 through 5, or the official ESPHome text-sensor example. First check how your running firmware reports invalid/unknown AQI values; do not force them into the Excellent category. The simpler and safer initial setup is to retain the native numeric AQI sensor.
Example Automation: Warn When the VOC Index Remains Elevated
The next automation sends an optional persistent notification if the ENS160-specific AQI has been 3 or above for ten continuous minutes. It does not control a safety-critical device and assumes you have already determined that this threshold is useful for your room. Replace the example sensor entity ID with the one Home Assistant actually generated.
alias: "ENS160 sustained VOC index notification"
description: "Notifies after a sustained high ENS160 VOC AQI"
triggers:
- trigger: numeric_state
entity_id: sensor.ens160_room_air_ens160_voc_aqi
above: 2.5
for: "00:10:00"
conditions: []
actions:
- action: persistent_notification.create
data:
title: "Room gas index elevated"
message: >-
The ENS160 VOC-related AQI has remained at 3 or higher
for 10 minutes. Check recent activities and ventilation.
mode: single
Paste the automation into Home Assistant’s YAML automation editor, not the ESPHome device configuration. Numeric-state triggers fire when a threshold is crossed and the required duration elapses. If Home Assistant starts while the index is already high, that alone may not fire this trigger; add a separate startup-state reconciliation check if you require a reminder after every restart. A notification is a prompt to investigate, not proof of unhealthy or unsafe exposure.
Optional: Switch a Non-Safety-Critical Air Purifier
You could use ENS160 AQI as one signal in a convenience automation—for example, to request a higher purifier speed while its VOC index is elevated—but only if the purifier’s filter is actually designed to remove gaseous contaminants. A standard particle HEPA filter addresses particles, not gases; activated-carbon performance depends on its media and capacity. If your main problem is cooking PM2.5, control the purifier from a particle sensor instead.
Use separate ON/OFF thresholds or a minimum run time to avoid rapid cycling. Define explicit behaviour for an unavailable sensor or disconnected Home Assistant. Do not let a “sensor value is missing” branch silently turn off equipment that is also required for some separate safety reason. In a bathroom or kitchen, building ventilation design and electrical protection take priority over a hobby sensor.
Choosing the Sensor Location and Enclosure
Air sampling and temperature compensation matter more than visual neatness. Install the board in a ventilated enclosure with airflow around its sensing openings. Keep it away from direct sun, heating vents, cooking steam, ultrasonic humidifier plumes and the ESP32’s warm voltage regulator. Avoid placing it next to cleaning products where it will mainly report the contents of one cupboard.
A useful location is at ordinary room height, away from windows or fans that create an unrepresentative stream of air. Leave enough space between the ESP32 and sensing PCB to reduce thermal bias. Use a secure low-voltage enclosure, strain-relief the power lead and keep the assembly away from water. For reliable long-term data, choose continuous mains-derived low-voltage power over aggressive deep-sleep cycles: restarting the gas sensor repeatedly defeats its intended warm-up and ongoing baseline behaviour.
Troubleshooting: ENS160 Missing from the I²C Scan
- Check that the power rail matches the breakout board specification and that grounds are common.
- Swap accidentally reversed SDA and SCL connections; check the module’s connector orientation rather than wire colour.
- Check ESPHome GPIO assignments for your exact ESP32 variant, especially if you copied pins from another board.
- Scan both
0x52and0x53; address-selection circuitry differs between breakouts. - Inspect pull-ups and logic voltage; the ESP32’s GPIOs are not 5 V tolerant.
- Shorten the wires, start with 100 kHz I²C and disconnect other modules to isolate a bus conflict.
If only 0x38 appears, the AHT20 on a combined board can be healthy while the ENS160 is unpowered, incorrectly strapped or defective. If only 0x52/0x53 appears, the reverse is true. A visible I²C address is a useful first check but does not prove that the device can produce valid measurements.
Troubleshooting: “Readings Not Ready”, No Values or Constant Values
First check uptime. A fresh ENS160 needs its warm-up window, which is considerably longer on its first power-up. Confirm that the device is not continuously restarting because of weak USB power or an ESP32 Wi-Fi reboot loop; a dashboard showing an online node does not automatically prove that the sensor has been running continuously. Examine serial or ESPHome logs for measurement-readiness and I²C errors.
After warm-up, check the ENS160 address, verify ESPHome’s current ens160_i2c platform, and temporarily remove unrelated I²C components if the bus is unstable. If AHT20 values are missing or wildly implausible, remove the ENS160 compensation: block only as a diagnostic step; restore it after repairing the environmental input. Do not fabricate fixed 25 °C/50% measurements and describe them as ambient compensation.
A constant VOC estimate is not enough on its own to prove that the module is defective. First establish that fresh readings are available and the device can respond to a normal environmental change over time. Avoid direct solvent exposure, which may overwhelm the sensor and is not a safe test of detector sensitivity.
Troubleshooting: ENS160 Has a Different Address or Two Boards Clash
Two ENS160 devices strapped to the same I²C address cannot normally coexist on one shared bus. If the boards provide independent address selection, arrange one at 0x52 and one at 0x53. If not, use separate ESP32 I²C controllers where supported or a TCA9548A I²C multiplexer to place identical-address modules on separate downstream channels. Each channel becomes its own virtual I²C bus for ESPHome components. For a working pattern, see our AHT20 I²C address and multiplexer guide.
If both modules also carry an AHT20 fixed at 0x38, changing the ENS160 address alone does not solve the AHT20 collision. A multiplexer or separate buses must isolate the combined boards as complete branches.
ENS160 vs BME680, SGP40, SEN55 and Real CO₂ Sensors
These products are not interchangeable, even though they all appear under “air quality” in electronics shops. ENS160 gives VOC-related algorithmic outputs, BME680 measures gas resistance with optional separate calculated IAQ processing, SGP40 gives a VOC index, and SEN55 combines particulate measurement with gas indices. SCD4x and other dedicated optical CO₂ sensors address the different question of actual CO₂ concentration. Choose based on the physical quantity you need to observe rather than the most impressive-looking dashboard number.
A useful small-room combination is ENS160 + AHT20 for VOC-related trends plus SCD41 for actual CO₂, with a PM sensor added if you also need information on smoke, cooking aerosols or dust. In practice, most rooms do not require every sensor: adding measurements is worthwhile only if there is a question you intend to answer or an action you intend to take.
Frequently Asked Questions
Does ENS160 measure actual CO₂?
No. The eco2 entity is an equivalent concentration inferred from a metal-oxide gas-sensor signal. Use SCD40/SCD41 or another genuine CO₂ sensor if your application needs actual CO₂ values or occupancy-related ventilation logic.
Why is my ENS160 at 0x53 instead of 0x52?
The chip supports two hardware-selectable I²C addresses. Your breakout’s address pin wiring determines which one appears at power-up. Use the address discovered by the scanner and check the board schematic before modifying straps.
Can I connect ENS160 and AHT20 to the same I²C pins?
Yes, their different I²C addresses allow them to share SDA and SCL. On many combined PCBs they are already connected internally. Ensure the complete bus stays at ESP32-safe logic levels.
Can it detect carbon monoxide or a dangerous gas leak?
No. The ENS160 is not a certified safety detector and must not be used as a substitute for a carbon-monoxide, combustible-gas or smoke alarm. An apparently reassuring VOC AQI cannot establish that an atmosphere is safe.
Should I reboot or deep-sleep the ENS160 frequently?
It is generally a poor fit for a frequently power-cycled room monitor: you lose continuous observations and re-enter warm-up after restart. For battery nodes, choose a sensing strategy designed for intermittent operation rather than assuming a 60-second publish interval makes the ENS160 low power.
Official References and Related Guides
- ESPHome: ENS160 — current YAML, I²C/SPI options, compensation and warm-up
- ESPHome: AHT10/AHT20 temperature and humidity driver
- ESPHome: native OTA encryption and existing-device migration
- esp32.co.uk/: real SCD41 CO₂ monitoring in a multi-sensor station
- esp32.co.uk/: BME680/BME688 gas resistance and IAQ
- esp32.co.uk/: SEN55 PM2.5, VOC and NOx
- esp32.co.uk/: AHT20 guide: fixed I²C addresses and multiplexers
Bottom line: ENS160 is useful for following VOC-related trends in a room, particularly with real temperature/humidity compensation. Wire it to a safe 3.3 V I²C bus, respect its warm-up period, label eCO₂ as an estimate and keep its 1–5 VOC AQI separate from outdoor AQI, PM2.5 and genuine CO₂ readings.