If you are migrating from a Raspberry Pi 4 to a Raspberry Pi 5, the first thing you will notice when debugging hardware is that your favorite GPIO tools are broken. The Pi 5 replaced the Broadcom BCM2711 SoC’s built-in GPIO controller with a dedicated RP1 southbridge chip. This architectural shift means legacy raspberry pi commands like raspi-gpio are deprecated on Raspberry Pi OS Bookworm. Attempting to use them will result in silent failures or command-not-found errors.

This guide provides a decision-forward approach to hardware debugging on the Pi 5. We will cover the exact diagnostic commands you need to troubleshoot I2C sensors, verify GPIO pin states, and monitor system-level power throttling, culminating in an automated Python diagnostic script.

The Pi 5 Paradigm Shift: Why Older Raspberry Pi Commands Fail

On the Pi 4 and earlier, the CPU directly managed the GPIO pins. On the Pi 5, the RP1 peripheral controller handles all GPIO, I2C, SPI, and UART interfaces, communicating with the main BCM2712 CPU over PCIe. Because of this, the kernel interfaces changed.

The board variant targeted in this guide is the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS Bookworm (64-bit). The legacy raspi-gpio tool bypasses the kernel's pin multiplexing (pinmux) subsystem, which is incompatible with the RP1’s Device Tree overlays. The modern replacement is pinctrl, a utility that interfaces directly with the kernel's pin control framework. For a deep dive into the RP1 architecture, refer to the official Raspberry Pi 5 technical documentation.

Hardware Parts List and GPIO Pin Mapping

To demonstrate these diagnostic commands, we will interface a BME280 environmental sensor over I2C. The Pi 5 requires a robust power supply to prevent peripheral brownouts during I2C polling.

Component Exact Variant / Specification Estimated Cost (2026)
Microcontroller Raspberry Pi 5 (8GB RAM) $80.00
Power Supply Official 27W USB-C PD Power Supply (5V/5A) $12.00
Sensor Module Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) $19.95
Wiring Adafruit STEMMA QT / Qwiic JST SH 4-Pin Cable $1.50

Pin Mapping Table (BCM Numbering)

The RP1 chip maintains backward compatibility with the standard 40-pin header BCM numbering. Ensure your physical wiring matches this table:

Sensor Pin Pi 5 Physical Pin BCM GPIO RP1 Function
VIN / VCC 1 or 17 N/A (3.3V Power) 3V3 Supply
GND 6 N/A (Ground) System Ground
SDA 3 GPIO 2 I2C1 SDA
SCL 5 GPIO 3 I2C1 SCL

The Core Diagnostic Raspberry Pi Commands

When a sensor fails to read, do not jump straight to rewriting your Python code. Use these four terminal commands to isolate the fault at the hardware and kernel layers.

1. Verifying Pin State and Multiplexing

To check if the kernel has correctly assigned the I2C function to GPIO 2 and 3, use pinctrl:

pinctrl get 2
pinctrl get 3

Expected Output: 2: a5 pn | hi || f5 | GPIO2 = I2C1_SDA. If it shows ip (input) or op (output) instead of f5 (function 5 / I2C), your Device Tree overlay failed to load.

2. Scanning the I2C Bus

Once pins are multiplexed, scan for the device address:

i2cdetect -y 1

The -y 1 flag disables interactive mode and targets I2C bus 1. A healthy BME280 will show 76 or 77 in the grid. If you see UU, the kernel driver has already claimed the device (which is fine if you are using spidev or kernel IIO, but will block user-space smbus2 scripts).

3. Checking System Throttling and Power

The Pi 5 will silently throttle I2C clock speeds or drop peripheral power if the USB-C supply cannot deliver 5A. Check the throttle state:

vcgencmd get_throttled

Expected Output: throttled=0x0. If you see 0x50005, your Pi has experienced under-voltage and frequency capping. You must upgrade to a 27W PD supply.

4. Inspecting Kernel I2C Errors

If the bus locks up, the kernel logs the exact failure reason:

dmesg | grep -i i2c

Look for I2C transfer timed out or controller timed out. This indicates missing pull-up resistors or a locked slave device.

Automated Debugging Script (Python + Bash Integration)

Rather than typing these commands manually, use this complete Python script to automate the diagnostic sequence. It executes the raspberry pi commands via subprocess, parses the hex output from vcgencmd, and verifies pin states before attempting an I2C read.

Prerequisite: Install the I2C tools and Python SMBus library via sudo apt install i2c-tools python3-smbus.
import subprocess
import sys
import smbus2

# --- Pin and Bus Definitions ---
PIN_SDA = 2
PIN_SCL = 3
I2C_BUS = 1
BME280_ADDR = 0x76

def run_cli_command(cmd):
    """Execute a bash command and return stdout, handling errors."""
    try:
        result = subprocess.run(
            cmd, shell=True, capture_output=True, text=True, check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        return f"ERROR: {e.stderr.strip()}"

def check_pinctrl(pin):
    """Verify RP1 pin multiplexing using pinctrl."""
    output = run_cli_command(f"pinctrl get {pin}")
    if "ERROR" in output:
        print(f"[FAIL] pinctrl not found. Are you on Pi 5 / Bookworm?")
        return False
    if "I2C" not in output:
        print(f"[FAIL] GPIO {pin} is not muxed for I2C. Output: {output}")
        return False
    print(f"[PASS] GPIO {pin} correctly muxed for I2C.")
    return True

def check_power_throttling():
    """Parse vcgencmd hex output for under-voltage flags."""
    output = run_cli_command("vcgencmd get_throttled")
    if "ERROR" in output:
        print("[WARN] Could not read vcgencmd. Skipping power check.")
        return True
    
    # Extract hex value (e.g., 'throttled=0x50005' -> '0x50005')
    hex_str = output.split('=')[1]
    throttle_val = int(hex_str, 16)
    
    # Bit 0 (0x1) is under-voltage, Bit 16 (0x10000) is under-voltage occurred
    if (throttle_val & 0x1) or (throttle_val & 0x10000):
        print(f"[FAIL] Under-voltage detected! Hex: {hex_str}. Check 5V/5A PSU.")
        return False
    
    print(f"[PASS] Power stable. Throttle state: {hex_str}")
    return True

def scan_i2c_bus():
    """Run i2cdetect and verify target address is present."""
    output = run_cli_command(f"i2cdetect -y {I2C_BUS}")
    if "ERROR" in output:
        print(f"[FAIL] i2cdetect failed: {output}")
        return False
    
    # Check if hex address (76) is in the output grid
    if "76" in output.split():
        print(f"[PASS] Device found at 0x{BME280_ADDR:02X}.")
        return True
    print(f"[FAIL] Device 0x{BME280_ADDR:02X} not found on bus {I2C_BUS}.")
    return False

def read_sensor_id():
    """Attempt to read the BME280 chip ID register (0xD0)."""
    try:
        bus = smbus2.SMBus(I2C_BUS)
        chip_id = bus.read_byte_data(BME280_ADDR, 0xD0)
        if chip_id == 0x60:
            print(f"[PASS] BME280 Chip ID verified: 0x{chip_id:02X}")
        else:
            print(f"[WARN] Unexpected Chip ID: 0x{chip_id:02X} (Expected 0x60)")
        bus.close()
    except OSError as e:
        print(f"[FAIL] I2C Read Error: {e}")

if __name__ == "__main__":
    print("--- Raspberry Pi 5 Hardware Diagnostic Sequence ---")
    
    # Step 1: Check Power
    if not check_power_throttling():
        sys.exit(1)
        
    # Step 2: Check Pin Muxing
    if not (check_pinctrl(PIN_SDA) and check_pinctrl(PIN_SCL)):
        sys.exit(1)
        
    # Step 3: Scan Bus
    if not scan_i2c_bus():
        sys.exit(1)
        
    # Step 4: Read Register
    read_sensor_id()
    print("--- Diagnostic Complete ---")

Troubleshooting: Exact Error Strings and Ranked Causes

When the script above or your terminal commands fail, match the exact error string to the ranked causes below.

Error 1: bash: raspi-gpio: command not found

  1. Cause: You are running Raspberry Pi OS Bookworm or using a Pi 5. The raspi-gpio package is no longer in the default repositories.
  2. Fix: Replace all instances of raspi-gpio get X with pinctrl get X. If you absolutely need the legacy tool for an older script, compile it from the raspi-gpio GitHub repository, but be aware it will not correctly manipulate RP1 pins.

Error 2: Error: Could not open file '/dev/i2c-1' or '/dev/i2c/1': No such file or directory

  1. Cause: The I2C kernel module is not loaded, or the interface is disabled in the bootloader config.
  2. Fix: Run sudo raspi-config, navigate to Interface Options > I2C, and enable it. Reboot. Alternatively, add dtparam=i2c_arm=on to /boot/firmware/config.txt.

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

  1. Cause (Most Likely): SDA and SCL wires are swapped, or the sensor is unpowered.
  2. Cause (Secondary): Missing I2C pull-up resistors. The Pi 5’s internal RP1 pull-ups are weak (~50kΩ). High-speed I2C requires external 4.7kΩ pull-ups to 3.3V.
  3. Cause (Edge Case): I2C clock stretching timeout. The sensor is holding SCL low to process data, but the Pi’s I2C controller times out before the sensor finishes.
  4. Fix: Swap SDA/SCL. If the issue persists, solder 4.7kΩ resistors between SDA/SCL and the 3.3V rail.
The First Three Things to Check When I2C Fails:
  1. Power Supply Voltage: Measure the 5V and 3.3V pins with a multimeter. If the 5V rail reads below 4.8V under load, the Pi 5 will brownout and drop the I2C bus.
  2. Pull-Up Resistors: Verify your breakout board has 4.7kΩ pull-ups populated. Many cheap clone boards omit them.
  3. Cable Seating: STEMMA QT / Qwiic connectors can feel seated while only making contact on 3 of the 4 pins. Unplug and firmly reseat the connector.

Decision Tree: Which Diagnostic Command to Use When

Use this decision matrix to select the exact raspberry pi command based on your hardware symptom. Do not guess; follow the tree to the terminal command.

Hardware Symptom Diagnostic Command Expected Healthy Output Concrete Action if Failing
Sensor not detected by Python script i2cdetect -y 1 Grid showing 76 or 77 If empty: check wiring. If UU: disable kernel driver overlay.
GPIO pin reads LOW when it should be HIGH pinctrl get [PIN] op | hi (output, high) If ip (input): run pinctrl set [PIN] op dh to force output/high.
Random I2C read timeouts / crashes vcgencmd get_throttled throttled=0x0 If 0x50005: replace USB-C cable and power supply with 27W PD.
Bus locks up after 10 minutes of operation dmesg | tail -n 20 No I2C timeout messages If transfer timed out: add external 4.7kΩ pull-up resistors.

Extending and Simplifying the Build

How to Simplify

If you are struggling to visualize I2C clock stretching or SDA/SCL timing issues, stop using terminal commands and install piscope. It is a digital logic analyzer specifically built for the Raspberry Pi. Run sudo apt install piscope, launch it, and it will graph the exact I2C transactions in real-time, allowing you to see if the BME280 is NACKing your address bytes.

How to Extend

To turn this diagnostic script into a permanent monitoring daemon, extend the Python code to publish the vcgencmd measure_temp and get_throttled outputs to an MQTT broker. By feeding the Pi 5’s thermal and power states into Home Assistant via MQTT, you can trigger an automation to shut down non-essential peripherals if the Pi reports an under-voltage hex code of 0x1, protecting your SD card from corruption during brownouts.