Building an I2C Environmental Sensor Module
Overview
This tutorial builds a compact I2C environmental sensor module around the BME280. The board exposes power, ground, SDA, and SCL on a host header, adds the pull-up resistors required by I2C, includes local decoupling near the sensor, and provides an optional second I2C header for a small OLED display.
The module is intended for 3.3 V systems. Many BME280 parts are not 5 V tolerant, so only connect the header to a 3.3 V microcontroller or add level shifting before using a 5 V host.
Requirements
- BME280 sensor for temperature, humidity, and barometric pressure.
- 4-pin host header:
GND,VCC,SDA,SCL. - 4.7 kΩ pull-up resistors from
SDAandSCLtoVCC. - 100 nF decoupling capacitor near the BME280 power pins.
- Optional 4-pin OLED header on the same I2C bus.
- Clear silkscreen labels for both headers.
I2C and BME280 integration notes
I2C devices pull the bus low but do not actively drive it high. Pull-up resistors return SDA and SCL to VCC when the bus is idle. For a short 3.3 V board, 4.7 kΩ is a practical default. If the host board already has strong pull-ups, you can mark R1 and R2 as do-not-populate.
The BME280 commonly responds at I2C address 0x76 or 0x77, depending on the state of the SDO address-select pin. Tie SDO to ground for 0x76 or to VCC for 0x77.
Step 1: Add the BME280 sensor
<chip
name="U1"
footprint="lga8_w2.5mm_h2.5mm_p0.65mm"
manufacturerPartNumber="BME280"
pinLabels={{ 1: "GND", 2: "CSB", 3: "SDI", 4: "SCK", 5: "SDO", 6: "VDDIO", 7: "GND", 8: "VDD" }}
/>
CSB is tied high so the part stays in I2C mode. SDI is used as SDA, and SCK is used as SCL.
Step 2: Add headers and pull-ups
<chip name="J1" footprint="pinrow4" manufacturerPartNumber="Host I2C header" pinLabels={{ 1: "GND", 2: "VCC", 3: "SDA", 4: "SCL" }} />
<chip name="J2" footprint="pinrow4" manufacturerPartNumber="Optional OLED header" pinLabels={{ 1: "GND", 2: "VCC", 3: "SDA", 4: "SCL" }} />
<resistor name="R1" resistance="4.7k" footprint="0402" />
<resistor name="R2" resistance="4.7k" footprint="0402" />
Schematic and 3D preview
export default () => (
<board width="35mm" height="25mm">
<chip
name="U1"
footprint="lga8_w2.5mm_h2.5mm_p0.65mm"
manufacturerPartNumber="BME280"
pcbX={0}
pcbY={0}
pinLabels={{ 1: "GND", 2: "CSB", 3: "SDI", 4: "SCK", 5: "SDO", 6: "VDDIO", 7: "GND2", 8: "VDD" }}
/>
<chip name="J1" footprint="pinrow4" manufacturerPartNumber="Host I2C header" pcbX={-13} pcbY={0} pinLabels={{ 1: "GND", 2: "VCC", 3: "SDA", 4: "SCL" }} />
<chip name="J2" footprint="pinrow4" manufacturerPartNumber="Optional OLED header" pcbX={13} pcbY={0} pinLabels={{ 1: "GND", 2: "VCC", 3: "SDA", 4: "SCL" }} />
<resistor name="R1" resistance="4.7k" footprint="0402" pcbX={-5} pcbY={7} />
<resistor name="R2" resistance="4.7k" footprint="0402" pcbX={5} pcbY={7} />
<capacitor name="C1" capacitance="100nF" footprint="0402" pcbX={0} pcbY={7} />
<trace from=".J1 > .GND" to="net.GND" />
<trace from=".J1 > .VCC" to="net.VCC" />
<trace from=".J1 > .SDA" to="net.SDA" />
<trace from=".J1 > .SCL" to="net.SCL" />
<trace from=".J2 > .GND" to="net.GND" />
<trace from=".J2 > .VCC" to="net.VCC" />
<trace from=".J2 > .SDA" to="net.SDA" />
<trace from=".J2 > .SCL" to="net.SCL" />
<trace from=".U1 > .GND" to="net.GND" />
<trace from=".U1 > .GND2" to="net.GND" />
<trace from=".U1 > .VDD" to="net.VCC" />
<trace from=".U1 > .VDDIO" to="net.VCC" />
<trace from=".U1 > .CSB" to="net.VCC" />
<trace from=".U1 > .SDO" to="net.GND" />
<trace from=".U1 > .SDI" to="net.SDA" />
<trace from=".U1 > .SCK" to="net.SCL" />
<trace from=".R1 > .pin1" to="net.VCC" />
<trace from=".R1 > .pin2" to="net.SDA" />
<trace from=".R2 > .pin1" to="net.VCC" />
<trace from=".R2 > .pin2" to="net.SCL" />
<trace from=".C1 > .pin1" to="net.VCC" />
<trace from=".C1 > .pin2" to="net.GND" />
</board>
)
Bill of materials
| Ref | Part | Notes |
|---|---|---|
| U1 | BME280 | Temperature, humidity, pressure sensor |
| J1 | 4-pin header | Host connection: GND, VCC, SDA, SCL |
| J2 | 4-pin header | Optional OLED expansion header |
| R1/R2 | 4.7 kΩ resistors | I2C pull-ups to VCC |
| C1 | 100 nF capacitor | Local sensor decoupling |
Microcontroller code examples
Arduino / ESP32
#include <Wire.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
Wire.begin();
if (!bme.begin(0x76)) {
Serial.println("BME280 not found; try address 0x77");
while (1) delay(10);
}
}
void loop() {
Serial.print("Temperature C: ");
Serial.println(bme.readTemperature());
Serial.print("Humidity %: ");
Serial.println(bme.readHumidity());
Serial.print("Pressure hPa: ");
Serial.println(bme.readPressure() / 100.0F);
delay(1000);
}
Raspberry Pi Python
import time
import board
import adafruit_bme280.basic as adafruit_bme280
i2c = board.I2C()
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)
while True:
print(f"Temp: {bme280.temperature:.1f} C")
print(f"Humidity: {bme280.relative_humidity:.1f} %")
print(f"Pressure: {bme280.pressure:.1f} hPa")
time.sleep(1)
PCB layout guidance
- Place
C1within a few millimeters of the BME280VDDandGNDpins. - Keep the sensor away from hot regulators, LEDs, processors, and board edges with strong airflow.
- Route
SDAandSCLtogether and keep them short. - Put the host header at the edge of the board and label every pin on silkscreen.
- If you add an OLED, remember it shares the same bus and must not conflict with the BME280 address.
Bring-up checklist
- Verify the host is 3.3 V before connecting the module.
- Use an I2C scanner and confirm the BME280 appears at
0x76or0x77. - Confirm temperature, humidity, and pressure readings change plausibly.
- If the bus does not scan, check pull-ups and confirm
CSBis tied high for I2C mode. - If readings are warm, move the module away from heat sources or add airflow isolation.