Skip to main content
Raspberry Pi HATs

Building an I2C Environmental Sensor HAT

Overview

This tutorial walks through a compact Raspberry Pi HAT that reads temperature, humidity, and barometric pressure with a BME280 sensor. The design uses the Raspberry Pi I2C bus, includes the required pull-up resistors, and exposes an optional OLED display header that can share the same bus.

Circuit Goals

The HAT should:

  • Power the BME280 VDD and VDDIO pins from the Raspberry Pi 3.3V rail
  • Connect BME280 SDI and SCK to GPIO2 and GPIO3 for I2C data and clock
  • Add 4.7k pull-up resistors on both I2C lines
  • Add local decoupling near the sensor
  • Provide a 4-pin expansion header for an optional I2C OLED display

Bill of Materials

ReferencePartValueNotes
U1BME280 environmental sensorI2C mode, 0x76 addressTemperature, humidity, and pressure
R1, R2Pull-up resistors4.7kSDA and SCL to 3.3V
C1Bypass capacitor100nFPlace close to U1
C2Bulk capacitor1uFStabilizes the local 3.3V rail
J1Pin header1x4, 2.54mmOptional OLED or external I2C device

Step 1: Add the HAT and Sensor

Start with the Raspberry Pi HAT template and place the BME280. The BME280 can run in SPI mode, but this design ties it into I2C mode by using SDI as SDA and SCK as SCL. Tie CSB high so the sensor uses I2C, then tie SDO low for address 0x76.

Schematic Circuit Preview

Step 2: Wire Power and I2C

Connect the sensor to the Raspberry Pi 3.3V rail, ground, and I2C pins. On the 40-pin Raspberry Pi header, GPIO2 is SDA and GPIO3 is SCL; on the BME280 those signals land on SDI and SCK.

<trace from=".HAT1_chip .V3_3_1" to=".U1 .VDD" />
<trace from=".HAT1_chip .V3_3_1" to=".U1 .VDDIO" />
<trace from=".HAT1_chip .GND_1" to=".U1 .GND" />
<trace from=".HAT1_chip .GND_1" to=".U1 .GND2" />
<trace from=".HAT1_chip .GPIO_2" to=".U1 .SDI" />
<trace from=".HAT1_chip .GPIO_3" to=".U1 .SCK" />
<trace from=".U1 .CSB" to=".U1 .VDDIO" />
<trace from=".U1 .SDO" to=".HAT1_chip .GND_1" />

Step 3: Add Pull-Up Resistors

I2C uses open-drain signaling, so SDA and SCL need pull-ups. Use 4.7k resistors for a short HAT trace length at standard 100 kHz or 400 kHz bus speeds.

Schematic Circuit Preview

Step 4: Add Decoupling

Place the 100nF capacitor close to VDDIO and ground. The 1uF capacitor can sit nearby between VDD and ground to absorb slower supply changes on the 3.3V rail.

Step 5: Add an Optional OLED Header

Many small SSD1306 OLED modules use the same four-pin I2C layout: VCC, GND, SDA, and SCL. The header can share the same bus as long as every device has a unique I2C address.

<pinheader
name="J1"
pinCount={4}
pitch="2.54mm"
pinLabels={["VCC", "GND", "SDA", "SCL"]}
/>
<trace from=".J1 .VCC" to=".HAT1_chip .V3_3_1" />
<trace from=".J1 .GND" to=".HAT1_chip .GND_1" />
<trace from=".J1 .SDA" to=".U1 .SDI" />
<trace from=".J1 .SCL" to=".U1 .SCK" />

PCB Layout Guidance

  • Keep the BME280 away from Raspberry Pi heat sources and voltage regulators.
  • Place C1 within a few millimeters of the BME280 VCC and GND pins.
  • Route SDA and SCL as short, direct traces and keep them away from noisy power switching areas.
  • Put the optional OLED header near the edge of the HAT so a display cable can leave the enclosure cleanly.
  • Add silkscreen labels for 3V3, GND, SDA, and SCL next to J1.

Raspberry Pi Setup

Enable I2C on the Raspberry Pi:

sudo raspi-config

Then choose Interface Options, enable I2C, and reboot if prompted.

Install the common Python libraries:

sudo apt update
sudo apt install -y python3-pip i2c-tools
pip3 install adafruit-circuitpython-bme280

Confirm the sensor appears on the bus:

i2cdetect -y 1

Most BME280 breakouts use address 0x76 or 0x77.

Firmware Examples

Use the example that matches your host board. The same SDA, SCL, 3.3V, and GND connections apply as long as the controller uses 3.3V I2C signaling.

Raspberry Pi Python

import time

import board
import busio
from adafruit_bme280 import basic as adafruit_bme280

i2c = busio.I2C(board.SCL, board.SDA)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)

bme280.sea_level_pressure = 1013.25

while True:
print(f"Temperature: {bme280.temperature:.1f} C")
print(f"Humidity: {bme280.relative_humidity:.1f} %")
print(f"Pressure: {bme280.pressure:.1f} hPa")
print(f"Altitude estimate: {bme280.altitude:.1f} m")
print()
time.sleep(2)

If 0x76 does not work, rerun the script with address=0x77.

CircuitPython Microcontroller

For a CircuitPython board such as a Raspberry Pi Pico, install the Adafruit BME280 library bundle and save this as code.py:

import time

import board
import busio
from adafruit_bme280 import basic as adafruit_bme280

i2c = busio.I2C(board.SCL, board.SDA)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)

while True:
print("Temperature: {:.1f} C".format(bme280.temperature))
print("Humidity: {:.1f} %".format(bme280.relative_humidity))
print("Pressure: {:.1f} hPa".format(bme280.pressure))
print()
time.sleep(2)

Arduino or ESP32

Install the Adafruit BME280 Library and Adafruit Unified Sensor packages from the Arduino Library Manager, then upload:

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

Adafruit_BME280 bme;

void setup() {
Serial.begin(115200);
Wire.begin();

if (!bme.begin(0x76)) {
Serial.println("BME280 not found, try address 0x77");
while (true) delay(10);
}
}

void loop() {
Serial.print("Temperature: ");
Serial.print(bme.readTemperature());
Serial.println(" C");

Serial.print("Humidity: ");
Serial.print(bme.readHumidity());
Serial.println(" %");

Serial.print("Pressure: ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");

delay(2000);
}

Testing Checklist

Before mounting the HAT on a Raspberry Pi:

  1. Check for shorts between 3.3V and GND with a multimeter.
  2. Confirm SDA and SCL each measure about 4.7k to 3.3V.
  3. Inspect the BME280 orientation and solder joints under magnification.
  4. Power the HAT from the Pi and run i2cdetect -y 1.
  5. Run the Python script and compare readings against a known room thermometer.

Next Improvements

You can extend this board with:

  • A small OLED display on J1 for local readings
  • A second I2C header for daisy-chaining other sensors
  • Mounting holes aligned with a weather-shield enclosure
  • A vent cutout near the BME280 to improve airflow