RS-485 and Modbus RTU for Makers: Wiring, Termination, and Reading Industrial Sensors
Most maker projects that talk between boards reach for I2C, SPI, or plain UART, and this site has a full comparison of those three. RS-485 solves a different problem: it's a differential serial standard built to survive electrically noisy environments and run wires far longer than any of those three protocols tolerate — hundreds of meters instead of a few feet. It shows up constantly once you start working with industrial sensors, VFDs, PLCs, energy meters, and irrigation controllers, almost always carrying the Modbus RTU protocol on top. This guide covers the electrical layer (RS-485 wiring, termination, biasing) and the application layer (Modbus RTU addressing and register reads) together, since neither makes much sense without the other.
RS-485 vs I2C, SPI, and UART
ProtocolSignalingPractical distanceBus topologyNoise immunity I2CSingle-ended, open-drain~1-2 mMulti-dropPoor SPISingle-ended<1 m typicalPoint-to-point (per CS)Poor UART (TTL/RS-232)Single-endedFew metersPoint-to-pointPoor RS-485Differential (A/B pair)Up to ~1200 m at low baudMulti-drop, up to 32 standard-load devicesExcellentThe differential pair (usually labeled A/B or D+/D−) is what makes RS-485 immune to the electrical noise that would corrupt a single-ended line: both wires pick up the same interference, and the receiver only cares about the voltage difference between them, which cancels the common-mode noise out. That's why RS-485 is the standard for wiring runs across a shop, between a barn and a house, or anywhere near VFDs, motors, and relays that inject noise onto nearby wiring.
Wiring an RS-485 Bus
- Twisted pair, ideally shielded (STP) for the A/B signal pair — standard Cat5e/Cat6 works fine for short-to-medium runs; dedicated shielded twisted pair with a drain wire is worth it for long runs near motors or VFDs.
- Daisy-chain topology, not a star. RS-485 wants each device wired in a line from one end of the bus to the other. Star topologies (a hub with spurs to each device) create reflections that corrupt data at higher baud rates — keep any stub off the main trunk under a few inches.
- A common ground reference between all devices, run as a third conductor alongside the A/B pair. RS-485 transceivers have a limited common-mode voltage range; without a shared ground reference, that range can be exceeded on longer runs with devices powered from different sources.
- 120 ohm termination resistors at each physical end of the bus (not at every device) — matching the resistor to the cable's characteristic impedance suppresses signal reflections. Most industrial devices have a DIP switch or jumper for this; add a discrete 120 ohm resistor across A/B if not.
- Bias resistors (typically 560 ohm to VCC on the A line, 560 ohm to ground on the B line) at one point on the bus, to hold the line in a known idle state when no device is actively driving it — without this, an idle bus can float and register as random noise, corrupting the start of the next transmission. Many RS-485 transceiver boards include these as a solder-jumper option.
Adding RS-485 to an ESP32, Arduino, or Raspberry Pi
None of these have RS-485 transceivers built in — they all speak plain TTL UART, so an external transceiver chip does the electrical conversion. The MAX485 and MAX3485 (3.3V-tolerant, important for the ESP32) are the ubiquitous cheap options, usually sold on small breakout boards.
Transceiver pinConnects to VCC / GND3.3V or 5V (check the chip's rated voltage) and ground RO (Receiver Out)Microcontroller RX pin DI (Driver In)Microcontroller TX pin DE / RE (Driver Enable / Receiver Enable)Tied together, driven by a spare GPIO — HIGH to transmit, LOW to receive A / BThe RS-485 bus twisted pairThe DE/RE pin is the part that trips people up: RS-485 is half-duplex, so the microcontroller has to explicitly switch the transceiver into transmit mode before sending a Modbus request and back to receive mode to read the response. Libraries like ModbusMaster (Arduino/ESP32) and pymodbus (Raspberry Pi/Python) handle this switching automatically if you pass them the DE/RE pin number, but if you're writing raw serial code yourself, forgetting to toggle it back to receive after a write is the most common cause of a bus that "sends fine but never gets a response."
Modbus RTU Basics
Modbus RTU is the binary framing and addressing scheme almost everything on an RS-485 industrial bus speaks. It's a master/slave (request/response) protocol: your microcontroller is almost always the master, polling one or more slave devices by address.
- Slave address — a 1-247 ID set on each device, usually via DIP switches or a configuration menu. No two devices on the same bus can share an address.
- Function code — what operation to perform: the common ones for reading sensors are 0x03 (Read Holding Registers) and 0x04 (Read Input Registers); 0x06 (Write Single Register) and 0x10 (Write Multiple Registers) cover writes to setpoints or relay outputs.
- Register map — every Modbus device publishes a datasheet table mapping register addresses to values (e.g. register 0x0001 = temperature in tenths of a degree C). There's no universal standard here; you always need the specific device's register map.
- CRC-16 checksum — appended to every frame; libraries calculate this automatically, but it's why hand-rolled Modbus code without a proper CRC implementation silently fails.
A typical read with the Arduino ModbusMaster library looks like:
ModbusMaster node; node.begin(1, Serial2); // slave address 1, hardware serial port uint8_t result = node.readHoldingRegisters(0x0001, 2); // start register, count if (result == node.ku8MBSuccess) { uint16_t value = node.getResponseBuffer(0); }Common Bus Problems and Fixes
SymptomLikely causeFix Works at short range, fails as cable gets longerMissing or wrong termination resistorsAdd 120 ohm resistors at both physical ends only Intermittent garbage on an idle busNo bias resistorsAdd pull-up/pull-down bias resistors at one point on the bus Address conflicts / no response from a specific deviceDuplicate slave address or wrong baud/parity settingsVerify address uniqueness and match baud, parity, and stop bits exactly across all devices Works with one slave, fails with multipleStar wiring instead of daisy-chain, or DE/RE pins not releasing the bus properlyRewire as a proper trunk line; check transmit-enable timing in code Noise near a VFD or motor corrupts dataUnshielded cable, poor groundingUse shielded twisted pair with the shield grounded at one end only, route away from power cablingSafety Note
RS-485 itself runs at low signal voltages and poses no shock hazard, but the devices it's commonly wired to — VFDs, motor controllers, industrial relays, mains-adjacent PLCs — often are not low voltage. Treat any enclosure or terminal block near RS-485 wiring on industrial equipment as potentially live, and use optically isolated RS-485 transceiver modules (not the bare MAX485 breakout) when bridging a hobby microcontroller to equipment that shares a chassis or ground with mains-powered gear. Isolation modules are inexpensive and remove a real path for a wiring mistake to put line voltage where a 3.3V ESP32 pin lives.
Once the electrical layer is solid — correct termination, one shared ground reference, no star topology — Modbus RTU itself is a fairly forgiving protocol to work with, and it opens up a huge range of inexpensive industrial-grade sensors, energy meters, and I/O modules that never show up with I2C or SPI interfaces. Combined with an ESP32 or Raspberry Pi publishing readings to MQTT or Home Assistant, an RS-485/Modbus bus is a solid way to bring genuinely industrial hardware into a home automation or monitoring setup.