3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pymodbusコード解説(1) Kickoff

3
Posted at

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 に渡され、byteorderwordorder を指定してエンディアンを明示します。ここでは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=3retries=3 を指定し、通信遅延や一時的な失敗に備えています。

connect() の戻り値を確認し、接続できなかった場合は ConnectionException を明示的に発生させています。これにより、通信不可とModbus処理エラーを明確に分離できます。接続後は保持レジスタを読み出し、isError()Modbusレベルの応答エラーを判定します。

except 節では、接続失敗・Modbus例外・予期しない例外をそれぞれ個別に捕捉し、原因切り分けを容易にしています。最後に finally で必ず client.close() を呼び、通信資源を確実に解放する堅牢な構成になっています。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?