Modbus is one of the most common communication protocols in industrial automation. It is used by PLCs, HMIs, SCADA systems, power meters, drives, temperature controllers, remote I/O modules, sensors, and many other field devices.

The idea behind Modbus is simple: a client asks a device to read or write data from a known address, and the device replies. The protocol defines how the request and response are formatted, but the meaning of each register is defined by the device manufacturer in a register map.

Client and Server

Modbus uses a client-server model. Older documents often use the words master and slave. In modern terminology, the client starts the transaction and the server answers it.

  • Client: PLC, SCADA software, HMI, gateway, or test tool that sends requests.
  • Server: Field device that owns the registers and responds to requests.

A normal Modbus server does not send data by itself. The client polls the server by asking for specific coils, inputs, or registers.

Modbus RTU

Modbus RTU is the serial version of Modbus. It is commonly used over RS-485 because RS-485 allows multiple devices to share the same pair of wires and works well in electrically noisy industrial environments.

A typical Modbus RTU frame contains a device address, a function code, the request or response data, and a CRC checksum.

Slave Address | Function Code | Data | CRC

Example RTU request:

01 03 00 00 00 02 C4 0B
  • 01 is the server address.
  • 03 means read holding registers.
  • 00 00 is the starting address.
  • 00 02 asks for two registers.
  • C4 0B is the CRC.

Modbus TCP

Modbus TCP carries Modbus messages over Ethernet using TCP/IP. The standard port is 502. Instead of the RTU CRC, Modbus TCP uses an MBAP header before the function code and data.

MBAP Header | Function Code | Data

The MBAP header includes the transaction ID, protocol ID, length, and unit ID. The unit ID is especially useful when a Modbus TCP gateway forwards requests to Modbus RTU devices behind it.

Feature Modbus RTU Modbus TCP
Transport Serial, usually RS-485 Ethernet TCP/IP
Addressing Slave address, usually 1 to 247 IP address plus unit ID
Error check CRC in the Modbus frame Handled by Ethernet and TCP layers
Typical use Field devices, meters, drives, remote I/O PLCs, HMIs, SCADA, gateways, Ethernet devices

The Four Modbus Data Areas

Modbus defines four logical data areas. These are often called registers, although technically coils and discrete inputs are single-bit values, not 16-bit registers.

Data area Access Size Classic address range Typical meaning
Coils Read and write 1 bit 00001 to 09999 Digital outputs or command bits
Discrete Inputs Read only 1 bit 10001 to 19999 Digital input states
Input Registers Read only 16 bits 30001 to 39999 Measured analog values
Holding Registers Read and write 16 bits 40001 to 49999 Configuration, setpoints, status words, parameters

The classic address ranges are human-readable conventions. Inside the Modbus message, the address is normally zero-based. For example, holding register 40001 is usually sent as address 0, and holding register 40010 is usually sent as address 9.

Coils

Coils are single-bit read/write values. They are often used for digital outputs or commands.

  • 0 means off, false, or inactive.
  • 1 means on, true, or active.
Function code Name Purpose
01 Read Coils Read one or more coil states.
05 Write Single Coil Write one coil on or off.
15 Write Multiple Coils Write a group of coil states.

Discrete Inputs

Discrete inputs are single-bit read-only values. They usually represent physical input states or feedback signals, such as limit switches, fault contacts, door sensors, emergency stop state, or motor running feedback.

Discrete inputs are read with function code 02. They are not written by the client.

Input Registers

Input registers are 16-bit read-only values. They are commonly used for live measurements: voltage, current, temperature, pressure, flow, speed, frequency, and other process values.

Input registers are read with function code 04. If a value is larger than 16 bits, the device combines multiple consecutive registers.

Holding Registers

Holding registers are 16-bit values that can usually be read and written. They are the most commonly used Modbus data area because they can hold configuration values, setpoints, control words, status words, counters, and calculated process data.

Function code Name Purpose
03 Read Holding Registers Read one or more holding registers.
06 Write Single Holding Register Write one 16-bit register.
16 Write Multiple Holding Registers Write multiple consecutive registers.

Register Size and Data Types

One Modbus register is always 16 bits, or 2 bytes. Real devices often need larger values, so they store one value across multiple registers.

Data type Register count Size Example
Boolean Bit value 1 bit Coil or discrete input
Unsigned 16-bit integer 1 16 bits 0 to 65535
Signed 16-bit integer 1 16 bits -32768 to 32767
32-bit integer 2 32 bits Counter, energy total, long value
32-bit float 2 32 bits Pressure, power, speed, temperature
64-bit value 4 64 bits Large counter or double precision value
String Multiple Variable Device name, serial number, firmware version

Byte Order and Word Order

Inside one 16-bit register, Modbus sends the high byte first. For example, the register value 0x1234 is sent as 12 34.

When a value uses two or more registers, word order can vary between manufacturers. A 32-bit value such as 0x12345678 might be stored as:

40001 = 0x1234
40002 = 0x5678

Or it might be stored with the two 16-bit words swapped:

40001 = 0x5678
40002 = 0x1234

This is why Modbus tools often include options such as word swap, byte swap, big endian, little endian, or float swap. The correct option must come from the device manual or from a known test value.

Scaling

Many Modbus devices store decimal engineering values as integers. A register may contain 2305, while the real value is 230.5 V because the scale factor is 0.1.

Raw register value = 2305
Scale factor       = 0.1
Engineering value  = 230.5 V

A good register map should define the address, function code, data type, unit, scale factor, valid range, and whether the value is read-only or writable.

Addressing Example

Suppose a manual gives the following holding register map.

Register Name Type Scale
40001 Voltage UInt16 0.1 V
40002 Current UInt16 0.01 A
40003 Frequency UInt16 0.1 Hz
40004 Power UInt32 1 W

To read voltage, current, and frequency with function code 03, the client usually sends start address 0 and quantity 3. To read power, the client sends start address 3 and quantity 2, because the UInt32 value uses registers 40004 and 40005.

Exception Responses

If a request is invalid, the server returns an exception response. Common exception codes include:

Exception code Meaning
01 Illegal Function: the device does not support that function code.
02 Illegal Data Address: the requested address does not exist.
03 Illegal Data Value: the request value is outside the allowed range.
04 Server Device Failure: the device could not complete the request.
06 Server Device Busy: the device is temporarily unable to respond normally.

Common Problems in Real Projects

  • Using address 1 when the device expects zero-based address 0.
  • Reading holding registers with function code 04 instead of 03.
  • Using the wrong byte order or word order for 32-bit integers and floats.
  • Forgetting the scale factor and displaying the raw integer value directly.
  • Writing to a read-only register.
  • Using the wrong RTU baud rate, parity, stop bits, or server address.
  • Swapping RS-485 A and B wires, or missing the 120 Ohm termination at the bus ends.
  • Using the wrong unit ID when communicating through a TCP-to-RTU gateway.
  • Reading more registers than the device allows in one request.

Conclusion

Modbus is popular because it is simple and practical. The client sends a request, the server answers, and the data is organized into coils, discrete inputs, input registers, and holding registers.

Modbus RTU is used on serial networks, typically RS-485. Modbus TCP is used over Ethernet. In both cases, successful integration depends on reading the register map carefully, choosing the correct function code, handling zero-based addressing, and applying the right data type, byte order, and scale factor.