Overview
The MAX485 module converts a microcontroller's TTL serial into RS-485 — the differential bus standard that industrial equipment has trusted for decades. Instead of one signal wire referenced to ground, RS-485 sends every bit as a voltage difference across a twisted pair (A and B). Noise hits both wires equally and cancels out, which is how RS-485 runs reliably across factory floors, long cable trays, and electrically hostile environments where plain UART gives up after a few meters: think 1200 meters at modest baud rates, with up to 32 devices sharing one pair.
The board is the classic C25B layout: the MAX485 transceiver in SOIC-8, a green screw terminal plus header pins for the A/B bus, a power LED, and a 120-ohm termination resistor (R7) already fitted between A and B. The TTL side breaks out RO (receive out), DI (drive in), and the two enables — RE and DE — which you tie together and treat as one direction pin: high to transmit, low to receive. That one wire is the whole trick of half-duplex RS-485.
Use a pair of these to link two microcontrollers across a building, poll Modbus RTU devices like power meters and VFDs, network a chain of sensor nodes, or drive DMX-style lighting experiments. It runs from 5V (and works with 3.3V logic on the ESP32/Pi side with the usual precaution on RO), and needs nothing but three data wires per end plus the twisted pair between them.
At a Glance
Specifications
| Parameter | Value |
| Transceiver IC | MAX485 (or equivalent), half-duplex RS-485/RS-422 |
| Supply Voltage | 5V DC (logic inputs 3.3V-compatible) |
| Data Rate | Up to 2.5 Mbps (short runs); 9600-115200 baud typical |
| Range vs Speed | ~1200 m at 9600-100k baud; shorter as speed rises |
| TTL Pins | RO (receiver out), RE (receiver enable, active low), DE (driver enable), DI (driver in) |
| Bus Pins | A (Data+), B (Data-) on screw terminal and header |
| Termination | 120 ohm (R7) fitted between A and B |
| Nodes per Segment | 32 unit loads |
| Common-Mode Range | -7V to +12V |
| Current Draw | ~300 uA idle, ~50 mA transmitting into terminated bus |
| Indicator | Power LED (D1) |
Pinout Diagram
Left header, top to bottom: RO, RE, DE, DI — the TTL side. Right side: VCC, B, A, GND on the header, with A and B duplicated on the green screw terminal for field wiring. R7 is the 120-ohm load resistor between A and B; it stays for the two modules at the ends of a long bus and comes off for middle nodes.
Wiring Guide
In every hookup: tie RE and DE together into one "direction" GPIO, cross A→A and B→B between modules with twisted pair, and join grounds between nodes when they run from separate supplies.
Arduino Wiring (SoftwareSerial)
| MAX485 Pin | Arduino Pin | Details |
|---|---|---|
| VCC / GND | 5V / GND | |
| RO | D10 | SoftwareSerial RX |
| DI | D11 | SoftwareSerial TX |
| RE + DE (tied) | D3 | HIGH = transmit, LOW = receive |
| A / B | Twisted pair to far module | A-A, B-B |
ESP32 Wiring (hardware UART2)
| MAX485 Pin | ESP32 Pin | Details |
|---|---|---|
| VCC / GND | VIN (5V) / GND | Logic pins accept 3.3V highs |
| RO | GPIO 16 (RX2) | Through 1k series resistor (see note) |
| DI | GPIO 17 (TX2) | |
| RE + DE | GPIO 4 | Direction control |
| A / B | Bus pair |
Raspberry Pi Wiring (UART0)
| MAX485 Pin | Pi Pin | Details |
|---|---|---|
| VCC / GND | Pin 2 (5V) / Pin 6 (GND) | |
| RO | Pin 10 (GPIO 15, RXD) | Via 1k series resistor |
| DI | Pin 8 (GPIO 14, TXD) | |
| RE + DE | Pin 11 (GPIO 17) | Direction control |
| A / B | Bus pair |
Raspberry Pi Pico Wiring (UART0)
| MAX485 Pin | Pico Pin | Details |
|---|---|---|
| VCC / GND | VBUS (5V) / GND | |
| RO | GP1 (UART0 RX) | Via 1k series resistor |
| DI | GP0 (UART0 TX) | |
| RE + DE | GP2 | Direction control |
| A / B | Bus pair |
Code Examples
The examples implement the same simple protocol — a sender that transmits a numbered message once a second and switches back to receive to await an "ACK". Flash the sender code on one board and adapt the receive loop on the other (roles are symmetric).
Arduino
// MAX485 RS-485 - Arduino Example (sender + listener)
// RO->D10, DI->D11, RE+DE->D3, VCC->5V
#include <SoftwareSerial.h>
const int DIR_PIN = 3; // HIGH = TX, LOW = RX
SoftwareSerial rs485(10, 11); // RX, TX
unsigned long counter = 0;
void setTransmit(bool tx) {
digitalWrite(DIR_PIN, tx ? HIGH : LOW);
delayMicroseconds(50); // let the driver settle
}
void setup() {
Serial.begin(115200);
rs485.begin(9600);
pinMode(DIR_PIN, OUTPUT);
setTransmit(false);
Serial.println("RS-485 node ready");
}
void loop() {
// --- send one message ---
setTransmit(true);
rs485.print("MSG ");
rs485.println(counter++);
rs485.flush(); // wait until fully shifted out
setTransmit(false);
// --- listen for replies for 1 second ---
unsigned long t0 = millis();
while (millis() - t0 < 1000) {
if (rs485.available()) {
String line = rs485.readStringUntil('\n');
Serial.print("Received: ");
Serial.println(line);
}
}
}
ESP32 (Arduino IDE, UART2)
// MAX485 RS-485 - ESP32 Example (hardware UART2)
// RO->GPIO16, DI->GPIO17, RE+DE->GPIO4
const int DIR_PIN = 4;
unsigned long counter = 0;
void setTransmit(bool tx) {
digitalWrite(DIR_PIN, tx ? HIGH : LOW);
delayMicroseconds(50);
}
void setup() {
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX=16, TX=17
pinMode(DIR_PIN, OUTPUT);
setTransmit(false);
Serial.println("RS-485 node ready");
}
void loop() {
setTransmit(true);
Serial2.printf("ESP32 MSG %lu\n", counter++);
Serial2.flush();
setTransmit(false);
unsigned long t0 = millis();
while (millis() - t0 < 1000) {
if (Serial2.available()) {
String line = Serial2.readStringUntil('\n');
Serial.print("Received: ");
Serial.println(line);
}
}
}
Raspberry Pi (Python)
#!/usr/bin/env python3
# MAX485 RS-485 - Raspberry Pi Example
# RO->GPIO15(RXD), DI->GPIO14(TXD), RE+DE->GPIO17
# Install: pip3 install pyserial ; enable UART in raspi-config
import serial
import time
import RPi.GPIO as GPIO
DIR_PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(DIR_PIN, GPIO.OUT, initial=GPIO.LOW)
port = serial.Serial("/dev/serial0", 9600, timeout=0.2)
def send(msg):
GPIO.output(DIR_PIN, GPIO.HIGH)
time.sleep(0.0001)
port.write((msg + "\n").encode())
port.flush()
time.sleep(0.002) # drain the UART FIFO
GPIO.output(DIR_PIN, GPIO.LOW)
counter = 0
print("RS-485 node ready")
try:
while True:
send("PI MSG {}".format(counter))
counter += 1
t0 = time.time()
while time.time() - t0 < 1.0:
line = port.readline()
if line:
print("Received:", line.decode(errors="ignore").strip())
except KeyboardInterrupt:
GPIO.cleanup()
print("Stopped by user")
Raspberry Pi Pico (MicroPython)
# MAX485 RS-485 - Pico MicroPython Example
# RO->GP1, DI->GP0, RE+DE->GP2
from machine import UART, Pin
import time
uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
dir_pin = Pin(2, Pin.OUT, value=0) # 0 = receive
def send(msg):
dir_pin.value(1)
time.sleep_us(100)
uart.write(msg + "\n")
# wait for the frame to leave the shift register
time.sleep_ms(2 + len(msg))
dir_pin.value(0)
counter = 0
print("RS-485 node ready")
while True:
send("PICO MSG {}".format(counter))
counter += 1
t0 = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), t0) < 1000:
if uart.any():
line = uart.readline()
if line:
print("Received:", line.decode().strip())