当主站请求保存Arduino从站的注册表值时,如何解决Pymodbus异常

How to solve Pymodbus exception when Master requests for holding registry values of the Arduino Slave

本文关键字:何解决 解决 异常 Pymodbus 保存 请求 主站 Arduino 从站 注册表      更新时间:2023-10-16

我目前正在尝试从我的Arduino从站接收数据到我的计算机。我成功地创建了一个Arduino奴隶。但是,当我尝试使用Pymodbus库从计算机接收数据时,我的代码无法从Arduino接收数据并引发ModbusIOException。对于我的项目的规格,我正在尝试使用Arduino构建一个Modbus RTU,以模拟以随机数作为读数的传感器。Arduino代码使用Andre Sarmento的Modbus-Arduino库。

https://github.com/andresarmento/modbus-arduino

我已经检查了我的Arduino奴隶是否正常工作。我尝试通过Modbus Master模拟器(QModMaster)读取数据,它工作得很好。这可能证明问题本身来自主控码的代码。此外,串行连接似乎工作正常,因为 self.client.connect() 返回 True。

这些是QModMaster配置的屏幕截图。

从属配置 串行端口配置

主站的 Python 代码:

class ModbusRTU:
def __init__(self, graph_name, port, baudrate=9600, 
stopbits=1, bytesize=8, parity='N', 
timeout=1):                                       
self.graph_name = graph_name
self.client = ModbusSerialClient(method='rtu', 
port=port, 
baudrate=baudrate, 
parity=parity, 
timeout=timeout)
self.connection = self.client.connect()
result = self.client.read_holding_registers(address=0, 
count=2, 
unit=1)
print(result.registers) 
if __name__ == '__main__':
modbus = ModbusRTU(graph_name='/dev/ttyACM0', 
port='/dev/ttyACM0', baudrate=9600, 
stopbits=1, bytesize=8, parity='N', 
timeout=1)
print(modbus.check_connection())

模拟从站和传感器的 Arduino 代码:

#include <Modbus.h>
#include <ModbusSerial.h>
ModbusSerial mb;
const int READING = 0;
const int DECIMAL = 1;
void setup() {
mb.config(&Serial, 9600, SERIAL_8N1);
mb.setSlaveId(1);
mb.addHreg(READING);
mb.addHreg(DECIMAL);
}
void loop() {
mb.task();
mb.Hreg(READING, random(1, 201));
mb.Hreg(DECIMAL, random(0, 4));
}

在打印results.registers时,它应该是一个整数列表。但是,它只是引发一个ModbusIOException,其中包含以下消息:

'ModbusIOException' object has no attribute 'registers'
File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 21, in __init__
print(result.registers)
File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 29, in <module>
timeout=1)

它也给出了这个信息。

Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)

print(result.registers)之前,请尝试以下代码片段:

if not result.isError():
print(result.registers)
else:
print("error: {}".format(result))

另外,填写其他ModbusSerialClient()参数。

以下是代码片段的更新:

from pymodbus.client.sync import ModbusSerialClient
class ModbusRTU:
def __init__(self, graph_name, port, baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1):
self.graph_name = graph_name
self.client = ModbusSerialClient(
method='rtu',
port=port,
baudrate=baudrate,
parity=parity,
timeout=timeout,
stopbits=stopbits,
bytesize=bytesize
)
self.connection = self.client.connect()
result = self.client.read_input_registers(address=1,
count=2,
unit=1)
if not result.isError():
print(result.registers)
else:
print("error: {}".format(result))
if __name__ == '__main__':
modbus = ModbusRTU(
graph_name='/dev/ttyACM0',
port='/dev/ttyACM0', baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1
)

您正在从站中定义保持寄存器,但您正在尝试将它们读取为输入寄存器,请尝试更改此行:

result = self.client.read_input_registers(address=1, count=2, unit=1)

自:

result = self.client.read_holding_registers(address=1, count=2, unit=1)

请注意,Modbus 规范定义了这两种不同类型的寄存器:保持和输入,具体取决于它们所在的内存区域。

我已经找到了解决方案,感谢少数人的帮助。QModMaster使用一个名为libmodbus的库。由于Arduino模拟从站和传感器与QModMaster配合使用,因此更改以前的库并使用libmodbus会更容易。幸运的是,libmodbus 有一个 python 等价物,它是 pylibmodbus。这是库 https://github.com/stephane/pylibmodbus 的链接。

from pylibmodbus import ModbusRtu

class ModbusRTU:
def __init__(self, port, baudrate=9600, databit=8, parity='None', 
stopbit=1, timeout=1000):
self.parity = {'Odd': 'O', 'Even': 'E', 'None': 'N'}
self.modbus = ModbusRtu(device=port.encode('ascii'),   
data_bit=databit, baud=baudrate,
parity=self.parity[parity] 
.encode('ascii'), 
stop_bit=stopbit)
self.modbus.set_response_timeout(timeout/1000)
self.modbus.connect()
self.modbus.set_slave(1)
result = self.modbus.read_registers(0, 2)
print(result)
self.modbus.close()

if __name__ == '__main__':
main = ModbusRTU('/dev/ttyACM0', baudrate=9600, databit=8, 
parity='None', stopbit=1)