To connect a Raspberry Pi to an RS485 network, you must use a 3.3V-compatible transceiver like the SP3485, wired to the Pi’s primary UART pins (GPIO 14/15), with the serial console disabled in /boot/firmware/config.txt. Never use the standard 5V MAX485 module directly; its 5V logic output will permanently destroy the Pi’s 3.3V RX pin.
RS485 is the industrial standard for long-distance, noise-immune serial communication, capable of running up to 1,200 meters at lower baud rates. Because the Raspberry Pi operates at 3.3V logic and lacks native differential signaling, a TTL-to-RS485 breakout board is mandatory. Below is the complete hardware selection, wiring procedure, and Python implementation to get your Pi talking on an RS485 bus.
Hardware Spec Sheet & Transceiver Comparison
Choosing the wrong RS485 module is the most common cause of fried Raspberry Pi SoCs. The ubiquitous blue "MAX485" modules found on Amazon require a 5V VCC and output 5V logic on the RO (Receiver Output) pin. Feeding 5V into the Pi’s GPIO 15 (RXD) exceeds the absolute maximum ratings and will brick the board. Always select a 3.3V native module or use a logic level shifter.
| Module / IC Variant | Logic Level | Galvanic Isolation | Max Baud Rate | Est. Price (2026) | Pi Compatibility |
|---|---|---|---|---|---|
| Generic MAX485 (HW-016) | 5V | No | 2.5 Mbps | $1.50 - $2.00 | Unsafe (Requires BSS138 Level Shifter) |
| SP3485 3.3V Breakout | 3.3V Native | No | 10 Mbps | $3.00 - $4.50 | Safe (Direct GPIO connection) |
| Waveshare RS485 CAN HAT | 3.3V Native | Yes (ISO1050) | 1 Mbps | $22.00 - $28.00 | Safe (Plugs directly onto 40-pin header) |
| Adafruit MAX13487 (Auto-Flow) | 3.3V - 5V | No | 16 Mbps | $9.95 | Safe (Auto TX/RX direction switching) |
Required Parts List
- Board: Raspberry Pi 4 Model B (2GB+ RAM) or Raspberry Pi 5. (Code targets Pi 4/5 primary UART).
- Transceiver: SP3485 3.3V RS485-to-TTL module (e.g., from LCSC or AliExpress vendors like TZT).
- Wiring: 24 AWG stranded silicone wire or standard breadboard jumper cables.
- Termination: 120Ω 1/4W resistor (for bus termination).
GPIO to SP3485 Pin Mapping & Wiring Steps
The Raspberry Pi exposes its primary UART on GPIO 14 (TXD) and GPIO 15 (RXD). Because RS485 is half-duplex, we use a third GPIO pin to control the Driver Enable (DE) and Receiver Enable (RE) pins on the transceiver, dictating whether the Pi is transmitting or listening.
| Raspberry Pi GPIO (Physical Pin) | Pi Function | SP3485 Module Pin | Wire Color Recommendation |
|---|---|---|---|
| Pin 1 (3.3V) | VCC Power | VCC | Red |
| Pin 6 (GND) | Ground | GND | Black |
| Pin 8 (GPIO 14) | UART TXD | DI (Driver Input) | Yellow |
| Pin 10 (GPIO 15) | UART RXD | RO (Receiver Output) | Orange |
| Pin 12 (GPIO 18) | Direction Control | DE & RE (Jumpered together) | Blue |
Step-by-Step Wiring Procedure
- De-energize the Pi: Disconnect the USB-C power supply before touching the GPIO header.
- Prepare the Transceiver: Solder a jumper wire between the
DEandREpins on the SP3485 module. This allows a single Pi GPIO pin to switch the module between transmit (HIGH) and receive (LOW) modes. - Connect Power and Ground: Wire Pi Pin 1 (3.3V) to the module VCC, and Pi Pin 6 to the module GND. Do not use Pin 2 (5V); the SP3485 is a 3.3V chip.
- Connect UART Lines: Wire Pi TXD (Pin 8) to module DI. Wire Pi RXD (Pin 10) to module RO.
- Connect Direction Control: Wire Pi GPIO 18 (Pin 12) to the jumpered DE/RE pad.
- Wire the Bus: Connect the module’s
Aterminal to the RS485 network’sD+(orA) wire, andBtoD-(orB). Connect the module GND to the bus shield/ground reference.
PySerial Implementation with Error Handling
Before running Python code, you must disable the Linux serial console so it stops outputting boot logs to the UART pins. Open your terminal and edit the config file:
sudo nano /boot/firmware/config.txt
Add the following lines at the bottom to disable Bluetooth (which hijacks the primary UART on Pi 4) and enable the UART:
enable_uart=1
dtoverlay=disable-bt
Reboot the Pi. The primary UART will now be mapped to /dev/ttyAMA0.
Below is the complete, compilable Python script using PySerial and RPi.GPIO to manage the half-duplex direction switching. Install dependencies first: sudo apt install python3-serial python3-rpi.gpio.
import serial
import RPi.GPIO as GPIO
import time
import sys
# --- Pin & Port Definitions ---
DE_RE_PIN = 18 # BCM numbering for Direction Control
UART_PORT = '/dev/ttyAMA0'
BAUD_RATE = 9600
# RS485 Direction States
TRANSMIT = GPIO.HIGH
RECEIVE = GPIO.LOW
def setup_rs485():
"""Initialize GPIO and Serial Port with error handling."""
GPIO.setmode(GPIO.BCM)
GPIO.setup(DE_RE_PIN, GPIO.OUT, initial=RECEIVE)
try:
ser = serial.Serial(
port=UART_PORT,
baudrate=BAUD_RATE,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1.0
)
return ser
except serial.SerialException as e:
print(f"Fatal Serial Error: {e}")
sys.exit(1)
def rs485_transmit(ser, message):
"""Switch to TX mode, send data, wait for shift register to clear, switch to RX."""
GPIO.output(DE_RE_PIN, TRANSMIT)
time.sleep(0.01) # Allow transceiver to settle
payload = (message + '\n').encode('utf-8')
ser.write(payload)
ser.flush() # Block until all data is written to the TX register
# Calculate time needed for the last byte to physically leave the wire
# 10 bits per byte (start + 8 data + stop) at configured baud rate
tx_time = (len(payload) * 10) / BAUD_RATE
time.sleep(tx_time + 0.02)
GPIO.output(DE_RE_PIN, RECEIVE)
def rs485_receive(ser):
"""Read incoming data while in RX mode."""
if ser.in_waiting > 0:
raw_data = ser.read(ser.in_waiting)
return raw_data.decode('utf-8', errors='ignore').strip()
return None
if __name__ == "__main__":
ser_port = setup_rs485()
print(f"RS485 Active on {UART_PORT} at {BAUD_RATE} baud. Press Ctrl+C to exit.")
try:
while True:
# Example: Send a Modbus-style or custom poll command
rs485_transmit(ser_port, "PING_SENSOR_01")
# Listen for response
response = rs485_receive(ser_port)
if response:
print(f"RX: {response}")
time.sleep(1.0)
except KeyboardInterrupt:
print("\nShutting down RS485 bus...")
finally:
if 'ser_port' in locals() and ser_port.is_open:
ser_port.close()
GPIO.cleanup()
Debugging RS485: Exact Error Strings & Ranked Fixes
When your Python script fails to open the port or receives garbage data, the OS and PySerial throw specific exceptions. Here are the exact error strings and how to fix them.
Error 1: serial.serialutil.SerialException: [Errno 13] Permission denied: '/dev/ttyAMA0'
Ranked Causes & Fixes:
- User lacks dialout group privileges (90% of cases): The
ttydevices are owned by thedialoutgroup. Fix: Runsudo usermod -a -G dialout $USER, then log out and log back in. - Serial console is still active (10% of cases): The OS is holding the port open for kernel logs. Fix: Run
sudo raspi-config-> Interface Options -> Serial Port -> Disable login shell, enable hardware. Alternatively, ensureconsole=serial0,115200is removed from/boot/firmware/cmdline.txt.
Error 2: serial.serialutil.SerialException: [Errno 2] could not open port /dev/ttyAMA0: No such file or directory
Ranked Causes & Fixes:
- Bluetooth UART overlay conflict: On the Pi 4, the primary UART is routed to the Bluetooth chip by default. Fix: Add
dtoverlay=disable-bttoconfig.txtand reboot. - Using the wrong device alias: You are hardcoding
ttyAMA0but the system mapped it tottyS0(mini UART). Fix: Use the stable symlink/dev/serial0in your Python code instead of hardcoding the hardware port name.
The First Three Things to Check When Hardware Fails
If the code runs without errors but you receive no data (or only garbage characters), perform these three physical checks:
- Verify the DE/RE Pin State: If GPIO 18 is stuck HIGH, the transceiver is locked in transmit mode and deaf to the bus. Use a multimeter to verify the DE/RE pin drops to 0V (LOW) when the Pi is supposed to be listening.
- Swap the A and B Bus Wires: RS485 differential signaling relies on A being the non-inverting (+) signal and B being the inverting (-) signal. Many manufacturers label these backward. If you receive garbage, swap the A and B screw terminal wires.
- Check for Ground Reference: RS485 is differential, but the transceivers still share a common-mode voltage range (typically -7V to +12V). If the Pi and the remote sensor have a massive ground potential difference, the signal will clip. Ensure a common ground wire runs alongside the A/B pair.
Extending the Bus: Bias Resistors and Termination
To extend this build from a simple point-to-point test into a robust, multi-drop industrial network, you must manage the physical layer of the bus. According to the Analog Devices RS-485 fundamentals guide, an unterminated or unbiased bus will float into the undefined logic region when no node is transmitting, causing the Pi to receive phantom noise bytes.
How to Simplify: Auto-Direction Modules
If you want to eliminate the GPIO 18 direction-control wiring and the time.sleep() calculations in Python, buy an auto-flow RS485 module (like the Adafruit MAX13487). These modules contain internal circuitry that automatically detects when the TX line goes active and switches the transceiver to transmit mode, reverting to receive mode instantly when the byte finishes sending. This simplifies your Python code to standard PySerial read/writes and frees up a GPIO pin.
How to Extend: Termination and Biasing
When your cable run exceeds 10 meters, or you have more than 3 nodes on the bus, signal reflections will corrupt your data. Implement the following resistor network:
- Termination Resistor: Solder a 120Ω resistor directly across the
AandBterminals at the first and last physical nodes on the daisy chain. Do not place termination resistors on nodes in the middle of the cable. - Bias Resistors (Fail-Safe): The Raspberry Pi (acting as the master) should provide bus biasing. Connect a 390Ω to 560Ω resistor from the
Aline up toVCC(3.3V), and an identical resistor from theBline down toGND. This ensures that when all nodes are in receive mode (high impedance), the bus idles in a defined "Mark" (logic 1) state, preventing the Pi UART from triggering false start-bit interrupts.
For deeper configuration details regarding Raspberry Pi UART routing and device tree overlays, consult the official Raspberry Pi Hardware Configuration Documentation. By pairing a 3.3V-safe SP3485 transceiver with proper biasing and robust PySerial error handling, your Pi will reliably integrate into legacy industrial Modbus or custom sensor networks without risking the host hardware.






