Temperature, humidity and air

AHT20 with ESP32

If a DHT22 gives you timing errors, the AHT20 is the cheap fix. It reports temperature and humidity over I2C, so there is no critical timing, and it shares two wires with whatever else is on the bus. That matters on a display board with only one spare connector.

Vendors quote ±0.3 °C and ±2 % RH at 25 °C, while some resellers give a looser ±1 °C and ±3 % across the full range. Assume the looser figure away from room temperature. The address is fixed at 0x38, which has two practical results: you cannot put two on one bus, and on a touch panel you should run a scan first in case another chip already uses it.

Many small modules combine an AHT20 with a BMP280 on one board, and those give you pressure too, at a second address. Put the sensor away from the display and regulator, because a warm board reads warm and that makes the humidity read low. Humidity sensors also dislike condensation, so keep it out of places where water forms. The code below talks to the chip directly, with no library: it sends a measure command, waits 80 ms and decodes the bytes. It also checks the busy flag and the CRC byte.

AHT20 wiring diagram for ESP32
Wiring diagram drawn by LCD Ultra Pro.

Specifications

MeasuresTemperature, humidity
Quoted accuracy±0.3 °C, ±2 % RH at 25 °C
InterfaceI2C
I2C address0x38, fixed
Measurement timeAbout 80 ms

Power and logic levels

3.3 V: safe to connect directly

Supply2.2–5.5 V, use 3V3
Signal levelI2C follows the supply
On an ESP32Run it from 3V3, so the I2C lines stay at 3.3 V.

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

AHT20Connect toNote
VIN / VCC3V3
GNDGND
SDAGPIO 8Any free pin, set in Wire.begin()
SCLGPIO 9Any free pin, set in Wire.begin()

Arduino code

#include <Wire.h>

const int SDA_PIN = 8, SCL_PIN = 9;
const uint8_t ADDR = 0x38;

void command(uint8_t a, uint8_t b, uint8_t c) {
  Wire.beginTransmission(ADDR);
  Wire.write(a); Wire.write(b); Wire.write(c);
  Wire.endTransmission();
}

uint8_t crc8(const uint8_t *b, int n) {   // polynomial 0x31, start 0xFF
  uint8_t c = 0xFF;
  while (n--) {
    c ^= *b++;
    for (int i = 0; i < 8; i++) c = (c & 0x80) ? (c << 1) ^ 0x31 : c << 1;
  }
  return c;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN, SCL_PIN);
  delay(40);
  command(0xBE, 0x08, 0x00);              // calibrate
  delay(10);
}

void loop() {
  command(0xAC, 0x33, 0x00);              // start measuring
  delay(80);
  uint8_t b[7];
  Wire.requestFrom(ADDR, (uint8_t)7);
  for (int i = 0; i < 7; i++) b[i] = Wire.read();
  if ((b[0] & 0x80) || crc8(b, 6) != b[6]) {   // busy or damaged
    Serial.println("No valid reading");
  } else {
    uint32_t rh = ((uint32_t)b[1] << 12) | (b[2] << 4) | (b[3] >> 4);
    uint32_t t = ((uint32_t)(b[3] & 0x0F) << 16) | (b[4] << 8) | b[5];
    Serial.printf("%.2f C  %.1f %%\n", t * 200.0 / 1048576 - 50, rh * 100.0 / 1048576);
  }
  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 AHT20

Boards 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 RoundMabee 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" roundI2C 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 BI2C 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.8CI2C 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-7BI2C connector (H1): SDA 8, SCL 9Same I2C bus as the touch controller (GT911) and the IO expander (CH422G)

Good to know

  • The address 0x38 cannot be changed.
  • Accuracy is looser away from room temperature.
  • Keep it away from warm parts.
  • Avoid condensation.

What it is good for

  • Room climate displays
  • Replacing a DHT22
  • Sharing an I2C connector

FAQ

Is the AHT20 better than a DHT22?

It avoids the DHT22's timing-sensitive protocol, because it uses I2C.

Can I use two AHT20 sensors?

Not on one bus, since the address is fixed.

Why does it read too warm?

Heat from the display or regulator. Move it away.

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.