首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >modbus-tk用于Modbus RTU,读写多个寄存器(fn代码23),返回异常代码1。

modbus-tk用于Modbus RTU,读写多个寄存器(fn代码23),返回异常代码1。
EN

Stack Overflow用户
提问于 2020-02-01 02:41:22
回答 4查看 5.4K关注 0票数 3

我使用modbus-tk通过RS-485网络通过Modbus RTU与设备进行串行通信。

我正在努力弄清楚如何使用函数23,READ_WRITE_MULTIPLE_REGISTERS。这是我第一次使用函数23。以下是我目前的实现:

代码语言:javascript
复制
response = modbus_master.execute(
    slave=SLAVE_NUM,
    function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
    starting_address=2,
    quantity_of_x=1,
    output_value=[1],
)

在运行此命令时,我得到以下错误:Modbus Error: Exception code = 1

我在维基百科上查找了这个异常代码,并看到:

在查询中接收的函数代码不被从服务器识别或允许。

你认为这是否意味着我的设备真的不支持这个功能代码?或者我有语法问题/误用这个函数吗?

我已经把我的完整剧本放在下面了。

全代码示例

输入

代码语言:javascript
复制
#!/usr/bin/env python3


import time
from collections import namedtuple
from logging import Logger

from serial import Serial
from modbus_tk.modbus_rtu import RtuMaster
import modbus_tk.defines as cst  # cst = constants
from modbus_tk.utils import create_logger


PORT = "COM3"
SLAVE_NUM = 1
MODBUS_MASTER_TIMEOUT_SEC = 5.0

ModbusHoldingReg = namedtuple(
    "ModbusHoldingRegister", ["name", "address", "last_read_value", "to_write_value"]
)
shutdown_delay = ModbusHoldingReg("shutdown delay", 2, 0, None)  # sec

logger = create_logger(name="console")  # type: Logger

serial_ = Serial(PORT)
modbus_master = RtuMaster(serial_)
modbus_master.set_timeout(MODBUS_MASTER_TIMEOUT_SEC)
modbus_master.set_verbose(True)
# Sleep some time per [1]
# [1]: https://github.com/ljean/modbus-tk/issues/73#issuecomment-284800980
time.sleep(2.0)

# Read/write from/to multiple registers
response = modbus_master.execute(
    slave=SLAVE_NUM,
    function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
    starting_address=shutdown_delay.address,
    quantity_of_x=1,
    output_value=[1],
)  # type: tuple
print(response)

输出

代码语言:javascript
复制
2020-01-31 10:43:24,885 INFO    modbus_rtu.__init__     MainThread      RtuMaster COM3 is opened
2020-01-31 10:43:26,890 DEBUG   modbus.execute  MainThread      -> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
2020-01-31 10:43:31,933 DEBUG   modbus.execute  MainThread      <- 1-151-1-143-240
---------------------------------------------------------------------------
ModbusError                               Traceback (most recent call last)
<ipython-input-1-f42d200d6c09> in <module>
     37     starting_address=shutdown_delay.address,
     38     quantity_of_x=1,
---> 39     output_value=[1],
     40 )  # type: tuple
     41 print(response)

c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
     37             ret = fcn(*args, **kwargs)
     38         except Exception as excpt:
---> 39             raise excpt
     40         finally:
     41             if threadsafe:

c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
     35             lock.acquire()
     36         try:
---> 37             ret = fcn(*args, **kwargs)
     38         except Exception as excpt:
     39             raise excpt

c:\path\to\venv\lib\site-packages\modbus_tk\modbus.py in execute(self, slave, function_code, starting_address, quantity_of_x, output_value, data_format, expected_length)
    312                 # the slave has returned an error
    313                 exception_code = byte_2
--> 314                 raise ModbusError(exception_code)
    315             else:
    316                 if is_read_function:

ModbusError: Modbus Error: Exception code = 1

设备规范

  • 器件: SST传感的OXY-LC-485
  • Modbus RTU,9600/8-N-1
  • 用户指南 ( 7.1.2.1节包含一组输入寄存器)
  • 设备插入到我运行Python脚本的Windows机器中

我在Windows 10上使用Python3.6。

代码语言:javascript
复制
pyserial==3.4
modbus-tk==1.1.0
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2020-02-02 00:14:41

在@maxy的回答之后,modbus规范指出异常代码1(非法函数)意味着:

在查询中接收的函数代码不是服务器(或从服务器)允许的操作。这可能是因为函数代码只适用于较新的设备,并且没有在选定的单元中实现。它还可能指示服务器(或从服务器)在处理此类请求时处于错误状态,例如,因为它未配置并被要求返回寄存器值。

所以,在这个例子中,我想说设备不支持这个命令。

但是,考虑到另一个用户报告了此命令的问题,我认为值得检查编码:

代码语言:javascript
复制
1- Slave ID
23- Function Code
0, 2- Read Starting Address
0, 1- Quantity to Read
0, 23- Write Starting Address
0, 1 - Quantity to write
2, Write Byte Count
0,1, - Write Registers value
55,131 - CRC (have not checked)

这在我看来是正确的,只有一个例外;不清楚“写开始地址”来自哪里(并且怀疑它与函数代码相同)。看着来源

代码语言:javascript
复制
pdu = struct.pack(
    ">BHHHHB",
    function_code, starting_address, quantity_of_x, defines.READ_WRITE_MULTIPLE_REGISTERS,
    len(output_value), byte_count
)

这在我看来是不对的(defines.READ_WRITE_MULTIPLE_REGISTERS总是23岁)。在提交dcb0a2f115d7a9d63930c9b4466c4501039880a3中,代码被更改为此;以前是:

代码语言:javascript
复制
pdu = struct.pack(
    ">BHHHHB",
    function_code, starting_address, quantity_of_x, starting_addressW_FC23,
    len(output_value), byte_count
)

这对我来说更有意义(您需要一种传递地址的方式来开始编写,而当前的接口似乎没有提供此功能)。我已经在github问题中添加了一个注释。

因此,总之,您的问题可能是由于设备,但是即使设备支持命令,我也不认为它会因为modbus-tk中的错误而工作。

票数 3
EN

Stack Overflow用户

发布于 2020-02-01 22:28:12

调试输出包含以下跟踪:

代码语言:javascript
复制
-> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
<- 1-151-1-143-240

请考虑以下几点:

  • 第二个字节为23,因此发送了正确的函数代码。
  • 你实际上在线路上得到了一个“非法的函数代码”,这肯定是由设备故意生成的。您不会收到CRC错误、“非法地址”或“非法值”。
  • 设备支持代码23是可能的(但我猜有点不太可能),但只支持某些地址。

剩下的唯一可能是错误的地方是库将实际请求的编码搞砸了。我没有检查其他字节,但作为英国人的评论,在modbus-tk中可能存在编码错误。实现奴隶的人可能决定用“非法函数代码”来响应格式错误的请求。

在我看来,他们只是懒得去实现这个函数代码,这似乎也是合理的。例如,简便车甚至没有列出它。

票数 2
EN

Stack Overflow用户

发布于 2020-02-04 01:20:31

基于@maxy的回答的严谨性,然后基于@英国人的回答,我决定进一步调查。目标是确定根本原因是modbus-tk错误,还是我的设备不支持函数代码23。

modbus-tk第121期中,OP提到pymodbus使用函数代码23,读写多个寄存器。

因此,我安装了pymodbus==2.3.0,然后给了它一个旋转。下面是我使用的代码:

输入

代码语言:javascript
复制
#!/usr/bin/env python3


import sys
import logging
from collections import namedtuple

from pymodbus.pdu import ModbusResponse, ExceptionResponse
from pymodbus.client.sync import ModbusSerialClient
from pymodbus.register_read_message import ReadWriteMultipleRegistersResponse


log = logging.getLogger()
log.addHandler(logging.StreamHandler(sys.stdout))
log.setLevel(logging.DEBUG)


ModbusHoldingReg = namedtuple(
    "ModbusHoldingRegister", ["name", "address", "last_read_value", "to_write_value"]
)

sensor_mode = ModbusHoldingReg("sensor on, off, and standby enum", 0, None, None)


PORT = "COM3"
SLAVE_NUM = 1
BAUD_RATE = 9600


with ModbusSerialClient(
    method="rtu", port=PORT, baudrate=BAUD_RATE, strict=False
) as modbus_client:
    regs_to_write = [0, 1, 3]
    response = modbus_client.readwrite_registers(
        read_address=sensor_mode.address,
        read_count=len(regs_to_write),
        write_address=sensor_mode.address,
        write_registers=regs_to_write,
        unit=SLAVE_NUM,
    )  # type: ModbusResponse

    if response.isError():
        response: ExceptionResponse
        print(
            f"Exception!  Original function code = {response.original_code}, "
            f"exception_code = {response.exception_code}."
        )
    else:
        response: ReadWriteMultipleRegistersResponse
        print(f"Success!  response.registers = {response.registers}.")

输出

代码语言:javascript
复制
Current transaction state - IDLE
Running transaction 1
SEND: 0x1 0x17 0x0 0x0 0x0 0x3 0x0 0x0 0x0 0x3 0x6 0x0 0x0 0x0 0x1 0x0 0x3 0x5d 0xce
New Transaction state 'SENDING'
Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
RECV: 0x1 0x97 0x1 0x8f 0xf0
Getting Frame - 0x97 0x1
Factory Response[151]
Frame advanced, resetting header!!
Adding transaction 1
Getting transaction 1
Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Original function code = 23, exception code = 1.

结论

可以看到,设备响应的异常代码1,Illegal Function。所以我认为这个设备不支持功能代码23。

如果我找到一个支持fn代码23的设备,我会回过头来。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60013544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档