Temperature, humidity and air
ENS160 with ESP32
The CCS811 that many old tutorials use has been discontinued, and DFRobot and Adafruit both point to the ENS160 as the replacement. It reports an air quality index, TVOC and eCO2 over I2C. Adafruit's listing gives about 3 minutes of warm-up, and under an hour for full start-up.
The 'e' in eCO2 is the important letter. It stands for equivalent: the chip estimates a CO2 level from gases it senses, and it does not measure CO2 itself. An empty room with a solvent smell can show high eCO2, and a closed bedroom full of real CO2 may not. For a ventilation decision based on real CO2, use an SCD40. The ENS160 is good at showing that air has got worse, for example after cooking or painting.
Readings improve with temperature and humidity compensation, so pairing it with an AHT20 or SHT31 on the same bus is common. Adafruit notes that this needs an external humidity sensor. The address is 0x53 on Adafruit's board. Other boards may use 0x52, so scan the bus. Some modules have a regulator and others do not, so read the board's marking before connecting 5 V. The example writes the operating mode and reads the registers directly, with no library.

Specifications
| Outputs | AQI, TVOC, eCO2 |
|---|---|
| Warm-up | About 3 minutes, under 1 h start-up |
| Interface | I2C |
| I2C address | 0x53, some boards 0x52 |
| Compensation | Takes temperature and humidity input |
Power and logic levels
3.3 V: check your module first
| Supply | Chip VDD 1.71–1.98 V, I/O up to 3.6 V |
|---|---|
| Signal level | Depends on the board |
| On an ESP32 | Use a board with its own 1.8 V regulator. A bare chip must not get 3.3 V on VDD. |
ESP32 pins, including those on a display board's connectors, work at 3.3 V and are not 5 V tolerant. Modules differ, so if in doubt, measure the signal before you connect it.
Wiring
| ENS160 | Connect to | Note |
|---|---|---|
| VIN / VCC | 3V3 | |
| GND | GND | |
| SDA | GPIO 8 | Any free pin, set in Wire.begin() |
| SCL | GPIO 9 | Any free pin, set in Wire.begin() |
Arduino code
#include <Wire.h>
const int SDA_PIN = 8, SCL_PIN = 9;
const uint8_t ADDR = 0x53; // 0x52 on some boards
uint16_t read16(uint8_t reg) { // low byte first
Wire.beginTransmission(ADDR);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(ADDR, (uint8_t)2);
uint16_t v = Wire.read();
return v | (Wire.read() << 8);
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
Serial.printf("Part ID 0x%04X\n", read16(0x00)); // expect 0x0160
Wire.beginTransmission(ADDR);
Wire.write(0x10); Wire.write(0x02); // standard gas sensing mode
Wire.endTransmission();
}
void loop() {
uint8_t aqi = read16(0x21) & 0x07;
Serial.printf("AQI %d TVOC %u ppb eCO2 %u ppm\n", aqi, read16(0x22), read16(0x24));
delay(2000);
}
No extra library needed. Compiled for ESP32-S3 with arduino-esp32 3.x. Not yet tested by us on a physical sensor. Change the pins to match your board.
On a display
Read the value in your sketch and pass it to a label, or send it over MQTT and show it with an MQTT value in the editor.
Design a display for ENS160Boards with a ready connector
Only boards whose connectors we have surveyed. Others may still work on free GPIOs.
| Elecrow CrowPanel ESP32 HMI 2.4" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32 HMI 2.8" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32 HMI 3.5" | I2C port: SDA 22, SCL 21Not shared with the touch controller (it is on SPI) — free for sensors |
| Elecrow CrowPanel ESP32-S3 HMI 5.0" | I2C port: SDA 19, SCL 20Same I2C bus as the GT911 touch (0x14 / 0x5D) and, on V3.0 boards, the PCA9557 touch-reset expander (0x18) |
| Elecrow CrowPanel ESP32-S3 HMI 7.0" | I2C port: SDA 19, SCL 20Same I2C bus as the GT911 touch (0x14 / 0x5D) and, on V3.0 boards, the PCA9557 touch-reset expander (0x18) |
| LilyGO T-Display-S3-Pro 2.33" | QWIIC RIGHT: SDA 5, SCL 6I2C only — same bus as the touch, the PMU (SY6970) and the light sensor |
| M5Stack AtomS3 0.85" | Bottom I2C pins: SDA 38, SCL 39Same I2C bus as the built-in IMU (MPU6886, 0x68) |
| Makerfabs MaTouch ESP32-S3 Rotary IPS 2.1 Round | Mabee I2C (J1): SDA 17, SCL 18Same I2C bus as the touch controller (CST826) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT 4.3" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT with Touch 5.0" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Makerfabs MaTouch ESP32-S3 Parallel TFT with Touch 7.0" | Mabee I2C: SDA 17, SCL 18Same I2C bus as the touch controller (GT911) |
| Sunton ESP32-4827S043R/C 4.3" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S043C 4.3" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S050C 5.0" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Sunton ESP32-8048S070C 7.0" | I2C header (P3): SDA 19, SCL 20Same I2C bus as the GT911 touch controller. P3 also carries GPIO 17 / 18. |
| Waveshare ESP32-S3 Touch LCD 2.1" round | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3 Touch LCD 2.8" | I2C connector: SDA 11, SCL 10Same I2C bus as the RTC and the IMU (0x51, 0x6B, 0x7E taken) — the touch has its own bus |
| Waveshare ESP32-S3 Touch LCD 2.8" Type B | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3-Touch-LCD-2.8C | I2C connector: SDA 15, SCL 7Same I2C bus as the touch controller, the IO expander, the RTC and the IMU (addresses 0x15/0x5D, 0x20, 0x51, 0x6B, 0x7E taken) |
| Waveshare ESP32-S3-LCD-7B | I2C connector (H1): SDA 8, SCL 9Same I2C bus as the touch controller (GT911) and the IO expander (CH422G) |
Good to know
- eCO2 is an estimate, not real CO2.
- Allow warm-up time.
- Add a humidity sensor for compensation.
- Check the address and supply on your module.
What it is good for
- Air quality trend displays
- Kitchen and workshop monitors
- Replacing a CCS811
FAQ
Does the ENS160 measure real CO2?
No. eCO2 is calculated. Use an SCD40 for real CO2.
Is the ENS160 a CCS811 replacement?
Yes, vendors list it as the replacement, but code and registers differ.
Why are the first values wrong?
The sensor needs a warm-up period.
Sources
Related parts
Use this information at your own risk. It is for general guidance only: the example code compiles, but it has not been tested on every part, and modules differ between sellers. Check the manufacturer's data before you build. Product names belong to their owners, and LCD Ultra Pro is not affiliated with them. See our Terms.