'Modbus Python Schneider PM5300
I'm trying to get voltage or current from PM5300 meter, but always come 32768 only. The code:
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
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 = 0x01
def run_sync_client():
client = ModbusClient(method='rtu', port='COM15', timeout=1, baudrate=19200, parity='E', stopbits=1, bytesize=8)
client.connect()
request = client.read_holding_registers(address=43010, count=2, unit=UNIT)
result = request.registers
print(result)
decoder = BinaryPayloadDecoder.fromRegisters(result, Endian.Little, wordorder=Endian.Big)
#dc2 = (decoder.decode_32bit_float() + 32768 /1000)
#print(dc2)
print(decoder.decode_32bit_float())
client.close()
if __name__ == "__main__":
run_sync_client()
The address 3010 in register list matches to current (4 is the holding register).
The log and print with Endian.Little:
2020-06-16 20:11:24,349 MainThread DEBUG transaction :115 Current transaction state - IDLE
2020-06-16 20:11:24,349 MainThread DEBUG transaction :120 Running transaction 1
2020-06-16 20:11:24,350 MainThread DEBUG transaction :219 SEND: 0x1 0x3 0xa8 0x2 0x0 0x2 0x45 0xab
2020-06-16 20:11:24,350 MainThread DEBUG sync :75 New Transaction state 'SENDING'
2020-06-16 20:11:24,350 MainThread DEBUG transaction :228 Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2020-06-16 20:11:24,364 MainThread DEBUG transaction :304 Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2020-06-16 20:11:24,365 MainThread DEBUG transaction :233 RECV: 0x1 0x3 0x4 0x80 0x0 0x80 0x0 0xb2 0x33
2020-06-16 20:11:24,365 MainThread DEBUG rtu_framer :180 Getting Frame - 0x3 0x4 0x80 0x0 0x80 0x0
2020-06-16 20:11:24,365 MainThread DEBUG factory :266 Factory Response[ReadHoldingRegistersResponse: 3]
2020-06-16 20:11:24,365 MainThread DEBUG rtu_framer :115 Frame advanced, resetting header!!
2020-06-16 20:11:24,366 MainThread DEBUG transaction :383 Adding transaction 1
2020-06-16 20:11:24,366 MainThread DEBUG transaction :394 Getting transaction 1
2020-06-16 20:11:24,366 MainThread DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
[32768, 32768]
**2020-06-16 20:11:24,366 MainThread DEBUG payload :312 [32768, 32768]**
2020-06-16 20:11:24,367 MainThread DEBUG payload :368 [b'\x00\x80', b'\x00\x80']
1.1755122874426309e-38
With Endian.Big the result: -4.591774807899561e-41
Thanks a lot!
Solution 1:[1]
Your problem is in the register number you are trying to read. On pymodbus you have to indicate registers as absolute address numbers; you don't have to add 40000 to the Modbus map as you did.
Just change this line:
request = client.read_holding_registers(address=43010, count=2, unit=UNIT)
to:
request = client.read_holding_registers(address=3010, count=2, unit=UNIT)
That should give you the current average according to the map of your meter.
I don't have access to a meter right now but if my notes are correct the endianness should be byteorder=Endian.Big, wordorder=Endian.Little
so you might want to fiddle with that in your code too.
Reading this question might be worth your while, it has some code that I think targets the same family of Schneider Electric devices.
I would argue it would be better if instead of an apparently correct reading you'd get a wrong address error, which is what you should be getting in this case. I imagine they wanted to keep open the option to add more registers to the map and they kept it unbounded.
EDIT: As discussed in the comments below the correct register to read for the current average is 3009, so the request should be:
request = client.read_holding_registers(address=3009, count=2, unit=UNIT)
Some devices give you holding register numbers starting from 40001. For pymodbus and others the first holding register is number 0, so if the Modbus map of the device you should read register number 45125, for instance, you need to subtract 40001 to get the address
for pymodbus:
address=45125-40001=5124
The case you are dealing with here is even more annoying: the map starts with register 1 so you need to subtract 1.
As you can see on the following screenshot:
ModbusPoll follows the same logic: by default you have to enter 0 for register 40001 (or 10 for register 40011. If you click the check box PLC addresses you will have to enter 1 to read from address 40001.
Yes, I know, confusing!
Note that the question in the link above does not mention the -1 offset. Some devices (like yours!) will not let you read from "the wrong" register. Such that, if you want to read current average (which is a FLOAT32 and hence occupies two registers) you need to start reading from register 3009 and read two registers (yes, the map says 3010 but keep in mind the -1 offset). If instead, you start from register 3010 you are actually trying to read the upper half part of the current average and the lower half of the next variable (current imbalance). Since you will not be able to get any useful data because you are reading two mixed up variables you get an error. Again, not the best error; it should at least give you some hint of what you are doing wrong, but alas!
Solution 2:[2]
I have a soluciĆ³n with this format the PM5500 series use ieee format to float data.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Juan Elias |