ESP32/Buses/34. RS485 and Modbus
Your chip
Your language
Buses · 34 of 81

RS485 and Modbus

The same UART, sent as the difference between two wires instead of a voltage against ground. That one change buys a kilometre of cable in an electrically filthy building - and adds one pin that ruins most first attempts.

/esp32/rs485-and-modbus · arduino · S3

One pin, one bug

DE released, relative to the last bit
message truncated
You drop DE0.4 character times (0.42 ms)
One character at 9600
1 ms
Turnaround
too early
You stopped driving the line before the UART had finished sending. Serial.write() returns as soon as the bytes are in the FIFO, not when they are on the wire — that is the trap. Call Serial.flush() first, and every Modbus reply becomes a CRC error until you do.

What the wiring looks like

The ESP32 does not speak RS485 itself. A transceiver — MAX485, SP3485, or one of the automatic ones — sits between the UART and the twisted pair.

ESP32Transceiver
TXDI
RXRO
Any GPIODE and RE, tied together
3V3 and GNDVCC and GND

Then A and B go to every device on the line, in parallel, with 120 Ω across the pair at each far end.

Modbus in one paragraph

Every device has an address, 1 to 247. The master sends address, function code, register number, count, and a CRC. The slave replies with the data and its own CRC. There is no discovery — you get the register map from the meter's manual — and there is exactly one master, which is why the direction pin matters so much.

Libraries do the framing and the CRC; the DE timing is still yours.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

The whole discipline is in the send function - raise DE, write, flush, drop DE. flush is the line everyone leaves out, and it is why the last byte gets cut off.

modbus_master.ino
const int DE = 4;          // driver enable, often tied to RE as well

void sendFrame(const uint8_t *buf, size_t n) {
  digitalWrite(DE, HIGH);       // we are talking
  Serial2.write(buf, n);
  Serial2.flush();              // wait for the last bit to leave
  digitalWrite(DE, LOW);        // now listen
}

void setup() {
  Serial.begin(115200);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);

  // read 1 holding register at 0x0000 from slave 1
  uint8_t req[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A };
  sendFrame(req, sizeof(req));
}

void loop() {
  while (Serial2.available()) Serial.printf("%02X ", Serial2.read());
}

Serial.write returns as soon as the bytes are in the FIFO, not when they are on the wire. Without flush you drop the driver mid-character and every reply is a CRC error.

When it does not work

Every reply is a CRC error

You dropped DE before the last byte finished sending, so the frame is truncated. Call flush between write and dropping DE.

The master reports a timeout and the slave looks dead

You held DE too long and were still driving the line when the slave answered. Two drivers on one pair means neither message survives.

It works with two devices and fails with eight

Termination. Put 120 Ω across A and B at each end of the run, and only at the ends. A resistor at every node loads the bus into silence.

Random errors on a long run in a factory

A and B swapped somewhere, or no common ground reference. RS485 is differential but not magic - most transceivers need the two ends within a few volts of each other.

Where this goes next

That is every bus. Next: where the data lands once it is on the board, and why flash is not simply a disk.

Filesystem on flash

Edit this page — content/esp32/rs485-and-modbus.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum