Project Spec Sheet & Overview
When makers search for cool things to do with a Raspberry Pi, they often find superficial media center builds or basic web servers. But the Pi's true power lies in bridging physical hardware with high-level Python automation. This project builds an I2C Climate Controller: a system that reads precise temperature and humidity data from a BME280 sensor and triggers a 5V relay to control a desk fan, space heater, or grow tent exhaust.
| Parameter | Specification |
|---|---|
| Difficulty Rating | Intermediate (Requires I2C bus config & mains-adjacent relay safety) |
| Estimated Build Time | 90 - 120 minutes |
| Target Board Variant | Raspberry Pi 4 Model B (4GB) or Raspberry Pi 5 (4GB/8GB) |
| OS Requirement | Raspberry Pi OS (Bookworm or newer, 64-bit) |
| Estimated Cost | $65 - $85 (excluding Pi and power supply) |
Hardware Bill of Materials & Pin Mapping
To ensure the code compiles and runs without modification, use these exact component variants. Substituting a DHT11 for the BME280 will break the I2C implementation, as the DHT series uses a proprietary single-bus protocol.
Exact Parts List
- Microcontroller: Raspberry Pi 4 Model B (4GB RAM) or Raspberry Pi 5
- Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652)
- Actuator: 2-Channel 5V Opto-Isolated Relay Module (SRD-05VDC-SL-C)
- Power: 5V 3A USB-C Power Supply (Official Raspberry Pi equivalent)
- Wiring: Female-to-Female and Male-to-Female Dupont jumper wires
GPIO & I2C Pin Mapping Table
The Raspberry Pi's I2C bus operates at 3.3V logic. The BME280 is natively 3.3V tolerant, but the 5V relay module requires a 5V power rail while accepting 3.3V logic triggers on its optocoupler inputs.
| Pi Physical Pin | BCM GPIO / Function | Target Module | Module Pin |
|---|---|---|---|
| Pin 1 | 3.3V Power | BME280 Breakout | VIN / 3Vo |
| Pin 3 | GPIO 2 (SDA.1) | BME280 Breakout | SDA |
| Pin 5 | GPIO 3 (SCL.1) | BME280 Breakout | SCL |
| Pin 6 | Ground | BME280 Breakout | GND |
| Pin 2 | 5V Power | Relay Module | VCC (JD-VCC jumpered) |
| Pin 16 | GPIO 23 | Relay Module | IN1 |
| Pin 9 | Ground | Relay Module | GND |
Step-by-Step Wiring & Software Setup
- Enable I2C Interface: Boot your Pi, open a terminal, and run
sudo raspi-config. Navigate to Interface Options > I2C and enable it. Reboot the Pi. - Verify Sensor Address: With the BME280 wired to Pins 1, 3, 5, and 6, run
i2cdetect -y 1. You should see77in the grid. (If you see76, your specific breakout has the address pad bridged; update the Python code variable accordingly). - Install Python Dependencies: We use
smbus2for raw I2C communication andgpiozerofor modern, safe GPIO control. Run:
sudo apt update && sudo apt install python3-smbus python3-gpiozero i2c-tools
pip3 install RPi.bme280 - Wire the Relay Opto-Isolator: Ensure the JD-VCC jumper on the relay module is intact. Connect Pi Pin 2 (5V) to the relay VCC, and Pi Pin 9 (GND) to relay GND. This powers the relay coil independently of the Pi's 3.3V logic rail, preventing brownouts when the coil energizes.
Complete Python Control Code
This script targets the Raspberry Pi 4 and 5 running Raspberry Pi OS. It utilizes the gpiozero library (the current standard over the deprecated RPi.GPIO) and includes robust exception handling for I2C bus failures.
import time
import smbus2
import bme280
from gpiozero import OutputDevice
from signal import pause
# --- Pin & Hardware Definitions ---
RELAY_PIN = 23 # BCM GPIO 23 (Physical Pin 16)
I2C_BUS_ID = 1 # I2C bus 1 (Pins 3 & 5)
BME_ADDRESS = 0x77 # Default Adafruit BME280 address
# Temperature thresholds (Celsius) with 2-degree hysteresis to prevent relay chatter
TEMP_HIGH = 28.0
TEMP_LOW = 26.0
# Initialize Hardware
# active_high=False because most 5V relay modules trigger on LOW (sink current)
relay = OutputDevice(RELAY_PIN, active_high=False)
bus = smbus2.SMBus(I2C_BUS_ID)
try:
calibration_params = bme280.load_calibration_params(bus, BME_ADDRESS)
except OSError:
print('Failed to load BME280 calibration. Check I2C wiring.')
exit(1)
def monitor_climate():
print(f'Starting climate monitor. Relay on GPIO {RELAY_PIN}.')
try:
while True:
# Sample sensor data
data = bme280.sample(bus, BME_ADDRESS, calibration_params)
temp_c = data.temperature
humidity = data.humidity
print(f'Temp: {temp_c:.2f}C | Humidity: {humidity:.1f}%')
# Hysteresis control logic
if temp_c >= TEMP_HIGH:
if not relay.is_active:
relay.on()
print(' -> Relay ON: Cooling triggered.')
elif temp_c <= TEMP_LOW:
if relay.is_active:
relay.off()
print(' -> Relay OFF: Target temp reached.')
time.sleep(5)
except OSError as e:
# Catch the specific I2C bus error
if e.errno == 121:
print('CRITICAL: OSError: [Errno 121] Remote I/O error.')
print('The I2C bus dropped. Check physical connections and pull-ups.')
else:
print(f'Unexpected OSError: {e}')
except KeyboardInterrupt:
print('\nUser interrupted. Shutting down safely.')
finally:
# Always clean up hardware state on exit
relay.off()
bus.close()
print('Relay deactivated. I2C bus closed.')
if __name__ == '__main__':
monitor_climate()
Debugging: Fixing the I2C Bus Drop
The most common failure mode when integrating I2C sensors on the Pi is the dreaded bus lockup. If your script crashes or hangs, you will likely see this exact error string in your terminal:
OSError: [Errno 121] Remote I/O error
The First Three Things to Check
When this error strikes, do not immediately rewrite your code. The issue is almost always physical or configuration-level. Check these three items in order:
- Run
i2cdetect -y 1: If the grid is entirely blank, your SDA/SCL wires are swapped, or the sensor lacks power. If you seeUUinstead of77, a kernel driver (likeiio_bme280) has already claimed the address. You must blacklist the conflicting driver in/boot/firmware/config.txt. - Verify the 3.3V Rail Stability: The BME280 requires a minimum of 1.71V on its VDD pin to maintain I2C logic levels. If you are drawing heavy current from the Pi's 5V rail (like a relay coil) without proper decoupling, the Pi's onboard 3.3V regulator can sag, causing the I2C transceiver to fail mid-transaction.
- Inspect Pull-Up Resistors: The Pi's internal I2C pull-ups are 1.8kΩ. This is sufficient for a sensor plugged directly into the GPIO header. However, if you are using Dupont wires longer than 30cm, parasitic capacitance will round off the I2C square waves, resulting in Errno 121. Solder external 4.7kΩ pull-up resistors between SDA/SCL and 3.3V on the sensor breakout.
Ranked Causes for Errno 121
- Cause 1 (60%): SDA and SCL pins swapped at the breadboard.
- Cause 2 (25%): Loose Dupont jumper wire causing intermittent contact.
- Cause 3 (10%): Missing I2C pull-up resistors on long wire runs.
- Cause 4 (5%): Address collision with another I2C device on the same bus.
How to Extend or Simplify the Build
Depending on your experience level and end goal, you can modify this architecture.
If raw I2C wiring and
smbus2 calibration math feel overwhelming, swap the BME280 breakout for the Pimoroni Enviro+ HAT. It plugs directly over the Pi's GPIO header, eliminating jumper wires entirely. You can then use Pimoroni's pre-packaged Python library, reducing the code to three lines.
Extending for Advanced Makers:
To turn this into a true IoT node, integrate the Eclipse Paho MQTT library. Publish the temp_c and humidity variables to a local Mosquitto broker. From there, Home Assistant can ingest the MQTT topics to build historical dashboards, completely decoupling the Pi from the relay logic and letting your smart home server handle the hysteresis automation.
FAQ: More Cool Things to Do with a Raspberry Pi
What are the coolest things to do with a Raspberry Pi for home automation?
Beyond climate control, the most impactful home automation project is building a local Home Assistant Green equivalent. By installing Home Assistant OS on a Pi 4 or 5, you can integrate Zigbee (via a Sonoff Zigbee 3.0 USB Dongle Plus) and Matter devices. This keeps your smart home data entirely off the cloud, reduces latency to under 50ms, and allows complex automations like triggering motorized blinds based on the sun's azimuth angle.
Are there cool things to do with a Raspberry Pi that don't require coding?
Yes. You can configure a Pi as a network-wide ad blocker using Pi-hole. It requires zero programming—just flashing the OS and running the automated installer script. Once configured as your router's DNS server, it blocks telemetry and ads across every device on your WiFi. Another no-code option is setting up RetroPie to emulate classic consoles using a USB controller and pre-compiled ROM packages.
What cool things can you do with a Raspberry Pi 5 compared to older models?
The Raspberry Pi 5 introduces a dedicated PCIe 2.0 interface, which changes the game for storage-bound projects. The coolest application is building a NAS (Network Attached Storage) using an NVMe M.2 SSD via a PCIe HAT. Unlike the Pi 4, which bottlenecked at USB 3.0 speeds (~300MB/s real-world), the Pi 5 can saturate gigabit Ethernet limits, making it a viable, low-power Plex media server capable of hardware-accelerated 4K transcoding via its upgraded VideoCore VII GPU.






