The Quick Decision Matrix: What Can I Use Raspberry Pi For?

When makers ask, "what can I use Raspberry Pi for," the answer usually drowns in a sea of vague project lists. The Raspberry Pi is a general-purpose Linux computer with GPIO pins, meaning it can do almost anything, but that breadth is exactly what causes decision paralysis. Instead of browsing endless idea boards, use this decision matrix to lock in your exact use case and hardware stack.

Your Primary GoalRequired Hardware ProfileSoftware StackConcrete Verdict & Default Pick
Media center / Retro gamingPi 5 (8GB) + active cooler + NVMe HATLibreELEC / RetroPieBuy the Pi 5 8GB. Skip the GPIO entirely.
Home automation hubPi 4 or 5 (4GB) + Zigbee USB dongleHome Assistant OSBuy the Pi 4 4GB (cheaper, lower idle power for 24/7).
Network-wide ad blockingPi Zero 2 W + microSDPi-hole / DockerBuy the Pi Zero 2 W. Overkill to use a full Pi 5.
Learn hardware interfacing & sensorsPi 5 (4GB) + I2C sensors + OLEDPython 3 + BlinkaDEFAULT PICK: Build the I2C Dashboard below using Adafruit BME280 (Part #2652).

If your goal is to understand how microcontrollers and single-board computers interact with the physical world, the default pick is the I2C Environmental Dashboard. It forces you to deal with real-world bus capacitance, I2C addressing, and Python hardware libraries. Here is exactly how to build, code, and debug it.

The Default Pick: I2C Environmental Dashboard Parts List

This build targets the Raspberry Pi 5 (4GB variant). The Pi 5 features a dedicated RP1 southbridge chip that handles GPIO, which slightly changes how I2C clocks are managed under the hood compared to the Pi 4, but the physical pinout and BCM numbering remain identical.

ComponentExact Variant / Part NumberApprox. Cost (2026)
Single Board ComputerRaspberry Pi 5 (4GB RAM)$60.00
Power SupplyOfficial 27W USB-C PD Power Supply (White/Black)$12.00
Environmental SensorAdafruit BME280 I2C/SPI Breakout (Part #2652)$14.95
DisplayAdafruit Monochrome 0.96" 128x64 OLED (Part #326)$12.50
WiringPremium Female/Male Extension Jumper Wires (20-pin)$3.95
Bench Note: Do not power the Pi 5 with a standard 15W USB-C phone charger while running I2C peripherals. The Pi 5 will throttle USB current and may cause brownouts on the 3V3 rail, leading to phantom I2C disconnects. Use the official 27W PD supply.

Pin Mapping and Physical Wiring

The Raspberry Pi uses Broadcom (BCM) pin numbering in software, but the physical header layout is what you actually wire. Both the BME280 and the SSD1306 OLED communicate over the primary I2C bus (I2C1).

Pi 5 Physical PinBCM GPIOFunctionBME280 PinOLED Pin
1N/A3V3 PowerVIN (or 3V)VIN (or VCC)
6N/AGroundGNDGND
3GPIO 2I2C1 SDASDI/SDASDA
5GPIO 3I2C1 SCLSCK/SCLSCL

Wiring Steps:

  1. Disconnect the Pi from power.
  2. Connect Physical Pin 1 (3V3) to the positive rail on your breadboard.
  3. Connect Physical Pin 6 (GND) to the negative rail.
  4. Wire the SDA and SCL lines from Physical Pins 3 and 5 to the respective pins on both the BME280 and OLED.
  5. Double-check that VCC on the OLED is connected to 3V3, not 5V. The SSD1306 logic is 3.3V tolerant, but feeding 5V into the SDA line can fry the Pi 5's RP1 GPIO bank.

Python Implementation: Complete Compilable Code

This code targets the Raspberry Pi 5 running Raspberry Pi OS (Bookworm or later). It uses Adafruit's Blinka compatibility layer to interface with the hardware. Install the dependencies in a virtual environment first:

sudo apt update
sudo apt install python3-pip python3-venv i2c-tools
python3 -m venv ~/env
source ~/env/bin/activate
pip3 install adafruit-circuitpython-bme280 adafruit-circuitpython-ssd1306

Save the following code as dashboard.py. It includes explicit pin definitions, hardware initialization, and error handling for common I2C faults.

import time
import board
import busio
import adafruit_bme280
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont

# --- PIN DEFINITIONS & BUS SETUP ---
# The Pi 5 primary I2C bus maps to BCM GPIO 2 (SDA) and BCM GPIO 3 (SCL)
# Physical header pins: 3 (SDA) and 5 (SCL)
I2C_SDA = board.SDA  # BCM 2
I2C_SCL = board.SCL  # BCM 3

# Initialize I2C bus at 100kHz (standard mode) to prevent signal degradation on long wires
i2c_bus = busio.I2C(I2C_SCL, I2C_SDA, frequency=100000)

# --- SENSOR INITIALIZATION WITH ERROR HANDLING ---
try:
    # BME280 default I2C address is 0x77, but Adafruit breakouts often use 0x76
    bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c_bus, address=0x76)
    bme280.sea_level_pressure = 1013.25
    print("BME280 sensor initialized successfully.")
except ValueError as e:
    print(f"[FATAL] BME280 Init Failed: {e}")
    print("Check I2C address. Run 'i2cdetect -y 1' in terminal to verify.")
    exit(1)
except RuntimeError as e:
    print(f"[FATAL] I2C Bus Error: {e}")
    exit(1)

# --- DISPLAY INITIALIZATION ---
try:
    # SSD1306 128x64 OLED default address is 0x3C
    oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c_bus, addr=0x3C)
    oled.fill(0)
    oled.show()
    print("SSD1306 OLED initialized successfully.")
except ValueError as e:
    print(f"[FATAL] OLED Init Failed: {e}")
    exit(1)

# --- MAIN LOOP ---
try:
    while True:
        temp_c = bme280.temperature
        humidity = bme280.relative_humidity
        pressure = bme280.pressure

        # Create a blank image for drawing
        image = Image.new('1', (oled.width, oled.height))
        draw = ImageDraw.Draw(image)
        
        # Use default font (Pillow's built-in bitmap font)
        draw.text((0, 0), f"Temp: {temp_c:.1f} C", font=ImageFont.load_default(), fill=255)
        draw.text((0, 16), f"Hum:  {humidity:.1f} %", font=ImageFont.load_default(), fill=255)
        draw.text((0, 32), f"Pres: {pressure:.1f} hPa", font=ImageFont.load_default(), fill=255)
        draw.text((0, 48), f"Alt:  {bme280.altitude:.1f} m", font=ImageFont.load_default(), fill=255)

        # Display image
        oled.image(image)
        oled.show()
        
        time.sleep(2.0)

except KeyboardInterrupt:
    print("\nDashboard stopped by user.")
    oled.fill(0)
    oled.show()
except Exception as e:
    print(f"[ERROR] Unexpected runtime failure: {e}")
    oled.fill(0)
    oled.show()

Debugging: First Three Things to Check When It Fails

Hardware debugging on the Pi is rarely a software issue; it is almost always a physical layer or kernel module issue. If the script crashes on boot, follow this exact sequence.

The First Three Things to Check:
  1. Kernel Visibility: Run i2cdetect -y 1 in the terminal. If you don't see 3c and 76 in the grid, the Pi's kernel cannot see the hardware. Stop and check physical wiring.
  2. Physical Pin Seating: Pull the jumper wires and re-seat them. Breadboard contacts wear out, and a 0.5mm gap on BCM 2 (SDA) will drop the entire bus.
  3. I2C Interface Enabled: Run sudo raspi-config, go to Interface Options, and ensure I2C is explicitly enabled. The Pi 5 requires this to load the i2c-dev kernel module.

Exact Error Strings and Ranked Causes

Error 1: OSError: [Errno 121] Remote I/O error

  • Cause 1 (Most Likely): The I2C clock speed is too high for the wire capacitance. Fix: Lower the frequency in the busio.I2C() call to 50000 (50kHz).
  • Cause 2: Missing pull-up resistors. The Pi 5 has 1.8k ohm internal pull-ups on the I2C lines, but if you are using extremely long wires (>12 inches), you need external 4.7k ohm pull-ups to 3V3.
  • Cause 3: A loose SDA or SCL jumper wire causing a mid-transaction bus disconnect.

Error 2: ValueError: No I2C device at address: 0x76

  • Cause 1: Wrong I2C address. Some BME280 breakouts default to 0x77. Check the silkscreen on your specific board or look at the i2cdetect output.
  • Cause 2: You wired the sensor to the secondary I2C bus (BCM 0/1, Physical pins 27/28) instead of the primary bus (BCM 2/3).

Error 3: ModuleNotFoundError: No module named 'board'

  • Cause 1: You are running the script in the system Python environment instead of the virtual environment where Blinka was installed. Fix: Run source ~/env/bin/activate before executing the script.

Scaling the Build: Extending or Simplifying

Once the baseline dashboard is running, you need to decide how to adapt it to your actual workspace needs. Here is how to modify the build without rewriting the core logic.

How to Extend the Build

If you want to push data off the local OLED and into a home automation stack, add MQTT. Install paho-mqtt in your virtual environment. Inside the while True loop, serialize the temp_c, humidity, and pressure variables into a JSON payload and publish to an MQTT broker (like Mosquitto running on a separate Pi or your router). This turns your Pi 5 into a high-fidelity environmental node for Home Assistant.

How to Simplify the Build

If the OLED is overkill and you just want data logging, strip the adafruit_ssd1306 and PIL imports entirely. Replace the display rendering block with a simple CSV append operation:

import csv
import datetime

with open('env_log.csv', 'a', newline='') as f:
    writer = csv.writer(f)
    writer.writerow([datetime.datetime.now(), temp_c, humidity, pressure])

This reduces CPU overhead to near zero, allowing you to safely downgrade the hardware to a Raspberry Pi Zero 2 W if you are building a remote, battery-powered sensor node.

Ultimately, what you use the Raspberry Pi for depends on where the Linux OS requirement intersects with your GPIO needs. For pure sensor reading, a microcontroller like an ESP32 is cheaper and lower power. But when you need to run local databases, execute complex Python data science libraries on the fly, or host a local web server alongside your hardware interfaces, the Pi 5 remains the undisputed workhorse of the maker bench.