Getting locked out of a headless board is a rite of passage. When you type ssh pi@raspberrypi.local and get stared down by a timeout or a refusal, the issue almost always traces back to three things: the SSH daemon never started, the Wi-Fi handshake failed, or your local network is blocking mDNS resolution. This guide cuts through outdated tutorials and targets the current Raspberry Pi OS (Bookworm) environment on the Raspberry Pi 5, where legacy tools like wpa_supplicant.conf and RPi.GPIO will actively break your build.
Below is the exact debugging matrix, hardware spec sheet, and remote-control Python script you need to get your headless Pi online and toggling GPIO pins from your laptop terminal.
Project Spec Sheet & Hardware BOM
This build targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS Bookworm (64-bit, Lite). The Pi 5 requires a dedicated 27W USB-C PD power supply to prevent brownouts when driving external relays, as the 5V rail current limits are strictly enforced by the new PMIC.
| Component | Exact Model / Variant | Notes & 2026 Pricing |
|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB RAM) | Requires active cooler; ~$80 USD |
| Power Supply | Official 27W USB-C PD PSU | 5V/5A; do not use standard phone chargers (~$12 USD) |
| Storage | SanDisk Extreme 64GB microSD | A2 rating required for OS Lite I/O (~$14 USD) |
| Actuator | Songle SRD-05VDC-SL-C Relay | 5V coil, optocoupler isolated module (~$4 USD) |
| Wiring | 22 AWG Silicone Wire | Stranded, pre-crimped with Dupont connectors (~$8 USD) |
The "Connection Refused" Debugging Matrix
Before tearing apart your hardware, match your terminal output to this matrix. These are the four most common failure modes when attempting an ssh pi raspberry pi connection on a fresh Bookworm image.
| Exact Error String | Root Cause | Probability | Diagnostic / Fix |
|---|---|---|---|
ssh: connect to host 192.168.x.x port 22: Connection refused |
The Pi is on the network, but the SSH daemon is disabled. Bookworm disables SSH by default for security. | High (45%) | Place an empty file named ssh (no extension) in the root of the FAT32 boot partition before first boot. |
ssh: Could not resolve hostname raspberrypi.local: Name or service not known |
mDNS (Avahi) is failing. Common on Windows machines lacking Bonjour, or across segmented VLANs/IoT subnets. | Medium (30%) | Ping the router's DHCP client list for the MAC address (b8:27:eb or 2c:cf:67) to find the direct IPv4 address. |
ssh: connect to host 192.168.x.x port 22: Connection timed out |
The Pi never joined the Wi-Fi network. In Bookworm, wpa_supplicant.conf is deprecated and ignored on first boot. |
High (20%) | Use Raspberry Pi Imager's 'Advanced Settings' (Ctrl+Shift+X) to inject Wi-Fi creds, or use custom.toml on the boot drive. |
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! |
You reflashed the SD card, but your laptop's known_hosts file remembers the old SSH key fingerprint. |
Low (5%) | Run ssh-keygen -R raspberrypi.local on your host machine to clear the stale fingerprint. |
The First Three Things to Check When SSH Fails
If you are staring at a blinking cursor and none of the quick fixes above worked, execute this triage sequence. Do not reflash the SD card until you have verified these three physical and logical states.
- Verify the Boot Partition Injection (The Headless File): Pop the microSD card back into your PC. Open the
bootfspartition. Ensure the file namedsshexists and has no file extension (Windows loves to hide.txtextensions, resulting in a file namedssh.txt, which the Pi ignores). If using Wi-Fi, ensure you used the Pi Imager's OS customization menu to inject the SSID and PSK; dropping a legacywpa_supplicant.conffile will silently fail on Bookworm. - Check the Power LED and PMIC State: The Raspberry Pi 5 has a dedicated power button and LED. A solid green light means the OS has loaded. If it's blinking in a specific pattern (e.g., 3 long, 3 short), the Pi is throwing a bootloader or RAM error. Ensure your USB-C cable is rated for 5A; a standard 3A phone cable will cause the Pi 5 to throttle or fail to mount the network stack under load.
- Isolate the Network Subnet: Connect a monitor and keyboard temporarily, or plug the Pi directly into your router via Ethernet. Run
ip a. If your laptop is on192.168.1.xand the Pi is on192.168.50.x(common with guest networks or mesh node backhauls), SSH will be blocked by client isolation rules. Move the Pi to your primary LAN VLAN.
Remote Relay Wiring & Pin Mapping
Once you are SSH'd in, let's build a remote-controlled relay circuit. The Pi 5 operates its GPIO header at 3.3V logic. Never connect a 5V relay coil directly to a Pi 5 GPIO pin. You will fry the BCM2712 SoC's GPIO bank. Always use an optocoupler-isolated relay module with its own 5V power feed from the Pi's 5V rail.
| Raspberry Pi 5 Pin (Physical) | BCM GPIO Number | Relay Module Pin | Wire Color (Standard) | Function |
|---|---|---|---|---|
| Pin 2 (5V Power) | N/A (Power) | VCC | Red | Provides 5V to the relay coil and optocoupler LED. |
| Pin 6 (Ground) | N/A (GND) | GND | Black | Common ground reference for logic and power. |
| Pin 11 | GPIO 17 | IN1 (Signal) | Blue | 3.3V logic trigger from Pi to optocoupler. |
Python GPIO Control Script (Bookworm Compatible)
Raspberry Pi OS Bookworm deprecated the legacy RPi.GPIO library because it relies on /dev/mem access, which conflicts with the Pi 5's new RP1 southbridge architecture. The modern, supported standard is gpiozero, which uses the lgpio backend under the hood.
SSH into your Pi and ensure the library is installed:
sudo apt update && sudo apt install python3-gpiozero -y
Create a file named relay_control.py and paste the following complete, compilable script. It includes signal handling to ensure the relay defaults to a safe 'OFF' state if you press Ctrl+C in your SSH session.
import time
import signal
import sys
from gpiozero import OutputDevice
# --- PIN DEFINITIONS ---
# Using BCM numbering (Physical Pin 11)
RELAY_PIN = 17
# Initialize the relay as an OutputDevice.
# active_high=False assumes a 'Low-Level Trigger' relay module (most common).
# If your relay clicks ON when the signal pin is HIGH, change to active_high=True.
relay = OutputDevice(RELAY_PIN, active_high=False, initial_value=False)
def graceful_exit(signum, frame):
"""Handles Ctrl+C (SIGINT) to ensure relay turns off before script exits."""
print("\n[INFO] SIGINT received. De-energizing relay and exiting safely...")
relay.off()
relay.close()
sys.exit(0)
# Bind the signal handler
signal.signal(signal.SIGINT, graceful_exit)
signal.signal(signal.SIGTERM, graceful_exit)
def main():
print(f"[START] Relay control active on GPIO {RELAY_PIN}.")
print("[INFO] Press Ctrl+C to stop and safely de-energize.")
try:
while True:
relay.on()
print("[STATE] Relay ENGAGED (Circuit Closed)")
time.sleep(5)
relay.off()
print("[STATE] Relay DISENGAGED (Circuit Open)")
time.sleep(5)
except Exception as e:
print(f"[ERROR] Unexpected failure: {e}")
relay.off()
relay.close()
sys.exit(1)
if __name__ == "__main__":
main()
Run the script in your SSH terminal:
python3 relay_control.py
How to Extend or Simplify the Build
Depending on your end goal, you can scale this SSH-based GPIO project up into a full home automation node, or strip it down for ultra-low-power remote sensing.
Simplifying: Headless Cron Jobs
If you don't need an interactive SSH session to toggle the relay, don't run a persistent Python loop. Use the Pi's native cron scheduler. Open the crontab via SSH (crontab -e) and add a one-liner using the gpiozero command-line equivalent or a 3-line Python script. This frees up RAM and eliminates the risk of an SSH disconnect leaving your relay stuck in the 'ON' state.
Extending: MQTT and Remote Telemetry
SSH is great for setup and debugging, but it's a poor protocol for continuous IoT control. To extend this build, install mosquitto and the paho-mqtt Python library. Wrap the gpiozero relay logic inside an MQTT callback function. This allows you to control the Pi 5 from a Node-RED dashboard or Home Assistant over your LAN without keeping an SSH tunnel open. For remote access outside your home network, bypass port-forwarding (which exposes your Pi to botnets) and use a secure tunnel like Tailscale or Cloudflare Tunnels to access your SSH session and MQTT broker from anywhere.






