RisalDash RisalDash

Guide · RS-485 · Modbus

Read industrial sensors 1200 m away with RS-485 & Modbus RTU

I²C and plain UART give up after a metre or two. When you need to reach a sensor across a barn, a boiler room or a factory floor — dozens of nodes, hundreds of metres, electrical noise — the answer is RS-485, and the language spoken on it is usually Modbus RTU. A $1 MAX485 module and an ESP32 get you there.

RS-485Modbus RTUMAX485ESP32ArduinoIndustrial

RS-485 isn’t a new protocol — it’s the same UART frame, just sent over a twisted pair at differential levels, like CAN. That’s where the range and noise immunity come from. If that idea is new, see the RS-232 / RS-485 section of the interactive bus explainer first — it shows why differential beats single-ended on the scope.

Why RS-485 instead of UART

Wiring a MAX485

The MAX485 is a transceiver: UART on one side, the RS-485 A/B pair on the other. The one non-obvious pin is direction: DE (driver enable) and RE (receiver enable) are tied together and driven by a GPIO — HIGH to transmit, LOW to receive.

MAX485 module            ESP32
-----------              -----
RO  (receiver out)  -->  RX  (e.g. GPIO16)
DI  (driver in)     <--  TX  (e.g. GPIO17)
DE + RE (tied)      <--  DIR (e.g. GPIO4)   // HIGH = transmit, LOW = receive
VCC                 -->  5V (module) / 3V3 logic-safe variants exist
GND                 -->  GND
A / B  -->  twisted pair to the bus (120 Ω at each far end)
ESP32 wired to a MAX485 RS-485 transceiver — RO to RX, DI to TX, DE and RE tied to a direction GPIO, A and B out to the twisted-pair bus
The MAX485 hookup: RO → RX, DI → TX, DE+RE tied to one direction GPIO, A/B out to the bus. Built in BoardLab.

Two hardware rules that bite everyone: put a 120 Ω terminator at each of the two far ends of the bus (not on every node), and keep A→A, B→B down the whole line. On long or quiet buses add bias resistors so the idle line sits at a defined level.

Modbus RTU in one screen

Modbus is a dead-simple master/slave protocol: the master asks, one slave answers, every frame ends with a CRC. You mostly need two function codes — 0x03 (read holding registers) and 0x04 (read input registers). Here’s an ESP32 reading two registers from slave #1 with the ModbusMaster library, flipping the direction pin automatically:

#include <ModbusMaster.h>
#define DIR 4                       // DE+RE direction pin
ModbusMaster node;

void preTx()  { digitalWrite(DIR, HIGH); }   // switch driver on before sending
void postTx() { digitalWrite(DIR, LOW);  }   // back to listening after

void setup() {
  pinMode(DIR, OUTPUT); digitalWrite(DIR, LOW);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);   // RS-485 UART
  node.begin(1, Serial2);                     // slave address 1
  node.preTransmission(preTx);
  node.postTransmission(postTx);
}

void loop() {
  // Read 2 holding registers starting at 0x0000 (function code 0x03)
  if (node.readHoldingRegisters(0x0000, 2) == node.ku8MBSuccess) {
    uint16_t temp = node.getResponseBuffer(0);
    uint16_t hum  = node.getResponseBuffer(1);
    Serial.printf("temp=%u  hum=%u\n", temp, hum);
  }
  delay(1000);
}

That’s the whole pattern: point the library at your UART, give it the direction-pin callbacks, and call readHoldingRegisters(). Swap in your sensor’s register map from its datasheet.

When it doesn’t work — the usual suspects

Get the pair, the terminators and the direction pin right, and one ESP32 can poll a whole building of Modbus sensors — energy meters, temperature probes, PLCs — over a single twisted pair.

See how a differential pair carries a byte where plain UART can’t.