If you need to connect an Ethernet-only device—like a retro gaming console, a legacy CNC machine, or an older desktop PC—to a modern WPA2/WPA3 wireless network, you can use a Raspberry Pi as a WiFi adapter. By configuring the Pi's wireless interface (wlan0) to share its connection with its wired Ethernet port (eth0), the Pi acts as a transparent wireless bridge. The connected device simply requests an IP via DHCP and accesses the network exactly as if it were plugged directly into your main router.

This guide walks through the hardware selection, the exact nmcli (NetworkManager) commands required on Raspberry Pi OS Bookworm, and a complete Python monitoring script with hardware GPIO indicators to track bridge health on your workbench.

Hardware Selection and Spec Comparison

Not every Pi is ideal for this job. The bottleneck in a wireless bridge is almost always the WiFi chipset's throughput and the Ethernet PHY's bus architecture. Below is a data-dense comparison of current Raspberry Pi variants for use as a dedicated WiFi-to-Ethernet adapter.

Board Variant WiFi Standard Ethernet Architecture Max Bridge Throughput Idle Power Draw (5V) Approx. 2026 Price
Pi 4 Model B (4GB) 802.11ac (Dual-band) True Gigabit (RGMI) ~85 Mbps ~3.0W $55
Pi 5 (4GB) 802.11ac (Dual-band) True Gigabit (RGMI) ~95 Mbps ~4.5W $80
Pi 3 Model B+ 802.11ac (Dual-band) Gigabit over USB 2.0 ~45 Mbps ~2.8W $35 (Used)
Pi Zero 2 W 802.11n (2.4GHz only) None (Requires USB OTG) ~25 Mbps ~1.2W $15
Bench Note: The Pi 4 Model B is the sweet spot for this build. The Pi 5 offers marginally better WiFi throughput but runs hotter and draws more idle power, which matters if this adapter is running 24/7 in a media cabinet. Avoid the Pi 3B+ if you need sustained transfers, as its USB 2.0 bus limits Ethernet to roughly 300 Mbps theoretical, and real-world bridging cuts that in half.

Parts List and Pin Mapping

This build targets the Raspberry Pi 4 Model B (4GB variant) running Raspberry Pi OS (64-bit, Bookworm). Bookworm is critical here because it replaces dhcpcd with NetworkManager as the default network stack.

Required Components

  • Compute: Raspberry Pi 4 Model B (4GB)
  • Storage: 32GB Samsung EVO Plus microSD (A2 rated)
  • Power: Official 27W USB-C PD Power Supply (prevents brownout warnings under load)
  • Networking: Cat6 UTP patch cable (keep it under 3 meters for optimal signal integrity)
  • Indicators: 2x 3mm LEDs (Green/Red) with 330Ω current-limiting resistors

GPIO Pin Mapping for Status Indicators

Since this adapter will likely run headless, we map two physical GPIO pins to external LEDs to provide instant visual feedback on bridge health without needing to SSH into the device.

Physical Pin BCM GPIO Function Hardware Connection
6 GND Common Ground Connected to LED cathodes (-)
38 GPIO 20 Fault / Error LED 330Ω resistor to Red LED anode (+)
40 GPIO 21 Bridge Active LED 330Ω resistor to Green LED anode (+)
8 GPIO 14 (TXD) UART Debug (Optional) USB-to-TTL serial adapter for headless recovery

Step-by-Step Bridge Configuration

We use nmcli (NetworkManager Command Line Interface) to create a shared connection profile. This automatically configures dnsmasq under the hood to hand out IP addresses to devices plugged into the Pi's Ethernet port.

  1. Connect to Upstream WiFi:
    nmcli device wifi connect 'YourSSID' password 'YourPassword' ifname wlan0
  2. Create the Ethernet Bridge Profile:
    nmcli connection add type ethernet ifname eth0 con-name eth0-bridge ipv4.method shared ipv6.method ignore
    Note: The 'shared' method tells NetworkManager to act as a DHCP server and NAT router on this interface.
  3. Activate the Bridge:
    nmcli connection up eth0-bridge
  4. Verify the Subnet:
    ip addr show eth0
    You should see an IP address assigned to eth0, typically in the 10.42.0.0/16 range. This is excellent because it prevents subnet collisions with standard home routers that use 192.168.1.x.
Security Caveat: By default, NetworkManager's 'shared' mode enables IP forwarding and masquerading (NAT). Devices on the Ethernet side can reach the internet, but they are isolated from direct inbound connections from the main WiFi network. If you need the bridged device to be fully visible on the main LAN (e.g., for local casting or mDNS discovery), you must change ipv4.method from 'shared' to 'link-local' and configure a manual static route on your main router, which is significantly more complex.

Python Automation and Error Handling Script

NetworkManager can occasionally drop the shared bridge if the upstream WiFi reconnects. The following Python 3 script monitors the bridge state, restarts it if it fails, and drives the GPIO status LEDs. It targets the Pi 4 Model B on Bookworm and uses the gpiozero library.

#!/usr/bin/env python3
import subprocess
import time
import sys
from gpiozero import LED

# --- Pin Definitions (BCM Numbering) ---
PIN_STATUS_GREEN = 21  # Physical Pin 40 (Bridge Active)
PIN_STATUS_RED = 20    # Physical Pin 38 (Bridge Fault)

green_led = LED(PIN_STATUS_GREEN)
red_led = LED(PIN_STATUS_RED)

BRIDGE_CON_NAME = 'eth0-bridge'
INTERFACE = 'eth0'

def get_nmcli_status():
    """Checks NetworkManager state for the specific bridge connection."""
    try:
        result = subprocess.run(
            ['nmcli', '-g', 'GENERAL.STATE', 'connection', 'show', BRIDGE_CON_NAME],
            capture_output=True, text=True, check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        return e.stderr.strip()

def restart_bridge():
    """Attempts to bring the bridge connection up."""
    try:
        subprocess.run(
            ['nmcli', 'connection', 'up', BRIDGE_CON_NAME, 'ifname', INTERFACE],
            capture_output=True, text=True, check=True
        )
        return True, ""
    except subprocess.CalledProcessError as e:
        return False, e.stderr.strip()

def main():
    print("Starting WiFi-to-Ethernet Bridge Monitor...")
    green_led.off()
    red_led.blink(on_time=0.5, off_time=0.5) # Boot sequence
    time.sleep(2)

    while True:
        state = get_nmcli_status()
        
        if 'activated' in state.lower():
            green_led.on()
            red_led.off()
        else:
            green_led.off()
            red_led.on()
            print(f"[WARN] Bridge state: {state}. Attempting restart...")
            
            success, err_msg = restart_bridge()
            if not success:
                print(f"[ERROR] Failed to restart bridge: {err_msg}")
                
                # Handle specific known NetworkManager errors
                if "No suitable device found for this connection" in err_msg:
                    print("[DIAG] Interface eth0 is missing or renamed. Check 'ip link'.")
                elif "Secrets were required, but no agent was available" in err_msg:
                    print("[DIAG] Upstream WiFi password missing or keyring locked.")
                    
                red_led.blink(on_time=0.1, off_time=0.1) # Fast blink on hard fault
                time.sleep(30) # Backoff before retrying
                continue
                
        time.sleep(10) # Polling interval

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print("\nShutting down monitor.")
        green_led.off()
        red_led.off()
        sys.exit(0)

Save this as bridge_monitor.py and set it up as a systemd service so it survives reboots. Ensure the script runs with sudo privileges, as nmcli requires root to alter connection states when not invoked from an interactive user session.

Debugging: When the Bridge Fails

If your Ethernet-only device cannot pull an IP address or reach the internet, do not immediately blame the Pi's hardware. Network bridging involves three distinct layers: the upstream WiFi link, the Pi's NAT engine, and the downstream DHCP handshake.

The First Three Things to Check

  1. Upstream WiFi Isolation: Verify the Pi itself has internet. Run ping -I wlan0 8.8.8.8. If this fails, the Pi has dropped from the main router. The bridge cannot route traffic it cannot reach.
  2. Interface Naming Gotchas: On recent Bookworm kernel updates, the Ethernet interface may be renamed from eth0 to end0 due to predictable network interface naming. Run ip link. If you see end0, you must recreate the nmcli profile using ifname end0.
  3. Downstream DHCP Client: Ensure the device plugged into the Pi's Ethernet port is actually configured to request an IP via DHCP. If it has a hardcoded static IP (common in industrial PLCs and retro consoles), it will not communicate with the Pi's 10.42.x.x subnet.

Exact Error Strings and Ranked Causes

When the Python script or manual nmcli commands fail, you will encounter specific stderr outputs. Here is how to decode them:

Exact Error String Most Likely Cause Resolution
Error: Connection activation failed: No suitable device found for this connection. 1. Interface renamed to end0.
2. Ethernet PHY is disabled in /boot/firmware/config.txt.
Run ip link to verify the interface name. Delete the old profile and recreate it with the correct ifname.
Error: Connection 'eth0-bridge' does not exist. 1. Profile was not saved to disk.
2. NetworkManager database corrupted.
Re-run the nmcli connection add command. Check /etc/NetworkManager/system-connections/ for the file.
Error: Connection activation failed: IP configuration could not be reserved 1. Subnet collision with upstream.
2. dnsmasq service failed to bind.
Force a specific subnet: nmcli connection modify eth0-bridge ipv4.addresses 192.168.50.1/24 and restart NetworkManager.

Extending and Simplifying the Build

Depending on your deployment environment, you may want to scale this project up for better visibility, or scale it down for faster deployment.

How to Extend the Build

  • Add an OLED Dashboard: Wire a Waveshare 1.3" I2C OLED (SSD1306 driver) to GPIO 2 (SDA) and GPIO 3 (SCL). Use the luma.oled Python library to display the upstream WiFi RSSI, the number of active DHCP leases (parsed from /var/lib/misc/dnsmasq.leases), and real-time throughput.
  • Integrate a UPS HAT: If this adapter is powering a sensitive legacy device, a sudden power loss can corrupt the legacy device's filesystem. Add a PiJuice or Waveshare UPS HAT to ensure graceful shutdowns via I2C signaling when mains power drops.

How to Simplify the Build

If you do not want to maintain a Python script or wrestle with nmcli syntax, bypass Raspberry Pi OS entirely. Flash OpenWrt onto the Pi's microSD card. OpenWrt includes a native "Relay Bridge" and "Routed Client" GUI via LuCI. You simply select the upstream WiFi network, check the "Create / Assign firewall-zone" box, and bind it to the LAN interface. It handles the DHCP, NAT, and watchdog restarts natively at the OS level with zero custom code required.

For further reading on NetworkManager's shared connection architecture, refer to the official nmcli documentation. For hardware-specific interface naming and PHY configurations on the Pi 4, consult the Raspberry Pi configuration reference.