User Guide
This comprehensive guide covers all aspects of using ModbusLink.
Architecture Overview
ModbusLink is built on a clean, layered architecture that separates concerns and promotes code reusability:
┌─────────────────────────────────────┐
│ Client Layer │
│ (ModbusClient, AsyncModbusClient) │
├─────────────────────────────────────┤
│ Transport Layer │
│ (TcpTransport, RtuTransport, │
│ AsciiTransport, AsyncTcpTransport,│
│ AsyncRtuTransport, AsyncAsciiTransport)│
├─────────────────────────────────────┤
│ Utility Layer │
│ (CRC16, PayloadCoder, Logger) │
└─────────────────────────────────────┘
Transport Layer
The transport layer handles the low-level communication details.
TCP Transport
The TCP transport handles Modbus TCP communication with MBAP header management:
from modbuslink import TcpTransport
transport = TcpTransport(
host='192.168.1.100',
port=502,
timeout=10.0
)
RTU Transport
The RTU transport handles Modbus RTU communication with CRC16 validation:
from modbuslink import RtuTransport
transport = RtuTransport(
port='/dev/ttyUSB0', # or 'COM1' on Windows
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
timeout=1.0
)
Async TCP Transport
For high-performance applications, use the async TCP transport:
from modbuslink import AsyncTcpTransport
transport = AsyncTcpTransport(
host='192.168.1.100',
port=502,
timeout=10.0
)
ASCII Transport
The ASCII transport handles Modbus ASCII communication with LRC validation:
from modbuslink import AsciiTransport
transport = AsciiTransport(
port='COM1',
baudrate=9600,
bytesize=7,
parity='E',
stopbits=1,
timeout=1.0
)
Async RTU Transport
For high-performance RTU applications, use the async RTU transport:
from modbuslink import AsyncRtuTransport
transport = AsyncRtuTransport(
port='COM1',
baudrate=9600,
timeout=3.0
)
Async ASCII Transport
For high-performance ASCII applications, use the async ASCII transport:
from modbuslink import AsyncAsciiTransport
transport = AsyncAsciiTransport(
port='COM1',
baudrate=9600,
timeout=3.0
)
Client Layer
The client layer provides high-level Modbus operations.
Synchronous Client
from modbuslink import ModbusClient, TcpTransport
transport = TcpTransport(host='192.168.1.100', port=502)
client = ModbusClient(transport)
# Context manager (recommended)
with client:
registers = client.read_holding_registers(
slave_id=1, start_address=0, quantity=10
)
# Manual connection management
try:
client.connect()
registers = client.read_holding_registers(
slave_id=1, start_address=0, quantity=10
)
finally:
client.disconnect()
Asynchronous Client
from modbuslink import AsyncModbusClient, AsyncTcpTransport
import asyncio
async def main():
transport = AsyncTcpTransport(host='192.168.1.100', port=502)
client = AsyncModbusClient(transport)
# Context manager (recommended)
async with client:
registers = await client.read_holding_registers(
slave_id=1, start_address=0, quantity=10
)
asyncio.run(main())
Supported Function Codes
Read Operations
Read Coils (0x01)
coils = client.read_coils(
slave_id=1,
start_address=0,
quantity=8
)
# Returns: [True, False, True, False, True, False, True, False]
Read Discrete Inputs (0x02)
inputs = client.read_discrete_inputs(
slave_id=1,
start_address=0,
quantity=8
)
# Returns: [True, False, True, False, True, False, True, False]
Read Holding Registers (0x03)
registers = client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=10
)
# Returns: [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
Read Input Registers (0x04)
registers = client.read_input_registers(
slave_id=1,
start_address=0,
quantity=10
)
# Returns: [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
Write Operations
Write Single Coil (0x05)
client.write_single_coil(
slave_id=1,
address=0,
value=True
)
Write Single Register (0x06)
client.write_single_register(
slave_id=1,
address=0,
value=1234
)
Write Multiple Coils (0x0F)
client.write_multiple_coils(
slave_id=1,
start_address=0,
values=[True, False, True, False, True]
)
Write Multiple Registers (0x10)
client.write_multiple_registers(
slave_id=1,
start_address=0,
values=[1000, 2000, 3000, 4000, 5000]
)
Advanced Data Types
ModbusLink provides built-in support for common data types:
32-bit Float
# Write float32
client.write_float32(
slave_id=1,
start_address=100,
value=3.14159,
byte_order='big',
word_order='big'
)
# Read float32
temperature = client.read_float32(
slave_id=1,
start_address=100,
byte_order='big',
word_order='big'
)
32-bit Integer
# Write int32
client.write_int32(
slave_id=1,
start_address=102,
value=-123456,
byte_order='big',
word_order='big'
)
# Read int32
counter = client.read_int32(
slave_id=1,
start_address=102,
byte_order='big',
word_order='big'
)
32-bit Unsigned Integer
# Write uint32
client.write_uint32(
slave_id=1,
start_address=104,
value=4294967295,
byte_order='big',
word_order='big'
)
# Read uint32
value = client.read_uint32(
slave_id=1,
start_address=104,
byte_order='big',
word_order='big'
)
Byte and Word Order
ModbusLink supports different byte and word orders:
Byte Order: ‘big’ (big-endian) or ‘little’ (little-endian)
Word Order: ‘big’ (high word first) or ‘little’ (low word first)
# Different combinations
value1 = client.read_float32(1, 100, byte_order='big', word_order='big') # >AB
value2 = client.read_float32(1, 100, byte_order='big', word_order='little') # >BA
value3 = client.read_float32(1, 100, byte_order='little', word_order='big') # <AB
value4 = client.read_float32(1, 100, byte_order='little', word_order='little') # <BA
Callback Mechanism
Async clients support callback functions for operation completion notifications:
def on_read_complete(registers):
print(f"Read completed: {registers}")
def on_write_complete():
print("Write completed")
async def main():
async with client:
# Read with callback
registers = await client.read_holding_registers(
slave_id=1,
start_address=0,
quantity=5,
callback=on_read_complete
)
# Write with callback
await client.write_single_register(
slave_id=1,
address=0,
value=1234,
callback=on_write_complete
)
Concurrent Operations
Async clients support concurrent operations for improved performance:
async def concurrent_operations():
async with client:
# Create multiple tasks
tasks = [
client.read_holding_registers(slave_id=1, start_address=0, quantity=5),
client.read_coils(slave_id=1, start_address=0, quantity=8),
client.read_input_registers(slave_id=1, start_address=0, quantity=5),
client.write_single_register(slave_id=1, address=100, value=9999),
]
# Execute all tasks concurrently
results = await asyncio.gather(*tasks)
print(f"Concurrent results: {results}")
Performance Optimization
For optimal performance, consider the following recommendations:
Use Async Clients: For high-concurrency applications, async clients provide better performance.
Batch Operations: Use batch read/write operations whenever possible instead of individual operations.
Connection Reuse: Keep connections open and reuse them rather than frequent connect/disconnect.
Proper Timeouts: Adjust timeout values based on network conditions.
Error Handling
ModbusLink provides comprehensive error handling:
Exception Types
from modbuslink import (
ModbusLinkError, # Base exception
ConnectionError, # Connection issues
TimeoutError, # Request timeout
CRCError, # CRC validation failure
InvalidResponseError, # Invalid response format
ModbusException # Modbus protocol errors
)
Error Handling Example
try:
client.connect()
registers = client.read_holding_registers(slave_id=1, start_address=0, quantity=10)
except ConnectionError as e:
print(f"Connection failed: {e}")
except TimeoutError as e:
print(f"Request timed out: {e}")
except CRCError as e:
print(f"CRC validation failed: {e}")
except ModbusException as e:
print(f"Modbus error code {e.error_code}: {e}")
except ModbusLinkError as e:
print(f"ModbusLink error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
finally:
client.disconnect()
Logging
ModbusLink includes a comprehensive logging system:
import logging
from modbuslink.utils.logger import setup_logging
# Enable debug logging
setup_logging(level=logging.DEBUG)
# Or configure manually
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Best Practices
Use Context Managers: Always use
with
statements orasync with
for automatic resource management.Handle Exceptions: Implement proper exception handling for robust applications.
Configure Timeouts: Set appropriate timeout values based on your network conditions.
Use Async for High Performance: Use async clients for applications requiring high throughput.
Enable Logging: Use logging for debugging and monitoring in production.
Validate Data: Always validate data ranges and types before writing to devices.
Connection Management: Properly manage connection lifecycle to avoid frequent connect/disconnect operations.