我目前有一个带有3个Raspberry Pi的RS-485总线,每个Raspberry Pi运行pymodbus(同步-1个主/客户端和2个从/服务器)。将命令发送到每个服务器的单独地址正在按预期方式工作。但是,到地址0的广播消息在服务器上给出了无效的unit id调试消息,因此每个服务器都丢弃来自主服务器的命令。通过阅读以前的问题和文档,我尝试将enable_broadcast常量添加到每个配置中,但是没有成功。我的配置有问题吗?
从站1
#!/usr/bin/env python
import serial
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
# --------------------------------------------------------------------------- #
# Logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = (' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_server():
block = ModbusSequentialDataBlock(0, [17]*100)
slave = {
0x01: ModbusSlaveContext(di=block, co=block, hr=block, ir=block),
}
context = ModbusServerContext(slaves=slave, single=False)
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'URL'
identity.ProductName = '#1 Test Slave'
identity.ModelName = 'Research Testbed'
identity.MajorMinorRevision = '2.3.0'
StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
port='/dev/ttySC0', timeout=1, baudrate=115200, enable_broadcast=True)
if __name__ == "__main__":
run_server()
从站2:
#!/usr/bin/env python
import serial
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
# --------------------------------------------------------------------------- #
# Logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = (' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_server():
block = ModbusSequentialDataBlock(0, [17]*100)
slave = {
0x0A: ModbusSlaveContext(di=block, co=block, hr=block, ir=block, zero_mode=True),
}
context = ModbusServerContext(slaves=slave, single=False)
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'URL'
identity.ProductName = '#3 Test Slave'
identity.ModelName = 'Research Testbed'
identity.MajorMinorRevision = '2.3.0'
StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
port='/dev/ttySC0', timeout=1, baudrate=115200, enable_broadcast = True)
if __name__ == "__main__":
run_server()
大师:
#!/usr/bin/env python
import serial
from pymodbus.repl.client import ModbusSerialClient as ModbusClient
import logging
#-------------------------------------------#
# Logging
#-------------------------------------------#
FORMAT = ('%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
#-------------------------------------------#
# Client Initialization
#-------------------------------------------#
client = ModbusClient(method='rtu', port='/dev/ttySC0', timeout=2,
baudrate=115200, broadcast_enable=True)
#-------------------------------------------#
# Command Interface
#-------------------------------------------#
def command_switch(UNIT , command):
#Command Switch
if command == 1:
print(client.read_device_information(read_code=0x02, object_id=4, unit = UNIT))
elif command == 2:
print(client.read_coils(0,8, unit=UNIT))
elif command == 3:
response = client.read_holding_registers(0,4,unit=UNIT)
print(response)
elif command == 4:
start = input("Enter starting address of coil (0-7 available): ")
number = input("Select number of coils to write: ")
value = input("Enter value to write (0-False, 1-True): ")
write_coil(UNIT, start, number, value)
elif command == 5:
rq = client.write_registers(1, [2, 3, 4], unit = 0)
assert(not rq.isError())
def write_coil(UNIT, start, number, value):
#Verify coil address is valid
if (start + number) >= 8:
number = start + number % 8;
#Write intended value
holder = False
if value:
holder = True
#if multiple use write_coils else write_coil
if number > 1:
rq = client.write_coils(start, [holder]*number, unit=UNIT)
else:
rq = client.write_coil(start, holder, unit=UNIT)
if __name__ == "__main__":
client.connect()
command = 0
while command != 8:
command = input("Enter Command to run:\n1. Device ID\n2. Read Coils\n3. Read Register\n4. Write Coils\n8. Exit\n")
if command != 8:
UNIT = input("Enter Device Address:")
command_switch(UNIT, command)
client.close()
从站1-地址1
DEBUG sync :46 Client Connected [/dev/ttySC0:/dev/ttySC0]
DEBUG sync :580 Started thread to serve client
DEBUG rtu_framer :180 Getting Frame - 0x5 0x0 0x0 0xff 0x0
DEBUG factory :137 Factory Request[WriteSingleCoilRequest: 5]
DEBUG rtu_framer :115 Frame advanced, resetting header!!
DEBUG context :64 validate: fc-[5] address-1: count-1
DEBUG context :90 setValues[5] 1:1
DEBUG context :78 getValues fc-[5] address-1: count-1
DEBUG sync :144 send: [WriteCoilResponse(0) => 1]- 01050000ff008c3a
DEBUG rtu_framer :229 Not a valid unit id - 10, ignoring!!
DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0xa 0x1 0x0 0x0 0x0 0x8 0x3c 0xb7
DEBUG rtu_framer :232 Frame check failed, ignoring!!
DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0xa 0x1 0x1 0xff 0x13 0xec
DEBUG rtu_framer :229 Not a valid unit id - 0, ignoring!!
DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x0 0x5 0x0 0x0 0xff 0x0 0x8d 0xeb
从站2:地址为10
DEBUG sync :46 Client Connected [/dev/ttySC0:/dev/ttySC0]
DEBUG sync :580 Started thread to serve client
DEBUG rtu_framer :229 Not a valid unit id - 1, ignoring!!
DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x5 0x0 0x0 0xff 0x0 0x8c 0x3a 0x1 0x5 0x0 0x0 0xff 0x0 0x8c 0x3a
DEBUG rtu_framer :180 Getting Frame - 0x1 0x0 0x0 0x0 0x8
DEBUG factory :137 Factory Request[ReadCoilsRequest: 1]
DEBUG rtu_framer :115 Frame advanced, resetting header!!
DEBUG context :64 validate: fc-[1] address-0: count-8
DEBUG context :78 getValues fc-[1] address-0: count-8
DEBUG sync :144 send: [ReadBitResponse(8)]- 0a0101ff13ec
DEBUG rtu_framer :229 Not a valid unit id - 0, ignoring!!
DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x0 0x5 0x0 0x0 0xff 0x0 0x8d 0xeb
大师:
Enter Command to run:
1. Device ID
2. Read Coils
3. Read Register
4. Write Coils
8. Exit
4
Enter Device Address:1
Enter starting address of coil (0-7 available): 0
Select number of coils to write: 1
Enter value to write (0-False, 1-True: 1
DEBUG transaction :115 Current transaction state - IDLE
DEBUG transaction :120 Running transaction 1
DEBUG transaction :219 SEND: 0x1 0x5 0x0 0x0 0xff 0x0 0x8c 0x3a
DEBUG sync :75 New Transaction state 'SENDING'
DEBUG transaction :228 Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG transaction :304 Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG transaction :233 RECV: 0x1 0x5 0x0 0x0 0xff 0x0 0x8c 0x3a
DEBUG rtu_framer :180 Getting Frame - 0x5 0x0 0x0 0xff 0x0
DEBUG factory :266 Factory Response[WriteSingleCoilResponse: 5]
DEBUG rtu_framer :115 Frame advanced, resetting header!!
DEBUG transaction :383 Adding transaction 1
DEBUG transaction :394 Getting transaction 1
DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Enter Command to run:
1. Device ID
2. Read Coils
3. Read Register
4. Write Coils
8. Exit
2
Enter Device Address:10
DEBUG transaction :115 Current transaction state - TRANSACTION_COMPLETE
DEBUG transaction :120 Running transaction 2
DEBUG transaction :219 SEND: 0xa 0x1 0x0 0x0 0x0 0x8 0x3c 0xb7
DEBUG rtu_framer :264 Changing state to IDLE - Last Frame End - 1595270440.95, Current Time stamp - 1595270458.87
DEBUG sync :75 New Transaction state 'SENDING'
DEBUG transaction :228 Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG transaction :304 Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG transaction :233 RECV: 0xa 0x1 0x1 0xff 0x13 0xec
DEBUG rtu_framer :180 Getting Frame - 0x1 0x1 0xff
DEBUG factory :266 Factory Response[ReadCoilsResponse: 1]
DEBUG rtu_framer :115 Frame advanced, resetting header!!
DEBUG transaction :383 Adding transaction 10
DEBUG transaction :394 Getting transaction 10
DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
{u'function_code': 1, u'bits': [True, True, True, True, True, True, True, True]}
Enter Command to run:
1. Device ID
2. Read Coils
3. Read Register
4. Write Coils
8. Exit
4
Enter Device Address:0
Enter starting address of coil (0-7 available): 0
Select number of coils to write: 1
Enter value to write (0-False, 1-True: 1
DEBUG transaction :115 Current transaction state - TRANSACTION_COMPLETE
DEBUG transaction :120 Running transaction 3
DEBUG transaction :219 SEND: 0x0 0x5 0x0 0x0 0xff 0x0 0x8d 0xeb
DEBUG rtu_framer :264 Changing state to IDLE - Last Frame End - 1595270459.16, Current Time stamp - 1595270469.71
DEBUG sync :75 New Transaction state 'SENDING'
DEBUG transaction :223 Changing transaction state from 'SENDING' to 'TRANSACTION_COMPLETE'
Traceback (most recent call last):
File "modbus-client-rtu.py", line 62, in <module>
command_switch(UNIT, command)
File "modbus-client-rtu.py", line 36, in command_switch
write_coil(UNIT, start, number, value)
File "modbus-client-rtu.py", line 52, in write_coil
rq = client.write_coil(start, holder, unit=UNIT)
File "/home/pi/.local/lib/python2.7/site-packages/pymodbus/repl/client.py", line 112, in write_coil
if not resp.isError():
AttributeError: 'str' object has no attribute 'isError'
答案 0 :(得分:0)
答案很简单,在从属设置中,我的设置为“ enable_broadcast = True”,而不是“ broadcast_enable = True”。可以发现此pull request on the github对单个设置的良好测试。一旦纠正此问题,从站将响应广播消息。需要将“ ignore_missing_slaves”设置为true以防止从属设备响应每个请求,因此从属设备的设置现在显示:
block = ModbusSequentialDataBlock(0, [17]*100)
slave = {
0x0A: ModbusSlaveContext(di=block, co=block, hr=block, ir=block),
}
context = ModbusServerContext(slaves=slave, single=False)
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'URL'
identity.ProductName = '#3 Test Slave'
identity.ModelName = 'Research Testbed'
identity.MajorMinorRevision = '2.3.0'
StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,ignore_missing_slaves=True,
port='/dev/ttySC0', timeout=0.1, baudrate=115200, broadcast_enable=True)
对于主调试错误“ AttributeError:'str'对象没有属性'isError'”。似乎广播支持尚未在repl客户端中实现。对于我的特定应用程序,我希望能够读取从设备信息以及发送广播消息,因此我正在使用两个客户端,并在必要时在主代码中进行连接:
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.repl.client import ModbusSerialClient as IdClient
#-------------------------------------------#
# Client Initialization
#-------------------------------------------#
client = ModbusClient(method='rtu', port='/dev/ttySC0', timeout=2,
baudrate=115200, broadcast_enable=True)
# Client sync has not implemented the device ID, may put in a request for broad$
# support on repl client
idClient = IdClient(method='rtu', port='/dev/ttySC0', timeout=2,
baudrate=115200, broadcast_enable=True)
client.connect()
在请求设备标识时:
client.close()
time.sleep(0.1)
idClient.connect()
print(idClient.read_device_information(read_code=0x02, object_id=4, unit=UNIT)
idClient.close()
time.sleep(0.1)
client.connect()
我很可能会在github上发布一个问题,要求在repl客户端上提供广播支持。