Building an I2C Environmental Sensor Module HAT
Overview
This tutorial walks through a compact Raspberry Pi HAT for environmental sensing. The board uses a BME280 sensor for temperature, humidity, and barometric pressure, adds the I2C pull-up resistors the bus needs, exposes the same bus on a four-pin header, and leaves room for an optional SSD1306 OLED display.
What You Are Building
The HAT has four useful sections:
- BME280 sensor for temperature, humidity, and pressure
- 4.7 kOhm pull-up resistors on SDA and SCL
- 100 nF and 1 uF bypass capacitors near the sensor power pins
- Shared I2C expansion header for an external cable or optional OLED display
The Raspberry Pi already exposes I2C on GPIO2 and GPIO3. This board simply makes that bus reliable and easier to attach to a small sensor module.
Bill of Materials
| Reference | Part | Value or package | Notes |
|---|---|---|---|
| U1 | BME280 | 8-pin sensor module or bare IC breakout | Measures temperature, humidity, and pressure |
| R1, R2 | Resistors | 4.7 kOhm, 0402 or 0603 | I2C pull-ups to 3.3 V |
| C1 | Capacitor | 100 nF | Local high-frequency decoupling |
| C2 | Capacitor | 1 uF | Local bulk decoupling |
| J1 | Pin header | 1x4, 2.54 mm | 3V3, GND, SDA, SCL expansion |
| OLED1 | Optional OLED header | 1x4, 2.54 mm | For SSD1306-style I2C OLED modules |
Step 1: Start With the HAT Board
Use RaspberryPiHatBoard so the outline and 40-pin header match the Raspberry Pi HAT shape.
import { RaspberryPiHatBoard } from "@tscircuit/common"
export default () => (
<RaspberryPiHatBoard name="HAT1">
{/* Sensor circuit goes here */}
</RaspberryPiHatBoard>
)
Step 2: Add the BME280
The BME280 can operate over I2C or SPI. For this HAT we use I2C mode, so the important pins are SDA, SCL, VDD, VDDIO, and GND.
Step 3: Connect Power and I2C
Connect the sensor to the Raspberry Pi 3.3 V rail and use GPIO2/GPIO3 for I2C. The Pi names those pins SDA1 and SCL1 in many pinout diagrams.
Step 4: Add Pull-Ups and Decoupling
I2C is an open-drain bus, so SDA and SCL need pull-up resistors. 4.7 kOhm is a good starting value for a short HAT trace. Place the bypass capacitors close to the BME280 power pins.
Step 5: Add the Expansion Header and OLED Option
The same I2C bus can leave the HAT through a small header. Keep the order friendly for jumper wires: power, ground, data, clock. The optional OLED header uses the same four nets.
<pinheader
name="J1"
pinCount={4}
pinLabels={["3V3", "GND", "SDA", "SCL"]}
schFacingDirection="right"
/>
<chip
name="OLED1"
manufacturerPartNumber="SSD1306 OLED header"
footprint="pinrow4"
pinLabels={{
pin1: "GND",
pin2: "VCC",
pin3: "SCL",
pin4: "SDA",
}}
/>
PCB Layout Notes
- Place the BME280 near a board edge and away from hot parts such as regulators.
- Put C1 and C2 close to the BME280 power pins.
- Keep SDA and SCL short and route them as a pair where practical.
- Avoid copper pours directly under the sensing port if your BME280 package exposes one.
- Put the optional OLED header on an edge so a display can sit above or beside the HAT.
Raspberry Pi Test Code
Enable I2C with raspi-config, then install the Python libraries:
python3 -m pip install adafruit-circuitpython-bme280
Create read_bme280.py:
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)
print(f"Temperature: {bme280.temperature:.1f} C")
print(f"Humidity: {bme280.relative_humidity:.1f} %")
print(f"Pressure: {bme280.pressure:.1f} hPa")
Run it:
python3 read_bme280.py
If the script cannot find the sensor, run i2cdetect -y 1. Most BME280 boards appear at 0x76 or 0x77.
Bring-Up Checklist
- Confirm 3.3 V is present on the sensor before inserting the BME280.
- Confirm SDA and SCL each measure high when idle.
- Run
i2cdetect -y 1and check for0x76or0x77. - Read temperature, humidity, and pressure for at least one minute.
- Plug in the optional OLED only after the BME280 works by itself.
Common Fixes
- No I2C address: check the HAT orientation and confirm I2C is enabled.
- Address is
0x77instead of0x76: update the Pythonaddressparameter. - Readings drift upward: move the sensor farther from heat sources or board regulators.
- OLED works but BME280 disappears: lower the pull-ups to 3.3 kOhm or shorten the I2C cable.