Modbus 错误:[无效消息] 收到的消息不完整,预计至少 2 个字节(收到 0 个字节)

2024-03-16

Problem

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

Setup

我使用笔记本电脑作为带有此适配器的服务器/从属设备:

我有一个 Raspberry Pi 3 / BananaPi 作为主/客户端,并附有此适配器:https://www.aliexpress.com/item/32781613765.html?spm=a2g0s.9042311.0.0.1aec4c4d0EXx8M 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 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()

服务器/从站上的 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 的电压都将被视为高电平。所以从理论上讲,3.3V 应该没问题,正如教程所建议的那样。

我特意没有在 tx/rx 线上连接任何电阻来上拉/下拉。教程不建议它们。

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


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

Now with your problem. You missed one very important step on the tutorial you followed. As you say Modbus is half-duplex1 https://stackoverflow.com/questions/56922031/modbus-error-invalid-message-incomplete-message-received-expected-at-least-2/56923891?noredirect=1#comment103584852_56923891, you have only two wires and only one device is allowed to talk on the bus so you need a way to take control of the bus, so to speak. In your USB-to-RS485/422 cable, that is done automatically for you by the hardware on the cable (your cable, in particular, uses the ubiquitous FTDI chip that has a TXEN -TX enable- signal, see here https://stackoverflow.com/a/56106253/11476836 for more details), that's why you noticed the cable works well. On the other hand, your tiny 3$ transceiver is the poor brother, and it does not even have an UART, it's just a single-ended to differential converter. That's the reason you need to provide a DE/~RE (Drive Enable/Not Read Enable) signal for the poor guy to know when it is allowed to take control of the bus.

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

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

这看起来很简单,但如果您考虑一下 Modbus 的工作原理……实际上并不那么容易。这行代码:

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

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

正如我在link https://stackoverflow.com/a/56106253/11476836我在上面已经提到过,这个问题有几种解决方案。我的首选(更多的是硬件人员)是通过硬件执行总线方向控制信号(最好的方法是拥有一个由硬件实现此功能的收发器,例如this one https://rads.stackoverflow.com/amzn/click/com/B07BJJ7ZF8,但在链接中您还可以找到使用 555 定时器的 DIY 解决方案)。现在,如果您更喜欢通过软件方式完成此操作,您有一些选择。你可以调整pymodbus根据 Modbus 需求切换控制线(我引用的答案中包含一些链接),或者,如果您更喜欢开箱即用的解决方案,请使用libmodbus.

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

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

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

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

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

正确,MAX485数据表 https://ecee.colorado.edu/~mcclurel/max485ds.pdf指定 VIH 和 VOL 的阈值,只要您使用 5V 为收发器供电,不同的逻辑电平就不会成为问题(在这种特殊情况下,请注意,这不是一般性声明,其他设备如果混合逻辑级别,可能会失败或最终被破坏)。

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

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

一旦一切准备就绪并运行,请注意您的客户端:

print(rr)

应该:

print(rr.registers)

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Modbus 错误:[无效消息] 收到的消息不完整,预计至少 2 个字节(收到 0 个字节) 的相关文章

随机推荐