Source: https://mokxi.com/libraries/modbusmaster
Updated: 2026-11-02

Libraries / Communication
Arduino library · Communication

# ModbusMaster: read meters and controllers over Modbus RTU

Modbus RTU is the industrial protocol behind energy meters such as the PZEM-004T, solar charge controllers, VFDs and PLCs. The ModbusMaster library makes the Arduino the master: it sends a request frame to one slave address, waits for the answer, checks its CRC and puts the registers it read into a response buffer.

Open the example in the editor See the calls

Built in on Mokxi: #include <ModbusMaster.h> and it builds, with nothing to install. Free, and no account needed.

## What ModbusMaster does

Most devices speak it over RS-485, a two-wire differential bus, through a MAX485 or similar transceiver. That chip has a driver enable pin (DE, usually tied to RE) that must be high while the Arduino talks and low while it listens, and the library’s preTransmission() and postTransmission() callbacks are where you switch it.

Register addresses are where most time goes. Datasheets list them in different styles (40001 notation, decimal offsets or hex such as 0x3100), and the library wants the zero-based offset. Holding registers are read with function 3 and input registers with function 4; asking with the wrong one returns an exception, not data.

Library Manager name | ModbusMaster

Author | Doc Walker

Include | #include <ModbusMaster.h>

Boards | every board Mokxi compiles for but ATtiny85

In the simulator | Runs, with a gap the note below names.

Parts it drives | RS-485 module, MAX485

## Install it

It builds on every board Mokxi compiles for but ATtiny85.

In Mokxi it is built in: #include <ModbusMaster.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "ModbusMaster" by Doc Walker from the Arduino IDE’s Library Manager (Sketch, Include Library, Manage Libraries), or add ModbusMaster to lib_deps in PlatformIO. Mokxi follows the calls of release 2.0.1, so a sketch written here builds there too.

What to know in the simulator: The pzem004t part (the PZEM-004T energy meter) answers as a slave. Through max485 modules the frames really cross the A/B pair: preTransmission() and postTransmission() switch DE, and the port is flushed before postTransmission() as upstream does. There is no Modbus slave part for RS-485 yet. A request on a port with nothing on it times out after 2 seconds, as on a bench, and a Stream of the sketch’s own can answer as a slave.

ESP32-C3 · Arduino Uno · Arduino Nano · Arduino Mega 2560 · Arduino Leonardo · SparkFun Pro Micro · Arduino Micro · ESP32-C6 · Raspberry Pi Pico · Raspberry Pi Pico W · Seeed XIAO SAMD21 · STM32 · STM32 Nucleo-F411RE · STM32 Blue Pill · micro:bit V2 · Arduino Uno R4 · ESP32 DevKit · ESP32-S3-DevKitC-1 · ESP8266 NodeMCU · Arduino Pro Mini 3.3 V · Seeed XIAO ESP32-C3 · ESP32-C3 SuperMini · Seeed XIAO ESP32-C6 · ESP32-DevKitC V4 · LOLIN32 Lite · Wemos D1 R32 · Cheap Yellow Display · TTGO T-Display · Seeed XIAO ESP32-S3 · Arduino Nano ESP32 · Arduino Nano RP2040 Connect · Seeed XIAO RP2040 · Waveshare RP2040-Zero · Arduino Uno R4 WiFi

## The calls you will use

In the order a sketch meets them.

### ModbusMaster node

One object per slave device you talk to.

### node.begin(1, Serial2)

Sets the slave address (1 here) and the serial port, which you start at the device’s baud rate first, often 9600.

### node.preTransmission(preTransmission)

Calls your function before each request; set the MAX485’s DE and RE pins high in it. node.postTransmission() sets them low again.

### node.readHoldingRegisters(0x3100, 6)

Reads six holding registers from 0x3100 (function 3). Returns node.ku8MBSuccess, 0, when the answer came back valid.

### node.readInputRegisters(0x0000, 10)

Reads input registers (function 4), which is where a PZEM-004T keeps voltage, current and power.

### node.getResponseBuffer(0)

The first register read, a 16-bit word. Values wider than 16 bits span two registers you combine yourself.

### node.writeSingleRegister(0x0001, 100)

Writes one holding register (function 6), for setting a parameter on the slave.

## Example: Holding registers read from a slave the sketch plays itself

On the Arduino Uno R3. It runs in the browser exactly as printed.

sketch.ino · Arduino Uno R3

Open it in the editor, wired and ready to run

## Common errors and how to fix them

### result is 0xE2 (ku8MBResponseTimedOut) on every request

Nothing answered within 2 seconds. Check the slave address and baud rate, that A and B are not swapped, that the DE pin is switched in the callbacks, and that GND is shared with the device.

### result is 0x02, illegal data address

The slave answered with an exception: the register does not exist. Convert the datasheet’s address to a zero-based offset (40001 is 0) and check whether it is a holding or an input register.

### result is 0xE3 (invalid CRC) now and then

Noise or timing on the bus. Add 120 ohm termination at the ends of a long cable, keep the DE switch tight around the send, and avoid SoftwareSerial at high baud rates on an Uno.

## Keep going

### Learn

- UART Serial Explained: Baud, Frames and the Wire

### Parts

- RS-485 module, MAX485

### More communication libraries

- OneWire
- TinyGPS
- TinyGPSPlus
- MFRC522
- SoftwareSerial
- AltSoftSerial
- i2cdetect
- Keyboard

Every built-in library · Open the editor

## Try ModbusMaster without installing anything

Open the example, change the sketch, and keep your version in a free account.

Start building free

Open the editor
