python - Modbus错误: [Invalid Message] Incomplete message received,至少应为2个字节(已收到0个)

标签 python modbus rs485 pymodbus3 banana-pi

问题

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()

服务器/从属服务器上的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。结果相同。

我已经尝试过baudrate = 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协议(protocol)发送简单号码。它们可以从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适配器组合+软件+设置的缺陷。

最佳答案

首先,让我开始说,很高兴回答这个问题。并不是每个人都花很多精力来解释自己做了什么以及如何做。阅读完之后,您的问题就是一个加号问题。

现在有您的问题。您错过了本教程中非常重要的一步。就像您说的Modbus是半双工1一样,您只有两根电线,并且只允许一个设备在总线上进行通话,因此,您需要一种控制总线的方法。在USB到RS485/422电缆中,电缆上的硬件会自动为您完成此操作(尤其是您的电缆使用具有TXEN -TX enable-信号的无处不在的FTDI芯片,有关更多详细信息,请参见here。 ),这就是为什么您注意到电缆工作良好的原因。另一方面,您的小型3 $收发器是可怜的兄弟,它甚至没有UART,它只是一个单端至差分转换器。这就是为什么您需要提供DE/〜RE(驱动器启用/未读取启用)信号的原因,以使可怜的人知道何时允许其控制总线。

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

IMPORTANT: Before writing values to the RS-485 module the pins DE & RE must be made HIGH.



这似乎很容易,但是如果您认为Modbus的工作原理……实际上并不是那么容易。这行代码:
rr = client.read_input_registers(1, 2, unit=3)

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

正如我在上面已经提到的link中解释的那样,针对此问题有几种解决方案。我更喜欢(更像是一个硬件专家)是通过硬件执行总线方向控制信号(最好的方法是让一个收发器具有此功能,该功能由硬件实现,例如this one,但是在链接中您还会找到一个使用555计时器进行DIY解决方案)。现在,如果您更喜欢以软件方式进行操作,则可以有一些选择。您可以根据MODBUS的需要(在我引用的答案中包含一些链接)来调整pymodbus来切换控制线,或者,如果您更喜欢现成的解决方案,请使用libmodbus。

如果决定使用最后一个选项,则可以找到有关如何使用Rpi上的GPIO引脚构建和安装具有半双工支持的lidmodbus的所有详细信息;如果要保留在Python上,请安装包装器并测试基本示例。还有一些示波器屏幕截图,以查看通过软件与硬件切换生产线之间的区别。对于大多数公司内部或业余爱好者而言,您应该能够使用该软件进行切换,但是对于工业或更重要的应用程序,我不信任它。

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

As RS485 only works in the one way, I do not think that pymodbus is the problem (?)... (My logic says that pymodbus builds in the RS485 standard, and if that underlying layer of RS485 does not work, pymodbus will not. Is that assumption correct?)



是的,是的,不是,也许...正如您在上面阅读的,pymodbus并不是真正的问题。只是希望您或您的硬件能处理控制谁访问总线的琐碎细节。我认为大多数人都将这种库用于Modbus TCP,因此对于大多数用户而言,这绝不是问题。在一般的Modbus方案中,您的PLC通过RS485链路上的Modbus RTU与另一设备通信,该问题由硬件解决,因此您也不必担心。

I know some people are talking about that the Raspberry Pi is 3.3V on the pins and does not work with 5V pin-units. Despite that does all tutorials seem to ignore that fact and work. - Or are they just faking that it works? The TTL specifications say that all above 2.5V will be accepted as HIGH. SO in THEORY, 3.3V should be OK, just as the tutorials suggest.



正确,MAX485 datahseet指定了VIH和VOL的阈值,只要您使用5V作为收发器的电源,就不会出现不同的逻辑电平(在这种情况下,请注意,这不是一般说来,如果您混用逻辑电平,其他设备可能会发生故障或最终被破坏)。

I have by purpose yet not attached any resistors on the tx/rx wires for pull up/down. The tutorials don't suggest them.



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

一旦一切就绪并开始运行,请在客户端上注意以下几点:
print(rr)

应该:
print(rr.registers)

如果要显示已读的值。

关于python - Modbus错误: [Invalid Message] Incomplete message received,至少应为2个字节(已收到0个),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56922031/

相关文章:

python - 使用 Python 抓取 RSS 提要

python - 在树莓派3中使用XBee python库出错

Python:如何更新程序中的模块

modbus - 自动发现网络中 modbus 设备的从站 ID

.net - 从轮询更改为事件的最佳方式是什么?

python - 如何用另一个数据框元素替换数据框元素

python - 无法通过 485 使用 python 读取 Modbus 的寄存器

c++ - Wince 6.0 串口配置

c# - RS485为什么会返回发送的数据?

python - 树莓派 RS485/Uart Modbus