Modbus RTU over RS-485 is the serial workhorse of industrial field wiring — the variant you'll meet when connecting an ESP32 directly to an energy meter, PLC, VFD, or temperature transmitter over a wired bus. This tutorial walks through wiring the hardware, installing a library, and flashing working RTU master code. What You'll Build A Modbus RTU master on ESP32 that polls holding registers from an RS-485 slave device over a wired bus. An understanding of register types, addressing, and the reliability practices that separate a demo from a production deployment. Prerequisites Arduino IDE (or PlatformIO) with the ESP32 board package installed. An ESP32 dev board, or an industrial ESP32 controller with a built-in RS-485 transceiver such as the NORVI X — this saves you from wiring a separate MAX485 module. A Modbus RTU slave device (energy meter, sensor, or PLC). Basic familiarity with the Arduino C++ syntax and serial monitor debugging. A 60-Second Modbus Primer Modbus is a master–slave protocol dating back to 1979. One master polls up to 247 slave devices, each with a unique address (1–247). Data lives in four register types, and knowing which one you need is half the battle: Register Type Access Width Typical Use Coils (0x) Read/Write 1-bit Relay outputs, digital controls Discrete Inputs (1x) Read only 1-bit Digital sensor inputs, switch states Input Registers (3x) Read only 16-bit Analog sensor values, process data Holding Registers (4x) Read/Write 16-bit Setpoints, configuration parameters Modbus RTU over RS-485 Step 1 — Wire the Hardware The ESP32's UART pins output 3.3V TTL logic, but RS-485 uses a differential voltage signal — so you need a TTL-to-RS-485 transceiver (typically a MAX485 or MAX3485 chip) between the ESP32 and the bus. UART TX → transceiver DI (driver input) UART RX ← transceiver RO (receiver output) A spare GPIO → transceiver DE and RE tied together (direction control) Transceiver A/B terminals → the RS-485 A+/B− pair on your slave device Skip the transceiver wiring Industrial controllers like the NORVI X have the TTL-to-RS-485 converter built into the board, so RS-485 slaves connect straight to a screw terminal — no breadboard, no separate module, and DE/RE switching is handled for you. Two more wiring details that matter more than they look: Place a 120Ω termination resistor at each physical end of the RS-485 bus if the run is longer than a few meters. Give every slave device a unique address in the 1–247 range — duplicate addresses cause bus conflicts. Step 2 — Install a Modbus Library For RTU master mode on Arduino, ModbusMaster is the simplest starting point. Install it from the Library Manager: Arduino IDE → Sketch → Include Library → Manage Libraries... Search: "ModbusMaster" by Doc Walker → Install Enter fullscreen mode Exit fullscreen mode Other options worth knowing about, depending on your needs: modbus-esp32 — supports both RTU and TCP/IP, master and slave modes. ArduinoModbus — the official Arduino library, straightforward setup. esp32ModbusRTU — interrupt-driven and non-blocking, useful if your loop() is already busy. Step 3 — Write the RTU Master Sketch This sketch polls two holding registers from slave address 1 every half-second and prints the values to the serial monitor: #include #define SLAVE_ID 1 #define RXD2 16 // ESP32 UART2 RX → transceiver RO / NORVI X RS-485 header #define TXD2 17 // ESP32 UART2 TX → transceiver DI / NORVI X RS-485 header ModbusMaster node; void setup() { Serial.begin(115200); Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // match your slave's baud/parity node.begin(SLAVE_ID, Serial2); Serial.println("Modbus RTU master ready"); } void loop() { // Read 2 holding registers starting at address 0x0000 uint8_t result = node.readHoldingRegisters(0x0000, 2); if (result == node.ku8MBSuccess) { uint16_t reg0 = node.getResponseBuffer(0); uint16_t reg1 = node.getResponseBuffer(1); Serial.printf("Register 0: %u | Register 1: %u\n", reg0, reg1); } else { Serial.printf("Modbus read failed — error code: 0x%02X\n", result); } delay(500); // respect the slave's minimum poll interval } Enter fullscreen mode Exit fullscreen mode A few things to adjust for your setup: Baud rate, parity, and stop bits in Serial2.begin() must match your slave device exactly — check the datasheet. 9600 8N1 is the most common default. The register start address (0x0000) and quantity (2) depend on your device's register map. If you're using an external MAX485 module instead of a built-in transceiver, you'll also need to toggle a DE/RE GPIO to HIGH before transmitting and LOW after — libraries like esp32ModbusRTU handle this automatically. Reliability Checklist for Production (RTU) A working sketch on the bench is not the same as a stable deployment on a factory floor. Before you ship: Termination: 120Ω resistors at both physical ends of an RS-485 bus longer than a few meters — omitting this causes reflections and intermittent read failures. Unique addressing: every RTU slave needs a distinct 1–247 address. Retry logic: treat a single failed poll as noise, not a fault. Retry two or three times before flagging an alarm. CRC validation: RTU frames carry a CRC checksum — confirm your library validates it (most do by default) rather than trusting raw bytes. Poll interval: don't poll faster than the slave's documented response time — 100–500 ms minimum is typical. Hammering the bus causes timeouts, not faster data. Floating-point data: Modbus registers are 16-bit integers. Many analog sensors split a float across two consecutive registers in IEEE 754 format — check the datasheet's register map before assuming a raw integer. Troubleshooting Common Issues (RTU) Symptom Likely Cause Reads return 0xE2 or timeout errors Baud rate, parity, or stop bits mismatch with the slave Intermittent garbage on long RS-485 runs Missing or incorrect 120 Ω termination resistors Bus works with one slave, fails with several Duplicate slave addresses on the bus Values look scrambled or nonsensical Register map mismatch — wrong start address or misreading a float as two integers Wrapping Up The ESP32 handles Modbus RTU comfortably, and the code in this tutorial is enough to get real data flowing from an RS-485 slave device today. RTU is the right call for field-level wiring to sensors, meters, and PLCs where a wired serial bus makes more sense than a network connection. From here, the natural next steps are adding retry/backoff logic around each poll, mapping out your specific device's full register table, and — if you're moving from prototype to a real deployment — considering hardware that removes the RS-485 wiring step entirely. Further Reading and Resources Original field guide this tutorial is based on: Modbus with ESP32 — RTU & TCP Guide (NORVI Blog) Hardware used in this tutorial: NORVI X — ESP32-S3 modular industrial IoT controller Full technical specs: NORVI X datasheet (PDF) Related deep-dive: NORVI X SCADA Integration — Modbus TCP to OPC-UA Gateway Setup Sample code and firmware: NORVI Controllers on GitHub Full documentation library: norvi.io/docs Questions about sizing hardware for your project? Book a free consultation
Getting Started with Modbus RTU on ESP32
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.