Integrating a Raspberry Pi and Alexa for local hardware control usually falls into two traps: relying on fragile third-party cloud bridges that break when APIs change, or using local UDP emulation that fails when you leave your home network. The most robust, production-grade method in 2026 is using AWS IoT Core as an MQTT broker linked to an Alexa Smart Home Skill. This allows your Pi to receive native voice directives securely, whether you are on your couch or across the country.
This guide targets the Raspberry Pi 5 (4GB RAM) running Raspberry Pi OS (Bookworm), controlling an optocoupler-isolated 5V relay module. We will write a persistent Python MQTT daemon using the AWS IoT Device SDK v2 and gpiozero.
Hardware Spec Sheet and Parts List
Before writing code, ensure your power delivery and isolation are correct. The Pi 5 has stricter power requirements than the Pi 4, and driving relays directly from the 3.3V GPIO pins without optocouplers will fry your SoC.
| Component | Exact Variant / Specification | Estimated Cost | Why This Specific Part |
|---|---|---|---|
| Microcontroller | Raspberry Pi 5 (4GB RAM) | $60.00 | PCIe Gen 2 and dual-core I/O chip handle TLS MQTT encryption without dropping packets. |
| Power Supply | Official 27W USB-C PD Power Supply | $12.00 | Required to prevent brownouts when the relay coils engage. Standard 5V/3A supplies will throttle the Pi 5. |
| Cooling | Official Active Cooler for Pi 5 | $5.00 | PWM-controlled fan prevents thermal throttling during sustained TLS handshakes. |
| Relay Module | Elegoo 4-Channel 5V Relay (Optocoupler) | $8.00 | Optocouplers electrically isolate the Pi GPIO from the relay coil inductive spikes. |
| Wiring | 22 AWG Dupont Female-to-Female | $5.00 | Pre-crimped for Pi header and standard 0.1" relay module pins. |
Pin Mapping and Wiring the Relay Module
Most hobbyist relay modules are Active LOW. This means the optocoupler LED turns on (and the relay clicks) when the GPIO pin is pulled to 0V (LOW), not when it is driven HIGH. The gpiozero library handles this elegantly, but your physical wiring must supply 5V to the relay's VCC pin to energize the coil.
| Raspberry Pi 5 Pin (Physical) | GPIO BCM Number | Relay Module Pin | Function |
|---|---|---|---|
| Pin 2 | 5V Power | VCC | Provides 5V to energize the relay coils. |
| Pin 6 | GND | GND | Common ground reference. |
| Pin 11 | GPIO 17 | IN1 | Control signal for Relay 1 (Living Room Light). |
| Pin 13 | GPIO 27 | IN2 | Control signal for Relay 2 (Desk Fan). |
| Pin 15 | GPIO 22 | IN3 | Control signal for Relay 3 (Workshop Dust Collector). |
| Pin 16 | GPIO 23 | IN4 | Control signal for Relay 4 (Spare). |
The Python MQTT Control Script
This script uses the AWS IoT Device SDK for Python v2. It connects to your AWS IoT Core endpoint, subscribes to the Alexa Smart Home directive topic, parses the JSON payload, and toggles the GPIO pins.
Prerequisites: Run pip install awsiotsdk gpiozero on your Pi. Download your AWS IoT certificates (device.pem.crt, private.pem.key, and AmazonRootCA1.pem) and place them in a /home/pi/certs/ directory.
import json
import logging
import time
from awscrt import mqtt
from awsiot import mqtt_connection_builder
from gpiozero import OutputDevice
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# --- Hardware Pin Definitions ---
# active_high=False is CRITICAL for optocoupler relays (Active LOW)
RELAY_MAPPING = {
"relay_1": OutputDevice(17, active_high=False, initial_value=True),
"relay_2": OutputDevice(27, active_high=False, initial_value=True),
"relay_3": OutputDevice(22, active_high=False, initial_value=True),
"relay_4": OutputDevice(23, active_high=False, initial_value=True)
}
# --- AWS IoT Configuration ---
IOT_ENDPOINT = "your-endpoint-ats.iot.us-east-1.amazonaws.com"
CLIENT_ID = "pi5_alexa_smart_home"
CERT_PATH = "/home/pi/certs/device.pem.crt"
KEY_PATH = "/home/pi/certs/private.pem.key"
CA_PATH = "/home/pi/certs/AmazonRootCA1.pem"
TOPIC = "alexa/smarthome/directive/pi5"
def on_message_received(topic, payload, **kwargs):
"""Parses Alexa Smart Home Skill directives and toggles relays."""
try:
decoded_payload = payload.decode('utf-8')
data = json.loads(decoded_payload)
# Extract Alexa directive components
directive = data.get("directive", {})
header = directive.get("header", {})
endpoint = directive.get("endpoint", {})
namespace = header.get("namespace")
name = header.get("name")
endpoint_id = endpoint.get("endpointId")
if namespace == "Alexa.PowerController" and endpoint_id in RELAY_MAPPING:
relay = RELAY_MAPPING[endpoint_id]
if name == "TurnOn":
relay.on() # Pulls GPIO LOW (Active)
logging.info(f"Engaged {endpoint_id}")
elif name == "TurnOff":
relay.off() # Pulls GPIO HIGH (Inactive)
logging.info(f"Disengaged {endpoint_id}")
except json.JSONDecodeError as e:
logging.error(f"Failed to parse Alexa JSON payload: {e}")
except Exception as e:
logging.error(f"Unexpected error processing directive: {e}")
def main():
event_loop_group = ... # Omitted for brevity, standard awsiot CRT setup
host_resolver = ... # Omitted for brevity
client_bootstrap = ... # Omitted for brevity
try:
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=IOT_ENDPOINT,
cert_filepath=CERT_PATH,
pri_key_filepath=KEY_PATH,
client_bootstrap=client_bootstrap,
ca_filepath=CA_PATH,
client_id=CLIENT_ID,
clean_session=False,
keep_alive_secs=30
)
logging.info(f"Connecting to {IOT_ENDPOINT}...")
connect_future = mqtt_connection.connect()
connect_future.result() # Blocks until connected or throws exception
logging.info("Successfully connected to AWS IoT Core.")
mqtt_connection.subscribe(
topic=TOPIC,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
# Keep the daemon alive
while True:
time.sleep(1)
except Exception as e:
logging.critical(f"MQTT Connection Failed: {e}")
finally:
# Safe shutdown: turn off all relays on crash/exit
for relay in RELAY_MAPPING.values():
relay.off()
logging.info("System shutdown. All relays disengaged.")
if __name__ == '__main__':
main()
Debugging: Connection Refused and Timeout Errors
When bridging a Raspberry Pi and Alexa via AWS IoT, the TLS handshake and IAM policies are where 90% of builds fail. If your script crashes on startup, look for these exact error strings.
Error 1: awscrt.mqtt.MQTTError: [Errno 111] Connection refused
This means the Pi reached the AWS server, but the server actively rejected the TCP connection before TLS could even begin.
- Cause A: You are using the legacy Data Endpoint instead of the ATS (Amazon Trust Services) endpoint. Ensure your endpoint URL ends in
-ats.iot.[region].amazonaws.com. - Cause B: Port 8883 is blocked by your local router or ISP firewall. AWS IoT Core strictly requires outbound TCP 8883.
Error 2: awscrt.exceptions.AwsCrtError: AWS_ERROR_MQTT_UNAUTHORIZED
The TLS handshake succeeded, but AWS IoT Core rejected your certificate's permissions.
- Cause A: Your IoT Core Policy JSON is missing the
iot:Connectoriot:Receiveactions. - Cause B: The
Client IDin your Python script does not match the restricted string in your IoT Policy.
- Verify the IoT Policy Document: Open the AWS Console, go to IoT Core → Security → Policies. Ensure your policy explicitly allows
iot:Connect,iot:Subscribe, andiot:Receiveon the specific topic ARN. - Ping the Endpoint: Run
openssl s_client -connect YOUR_ENDPOINT:8883from the Pi terminal. If it hangs, it is a network/firewall issue, not a code issue. - Check Certificate Status: Ensure the certificate attached to your "Thing" in AWS IoT is marked as Active, not Inactive or Revoked.
Extending and Simplifying the Build
Not every project requires enterprise-grade cloud infrastructure. Here is how to scale this Raspberry Pi and Alexa integration up or down based on your actual needs.
How to Simplify (Local Network Only)
If you do not need remote access outside your home and want to skip AWS entirely, replace the MQTT script with the fauxmo Python library. Fauxmo emulates Belkin WeMo smart plugs using local UDP broadcast. Alexa discovers the Pi natively via the "Discover Devices" button in the Alexa app. The trade-off is that it only works while your phone and Pi are on the same subnet, and it lacks the robust state-reporting of the Smart Home Skill API.
How to Extend (Bidirectional State Reporting)
The current script is "fire and forget"—Alexa tells the Pi to turn on, but if you manually flip the physical relay switch, Alexa doesn't know. To extend this, implement the Alexa ReportState API. Add a physical momentary push-button wired to another GPIO pin. When the button is pressed, toggle the relay, and publish a JSON state report back to the AWS IoT Core shadow topic. Alexa will then accurately reflect the device state in the mobile app.
Frequently Asked Questions
Can I connect Raspberry Pi and Alexa without AWS IoT Core?
Yes. The most common alternative is running a local Node-RED instance on the Pi with the node-red-contrib-alexa-local package, or using Python's fauxmo library to emulate WeMo switches. These methods rely on local network discovery and do not require cloud accounts, but they will not work with Alexa Routines that trigger while you are away from home, nor will they work if your router isolates IoT devices on a separate VLAN from your Echo devices.
Why does my Raspberry Pi and Alexa relay click twice when triggered?
A double-click usually indicates a power brownout. When the relay coil engages, it draws a sudden spike of current (often 70mA+ per coil). If your Pi 5 is powered by a standard phone charger rather than the official 27W USB-C PD supply, the 5V rail dips. The Pi's brownout detector briefly resets the GPIO states, causing the relay to drop out and re-engage. Always use the official 27W power supply and ensure your relay module has adequate decoupling capacitors.
How do I troubleshoot Raspberry Pi and Alexa offline status in the app?
If the Alexa app shows your device as "Offline" or "Not Responding," the issue is almost always a failure in the Lambda function or the Smart Home Skill's ReportState response, rather than the Pi itself. Alexa requires your backend (Lambda) to acknowledge the directive within 8 seconds. If your Pi's MQTT connection drops and the Lambda function times out waiting for a shadow update, Alexa flags the device offline. Check your AWS CloudWatch logs for the Lambda function to verify if the timeout is occurring at the cloud layer.






