AHT20
A low-cost Aosong AHT20 breakout that reads relative humidity and temperature over I2C at the fixed address 0x38.
- Supply voltage2.2 V to 5.5 V
- Absolute maximum supply-0.3 V to 5.5 V on VDD
- I2C address0x38, fixed
- I2C clock10 kHz to 400 kHz

The AHT20 is Aosong's calibrated I2C humidity and temperature sensor: ±2 %RH and ±0.3 °C typical, running from 2.2 V to 5.5 V (Aosong AHT20 datasheet V1.0, pp. 2, 9). It answers only at I2C address 0x38 and needs 100 ms after power-up and 80 ms per measurement (pp. 11 to 12). On the Adafruit breakout the SDA and SCL pull-ups follow VIN, so power it from 5V on an Uno and from 3.3 V on an ESP32 or Pico.
The AHT20 is the successor to the AHT10 and one of the cheapest calibrated digital humidity sensors makers can buy. It reports humidity with 0.024 %RH resolution and a typical accuracy of ±2 %RH, and temperature with 0.01 °C resolution and ±0.3 °C typical accuracy over -40 to 85 °C (Aosong datasheet V1.0, p. 2). Its normal humidity range is 0 to 80 %RH; long stays above 80 %RH shift the reading for a while (p. 3).
The chip runs from 2.2 V to 5.5 V and wants its I2C pull-ups tied to its own supply (p. 9). The Adafruit breakout does exactly that: VIN feeds the chip and the 10 kΩ pull-ups on SDA and SCL, so the bus runs at whatever voltage you give VIN (Adafruit AHT20 guide, p. 5). The address is fixed at 0x38 and cannot be changed (p. 4 of the guide).
A reading is a request: the host sends the 0xAC trigger, waits 80 ms, then reads six data bytes (datasheet, p. 12). The Adafruit_AHTX0 library hides that sequence and the status-word calibration check behind one getEvent() call.
AHT20 pinout
| Pin | Function | Type |
|---|---|---|
| VIN | VIN2.7 V to 5.5 V, equal to your board's logic level | power in |
| GND | GND | power in |
| SCL | I2C SCLPulled up to VIN through 10 kΩ | input |
| SDA | I2C SDAPulled up to VIN through 10 kΩ | bidirectional |
How to wire the AHT20
Pin by pin, for the board you have.
| AHT20 | Arduino Uno | Note |
|---|---|---|
| VIN | 5V | |
| GND | GND | |
| SCL | A5 | |
| SDA | A4 |
| AHT20 | ESP32 DevKit V1 | Note |
|---|---|---|
| VIN | 3V3 | |
| GND | GND | |
| SCL | GPIO22 (D22) | |
| SDA | GPIO21 (D21) |
- Power VIN from 3V3 only: the pull-ups follow VIN, so 5 V on VIN would put 5 V on GPIO21 and GPIO22.
| AHT20 | Raspberry Pi Pico | Note |
|---|---|---|
| VIN | 3V3(OUT) | |
| GND | GND | |
| SCL | GP5 | |
| SDA | GP4 |
- Power VIN from 3V3(OUT) only, never VBUS: the pull-ups follow VIN and the Pico's pins are not 5 V tolerant.
AHT20 code
Every example here compiles for its board.
// AHT20 on Arduino Uno: wiring and a free simulation at https://www.tinkered.ai/components/aht20
// AHT20 on an Arduino Uno: VIN 5V, SDA A4, SCL A5, address 0x38
#include <Wire.h>
#include <Adafruit_AHTX0.h>
Adafruit_AHTX0 aht;
void setup() {
Serial.begin(115200);
Wire.begin();
delay(100); // the AHT20 needs 100 ms after power-up before its first command
if (!aht.begin()) {
Serial.println("AHT20 not found at 0x38. Check wiring.");
while (true) delay(10);
}
Serial.println("AHT20 ready");
}
void loop() {
sensors_event_t humidity, temp;
if (!aht.getEvent(&humidity, &temp)) {
Serial.println("Read failed");
} else {
Serial.print("Temperature: ");
Serial.print(temp.temperature, 2);
Serial.print(" C Humidity: ");
Serial.print(humidity.relative_humidity, 2);
Serial.println(" %RH");
}
delay(2000);
}
Compiled for arduino:avr:uno on 2026-10-30// AHT20 on ESP32 DevKit V1: wiring and a free simulation at https://www.tinkered.ai/components/aht20
// AHT20 on an ESP32 DevKit V1: VIN 3V3, SDA GPIO21, SCL GPIO22, address 0x38
#include <Wire.h>
#include <Adafruit_AHTX0.h>
Adafruit_AHTX0 aht;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL
delay(100); // the AHT20 needs 100 ms after power-up before its first command
if (!aht.begin(&Wire)) {
Serial.println("AHT20 not found at 0x38. Check wiring.");
while (true) delay(10);
}
Serial.println("AHT20 ready");
}
void loop() {
sensors_event_t humidity, temp;
if (!aht.getEvent(&humidity, &temp)) {
Serial.println("Read failed");
} else {
Serial.print("Temperature: ");
Serial.print(temp.temperature, 2);
Serial.print(" C Humidity: ");
Serial.print(humidity.relative_humidity, 2);
Serial.println(" %RH");
}
delay(2000);
}
Compiled for esp32:esp32:esp32 on 2026-10-30// AHT20 on Raspberry Pi Pico: wiring and a free simulation at https://www.tinkered.ai/components/aht20
// AHT20 on a Raspberry Pi Pico (arduino-pico): VIN 3V3(OUT), SDA GP4, SCL GP5, address 0x38
#include <Wire.h>
#include <Adafruit_AHTX0.h>
Adafruit_AHTX0 aht;
void setup() {
Serial.begin(115200);
Wire.setSDA(4);
Wire.setSCL(5);
Wire.begin();
delay(100); // the AHT20 needs 100 ms after power-up before its first command
if (!aht.begin(&Wire)) {
Serial.println("AHT20 not found at 0x38. Check wiring.");
while (true) delay(10);
}
Serial.println("AHT20 ready");
}
void loop() {
sensors_event_t humidity, temp;
if (!aht.getEvent(&humidity, &temp)) {
Serial.println("Read failed");
} else {
Serial.print("Temperature: ");
Serial.print(temp.temperature, 2);
Serial.print(" C Humidity: ");
Serial.print(humidity.relative_humidity, 2);
Serial.println(" %RH");
}
delay(2000);
}
Compiled for rp2040:rp2040:rpipico on 2026-10-30Before you wire it
- Power VIN at your board's logic voltage: the datasheet requires the SDA and SCL pull-ups to come from VDD (p. 9), and on the breakout they come from VIN.
- Wait at least 100 ms after power-up before the first command (datasheet, p. 11).
- Keep the I2C clock between 10 kHz and 400 kHz (p. 7).
- Plan the bus around 0x38: it is fixed, so two AHT20s need a multiplexer or two I2C buses.
- Never exceed 5.5 V on VIN, which is both the top of the supply range and the absolute maximum (pp. 9 to 10).
- Never poll faster than the 80 ms a measurement takes (p. 12); reading every second or two also keeps self-heating down.
- Never put a second sensor at 0x38 (another AHT, some AHT21 combo boards) on the same bus.
- The breakout carries the 10 kΩ pull-ups and the decoupling the datasheet asks for (p. 9), so no extra parts are needed on a short cable.
- The breakout's power LED can be disabled on Rev C boards by cutting the jumper beside it (Adafruit guide, p. 5); worthwhile on a coin-cell logger.
- Long exposure above 80 %RH offsets the humidity reading by around +3 %RH after 60 hours; it recovers back in the normal range (datasheet, p. 3).
- Mount the sensor away from the regulator and microcontroller and in moving air; humidity takes 8 s to reach 63 % of a step (p. 2).
Specifications
- Supply voltage
- 2.2 V to 5.5 VAosong AHT20 datasheet V1.0 (May 2021), pp. 9, 11
- Absolute maximum supply
- -0.3 V to 5.5 V on VDDAosong AHT20 datasheet V1.0 (May 2021), p. 10
- I2C address
- 0x38, fixedAosong AHT20 datasheet V1.0 (May 2021), p. 12
- I2C clock
- 10 kHz to 400 kHzAosong AHT20 datasheet V1.0 (May 2021), p. 7
- Humidity
- ±2 %RH typical, 0.024 %RH resolution, 8 s response (63 %)Aosong AHT20 datasheet V1.0 (May 2021), p. 2
- Temperature
- ±0.3 °C typical, 0.01 °C resolution, -40 to 85 °CAosong AHT20 datasheet V1.0 (May 2021), p. 2
- Timing
- At least 100 ms after power-up; 80 ms from trigger (0xAC) to dataAosong AHT20 datasheet V1.0 (May 2021), pp. 11 to 12
- Pull-up voltage
- SDA and SCL pull-ups must be powered from VDDAosong AHT20 datasheet V1.0 (May 2021), p. 9
- Breakout
- VIN 2.7 V to 5.5 V; 10 kΩ pull-ups on SDA and SCL at the VIN levelAdafruit AHT20 breakout guide (PDF), pp. 4 to 5
From the AHT20 data sheet V1.0, published by Aosong (ASAIR).
Common mistakes
Treating the AHT10 as the same chip
The AHT10 and AHT20 share the 0x38 address but not every command, and the AHT10 is a 3.3 V part. Use a library that names the AHT20 (Adafruit_AHTX0 supports both) and don't feed an AHT10 board 5 V unless it has a regulator.
Address collision on combo boards
Popular ENS160 + AHT21 and AHT20 + BMP280 combo boards already have an AHT at 0x38. Adding a separate AHT20 to the same bus makes both answer at once and the readings turn to garbage.
Reading right after power-up
A sketch that powers the sensor from a GPIO pin and reads immediately gets a failed begin(). The chip needs 100 ms to reach its idle state (datasheet, p. 11).
Feeding a 3.3 V board's bus from 5 V
There is no level shifter: VIN sets the bus voltage. On an ESP32 or Pico, VIN goes to 3.3 V.
AHT20 questions
What is the AHT20's I2C address?
0x38, and it cannot be changed (Aosong datasheet V1.0, p. 12; Adafruit guide, p. 4). Two AHT20s on one bus need an I2C multiplexer such as a TCA9548A, or a second I2C port.
Is the AHT20 5 V or 3.3 V?
Both: the chip runs from 2.2 V to 5.5 V (p. 9). The catch is the bus: its pull-ups must come from the same supply, so power it at your board's logic level.
AHT20 or SHT31?
Both give ±2 %RH typical. The SHT31 is tighter on temperature (±0.2 °C against ±0.3 °C), faster (15 ms against 80 ms per reading) and has two addresses; the AHT20 costs less and is fine for room monitoring.
AHT20 or DHT22?
The AHT20 uses standard I2C, so it shares a bus with displays and other sensors and needs no timing-critical code; the DHT22 needs its own pin and a one-wire protocol. The AHT20 also takes a reading in 80 ms (p. 12).
Which library should I use?
Adafruit_AHTX0 (PlatformIO: adafruit/Adafruit AHTX0) handles the trigger, the 80 ms wait and the calibration check, and returns temperature and humidity as Unified Sensor events.
Build something with the AHT20
- Build this
Desk thermo-hygrometer
Show temperature and humidity on a small OLED with a comfort indicator.
- Build this
Wi-Fi humidity logger
Post readings every five minutes to a web dashboard.
- Build this
Plant shelf monitor
Warn with an LED when the air around seedlings gets too dry.
This page as plain Markdown, for ChatGPT, Claude or your own agent. /components/aht20.md