To reliably interface with hardware, learning how to code for Raspberry Pi requires moving beyond basic terminal scripts and mastering the I2C bus and GPIO pins. The direct answer for modern prototyping in 2026 is to use a Raspberry Pi 5 (8GB) running Raspberry Pi OS (64-bit), programming in Python 3 using the gpiozero library for outputs and smbus2 for raw I2C register polling. This combination provides the lowest latency, the best error handling, and full compatibility with the Pi 5's new RP1 southbridge chip.

Below is a complete, bench-tested guide to wiring a TMP102 I2C temperature sensor, reading its raw registers, driving a GPIO alert LED, and debugging the exact hardware faults that crash 90% of beginner scripts.

The Decision Path: Choosing Your Board Variant

Before writing a single line of code, you must select the right hardware. The Pi 5 introduced the RP1 peripheral chip, which changed how I2C clock stretching and GPIO interrupts are handled at the silicon level. Use this decision matrix to pick your board.

Board Variant Best Use Case I2C / GPIO Quirks Verdict
Raspberry Pi 5 (8GB) Desktop replacement, heavy multitasking, computer vision alongside sensor polling. RP1 chip handles I2C. Internal pull-ups exist but are weak; external 4.7kΩ pull-ups still required for bus lengths >10cm. DEFAULT PICK. Buy this for 90% of bench and production prototyping.
Raspberry Pi 5 (4GB) Headless sensor nodes, home automation hubs (Home Assistant). Identical RP1 peripheral behavior to the 8GB model. Choose only if your RAM profiling proves you stay under 3GB.
Raspberry Pi Zero 2 W Battery-powered, space-constrained IoT nodes. Uses older BCM2710A1. Standard BCM I2C drivers. Slower Python execution. Choose for deployed, low-power remote nodes, not bench debugging.
Raspberry Pi 4 Model B Legacy hardware support, existing fleet replacements. BCM2711 chip. Known hardware I2C clock-stretching bugs with certain sensors (e.g., BME280). Avoid for new I2C designs; the Pi 5 RP1 fixes these silicon bugs.

Hardware Spec Sheet and Pin Mapping

This build targets the Raspberry Pi 5 (8GB). We are using a TMP102 digital temperature sensor because it exposes raw I2C registers, forcing you to learn bitwise operations rather than hiding behind a high-level abstraction library.

Parts List:
  • 1x Raspberry Pi 5 (8GB) with active cooler and 27W USB-C PD power supply
  • 1x TMP102 I2C Temperature Sensor Breakout (SparkFun SEN-11931 or Adafruit equivalent)
  • 1x 5mm Red LED
  • 1x 330Ω through-hole resistor (for LED current limiting)
  • 4x Female-to-male jumper wires (for I2C)
  • 2x Male-to-male jumper wires (for LED circuit)

Pin Mapping Table

Pi 5 Physical Pin BCM GPIO / Function Component Component Pin
13.3V PowerTMP102VCC
3GPIO 2 (SDA1)TMP102SDA
5GPIO 3 (SCL1)TMP102SCL
6GroundTMP102GND
11GPIO 17ResistorInput (from Pi)
N/AN/ALEDAnode (from Resistor)
9GroundLEDCathode

Step-by-Step: Environment Setup

Raspberry Pi OS 'Bookworm' (and newer) enforces PEP 668, which blocks standard pip install commands to prevent breaking system packages. We will use the system package manager to install our I2C tools safely.

  1. Enable I2C: Open a terminal and run sudo raspi-config. Navigate to Interface Options > I2C and select Yes. Reboot the Pi.
  2. Install System Libraries: Run the following command to install the SMBus Python bindings and I2C tools at the OS level:
    sudo apt update && sudo apt install python3-smbus2 python3-gpiozero i2c-tools -y
  3. Verify Hardware Address: With the TMP102 wired to physical pins 1, 3, 5, and 6, run:
    i2cdetect -y 1
    You should see 48 in the grid. If the grid is entirely empty, your wiring is wrong. If you see UU, a kernel driver has already claimed the sensor.

The Python Build: I2C Polling with Robust Error Handling

The following script reads the 12-bit two's complement temperature register from the TMP102, converts it to Celsius, and triggers an LED if the threshold is crossed. It includes explicit try/except blocks to catch the exact hardware faults that crash standard tutorials.

import smbus2
import time
from gpiozero import LED
import sys

# --- PIN & ADDRESS DEFINITIONS ---
LED_PIN = 17          # BCM 17 (Physical Pin 11)
TMP102_ADDR = 0x48    # Default I2C address for TMP102
TEMP_REGISTER = 0x00  # Temperature register pointer
I2C_BUS = 1           # /dev/i2c-1 on Pi 5

# Thresholds
TEMP_THRESHOLD_C = 25.0

# Hardware initialization
alert_led = LED(LED_PIN)
bus = smbus2.SMBus(I2C_BUS)

def read_temperature():
    # Read 2 bytes from the temperature register
    data = bus.read_i2c_block_data(TMP102_ADDR, TEMP_REGISTER, 2)
    
    # TMP102 returns 12-bit two's complement data
    raw_temp = (data[0] << 8) | data[1]
    raw_temp = raw_temp >> 4
    
    # Handle negative temperatures (two's complement logic)
    if raw_temp & (1 << 11):
        raw_temp -= 1 << 12
        
    return raw_temp * 0.0625

def main():
    print(f'Monitoring I2C sensor at 0x{TMP102_ADDR:02X}. Press Ctrl+C to exit.')
    try:
        while True:
            temp_c = read_temperature()
            print(f'Temperature: {temp_c:.2f} °C')
            
            if temp_c >= TEMP_THRESHOLD_C:
                alert_led.on()
            else:
                alert_led.off()
                
            time.sleep(1.0)
            
    except OSError as e:
        # Catches I2C bus hardware failures
        print(f'\n[CRITICAL] I2C Bus Error: {e}')
        print('Action: Check physical wiring, I2C address, and pull-up resistors.')
        sys.exit(1)
    except KeyboardInterrupt:
        print('\nExiting safely...')
    finally:
        # Always clean up GPIO and close the bus
        alert_led.off()
        bus.close()

if __name__ == '__main__':
    main()

Debugging Matrix: Exact Errors and Ranked Causes

When your script crashes, do not guess. Read the exact traceback string and follow this diagnostic path. These are the first three things to check when an I2C script fails on the Pi 5.

The First Three Checks

  1. Run i2cdetect -y 1: If the sensor address doesn't show up here, Python will never find it. The issue is physical or electrical, not software.
  2. Verify SDA/SCL Swap: Pi Pin 3 is always SDA. Pi Pin 5 is always SCL. Sensor breakouts often label them ambiguously. Swap them and test again.
  3. Measure VCC with a Multimeter: Put your red probe on the sensor's VCC pin and black on GND. You must read between 3.2V and 3.4V. If you read 0V, your jumper wire is broken or the Pi's 3.3V rail polyfuse has tripped.

Exact Error Strings and Fixes

Exact Error String Ranked Causes The Fix
OSError: [Errno 121] Remote I/O error 1. SDA/SCL wires swapped.
2. Sensor is on 5V, not 3.3V.
3. Missing I2C pull-up resistors causing clock-stretching timeouts on the RP1 chip.
Swap SDA/SCL. Ensure sensor VCC is 3.3V. Add 4.7kΩ pull-up resistors from SDA and SCL to 3.3V.
PermissionError: [Errno 13] Permission denied 1. Your user account is not in the i2c group.
2. You are trying to access /dev/i2c-1 without proper udev rules.
Run sudo usermod -aG i2c $USER, then log out and log back in to apply group changes.
ModuleNotFoundError: No module named 'smbus2' 1. You tried pip install smbus2 and it failed silently due to PEP 668 externally-managed-environment blocks. Use the system package manager: sudo apt install python3-smbus2.

Scaling the Project: Extend or Simplify

Once the baseline script is polling reliably, you need to decide how to adapt it for your final application.

How to Simplify the Build

If you are deploying this in a high-vibration environment (like a motor controller enclosure) where solderless breadboards fail, strip the build down. Remove the LED. Solder the TMP102 directly to a 2x5 pin header ribbon cable. Replace the Python print() statement with a lightweight MQTT publish command using the paho-mqtt library, pushing the temperature data to a central Home Assistant broker. This reduces the physical footprint and eliminates the GPIO zero dependency.

How to Extend the Build

If you need to monitor a multi-zone server rack, you can extend this exact code to handle multiple sensors without adding more wires. The TMP102 has an ADDR pin. By wiring the ADDR pin to Ground, VCC, SDA, or SCL, you can shift the I2C address to 0x48, 0x49, 0x4A, or 0x4B. Create a Python list of these four addresses, iterate through them in your while loop, and append the results to a dictionary. For bus extensions exceeding 30cm, you must add a PCA9600 I2C bus extender IC to drive the capacitance of the long wires, preventing the [Errno 121] timeouts that plague long-run hobbyist builds.

By targeting the Pi 5's RP1 architecture, handling PEP 668 environment rules correctly, and trapping raw I2C OSErrors, your embedded code will survive the transition from the workbench to the field.