Network Activity LED (GPIO 5)
The kernel doesn't drive an activity LED on a header GPIO directly, so a tiny userspace daemon polls the interface's byte counter from /proc/net/dev and blinks the LED on every change. Resistor + wiring follow the Pi LEDS section (220 Ω from GPIO 5 to LED anode, cathode to GND).
One script, parameterised by interface and LED pin via CLI args — the same binary drives an eth0 LED, a wlan0 LED, or both at once on different GPIOs.
#!/usr/bin/env python3
"""Blink an LED briefly whenever <iface> has RX/TX traffic.
Usage: net-activity-led.py <iface> [gpio] [min_bytes]
iface - e.g. eth0, end0, wlan0
gpio - BCM pin number (default 5)
min_bytes - ignore counter deltas below this (default 0; use ~1500 on wlan0
to filter beacons / mDNS / background chatter)
"""
import sys
from gpiozero import LED
from time import sleep
IFACE = sys.argv[1]
LED_PIN = int(sys.argv[2]) if len(sys.argv) > 2 else 5
MIN_BYTES = int(sys.argv[3]) if len(sys.argv) > 3 else 0
POLL = 0.05 # seconds between counter reads
BLINK = 0.03 # LED on-time per detected activity tick
led = LED(LED_PIN)
def iface_bytes(name):
with open("/proc/net/dev") as f:
for line in f:
n, _, rest = line.partition(":")
if n.strip() == name:
cols = rest.split()
return int(cols[0]) + int(cols[8]) # rx_bytes + tx_bytes
return 0
prev = iface_bytes(IFACE)
while True:
sleep(POLL)
now = iface_bytes(IFACE)
if now - prev > MIN_BYTES:
led.on(); sleep(BLINK); led.off()
prev = now
Install it:
sudo install -m 0755 net-activity-led.py /usr/local/bin/net-activity-led.py
Interface name on Pi 4/5 may be end0 rather than eth0 for wired — check with ip -br link. The wireless interface is wlan0 on all current Pi OS releases.
Setup A — eth0 on GPIO 5
/etc/systemd/system/net-activity-led-eth.service:
[Unit]
Description=Ethernet activity LED (eth0 → GPIO 5)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/net-activity-led.py eth0 5
Restart=on-failure
RestartSec=2
User=pi
Group=gpio
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
Setup B — wlan0 on GPIO 5
/etc/systemd/system/net-activity-led-wlan.service:
[Unit]
Description=WLAN activity LED (wlan0 → GPIO 5)
Requires=sys-subsystem-net-devices-wlan0.device
After=sys-subsystem-net-devices-wlan0.device network-online.target
Wants=network-online.target
[Service]
Type=simple
# 3rd arg = min bytes per tick; ~1500 filters Wi-Fi beacons / mDNS chatter.
ExecStart=/usr/bin/python3 /usr/local/bin/net-activity-led.py wlan0 5 1500
Restart=on-failure
RestartSec=2
User=pi
Group=gpio
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
Two differences from setup A:
Requires=/After=sys-subsystem-net-devices-wlan0.device— udev creates this unit automatically once the kernel publisheswlan0, so the script doesn't start before the interface exists.- The extra
1500argument enables the noise filter so background beacons / ARP / mDNS don't keep the LED on solid.
Enable
Pick whichever setup you want (or run both on different GPIOs by giving each its own pin in ExecStart):
sudo systemctl daemon-reload
sudo systemctl enable --now net-activity-led-eth.service # or -wlan
systemctl status net-activity-led-eth.service
journalctl -u net-activity-led-eth.service -f
The same polling pattern works for other activity sources — swap iface_bytes() for a reader of /proc/diskstats (disk), /proc/stat (CPU) or /sys/class/thermal/thermal_zone0/temp (thermal threshold) and blink on change.