← All Posts

Modbus Register Types Explained: Coils, Discrete Inputs, Holding Registers, Input Registers

Every Modbus device exposes its data through four types of data tables. Understanding these register types (what they hold, how they are addressed, and which function codes read or write them) is the foundation of working with any Modbus device. If you get the register type wrong, your poll returns an exception code or garbage data. For broader context on how Modbus works as a protocol, see What Is Modbus? The Protocol That Runs Industrial Automation. For how documented addresses like 40001 map to wire addresses, see Modbus addressing explained.

The Four Modbus Data Tables

The Modbus data model is organized into four separate tables, each with a distinct purpose, address range, and access permission:

Type
Access
Size
Coils
Read / Write
1 bit (boolean)
Discrete Inputs
Read Only
1 bit (boolean)
Holding Registers
Read / Write
16 bits (word)
Input Registers
Read Only
16 bits (word)

Two types hold single bits (on/off, true/false). Two types hold 16-bit words (numeric values). Two are read-write. Two are read-only. The naming is consistent: "input" means read-only (data comes from the physical input of the device), and everything else is read-write.

Coils (FC01, FC05, FC15)

Coils represent boolean outputs: relays, solenoid valves, motor starters, LED indicators. A coil is either ON (1) or OFF (0). The name comes from the electromagnetic coil inside a relay.

Function Codes

  • FC01 (Read Coils): Read the current state of one or more coils. The master sends the starting address and quantity; the slave responds with bit-packed data.
  • FC05 (Write Single Coil): Set a single coil to ON (0xFF00) or OFF (0x0000).
  • FC15 (Write Multiple Coils): Set multiple coils in a single request. Useful for toggling several outputs simultaneously.

Addressing

Coil addresses start at 0 in the protocol, but documentation often uses a 1-based convention with a leading 0 prefix: coil address 0 is documented as 00001, coil address 9 is 00010. The protocol frame always uses 0-based addressing.

Practical Example

A PLC with a Modbus interface exposes its digital outputs as coils. Coil 0 controls a pump start relay, coil 1 controls a valve, coil 2 controls a fan. To start the pump, the master writes 0xFF00 (ON) to coil address 0 using FC05.

Discrete Inputs (FC02)

Discrete inputs are read-only boolean values representing the state of physical digital inputs: limit switches, proximity sensors, push buttons, circuit breaker status contacts. The slave device reports their state but cannot be changed via Modbus.

Function Code

  • FC02 (Read Discrete Inputs): Read the state of one or more discrete inputs. Same bit-packed format as FC01.

Addressing

Discrete inputs have their own address space, documented with a 1 prefix: address 0 is 10001, address 9 is 10010. Completely independent from coil addresses.

Practical Example

A circuit breaker with a Modbus module exposes its trip status as discrete input 0 (10001). Reading this input tells you if the breaker has tripped (1) or is closed (0). You cannot write to it: the breaker state changes only physically or through its own control circuit.

Holding Registers (FC03, FC06, FC16)

Holding registers are the workhorses of Modbus. Each register holds a 16-bit unsigned integer (0–65535). They store setpoints, configuration parameters, command values, and output values. This is where most of your read-write interaction happens.

Function Codes

  • FC03 (Read Holding Registers): Read one or more consecutive holding registers. The most commonly used function code.
  • FC06 (Write Single Register): Write a 16-bit value to a single holding register.
  • FC16 (Write Multiple Registers): Write values to multiple consecutive holding registers in one request.

Addressing

Documented with a 4 prefix: address 0 is 40001, address 99 is 40100. Again, the protocol uses 0-based addressing.

Data Types Beyond 16-bit

Since a single register is only 16 bits, larger data types span multiple consecutive registers:

  • 32-bit integer (INT32, UINT32): Stored in 2 consecutive registers. The byte order (big-endian vs little-endian, word swap) varies by manufacturer. Always check the device documentation.
  • 32-bit float (REAL/IEEE 754): Also 2 registers. Common for process values like temperature (72.5°C), pressure (4.2 bar), or flow rate (150.3 m³/h).
  • 64-bit float (DOUBLE): 4 consecutive registers. Used for high-precision metering values like totalized energy (kWh) in power meters.
  • Strings: Packed as 2 ASCII characters per register across multiple registers.
Byte order is the #1 source of confusion in Modbus. A 32-bit float in registers 100-101 could be stored as [High Word][Low Word] or [Low Word][High Word], and within each word as [High Byte][Low Byte] or [Low Byte][High Byte]. There are four possible combinations. Check your device manual.

Input Registers (FC04)

Input registers are the read-only counterpart to holding registers. They represent measured values from physical sensors: voltage, current, temperature, pressure, frequency. The device firmware writes sensor readings into these registers; external Modbus masters can only read them.

Function Code

  • FC04 (Read Input Registers): Read one or more consecutive input registers.

Addressing

Documented with a 3 prefix: address 0 is 30001, address 29 is 30030.

Practical Example

A flow meter might publish its measured flow rate as a 32-bit float in input registers 0 and 1 (documented as 30001). You read 2 registers starting at address 0 with FC04 and decode them as one float. Not every device follows the textbook split: the Schneider PM5560, for example, exposes its measurements as holding registers read with FC03. See the PM5560 register map guide.

Complete Function Code Reference

FC
Action
Data Type
FC01
Read
Coils
FC02
Read
Discrete Inputs
FC03
Read
Holding Registers
FC04
Read
Input Registers
FC05
Write Single
Coil
FC06
Write Single
Holding Register
FC15
Write Multiple
Coils
FC16
Write Multiple
Holding Registers

Common Pitfalls

  • Using the wrong function code for the register type. FC03 reads holding registers and FC04 reads input registers. Some devices answer FC03 and FC04 from the same table, but a strict device returns an exception or no useful data if you use the wrong one. Check the device manual for which function code each value needs.
  • 1-based vs 0-based addressing. Documentation says register 40100, but the Modbus frame requires address 99. Always subtract 1 from the documented address when building the request.
  • Register gaps. Not all addresses are valid. A device might expose registers 0-9 and 20-29, with nothing at addresses 10-19. Reading non-existent registers returns exception code 2 (illegal data address).
  • Byte order mismatch. When reading 32-bit values across two registers, the byte order must match what the device expects. If your values look wrong (e.g., temperature reads as -6.2×10³⁰), swap the word order. See Modbus floating-point byte order for the four orderings.

Test It Yourself

MacTools Modbus Poll reads all four register types (FC01 to FC04) and writes coils and holding registers (FC05, FC06, FC15, FC16). Discrete inputs and input registers are read-only in the protocol. Built-in device templates for PM5560, SMA inverters, and more auto-populate the correct registers. $19.99 one-time on Mac; there is also a Windows build, free with limits.

Get MacTools Modbus Poll

Related: Full SCADA System

Need continuous monitoring with dashboards, alarms, and trending across your Modbus TCP devices? Voltrus SCADA supports Modbus TCP, OPC-UA, Siemens S7, EtherNet/IP, DNP3, BACnet and MQTT. The core is free and open source (AGPL-3.0).

Further Reading