Function Codes
Complete reference for Modbus function codes with PyModbus. Standard read/write codes, advanced functions, custom function codes, and device support tables.
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.
Standard Function Codes
| Code | Name | PyModbus method | Direction |
|---|---|---|---|
| 0x01 | Read Coils | read_coils() | Read |
| 0x02 | Read Discrete Inputs | read_discrete_inputs() | Read |
| 0x03 | Read Holding Registers | read_holding_registers() | Read |
| 0x04 | Read Input Registers | read_input_registers() | Read |
| 0x05 | Write Single Coil | write_coil() | Write |
| 0x06 | Write Single Register | write_register() | Write |
| 0x0F | Write Multiple Coils | write_coils() | Write |
| 0x10 | Write Multiple Registers | write_registers() | Write |
| 0x16 | Mask Write Register | mask_write_register() | Write |
| 0x17 | Read/Write Multiple Registers | readwrite_registers() | Both |
Log Modbus operations automatically
TofuPilot records test results from your PyModbus scripts, tracks pass/fail rates, and generates compliance reports. Free to start.
Reading Functions
0x01 - Read Coils
Read discrete outputs (ON/OFF states).
result = client.read_coils(address=0, count=8, slave=1)
if not result.isError():
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).
result = client.read_discrete_inputs(address=0, count=16, slave=1)
if not result.isError():
if result.bits[5]:
print("Input 5 is ON")0x03 - Read Holding Registers
Read read/write registers (configuration, setpoints).
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).
result = client.read_input_registers(address=0, count=4, slave=1)
if not result.isError():
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
client.write_coil(address=0, value=True, slave=1) # ON
client.write_coil(address=0, value=False, slave=1) # OFF0x06 - Write Single Register
# Set temperature setpoint to 25.0C (scaled by 10)
client.write_register(address=1000, value=250, slave=1)0x0F - Write Multiple Coils
relay_states = [True, True, False, True, False, False, True, False]
client.write_coils(address=0, values=relay_states, slave=1)0x10 - Write Multiple Registers
config_values = [100, 200, 300, 400]
client.write_registers(address=1000, values=config_values, slave=1)Advanced Functions
0x07 - Read Exception Status
result = client.read_exception_status(slave=1)
if not result.isError():
print(f"Exception status: {bin(result.exception_status)}")0x08 - Diagnostics
from pymodbus.diag_message import DiagnosticStatusRequest
# Echo test (subfunction 0x00)
request = DiagnosticStatusRequest(sub_function_code=0x00, data=0x1234)
result = client.execute(request)0x0B - Get Comm Event Counter
result = client.get_comm_event_counter(slave=1)
if not result.isError():
print(f"Status: {result.status}, Events: {result.count}")0x0C - Get Comm Event Log
result = client.get_comm_event_log(slave=1)
if not result.isError():
print(f"Events: {result.event_count}, Messages: {result.message_count}")0x11 - Report Server ID
result = client.report_server_id(slave=1)
if not result.isError():
print(f"Server ID: {result.identifier}")0x16 - Mask Write Register
Modify individual bits in a register without affecting others.
# 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 a single transaction.
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
result = client.read_fifo_queue(address=100, slave=1)
if not result.isError():
print(f"FIFO values: {result.fifo}")0x2B - Read Device Identification
from pymodbus.mei_message import ReadDeviceInformationRequest
request = ReadDeviceInformationRequest(
read_code=0x01, # Basic 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
import struct
from pymodbus.pdu import ModbusRequest, ModbusResponse
class CustomRequest(ModbusRequest):
function_code = 0x55
def __init__(self, data=0):
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]
request = CustomRequest(data=0x1234)
result = client.execute(request)Device Support by 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, the device returns the function code + 0x80 with an exception code.
result = client.read_holding_registers(9999, 10)
if result.isError():
print(f"Exception code: {result.exception_code}")
# 1 = Illegal Function
# 2 = Illegal Data Address
# 3 = Illegal Data Value
# 4 = Slave Device FailurePerformance Tips
# Single request for 10 registers (fast)
client.read_holding_registers(0, 10)
# 10 separate requests (slow)
for i in range(10):
client.read_holding_registers(i, 1)
# Combined read/write in one transaction (fast)
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 before using advanced functions.
Test Function Support
def test_function_support(client, slave=1):
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")
elif 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_function_support(client)