The Verdict: Choosing Your Raspberry Pi Remote Connection Protocol
When builders ask about setting up a raspberry pi remote connection, they usually conflate administrative access with machine-to-machine (M2M) control. If you just need to run terminal commands, SSH is fine. But if you are building an IoT node that needs to trigger relays, read sensors, and report state back to a dashboard (like Home Assistant or Node-RED) with minimal latency and bandwidth, SSH and REST APIs are the wrong tools.
Here is the decision matrix to terminate your protocol search. For GPIO-level hardware control, MQTT is the definitive pick.
| Protocol | Best For | Latency | Payload Overhead | Verdict for GPIO Control |
|---|---|---|---|---|
| SSH / Bash | OS administration, file transfers | High (session setup) | Heavy (encryption) | Reject: No persistent state feedback. |
| VNC / RDP | Remote GUI desktop access | Very High | Massive (video) | Reject: Useless for headless M2M. |
| REST API (HTTP) | Infrequent web-triggered actions | Medium (TCP handshake) | Medium (HTTP headers) | Reject: Polling wastes CPU; no push. |
| WebSockets | Real-time browser UI dashboards | Low | Low | Conditional: Good for custom web apps. |
| MQTT | IoT telemetry & GPIO triggers | Ultra-Low | Minimal (2-byte header) | PICK: Use MQTT (Mosquitto + Paho). |
Hardware BOM and GPIO Pin Mapping
This build targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS Bookworm (64-bit). The Pi 5's PCIe southbridge and updated GPIO matrix mean legacy libraries like RPi.GPIO are officially deprecated and will fail. We use the modern gpiozero library with the lgpio backend.
Parts List (Exact Variants & Pricing)
- Compute: Raspberry Pi 5 (8GB) - $80.00
- Power: Official Raspberry Pi 27W USB-C PD Power Supply - $12.00 (Do not use a generic phone charger; the Pi 5 will throttle USB current if it doesn't negotiate 5V/5A PD).
- Actuator: SainSmart 2-Channel 5V Relay Module (Optocoupler isolated) - $8.50
- Wiring: Adafruit Perma-Proto HAT for Pi (no components) - $12.00
- Consumables: 22 AWG solid hook-up wire, 1/4W 10kΩ resistor (for pull-up if needed).
Pin Mapping Table (BCM Numbering)
Most cheap 5V relay modules are Active-LOW. This means the optocoupler LED illuminates (and the relay clicks) when the GPIO pin sinks current to ground, not when it sources 3.3V. We map this explicitly in the code.
| Component | Pi 5 GPIO (BCM) | Physical Pin | Logic Level |
|---|---|---|---|
| Relay 1 (IN1) | GPIO 17 | Pin 11 | Active-LOW (0 = ON) |
| Relay 2 (IN2) | GPIO 27 | Pin 13 | Active-LOW (0 = ON) |
| Relay VCC | 5V Power | Pin 2 or 4 | N/A |
| Relay GND | Ground | Pin 6 | N/A |
Step-by-Step Setup: Mosquitto and Bookworm Dependencies
Before writing code, we must prepare the OS environment. Bookworm enforces PEP 668, which prevents you from using pip to install system-wide Python packages. You must use the apt repositories for GPIO and MQTT libraries to avoid breaking the OS Python environment.
- Install the Broker and Libraries:
sudo apt update
sudo apt install mosquitto mosquitto-clients python3-gpiozero python3-lgpio python3-paho-mqtt - Enable Mosquitto on Boot:
sudo systemctl enable mosquitto
sudo systemctl start mosquitto - Verify Broker is Listening:
sudo netstat -tlnp | grep 1883
Expected output should show mosquitto listening on 0.0.0.0:1883. - Create the Project Directory:
mkdir ~/pi_remote_gpio && cd ~/pi_remote_gpio
The Python Control Script (Target: Pi 5 / Bookworm)
This script connects to the local Mosquitto broker, subscribes to a command topic, and toggles the relays. It includes robust error handling and graceful GPIO cleanup.
import paho.mqtt.client as mqtt
from gpiozero import OutputDevice
import time
import sys
import json
# --- PIN DEFINITIONS (BCM) ---
RELAY_1_PIN = 17
RELAY_2_PIN = 27
# --- MQTT CONFIGURATION ---
BROKER = 'localhost'
PORT = 1883
TOPIC_CMD = 'pi5/relays/cmd'
TOPIC_STAT = 'pi5/relays/status'
# --- HARDWARE INITIALIZATION ---
# active_high=False is CRITICAL for standard 5V optocoupler relay modules.
# initial_value=True means the pin starts HIGH (3.3V), which turns the relay OFF.
try:
relay1 = OutputDevice(RELAY_1_PIN, active_high=False, initial_value=True)
relay2 = OutputDevice(RELAY_2_PIN, active_high=False, initial_value=True)
except Exception as e:
print(f'GPIO Initialization Failed: {e}')
sys.exit(1)
def publish_status():
state = {
'relay1': 'ON' if relay1.value == 1 else 'OFF',
'relay2': 'ON' if relay2.value == 1 else 'OFF'
}
client.publish(TOPIC_STAT, json.dumps(state), retain=True)
def on_connect(client, userdata, flags, rc, properties=None):
if rc == 0:
print('Connected to MQTT Broker')
client.subscribe(TOPIC_CMD)
publish_status()
else:
print(f'Connection failed with code {rc}')
def on_message(client, userdata, msg):
try:
payload = msg.payload.decode('utf-8').strip().upper()
print(f'Received: {payload} on {msg.topic}')
if payload == 'RELAY1_ON':
relay1.on() # Sinks pin to GND (Active LOW)
elif payload == 'RELAY1_OFF':
relay1.off()
elif payload == 'RELAY2_ON':
relay2.on()
elif payload == 'RELAY2_OFF':
relay2.off()
elif payload == 'ALL_OFF':
relay1.off(); relay2.off()
publish_status()
except Exception as e:
print(f'Payload parsing error: {e}')
# --- CLIENT SETUP ---
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id='Pi5_GPIO_Node')
client.on_connect = on_connect
client.on_message = on_message
if __name__ == '__main__':
try:
client.connect(BROKER, PORT, 60)
print('Starting MQTT loop...')
client.loop_forever()
except KeyboardInterrupt:
print('\nShutdown signal received.')
except Exception as e:
print(f'Fatal MQTT Error: {e}')
finally:
print('Cleaning up GPIO pins...')
relay1.close()
relay2.close()
sys.exit(0)
Debugging: Exact Error Strings and Ranked Fixes
When your raspberry pi remote connection fails, do not guess. Read the traceback. Here are the exact error strings you will encounter on a Pi 5 Bookworm build, ranked by frequency, and how to fix them.
The First Three Things to Check
- Is Mosquitto actually running? Run
systemctl status mosquitto. If it's dead, the Python script will instantly fail to connect. - Did you use pip instead of apt? If you used
pip install paho-mqttinside the global environment, Bookworm's PEP 668 guardrails will either block it or create a broken shadow package. Useapt. - Is your relay chattering or inverted? You forgot
active_high=Falsein theOutputDevicedefinition, or you wired the relay VCC to 3.3V instead of 5V.
Exact Error Strings and Fixes
| Exact Error String | Root Cause | The Fix |
|---|---|---|
ConnectionRefusedError: [Errno 111] Connection refused |
Mosquitto broker is stopped, or a local UFW firewall is blocking port 1883. | Run sudo systemctl start mosquitto. If using UFW, run sudo ufw allow 1883/tcp. |
ModuleNotFoundError: No module named 'lgpio' |
gpiozero is installed, but the Pi 5 backend driver (lgpio) is missing. |
Run sudo apt install python3-lgpio. Do not use pip. |
RuntimeError: Failed to load library liblgpio.so |
Corrupted apt package or running a 32-bit OS on a Pi 5 (lgpio expects 64-bit Bookworm). | Verify OS: uname -m should return aarch64. Re-flash 64-bit Bookworm if it says armv7l. |
paho.mqtt.exceptions.WebsocketConnectionError |
You accidentally configured the client to use WebSockets (port 9001) but Mosquitto is only listening on raw TCP (1883). | Ensure you are using standard client.connect() and not ws_connect(). |
For deeper broker configuration, such as setting up TLS certificates or username/password authentication, refer to the official Mosquitto configuration manual. For advanced GPIO edge-detection, consult the Raspberry Pi gpiozero documentation.
Extending and Simplifying the Build
Once the baseline MQTT connection is stable, you need to decide how to scale the project based on your deployment environment.
How to Simplify (The 'Kiosk' Approach)
If you don't need a full MQTT broker and just want a web browser to toggle a relay from your phone on the same LAN:
- Drop Mosquitto. Install
python3-flask. - Use a GET request: Create a route
@app.route('/relay1/toggle')that flips thegpiozerostate. - Trade-off: You lose push-state updates. If someone else toggles the relay, your phone's browser won't know until you refresh the page.
How to Extend (The 'Production IoT' Approach)
If you are deploying this in a garage or greenhouse where WiFi drops out:
- Add Last Will and Testament (LWT): In the Python script, configure the MQTT client LWT to publish 'OFFLINE' to a status topic if the Pi loses power or crashes. This prevents your dashboard from showing stale 'ON' states.
- Move the Broker: Running Mosquitto on the same Pi that controls the hardware is fine for a desk toy. For a real house, run Mosquitto on a dedicated Home Assistant Green or an always-on mini-PC, and point the Pi's
BROKERvariable to that static IP. - Watchdog Timer: Enable the Pi 5's hardware watchdog (
sudo apt install watchdog) to automatically reboot the Pi if the Python script hangs and stops checking in.
By standardizing on MQTT and respecting the Pi 5's Bookworm library requirements, you eliminate the 90% of headaches that plague legacy Raspberry Pi remote connection tutorials. Wire it clean, handle your exceptions, and let the broker do the heavy lifting.






