What You'll Build
A small RS485 network in which one Arduino (the master) talks to one or more slave nodes over a single twisted pair. The master polls each slave for a sensor reading and switches an LED on a slave on and off. Then you will switch the same hardware to Modbus RTU and read registers from an industrial-style sensor.
You will learn:
- Why RS485 survives noise and long cables where plain UART and TTL do not
- How the MAX485 transceiver works and what DE and RE do
- How to wire it to an Arduino Uno and to an ESP32 (and the level trap on the ESP32 receive line)
- How to terminate and bias a bus so it stays reliable
- How to design a simple addressed, checksummed protocol, then use Modbus RTU
| Difficulty | Intermediate |
| Build time | About 45 minutes |
| Boards | Arduino Uno or ESP32 (two or more for the bus) |
| Parts | MAX485 modules, twisted pair, 120 ohm resistors |
Why RS485?
A normal UART sends each bit as a voltage on one wire measured against ground. Over a long cable, noise and ground differences corrupt that voltage quickly, and only two devices can share the link.
RS485 fixes both problems:
- Differential signalling. Data travels on two wires, A and B. The receiver looks at the difference between them, not the absolute level. Noise that couples equally onto both wires cancels out.
- Multi-drop. Many devices connect to the same pair. The standard allows 32 unit loads, and modern low-load transceivers allow more.
- Distance. Cable runs of up to about 1200 m are possible at lower baud rates.
- Half duplex. With one pair, only one device may talk at a time. Everyone else listens.
| Property | UART (TTL) | RS232 | RS485 |
|---|---|---|---|
| Signalling | Single-ended | Single-ended, +/-12 V | Differential |
| Devices | 2 | 2 | Up to 32 (more with low-load parts) |
| Typical max distance | 1 to 2 m | About 15 m | About 1200 m at low speed |
| Noise immunity | Poor | Fair | Excellent |
| Wires | TX, RX, GND | TX, RX, GND | A, B (plus a reference ground) |
The figures are standard-spec rules of thumb. Real limits depend on cable quality, baud rate and termination.
The MAX485 Module
The MAX485 is a half-duplex RS485 transceiver. It has a driver that converts your UART TX into the A/B differential pair, and a receiver that turns A/B back into a logic signal.
| Pin | Name | Function |
|---|---|---|
| 1 | RO | Receiver output: goes to your board's RX |
| 2 | RE (active low) | Receiver enable: LOW = receiver on |
| 3 | DE | Driver enable: HIGH = driver on |
| 4 | DI | Driver input: comes from your board's TX |
| 5 | GND | Ground |
| 6 | A | Non-inverting line |
| 7 | B | Inverting line |
| 8 | VCC | 5 V |
DE and RE tied together. Because RE is active low and DE is active high, joining them gives one control pin: HIGH = transmit, LOW = receive. Most ready-made modules already bridge the two, and the examples here use one GPIO for both.
Direction control is the heart of every RS485 bug. Raise the pin just before you send, and lower it as soon as the last bit has physically left the UART. Lower it too early and the end of your message is cut off. Lower it too late and you miss the start of the reply.
Components
9 itemsWiring
Arduino Uno to MAX485
| MAX485 | Uno |
|---|---|
| VCC | 5V |
| GND | GND |
| RO | D10 (SoftwareSerial RX) |
| DI | D11 (SoftwareSerial TX) |
| DE + RE (joined) | D3 |
ESP32 to MAX485
| MAX485 | ESP32 |
|---|---|
| VCC | VIN (5 V) |
| GND | GND |
| RO | GPIO 16 (RX2) through a voltage divider |
| DI | GPIO 17 (TX2) |
| DE + RE (joined) | GPIO 4 |
Between the nodes
| Node 1 | Node 2 |
|---|---|
| A | A |
| B | B |
| GND | GND (see note below) |
The slave in the sketch reads an analog sensor on A0 (Uno) or GPIO 34 (ESP32) and drives an LED on pin 8.
⚠️ Warning
The MAX485 is a **5 V part**, so its RO output swings to 5 V. That is too much for an ESP32 input (3.3 V max). Put a divider between RO and GPIO 16: RO to 1 k to the GPIO, and from the GPIO to GND with 2 k, which gives about 3.3 V. Or avoid the problem by using a 3.3 V transceiver such as the **MAX3485** or **SP3485**. The ESP32 TX line into DI is fine: a 3.3 V high is still above the MAX485 input threshold.
Termination, Bias and Topology
Three things separate a bus that works on the bench from one that works in the field.
Topology: a line, not a star. Run one continuous cable from device to device (a daisy chain), keeping any branch stubs very short. Star wiring causes reflections that scramble data at higher speeds.
Termination. A signal reaching the end of an unterminated cable reflects back and interferes with itself. Place a 120 ohm resistor across A and B at each of the two physical ends of the bus, and nowhere else. Over a few metres at 9600 baud you can often get away without it, but add it as soon as the cable gets long or the baud rate rises. Many modules have an on-board termination jumper.
Bias (failsafe) resistors. When nobody is driving the bus, A and B float and the receiver can see random noise as data. A pull-up on A and pull-down on B, usually somewhere in the 560 to 1k ohm range and fitted in one place only, holds the idle line in a clean state. Cheap modules often lack them. If you see stray garbage characters while the bus is idle, add bias.
Ground. RS485 is differential, but receivers have a limited common-mode range. Tie the grounds of all nodes together (many designs use a third wire or the cable shield through a small resistor) so the nodes stay within that range.
A and B labelling. Vendors disagree on which terminal is A and which is B. If nothing works and everything else is right, swap the two wires.
Speed Versus Distance
Higher baud rates need shorter cables. These are the commonly quoted RS485 limits for good twisted-pair cable with proper termination:
| Baud rate | Typical maximum cable length |
|---|---|
| 9,600 | About 1200 m |
| 115,200 | A few hundred metres |
| 1 Mbps | About 100 m |
| 10 Mbps | About 12 m |
Treat the table as an upper bound. A cheap module on thin wire will fall short, which is another reason to start at 9600 baud and only raise it once the bus is stable.
Designing the Protocol
RS485 only moves bytes. It has no concept of "who is this for" or "did it arrive intact". You supply that. The example uses a readable ASCII frame:
$<addr>,<cmd>,<value>*<checksum>\n
$marks the start of a frame, so a receiver can resynchronise after noise.- addr says which slave the frame is for (and which slave a reply is from).
- cmd is
GET,SET, or a reply:VAL,ACK,ERR. - checksum is the XOR of every character between
$and*, written as two hex digits. \nends the frame.
The master sends $01,GET,0*xx and slave 1 answers $01,VAL,512*xx. A slave that sees a bad checksum stays silent, and the master treats a missing reply as a timeout. Only the addressed slave ever drives the bus, which is how half duplex stays collision free.
Steps
- 1Wire each board to its MAX485 as shown, with the board unpowered.
- 2Connect A to A, B to B and GND to GND between the two nodes; add 120 ohm resistors across A and B at both ends.
- 3Upload the slave sketch to node 2 (set MY_ADDR = 1) and open its Serial Monitor at 115200.
- 4Upload the master sketch to node 1 and open its Serial Monitor at 115200.
- 5Turn the potentiometer on the slave and watch the master print the changing value; the slave LED should toggle once per second.
Slave Code
Upload this to every slave and give each a different MY_ADDR. It works on both the Uno and the ESP32. A slave only answers frames addressed to it.
Master Code
The master asks each slave for a value, prints it, then toggles the LED on slave 1. Each request() drains stale bytes, sends a frame, and waits up to 200 ms for the matching reply. A corrupted or missing reply is reported as "no reply" instead of hanging the sketch.
If you only built one slave, the master will print Slave 2: no reply every cycle. That is the timeout path working as designed. Add a second slave with MY_ADDR = 2 and it disappears.
How the Direction Control Works
The three lines around bus.print() in sendFrame() are the most important in the whole project:
digitalWrite(DE_RE_PIN, HIGH)turns the driver on and the receiver off. A 50 microsecond pause lets the driver settle.bus.flush()blocks until the UART has shifted out the last bit. Without it, the code would flip back to receive mode while bytes were still being sent, truncating the frame.- A 2-bit-time delay, then
LOW, adds a little margin and returns the transceiver to listening, ready for the slave's reply.
With SoftwareSerial on the Uno, bytes are sent in software, so flush() behaves differently from hardware serial. The extra delay covers that. On the ESP32 hardware UART it is cheap insurance.
Going Industrial: Modbus RTU
Most commercial RS485 sensors, meters and PLCs speak Modbus RTU. It is a standard on top of the same hardware, so your wiring does not change. Instead of ASCII, frames are binary and protected by a 16-bit CRC:
| Field | Size | Example (read 2 registers) |
|---|---|---|
| Slave address | 1 byte | 01 |
| Function code | 1 byte | 03 (read holding registers) |
| Start register | 2 bytes | 00 00 |
| Register count | 2 bytes | 00 02 |
| CRC16 | 2 bytes (low byte first) | C4 0B |
So the full request is 01 03 00 00 00 02 C4 0B. A temperature and humidity sensor might answer 01 03 04 00 FA 01 F4 DA 15: four data bytes holding 250 and 500, which the sensor's manual says means 25.0 degrees and 50.0 percent.
You do not need to calculate the CRC by hand. The ModbusMaster library by Doc Walker (install from Library Manager) builds the frame and checks the reply. Its two callbacks switch the DE/RE pin before and after each transmission.
Always take the address, baud rate, parity, register map and scaling from your device's datasheet. They vary widely. A very common mismatch is parity: many devices use 8E1 or 8N2 instead of 8N1, and SoftwareSerial cannot do parity. For those devices use a hardware serial port, for example Serial2.begin(9600, SERIAL_8E1, 16, 17) on the ESP32.
ModbusMaster returns a status code. The ones you will meet most:
| Code | Meaning | Usual cause |
|---|---|---|
| 0x00 | Success | |
| 0xE0 | Invalid slave ID | Reply came from a different address |
| 0xE1 | Invalid function | Device does not support that function code |
| 0xE2 | Response timeout | Wrong wiring, A/B swapped, wrong baud, wrong address, no power |
| 0xE3 | Invalid CRC | Noise, missing termination, parity mismatch |
Inspecting the Bus
When something is wrong, look at the signal. A logic analyzer or oscilloscope on A and B shows frames directly. Even a cheap USB-to-RS485 adapter on a PC with a serial terminal lets you see exactly what bytes are on the wire, which separates a wiring problem from a code problem in seconds.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Master always prints "no reply" | A and B swapped, no common ground, wrong address | Swap A and B; connect GND; check MY_ADDR. |
| Garbage characters | Baud mismatch, parity mismatch, no termination | Match settings on both ends; add 120 ohm terminators. |
| First bytes of the reply are lost | DE/RE released late or master raised it late | Keep the direction switching tight; the master should be listening before the slave replies. |
| End of the message is cut off | DE/RE dropped too early | Call flush() and keep the short delay before going low. |
| Works on the bench, fails on the long cable | Missing termination or bias, star wiring | Terminate both ends, daisy-chain, add bias, lower the baud. |
| Random errors when a motor runs | Noise or ground shift | Use twisted pair, keep it away from motor wiring, and use shielded cable with a single ground point. |
| ESP32 receives nothing | RO into GPIO 16 without divider, wrong pins | Fit the divider or a 3.3 V transceiver; confirm RX2/TX2 pins. |
| ESP32 resets or behaves oddly | 5 V on a 3.3 V pin | Check every line crossing the 5 V / 3.3 V boundary. |
| Modbus gives 0xE2 | Wrong slave ID, baud, parity or wiring | Verify against the device manual; try swapping A and B. |
| Two slaves answer at once | Same address | Give every slave a unique address. |
Ideas to Extend
- More slaves. Add nodes 3, 4 and beyond, each with its own
MY_ADDR. Keep the polling timeout in mind: each missing slave costs one timeout per cycle. - Isolation. For noisy industrial settings use an isolated RS485 transceiver so ground differences cannot damage your board.
- Bridge to the cloud. Let an ESP32 master poll the bus and forward readings with the ESP32 MQTT tutorial.
- Longer reach without wires. If the nodes are too far apart to run cable, look at the LoRa guide instead.
- Another wired option. For short on-board links compare with I2C vs SPI.
FAQ
Can I use RS485 with just two wires? Yes: A and B are the signal pair. Tie the grounds as well for reliability.
Does RS485 need a master? The electrical standard does not, but practical protocols like Modbus RTU use one master and many slaves to avoid collisions on the shared pair.
What is the difference between RS485 and RS422? RS422 is full duplex with separate transmit and receive pairs; RS485 is usually half duplex on a single pair.
Can I use SoftwareSerial for this? For simple 9600 baud links on the Uno, yes. For higher speeds, parity, or when the board is busy, use a hardware serial port.
Why do I need two 120 ohm resistors, not one? Each end of the line needs a terminator to absorb the signal; with only one end terminated, reflections still occur from the other.
Related on SolderHub
- I2C vs SPI: Differences and When to Use Each
- Arduino Uno Pinout: The Complete Guide
- ESP32 Pinout: The Complete Guide
- LoRa Communication with ESP32 and Arduino Uno
Published on SolderHub