Quick Start
install pymodbus
pip install pymodbus
Connect to Device
from pymodbus.client import ModbusTcpClient
# Connect to Modbus TCP device
client = ModbusTcpClient('192.168.1.100')
client.connect()
# Read 10 holding registers starting at address 0
result = client.read_holding_registers(0, 10, unit=1)
if not result.isError():
print(f"Registers: {result.registers}")
client.close()
このコードは、Pythonから Modbus TCP対応機器(PLCやセンサ) に接続し、保持レジスタ(Holding Registers)を読み取る最小構成の例です。
まず ModbusTcpClient を使って、IPアドレス 192.168.1.100 のModbus TCPデバイスを指定し、connect() でTCP接続を確立します。次に read_holding_registers(0, 10, unit=1) により、アドレス0番から10個分の保持レジスタを読み出します。ここで注意すべき点は、pymodbusではアドレスが 0-based であることです。PLCの「40001番地」は、このコードではアドレス0に対応します。
unit=1 はUnit ID(Slave ID)を指定しており、ゲートウェイ配下の機器やPLC設定によって必須になる場合があります。通信結果は result に格納され、isError() でエラー判定を行った後、正常時のみ result.registers から数値リストとして取得します。最後に close() を呼び、通信を正しく終了しています。
Read Data
# Read different register types
holding = client.read_holding_registers(0, 10)
input_regs = client.read_input_registers(0, 10)
coils = client.read_coils(0, 16)
discrete = client.read_discrete_inputs(0, 16)
# Write data
client.write_register(0, 1234)
client.write_registers(0, [10, 20, 30, 40])
client.write_coil(0, True)
このコードは、Modbusの主要な4種類のデータ領域の読み取りと書き込みをまとめて示した例です。まず読み取り部分では、read_holding_registers により読み書き可能な保持レジスタを、read_input_registers で読み取り専用の入力レジスタを取得しています。どちらも16bit単位の数値データで、センサ値や設定値に使われます。
次に read_coils は1bitのON/OFF情報(リレーや起動フラグ)を、read_discrete_inputs は読み取り専用の1bit入力(スイッチや接点状態)を取得します。いずれもアドレスは0-basedで指定します。
書き込みでは、write_register により単一の保持レジスタへ値を書き込み、write_registers で複数レジスタを一括更新します。write_coil は1bitの制御信号を書き込み、機器の動作制御に使われます。誤ったアドレス指定は装置動作に影響するため、仕様確認が重要です。
Protocol Support
- Modbus TCP
- Modbus RTU
- Modbus ASCII
Real-World Example
Reading temperature and pressure from industrial sensor:
from pymodbus.client import ModbusTcpClient
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.constants import Endian
# Connect to Modbus device
client = ModbusTcpClient('192.168.1.50', port=502)
client.connect()
try:
# Read temperature (32-bit float at address 100)
result = client.read_holding_registers(100, 2, unit=1)
if not result.isError():
decoder = BinaryPayloadDecoder.fromRegisters(
result.registers,
byteorder=Endian.Big,
wordorder=Endian.Big
)
temperature = decoder.decode_32bit_float()
print(f"Temperature: {temperature:.2f}°C")
# Read pressure (32-bit float at address 102)
result = client.read_holding_registers(102, 2, unit=1)
if not result.isError():
decoder = BinaryPayloadDecoder.fromRegisters(
result.registers,
byteorder=Endian.Big,
wordorder=Endian.Big
)
pressure = decoder.decode_32bit_float()
print(f"Pressure: {pressure:.2f} bar")
# Alert on high pressure
if pressure > 10:
print("⚠️ High pressure alert!")
except Exception as e:
print(f"Error: {e}")
finally:
client.close()
このコードは、Modbus TCP機器から32bit浮動小数点(Float)データを読み取り、物理量として解釈する実践例です。温度と圧力という、産業機器で典型的なセンサ値を想定しています。
まず ModbusTcpClient を使って、IPアドレス 192.168.1.50、ポート502のModbus機器へ接続します。次に、温度データとして保持レジスタ100番地から2レジスタ分を読み出しています。Modbusでは1レジスタが16bitのため、32bitのFloatは必ず2レジスタを使用します。
読み出した生データは BinaryPayloadDecoder に渡され、byteorder と wordorder を指定してエンディアンを明示します。ここではBig Endianを使用し、decode_32bit_float() により人が扱える浮動小数点値へ変換しています。同様の処理を圧力データ(アドレス102)にも行い、一定値を超えた場合にはアラートを表示します。
try-except-finally により、通信エラーが発生しても必ず client.close() が実行される安全な構成になっています。ModbusでFloatを扱う際の基本パターンを示したコードです。
Error Handling
Robust error handling for industrial applications:
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException, ConnectionException
client = ModbusTcpClient('192.168.1.100', timeout=3, retries=3)
try:
if not client.connect():
raise ConnectionException("Failed to connect")
result = client.read_holding_registers(0, 10, unit=1)
if result.isError():
print(f"Modbus error: {result}")
else:
print(f"Data: {result.registers}")
except ConnectionException as e:
print(f"Connection failed: {e}")
except ModbusException as e:
print(f"Modbus error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
finally:
client.close()
このコードは、Modbus TCP通信を安全に行うためのエラーハンドリング付き実装例です。ModbusTcpClient 生成時に timeout=3 と retries=3 を指定し、通信遅延や一時的な失敗に備えています。
connect() の戻り値を確認し、接続できなかった場合は ConnectionException を明示的に発生させています。これにより、通信不可とModbus処理エラーを明確に分離できます。接続後は保持レジスタを読み出し、isError() でModbusレベルの応答エラーを判定します。
except 節では、接続失敗・Modbus例外・予期しない例外をそれぞれ個別に捕捉し、原因切り分けを容易にしています。最後に finally で必ず client.close() を呼び、通信資源を確実に解放する堅牢な構成になっています。