The raspi-config utility is the gateway between the Raspberry Pi OS software stack and the physical 40-pin GPIO header. While it is famous for setting locale and hostname, its true power for embedded engineers lies in the Interface Options menu. Without properly configuring these interfaces via raspi-config, the underlying device tree overlays will not load, leaving your I2C, SPI, and UART hardware buses completely invisible to the Linux kernel.
This guide targets the Raspberry Pi 5 (8GB variant) running the 64-bit Raspberry Pi OS (Bookworm). We will map the configuration matrix, build a dual-bus environmental and GPS logger, and debug the exact kernel errors that occur when interface configurations fail.
The Interface Options Matrix
Before wiring any sensors, you must understand what raspi-config is actually doing under the hood. When you toggle an interface, the tool modifies /boot/firmware/config.txt (note the firmware subdirectory introduced in Bookworm) to inject specific dtparam and dtoverlay directives. Here is the data-dense breakdown of the primary hardware buses.
| raspi-config Menu Path | Default State | Primary GPIO Pins (Physical) | Max Clock / Speed | Kernel Overlay Injected | Common Modules |
|---|---|---|---|---|---|
| Interface Options > I2C | Disabled | Pin 3 (SDA), Pin 5 (SCL) | 400 kHz (Fast Mode) | dtparam=i2c_arm=on |
BME280, SSD1306, MPU6050 |
| Interface Options > SPI | Disabled | 19 (MISO), 21 (MOSI), 23 (SCLK), 24 (CE0) | 125 MHz (Theoretical) | dtparam=spi=on |
RFID-RC522, MAX7219, ILI9341 |
| Interface Options > Serial Port | Disabled | 8 (TXD), 10 (RXD) | 115,200 baud (Standard) | enable_uart=1 |
NEO-6M GPS, HC-05 Bluetooth |
| Interface Options > 1-Wire | Disabled | Pin 7 (GPIO 4) | 15 kbps | dtoverlay=w1-gpio |
DS18B20 Temp Sensor |
For headless deployments or automated fleet provisioning, skip the blue TUI screen. Use the non-interactive CLI flags directly in your bash setup scripts:
sudo raspi-config nonint do_i2c 0 (0 = Enable, 1 = Disable)sudo raspi-config nonint do_serial 2 (2 = Disable login shell, Enable hardware)
Project Build: Serial GPS & I2C Sensor Logger
To demonstrate the necessity of raspi-config, we will build a data logger that reads ambient environmental data over I2C and parses NMEA GPS sentences over the hardware UART. The Raspberry Pi 5 utilizes the RP1 southbridge chip, which handles the GPIO multiplexing differently than the legacy BCM2711, making proper device tree configuration via raspi-config mandatory.
Parts List
- Microcontroller: Raspberry Pi 5 (8GB RAM)
- Environmental Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652)
- GPS Module: Adafruit Ultimate GPS Breakout (Product ID: 746)
- Wiring: 24 AWG silicone jumper wires, 4.7kΩ pull-up resistors (if not using Adafruit breakouts with onboard pull-ups)
Pin Mapping Table
| Module | Module Pin | Pi 5 Physical Pin | Pi 5 GPIO / Function |
|---|---|---|---|
| BME280 | VIN | 1 | 3.3V Power |
| BME280 | GND | 6 | Ground |
| BME280 | SCK (SCL) | 5 | GPIO 3 (SCL) |
| BME280 | SDI (SDA) | 3 | GPIO 2 (SDA) |
| GPS | VIN | 17 | 3.3V Power |
| GPS | GND | 20 | Ground |
| GPS | TX | 10 | GPIO 15 (RXD / UART RX) |
| GPS | RX | 8 | GPIO 14 (TXD / UART TX) |
Configuration Steps
- Open the terminal and launch the tool:
sudo raspi-config - Navigate to 3 Interface Options.
- Select I4 I2C, choose Yes to enable the ARM I2C interface.
- Return to the menu, select I5 Serial Port.
- When asked "Would you like a login shell to be accessible over serial?", select No. (Crucial: leaving this Yes will corrupt your GPS data with Linux boot logs).
- When asked "Would you like the serial port hardware to be enabled?", select Yes.
- Exit and reboot the Pi:
sudo reboot.
Python Code & Error Handling
Before running this script, install the required libraries: sudo apt install python3-smbus2 python3-serial. This script includes explicit pin definitions and robust error handling for common bus failures.
#!/usr/bin/env python3
"""
Dual-Bus Logger: BME280 (I2C) + GPS (UART)
Target: Raspberry Pi 5 (8GB) / Bookworm 64-bit
"""
import time
import sys
from smbus2 import SMBus, i2c_msg
import serial
# ==========================================
# PIN & INTERFACE DEFINITIONS
# ==========================================
I2C_BUS_ID = 1 # /dev/i2c-1 (Physical pins 3 & 5)
BME280_I2C_ADDR = 0x77 # Default Adafruit BME280 address
GPS_SERIAL_PORT = '/dev/ttyAMA0' # Pi 5 primary hardware UART (Physical pins 8 & 10)
GPS_BAUD_RATE = 9600 # Standard NMEA baud rate
def read_bme280_raw_data(bus, address):
"""Reads raw compensation data from BME280 registers."""
try:
# Request 8 bytes starting from register 0xF7 (pressure, temp, humidity)
msg = i2c_msg.read(address, 8)
bus.i2c_rdwr(msg)
data = list(msg)
# Simplified parsing for demonstration (raw ADC values)
raw_temp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
return raw_temp
except OSError as e:
print(f"[I2C ERROR] Hardware communication failed: {e}")
return None
def read_gps_sentence(ser):
"""Reads and filters NMEA sentences from the UART stream."""
try:
if ser.in_waiting > 0:
line = ser.readline().decode('ascii', errors='replace').strip()
if line.startswith('$GPGGA') or line.startswith('$GNGGA'):
return line
except serial.SerialException as e:
print(f"[UART ERROR] Serial port disconnected or locked: {e}")
return None
def main():
print("Initializing Hardware Interfaces...")
# Initialize I2C Bus
try:
i2c_bus = SMBus(I2C_BUS_ID)
print(f"[OK] I2C Bus {I2C_BUS_ID} opened.")
except FileNotFoundError:
print("[FATAL] /dev/i2c-1 not found. Did you enable I2C in raspi-config and reboot?")
sys.exit(1)
except PermissionError:
print("[FATAL] Permission denied. Run with sudo or add user to i2c group.")
sys.exit(1)
# Initialize UART
try:
gps_serial = serial.Serial(
port=GPS_SERIAL_PORT,
baudrate=GPS_BAUD_RATE,
timeout=1
)
print(f"[OK] UART {GPS_SERIAL_PORT} opened.")
except serial.SerialException as e:
print(f"[FATAL] Could not open {GPS_SERIAL_PORT}: {e}")
sys.exit(1)
try:
while True:
temp_raw = read_bme280_raw_data(i2c_bus, BME280_I2C_ADDR)
gps_nmea = read_gps_sentence(gps_serial)
if temp_raw is not None:
print(f"BME280 Raw Temp ADC: {temp_raw}")
if gps_nmea:
print(f"GPS NMEA: {gps_nmea}")
time.sleep(1.0)
except KeyboardInterrupt:
print("\nShutting down gracefully...")
finally:
i2c_bus.close()
gps_serial.close()
if __name__ == '__main__':
main()
Debugging: When raspi-config Settings Fail to Apply
Even after running raspi-config, you may encounter kernel-level rejections when your Python script attempts to poll the hardware. Here are the exact error strings and their ranked causes.
1. FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'
- Cause A (Most Likely): You forgot to reboot.
raspi-configmodifies the boot configuration, but the kernel device tree is only parsed during the bootloader sequence. - Cause B: A manual override exists in
/boot/firmware/config.txt. Look fordtparam=i2c_arm=offor a commented-out#dtparam=i2c_arm=onand fix it.
2. PermissionError: [Errno 13] Permission denied: '/dev/ttyAMA0'
- Cause A (Most Likely): Your user lacks the dialout group permissions required to access serial TTY devices.
- Fix: Run
sudo usermod -a -G dialout $USER, then log out and log back in to refresh the group token. - Cause B: The Bluetooth module has claimed the primary UART. On the Pi 5, you may need to add
dtoverlay=disable-bttoconfig.txtto free up/dev/ttyAMA0for the GPIO header.
3. OSError: [Errno 121] Remote I/O error
- Cause A (Most Likely): Missing pull-up resistors on the I2C SDA/SCL lines. The RP1 chip requires clean logic edges.
- Cause B: Incorrect I2C address. Run
i2cdetect -y 1in the terminal to scan the bus and verify the physical hex address of your sensor.
- Verify the Device Node: Run
ls -l /dev/i2c*andls -l /dev/ttyAMA*. If the files don't exist, the kernel overlay failed to load. - Check Group Memberships: Run
groups. You must seei2c,spi, anddialoutin the output. - Inspect the Boot Log: Run
dmesg | grep -i i2cordmesg | grep -i uartto see if the kernel threw a pin-muxing conflict error during boot.
Extending and Simplifying the Build
Once you have mastered the raspi-config TUI, you can manipulate the deployment pipeline to suit your specific project constraints.
How to Extend: Adding SPI Displays
If you want to add an SPI-based OLED (like the SSD1351) to this logger, return to raspi-config and enable the SPI interface. Because SPI supports multiple Chip Select (CS) lines, you can map the OLED to Physical Pin 24 (CE0) and an RFID reader to Physical Pin 26 (CE1) on the same MOSI/MISO/SCLK bus. Ensure you enable the SPI device tree overlay by verifying dtparam=spi=on is present in your boot config.
How to Simplify: Headless Fleet Provisioning
If you are building 50 of these loggers for a commercial greenhouse deployment, using the interactive raspi-config menu is unscalable. Instead, use the Raspberry Pi Imager's advanced settings (the gear icon) before flashing the SD card. You can pre-configure the hostname, SSH keys, and WiFi.
For the hardware interfaces, create a custom config.txt file on your host machine and inject it into the /boot/firmware/ partition of the flashed SD card before the Pi ever boots. By pre-populating dtparam=i2c_arm=on and enable_uart=1, the Pi will boot with the correct device tree overlays already active, entirely bypassing the need to run raspi-config on the target device. For comprehensive details on device tree parameters, consult the official Raspberry Pi configuration documentation and the Adafruit BME280 wiring guide.






