Every Modbus message contains a function code. It is a single byte that tells the slave device what operation to perform — read coils, write registers, or diagnose communication.
The Modbus specification defines multiple function codes, but in practice you will use about six of them for 95% of your tasks. The rest are either rarely implemented, vendor-specific, or reserved for legacy use.
This guide explains each commonly used function code with real hex frame examples. For every function code, you will see the request, the response, and what each byte means — so you can read raw Modbus frames in Wireshark, a serial analyzer, or any debugging tool.
Table of Contents
1. How Function Codes Work
Every Modbus RTU transaction follows the same frame structure:
- The master sends a request:
[Slave ID] [Function Code] [Data] [CRC-16] - The slave processes the request.
- The slave sends a response:
[Slave ID] [Function Code] [Data] [CRC-16]
If the slave cannot process the request, it sends an exception response with the function code + 0x80 and an exception code explaining the error.
The Slave ID (1 byte) identifies which device on the bus should respond. The function code (1 byte) tells the slave what to do. The CRC-16 (2 bytes) provides error checking. All examples in this guide show the complete Modbus RTU frame including Slave ID and CRC.
2. Modbus Register Types and Matching Function Codes
Each function code operates on a specific register type. Using the wrong function code for a register type is one of the most common Modbus errors.
| Register Type | Legacy Address Range | Access | Read FC | Write FC |
|---|---|---|---|---|
| Coils (digital outputs) | 0xxxx | Read/Write | FC 01 | FC 05 (single) / FC 15 (multiple) |
| Discrete Inputs (digital inputs) | 1xxxx | Read-Only | FC 02 | — |
| Input Registers (analog inputs) | 3xxxx | Read-Only | FC 04 | — |
| Holding Registers (analog outputs/config) | 4xxxx | Read/Write | FC 03 | FC 06 (single) / FC 16 (multiple) |
💡 Key rule: You cannot write to Discrete Inputs (1xxxx) or Input Registers (3xxxx). They are read-only. Attempting to write will return exception code 01 (Illegal Function).
3. FC 01 — Read Coils (0x01)
Purpose: Read the ON/OFF status of 1 to 2,000 coils (digital outputs).
Use case: Read the state of relay outputs, motor contactors, or valve solenoids.
Request Frame (Modbus RTU)
Read 19 coils starting at coil 20, from slave device 1:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 01 | Target slave address 1 |
| Function Code | 1 byte | 01 | Read Coils |
| Starting Address | 2 bytes | 00 13 | Start at coil 20 (address 19 = 0x13) |
| Quantity | 2 bytes | 00 13 | Read 19 coils (20 through 38) |
| CRC | 2 bytes | 8C 02 | CRC-16 error check |
Raw frame: 01 01 00 13 00 13 8C 02
Response Frame (Modbus RTU)
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 01 | Response from slave 1 |
| Function Code | 1 byte | 01 | Read Coils |
| Byte Count | 1 byte | 03 | 3 bytes of coil data follow |
| Coil Status | 3 bytes | CD 6B 05 | Bit-packed status of coils 20–38 |
| CRC | 2 bytes | 42 82 | CRC-16 error check |
Raw frame: 01 01 03 CD 6B 05 42 82
Reading the Response
The coil status bytes are bit-packed, LSB first:
- 0xCD = 1100 1101 → coils 27–20: ON ON OFF OFF ON ON OFF ON
- 0x6B = 0110 1011 → coils 35–28: OFF ON ON OFF ON OFF ON ON
- 0x05 = 0000 0101 → coils 38–36: OFF OFF OFF OFF OFF ON OFF ON (upper 5 bits are padding)
4. FC 02 — Read Discrete Inputs (0x02)
Purpose: Read the ON/OFF status of 1 to 2,000 discrete inputs (digital inputs).
Use case: Read limit switches, door contacts, level sensors, or alarm inputs.
Works exactly like FC 01, but reads from the discrete input table (1xxxx) instead of coils (0xxxx).
Example
Read 22 discrete inputs starting at input 197, from slave device 3:
Request: 03 02 00 C4 00 16 B9 DB
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 03 | Slave address 3 |
| FC | 02 | Read Discrete Inputs |
| Start Address | 00 C4 | Input 197 (address 196 = 0xC4) |
| Quantity | 00 16 | Read 22 inputs |
Response: 03 02 03 AC DB 35 23 6A
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 03 | From slave 3 |
| FC | 02 | Read Discrete Inputs |
| Byte Count | 03 | 3 bytes follow |
| Input Status | AC DB 35 | Bit-packed status of inputs 197–218 |
5. FC 03 — Read Holding Registers (0x03)
Purpose: Read 1 to 125 contiguous holding registers (16-bit words).
Use case: Read setpoints, configuration parameters, energy values, PID outputs — this is the most frequently used function code in Modbus.
Request Frame (Modbus RTU)
Read 3 holding registers starting at register 108, from slave device 6:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 06 | Target slave address 6 |
| Function Code | 1 byte | 03 | Read Holding Registers |
| Starting Address | 2 bytes | 00 6B | Register 108 (address 107 = 0x6B) |
| Quantity | 2 bytes | 00 03 | Read 3 registers (108, 109, 110) |
| CRC | 2 bytes | 75 A0 | CRC-16 error check |
Raw frame: 06 03 00 6B 00 03 75 A0
Response Frame (Modbus RTU)
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 06 | Response from slave 6 |
| Function Code | 1 byte | 03 | Read Holding Registers |
| Byte Count | 1 byte | 06 | 6 bytes follow (3 registers × 2 bytes) |
| Register 108 | 2 bytes | 02 2B | Value = 555 decimal |
| Register 109 | 2 bytes | 00 00 | Value = 0 |
| Register 110 | 2 bytes | 00 64 | Value = 100 decimal |
| CRC | 2 bytes | 23 4A | CRC-16 error check |
Raw frame: 06 03 06 02 2B 00 00 00 64 23 4A
Practical Notes
- Maximum 125 registers per request (250 bytes of data).
- Data is big-endian — high byte first, then low byte.
- For 32-bit values (floats, long integers), two consecutive registers are used. Check the device manual for byte order — some devices use AB CD (big-endian), others use CD AB (word-swapped).
6. FC 04 — Read Input Registers (0x04)
Purpose: Read 1 to 125 contiguous input registers (16-bit words, read-only).
Use case: Read live measurements — voltage, current, temperature, pressure, flow rate.
Works identically to FC 03 in frame structure. The only difference is the register table it reads from (3xxxx instead of 4xxxx).
Example
Read 1 input register (register 9) from slave device 2:
Request: 02 04 00 08 00 01 B0 3B
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 02 | Slave address 2 |
| FC | 04 | Read Input Registers |
| Start Address | 00 08 | Register 9 (address 8 = 0x08) |
| Quantity | 00 01 | Read 1 register |
Response: 02 04 02 00 0A 7D 37
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 02 | From slave 2 |
| FC | 04 | Read Input Registers |
| Byte Count | 02 | 2 bytes follow |
| Register 9 | 00 0A | Value = 10 decimal |
⚠️ Common mistake: Using FC 03 to read input registers or FC 04 to read holding registers. Some devices map everything to holding registers and respond to both. Others are strict and will return exception code 02 (Illegal Data Address). Always check the device documentation.
7. FC 05 — Write Single Coil (0x05)
Purpose: Turn a single coil ON or OFF.
Use case: Control a relay, start/stop a motor, open/close a valve.
Important
The value to write is not 0 or 1. It is:
- 0xFF00 = ON
- 0x0000 = OFF
- All other values are illegal and will be rejected.
Request Frame (Modbus RTU)
Turn ON coil 173 on slave device 5:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 05 | Target slave address 5 |
| Function Code | 1 byte | 05 | Write Single Coil |
| Coil Address | 2 bytes | 00 AC | Coil 173 (address 172 = 0xAC) |
| Value | 2 bytes | FF 00 | Turn ON |
| CRC | 2 bytes | 4D 9F | CRC-16 error check |
Raw frame: 05 05 00 AC FF 00 4D 9F
Response Frame (Modbus RTU)
The response is an exact echo of the request — confirming the write was successful.
Raw frame: 05 05 00 AC FF 00 4D 9F
8. FC 06 — Write Single Register (0x06)
Purpose: Write a single 16-bit value to one holding register.
Use case: Change a setpoint, write a configuration parameter, set an analog output value.
Request Frame (Modbus RTU)
Write value 3 to holding register 2 on slave device 1:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 01 | Target slave address 1 |
| Function Code | 1 byte | 06 | Write Single Register |
| Register Address | 2 bytes | 00 01 | Register 2 (address 1 = 0x01) |
| Value | 2 bytes | 00 03 | Write value 3 |
| CRC | 2 bytes | 98 0B | CRC-16 error check |
Raw frame: 01 06 00 01 00 03 98 0B
Response Frame (Modbus RTU)
The response is an exact echo of the request.
Raw frame: 01 06 00 01 00 03 98 0B
9. FC 15 — Write Multiple Coils (0x0F)
Purpose: Write the state of multiple consecutive coils in a single message.
Use case: Set a group of relay outputs at once, configure a bank of digital outputs.
Request Frame (Modbus RTU)
Write 10 coils starting at coil 20 on slave device 1:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 01 | Target slave address 1 |
| Function Code | 1 byte | 0F | Write Multiple Coils |
| Starting Address | 2 bytes | 00 13 | Start at coil 20 |
| Quantity | 2 bytes | 00 0A | Write 10 coils |
| Byte Count | 1 byte | 02 | 2 bytes of coil data follow |
| Coil Values | 2 bytes | CD 01 | Bit-packed ON/OFF values |
| CRC | 2 bytes | 72 CB | CRC-16 error check |
Raw frame: 01 0F 00 13 00 0A 02 CD 01 72 CB
Response Frame (Modbus RTU)
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 01 | From slave 1 |
| FC | 0F | Write Multiple Coils |
| Start Address | 00 13 | Starting at coil 20 |
| Quantity | 00 0A | 10 coils written |
Raw frame: 01 0F 00 13 00 0A 24 09
Note: Some devices only support FC 15 and do not support FC 05. In that case, configure your master to use FC 15 even for single coil writes.
10. FC 16 — Write Multiple Registers (0x10)
Purpose: Write values to multiple consecutive holding registers in a single message.
Use case: Write a batch of setpoints, download configuration, write 32-bit values that span two registers.
Request Frame (Modbus RTU)
Write 2 holding registers starting at register 2 on slave device 1:
| Field | Size | Example | Meaning |
|---|---|---|---|
| Slave ID | 1 byte | 01 | Target slave address 1 |
| Function Code | 1 byte | 10 | Write Multiple Registers |
| Starting Address | 2 bytes | 00 01 | Start at register 2 |
| Quantity | 2 bytes | 00 02 | Write 2 registers |
| Byte Count | 1 byte | 04 | 4 bytes of data follow |
| Register 2 | 2 bytes | 00 0A | Value = 10 |
| Register 3 | 2 bytes | 01 02 | Value = 258 |
| CRC | 2 bytes | 92 30 | CRC-16 error check |
Raw frame: 01 10 00 01 00 02 04 00 0A 01 02 92 30
Response Frame (Modbus RTU)
| Field | Example | Meaning |
|---|---|---|
| Slave ID | 01 | From slave 1 |
| FC | 10 | Write Multiple Registers |
| Start Address | 00 01 | Starting at register 2 |
| Quantity | 00 02 | 2 registers written |
Raw frame: 01 10 00 01 00 02 10 08
Maximum: 123 registers per request (246 bytes of data).
11. FC 23 — Read/Write Multiple Registers (0x17)
Purpose: Perform a read and a write in a single transaction. The write is executed before the read.
Use case: Atomic read-modify-write operations. Less common but useful when you need to guarantee that a read and write happen together without another master interfering.
Request Frame
| Field | Size | Meaning |
|---|---|---|
| Function Code | 1 byte | 0x17 |
| Read Starting Address | 2 bytes | First register to read |
| Read Quantity | 2 bytes | Number of registers to read (1–125) |
| Write Starting Address | 2 bytes | First register to write |
| Write Quantity | 2 bytes | Number of registers to write (1–121) |
| Write Byte Count | 1 byte | 2 × Write Quantity |
| Write Data | N bytes | Values to write |
Response Frame
The response contains only the read data — the write is confirmed by the absence of an exception.
12. FC 43/14 — Read Device Identification (0x2B/0x0E)
Purpose: Read vendor name, product code, firmware version, and other identification data from a device.
Use case: Device discovery, inventory management, verifying firmware versions during commissioning.
How It Works
This function code uses an MEI (Modbus Encapsulated Interface) with sub-function code 14 (0x0E). The request specifies which identification objects to read:
| Object ID | Description |
|---|---|
| 0x00 | Vendor Name |
| 0x01 | Product Code |
| 0x02 | Major/Minor Revision |
| 0x03 | Vendor URL (optional) |
| 0x04 | Product Name (optional) |
| 0x05 | Model Name (optional) |
| 0x06 | User Application Name (optional) |
Not all devices support this function code. Many legacy devices only support FC 01–06 and FC 15–16.
13. Exception Responses — When Things Go Wrong
When a slave cannot process a request, it returns an exception response. The function code in the response is the original function code + 0x80.
For example, if FC 03 (0x03) fails, the response contains FC 0x83.
Exception Codes
| Code | Name | Meaning | Common Cause |
|---|---|---|---|
| 01 | Illegal Function | The slave does not support this function code | Using FC 04 on a device that only supports FC 03 |
| 02 | Illegal Data Address | The requested register address does not exist | Wrong address, wrong offset, or register not mapped |
| 03 | Illegal Data Value | The value in the request is out of range | Writing 0x0001 to a coil (must be 0xFF00 or 0x0000) |
| 04 | Slave Device Failure | Internal error in the slave device | Hardware fault, memory error, or processing failure |
| 05 | Acknowledge | The slave has accepted the request but needs time to process | Long operations like firmware updates |
| 06 | Slave Device Busy | The slave is processing another request | Try again later |
Example
Master requests register at address 0x0500 from slave 1 (which does not exist on this device):
Request: 01 03 05 00 00 01 84 C6
| Field | Value | Meaning |
|---|---|---|
| Slave ID | 01 | Slave address 1 |
| FC | 03 | Read Holding Registers |
| Address | 05 00 | Register address 1281 |
| Quantity | 00 01 | Read 1 register |
Exception Response: 01 83 02 C0 F1
| Field | Value | Meaning |
|---|---|---|
| Slave ID | 01 | From slave 1 |
| FC | 83 | FC 03 + 0x80 = exception |
| Exception Code | 02 | Illegal Data Address — register does not exist |
14. Function Code Quick Reference Table
| FC | Hex | Name | Registers | Direction | Max Per Request |
|---|---|---|---|---|---|
| 01 | 0x01 | Read Coils | 0xxxx | Read | 2,000 coils |
| 02 | 0x02 | Read Discrete Inputs | 1xxxx | Read | 2,000 inputs |
| 03 | 0x03 | Read Holding Registers | 4xxxx | Read | 125 registers |
| 04 | 0x04 | Read Input Registers | 3xxxx | Read | 125 registers |
| 05 | 0x05 | Write Single Coil | 0xxxx | Write | 1 coil |
| 06 | 0x06 | Write Single Register | 4xxxx | Write | 1 register |
| 15 | 0x0F | Write Multiple Coils | 0xxxx | Write | 1,968 coils |
| 16 | 0x10 | Write Multiple Registers | 4xxxx | Write | 123 registers |
| 23 | 0x17 | Read/Write Multiple Registers | 4xxxx | Read + Write | Read 125 / Write 121 |
| 43 | 0x2B | Read Device Identification | — | Read | — |
15. Common Mistakes with Function Codes
| Mistake | What Happens | Fix |
|---|---|---|
| Using FC 03 to read input registers (3xxxx) | Some devices respond, others return exception 02 | Use FC 04 for input registers, FC 03 for holding registers |
| Writing 0x0001 to a coil instead of 0xFF00 | Exception 03 (Illegal Data Value) | Use 0xFF00 for ON, 0x0000 for OFF |
| Requesting more than 125 registers in FC 03 | Exception 03 or no response | Split into multiple requests, max 125 registers each |
| Forgetting the zero-based offset | Reading the wrong register (off by one) | Register 40001 = address 0x0000 in the PDU |
| Assuming all devices support FC 05 | Some devices only support FC 15 for coil writes | Check device documentation. Use FC 15 as fallback. |
| Not checking exception responses | Master retries endlessly on an invalid request | Parse the response. If FC ≥ 0x80, it is an exception — read the exception code. |
| Wrong byte order for 32-bit values | Float values read as garbage | Check device manual: big-endian (AB CD) vs word-swapped (CD AB) vs little-endian (DC BA) |
Summary
Modbus function codes are simple by design. A one-byte code tells the slave exactly what to do. The six most important codes for daily work are:
- FC 03 — Read Holding Registers (your most-used code)
- FC 04 — Read Input Registers (live measurements)
- FC 06 — Write Single Register (change a setpoint)
- FC 16 — Write Multiple Registers (batch writes, 32-bit values)
- FC 01 — Read Coils (digital output status)
- FC 05 — Write Single Coil (control a relay)
Learn these six, understand the zero-based addressing offset, and know how to read exception responses — and you can debug any Modbus system.
Decode Any Modbus Frame Instantly
Want to verify your Modbus frames or decode raw bytes from Wireshark? Use our free Online Modbus Frame Decoder & Encoder Tool — paste any Modbus RTU or TCP frame and get a full byte-by-byte breakdown with CRC validation, register values, and function code explanation.
