An AHT20 + BMP280 Weather Station: Temperature, Humidity, Pressure and Dew Point
The AHT20+BMP280 module costs as much as a single BME280 and measures the same things. We put both sensors on one I2C bus, calculate the dew point, and see why the BME280 library cannot see a BMP280 and vice versa.
About the project
A BME280 measures temperature, humidity and pressure in one chip, but it costs more, and fakes are common — a BMP280 labelled "BME280". A popular alternative is a board with two chips: an AHT20 (temperature and humidity) and a BMP280 (pressure and temperature). Both share one I2C bus, so the wiring stays the same.
What you need
- ESP32 DevKit
- An AHT20 + BMP280 module (or two separate modules)
- 4 wires
Wiring
| Module | ESP32 |
|---|---|
| VIN / VCC | 3V3 |
| GND | GND |
| SDA | GPIO21 |
| SCL | GPIO22 |
The combo module already has I2C pull-up resistors. The addresses differ: the AHT20 is 0x38 and the BMP280 is 0x77 (standalone purple BMP280 modules are often 0x76, because SDO is tied to GND). If unsure, run an I2C scanner.
BMP280 or BME280
The chips look alike and share the temperature and pressure registers, but their Chip ID differs: 0x58 for the BMP280, 0x60 for the BME280. The libraries check it in begin():
Adafruit_BME280returnsfalseon a BMP280 — that chip simply has no humidity sensor;Adafruit_BMP280returnsfalseon a BME280 too, although you can callbmp.begin(0x76, 0x60).
The simulator reproduces both cases and says in the Problems panel which chip it found.
Dew point
Relative humidity alone says little: 60 % at 25 °C and at 5 °C are very different amounts of water. The dew point is the temperature at which that water starts to condense. The Magnus formula gives it:
γ = ln(RH/100) + a·T/(b + T), Td = b·γ/(a − γ), with a = 17.62 and b = 243.12 °C.
If a wall or a window is colder than the dew point, it gets damp and eventually mouldy.
Code
// Weather station: AHT20 (temperature, humidity) + BMP280 (pressure) on one I2C bus
#include <Wire.h>
#include <Adafruit_AHTX0.h>
#include <Adafruit_BMP280.h>
Adafruit_AHTX0 aht;
Adafruit_BMP280 bmp;
float dewPoint(float t, float rh) { // Magnus formula
const float a = 17.62, b = 243.12;
float g = log(rh / 100.0) + a * t / (b + t);
return b * g / (a - g);
}
void setup() {
Serial.begin(115200);
Wire.begin();
if (!aht.begin()) {
Serial.println("AHT20 not found");
while (true) delay(1000);
}
if (!bmp.begin(0x77)) { // a standalone BMP280 module is usually 0x76
Serial.println("BMP280 not found");
while (true) delay(1000);
}
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, Adafruit_BMP280::SAMPLING_X2,
Adafruit_BMP280::SAMPLING_X16, Adafruit_BMP280::FILTER_X16,
Adafruit_BMP280::STANDBY_MS_500);
}
void loop() {
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp); // a measurement takes ~80 ms
float t = temp.temperature;
float rh = humidity.relative_humidity;
float hpa = bmp.readPressure() / 100.0;
Serial.printf("%.1f °C %.0f %% dew point %.1f °C %.1f hPa\n", t, rh, dewPoint(t, rh), hpa);
delay(2000);
}
Pressure and the weather
The absolute pressure depends on your altitude, so forecasts look at the change over 3 hours: a drop of more than 3 hPa means the weather is getting worse, a rise means it is improving. Store a reading every 10 minutes in a ring buffer of 18 values and compare the newest with the oldest.
Tips
- Take the temperature from the AHT20. The BMP280 sits right next to it and warms up with the board; its thermometer is mostly there to compensate the pressure reading.
- Keep the board away from the ESP32. Wi-Fi warms the chip by 2–5 °C — put the module on a wire or in a separate vented compartment.
- Altitude.
bmp.readAltitude(1013.25)uses standard pressure. For accurate metres, pass the current sea-level pressure from the nearest weather station.