【问题标题】:Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)Modbus 错误:[Invalid Message] 收到不完整的消息,预计至少 2 个字节(收到 0)
【发布时间】:2019-11-17 04:59:26
【问题描述】:

问题

pymodbus 主/客户端可以向从/服务器发送请求。从站/服务器使事情准备好返回,并等待主站/客户端来接他们。尽管服务器/从机准备就绪,主/客户端只是返回错误“Modbus 错误:[输入/输出] Modbus 错误:[无效消息] 收到不完整的消息,预计至少 2 个字节(收到 0)”。

设置

我通过此适配器将笔记本电脑用作服务器/从机:https://www.amazon.com/dp/B076WVFXN8/ref=twister_B076X1BS4H?_encoding=UTF8&psc=1

我有一个 Raspberry Pi 3 / BananaPi 作为主/客户端,并附有此适配器:https://www.aliexpress.com/item/32781613765.html?spm=a2g0s.9042311.0.0.1aec4c4d0EXx8M

我正在按照本教程的大部分内容进行设置,除了 Arduino 与笔记本电脑适配器交换:https://circuitdigest.com/microcontroller-projects/rs485-serial-communication-between-arduino-and-raspberry-pi - Raspberry 的引脚连接与教程中的一样。

我的笔记本电脑上有这个程序作为服务器/从机:

#!/usr/bin/env python
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer

import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
          ' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def run_server():

    slave_store1 = ModbusSlaveContext(co=ModbusSequentialDataBlock(0, [1]*16))
    slave_store2 = ModbusSlaveContext(di=ModbusSequentialDataBlock(0, [1]*16))
    slave_store3 = ModbusSlaveContext(ir=ModbusSequentialDataBlock(0, [5]*16))
    slave_store4 = ModbusSlaveContext(hr=ModbusSequentialDataBlock(0, [5]*16))

    slaves = {
        0x01: slave_store1,
        0x02: slave_store2,
        0x03: slave_store3,
        0x04: slave_store4,
    }

    context = ModbusServerContext(slaves=slaves, single=False)

    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = '2.2.0'

    # RTU:
    StartSerialServer(context, framer=ModbusRtuFramer, identity=identity, port='/dev/ttyUSB0', timeout=4, baudrate=115200, stopbits=1, bytesize=8, parity='N') 

if __name__ == "__main__":
    run_server()

server/slave上的python版本是:

$ python3 --version
Python 3.5.2

我用这个命令开始它:

$ python3 pymodbus_sync_serv_example_2019.07.05-1316.py

我在 Raspberry Pi 3 / BananaPi 上有以下主/客户端:

#!/usr/bin/env python

import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

UNIT = 0x1

def run_sync_client():

    client = ModbusClient(method='rtu', port='/dev/ttyS2', timeout=4, baudrate=115200, stopbits=1, bytesize=8, parity='N')

    print(client)

    client.connect()

    log.debug("===================================")
    log.debug("Read input registers")
    log.debug("")
    rr = client.read_input_registers(1, 2, unit=3)
    print(rr)

    client.close()

if __name__ == "__main__":
    #for _ in range(10):
    run_sync_client()

测试和分析

我尝试过 Raspberry Pi 3 和 BananaPi。结果相同。

我尝试了波特率 = 9600、38400,现在是 115200。

正如您在代码中看到的那样,超时时间已经很高了。

服务器/从属的日志:

2019-07-07 13:35:00,333 MainThread      DEBUG    sync           :45       Client Connected [/dev/ttyUSB0:/dev/ttyUSB0]
2019-07-07 13:35:00,333 MainThread      DEBUG    sync           :522      Started thread to serve client
2019-07-07 13:35:08,341 MainThread      DEBUG    rtu_framer     :180      Getting Frame - 0x4 0x0 0x1 0x0 0x2
2019-07-07 13:35:08,341 MainThread      DEBUG    factory        :137      Factory Request[ReadInputRegistersRequest: 4]
2019-07-07 13:35:08,341 MainThread      DEBUG    rtu_framer     :115      Frame advanced, resetting header!!
2019-07-07 13:35:08,342 MainThread      DEBUG    context        :64       validate: fc-[4] address-2: count-2
2019-07-07 13:35:08,342 MainThread      DEBUG    context        :78       getValues fc-[4] address-2: count-2
2019-07-07 13:35:08,342 MainThread      DEBUG    sync           :143      send: [ReadRegisterResponse (2)]- b'030404000500050846'

上面的服务器/从机只是在最后一行日志之后用闪烁的光标等待......

主/客户端日志:

ModbusSerialClient(rtu baud[115200])
2019-07-07 13:35:04,428 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:165      ===================================
2019-07-07 13:35:04,429 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:166      Read input registers
2019-07-07 13:35:04,430 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:167      
2019-07-07 13:35:04,430 MainThread      DEBUG    transaction    :111      Current transaction state - IDLE
2019-07-07 13:35:04,430 MainThread      DEBUG    transaction    :116      Running transaction 1
2019-07-07 13:35:04,431 MainThread      DEBUG    transaction    :215      SEND: 0x3 0x4 0x0 0x1 0x0 0x2 0x21 0xe9
2019-07-07 13:35:04,431 MainThread      DEBUG    sync           :73       New Transaction state 'SENDING'
2019-07-07 13:35:04,432 MainThread      DEBUG    transaction    :224      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-07-07 13:35:08,439 MainThread      DEBUG    transaction    :234      Transaction failed. (Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)) 
2019-07-07 13:35:08,440 MainThread      DEBUG    rtu_framer     :235      Frame - [b''] not ready
2019-07-07 13:35:08,441 MainThread      DEBUG    transaction    :390      Getting transaction 3
2019-07-07 13:35:08,442 MainThread      DEBUG    transaction    :189      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)

master/client上的python版本是:

$ python3 --version
Python 3.5.2

我用这个命令开始它:

$ python3 pymodbus_sync_client_example_2019.07.05-1319.py

Raspberry/BananaPi 上 /dev 的权限是:

$ ls -l /dev/ttyS*
crw--w---- 1 root tty     249, 0 Jul  7 11:21 /dev/ttyS0
crw-rw---- 1 root dialout 249, 1 Jul  7 11:22 /dev/ttyS1
crw-rw---- 1 root dialout 249, 2 Jul  7 13:35 /dev/ttyS2
crw-rw---- 1 root dialout 249, 3 Jul  7 11:20 /dev/ttyS3

在笔记本电脑上的服务器/从机上:

$ ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Jul  7 13:35 /dev/ttyUSB0

我尝试使用 RS485 协议发送简单数字。它们可以从 master/Raspberry/BananaPi 发送到笔记本电脑,但不能反过来。

我的设备权限设置是否错误?...

我做错了什么?...

我错过了什么?...

由于 RS485 只能以一种方式工作,我认为 pymodbus 不是问题(?)...(我的逻辑是 pymodbus 建立在 RS485 标准中,如果 RS485 的底层不起作用, pymodbus 不会。这个假设正确吗?)

我知道有些人在谈论 Raspberry Pi 的引脚为 3.3V,不适用于 5V 引脚单元。尽管如此,所有教程似乎都忽略了这一事实并起作用。 - 或者他们只是假装它有效? TTL 规范表明所有高于 2.5V 的电压都将被视为 HIGH。所以在理论上,3.3V 应该没问题,就像教程建议的那样。

我故意没有在 tx/rx 线上连接任何用于上拉/下拉的电阻器。教程不建议使用它们。

我已经使用 modbus 温湿度传感器测试了笔记本电脑上的 RS85 适配器。这似乎完美无缺。所以这个事实指向 BananaPi/Raspberry Pi 和 RS485 适配器组合 + 软件 + 设置在某种程度上存在缺陷。

【问题讨论】:

    标签: python modbus rs485 pymodbus3 banana-pi


    【解决方案1】:

    首先,让我开始说很高兴回答这样一个精心设计的问题。不是每个人都花这么多精力来解释他们做了什么以及他们是如何做到的。你的问题在你读完后马上加一。

    现在解决您的问题。您错过了您遵循的教程中的一个非常重要的步骤。正如您所说的 Modbus 是半双工1,您只有两条线,并且只允许一个设备在总线上通话,因此您需要一种控制总线的方法,可以这么说.在您的 USB 转 RS485/422 电缆中,这是由电缆上的硬件自动为您完成的(特别是您的电缆使用具有 TXEN -TX 启用信号的无处不在的 FTDI 芯片,请参阅here更多详细信息),这就是您注意到电缆工作良好的原因。另一方面,你的 3 美元小收发器是可怜的兄弟,它甚至没有 UART,它只是一个单端到差分转换器。这就是为什么你需要提供一个 DE/~RE(驱动启用/不读取启用)信号,让可怜的家伙知道什么时候允许它控制总线。

    这是您没有从教程中得到的警告:

    重要提示:在向 RS-485 模块写入值之前,必须将引脚 DE 和 RE 设为高电平。

    这似乎很容易,但如果您认为 Modbus 是如何工作的……实际上并不那么容易。这行代码:

    rr = client.read_input_registers(1, 2, unit=3)
    

    如果您要成功与 RS485 半双工通信,应该做很多事情:控制总线(在您的设置中将 RE/~DE 信号设置为高电平),发送 Modbus 查询帧,请求两个在写完查询(在 3.5 个字符的时间之后)释放总线控制(现在将 RE/~DE 设置为低)并从从站读取答案之后,在 UNIT ID 3 上注册。

    正如我在上面已经提到的link 中解释的那样,这个问题有几种解决方案。我更喜欢的一个(更多的是硬件人)是通过硬件来做总线方向控制信号(最好的方法是让一个收发器通过硬件实现这个功能,比如this one,但在链接中你也会找到使用 555 计时器的 DIY 解决方案)。现在,如果您更喜欢软件方式,您有一些选择。您可以调整 pymodbus 以根据 Modbus 需求切换控制线(我引用的答案中包含一些链接),或者,如果您更喜欢开箱即用的解决方案使用 libmodbus

    如果您决定选择最后一个选项,您可以找到有关如何使用 Rpi 上的 GPIO 引脚构建和安装具有半双工支持的 lidmodbus 以及如果您想继续使用 Python 的所有详细信息,安装包装器并测试基本示例。还有几个示波器屏幕截图可以查看通过软件和硬件切换线路之间的区别。对于大多数内部或业余爱好者的目的,您应该能够使用软件切换,但我不相信它用于工业或更关键的应用程序。

    最后,我认为值得一一回答您的所有问题:

    由于 RS485 只能以一种方式工作,我不认为 pymodbus 是 问题(?)......(我的逻辑说 pymodbus 内置于 RS485 标准,如果 RS485 的底层不起作用, pymodbus 不会。这个假设正确吗?)

    嗯,是的,不是的,也许……正如您在上面所读到的,pymodbus 并不是真正的问题。它只是期望您或您的硬件处理控制谁访问总线的不那么次要的细节。我认为大多数人将这种库用于 Modbus TCP,所以这对大多数用户来说从来都不是问题。在一般 Modbus 场景中,PLC 通过 RS485 链路上的 Modbus RTU 与另一台设备通信,问题由硬件处理,因此您也不必担心。

    我知道有些人在谈论 Raspberry Pi 是 3.3V on 引脚,不适用于 5V 引脚单元。尽管如此 教程似乎忽略了这一事实并起作用。 - 或者他们只是假装 它有效吗? TTL 规范说所有高于 2.5V 的电压都将 接受为高。所以在理论上,3.3V 应该没问题,就像 教程建议。

    正确,MAX485 datahseet 指定了 VIH 和 VOL 的阈值,只要您使用 5V 作为收发器的电源,不同的逻辑电平就不会成为问题(在这种特殊情况下,请注意这不是一般性陈述,如果您混合逻辑级别,其他设备可能会失败或最终被破坏)。

    我故意没有在 tx/rx 线上连接任何电阻器 用于上拉/下拉。教程不建议使用它们。

    对于内部项目,您很可能不需要在总线上连接任何终端电阻。对于长途巴士(在设备可能相距数百米的工厂或设施中),您可能会担心这个问题。你的微型收发器实际上已经包含了这些终端电阻,所以最好不要增加更多的电阻。对于您的电缆,我没有足够的耐心找到手册(我不知道是否有手册;我有一条类似的电缆,唯一确定的方法是取下盖子并查看其引擎盖下)。

    一切就绪并运行后,请注意在您的客户端上:

    print(rr)
    

    应该是:

    print(rr.registers)
    

    如果你想要显示你读过的值。

    【讨论】:

    • 精彩的答案。深入的解释和严肃而好的解决方案建议。 - 我现在通过修改pymodbus来选择软件解决方案。只是为了得到一些快速的结果。有用。我一定会考虑你的硬件建议。
    • 你说 Modbus 是半双工协议....但是 AFAIK 这不是协议的一部分,是吗? AFAIK Modbus 是一种串行协议,如果它在 RS-232、RS-485 上运行,半双工或全双工没有区别,因为它只是定义了数据包将如何携带信息和验证。
    • 是的,你是对的,Modbus 是半双工 的说法可能有点难以消化。 Modbus 实际上并不关心总线上使用的传输模式,但如果您在全双工链路上构建 Modbus 网络,它将始终以半双工方式使用。正如我提到的somewhere else,将两线 RS485(实际上是三线:A、B 和 GND)替换为 RS232(三线、RX、TX 和 GND)将是解决总线缺失的一种方法我们非常熟悉的方向控制信号问题(可能的地方:短总线)
    【解决方案2】:

    正如上面的 Marcos G. 所建议的,我是否修改了 pymodbus 来控制所选择的 GPIO。

    我选择了软件解决方案,因为我现在只需要一些快速工作的东西,而无需订购新硬件并等待。我稍后会找到合适/更好的硬件。

    修改pymodbus的软件解决方案

    在“client”文件夹中找到“sync.py”文件,以修改设置的客户端/主控端。

    我在这里修改客户端/主控,因为那一侧的 RS485 硬件很“差”。如果您有两个“差”的硬件,您可能还需要修改服务器端。

    文件sync.py大概可以在

    中找到

    ~/.local/lib/python3.5/site-packages/pymodbus/client

    这可能会根据您使用的 python 版本而有所不同。我的现在是3.5。 “~/”部分表示它在您的主文件夹中。 “local”前面的点使文件隐藏为标准。在终端中,您也可以使用命令“ls -al”来显示隐藏文件。你的 Linux 发行版的图形用户界面肯定也能以某种方式显示隐藏文件。

    在“sync.py”文件的开头,添加如下代码:

    import RPi.GPIO as GPIO
    pin_de_re = 7
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(pin_de_re, GPIO.OUT, initial=GPIO.HIGH)
    

    这可能看起来像下面这样:

    more imports ...
    
    from pymodbus.transaction import ModbusSocketFramer, ModbusBinaryFramer
    from pymodbus.transaction import ModbusAsciiFramer, ModbusRtuFramer
    from pymodbus.client.common import ModbusClientMixin
    
    import RPi.GPIO as GPIO
    pin_de_re = 7
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(pin_de_re, GPIO.OUT, initial=GPIO.HIGH)
    
    # --------------------------------------------------------------------------- #
    # Logging
    # --------------------------------------------------------------------------- #
    import logging
    _logger = logging.getLogger(__name__)
    
    ...more code
    

    根据您的选择设置密码。我的控制引脚为 GPIO4 - 即 Raspberry Pi/BananaPi 中的引脚 7。

    接下来向下滚动并找到名为

    的部分
    # --------------------------------------------------------------------------- #
    # Modbus Serial Client Transport Implementation
    # --------------------------------------------------------------------------- #
    

    我修改了这一部分,因为我使用 Modbus RTU 并因此使用串行来传输数据。

    在那个部分你必须找到“发送”的定义:

        def _send(self, request):
            """ Sends data on the underlying socket
    

    在该函数中,找到以下行:

                size = self.socket.write(request)
    

    并用大头针的控制来拥抱它:

                _logger.debug("GPIO - Setting pin high")
                GPIO.output(pin_de_re, 1)
                time.sleep(.300)
                size = self.socket.write(request)
                time.sleep(.300)
                _logger.debug("GPIO - Setting pin low")
                GPIO.output(pin_de_re, 0)
    

    我使用'_logger.debug("GPIO - Setting pin high/low")'行的原因是我可以在终端的日志中看到程序执行这些事情,我可以放心如果他们被执行。如果他们没有出现在日志中,我已经在错误的地方完成了 - 或者其他什么......

    使用 time.sleep(.300) 的原因是为了让硬件有时间行动。 .300 是 0.3 秒。在这种情况下是一个很大的数字。

    当我使用上述解决方案时,我得到以下日志。

    从/服务器:

    2019-07-07 23:08:43,532 MainThread      DEBUG    sync           :45       Client Connected [/dev/ttyUSB0:/dev/ttyUSB0]
    2019-07-07 23:08:43,533 MainThread      DEBUG    sync           :522      Started thread to serve client
    2019-07-07 23:08:47,534 MainThread      DEBUG    rtu_framer     :232      Frame check failed, ignoring!!
    2019-07-07 23:08:47,535 MainThread      DEBUG    rtu_framer     :128      Resetting frame - Current Frame in buffer - 0x3 0x4 0x0 0x1 0x0 0x82
    2019-07-07 23:08:59,543 MainThread      DEBUG    rtu_framer     :180      Getting Frame - 0x4 0x0 0x1 0x0 0x2
    2019-07-07 23:08:59,544 MainThread      DEBUG    factory        :137      Factory Request[ReadInputRegistersRequest: 4]
    2019-07-07 23:08:59,544 MainThread      DEBUG    rtu_framer     :115      Frame advanced, resetting header!!
    2019-07-07 23:08:59,544 MainThread      DEBUG    context        :64       validate: fc-[4] address-2: count-2
    2019-07-07 23:08:59,544 MainThread      DEBUG    context        :78       getValues fc-[4] address-2: count-2
    2019-07-07 23:08:59,545 MainThread      DEBUG    sync           :143      send: [ReadRegisterResponse (2)]- b'030404000500050846'
    
    

    主/客户:

    ModbusSerialClient(rtu baud[115200])
    2019-07-07 23:08:55,839 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:165      ===================================
    2019-07-07 23:08:55,840 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:166      Read input registers
    2019-07-07 23:08:55,841 MainThread      DEBUG    pymodbus_sync_client_example_2019.07.05-1319:167      
    2019-07-07 23:08:55,842 MainThread      DEBUG    transaction    :111      Current transaction state - IDLE
    2019-07-07 23:08:55,842 MainThread      DEBUG    transaction    :116      Running transaction 1
    2019-07-07 23:08:55,843 MainThread      DEBUG    transaction    :215      SEND: 0x3 0x4 0x0 0x1 0x0 0x2 0x21 0xe9
    2019-07-07 23:08:55,843 MainThread      DEBUG    sync           :79       New Transaction state 'SENDING'
    2019-07-07 23:08:55,844 MainThread      DEBUG    sync           :538      GPIO - Setting pin high
    2019-07-07 23:08:55,845 MainThread      DEBUG    sync           :541      GPIO - Setting pin low
    2019-07-07 23:08:55,845 MainThread      DEBUG    transaction    :224      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
    2019-07-07 23:08:59,516 MainThread      DEBUG    transaction    :300      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
    2019-07-07 23:08:59,518 MainThread      DEBUG    transaction    :229      RECV: 0x3 0x4 0x4 0x0 0x5 0x0 0x5 0x8 0x46
    2019-07-07 23:08:59,519 MainThread      DEBUG    rtu_framer     :180      Getting Frame - 0x4 0x4 0x0 0x5 0x0 0x5
    2019-07-07 23:08:59,519 MainThread      DEBUG    factory        :266      Factory Response[ReadInputRegistersResponse: 4]
    2019-07-07 23:08:59,520 MainThread      DEBUG    rtu_framer     :115      Frame advanced, resetting header!!
    2019-07-07 23:08:59,521 MainThread      DEBUG    transaction    :379      Adding transaction 3
    2019-07-07 23:08:59,522 MainThread      DEBUG    transaction    :390      Getting transaction 3
    2019-07-07 23:08:59,522 MainThread      DEBUG    transaction    :189      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
    ReadRegisterResponse (2)
    

    传输可能并非总是如此,但它可以查明问题的原因,从而找出潜在的解决方案。

    我还不知道我最终会得到什么。更稳定的硬件是肯定的。

    关于修改pymodbus或其他软件关于这个问题,我想引用from the following post in another threat

    任何在多任务操作系统(如 linux 或 windows)上运行 modbus 的人都会 永远无法满足串行规范的要求,没有 对此进行辩论,任务通常是 10 毫秒,因此满足 3.5 毫秒的时间 要求只是不适合而且永远不会。

    最好是硬件方面的解决方案。

    感谢马科斯 G。

    【讨论】:

    • 我很高兴你设法接近了一个可行的解决方案安德烈亚斯。也许您可以提高解决方案的可靠性,从而减少延迟。对于 Modbus,您需要等待 3.5 个字符的时间,也就是说,以您使用的波特率 (115.2kbps) 大约 0.3 毫秒(我认为您引用的 3.5 微秒是原始帖子中的错字)。如果您将等待时间减少到 0.5 毫秒,那么一切都应该非常可靠地工作。至少那是我在玩 pymodbus 时得到的。我
    • 如果您以阻塞方式进行 pyserial 工作,您甚至可以完全放弃延迟(直到所有数据都写入总线上,端口才会返回;这在 Rpi 上应该是可能的,但这不是大多数 USB 适配器的选项,这就是为什么我 modified 我的 FTDI 电缆使用 TXEN 信号)。如果您查看我上面链接的原始帖子,您可以看到使用 libmodbus 捕获的范围我从最后一个边缘到总线被释放有 500-600 微秒的延迟。
    • 但是,正如您所说,在这种情况下,硬件解决方案会更加可靠。这个软件技巧足以让我们在家里玩,但任何专业的 Modbus 实现都可以用硬件来完成。或者可能使用硬实时操作系统。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多