PyModbus
PyModbusDocs

Modbus Function Codes Reference

Complete guide to Modbus function codes - what they do, when to use them, PyModbus implementation.

Modbus Function Codes

Function codes tell the slave what action to perform. Here's every standard function code and how to use it in PyModbus.

Reading Functions

0x01 - Read Coils

Read discrete outputs (ON/OFF states).

# Read 8 coils starting at address 0
result = client.read_coils(address=0, count=8, slave=1)

if not result.isError():
    # result.bits is a list of booleans
    for i, bit in enumerate(result.bits[:8]):
        print(f"Coil {i}: {'ON' if bit else 'OFF'}")

0x02 - Read Discrete Inputs

Read discrete inputs (switches, sensors).

# Read 16 discrete inputs
result = client.read_discrete_inputs(address=0, count=16, slave=1)

if not result.isError():
    # Check if specific input is ON
    if result.bits[5]:
        print("Input 5 is ON")

0x03 - Read Holding Registers

Read read/write registers (configuration, setpoints).

# Read 10 holding registers
result = client.read_holding_registers(address=100, count=10, slave=1)

if not result.isError():
    for i, value in enumerate(result.registers):
        print(f"Register {100+i}: {value}")

0x04 - Read Input Registers

Read read-only registers (measurements, status).

# Read analog input values
result = client.read_input_registers(address=0, count=4, slave=1)

if not result.isError():
    # Often scaled values
    voltage = result.registers[0] / 10.0  # 0.1V resolution
    current = result.registers[1] / 100.0  # 0.01A resolution
    print(f"Voltage: {voltage}V, Current: {current}A")

Writing Functions

0x05 - Write Single Coil

Write single discrete output.

# Turn relay ON
client.write_coil(address=0, value=True, slave=1)

# Turn relay OFF
client.write_coil(address=0, value=False, slave=1)

0x06 - Write Single Register

Write single holding register.

# Set temperature setpoint to 25.0°C (scaled by 10)
client.write_register(address=1000, value=250, slave=1)

0x0F - Write Multiple Coils

Write multiple discrete outputs.

# Control 8 relays at once
relay_states = [True, True, False, True, False, False, True, False]
client.write_coils(address=0, values=relay_states, slave=1)

0x10 - Write Multiple Registers

Write multiple holding registers.

# Write configuration block
config_values = [
    100,  # Parameter 1
    200,  # Parameter 2
    300,  # Parameter 3
    400   # Parameter 4
]
client.write_registers(address=1000, values=config_values, slave=1)

Advanced Functions

0x07 - Read Exception Status

Read 8 exception status bits.

result = client.read_exception_status(slave=1)
if not result.isError():
    status = result.exception_status
    print(f"Exception status: {bin(status)}")

0x08 - Diagnostics

Various diagnostic subfunctions.

# Subfunction 0x00 - Return Query Data (echo test)
from pymodbus.diag_message import DiagnosticStatusRequest
request = DiagnosticStatusRequest(sub_function_code=0x00, data=0x1234)
result = client.execute(request)

0x0B - Get Comm Event Counter

Get communication event counter.

result = client.get_comm_event_counter(slave=1)
if not result.isError():
    print(f"Status: {result.status}")
    print(f"Event count: {result.count}")

0x0C - Get Comm Event Log

Get communication event log.

result = client.get_comm_event_log(slave=1)
if not result.isError():
    print(f"Status: {result.status}")
    print(f"Event count: {result.event_count}")
    print(f"Message count: {result.message_count}")
    print(f"Events: {result.events}")

0x11 - Report Server ID

Get server identification.

result = client.report_server_id(slave=1)
if not result.isError():
    print(f"Server ID: {result.identifier}")
    print(f"Status: {'Running' if result.status else 'Stopped'}")

0x14 - Read File Record

Read from file records.

from pymodbus.file_message import ReadFileRecordRequest

# Read 10 bytes from file 4, starting at record 1
request = ReadFileRecordRequest(
    records=[
        {'file_number': 4, 'record_number': 1, 'record_length': 10}
    ]
)
result = client.execute(request)

0x15 - Write File Record

Write to file records.

from pymodbus.file_message import WriteFileRecordRequest

# Write data to file 4, starting at record 1
request = WriteFileRecordRequest(
    records=[
        {'file_number': 4, 'record_number': 1, 'record_data': b'Hello'}
    ]
)
result = client.execute(request)

0x16 - Mask Write Register

Modify single register using AND/OR masks.

# Set bit 2, clear bit 5 in register 100
# AND mask: 0xFFDF (clear bit 5)
# OR mask:  0x0004 (set bit 2)
client.mask_write_register(
    address=100,
    and_mask=0xFFDF,
    or_mask=0x0004,
    slave=1
)

0x17 - Read/Write Multiple Registers

Read and write in single transaction.

# Read 3 registers from address 100
# Write 2 values to address 200
result = client.readwrite_registers(
    read_address=100,
    read_count=3,
    write_address=200,
    write_registers=[123, 456],
    slave=1
)

if not result.isError():
    print(f"Read values: {result.registers}")

0x18 - Read FIFO Queue

Read FIFO queue contents.

result = client.read_fifo_queue(address=100, slave=1)
if not result.isError():
    print(f"FIFO values: {result.fifo}")

0x2B - Read Device Identification

Get device identification (Modbus Plus).

from pymodbus.mei_message import ReadDeviceInformationRequest

request = ReadDeviceInformationRequest(
    read_code=0x01,  # Basic device identification
    object_id=0x00   # VendorName
)
result = client.execute(request)

if not result.isError():
    for object_id, value in result.information.items():
        print(f"Object {object_id}: {value}")

Custom Function Codes

from pymodbus.pdu import ModbusRequest, ModbusResponse

class CustomRequest(ModbusRequest):
    function_code = 0x55  # Custom function code
    
    def __init__(self, data):
        super().__init__()
        self.data = data
    
    def encode(self):
        return struct.pack('>H', self.data)
    
    def decode(self, data):
        self.data = struct.unpack('>H', data)[0]

# Use custom function
request = CustomRequest(data=0x1234)
result = client.execute(request)

Function Code Support by Device Type

Device TypeCommon Function Codes
PLC01, 02, 03, 04, 05, 06, 0F, 10
Energy Meter03, 04
Temperature Controller03, 04, 06, 10
VFD (Motor Drive)03, 06, 10
I/O Module01, 02, 05, 0F
HMI/SCADAAll standard codes

Error Responses

When a function fails, you get an exception response:

result = client.read_holding_registers(9999, 10)

if result.isError():
    # Function code + 0x80 = error response
    # e.g., 0x03 becomes 0x83 for error
    print(f"Exception code: {result.exception_code}")
    
    # Common exception codes:
    # 1 = Illegal Function
    # 2 = Illegal Data Address  
    # 3 = Illegal Data Value
    # 4 = Slave Device Failure

Performance Tips

Batch Operations

# SLOW - Multiple requests
for i in range(10):
    client.read_holding_registers(i, 1)

# FAST - Single request
client.read_holding_registers(0, 10)

Use Read/Write Multiple

# SLOW - Separate read and write
read_result = client.read_holding_registers(100, 5)
write_result = client.write_registers(200, [1, 2, 3])

# FAST - Combined operation
result = client.readwrite_registers(
    read_address=100, read_count=5,
    write_address=200, write_registers=[1, 2, 3]
)

Most devices only support basic function codes (01-06, 0F, 10). Check your device manual.

Testing Function Support

def test_function_support(client, slave=1):
    """Test which functions a device supports."""
    tests = [
        ("Read Coils (01)", lambda: client.read_coils(0, 1, slave)),
        ("Read Discrete Inputs (02)", lambda: client.read_discrete_inputs(0, 1, slave)),
        ("Read Holding Registers (03)", lambda: client.read_holding_registers(0, 1, slave)),
        ("Read Input Registers (04)", lambda: client.read_input_registers(0, 1, slave)),
        ("Write Single Coil (05)", lambda: client.write_coil(0, True, slave)),
        ("Write Single Register (06)", lambda: client.write_register(0, 0, slave)),
    ]
    
    for name, test_func in tests:
        try:
            result = test_func()
            if not result.isError():
                print(f"✓ {name} supported")
            else:
                if result.exception_code == 1:
                    print(f"✗ {name} not supported")
                else:
                    print(f"? {name} error: {result}")
        except Exception as e:
            print(f"✗ {name} failed: {e}")

# Test device capabilities
test_function_support(client)

Next Steps

How is this guide?