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 Type | Common Function Codes |
---|---|
PLC | 01, 02, 03, 04, 05, 06, 0F, 10 |
Energy Meter | 03, 04 |
Temperature Controller | 03, 04, 06, 10 |
VFD (Motor Drive) | 03, 06, 10 |
I/O Module | 01, 02, 05, 0F |
HMI/SCADA | All 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
- Data Types - Handle complex data types
- Error Handling - Handle function errors
- Custom Messages - Create custom functions
How is this guide?