Project Overview & Bill of Materials
When deploying a headless Raspberry Pi for home automation, NAS duties, or sensor logging, relying on DHCP for IP assignment is a gamble. Router reboots or lease expirations can shift your Pi's IP address, breaking SSH shortcuts, MQTT broker connections, and NFS mounts. To build a reliable embedded node, you need to configure network Raspberry Pi interfaces with static reservations and, ideally, a physical way to verify the connection state without plugging in an HDMI monitor.
This guide targets the Raspberry Pi 4 Model B (4GB) and Raspberry Pi 5 (4GB) running Raspberry Pi OS (64-bit, Bookworm). This distinction is critical: Bookworm completely deprecated the legacy dhcpcd daemon in favor of NetworkManager. If you are following older tutorials that tell you to edit /etc/dhcpcd.conf, they will fail on modern Pi OS.
Estimated Time: 45 minutes
Target Board: Raspberry Pi 4B / 5 (Bookworm OS)
Parts List
- Compute: Raspberry Pi 4 Model B (4GB) or Raspberry Pi 5 (4GB)
- Storage: 32GB microSD card (SanDisk Extreme A2 rating recommended for I/O longevity)
- Display Module: Waveshare 1.3" OLED HAT (SH1106 driver, 128x64 resolution, I2C interface)
- Power: Official USB-C Power Supply (15W/3A for Pi 4; 27W PD for Pi 5)
- Networking: Cat6 Ethernet patch cable (or 2.4GHz/5GHz Wi-Fi credentials)
Step-by-Step: Configure Network Raspberry Pi via NetworkManager
NetworkManager uses the nmcli command-line tool. Unlike the old dhcpcd method which required manual file editing and service restarts, nmcli modifies connection profiles directly and applies them on the fly.
- Identify your active interface.
Runnmcli device status. You will seeeth0(Ethernet) orwlan0(Wi-Fi) listed asconnected. Note the exact NAME under the 'CONNECTION' column, usually 'Wired connection 1' for Ethernet. - Assign a Static IP Address.
Assuming your subnet is 192.168.1.x and your router is 192.168.1.1, modify the connection profile. Replace 'Wired connection 1' with your actual connection name if it differs.sudo nmcli con mod 'Wired connection 1' ipv4.addresses 192.168.1.50/24 sudo nmcli con mod 'Wired connection 1' ipv4.gateway 192.168.1.1 sudo nmcli con mod 'Wired connection 1' ipv4.dns '1.1.1.1 8.8.8.8' sudo nmcli con mod 'Wired connection 1' ipv4.method manual - Apply the configuration.
Bring the connection down and back up to force the new IP binding:
Note: If you are doing this over SSH via the old DHCP IP, your session will drop immediately. Reconnect using the new static IP (192.168.1.50).sudo nmcli con down 'Wired connection 1' && sudo nmcli con up 'Wired connection 1' - Verify the routing table.
Runip route show. Ensure the default gateway points to your router. For deeper OS-level networking context, refer to the Debian NetworkManager documentation.
Hardware Pin Mapping for I2C OLED Monitor
To eliminate the need to scan your network for the Pi's IP address after a power outage, we will wire a Waveshare 1.3" OLED HAT directly to the GPIO header. This display uses the I2C bus, which requires only four physical connections.
| Raspberry Pi GPIO Pin | BCM Number | Waveshare OLED HAT Pin | Function / Notes |
|---|---|---|---|
| Pin 1 | 3.3V Power | VCC | Logic level power (Do not use 5V, SH1106 is 3.3V tolerant) |
| Pin 6 | Ground | GND | Common ground reference |
| Pin 3 | GPIO 2 (SDA1) | SDA | I2C Data Line (Requires physical pull-up resistors, built into Pi) |
| Pin 5 | GPIO 3 (SCL1) | SCL | I2C Clock Line |
Before proceeding to the code, ensure the I2C interface is enabled. Run sudo raspi-config, navigate to Interface Options > I2C, and enable it. Reboot the Pi, then verify the display is detected at address 0x3C by running i2cdetect -y 1.
Live Network Status Python Script
This script polls the active network interface for its IPv4 address and subnet mask, then renders it to the SH1106 OLED. It relies on the luma.oled library for hardware abstraction and psutil for network statistics. Install the dependencies via pip: pip3 install luma.oled psutil netifaces.
For comprehensive hardware driver details, consult the Luma OLED ReadTheDocs.
import time
import socket
import psutil
import netifaces
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import sh1106
from PIL import ImageFont
# --- Pin & Interface Definitions ---
I2C_PORT = 1 # BCM I2C1 (Pins 3 & 5)
I2C_ADDRESS = 0x3C # Default SH1106 address
UPDATE_INTERVAL = 5 # Seconds between screen refreshes
def get_primary_interface():
"""Finds the first interface with a valid IPv4 address, excluding loopback."""
for iface in netifaces.interfaces():
addrs = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addrs and iface != 'lo':
ip_info = addrs[netifaces.AF_INET][0]
if not ip_info['addr'].startswith('127.'):
return iface, ip_info['addr']
return None, 'No IP'
def draw_display(device, font_large, font_small):
while True:
try:
iface, ip_addr = get_primary_interface()
stats = psutil.net_io_counters(pernic=True).get(iface, None)
with canvas(device) as draw:
# Draw Header
draw.text((0, 0), 'Network Status', font=font_small, fill='white')
draw.line([(0, 12), (128, 12)], fill='white')
# Draw IP Address
draw.text((0, 16), f'IP: {ip_addr}', font=font_large, fill='white')
# Draw Interface Name & Traffic
if stats and iface:
mb_sent = stats.bytes_sent / (1024 * 1024)
mb_recv = stats.bytes_recv / (1024 * 1024)
draw.text((0, 36), f'IF: {iface}', font=font_small, fill='white')
draw.text((0, 48), f'DL: {mb_recv:.1f} MB UL: {mb_sent:.1f} MB', font=font_small, fill='white')
else:
draw.text((0, 36), 'IF: Disconnected', font=font_small, fill='white')
except Exception as e:
with canvas(device) as draw:
draw.text((0, 20), f'Error:\n{str(e)[:20]}', font=font_small, fill='white')
time.sleep(UPDATE_INTERVAL)
if __name__ == '__main__':
try:
# Initialize I2C serial interface
serial = i2c(port=I2C_PORT, address=I2C_ADDRESS)
device = sh1106(serial, rotate=0)
# Load fonts (using default PIL bitmap fonts for broad compatibility)
font_large = ImageFont.load_default()
font_small = ImageFont.load_default()
print('Display initialized. Starting network monitor...')
draw_display(device, font_large, font_small)
except FileNotFoundError as e:
print(f'FATAL: I2C bus not found. Is I2C enabled in raspi-config?\nDetails: {e}')
except ImportError as e:
print(f'FATAL: Missing library. Run: pip3 install luma.oled psutil netifaces\nDetails: {e}')
except KeyboardInterrupt:
print('\nMonitor stopped by user.')
Debugging: Network & Display Errors
Embedded networking often fails at the intersection of OS routing tables and hardware permissions. Here is how to diagnose the most common roadblocks when you configure network Raspberry Pi nodes.
The First Three Things to Check When It Fails
- Check Interface State: Run
nmcli device status. If your interface saysdisconnectedorunmanaged, NetworkManager is either misconfigured or another service (like legacy dhcpcd) is fighting it for control. - Verify the Default Gateway: Run
ip route. If the line starting withdefault viais missing or points to the wrong IP, your Pi can talk to the local subnet but cannot reach the internet or external DNS servers. - Isolate DNS vs. Routing: Run
ping 8.8.8.8followed byping google.com. If the IP ping works but the domain ping fails, your network is up, but your DNS configuration in NetworkManager is broken.
Exact Error Strings and Ranked Causes
Error 1: OSError: [Errno 101] Network is unreachable
- Cause A (Most Likely): Missing or incorrect default gateway in your
nmcliprofile. The OS doesn't know where to send packets destined for outside the local subnet. - Cause B: The Ethernet cable is physically unplugged, or the switch port is dead, causing the
eth0interface to drop its carrier signal. - Cause C: You assigned a static IP on the wrong subnet (e.g., Pi is 192.168.1.50, but router is 10.0.0.1).
Error 2: FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'
- Cause A (Most Likely): The I2C kernel module is not loaded. You forgot to enable it via
sudo raspi-configor failed to reboot after enabling it. - Cause B: You are running the script on a compute module or custom board where the primary I2C bus is mapped to
/dev/i2c-0or/dev/i2c-3instead of the standard1.
Error 3: PermissionError: [Errno 13] Permission denied: '/dev/i2c-1'
- Cause A: You are executing the Python script as a standard user who is not a member of the
i2cgroup. Fix this by runningsudo usermod -aG i2c $USER, then log out and log back in.
Extending and Simplifying the Build
Depending on your deployment environment, you may want to scale this project up for production or strip it down for minimal resource usage.
How to Simplify the Build
If you do not want to solder headers or allocate GPIO pins to a display, you can drop the OLED entirely and rely on mDNS (Multicast DNS). Raspberry Pi OS ships with Avahi pre-installed. This allows you to SSH into your Pi using ssh pi@raspberrypi.local regardless of what IP address the DHCP server assigns. To simplify further, use the Raspberry Pi Imager tool on your desktop; its 'Advanced Options' (Ctrl+Shift+X) menu allows you to pre-configure Wi-Fi SSIDs, static IPs, and SSH keys before you even flash the SD card, achieving a true zero-touch headless boot.
How to Extend the Build
For advanced home lab monitoring, extend the Python script to publish network statistics to an MQTT broker. By importing paho.mqtt.client, you can push the mb_recv and mb_sent variables to a Home Assistant MQTT sensor every 60 seconds. This allows you to track Pi network bandwidth usage on a dashboard and trigger automations if the Pi unexpectedly drops offline. Additionally, you can add a physical push-button to GPIO 17; programming an interrupt to trigger a graceful sudo shutdown -h now command via the button press prevents SD card corruption from hard power cuts.
Frequently Asked Questions
How do I configure network Raspberry Pi headless on first boot?
The modern, safest method is using the official Raspberry Pi Imager software on your PC or Mac. Before clicking 'Write', click the gear icon (or press Ctrl+Shift+X) to open Advanced Options. Here, you can enable SSH, set a custom username/password, input Wi-Fi SSID/password, and define a static IP address. The Imager writes these parameters to a hidden configuration partition that the Pi reads exactly once during its very first boot sequence, securely applying them without requiring a monitor or keyboard.
Why did my static IP stop working after updating to Pi OS Bookworm?
Prior to Pi OS Bookworm (released late 2023), networking was handled by dhcpcd, and static IPs were configured by appending lines to /etc/dhcpcd.conf. Bookworm replaced dhcpcd with NetworkManager. If you upgrade your OS, the old dhcpcd service is disabled, and your custom dhcpcd.conf rules are ignored. You must migrate your static IP rules using the nmcli commands outlined in this guide or via the nmtui text-based user interface.
Can I configure network Raspberry Pi to use both Wi-Fi and Ethernet simultaneously?
Yes, but the OS will not automatically 'bond' them for double speed. By default, Linux routing metrics prioritize Ethernet (eth0) over Wi-Fi (wlan0) because Ethernet generally has a lower route metric (higher priority). Traffic will flow over Ethernet unless the cable is unplugged, at which point it fails over to Wi-Fi. If you need specific traffic (like an IoT MQTT subnet) to route over Wi-Fi while internet traffic uses Ethernet, you must configure policy-based routing using ip rule and custom routing tables.
How do I find my Raspberry Pi IP address without a monitor or mDNS?
If mDNS (.local) is blocked by your enterprise router and you don't have a display, you need to scan your local subnet from another computer. Download a tool like Advanced IP Scanner (Windows) or use nmap (Linux/macOS) via the terminal: nmap -sn 192.168.1.0/24. Look for a device where the MAC address vendor is listed as 'Raspberry Pi Foundation'. Alternatively, log into your router's admin panel and check the DHCP client lease table for the Pi's hostname.






