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

时间:2019-07-07 12:09:19

标签: 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)

主服务器/客户端上的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适配器组合+软件+设置存在某种缺陷。

2 个答案:

答案 0 :(得分:5)

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

现在解决您的问题。您错过了本教程中非常重要的一步。正如您所说的,Modbus是半双工的,您只有两条线,并且只允许一个设备在总线上进行通话,因此,您需要一种控制总线的方法。在USB到RS485 / 422电缆中,电缆上的硬件会自动为您完成此操作(尤其是您的电缆使用具有TXEN -TX使能信号的无处不在的FTDI芯片,请参见{{3} }以获取更多详细信息),这就是为什么您注意到电缆运行良好的原因。另一方面,您的小型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设置为低电平)并从从机读取答案。

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

如果您决定使用最后一个选项,则可以找到有关如何使用Rpi上的GPIO引脚构建和安装具有半双工支持的 lidmodbus 的所有详细信息,以及是否希望继续使用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 this one指定了VIH和VOL的阈值,只要您使用5V作为收发器的电源,就不会出现不同的逻辑电平(在这种情况下) ,请注意,这不是一般性的陈述,如果您混用逻辑电平,其他设备可能会发生故障或最终被破坏。

  

我故意没有在TX / RX线上连接任何电阻器   用于上拉/下拉。这些教程不建议他们。

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

一旦一切就绪并运行,请注意客户端上的内容:

print(rr)

应该是:

print(rr.registers)

如果要显示已读取的值。

答案 1 :(得分:2)

就像上面的Marcos G.所建议的那样,我是否修改了pymodbus以控制所选的GPIO。

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

修改pymodbus的软件解决方案

在文件夹“ client”中找到文件“ sync.py”,以修改安装程序的客户端/主端。

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

可能在

中找到文件sync.py

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

这可能会因您使用的python版本而异。我的现在是3.5。 “〜/”部分表示它位于您的主文件夹中。 “本地”前面的点使文件作为标准隐藏。您是否可以在终端中使用命令“ 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-将引脚设置为高/低”)行的原因是,我随后可以在终端的日志中看到该程序执行了这些操作,因此可以放心如果执行。如果他们没有出现在日志中,那么我已经在错误的地方完成了-或其他...

使用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的任何人都会   永远无法满足串行规范的要求,没有   关于此的辩论,任务通常为10ms,因此满足3.5us时序   需求只是不合适,而且永远不会满足。

在硬件方面最好是一种解决方案。

感谢Marcos G。