2

我有一个设置,在同一个串行端口上菊花链连接了 2 个 SDM120 kWh 电能表(将来我想添加一个 SDM630)。我在 MinimalModbus 通信中找到了“使用多种仪器”。我成功读取地址 1 上 SDM120 上的寄存器,但读取地址 2 时出现错误。错误:minimalmodbus.NoResponseError: No communication with the instrument (no answer).

我可以通过添加来解决它time.sleep(0.1),但我认为 RS485 允许在第一个地址完成后立即读取第二个地址的寄存器。我也尝试了较低的值,但是例如。time.sleep(0.01)还给了一个NoResponseError

我个人认为该设置instrument.serial.timeout = 1已经产生了预期的效果,但显然我真的需要time.sleep. time.sleep(0.1) 是正确的做法吗?如果是这样:我怎么知道最低值,所以我没有NoResponseError?试错?我的脚本可以优化吗?特别是当时间很重要时,例如。避免注入电网(光伏分流器,...)。提前致谢!

剧本:

#!/usr/bin/env python3
import minimalmodbus
import time

instrumentA = minimalmodbus.Instrument('/dev/ttyUSB0', 1, debug = True)  # port name, slave address (in decimal)
instrumentA.serial.baudrate = 9600
instrumentA.serial.timeout  = 1          # seconds
instrumentA.serial.bytesize = 8
instrumentA.serial.parity   = minimalmodbus.serial.PARITY_NONE
instrumentA.serial.stopbits = 1
instrumentA.mode = minimalmodbus.MODE_RTU

instrumentB = minimalmodbus.Instrument('/dev/ttyUSB0', 2, debug = True)
instrumentB.mode = minimalmodbus.MODE_RTU

print ("====== SDM120 instrumentA on addres 1 ======")
print (instrumentA)
P = instrumentA.read_float(12, 4, 2)
print ("Active Power in Watts:", P)

#time.sleep(0.1)  #workaround to avoid NoResponseError 

print ("====== SDM120 instrumentB on addres 2 ======")
print (instrumentB)
P = instrumentB.read_float(12, 4, 2)
print ("Active Power in Watts:", P)

没有的输出time.sleep(0.1)

MinimalModbus debug mode. Create serial port /dev/ttyUSB0
MinimalModbus debug mode. Serial port /dev/ttyUSB0 already exists
====== SDM120 instrumentA on addres 1 ======
minimalmodbus.Instrument<id=0x7f36e3dc0df0, address=1, mode=rtu, close_port_after_each_call=False, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=True, serial=Serial<id=0x7f36e3dd90d0, open=True>(port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)>
MinimalModbus debug mode. Will write to instrument (expecting 9 bytes back): '\x01\x04\x00\x0c\x00\x02±È' (01 04 00 0C 00 02 B1 C8)
MinimalModbus debug mode. Clearing serial buffers for port /dev/ttyUSB0
MinimalModbus debug mode. No sleep required before write. Time since previous read: 190954.73 ms, minimum silent period: 4.01 ms.
MinimalModbus debug mode. Response from instrument: '\x01\x04\x04\x00\x00\x00\x00û\x84' (01 04 04 00 00 00 00 FB 84) (9 bytes), roundtrip time: 53.3 ms. Timeout for reading: 1000.0 ms.

Active Power in Watts: 0.0
====== SDM120 instrumentB on addres 2 ======
minimalmodbus.Instrument<id=0x7f36e3c55940, address=2, mode=rtu, close_port_after_each_call=False, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=True, serial=Serial<id=0x7f36e3dd90d0, open=True>(port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)>
MinimalModbus debug mode. Will write to instrument (expecting 9 bytes back): '\x02\x04\x00\x0c\x00\x02±û' (02 04 00 0C 00 02 B1 FB)
MinimalModbus debug mode. Clearing serial buffers for port /dev/ttyUSB0
MinimalModbus debug mode. Sleeping 2.31 ms before sending. Minimum silent period: 4.01 ms, time since read: 1.70 ms.
MinimalModbus debug mode. Response from instrument: '' () (0 bytes), roundtrip time: 1001.3 ms. Timeout for reading: 1000.0 ms.

Traceback (most recent call last):
  File "./sdm120-daisychain_v3.py", line 25, in <module>
    P = instrumentB.read_float(12, 4, 2)
  File "/home/mattias/.local/lib/python3.8/site-packages/minimalmodbus.py", line 662, in read_float
    return self._generic_command(
  File "/home/mattias/.local/lib/python3.8/site-packages/minimalmodbus.py", line 1170, in _generic_command
    payload_from_slave = self._perform_command(functioncode, payload_to_slave)
  File "/home/mattias/.local/lib/python3.8/site-packages/minimalmodbus.py", line 1240, in _perform_command
    response = self._communicate(request, number_of_bytes_to_read)
  File "/home/mattias/.local/lib/python3.8/site-packages/minimalmodbus.py", line 1406, in _communicate
    raise NoResponseError("No communication with the instrument (no answer)")
minimalmodbus.NoResponseError: No communication with the instrument (no answer)

输出time.sleep(0.1)

MinimalModbus debug mode. Create serial port /dev/ttyUSB0
MinimalModbus debug mode. Serial port /dev/ttyUSB0 already exists
====== SDM120 instrumentA on addres 1 ======
minimalmodbus.Instrument<id=0x7f91feddcdf0, address=1, mode=rtu, close_port_after_each_call=False, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=True, serial=Serial<id=0x7f91fedf50d0, open=True>(port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)>
MinimalModbus debug mode. Will write to instrument (expecting 9 bytes back): '\x01\x04\x00\x0c\x00\x02±È' (01 04 00 0C 00 02 B1 C8)
MinimalModbus debug mode. Clearing serial buffers for port /dev/ttyUSB0
MinimalModbus debug mode. No sleep required before write. Time since previous read: 176619.62 ms, minimum silent period: 4.01 ms.
MinimalModbus debug mode. Response from instrument: '\x01\x04\x04\x00\x00\x00\x00û\x84' (01 04 04 00 00 00 00 FB 84) (9 bytes), roundtrip time: 53.3 ms. Timeout for reading: 1000.0 ms.

Active Power in Watts: 0.0
====== SDM120 instrumentB on addres 2 ======
minimalmodbus.Instrument<id=0x7f91fec70940, address=2, mode=rtu, close_port_after_each_call=False, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=True, serial=Serial<id=0x7f91fedf50d0, open=True>(port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)>
MinimalModbus debug mode. Will write to instrument (expecting 9 bytes back): '\x02\x04\x00\x0c\x00\x02±û' (02 04 00 0C 00 02 B1 FB)
MinimalModbus debug mode. Clearing serial buffers for port /dev/ttyUSB0
MinimalModbus debug mode. No sleep required before write. Time since previous read: 102.09 ms, minimum silent period: 4.01 ms.
MinimalModbus debug mode. Response from instrument: '\x02\x04\x04\x00\x00\x00\x00È\x84' (02 04 04 00 00 00 00 C8 84) (9 bytes), roundtrip time: 52.8 ms. Timeout for reading: 1000.0 ms.

Active Power in Watts: 0.0
4

2 回答 2

1

您的代码或您正在使用的库(minimalmodbus)似乎没有任何问题。

您可能知道,Modbus 在半双工链路上以查询-响应模式工作。简而言之:您首先发送一个查询,该查询的设备将使用您要求的数据来回答。

事务的两个部分(查询和响应)通过同一总线传输。但是总线是一种共享介质,任何时候都只允许一个设备控制总线(通话)。

当您有一个主设备和一个或多个从设备时,只要您保证在任何设备写入总线后有一个短暂的静默期,这个过程就不会出现任何问题。Modbus 规范将此值确定为 3.5 个字符(以您使用的波特率在总线上串行发送 3 个半字符所需的时间)。

不幸的是,一些制造商不遵守这条规则。所以其中一些设备只需要超过 3.5 个字符的时间来释放对总线的控制。

至少您的一台设备似乎是这种情况。本手册可以为您提供一些线索:

手动截图

我敢打赌,在您的两台设备中,其中一台设备释放总线所需的时间明显少于另一台设备,但您必须通过调试详细信息确认这一点。如果您查询 20 或 40 个寄存器而不是 4 或 8 个寄存器,甚至可能是设备需要更长的时间来释放总线......

你能为这个做什么?好吧,从设备方面来说,不多,就是这样。在您的软件上,您可以做许多不同的事情。正如我在上面的评论中所说,time.sleep()考虑到这是 minimummodbus 试图应对总线争用问题的方式,你不应该对使用感到难过。

为了使您的代码更健壮,您可以添加try: ... except:. 此方法在文档中进行了说明。您可以在一个循环内继续重试读取多次尝试,或者为except块添加一个小的延迟。也许是这样的。

于 2021-04-13T09:24:19.990 回答
1

Marcos G. 的回答(4 月 13 日 9:24 回答)包括一些背景细节。简而言之:

  1. 通过一些试验和错误,可以有一个价值,time.sleep以便 minimummodbus 可以处理总线争用问题。
  2. 您可以使用try: ... except:. 最好只尝试多次,以避免无限循环。

我包含两个脚本,它们使用这些方法来解决我发布的问题。与我原来的问题相比,使用了一个 for 循环和一个地址数组。

第一个是与time.sleep

#!/usr/bin/env python3
import minimalmodbus
import time

addr = 1
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', addr)  # port name, slave address (in decimal)

instrument.serial.baudrate = 9600         # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = minimalmodbus.serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout  = 1          # seconds
instrument.mode = minimalmodbus.MODE_RTU   # rtu or ascii mode

addresses = [1,2]
registers = [ 0,  6, 12, 18,    24,  30, 70,   72,   74,     76,     78,   84,   86,  88,    90,   92,   94, 258,  264,  342,    344]
names =     ["V","I","P","S",   "Q","PF","f","IAE","EAE",  "IRE",  "ERE","TSP","MSP","ISP","MIP","ESP","MEP","ID","MID","TAE",  "TRE"]
units =     ["V","A","W","VA","var", "","Hz","kWh","kWh","kvarh","kvarh",  "W",  "W",  "W",  "W",  "W",  "W", "A",  "A","kWh","kvarh"]
info = [
"(V for Voltage in volt)",
"(I for Current in ampere)",
"(P for Active Power in watt)",
"(S for Apparent power in volt-ampere)",
"(Q for Reactive power in volt-ampere reactive)",
"(PF for Power Factor)",
"(f for Frequency in hertz)",
"(IAE for Import active energy in kilowatt-hour)",
"(EAE for Export active energy in kilowatt-hour)",
"(IRE for Import reactive energy in kilovolt-ampere reactive hours)",
"(ERE for Export reactive energy in kilovolt-ampere reactive hours)",
"(TSP for Total system power demand in watt)",
"(MSP for Maximum total system power demand in watt)",
"(ISP for Import system power demand in watt)",
"(MIP for Maximum import system power demand in watt)",
"(ESP for Export system power demand in watt)",
"(MEP for MaximumExport system power demand in watt)",
"(ID for current demand in ampere)",
"(MID for Maximum current demand in ampere)",
"(TAE for Total active energy in kilowatt-hour)",
"(TRE for Total reactive energy in kilovolt-ampere reactive hours)",
]
for addr in addresses:
    instrument.address = addr
    print ("=== General info about address", addr, "===")
    print (instrument)
    print ("=== The registers for address", addr, "===")
    for i in range(len(registers)):
        value = instrument.read_float(registers[i], 4, 2)
        print (str(registers[i]).rjust(3), str(value).rjust(20), units[i].ljust(5), info[i])
    time.sleep(0.1) # To avoid minimalmodbus.NoResponseError
    print ("")

第二个与try: ... except:

#!/usr/bin/env python3
import minimalmodbus

# This alternative script `sdm120-daisy-alt.py` will try to reread a device an extra 9 times before giving up and continuing with the other addresses in the array.

addr = 1
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', addr)  # port name, slave address (in decimal)

instrument.serial.baudrate = 9600         # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = minimalmodbus.serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout  = 1          # seconds
instrument.mode = minimalmodbus.MODE_RTU   # rtu or ascii mode

addresses = [1,2]
registers = [ 0,  6, 12, 18,    24,  30, 70,   72,   74,     76,     78,   84,   86,  88,    90,   92,   94, 258,  264,  342,    344]
names =     ["V","I","P","S",   "Q","PF","f","IAE","EAE",  "IRE",  "ERE","TSP","MSP","ISP","MIP","ESP","MEP","ID","MID","TAE",  "TRE"]
units =     ["V","A","W","VA","var", "","Hz","kWh","kWh","kvarh","kvarh",  "W",  "W",  "W",  "W",  "W",  "W", "A",  "A","kWh","kvarh"]
info = [
"(V for Voltage in volt)",
"(I for Current in ampere)",
"(P for Active Power in watt)",
"(S for Apparent power in volt-ampere)",
"(Q for Reactive power in volt-ampere reactive)",
"(PF for Power Factor)",
"(f for Frequency in hertz)",
"(IAE for Import active energy in kilowatt-hour)",
"(EAE for Export active energy in kilowatt-hour)",
"(IRE for Import reactive energy in kilovolt-ampere reactive hours)",
"(ERE for Export reactive energy in kilovolt-ampere reactive hours)",
"(TSP for Total system power demand in watt)",
"(MSP for Maximum total system power demand in watt)",
"(ISP for Import system power demand in watt)",
"(MIP for Maximum import system power demand in watt)",
"(ESP for Export system power demand in watt)",
"(MEP for MaximumExport system power demand in watt)",
"(ID for current demand in ampere)",
"(MID for Maximum current demand in ampere)",
"(TAE for Total active energy in kilowatt-hour)",
"(TRE for Total reactive energy in kilovolt-ampere reactive hours)",
]
for addr in addresses:
    instrument.address = addr
    print ("=== General info about address", addr, "===")
    print (instrument)
    print ("=== The registers for address", addr, "===")
    for attempt in range(10):
        try:
            for i in range(len(registers)):
                value = instrument.read_float(registers[i], 4, 2)
                print (str(registers[i]).rjust(3), str(value).rjust(20), units[i].ljust(5), info[i])
        except minimalmodbus.NoResponseError:
            print("NoResponseError: did't work on attempt ", attempt+1, "I will retry")
        else:
            print ("Succeeded on attempt", attempt+1)
            break
    print ("")
于 2021-05-01T12:02:12.247 回答