1

I was assigned to perform the task without any documentation. I have a problem with reading data from MODBUS. This is the script that I was able to create:

from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.client.sync import ModbusTcpClient

client = ModbusTcpClient('X.X.X.X')
connection = client.connect()

request = client.read_holding_registers(12606,2)
result = request.registers
decoder = BinaryPayloadDecoder.fromRegisters(result, Endian.Big, wordorder=Endian.Little)
print "Counter1: %0.2f" % decoder.decode_32bit_float()

request = client.read_holding_registers(12482,2)
result = request.registers
decoder = BinaryPayloadDecoder.fromRegisters(result, Endian.Big, wordorder=Endian.Little)
print "Counter2: %0.2f" % decoder.decode_32bit_float()

client.close()

Everything looks fine, But the data on the counter is different from those in the script for example:

Value on the counter : 39558853.30 (value is decimal)
Value from the script: 58853.30
(value is decimal)

Read input registers (HEX): E54D 4765

And this is how the address documentation looks like"

P   12458       Q2  4\DW12458 = 1\ND20_Q2\P(F)
Q   12462       Q2  4\DW12462 = 1\ND20_Q2\Q(F)
S   12466       Q2  4\DW12466 = 1\ND20_Q2\S(F)
I   12470       Q2  4\DW12470 = 1\ND20_Q2\I(F)
U   12474       Q2  4\DW12474 = 1\ND20_Q2\U(F)
f   12478       Q2  4\DW12478 = 1\ND20_Q2\f(F)
EP_POB  12482       Q2  4\DW12482 = 1\ND20_Q2\EP_POB(F)
EP_ODD  12486       Q2  4\DW12486 = 1\ND20_Q2\EP_ODD(F)
EQ_IND  12490       Q2  4\DW12490 = 1\ND20_Q2\EQ_IND(F)
EQ_POJ  12494       Q2  4\DW12494 = 1\ND20_Q2\EQ_POJ(F)
THDVL1  12498       Q2  4\DW12498 = 1\ND20_Q2\THDVL1(F)
THDVL2  12502       Q2  4\DW12502 = 1\ND20_Q2\THDVL2(F)
THDVL3  12506       Q2  4\DW12506 = 1\ND20_Q2\THDVL3(F)
THDIL1  12510       Q2  4\DW12510 = 1\ND20_Q2\THDIL1(F)
THDIL2  12514       Q2  4\DW12514 = 1\ND20_Q2\THDIL2(F)
THDIL3  12518       Q2  4\DW12518 = 1\ND20_Q2\THDIL3(F)
UL1 12522       Q2  4\DW12522 = 1\ND20_Q2\UL1(F)
UL2 12526       Q2  4\DW12526 = 1\ND20_Q2\UL2(F)
UL3 12530       Q2  4\DW12530 = 1\ND20_Q2\UL3(F)
IL1 12534       Q2  4\DW12534 = 1\ND20_Q2\IL1(F)
IL2 12538       Q2  4\DW12538 = 1\ND20_Q2\IL2(F)
IL3 12542       Q2  4\DW12542 = 1\ND20_Q2\IL3(F)
PL1 12546       Q2  4\DW12546 = 1\ND20_Q2\PL1(F)
PL2 12550       Q2  4\DW12550 = 1\ND20_Q2\PL2(F)
PL3 12554       Q2  4\DW12554 = 1\ND20_Q2\PL3(F)
QL1 12558       Q2  4\DW12558 = 1\ND20_Q2\QL1(F)
QL2 12562       Q2  4\DW12562 = 1\ND20_Q2\QL2(F)
QL3 12566       Q2  4\DW12566 = 1\ND20_Q2\QL3(F)
S1  12570       Q2  4\DW12570 = 1\ND20_Q2\S1(F)
S2  12574       Q2  4\DW12574 = 1\ND20_Q2\S2(F)
S3  12578       Q2  4\DW12578 = 1\ND20_Q2\S3(F)
Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
miesiu
  • 11
  • 1
  • 1
  • 5
  • What are the raw register values (preferably in hex) that you are reading when the meter shows 1355723,9? Do you have documentation on the registers you are reading? BTW, does the "," in the number represent decimal point? – Marker Oct 26 '18 at 19:07
  • "," is decimal Current value on the counter : 39558853.30 Current value from the script: 58853.30 Read input registers (HEX): E54D 4765 --- this is the only thing I have in the documentation, unfortunately I doubt that someone would understand it. Counter1 12482 Q2 Counter1: 4\DW12482 = 1\ND20_Q2\EP_POB(F) – miesiu Oct 27 '18 at 09:24
  • @miesiu Can you more explain that what is `12606` and `12482` addresses and where are in the mentioned document? – Benyamin Jafari Oct 27 '18 at 10:12
  • 12482 is in 7 line | EP_POB 12482 Q2 4\DW12482 = 1\ND20_Q2\EP_POB(F) – miesiu Oct 27 '18 at 10:26
  • 12482 it seems to be a MODBUS address which stores data from the meter. – miesiu Oct 27 '18 at 10:29
  • as I wrote earlier, these are the only information I have :( – miesiu Oct 27 '18 at 10:30
  • @miesiu Maybe you need to double reading and decoding, I will update my answer for that. – Benyamin Jafari Oct 27 '18 at 10:31
  • @miesiu Did your problem solved? If done, you can accept an answer in below. – Benyamin Jafari Nov 13 '18 at 09:15

2 Answers2

3

I improved your code as follows:

from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.client.sync import ModbusTcpClient

def validator(instance):
    if not instance.isError():
        '''.isError() implemented in pymodbus 1.4.0 and above.'''
        decoder = BinaryPayloadDecoder.fromRegisters(
            instance.registers,
            byteorder=Endian.Big, wordorder=Endian.Little
        )   
        return float('{0:.2f}'.format(decoder.decode_32bit_float()))

    else:
        # Error handling.
        print("The register does not exist, Try again.")
        return None


client = ModbusTcpClient('X.X.X.X', port=502)  # Specify the port.
connection = client.connect()

if connection:
    request = client.read_holding_registers(12606, 2, unit=1)  # Specify the unit.
    data = validator(request)
    print(data)

    request = client.read_holding_registers(12482, 2, unit=1)  # Specify the unit.
    data = validator(request)
    print(data)

    client.close()

else:
    print('Connection lost, Try again')

[NOTE]:

Are you sure about the desired float32 decoding?

  1. float AB CD == byteorder=Endian.Big, wordorder=Endian.Big
  2. float CD AB == byteorder=Endian.Big, wordorder=Endian.Little
  3. float BA DC == byteorder=Endian.Little, wordorder=Endian.Big
  4. float DC BA == byteorder=Endian.Little, wordorder=Endian.Little

Set the unit_id:

  • In many cases unit is 1 as default.

[UPDATE]:

Maybe you need to read and decode the result as double/float64 value for the 12482 register address, because I think when the respective register in the doc is 12482 and the next register is 12846, you need to read 4regsfloat64/double:

request = client.read_holding_registers(12482, 4, unit=1)

And

return float('{0:.2f}'.format(decoder.decode_64bit_float()))
Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
  • It seems to me that this is because the values ​​almost match, can the problem be a result, is it bigger than a 32 bit word? and thx for improvement. – miesiu Oct 27 '18 at 09:43
  • @miesiu I think no, because a float32 number is a great number (`3.402823466 E + 38`). So I think your problem is in decode section, If you do not ensure about decoder type, test the four cases of the decoders. – Benyamin Jafari Oct 27 '18 at 09:54
  • 1
    2 counters BIG.BIG/2.33127530468e+25/82827853824.0 | BIG.LITTLE/3446.6/58964.4(1 is correct and 2nd not full lenght) | LITTLE.BIG/-0.0/-223765616.0 | LITTLE.LITTLE/2.17266977243e+14/2.35059257655e+23 | – miesiu Oct 27 '18 at 10:16
  • @miesiu Is 1st address that corrects with `big, little`, `12606`? and 2nd address that half correct is `12482`? – Benyamin Jafari Oct 27 '18 at 10:28
  • That's right, the script gets the correct data from the counters which number does not exceed 100000 (big, little) – miesiu Oct 27 '18 at 10:40
  • @miesiu Did you test the double reading/decoding through the [UPDATE] section? – Benyamin Jafari Oct 27 '18 at 10:45
  • print with raw data and conversion [27034, 17751, 0, 0] Q2: 0.00 (earlier 58964,4) [43494, 18278, 0, 0] Q3: 0.00 (earlier 3446,6) – miesiu Oct 27 '18 at 10:58
  • It means that 12482 is 27034, 12483 is 17751, 12484 is 0 and 12485 is 0. – miesiu Oct 27 '18 at 11:06
  • @miesiu I think when the desired register in the doc is `12482` and the next register is `12846`, therefore we need to read `4regs` (float64/double). Do you test the four cases of decoder on this case (double reading)? – Benyamin Jafari Oct 27 '18 at 11:57
  • here is the result BIG/BIG 5.02726060776e+200/0.0 | BIG/LITTLE 0.0/0.0 | LITTLE/BIG -0.0/0.0 | LITTLE/LITTLE 0.0/0.0 – miesiu Oct 27 '18 at 12:20
  • @miesiu OK, I haven't any other idea. – Benyamin Jafari Oct 27 '18 at 12:47
0

I'm pretty sure the value 39558853.30 is to large to store in a IEEE single precision float. There are 7.22 digits of precision, that number requires 9 digits. I did some experimentation with assigning the value to float and double values in C# confirms this.

That leads me believe:

1) Like Benyamin Jafari suggested, you need to read four registers. However, that number (as a double) in hex is 0x4182dcf62a666666 which doesn't seem to correspond any of the data that you are reading.

OR

2) It is also possible that it is returned as a UINT32 which must be scaled by (1/100) to give you what the counter is showing. 0xE54D4765 = 3847047013 => scaled by 1/100.0 = 38470470.13 which is close to what you are seeing on the counter. In my experience, this is a common practice in Modbus.

OR

3) They are using some other (non-standard) format to represent the data.

Can you give us the product name, model, etc?

Marker
  • 972
  • 6
  • 9