Articles › Microcontrollers and code

Read temperature, humidity and pressure from a BME280 over I2C with an ESP32

Wire the two I2C lines, find the address with an I2C scanner, read values with the Adafruit library, tell a BME280 from a look-alike BMP280, and why it reads too warm.

Beginner 5 min read October 16, 2026 Your first maker project · lesson 2/3

What you will learn
  • I2C uses two data lines (SDA, SCL) shared by several devices, each with its own address.
  • Run an I2C scanner before the real code and you know at once whether the wiring is right and which address the device uses.
  • Many cheap modules sold as BME280 carry a BMP280, which has no humidity sensor.
  • A sensor sitting next to the board reads too warm, because of the board's own heat.

The BME280 is a small sensor that measures three things: temperature, relative humidity and air pressure. It talks to a microcontroller over I2C, which needs only two data wires. It makes an excellent first sensor, because in one go you learn a bus protocol, a library, and a real-world problem: heat from your own board.

The examples use an ESP32 DevKit (ESP32-WROOM-32) with Arduino IDE 2 and a common small BME280 breakout board, following on from the previous article on GPIO.

How I2C works

I2C uses two lines: SDA carries data and SCL carries the clock. The ESP32 is in charge. Every device hangs in parallel on the same pair of wires, and each has its own number, its address. The ESP32 calls an address and that device answers.

The lines need pull-up resistors to the supply, because devices can only pull a line low. Most ready-made modules already have pull-ups on board, so you normally add nothing.

ESP32GPIO21GPIO22SDASCL3V3pull-upBME2800x76OLED0x3C
Every device sits in parallel on one pair of wires, told apart by its address (power and ground not drawn)

Wiring

BME280 module pinESP32
VIN (or VCC)3V3
GNDGND
SCLGPIO22
SDAGPIO21

GPIO21 and GPIO22 are the default I2C pins for the original ESP32 in the Arduino core. On an ESP32-S3 or C3 the defaults differ, so check your board’s pinout.

About power: some modules have a voltage regulator and accept 5 V; others do not, and 5 V can kill the sensor. The BME280 chip itself is rated up to 3.6 V. 3V3 is safe for both kinds, so when in doubt, use 3V3.

Step one: find the address with an I2C scanner

Before writing the real code, check that the wiring works and which address the sensor uses. This sketch calls every address and prints the ones that answer.

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);  // SDA, SCL
  for (uint8_t addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.printf("found device at 0x%02X\n", addr);
    }
  }
  Serial.println("scan done");
}

void loop() {}

Open the Serial Monitor at 115200 baud. You should see 0x76 or 0x77, depending on whether the chip’s SDO pin is tied to ground (0x76) or to the supply (0x77). Many common modules use 0x76. If nothing shows up at all, the problem is the wiring, not the code.

Read values with the Adafruit library

In Arduino IDE, open the Library Manager, search for Adafruit BME280 Library and install it, accepting the libraries it depends on (Adafruit Unified Sensor and Adafruit BusIO).

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Adafruit_BME280 bme;

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);           // SDA, SCL
  if (!bme.begin(0x76)) {       // try 0x77 if this fails
    Serial.println("BME280 not found: check wiring and address");
    while (true) delay(1000);
  }
}

void loop() {
  float t = bme.readTemperature();          // °C
  float h = bme.readHumidity();             // %RH
  float p = bme.readPressure() / 100.0F;    // Pa -> hPa
  Serial.printf("T=%.2f C  H=%.1f %%  P=%.1f hPa\n", t, h, p);
  delay(2000);
}

readPressure() returns pascals; divide by 100 for hPa, the unit weather forecasts use. Standard sea-level pressure is 1013.25 hPa; real readings vary with your altitude and the weather.

The scanner finds it but the library does not

This one is very common. The number one cause: the chip on the module is a BMP280, not a BME280. They share addresses and pinouts, some shops mix them up, but the BMP280 has no humidity sensor.

The library reads the chip ID before starting: 0x60 for a BME280, 0x58 for a BMP280. Anything other than 0x60 and begin() returns false. You can tell them apart by eye too: the BME280 is a square package of about 2.5 × 2.5 mm, the BMP280 a rectangle of about 2.0 × 2.5 mm. If you have a BMP280, use the Adafruit BMP280 library instead; you get temperature and pressure only.

Why the temperature reads too high

The BME280 is accurate enough for general use, but it measures the temperature where it is. Mounted right next to an ESP32, which warms up while running, especially with Wi-Fi on, it reads above the room. And because relative humidity depends on temperature, the humidity reading comes out low as well.

A temperature sensor measures the heat around it, including the heat of the board it sits on.

What works:

  • Put the sensor on wires, away from the main board.
  • In a case, give the sensor its own vented compartment, separate from the ESP32.
  • Read less often, or put the ESP32 into deep sleep between readings, so the board stays cooler.

Common mistakes

SymptomUsual causeFix
Scanner finds nothingSDA and SCL swapped, or GND missingCheck each wire
Finds 0x77, code uses 0x76Address mismatchChange to bme.begin(0x77)
Scanner finds it, begin() failsIt is a BMP280Use the BMP280 library
Reads work, then fail at randomWires too long or looseShorter wires, firm connections
Temperature several degrees above the roomHeat from the boardMove the sensor away

Next, we design a 3D-printed case for this board, with a vented pocket that keeps the sensor away from the ESP32’s heat.

FAQ

Can I use pins other than GPIO21 and GPIO22?

Yes. The ESP32 can put I2C on almost any output-capable pin; pass them to Wire.begin(SDA, SCL). Avoid the strapping pins and the input-only pins 34–39.

How long can the wires be?

I2C was designed for short runs on one board. A few tens of centimetres usually works. Longer than that, reads start failing, and you need a lower clock speed, suitable pull-ups or a different way to carry the signal.

Can an OLED and the BME280 share the same two wires?

Yes. A typical SSD1306 OLED sits at address 0x3C and the BME280 at 0x76 or 0x77, so they do not clash. Wire them in parallel on SDA and SCL.

Circuits and code were tested on sample boards. Boards differ in pin layout, so check your board's pinout before you power anything.