Overview
Modbus function codes define the operations a master can perform on a slave device — reading registers, writing values, and diagnostic checks. While the specification defines over 20 function codes, only 9 are commonly used in building automation and industrial integration projects.
This guide provides the byte-level request and response structure for each function code, practical usage notes from Chipkin’s 437+ FSE project history, and guidance on choosing the right function code for each data type.
Function Code Summary
Data Access Function Codes
| FC | Name | Data Type | Operation | Max Items Per Request |
|---|---|---|---|---|
| 01 | Read Coils | Coils (bits) | Read | 2,000 |
| 02 | Read Discrete Inputs | Discrete Inputs (bits) | Read | 2,000 |
| 03 | Read Holding Registers | Holding Registers (16-bit) | Read | 125 |
| 04 | Read Input Registers | Input Registers (16-bit) | Read | 125 |
| 05 | Write Single Coil | Coil (bit) | Write | 1 |
| 06 | Write Single Register | Holding Register (16-bit) | Write | 1 |
| 15 | Write Multiple Coils | Coils (bits) | Write | 1,968 |
| 16 | Write Multiple Registers | Holding Registers (16-bit) | Write | 123 |
| 23 | Read/Write Multiple Registers | Holding Registers (16-bit) | Read + Write | Read: 125 / Write: 121 |
Choosing the Right Function Code
| You Need To… | Use FC | Notes |
|---|---|---|
| Read a binary status (on/off, open/closed) | 01 or 02 | FC01 for read/write coils, FC02 for read-only inputs |
| Read analog values (temperature, pressure) | 03 or 04 | FC03 for read/write registers, FC04 for read-only inputs |
| Write a single setpoint | 06 | Simplest write — one register |
| Write a single command (on/off) | 05 | Single coil write |
| Write multiple values atomically | 16 | All registers written in one transaction |
| Read and write in one request | 23 | Reduces round trips — not universally supported |
[!TIP] When a device’s documentation only says “registers” without specifying holding vs input, try FC03 first. If that returns exception code 01 or 02, try FC04. Most devices use holding registers (FC03) for both read/write and read-only data.
FC01: Read Coils
Reads the ON/OFF status of 1 to 2,000 coils (single-bit outputs).
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x01 | 0x01 |
| Starting Address | 2 bytes | First coil address (0-based) | 0x0000 |
| Quantity | 2 bytes | Number of coils to read | 0x000A (10) |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Responding device | 0x01 |
| Function Code | 1 byte | 0x01 | 0x01 |
| Byte Count | 1 byte | Number of data bytes following | 0x02 |
| Coil Status | N bytes | Bit-packed coil values (LSB = lowest address) | 0xCD 0x01 |
| CRC/LRC | 2/1 bytes | Error check | — |
Bit packing: Coils are packed into bytes with the lowest numbered coil in the least significant bit. For 10 coils starting at address 0:
- Byte 1:
0xCD=11001101→ Coils 7–0 (coil 0 = bit 0 = ON) - Byte 2:
0x01=00000001→ Coils 9–8 (coil 8 = bit 0 = ON, coil 9 = bit 1 = OFF)
FC02: Read Discrete Inputs
Identical structure to FC01, but reads discrete inputs — read-only bits typically connected to physical switches, sensors, or status contacts.
| Difference from FC01 | Detail |
|---|---|
| Function code | 0x02 instead of 0x01 |
| Data type | Discrete inputs (read-only) instead of coils (read/write) |
| Address space | Separate from coils — address 0 in FC02 is a different point than address 0 in FC01 |
FC03: Read Holding Registers
The most commonly used function code. Reads 1 to 125 holding registers (16-bit read/write values).
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x03 | 0x03 |
| Starting Address | 2 bytes | First register address (0-based) | 0x0000 |
| Quantity | 2 bytes | Number of registers to read (1–125) | 0x0002 (2) |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Responding device | 0x01 |
| Function Code | 1 byte | 0x03 | 0x03 |
| Byte Count | 1 byte | Number of data bytes (= quantity × 2) | 0x04 |
| Register Values | N × 2 bytes | Register data (high byte first per register) | 0x00 0x64 0x00 0xC8 |
| CRC/LRC | 2/1 bytes | Error check | — |
In this example, register 0 = 0x0064 (100 decimal) and register 1 = 0x00C8 (200 decimal).
[!NOTE] Each register is 16 bits (2 bytes) transmitted high byte first (big-endian) within that register. However, for 32-bit values spanning two registers, the register order varies by manufacturer — see Modbus Data Types & Byte Order Reference.
FC04: Read Input Registers
Identical structure to FC03, but reads input registers — read-only 16-bit values typically representing measured/monitored data (sensor readings, status values).
| Difference from FC03 | Detail |
|---|---|
| Function code | 0x04 instead of 0x03 |
| Data type | Input registers (read-only) instead of holding registers (read/write) |
| Address space | Separate from holding registers |
[!TIP] Many devices (especially building automation controllers) map all data to holding registers (FC03) and don’t use input registers (FC04) at all. If FC04 returns exception 01, try FC03 for the same address.
FC05: Write Single Coil
Forces a single coil ON or OFF.
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x05 | 0x05 |
| Coil Address | 2 bytes | Target coil (0-based) | 0x0000 |
| Value | 2 bytes | 0xFF00 = ON, 0x0000 = OFF | 0xFF00 |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
The response is an exact echo of the request (same address and value), confirming the write was accepted.
[!CAUTION] The ON value is
0xFF00, NOT0x0001. Sending0x0001is technically an illegal data value (exception 03), though some devices accept it anyway.
FC06: Write Single Register
Writes a single 16-bit value to a holding register.
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x06 | 0x06 |
| Register Address | 2 bytes | Target register (0-based) | 0x0001 |
| Value | 2 bytes | 16-bit value to write | 0x0064 (100) |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
Exact echo of the request, confirming the write was accepted.
FC06 is the simplest write operation and the most widely supported. Use it for:
- Setting individual setpoints (temperature, speed, pressure)
- Writing configuration parameters one at a time
- Sending single-value commands
FC15: Write Multiple Coils (0x0F)
Writes 1 to 1,968 coils in a single request. Coil values are bit-packed the same way as FC01 responses.
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x0F | 0x0F |
| Starting Address | 2 bytes | First coil address | 0x0000 |
| Quantity | 2 bytes | Number of coils to write | 0x000A (10) |
| Byte Count | 1 byte | Number of data bytes | 0x02 |
| Coil Values | N bytes | Bit-packed values (LSB first) | 0xCD 0x01 |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
| Field | Size | Description |
|---|---|---|
| Slave Address | 1 byte | Responding device |
| Function Code | 1 byte | 0x0F |
| Starting Address | 2 bytes | Echoed from request |
| Quantity | 2 bytes | Echoed from request |
| CRC/LRC | 2/1 bytes | Error check |
FC16: Write Multiple Registers (0x10)
Writes 1 to 123 holding registers in a single request. This is the standard function code for writing 32-bit values (which span 2 registers) and for atomic multi-register writes.
Request
| Field | Size | Description | Example |
|---|---|---|---|
| Slave Address | 1 byte | Target device | 0x01 |
| Function Code | 1 byte | 0x10 | 0x10 |
| Starting Address | 2 bytes | First register address | 0x0000 |
| Quantity | 2 bytes | Number of registers to write (1–123) | 0x0002 |
| Byte Count | 1 byte | Number of data bytes (= quantity × 2) | 0x04 |
| Register Values | N × 2 bytes | Values to write (high byte first per register) | 0x00 0x64 0x00 0xC8 |
| CRC/LRC | 2/1 bytes | Error check | — |
Response
| Field | Size | Description |
|---|---|---|
| Slave Address | 1 byte | Responding device |
| Function Code | 1 byte | 0x10 |
| Starting Address | 2 bytes | Echoed from request |
| Quantity | 2 bytes | Echoed from request |
| CRC/LRC | 2/1 bytes | Error check |
[!TIP] When writing a 32-bit floating-point value, use FC16 to write both registers atomically. Using two separate FC06 writes risks the device reading the value between writes — getting half of the old value and half of the new value.
FC23: Read/Write Multiple Registers (0x17)
Reads and writes holding registers in a single transaction. The write operation is performed before the read operation.
Request
| Field | Size | Description |
|---|---|---|
| Slave Address | 1 byte | Target device |
| 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 | Number of write data bytes |
| Write Register Values | N × 2 bytes | Values to write |
| CRC/LRC | 2/1 bytes | Error check |
Response
Same format as an FC03 response — just the read data.
[!WARNING] FC23 is not widely supported by field devices. Many controllers, meters, and sensors return exception code 01 (Illegal Function) for this function code. Always verify support before relying on FC23 in a gateway configuration. If unsupported, use separate FC03 + FC16 requests instead.
Function Code vs Data Type Matrix
| Data Type | Read FC | Write Single FC | Write Multiple FC |
|---|---|---|---|
| Coils (0xxxx) | FC01 | FC05 | FC15 |
| Discrete Inputs (1xxxx) | FC02 | — (read-only) | — (read-only) |
| Input Registers (3xxxx) | FC04 | — (read-only) | — (read-only) |
| Holding Registers (4xxxx) | FC03 | FC06 | FC16 |
[!NOTE] The Modicon address prefixes (0xxxx, 1xxxx, 3xxxx, 4xxxx) are documentation conventions — they don’t appear on the wire. The function code determines which data type is accessed. See Modbus Register Addressing: Modicon, PDU, and Page-Based Conventions for details.
Diagnostic Function Codes (Less Common)
| FC | Name | Purpose |
|---|---|---|
| 07 | Read Exception Status | Reads 8 predefined status coils — device-specific |
| 08 | Diagnostics | Loopback test, clear counters, restart — various sub-function codes |
| 11 | Get Comm Event Counter | Returns event count and busy status |
| 17 | Report Server ID | Returns device identification string |
| 43 | Read Device Identification | Returns vendor name, product code, revision — structured ID data |
These function codes are optional and rarely supported in building automation devices. They’re useful for advanced diagnostics but should not be relied upon for normal operation.
Related Articles
- Modbus Exception Codes & Error Handling Reference — what happens when a function code is rejected
- How to Read a Modbus Register Map — interpreting manufacturer documentation
- Modbus Register Addressing: Modicon, PDU, and Page-Based Conventions — address conventions
- Modbus Data Types & Byte Order Reference — interpreting 32-bit and floating-point values
- Modbus Addressing & Register Reference — quick-reference tables
Chipkin Tools
- CAS Modbus Scanner — Test any function code against a target device
- QuickServer — Protocol conversion gateway supporting all standard function codes
- QuickServer — Multi-protocol gateway with per-point function code configuration
- Chipkin Support — Function code compatibility analysis for specific device integrations