Modbus Function Codes — Complete Reference
Every Modbus request begins with two bytes: the slave address and a function code (FC). The function code tells the slave device exactly what operation to perform — read coils, write a register, read input values, and so on. If you pick the wrong function code, you get Exception Code 01 (Illegal Function). If you pick the right one but address the wrong register type, you get garbage data or Exception Code 02.
The standard function codes are defined in the Modbus Application Protocol Specification maintained by the Modbus Organization. In practice, most devices only implement a subset of these codes. The eight function codes covered on this page handle the vast majority of real-world Modbus communication.
Function codes map directly to register types. Coils and Discrete Inputs are 1-bit values. Holding Registers and Input Registers are 16-bit values. Using the wrong function code for a register type is the single most common cause of Exception Code 01.
Function Code Summary Table
| FC | Hex | Name | Data Type | Access | Classic Address Range |
|---|---|---|---|---|---|
| 01 | 0x01 | Read Coils | 1-bit (boolean) | Read | 00001 – 09999 |
| 02 | 0x02 | Read Discrete Inputs | 1-bit (boolean) | Read-only | 10001 – 19999 |
| 03 | 0x03 | Read Holding Registers | 16-bit (word) | Read/Write | 40001 – 49999 |
| 04 | 0x04 | Read Input Registers | 16-bit (word) | Read-only | 30001 – 39999 |
| 05 | 0x05 | Write Single Coil | 1-bit (boolean) | Write | 00001 – 09999 |
| 06 | 0x06 | Write Single Register | 16-bit (word) | Write | 40001 – 49999 |
| 15 | 0x0F | Write Multiple Coils | N × 1-bit | Write | 00001 – 09999 |
| 16 | 0x10 | Write Multiple Registers | N × 16-bit | Write | 40001 – 49999 |
FC01 — Read Coils
Reads the ON/OFF status of discrete coils in the slave. Coils are single-bit outputs that your master can both read and write. Think of them as digital output flags — relay states, valve positions, motor run/stop commands.
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x01 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Byte Count | Coil Data | CRC
1 byte | 0x01 | 1 byte | N bytes | 2 bytes
Coil data is packed as bits, LSB first. If you request 10 coils, you get 2 bytes back (16 bits, with 6 unused bits padded with zeros).
Example: Read 8 coils starting at address 0 from slave 1
Request: 01 01 00 00 00 08 3D CC
Response: 01 01 01 6B 11 C8
Breakdown of response data byte 0x6B = 0110 1011:
Coil 0 = ON (bit 0)
Coil 1 = ON (bit 1)
Coil 2 = OFF (bit 2)
Coil 3 = ON (bit 3)
Coil 4 = OFF (bit 4)
Coil 5 = ON (bit 5)
Coil 6 = ON (bit 6)
Coil 7 = OFF (bit 7)
Common use cases: Reading the status of relays on a PLC output module, checking whether a digital output is energised, polling a bank of indicator lamp states.
FC02 — Read Discrete Inputs
Reads the ON/OFF status of discrete input contacts. These are read-only single-bit values representing physical digital inputs — limit switches, pushbuttons, proximity sensors, door contacts. The frame format is identical to FC01, but the register space is different.
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x02 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Byte Count | Input Data | CRC
1 byte | 0x02 | 1 byte | N bytes | 2 bytes
Example: Read 4 discrete inputs starting at address 0 from slave 2
Request: 02 02 00 00 00 04 79 FA
Response: 02 02 01 05 A1 88
Data byte 0x05 = 0000 0101:
Input 0 = ON (bit 0) - e.g. limit switch closed
Input 1 = OFF (bit 1)
Input 2 = ON (bit 2) - e.g. pushbutton pressed
Input 3 = OFF (bit 3)
Common use cases: Monitoring limit switches, reading door contacts or safety interlocks, checking pushbutton states, polling sensor digital outputs.
FC03 — Read Holding Registers
This is the most widely used function code in Modbus. It reads one or more 16-bit holding registers. Holding registers are the workhorse of Modbus — they store configuration parameters, setpoints, measurement values, and anything else the device exposes. They are readable and writable (via FC06 or FC16).
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x03 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Byte Count | Register Data | CRC
1 byte | 0x03 | 1 byte | N x 2 bytes | 2 bytes
Each register is 2 bytes, big-endian (high byte first). If you request 3 registers, you get 6 data bytes back.
Example: Read 2 holding registers starting at address 0 from slave 1
Request: 01 03 00 00 00 02 C4 0B
Response: 01 03 04 01 F4 00 64 FA 6D
Register 0 = 0x01F4 = 500 (e.g. voltage: 500 = 50.0V with scale factor /10)
Register 1 = 0x0064 = 100 (e.g. current: 100 = 10.0A with scale factor /10)
Common use cases: Reading energy meter values (voltage, current, power, kWh), reading PLC holding registers, fetching device configuration parameters, reading setpoints.
When a device manual says "read register 40001," that usually means FC03 at address 0. When it says "read register 30001," that means FC04 at address 0. If FC03 returns Exception 01, try FC04 — the device may have implemented measurements as input registers rather than holding registers.
FC04 — Read Input Registers
Reads one or more 16-bit input registers. Input registers are read-only — they typically represent live measurements or status values that the device updates internally. The frame format is identical to FC03, just with function code 0x04.
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x04 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Byte Count | Register Data | CRC
1 byte | 0x04 | 1 byte | N x 2 bytes | 2 bytes
Example: Read 1 input register at address 5 from slave 3
Request: 03 04 00 05 00 01 21 E8
Response: 03 04 02 01 A4 99 CA
Register 5 = 0x01A4 = 420 (e.g. temperature: 420 = 42.0 degrees C)
Common use cases: Reading temperature sensor values, live measurements from energy meters (some meters use FC04 for measurements and FC03 for configuration), ADC conversion results from I/O modules.
FC05 — Write Single Coil
Forces a single coil to ON or OFF. The value field must be either 0xFF00 for ON or 0x0000 for OFF. Any other value triggers Exception Code 03 (Illegal Data Value). The response is an exact echo of the request.
Request Frame (RTU)
Slave Addr | FC | Coil Address | Value | CRC
1 byte | 0x05 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Coil Address | Value | CRC
1 byte | 0x05 | 2 bytes | 2 bytes | 2 bytes
(exact echo of request)
Example: Turn ON coil 0 on slave 1
Request: 01 05 00 00 FF 00 8C 3A
Response: 01 05 00 00 FF 00 8C 3A (echo)
Example: Turn OFF coil 0 on slave 1
Request: 01 05 00 00 00 00 CD CA
Response: 01 05 00 00 00 00 CD CA (echo)
Common use cases: Toggling a relay on a remote I/O module, activating a solenoid valve, triggering a reset command, starting or stopping a motor via PLC digital output.
FC05 only accepts 0xFF00 (ON) and 0x0000 (OFF). Sending 0x0001 or 0x00FF will cause Exception Code 03 on a compliant device. Some non-compliant devices accept 0x0001 as ON, but don't rely on this.
FC06 — Write Single Register
Writes a single 16-bit value to a holding register. Used for setting configuration parameters, changing setpoints, or writing a control value. Like FC05, the response is an exact echo of the request.
Request Frame (RTU)
Slave Addr | FC | Register Addr | Value | CRC
1 byte | 0x06 | 2 bytes (Hi-Lo)| 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Register Addr | Value | CRC
1 byte | 0x06 | 2 bytes | 2 bytes | 2 bytes
(exact echo of request)
Example: Write value 9600 (0x2580) to register 3 on slave 1
Request: 01 06 00 03 25 80 78 36
Response: 01 06 00 03 25 80 78 36 (echo)
This might set the device's baud rate register to 9600.
Common use cases: Setting a device's slave address, configuring baud rate, writing a temperature setpoint, adjusting PID parameters, setting alarm thresholds on energy meters.
FC15 (0x0F) — Write Multiple Coils
Forces multiple consecutive coils in a single request. The coil values are bit-packed, LSB first, the same format as FC01 response data. Useful when you need to set a bank of digital outputs atomically rather than one at a time with FC05.
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | Byte Count | Coil Data | CRC
1 byte | 0x0F | 2 bytes | 2 bytes | 1 byte | N bytes | 2 bytes
Response Frame
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x0F | 2 bytes | 2 bytes | 2 bytes
Example: Write coils 0-7 on slave 1 (turn ON coils 0, 1, 3, 5)
Request: 01 0F 00 00 00 08 01 2B BE A6
Response: 01 0F 00 00 00 08 54 0D
Data byte 0x2B = 0010 1011:
Coil 0 = ON (bit 0)
Coil 1 = ON (bit 1)
Coil 2 = OFF (bit 2)
Coil 3 = ON (bit 3)
Coil 4 = OFF (bit 4)
Coil 5 = ON (bit 5)
Coil 6 = OFF (bit 6)
Coil 7 = OFF (bit 7)
Common use cases: Setting an entire bank of relay outputs at once, initialising all digital outputs on a PLC module during startup, batch-writing output states for coordinated control.
FC16 (0x10) — Write Multiple Registers
Writes to multiple consecutive holding registers in a single request. This is essential for writing 32-bit float values (which span two consecutive 16-bit registers) and for batch configuration updates. Most devices support writing up to 123 registers per request (the maximum that fits in a single Modbus RTU frame).
Request Frame (RTU)
Slave Addr | FC | Start Address | Quantity | Byte Count | Register Data | CRC
1 byte | 0x10 | 2 bytes | 2 bytes | 1 byte | N x 2 bytes | 2 bytes
Response Frame
Slave Addr | FC | Start Address | Quantity | CRC
1 byte | 0x10 | 2 bytes | 2 bytes | 2 bytes
Example: Write two registers (a 32-bit float) starting at address 10 on slave 1
Request: 01 10 00 0A 00 02 04 42 C8 00 00 2B 05
Response: 01 10 00 0A 00 02 01 C9
Register 10 = 0x42C8, Register 11 = 0x0000
As a 32-bit float (Big Endian): 100.0
Common use cases: Writing 32-bit float setpoints (temperature, pressure), batch-writing PLC configuration, writing multi-register values like date/time, updating firmware data blocks.
No Manual Hex Calculation Needed
ModBus Pro supports all standard function codes out of the box. Select your register type and the correct FC is automatically chosen — no manual hex calculation needed. Raw frame view shows every byte for debugging.
Download FreeException Codes Quick Reference
When a slave cannot fulfil a request, it returns an exception response. The function code in the response has bit 7 set (e.g., FC03 becomes 0x83), followed by an exception code byte.
| Code | Hex | Name | Common Trigger |
|---|---|---|---|
| 01 | 0x01 | Illegal Function | Device does not support this function code (e.g., using FC03 when only FC04 is implemented) |
| 02 | 0x02 | Illegal Data Address | Register address does not exist on this device, or address + quantity exceeds valid range |
| 03 | 0x03 | Illegal Data Value | Value in write request is out of range (e.g., FC05 with a value other than 0x0000 or 0xFF00) |
| 04 | 0x04 | Slave Device Failure | Unrecoverable internal error on the slave while processing the request |
| 05 | 0x05 | Acknowledge | Slave accepted request but needs time to process (e.g., firmware update, EEPROM write) |
| 06 | 0x06 | Slave Device Busy | Slave is currently processing another long-duration command; retry after a delay |
For detailed troubleshooting of each exception code, see the dedicated guides: Exception 01 (Illegal Function) and Exception 02 (Illegal Data Address).
Register Type Mapping & The 40001 Offset
The classic Modbus addressing convention uses a numeric prefix to indicate the register type:
| Prefix | Range | Register Type | Function Codes |
|---|---|---|---|
| 0xxxx | 00001 – 09999 | Coils (1-bit, R/W) | FC01 (read), FC05 (write one), FC15 (write many) |
| 1xxxx | 10001 – 19999 | Discrete Inputs (1-bit, read-only) | FC02 (read) |
| 3xxxx | 30001 – 39999 | Input Registers (16-bit, read-only) | FC04 (read) |
| 4xxxx | 40001 – 49999 | Holding Registers (16-bit, R/W) | FC03 (read), FC06 (write one), FC16 (write many) |
The 40001 Confusion
A device manual says "read register 40001." What Modbus address do you actually send on the wire? The answer is address 0 using function code 03. The "4" prefix just tells you this is a holding register, and the numbering starts at 1 (not 0). So:
- 40001 in documentation = FC03, address
0x0000on the wire - 40100 in documentation = FC03, address
0x0063(99 decimal) on the wire - 30001 in documentation = FC04, address
0x0000on the wire
Not all Modbus tools handle this offset the same way. Some expect you to enter the wire address (0-based), while others expect the documentation address (40001-style). If you enter 40001 in a tool that expects wire addresses, you'll be reading address 40001 (0x9C41) — almost certainly an invalid register. Check your tool's documentation to know which convention it uses.
For a deep dive into addressing, see the Register Offset guide.