Armadilloフォーラム

Armadillo A6EのRS485ポートを使用したMODBUS-RTUマスターモードでのKeyence PLCレジスタ読取問題

hhlin

2023年8月12日 17時31分

Armadillo A6EのRS485ポートを使用して、MODBUS-RTUのマスターモードでKeyenceのPLC(CPU: KV-7500、通信UNIT: KV-L21V)のレジスタを読み取ろうとしたところ、読み取ったデータの先頭に余計な0が付加され、正常に通信できません。

通信設定は以下の通りです:
BAUDRATE:9600
PARITY:EVEN
STOPBITS:1
SLAVE ID:1

実装については、python3のpymodbus、modbus_tk、minimalmodbusの3つのライブラリーで試してみたのですが、結果は変わらないです。
modbus_tkライブラリーの実際の通信ログは以下の通りです:

python3 modbustktest.py 
DEBUG:modbus_tk:RtuMaster /dev/ttymxc4 is opened
DEBUG:modbus_tk:-> 2-3-0-0-0-100-68-18
DEBUG:modbus_tk:<- 0-2-3-200-0-43-0-4-0-1-0-0-0-0-0-0-0-0-0-0-0-0-0-0-13-1-0-0-0-0-0-0-0-0-0-0-0-0-13-1-7-231-2-216-5-136-7-231-2-216-5-136-13-1-7-231-2-216-5-25-7-231-2-216-5-25-13-0-7-231-2-216-5-24-7-231-2-216-5-24-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-44
Traceback (most recent call last):
  File "/root/modbustktest.py", line 35, in fetch_plc_data
    data = master.execute(PLC_SLAVE_ID, defines.READ_HOLDING_REGISTERS, 0, 100)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 39, in new
    raise excpt
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 37, in new
    ret = fcn(*args, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus.py", line 356, in execute
    response_pdu = query.parse_response(response)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus_rtu.py", line 51, in parse_response
    raise ModbusInvalidResponseError(
modbus_tk.exceptions.ModbusInvalidResponseError: Response address 0 is different from request address 2

しかし、同じコードをPC上にて実行すると、結果は正常です。
Slave IDを2に変えてみたのですが、結果は一緒でした。

以下のコードはpython3でmodbus_tkライブラリを使用したコードです。

import serial
from modbus_tk import modbus_rtu
from modbus_tk import modbus_tcp
from modbus_tk import hooks
from modbus_tk import defines
from modbus_tk.exceptions import ModbusInvalidResponseError
import threading
import os
import logging
import traceback
 
PLC_PORT = 'COM27'
PLC_BAUDRATE = 9600
PLC_PARITY = 'E'
PLC_STOPBITS = 1
PLC_SLAVE_ID = 1
 
logging.basicConfig(level=logging.DEBUG)
 
current_path = os.path.dirname(os.path.abspath(__file__))
 
 
# function to fetch PLC data
def fetch_plc_data():
    try :
        master = modbus_rtu.RtuMaster(serial.Serial(port=PLC_PORT, baudrate=PLC_BAUDRATE, bytesize=8, parity=PLC_PARITY, stopbits=PLC_STOPBITS))
    except Exception as e:
        print(traceback.format_exc())
 
    master.set_timeout(5.0)
    master.set_verbose(True)
 
    try:
        data = master.execute(PLC_SLAVE_ID, defines.READ_HOLDING_REGISTERS, 0, 100)
    except Exception as e:
        print(traceback.format_exc())
 
    # close connection to PLC
    master.close()
 
def run():
    # create and start PLC_data_fetch_thread
    PLC_data_fetch_thread = threading.Thread(target=fetch_plc_data, name='PLC_data_fetch_thread')
    PLC_data_fetch_thread.start()
 
if __name__ == "__main__":
    run()

また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。
KeyenceのPLCとの通信に特有の問題なのか、あるいは設定やコードに何か誤りがあるのかをご教示いただければ幸いです。
よろしくお願いいたします。

コメント

アットマークテクノの古賀です。

hhlinさん(2023年8月12日 17時31分):
>Armadillo A6EのRS485ポートを使用して、MODBUS-RTUのマスターモードでKeyenceのPLC(CPU: KV-7500、通信UNIT: KV-L21V)のレジスタを読み取ろうとしたところ、読み取ったデータの先頭に余計な0が付加され、正常に通信できません。
>
>通信設定は以下の通りです:
>BAUDRATE:9600
>PARITY:EVEN
>STOPBITS:1
>SLAVE ID:1
>
>実装については、python3のpymodbus、modbus_tk、minimalmodbusの3つのライブラリーで試してみたのですが、結果は変わらないです。
>modbus_tkライブラリーの実際の通信ログは以下の通りです:

python3 modbustktest.py 
DEBUG:modbus_tk:RtuMaster /dev/ttymxc4 is opened
DEBUG:modbus_tk:->2-3-0-0-0-100-68-18
DEBUG:modbus_tk:<- 0-2-3-200-0-43-0-4-0-1-0-0-0-0-0-0-0-0-0-0-0-0-0-0-13-1-0-0-0-0-0-0-0-0-0-0-0-0-13-1-7-231-2-216-5-136-7-231-2-216-5-136-13-1-7-231-2-216-5-25-7-231-2-216-5-25-13-0-7-231-2-216-5-24-7-231-2-216-5-24-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-44
Traceback (most recent call last):
  File "/root/modbustktest.py", line 35, in fetch_plc_data
    data = master.execute(PLC_SLAVE_ID, defines.READ_HOLDING_REGISTERS, 0, 100)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 39, in new
    raise excpt
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 37, in new
    ret = fcn(*args, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus.py", line 356, in execute
    response_pdu = query.parse_response(response)
  File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus_rtu.py", line 51, in parse_response
    raise ModbusInvalidResponseError(
modbus_tk.exceptions.ModbusInvalidResponseError: Response address 0 is different from request address 2

レスポンスの受信データ先頭に 0 が付いているために、レスポンスヘッダのパースでエラーしてしまう状況なのですね。

>以下のコードはpython3でmodbus_tkライブラリを使用したコードです。

>また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。
>KeyenceのPLCとの通信に特有の問題なのか、あるいは設定やコードに何か誤りがあるのかをご教示いただければ幸いです。

設定やソースコードは、問題ないように思われます。

>しかし、同じコードをPC上にて実行すると、結果は正常です。

質問ですが、PC と KV-7500 & KV-L21V とを接続する際の RS485 I/F には、何をお使いでしょうか?

原因について、僕は分からないです。ごめんなさい。
レスポンスヘッダ先頭の SLAVE ID として 0 があり得ないことから、modbus_tk であれば、after_recv の hook 関数を設定して、レスポンスヘッダ先頭が 0 であれば、それを hook 関数で削るというのがアドホックな対策になろうかと思います。

古賀様、ご返事ありがとうございます。

PC と KV-7500 & KV-L21V とを接続する際の RS485 I/F は、RS232-RS485のCONVERTER(MODEL:STM485S)を使用しています。

MODBUS_TKのソースコードを修正して最初の0を無視するように試みましたが、その後も別のCRC不一致の問題が発生しているようです。最後の1バイトのデータも正しく受信されていないようです。

Keyence社にもお問い合わせする予定ですが、その前に他にも考慮すべき対策があれば、ぜひご提案いただけると幸いです。

何卒よろしくお願いいたします。

> アットマークテクノの古賀です。
>
> hhlinさん(2023年8月12日 17時31分):
> >Armadillo A6EのRS485ポートを使用して、MODBUS-RTUのマスターモードでKeyenceのPLC(CPU: KV-7500、通信UNIT: KV-L21V)のレジスタを読み取ろうとしたところ、読み取ったデータの先頭に余計な0が付加され、正常に通信できません。
> >
> >通信設定は以下の通りです:
> >BAUDRATE:9600
> >PARITY:EVEN
> >STOPBITS:1
> >SLAVE ID:1
> >
> >実装については、python3のpymodbus、modbus_tk、minimalmodbusの3つのライブラリーで試してみたのですが、結果は変わらないです。
> >modbus_tkライブラリーの実際の通信ログは以下の通りです:
>

> python3 modbustktest.py 
> DEBUG:modbus_tk:RtuMaster /dev/ttymxc4 is opened
> DEBUG:modbus_tk:->2-3-0-0-0-100-68-18
> DEBUG:modbus_tk:<- 0-2-3-200-0-43-0-4-0-1-0-0-0-0-0-0-0-0-0-0-0-0-0-0-13-1-0-0-0-0-0-0-0-0-0-0-0-0-13-1-7-231-2-216-5-136-7-231-2-216-5-136-13-1-7-231-2-216-5-25-7-231-2-216-5-25-13-0-7-231-2-216-5-24-7-231-2-216-5-24-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-44
> Traceback (most recent call last):
>   File "/root/modbustktest.py", line 35, in fetch_plc_data
>     data = master.execute(PLC_SLAVE_ID, defines.READ_HOLDING_REGISTERS, 0, 100)
>   File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 39, in new
>     raise excpt
>   File "/usr/local/lib/python3.9/dist-packages/modbus_tk/utils.py", line 37, in new
>     ret = fcn(*args, **kwargs)
>   File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus.py", line 356, in execute
>     response_pdu = query.parse_response(response)
>   File "/usr/local/lib/python3.9/dist-packages/modbus_tk/modbus_rtu.py", line 51, in parse_response
>     raise ModbusInvalidResponseError(
> modbus_tk.exceptions.ModbusInvalidResponseError: Response address 0 is different from request address 2
> 

>
> レスポンスの受信データ先頭に 0 が付いているために、レスポンスヘッダのパースでエラーしてしまう状況なのですね。
>
> >以下のコードはpython3でmodbus_tkライブラリを使用したコードです。
> …
> >また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。
> >KeyenceのPLCとの通信に特有の問題なのか、あるいは設定やコードに何か誤りがあるのかをご教示いただければ幸いです。
>
> 設定やソースコードは、問題ないように思われます。
>
> >しかし、同じコードをPC上にて実行すると、結果は正常です。
>
> 質問ですが、PC と KV-7500 & KV-L21V とを接続する際の RS485 I/F には、何をお使いでしょうか?
>
> 原因について、僕は分からないです。ごめんなさい。
> レスポンスヘッダ先頭の SLAVE ID として 0 があり得ないことから、modbus_tk であれば、after_recv の hook 関数を設定して、レスポンスヘッダ先頭が 0 であれば、それを hook 関数で削るというのがアドホックな対策になろうかと思います。

アットマークテクノの古賀です。

hhlinさん:
>PC と KV-7500 & KV-L21V とを接続する際の RS485 I/F は、RS232-RS485のCONVERTER(MODEL:STM485S)を使用しています。

こちらですね:
 https://www.aitendo.com/product/382
 https://aitendo3.sakura.ne.jp/aitendo_data/product_img/converter-all/ST…

>MODBUS_TKのソースコードを修正して最初の0を無視するように試みましたが、その後も別のCRC不一致の問題が発生しているようです。最後の1バイトのデータも正しく受信されていないようです。
>
>Keyence社にもお問い合わせする予定ですが、その前に他にも考慮すべき対策があれば、ぜひご提案いただけると幸いです。

>>原因について、僕は分からないです。ごめんなさい。
>>レスポンスヘッダ先頭の SLAVE ID として 0 があり得ないことから、modbus_tk であれば、after_recv の hook 関数を設定して、レスポンスヘッダ先頭が 0 であれば、それを hook 関数で削るというのがアドホックな対策になろうかと思います。

modbus_tk のソースを改変して対処されたということですが、差し支えなければ、改変部の内容を教えて頂けますか。
また、「最後の1バイトのデータも正しく受信されていないようです。」というのは、どうやって判定なさったのかも教えてくださいませ。

古賀さん、お返事が遅くなり、申し訳ございません。

KEYENCE社に問い合わせし、KV-L21Vの出力をKV Studioのトレースモニタで確認した結果、KV-L21V自体に問題はないことが確認できました。
添付のトレースモニタのスクリーンショットによれば、KV-L21Vの出力には先頭に00が表示されないことから、問題はKV-L21VからArmadillo A6Eへの接続やA6Eの受信側にある可能性が高いです。

A6Eの送受信ログを確認すると、データの先頭に余分な0が挿入され、最後のCRCも1バイトしか含まれていないことが分かりました。
恐らくA6Eのバッファからデータを読み取る際に、データが1バイトずれている可能性が考えられます。
この点についてご確認いただければ幸いです。

よろしくお願いいたします。

> アットマークテクノの古賀です。
>
> hhlinさん:
> >PC と KV-7500 & KV-L21V とを接続する際の RS485 I/F は、RS232-RS485のCONVERTER(MODEL:STM485S)を使用しています。
>
> こちらですね:
>  https://www.aitendo.com/product/382
>  https://aitendo3.sakura.ne.jp/aitendo_data/product_img/converter-all/ST…
>
> >MODBUS_TKのソースコードを修正して最初の0を無視するように試みましたが、その後も別のCRC不一致の問題が発生しているようです。最後の1バイトのデータも正しく受信されていないようです。
> >
> >Keyence社にもお問い合わせする予定ですが、その前に他にも考慮すべき対策があれば、ぜひご提案いただけると幸いです。
> …
> >>原因について、僕は分からないです。ごめんなさい。
> >>レスポンスヘッダ先頭の SLAVE ID として 0 があり得ないことから、modbus_tk であれば、after_recv の hook 関数を設定して、レスポンスヘッダ先頭が 0 であれば、それを hook 関数で削るというのがアドホックな対策になろうかと思います。
>
> modbus_tk のソースを改変して対処されたということですが、差し支えなければ、改変部の内容を教えて頂けますか。
> また、「最後の1バイトのデータも正しく受信されていないようです。」というのは、どうやって判定なさったのかも教えてくださいませ。

ファイル ファイルの説明
keyence-trace.png
a6e-log.png

アットマークテクノの古賀です。

hhlinさん:
>古賀さん、お返事が遅くなり、申し訳ございません。

あ、いえいえ。

>KEYENCE社に問い合わせし、KV-L21Vの出力をKV Studioのトレースモニタで確認した結果、KV-L21V自体に問題はないことが確認できました。

了解しました。

>添付のトレースモニタのスクリーンショットによれば、KV-L21Vの出力には先頭に00が表示されないことから、問題はKV-L21VからArmadillo A6Eへの接続やA6Eの受信側にある可能性が高いです。

そうですね。最初に質問を頂いた際に

hhlinさん(2023年8月12日 17時31分):
>また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。

ということでしたから、対向デバイスが KV-L21V の場合に問題が起きるのですよね。

>A6Eの送受信ログを確認すると、データの先頭に余分な0が挿入され、最後のCRCも1バイトしか含まれていないことが分かりました。

A6E が KV-L21V からのレスポンスを受信した時に、受信データ先頭に 0 が 1Byte 付いてしまい、レスポンス末尾の CRC の 2Byter めが欠けている、という状況ですよね。
添付して頂いたトレースモニタのスクリーンショットを拝見しました。有難うございます。

>恐らくA6Eのバッファからデータを読み取る際に、データが1バイトずれている可能性が考えられます。
>この点についてご確認いただければ幸いです。

今のところ、対向デバイスが KV-L21V の時にのみ出る症状ということで、こちらには KV-L21V が無いため再現確認できません。ごめんなさい。
試しにですが、次のことをやってみるのは、いかがでしょうか?

・modbus_tk の install_hook() 関数を使い、"modbus_rtu.RtuMaster.before_send" という名前のフック関数をセットして、対向機器へのリクエスト送信前に、RS485 ポートに余計な受信データが届いていないか確認する。
 ←フック関数の第一引数に渡される RtuMaster クラスのインスタンスに対して、_serial メンバの read() を呼び出し、戻り値の bytes の長さが 0 でないか確認する。このフック関数は、第二引数に受け取った request を、自身の戻り値として返せばよい。
  https://github.com/ljean/modbus-tk/blob/master/modbus_tk/hooks.py
  https://github.com/ljean/modbus-tk/blob/master/modbus_tk/modbus_rtu.py#…

・modbus_rtu.RtuMaster のコンストラクタに渡す Serial オブジェクトを、Serial クラスではなく、その派生クラスである RS485 クラスのインスタンスにする。その際、RS485 クラスの rs485_mode() メソッドにデフォルト以外の設定にした RS485Settings を渡す。この RS485Setting の delay_before_tx や delay_before_rx を設定して変化があるか見てみる。
  https://github.com/pyserial/pyserial/blob/master/serial/rs485.py#L65

直接の解決策を提示できず恐縮ですが、もし参考になりましたら幸いです。

古賀様、アドバイスありがとうございます。

"before_send" のフックを設定し、RS485のreadコマンドを実行する前に "master._serial.read()" を呼び出してバッファが空であることを確認しましたが、読み取ったデータには依然として先頭に "00" が含まれています。

また、KeyenceのPLC以外にもRS485のサーマルカメラでも同様の問題が発生したことあります。
RS485の接続は2線式で行われており、A6Eをrebootで再起動しても問題が解決しない状況でしたが、RS485のケーブルを抜き差しすると正常に復旧することがあります。
配線に問題があるかと考えておりますが、それが原因である可能性はあるでしょうか。

ご確認のほどよろしくお願いいたします。

> アットマークテクノの古賀です。
>
> hhlinさん:
> >古賀さん、お返事が遅くなり、申し訳ございません。
>
> あ、いえいえ。
>
> >KEYENCE社に問い合わせし、KV-L21Vの出力をKV Studioのトレースモニタで確認した結果、KV-L21V自体に問題はないことが確認できました。
>
> 了解しました。
>
> >添付のトレースモニタのスクリーンショットによれば、KV-L21Vの出力には先頭に00が表示されないことから、問題はKV-L21VからArmadillo A6Eへの接続やA6Eの受信側にある可能性が高いです。
>
> そうですね。最初に質問を頂いた際に
>
> hhlinさん(2023年8月12日 17時31分):
> >また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。
>
> ということでしたから、対向デバイスが KV-L21V の場合に問題が起きるのですよね。
>
> >A6Eの送受信ログを確認すると、データの先頭に余分な0が挿入され、最後のCRCも1バイトしか含まれていないことが分かりました。
>
> A6E が KV-L21V からのレスポンスを受信した時に、受信データ先頭に 0 が 1Byte 付いてしまい、レスポンス末尾の CRC の 2Byter めが欠けている、という状況ですよね。
> 添付して頂いたトレースモニタのスクリーンショットを拝見しました。有難うございます。
>
> >恐らくA6Eのバッファからデータを読み取る際に、データが1バイトずれている可能性が考えられます。
> >この点についてご確認いただければ幸いです。
>
> 今のところ、対向デバイスが KV-L21V の時にのみ出る症状ということで、こちらには KV-L21V が無いため再現確認できません。ごめんなさい。
> 試しにですが、次のことをやってみるのは、いかがでしょうか?
>
> ・modbus_tk の install_hook() 関数を使い、"modbus_rtu.RtuMaster.before_send" という名前のフック関数をセットして、対向機器へのリクエスト送信前に、RS485 ポートに余計な受信データが届いていないか確認する。
>  ←フック関数の第一引数に渡される RtuMaster クラスのインスタンスに対して、_serial メンバの read() を呼び出し、戻り値の bytes の長さが 0 でないか確認する。このフック関数は、第二引数に受け取った request を、自身の戻り値として返せばよい。
>   https://github.com/ljean/modbus-tk/blob/master/modbus_tk/hooks.py
>   https://github.com/ljean/modbus-tk/blob/master/modbus_tk/modbus_rtu.py#…
>
> ・modbus_rtu.RtuMaster のコンストラクタに渡す Serial オブジェクトを、Serial クラスではなく、その派生クラスである RS485 クラスのインスタンスにする。その際、RS485 クラスの rs485_mode() メソッドにデフォルト以外の設定にした RS485Settings を渡す。この RS485Setting の delay_before_tx や delay_before_rx を設定して変化があるか見てみる。
>   https://github.com/pyserial/pyserial/blob/master/serial/rs485.py#L65
>
> 直接の解決策を提示できず恐縮ですが、もし参考になりましたら幸いです。

ファイル ファイルの説明
pic-thermal.png
pic-a6e.png

状況を補足説明いたします。サーマルカメラのケースでは、データの先頭に "00" がついたりつかなかったりすることがあります。
添付のスクリーンショットをご確認いただければ幸いです。どうぞよろしくお願いいたします。

> 古賀様、アドバイスありがとうございます。
>
> "before_send" のフックを設定し、RS485のreadコマンドを実行する前に "master._serial.read()" を呼び出してバッファが空であることを確認しましたが、読み取ったデータには依然として先頭に "00" が含まれています。
>
> また、KeyenceのPLC以外にもRS485のサーマルカメラでも同様の問題が発生したことあります。
> RS485の接続は2線式で行われており、A6Eをrebootで再起動しても問題が解決しない状況でしたが、RS485のケーブルを抜き差しすると正常に復旧することがあります。
> 配線に問題があるかと考えておりますが、それが原因である可能性はあるでしょうか。
>
> ご確認のほどよろしくお願いいたします。
>
> > アットマークテクノの古賀です。
> >
> > hhlinさん:
> > >古賀さん、お返事が遅くなり、申し訳ございません。
> >
> > あ、いえいえ。
> >
> > >KEYENCE社に問い合わせし、KV-L21Vの出力をKV Studioのトレースモニタで確認した結果、KV-L21V自体に問題はないことが確認できました。
> >
> > 了解しました。
> >
> > >添付のトレースモニタのスクリーンショットによれば、KV-L21Vの出力には先頭に00が表示されないことから、問題はKV-L21VからArmadillo A6Eへの接続やA6Eの受信側にある可能性が高いです。
> >
> > そうですね。最初に質問を頂いた際に
> >
> > hhlinさん(2023年8月12日 17時31分):
> > >また、他のPLCやPCへの通信、例えば三菱のRシリーズPLCとか、PCのModbusSlaveソフトウェアへの通信は正常に行われます。
> >
> > ということでしたから、対向デバイスが KV-L21V の場合に問題が起きるのですよね。
> >
> > >A6Eの送受信ログを確認すると、データの先頭に余分な0が挿入され、最後のCRCも1バイトしか含まれていないことが分かりました。
> >
> > A6E が KV-L21V からのレスポンスを受信した時に、受信データ先頭に 0 が 1Byte 付いてしまい、レスポンス末尾の CRC の 2Byter めが欠けている、という状況ですよね。
> > 添付して頂いたトレースモニタのスクリーンショットを拝見しました。有難うございます。
> >
> > >恐らくA6Eのバッファからデータを読み取る際に、データが1バイトずれている可能性が考えられます。
> > >この点についてご確認いただければ幸いです。
> >
> > 今のところ、対向デバイスが KV-L21V の時にのみ出る症状ということで、こちらには KV-L21V が無いため再現確認できません。ごめんなさい。
> > 試しにですが、次のことをやってみるのは、いかがでしょうか?
> >
> > ・modbus_tk の install_hook() 関数を使い、"modbus_rtu.RtuMaster.before_send" という名前のフック関数をセットして、対向機器へのリクエスト送信前に、RS485 ポートに余計な受信データが届いていないか確認する。
> >  ←フック関数の第一引数に渡される RtuMaster クラスのインスタンスに対して、_serial メンバの read() を呼び出し、戻り値の bytes の長さが 0 でないか確認する。このフック関数は、第二引数に受け取った request を、自身の戻り値として返せばよい。
> >   https://github.com/ljean/modbus-tk/blob/master/modbus_tk/hooks.py
> >   https://github.com/ljean/modbus-tk/blob/master/modbus_tk/modbus_rtu.py#…
> >
> > ・modbus_rtu.RtuMaster のコンストラクタに渡す Serial オブジェクトを、Serial クラスではなく、その派生クラスである RS485 クラスのインスタンスにする。その際、RS485 クラスの rs485_mode() メソッドにデフォルト以外の設定にした RS485Settings を渡す。この RS485Setting の delay_before_tx や delay_before_rx を設定して変化があるか見てみる。
> >   https://github.com/pyserial/pyserial/blob/master/serial/rs485.py#L65
> >
> > 直接の解決策を提示できず恐縮ですが、もし参考になりましたら幸いです。

ファイル ファイルの説明
スクリーンショット 2023-09-08 181433.png

アットマークテクノの古賀です。

hhlinさん(2023年9月8日 15時21分):
>古賀様、アドバイスありがとうございます。
>
>"before_send" のフックを設定し、RS485のreadコマンドを実行する前に
>"master._serial.read()" を呼び出してバッファが空であることを確認しましたが、

>>試しにですが、次のことをやってみるのは、いかがでしょうか?
>>
>>・modbus_tk の install_hook() 関数を使い、
>>"modbus_rtu.RtuMaster.before_send" という名前のフック関数をセットして、
>>対向機器へのリクエスト送信前に、RS485 ポートに余計な受信データが届いて
>>いないか確認する。

了解しました。

>読み取ったデータには依然として先頭に "00" が含まれています。
>
>また、KeyenceのPLC以外にもRS485のサーマルカメラでも同様の問題が発生
>したことあります。
>RS485の接続は2線式で行われており、A6Eをrebootで再起動しても問題が
>解決しない状況でしたが、RS485のケーブルを抜き差しすると正常に復旧する
>ことがあります。

hhlinさん(2023年9月8日 18時22分):
>状況を補足説明いたします。サーマルカメラのケースでは、データの先頭に
>"00" がついたりつかなかったりすることがあります。
>添付のスクリーンショットをご確認いただければ幸いです。どうぞよろしく
>お願いいたします。

症状が起きる場合、リクエスト送信前に余計な先頭 1Byte の 0 が届いている(残っている)ことはなく、リクエスト送信後のレスポンス受信時に、余計な
先頭 1Byte の 0 が付いてしまう、というわけですね。

>配線に問題があるかと考えておりますが、それが原因である可能性はあるで
>しょうか。

これについては、これまで分かっていることだけでは判断できないです。ごめんなさい。
とはいえ、サーマルカメラが対向デバイスの場合に、RS485 ケーブルの抜き差しで症状が起きなくなる場合もあるということですから、配線が要因になっている可能性は、あるかも知れません。

追加の確認として、以下については、いかがでしょうか?

>>試しにですが、次のことをやってみるのは、いかがでしょうか?

>>・modbus_rtu.RtuMaster のコンストラクタに渡す Serial
>>オブジェクトを、Serial クラスではなく、その派生クラスである
>>RS485 クラスのインスタンスにする。その際、RS485 クラスの
>>rs485_mode() メソッドにデフォルト以外の設定にした
>>RS485Settings を渡す。この RS485Setting の delay_before_tx や
>>delay_before_rx を設定して変化があるか見てみる。
>>  https://github.com/pyserial/pyserial/blob/master/serial/rs485.py#L65

rs485.py の上記箇所を見ると、delay_before_rx に値がセットされている場合、RS485 ポートへの書き込みを行った後(つまり、リクエストを送信した後)、delay_before_rtx の値を time.sleep() に渡してスリープしたのち、setRTS() を呼び出すようになっています。従って、delay_before_rx に値をセットした RS485Settings を引数にして RS485 の rs485_mode() を呼び出してから、その RS485 を modbus_rtu.RtuMaster のコンストラクタに渡すと、リクエスト送信と受信の間に、待ち動作と RTS 設定変更が起きるようになるはずです。

その変更により症状が変わるかどうか見てみるのは、ありかも知れません。