Skip to main content
Tutorials

Building an I2C Environmental Sensor Module Tutorial

Overview

This tutorial covers designing a comprehensive environmental monitoring sensor module using tscircuit. You'll learn about I2C communication, multi-sensor integration, environmental sensing principles, and low-power design for battery-operated applications.

Project Specifications

ParameterValue
Board Size25mm × 35mm
Power Supply3.3V regulated
CommunicationI2C (SDA/SCL)
SensorsBME280 + CCS811
InterfaceQwiic/Stemma QT compatible

Sensors Overview

BME280 (Bosch)

Measures temperature, humidity, and barometric pressure:

ParameterRangeAccuracy
Temperature-40°C to +85°C±1°C
Humidity0-100% RH±3%
Pressure300-1100 hPa±1 hPa

CCS811 (AMS)

Measures carbon dioxide (CO2) and volatile organic compounds (VOCs):

ParameterRangeAccuracy
eCO2400-8192 ppm±50 ppm
TVOC0-1187 ppb

Components

RefPartDescriptionFootprint
U1BME280Temp/Humidity/Pressurelga-8
U2CCS811CO2/VOC Sensormlga-10
U3AP2112-3.33.3V LDO regulatorsot-23-5
J1Qwiic ConnectorI2C + Powerjst-ph-4pin
J2Optional HeaderBreadboard pinspin_header_1x4
C1Input capacitor10µF0805
C2Output capacitor10µF0805
R1I2C pull-up4.7kΩ0805
R2I2C pull-up4.7kΩ0805
LED1Power LEDBlue0805

Step 1: BME280 Sensor

The BME280 uses an I2C interface with selectable address (0x76 or 0x77):

<chip
name="U1"
footprint="lga-8"
pcbX={0}
pcbY={5}
/>

/* BME280 Pinout */
<trace from=".U1 .VDD" to="net.3V3" />
<trace from=".U1 .GND" to="net.GND" />
<trace from=".U1 .SDA" to="net.SDA" />
<trace from=".U1 .SCL" to="net.SCL" />
<trace from=".U1 .CSB" to="net.3V3" /> /* I2C mode */
<trace from=".U1 .SDO" to="net.GND" /> /* Address 0x76 */

BME280 Mounting Considerations

The BME280 should be mounted with the sensing element exposed to air:

  1. Top side: Keep the port hole unobstructed
  2. No conformal coating: Over the sensor
  3. Isolation: Separate from heated components

Step 2: CCS811 Sensor

The CCS811 requires a burn-in period and periodic baseline resets:

<chip
name="U2"
footprint="mlga-10"
pcbX={0}
pcbY={-5}
/>

/* CCS811 Pinout */
<trace from=".U2 .VCC" to="net.3V3" />
<trace from=".U2 .GND" to="net.GND" />
<trace from=".U2 .SDA" to="net.SDA" />
<trace from=".U2 .SCL" to="net.SCL" />
<trace from=".U2 .WAK" to="net.GND" /> /* Wake pin low */
<trace from=".U2 .INT" to="net.INT" />
<trace from=".U2 .RST" to="net.3V3" /> /* Hardware reset */

CCS811 Environmental Considerations

The CCS811 is sensitive to:

  • Temperature changes: Allow 20 minutes warm-up
  • High humidity: Can affect readings temporarily
  • Airflow: Needs fresh air for accurate VOC measurements

Step 3: I2C Bus Design

Address Configuration

/* BME280: SDO grounded = 0x76 */
/* CCS811: Addr pin = low = 0x5A */

<resistor name="R1" footprint="0805" resistance="4k7" pcbX={15} pcbY={0} />
<resistor name="R2" footprint="0805" resistance="4k7" pcbX={15} pcbY={3} />

<trace from="net.3V3" to=".R1 .pos" />
<trace from="net.3V3" to=".R2 .pos" />
<trace from=".R1 .neg" to="net.SDA" />
<trace from=".R2 .neg" to="net.SCL" />

I2C Pull-Up Resistor Selection

Bus SpeedMin R (mA)Max R (kΩ)
100kHz1kΩ10kΩ
400kHz2kΩ4kΩ
1MHz1kΩ2kΩ

We use 4.7kΩ for standard 100kHz operation with 2 sensors.

Step 4: Qwiic/Stemma QT Connector

<chip name="J1" footprint="jst-ph-4pin" pcbX={20} pcbY={0} />

/* Qwiic pinout: 1=GND, 2=3V3, 3=SDA, 4=SCL */
<trace from=".J1 .1" to="net.GND" />
<trace from=".J1 .2" to="net.3V3" />
<trace from=".J1 .3" to="net.SDA" />
<trace from=".J1 .4" to="net.SCL" />

Step 5: Power Supply

<chip name="U3" footprint="sot-23-5" pcbX={-15} pcbY={0} />

<capacitor name="C_IN" footprint="0805" capacitance="10µF" pcbX={-17} pcbY={2} />
<capacitor name="C_OUT" footprint="0805" capacitance="10µF" pcbX={-13} pcbY={2} />

<trace from=".U3 .VIN" to="net.VIN" />
<trace from=".U3 .GND" to="net.GND" />
<trace from=".U3 .VOUT" to="net.3V3" />

<trace from="net.VIN" to=".C_IN .pos" />
<trace from="net.GND" to=".C_IN .neg" />
<trace from="net.3V3" to=".C_OUT .pos" />
<trace from="net.GND" to=".C_OUT .neg" />

Step 6: Power LED

<led name="LED1" color="blue" footprint="0805" pcbX={-20} pcbY={8} />
<resistor name="R_LED" footprint="0805" resistance="4k7" pcbX={-22} pcbY={8} />

<trace from="net.3V3" to=".R_LED .pos" />
<trace from=".R_LED .neg" to=".LED1 .pos" />
<trace from=".LED1 .neg" to="net.GND" />

Complete Circuit

export default () => {
return (
<board width="35mm" height="25mm">
{/* BME280 - Temp/Humidity/Pressure */}
<chip name="U1" footprint="lga-8" pcbX={0} pcbY={5} />

{/* CCS811 - CO2/VOC */}
<chip name="U2" footprint="mlga-10" pcbX={0} pcbY={-5} />

{/* 3.3V LDO */}
<chip name="U3" footprint="sot-23-5" pcbX={-15} pcbY={0} />

{/* Qwiic Connector */}
<chip name="J1" footprint="jst-ph-4pin" pcbX={20} pcbY={0} />

{/* Power LED */}
<led name="LED1" color="blue" footprint="0805" pcbX={-20} pcbY={8} />
<resistor name="R_LED" footprint="0805" resistance="4k7" pcbX={-22} pcbY={8} />

{/* Bypass Capacitors */}
<capacitor name="C_IN" footprint="0805" capacitance="10µF" pcbX={-17} pcbY={2} />
<capacitor name="C_OUT" footprint="0805" capacitance="10µF" pcbX={-13} pcbY={2} />
</board>
)
}

Arduino Code Example

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

Adafruit_BME280 bme;
CCS811 ccs811;

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

// Initialize BME280
if (!bme.begin(0x76)) {
Serial.println("BME280 not found!");
}

// Initialize CCS811
ccs811.begin();
}

void loop() {
// Read BME280
float temp = bme.readTemperature();
float hum = bme.readHumidity();
float pres = bme.readPressure() / 100.0F;

// Read CCS811
ccs811.readData();
int co2 = ccs811.geteCO2();
int tvoc = ccs811.getTVOC();

Serial.print("Temp: "); Serial.print(temp, 1); Serial.println(" C");
Serial.print("Humidity: "); Serial.print(hum, 1); Serial.println(" %");
Serial.print("Pressure: "); Serial.print(pres, 1); Serial.println(" hPa");
Serial.print("CO2: "); Serial.print(co2); Serial.println(" ppm");
Serial.print("TVOC: "); Serial.print(tvoc); Serial.println(" ppb");

delay(1000);
}

Python (Raspberry Pi) Example

import smbus2
import bme280
import ccs811
import time

bus = smbus2.SMBus(1)

# Initialize BME280
calibration_params = bme280.load_calibration_params(bus, 0x76)

# Initialize CCS811
sensor = ccs811.CCS811(bus, 0x5a)
time.sleep(1) # Wait for sensor to warm up

while True:
# Read BME280
data = bme280.read(bus, 0x76, calibration_params)
print(f"Temp: {data.temperature:.1f} C")
print(f"Humidity: {data.humidity:.1f} %")
print(f"Pressure: {data.pressure:.1f} hPa")

# Read CCS811
if sensor.data_ready:
print(f"CO2: {sensor.co2} ppm")
print(f"TVOC: {sensor.tvoc} ppb")

time.sleep(1)

Enclosure Design

Ventilation Requirements

  • Sensor area: Include ventilation holes near sensors
  • Size: Minimum 2mm diameter holes
  • Pattern: Staggered pattern to prevent dust ingress
  • Location: Bottom of enclosure for convection

Material Considerations

  • Avoid: ABS plastic can outgas VOCs
  • Preferred: Acrylic, aluminum, or ABS with outgassing period
  • Sealing: Use silicone gaskets for weatherproofing

PCB Layout Guidelines

Sensor Placement

  • Keep sensors away from heat sources: MCU, voltage regulator
  • Consider airflow: Position sensors in natural airflow path
  • Separation: Keep BME280 and CCS811 at least 5mm apart

Power Design

  • Low noise: CCS811 is sensitive to supply noise
  • Decoupling: Use ceramic capacitors near each sensor
  • Ground plane: Solid ground under sensor area

Cost Estimate

ComponentUnit CostQtyTotal
BME280$2.501$2.50
CCS811$3.001$3.00
AP2112-3.3$0.251$0.25
Qwiic connector$0.301$0.30
Passives$0.055$0.25
PCB (2-layer)$0.501$0.50
Total$6.80

Summary

You've designed a complete environmental sensor module covering:

  • I2C communication with multiple sensors
  • BME280 temperature, humidity, and pressure sensing
  • CCS811 CO2 and VOC detection
  • Qwiic/Stemma QT compatibility
  • Power supply design with filtering
  • Arduino and Python code examples
  • Enclosure design for sensor accuracy

This module is ready for integration into environmental monitoring projects, smart home systems, or air quality monitoring applications.