← All Posts

Modbus Function Codes Explained: FC01–FC06, FC15, FC16

Modbus is a request/response protocol. The master sends a request that starts with a one-byte function code, and the slave answers with either the requested data or an exception. That single byte decides what the rest of the frame means, which data table to touch, whether it is a read or a write, and how many bytes of payload follow.

This is the reference most engineers keep open while commissioning. It covers the eight function codes you will see in 99% of real deployments, the exact request/response byte layout for each, and the gotchas that bite on the first project. For the broader protocol background, start with What Is Modbus? and the register types guide.

The Four Data Tables

Before the function codes, you need the four data tables they operate on. Every Modbus slave is modelled as four tables, each with a different data width and access level:

  • Coils, 1-bit, read/write. Discrete outputs: relay states, lamp on/off. Addressed from 00001.
  • Discrete Inputs, 1-bit, read-only. Digital inputs: limit switches, push buttons. Addressed from 10001.
  • Input Registers, 16-bit, read-only. Measured values: live voltage, current, temperature. Addressed from 30001.
  • Holding Registers, 16-bit, read/write. Configuration and status: setpoints, energy totals, mode flags. Addressed from 40001.

The function code picks the table and the operation. That is all it does.

The Eight Function Codes You Will Actually Use

Code
Name
Data Table
Operation
01
Read Coils
Coils
Read N bits
02
Read Discrete Inputs
Discrete Inputs
Read N bits (RO)
03
Read Holding Registers
Holding Registers
Read N × 16-bit
04
Read Input Registers
Input Registers
Read N × 16-bit (RO)
05
Write Single Coil
Coils
Write 1 bit
06
Write Single Register
Holding Registers
Write 1 register
15
Write Multiple Coils
Coils
Write N bits
16
Write Multiple Registers
Holding Registers
Write N registers

Codes 1–4 are reads. Codes 5 and 6 write a single value. Codes 15 and 16 write many values in one atomic frame. That is the entire working set for monitoring and basic control.

FC03: Read Holding Registers (the one you will use most)

FC03 is the workhorse of industrial monitoring. You send a start address and a quantity, the slave returns that many 16-bit registers. Reading three holding registers from address 40321 (protocol address 320):

Request: 01 03 01 40 00 03 CRC
            unit fc start-hi start-lo qty-hi qty-lo
Response: 01 03 06 00 C8 01 2C 01 90 CRC
            unit fc bytecount=6  reg1 reg2 reg3

The response carries a byte-count field (here 06 = six data bytes = three registers), then the register values high-byte first. The master already knows how many to expect because it asked for them.

FC04 vs FC03

The wire format of FC04 is byte-for-byte identical to FC03. The only difference is which table the slave reads from. FC04 reads input registers (read-only measured values); FC03 reads holding registers (read/write configuration). A Schneider PM5560 power meter exposes live phase voltage as input registers (FC04) and the demand-interval setpoint as a holding register (FC03). Try FC03 on an input register and you get an exception, see the exception codes guide.

FC01 and FC02: Reading Bits

FC01 reads coils (read/write bits) and FC02 reads discrete inputs (read-only bits). The response packs the bits eight to a byte, least-significant bit first. If you read 10 coils you get two bytes; the six unused bits in the second byte are zero-padded and must be ignored.

Request: 01 01 00 13 00 0A CRC
            read 10 coils starting at coil 0020
Response: 01 01 02 03 01 CRC
            bytecount=2  0x03=00000011  0x01=00000001

FC06: Write Single Register

FC06 writes one 16-bit holding register. It is the simplest write: address plus value. The response is an exact echo of the request, which the master uses as confirmation.

Request: 01 06 01 40 00 64 CRC
            write 100 (0x0064) to register 40321
Response: 01 06 01 40 00 64 CRC (echo)

FC05: Write Single Coil

FC05 turns one coil on or off. The value is fixed: 0xFF00 means ON, 0x0000 means OFF. Any other value is illegal. Like FC06, the response echoes the request.

ON: 01 05 00 13 FF 00 CRC
OFF: 01 05 00 13 00 00 CRC

FC16: Write Multiple Registers

When you need to set several registers, FC16 beats issuing many FC06 calls. It is also atomic on most devices, the slave applies all values in one transaction, so a control block is never left half-written. The request carries a byte-count, then the data.

Request: 01 10 01 40 00 02 04 00 0A 00 14 CRC
            write 10 and 20 to registers 40321–40322
Response: 01 10 01 40 00 02 CRC (addr + qty ack)

The response does not echo the data, only the start address and quantity. If those match the request, the write succeeded.

The Lesser Function Codes

You will occasionally meet the rest. You rarely need to issue them by hand:

  • FC07 Read Exception Status, a legacy one-byte status read.
  • FC08 Diagnostics, loopback, bus health, counters. Useful for commissioning a serial link.
  • FC11 Get Comm Event Counter / FC12 Get Comm Event Log.
  • FC17 Report Slave ID, returns vendor, model, and firmware. Handy to identify a mystery device.
  • FC20/21 Read/Write File Records, block transfers to memory files.
  • FC22 Mask Write Register, set/clear individual bits without a read-modify-write round trip.
  • FC23 Read/Write Multiple Registers, atomic read-then-write in one frame. Rarely supported.
  • FC43 Encapsulated Interface Transport, vendor extensions (e.g. Schneider's ION objects).

Reading 32-bit Values: It Is Still Two FC03 Registers

Modbus has no 32-bit or floating-point type. A 32-bit value is just two adjacent 16-bit registers read with a normal FC03. The catch: every vendor orders the two words, and the four bytes, differently. The four conventions are called ABCD, DCBA, BADC, and CDAB.

The #1 first-project bug: you read a float that should be 230.0 V and it shows 0.0039 or 1.4e-39. The bytes are correct, your decoder is using the wrong swap. Cycle the four byte orders until it reads sane. See Modbus float byte order explained for the full walkthrough.

Choosing the Right Code

To read data:

  • 16-bit measured value (voltage, current, temperature) → FC04 Input Register
  • 16-bit configuration or status (setpoint, energy total) → FC03 Holding Register
  • On/off state, read/write (relay output) → FC01 Coil
  • On/off state, read-only (digital input) → FC02 Discrete Input

To write data:

  • One register → FC06
  • Two or more registers (or any atomic block) → FC16
  • One coil → FC05
  • Two or more coils → FC15

When in doubt, prefer FC16 over repeated FC06. The atomic multi-write is safer for anything a partial write could leave in a bad state, setpoint tables, scheduling blocks, calibration constants.

Quantity Limits and Batch Reads

The spec allows up to 2000 registers per request, but real devices and masters cap lower: typically 125 registers for FC03/FC04 and 1968 coils for FC01/FC02. Push past the device limit and you get an exception 03 (Illegal Data Value). Over a long RTU bus, huge frames also raise CRC risk and response timeouts. Batch reads in groups of 100–125 registers and you will never hit the limit.

TCP vs RTU: Same Codes, Different Framing

The function codes are identical over Modbus TCP and Modbus RTU. Only the transport framing differs. RTU wraps each frame with a 2-byte CRC and uses silent gaps to delimit frames; TCP prepends a 7-byte MBAP header (transaction ID, protocol ID, length, unit ID) and drops the CRC. The function-code byte and everything after it is the same. See Modbus RTU vs TCP for the transport details.

Frequently Asked Questions

What is the difference between FC03 and FC04 in Modbus?

FC03 reads holding registers (read/write storage, address range 40001–49999) and FC04 reads input registers (read-only measured values, address range 30001–39999). The wire format is identical, only the register type differs. A power meter exposes live voltage and current as input registers (FC04) and configuration thresholds as holding registers (FC03).

What is the most used Modbus function code?

FC03 (Read Holding Registers) is by far the most common function code. Most monitoring applications read configuration and status data from holding registers. FC04 (Read Input Registers) is second, used for live measured values. FC06 and FC16 handle writes; FC16 is preferred over FC06 when setting several registers at once because it is atomic on most devices.

What is the maximum number of registers in one Modbus request?

The Modbus specification allows up to 2000 registers per request, but in practice most devices and masters cap reads at 125 registers. Reading more risks buffer limits, response timeouts, or RTU CRC issues on long serial busses. Batch large reads in groups of 100–125 registers.

Why does my write succeed but the value reads back wrong?

The write almost certainly succeeded, but you are decoding the response with the wrong byte order. 32-bit floats and integers span two registers and every vendor picks a different word/byte swap (ABCD, DCBA, BADC, CDAB). Write 1000.0 and read back 0.0039? Swap the words or bytes until it decodes correctly.

Test Function Codes Without Hardware

MacTools Modbus Poll speaks every standard function code, FC01 through FC06, FC15, FC16, over TCP and RTU, with a built-in slave that responds like a real Schneider PM5560 or SMA inverter. Send the frame, see the hex response, and stop guessing at byte order.

Get Modbus Poll

Further Reading