PyModbus
PyModbusDocs

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

CodeNamePyModbus methodDirection
0x01Read Coilsread_coils()Read
0x02Read Discrete Inputsread_discrete_inputs()Read
0x03Read Holding Registersread_holding_registers()Read
0x04Read Input Registersread_input_registers()Read
0x05Write Single Coilwrite_coil()Write
0x06Write Single Registerwrite_register()Write
0x0FWrite Multiple Coilswrite_coils()Write
0x10Write Multiple Registerswrite_registers()Write
0x16Mask Write Registermask_write_register()Write
0x17Read/Write Multiple Registersreadwrite_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)  # OFF

0x06 - 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 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, 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 Failure

Performance 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)