To SSH to a Raspberry Pi 5 for headless hardware control, enable the SSH daemon via raspi-config or by placing an empty file named ssh in the boot partition, connect the board to your network, and execute ssh username@<IP_ADDRESS> from your host machine. For reliable embedded automation, do not rely on an active SSH session to keep your hardware running; instead, use SSH to deploy your Python script and register it as a systemd service.
The Verdict: Choosing Your Headless Access Method
When stripping the monitor and keyboard off your workbench, you need a reliable way into the OS. Here is the decision matrix for remote access on the Pi 5, terminating in the optimal setup for embedded hardware projects.
| Access Method | Best For | Failure Mode | Verdict |
|---|---|---|---|
| Direct UART Serial Console | Boot-level debugging, kernel panics, WiFi misconfiguration | Requires USB-to-TTL adapter; no network stack visibility | Use only for rescue operations |
| VNC over WiFi | GUI configuration, browser-based testing | High bandwidth; drops out if WiFi sleeps or router reboots | Avoid for headless hardware nodes |
| SSH over WiFi | Mobile robots, remote sensors where cables are impossible | Susceptible to 2.4GHz interference and IP drift | Acceptable if static IP is reserved |
| SSH over Ethernet | Stationary relay controllers, home automation hubs | Requires physical cable run to switch/router | DEFAULT PICK: SSH via Ethernet + systemd |
Parts List & Hardware Spec Sheet
This guide targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm 64-bit). The Pi 5 requires a 27W USB-C PD power supply to prevent brownouts when driving peripherals via the GPIO header.
| Component | Exact Variant / Model | Approx. Cost | Notes |
|---|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB RAM) | $80.00 | Requires active cooling for sustained loads |
| Power Supply | Official 27W USB-C PD PSU | $12.00 | Prevents peripheral USB/GPIO current limiting |
| Actuator | Songle SRD-05VDC-SL-C (1-Ch Relay) | $4.50 | Opto-isolated, active-LOW trigger |
| Wiring | 22 AWG Dupont Jumper Wires (F-F) | $6.00 | Use silicone-jacketed for high-temp environments |
| Storage | SanDisk Extreme 32GB microSD (A2) | $11.00 | A2 rating critical for OS responsiveness |
GPIO Pin Mapping Table
The Pi 5 uses the RP1 southbridge chip for GPIO, which changes the underlying hardware addressing but maintains backward-compatible BCM numbering in software. Wire the 5V relay module exactly as follows:
| Relay Module Pin | Pi 5 Physical Pin | BCM GPIO Number | Function |
|---|---|---|---|
| VCC | Pin 2 | N/A (5V Power) | Powers the relay coil and optocoupler |
| GND | Pin 6 | N/A (Ground) | Common ground reference |
| IN | Pin 11 | GPIO 17 | Control signal (Active LOW) |
Step-by-Step: Enabling SSH and Deploying the Service
Follow this sequence to get headless access and ensure your hardware script survives a reboot or a dropped SSH session.
- Enable SSH Headlessly: Before booting the Pi, insert the flashed microSD card into your PC. Open the
bootfspartition and create an empty file named exactlyssh(no file extension). This tells the OS to enable the SSH daemon on first boot. - Configure WiFi (If not using Ethernet): In the same
bootfspartition, createcustom.toml(for Bookworm) orwpa_supplicant.conf(for older OS versions) with your network credentials. For Ethernet, simply plug in the CAT6 cable. - Boot and Locate IP: Power the Pi 5. Check your router's DHCP client list for a device named
raspberrypi, or use a network scanner likenmapor Fing to find its IP address. - Establish the SSH Session: From your host terminal, run
ssh pi@192.168.1.XX(replace 'pi' with your actual username if you changed the default during imaging). Accept the ECDSA fingerprint. - Install Dependencies: The Pi 5 requires the
lgpiobackend for Python GPIO control. Run:sudo apt update && sudo apt install python3-gpiozero python3-lgpio -y. - Create the Systemd Service: Do not run hardware scripts directly in the SSH terminal. Create a service file:
sudo nano /etc/systemd/system/relay.service. Paste the systemd configuration (see code block below), then runsudo systemctl enable relay.serviceandsudo systemctl start relay.service.
The Code: SSH-Triggered Python GPIO Script
This Python script targets the Pi 5 using the modern gpiozero library backed by lgpio. It includes explicit pin definitions, state management, and error handling to prevent the GPIO pins from locking up if the script crashes.
#!/usr/bin/env python3
import time
import sys
import logging
from gpiozero import OutputDevice
from signal import pause
# --- Pin Definitions (BCM Numbering) ---
RELAY_PIN = 17
# Configure basic logging for systemd journal
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def main():
relay = None
try:
# Initialize relay. Most 5V relay modules are Active LOW.
# active_high=False means relay.on() pulls the pin to GND.
relay = OutputDevice(RELAY_PIN, active_high=False, initial_value=False)
logging.info(f"Relay controller initialized on BCM GPIO {RELAY_PIN}.")
# Main control loop
while True:
relay.on()
logging.info("Relay ENGAGED (Circuit Closed)")
time.sleep(10)
relay.off()
logging.info("Relay DISENGAGED (Circuit Open)")
time.sleep(10)
except KeyboardInterrupt:
logging.info("Shutdown requested via keyboard interrupt.")
except Exception as e:
logging.critical(f"Fatal hardware or logic error: {e}", exc_info=True)
sys.exit(1)
finally:
# Explicitly release the GPIO pin back to the OS
if relay is not None:
relay.close()
logging.info("GPIO resources released cleanly.")
if __name__ == '__main__':
main()
Systemd Service File (/etc/systemd/system/relay.service):
[Unit]
Description=Pi 5 GPIO Relay Controller
After=network.target
[Service]
ExecStart=/usr/bin/python3 /home/pi/relay_control.py
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
Debugging: Exact Error Strings and Ranked Causes
When your SSH connection fails, the terminal output tells you exactly where the breakdown occurred. Here are the three most common errors and how to fix them.
1. Ping the IP: Run
ping 192.168.1.XX. If it times out, the Pi is off, disconnected, or on a different subnet.2. Check Port 22: Run
nc -vz 192.168.1.XX 22. If it says 'Connection refused', the Pi is online but the SSH daemon is dead or blocked.3. Verify Credentials: Ensure you are using the username created during the Raspberry Pi Imager process, not the deprecated default 'pi' user.
Error 1: "Connection refused"
Exact String: ssh: connect to host 192.168.1.50 port 22: Connection refused
- Cause A (Most Likely): The
sshfile was not created in the boot partition, or it had a hidden.txtextension (e.g.,ssh.txt). Fix: Re-flash or mount the SD card on a PC and create the extension-less file. - Cause B: The SSH daemon crashed or is masked. Fix: Connect a monitor/keyboard, log in locally, and run
sudo systemctl unmask ssh && sudo systemctl enable --now ssh. - Cause C: UFW (Uncomplicated Firewall) is active and blocking port 22. Fix: Run
sudo ufw allow ssh.
Error 2: "Network is unreachable" or "Timed out"
Exact String: ssh: connect to host 192.168.1.50 port 22: Network is unreachable
- Cause A (Most Likely): You are targeting the wrong IP address. The Pi's DHCP lease expired or changed. Fix: Check your router's admin panel for the current IP, or set a static DHCP reservation for the Pi's MAC address.
- Cause B: WiFi credentials in
custom.tomlare incorrect, or the Pi is out of range of the 5GHz band. Fix: Force the Pi to connect to a 2.4GHz SSID, or switch to wired Ethernet.
Error 3: "REMOTE HOST IDENTIFICATION HAS CHANGED"
Exact String: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
- Cause A (Most Likely): You re-flashed the SD card or swapped in a new Pi, but kept the same static IP. Your host machine remembers the old cryptographic fingerprint. Fix: Run
ssh-keygen -R 192.168.1.50on your host machine to clear the old key, then reconnect.
Extending or Simplifying the Build
Once you have reliable SSH access and a functioning relay, you can scale the project to match your specific application requirements.
To Simplify (The Minimalist Approach):
If you only need to trigger a relay once a day and want to eliminate Python entirely, strip the build down to a bash cron job. SSH into the Pi and type crontab -e. Add a line like 0 8 * * * /usr/bin/raspi-gpio set 17 op dh to pull the pin high at 8 AM. This removes the Python dependency and reduces RAM usage to near zero, ideal for a Pi Zero 2 W.
To Extend (The Industrial Approach):
If you are scaling to 8+ relays or adding I2C sensors (like a BME280 temperature sensor), the Pi 5's 3.3V logic and limited 5V pin current become bottlenecks.
1. Upgrade to an official Raspberry Pi M.2 HAT+ and an NVMe drive to eliminate SD card corruption from frequent logging.
2. Replace the raw relay module with an I2C GPIO expander like the MCP23017, which isolates the Pi's sensitive RP1 chip from inductive kickback generated by relay coils.
3. Expose the relay state over MQTT using the paho-mqtt Python library, allowing Home Assistant to control the hardware without needing to SSH in to run manual scripts.
For detailed specifications on the Pi 5's power delivery and GPIO tolerances, refer to the official Raspberry Pi 5 hardware documentation. For deeper reading on securing your SSH daemon against external threats, consult the Raspberry Pi remote access security guidelines.






