Modbus错误:[无效消息]接收到的消息不完整,预期至少2个字节(接收到0个字节) [英] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)

查看:2340
本文介绍了Modbus错误:[无效消息]接收到的消息不完整,预期至少2个字节(接收到0个字节)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

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与笔记本适配器互换外:

服务器/从属服务器上的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协议发送简单号码.可以将它们从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是半双工 此处(了解更多详细信息),这就是为什么您注意到电缆运行良好的原因.另一方面,您的小型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设置为低电平)并从从机读取答案.

正如我在上面已经提到的链接中所述,有几种解决方案.我更喜欢(更像是硬件专家)是通过硬件执行总线方向控制信号(最好的方法是让收发器具有由硬件实现的此功能,例如 datahseet 指定了VIH和VOL的阈值,并且只要将5V用作收发器的电源,就不会出现不同的逻辑电平(在这种情况下,请注意,这不是一般性的陈述,如果混用,其他设备可能会发生故障或最终毁坏逻辑级别).

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

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

一旦一切正常运行,请注意客户端上的内容:

print(rr)

应该是:

print(rr.registers)

如果要显示已读取的值.

Problem

pymodbus master/client can send a request to the slave/server. The slave/server make the things to return ready and is waiting for the master/client to pick them up. Despite the readiness of the server/slave, the master/client just returns the error "Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)".

Setup

I use the laptop as server/slave with this adapter: https://www.amazon.com/dp/B076WVFXN8/ref=twister_B076X1BS4H?_encoding=UTF8&psc=1

I have an Raspberry Pi 3 / BananaPi as master/client with this adapter attached: https://www.aliexpress.com/item/32781613765.html?spm=a2g0s.9042311.0.0.1aec4c4d0EXx8M

I am following most of this tutorial for the setup, except for the Arduino is swapped with the laptop adapter: https://circuitdigest.com/microcontroller-projects/rs485-serial-communication-between-arduino-and-raspberry-pi - pin connections for the Raspberry is as in the tutorial.

I have this program as server/slave on my laptop:

#!/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()

The python version on server/slave is:

$ python3 --version
Python 3.5.2

And I start it with this command:

$ python3 pymodbus_sync_serv_example_2019.07.05-1316.py

I have the following as master/client on the 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()

Test and analyse

I have tried Raspberry Pi 3 as well as BananaPi. Same results.

I have tried baudrate= 9600, 38400 and now 115200.

timeout is already high as you can see in the code.

Logs for server/slave:

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'

The above server/slave just waits with a blinking curser after this last log line...

Logs for master/client:

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)

The python version on master/client is:

$ python3 --version
Python 3.5.2

And I start it with this command:

$ python3 pymodbus_sync_client_example_2019.07.05-1319.py

The rights for the /dev's on the Raspberry/BananaPi are:

$ 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

And on the server/slave on the laptop:

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

I have tried to send simple numbers with RS485 protocol. They can be send from master/Raspberry/BananaPi to the laptop, but not the other way around.

Have I the wrong rights settings for the devices?...

What am I doing wrong?...

What am I missing?...

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

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.

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

I have tested the RS85 adapter sitting on the laptop with a modbus temperature-humidity sensor. This seems to work flawless. So this fact points in direction of BananaPi/Raspberry Pi and the RS485 adapter combination + software + settings to be flawed somehow.

解决方案

First off, let me start saying it's a pleasure to answer such a well laid down question. Not everyone puts so much effort on explaining what they did and how they did it. Yours is a plus one question right after you finish reading it.

Now with your problem. You missed one very important step on the tutorial you followed. As you say Modbus is half-duplex1, 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 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.

This is the warning you did not take from the tutorial:

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

That seems easy enough, but if you think how Modbus works... it's actually not so easy. This line of code:

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

should be doing quite a number of things if you are to communicate with RS485 half-duplex successfully: take control of the bus (in your setup setting the RE/~DE signal high), send the Modbus query frame asking for two registers on UNIT ID 3, right after finishing writing the query (after 3.5 characters' time) release control of the bus (now setting RE/~DE low) and reading the answer from the slave.

As I explain in the link I already referred to above, there are several solutions to this problem. My preferred one (being more of a hardware guy) is doing the bus direction control signal by hardware (the best way is to have a transceiver that has this function implemented by hardware, like this one, but in the link you'll also find a DIY solution using a 555 timer). Now, if you prefer to do it the software way, you have some choices. You can tweak pymodbus to toggle the control line according to the Modbus needs (there are some links included in the answer I've quoted) or, if you prefer a more out-of-the-box solution use libmodbus.

If you decide for this last option, you can find all details on how to build and install lidmodbus with half-duplex support using the GPIO pins on the Rpi and if you want to stay on Python, install the wrapper and test the basic example. There are also a couple of scope screenshots to see the difference between toggling the line via software vs. hardware. For most in-house or hobbyist purposes, you should be able to use the software toggling but I would not trust it for industrial or more critical applications.

To finish off, I think it's worthwhile answering all your questions one by one:

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

Well, yes and no and maybe... As you read above, pymodbus is not really the problem. It is just expecting for you or your hardware to take care of the not so minor detail of controlling who accesses the bus. I think most people use this kind of library for Modbus TCP so this is never a problem for most users. In a general Modbus scenario where you have a PLC talking to another device via Modbus RTU on an RS485 link, the problem is dealt with by hardware, so you wouldn't have to worry about it either.

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.

Correct, the MAX485 datahseet specifies the threshold values for VIH and VOL and as long as you use 5V for the power supply of your transceivers, the different logic levels won't be an issue (in this particular case, note that this is not a general statement, other devices might fail or end up destroyed if you mix logic levels).

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

Most likely you won't need to attach any terminating resistors to the bus for an in-house project. For long buses (in a factory or facility where devices can be hundreds of meters apart) you would probably worry about this issue. Your tiny transceiver actually has these terminating resistors already included so on its side, better not to add more resistance. For your cable, I did not have enough patience to find a manual (I don't know if there is one; I have a similar cable and the only way to be sure was to remove the cover and look under its hood).

Once you have everything up and running note that on your client:

print(rr)

Should be:

print(rr.registers)

If what you want is to show the values you have read.

这篇关于Modbus错误:[无效消息]接收到的消息不完整,预期至少2个字节(接收到0个字节)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆