Mapping a local subnet manually is a tedious process of pinging IP ranges and checking ARP tables. By turning a Raspberry Pi into a dedicated network edge scanner, you can automate the discovery of local devices and generate a live raspberry pi network diagram that exports to a visual graph while displaying real-time node stats on a physical OLED screen. This project uses Layer 2 ARP scanning to bypass host-level ICMP firewalls, ensuring you see every active device on your LAN, from smart bulbs to hidden IoT hubs.
Project Overview & Hardware Requirements
This build targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm, 64-bit). The Pi 5's PCIe-exposed architecture and upgraded Gigabit Ethernet controller make it ideal for high-speed subnet sweeping without bottlenecking the I2C bus used for the display. We will also use a USB 2.5G Ethernet adapter to create a dual-homed setup, allowing the Pi to scan a specific isolated LAN segment while remaining connected to your main WAN.
Parts List:
- Compute: Raspberry Pi 5 (8GB) - ~$80
- Display: 128x64 I2C OLED (SSD1306 driver, 3.3V logic) - ~$12
- Network: Realtek RTL8156 USB 2.5G Ethernet Adapter (for isolated LAN port) - ~$18
- Indicators: 2x 5mm LEDs (1 Green, 1 Red), 2x 330Ω resistors - ~$2
- Consumables: Female-to-female Dupont jumper wires, solderless breadboard or Pi GPIO hammer header.
Network Interface & GPIO Pin Mapping
Before wiring, it is critical to map the physical pins to the BCM GPIO numbers. The Raspberry Pi 5 maintains the standard 40-pin header layout, but I2C bus speeds default to 100kHz. We will configure it for 400kHz later to ensure the OLED refreshes fast enough to draw the network diagram nodes without flickering.
| Component | Pi 5 Pin (Physical) | BCM GPIO | Function | Wiring Notes & Constraints |
|---|---|---|---|---|
| OLED VCC | 1 | 3.3V Power | Logic Power | Do NOT connect to Pin 2 (5V) unless your OLED has an onboard regulator. |
| OLED GND | 6 | GND | Common Ground | Must share ground with the Pi and LED circuit. |
| OLED SDA | 3 | GPIO 2 | I2C Data | Pi 5 has onboard 1.8kΩ pull-ups; no external resistors needed. |
| OLED SCL | 5 | GPIO 3 | I2C Clock | Keep wire length under 10cm to prevent capacitance issues at 400kHz. |
| WAN LED (+) | 11 | GPIO 17 | Status High | Connect anode to GPIO 17 via a 330Ω current-limiting resistor. |
| LAN LED (+) | 13 | GPIO 27 | Status High | Connect anode to GPIO 27 via a 330Ω current-limiting resistor. |
| LEDs (-) | 9 | GND | Cathode Ground | Common ground for both LED cathodes. |
Wiring the OLED and Status LEDs
Follow these steps to assemble the hardware. Precision here prevents I2C bus lockups and blown GPIO pins.
- De-energize the system: Unplug the Raspberry Pi 5 power supply completely. Never wire GPIO pins while the board is live; a slipped jumper on the 5V rail will instantly destroy the Pi 5's power management IC (PMIC).
- Connect the I2C Display: Using female-to-female jumpers, connect the SSD1306 OLED VCC to Physical Pin 1 (3.3V), GND to Pin 6, SDA to Pin 3, and SCL to Pin 5.
- Wire the Status LEDs: Insert the 330Ω resistors into the breadboard. Connect GPIO 17 (Pin 11) through a resistor to the Green LED anode. Connect GPIO 27 (Pin 13) through a resistor to the Red LED anode. Tie both cathodes to Physical Pin 9 (GND).
- Attach the USB NIC: Plug the Realtek RTL8156 USB 2.5G adapter into one of the blue USB 3.0 ports on the Pi 5. Connect an Ethernet cable from your isolated target LAN to this adapter.
- Enable I2C and set bus speed: Boot the Pi, open a terminal, and run
sudo raspi-config. Navigate to Interface Options > I2C and enable it. Next, edit the boot config to increase I2C speed:sudo nano /boot/firmware/config.txtand add the linedtparam=i2c_arm_baudrate=400000. Reboot.
Python Network Scanner & Diagram Code
This script targets the Raspberry Pi 5 (Bookworm OS). It uses scapy for Layer 2 ARP scanning and luma.oled for the display. It also generates a Graphviz .dot file, which is the industry standard for rendering programmatic network diagrams.
Prerequisites: Install the required libraries via terminal:
sudo apt update && sudo apt install python3-pip graphviz -y
pip3 install scapy luma.oled luma.core pillow gpiod --break-system-packages
#!/usr/bin/env python3
# network_diagram_scanner.py
# Target: Raspberry Pi 5 (Bookworm 64-bit)
import time
import socket
import subprocess
import scapy.all as scapy
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
from PIL import ImageFont
import RPi.GPIO as GPIO
# --- PIN DEFINITIONS ---
WAN_LED_PIN = 17 # BCM 17 (Physical 11)
LAN_LED_PIN = 27 # BCM 27 (Physical 13)
# --- I2C SETUP ---
try:
serial = i2c(port=1, address=0x3C)
oled = ssd1306(serial, width=128, height=64)
except Exception as e:
print(f'FATAL: OLED initialization failed. Check I2C wiring. Error: {e}')
exit(1)
# --- GPIO SETUP ---
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(WAN_LED_PIN, GPIO.OUT)
GPIO.setup(LAN_LED_PIN, GPIO.OUT)
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
except Exception:
ip = '127.0.0.1'
finally:
s.close()
return ip
def scan_network(ip_range):
GPIO.output(LAN_LED_PIN, GPIO.HIGH)
arp_request = scapy.ARP(pdst=ip_range)
broadcast = scapy.Ether(dst='ff:ff:ff:ff:ff:ff')
arp_broadcast = broadcast / arp_request
# Timeout set to 2s to prevent hanging on unresponsive segments
answered_list = scapy.srp(arp_broadcast, timeout=2, verbose=False)[0]
devices = []
for element in answered_list:
devices.append({'ip': element[1].psrc, 'mac': element[1].hwsrc})
GPIO.output(LAN_LED_PIN, GPIO.LOW)
return devices
def generate_dot_diagram(local_ip, devices):
dot_content = 'digraph NetworkTopology {\n'
dot_content += ' node [shape=box, style=filled, fillcolor=lightblue];\n'
dot_content += f' "Pi5_Router" [fillcolor=gold, label="Pi5\n{local_ip}"];\n'
for dev in devices:
if dev['ip'] != local_ip:
dot_content += f' "Pi5_Router" -> "{dev["ip"]}" [label="{dev["mac"]}"];\n'
dot_content += '}\n'
with open('/home/pi/network_topology.dot', 'w') as f:
f.write(dot_content)
# Render to PNG using Graphviz
subprocess.run(['dot', '-Tpng', '/home/pi/network_topology.dot', '-o', '/home/pi/network_diagram.png'])
def draw_oled_ui(local_ip, device_count):
font = ImageFont.load_default()
with canvas(oled) as draw:
draw.text((0, 0), text='NET SCANNER v1.2', font=font, fill='white')
draw.text((0, 15), text=f'Host: {local_ip}', font=font, fill='white')
draw.text((0, 30), text=f'Nodes Found: {device_count}', font=font, fill='white')
draw.rectangle((0, 50, 128, 64), outline='white')
draw.text((5, 52), text='Diagram Exported OK', font=font, fill='white')
if __name__ == '__main__':
try:
local_ip = get_local_ip()
subnet = f'{".".join(local_ip.split(".")[:3])}.0/24'
GPIO.output(WAN_LED_PIN, GPIO.HIGH)
print(f'Scanning subnet: {subnet}')
devices = scan_network(subnet)
print(f'Discovered {len(devices)} devices.')
generate_dot_diagram(local_ip, devices)
draw_oled_ui(local_ip, len(devices))
print('Network diagram saved to /home/pi/network_diagram.png')
time.sleep(10) # Keep OLED on for 10s
except PermissionError:
print('ERROR: Raw sockets require root. Run with sudo.')
except KeyboardInterrupt:
print('Scan aborted by user.')
finally:
GPIO.cleanup()
oled.cleanup()
Execute the script with root privileges (required for Scapy's raw socket manipulation):
sudo python3 network_diagram_scanner.py
Debugging: Exact Error Strings & First Checks
When working with raw network sockets and I2C hardware on the Pi 5, you will inevitably hit a few snags. If your script fails, here is the diagnostic decision tree.
The First Three Things to Check
- Verify I2C Address: Run
sudo i2cdetect -y 1. You should see3cin the grid. If the grid is empty, your SDA/SCL wires are swapped or the OLED is dead. - Check Interface Names: Run
ip a. Ensure your USB Ethernet adapter is recognized (usuallyeth1orenx...). If it's missing, the RTL8156 driver isn't loaded. Runsudo apt install r8152-dkms. - Confirm Root Execution: Scapy crafts custom Ethernet frames. The Linux kernel blocks non-root users from doing this. You must use
sudo.
Ranked Causes for Common Error Strings
Error 1: PermissionError: [Errno 1] Operation not permitted
- Cause A (Most Likely): You ran the script without
sudo. Scapy requiresCAP_NET_RAWcapabilities. - Cause B: AppArmor or SELinux is restricting Python's network access (rare on default Raspberry Pi OS, but possible on custom hardened images).
Error 2: luma.core.error.DeviceNotFoundError: I2C device not found on address 0x3C
- Cause A: The OLED uses address
0x3Dinstead of0x3C(check the silkscreen on the back of the PCB). Changeaddress=0x3Cto0x3Din the code. - Cause B: I2C is disabled in
raspi-config. - Cause C: The Pi 5's I2C bus is locked up due to a previous crash. Fix by running
sudo rmmod i2c_bcm2835 && sudo modprobe i2c_bcm2835.
Error 3: OSError: [Errno 101] Network is unreachable
- Cause A: The Pi doesn't have an active IP address on the interface Scapy is trying to bind to. This happens if the Ethernet cable is unplugged or the DHCP lease expired.
- Cause B: You are trying to scan a subnet that doesn't match your current interface's IP range. Scapy will refuse to send ARP requests out of an interface that isn't routed for that subnet.
Extending and Simplifying the Build
Depending on your deployment environment, you may want to scale this project up into a permanent network monitor or strip it down for a quick one-off audit.
How to Simplify (The Headless Audit)
If you are doing a temporary site survey and don't want to wire the OLED or LEDs, delete the luma and RPi.GPIO imports and their associated function calls. Rely entirely on the Graphviz .dot export. You can pull the generated network_diagram.png off the Pi via SCP (scp pi@ip:/home/pi/network_diagram.png ./) and view it on your laptop. This reduces the hardware requirement to just the Pi and the USB NIC.
How to Extend (Continuous MQTT Monitoring)
To turn this from a manual scanner into an active topology monitor, integrate the paho-mqtt library. Wrap the scan_network() function in a while True loop with a 60-second sleep interval. Publish the discovered MAC addresses and IP pairs to an MQTT broker (like Mosquitto or Eclipse). From there, you can ingest the data into Home Assistant or Grafana to build a real-time, interactive web-based raspberry pi network diagram that alerts you via Telegram or Discord when an unauthorized MAC address joins the subnet.
For deeper packet analysis, refer to the official Scapy documentation to implement DHCP sniffing, which will allow your Pi to capture device hostnames as they request IP addresses, adding a layer of human-readable identification to your topology map.






