Open ductsoup opened 6 years ago
Hello,
I would say that the problem comes from Modpoll. Did you contact them?
Best luc
As I mentioned, our SCADA solution is confirming the odd behavior. It seems unlikely two independent MODBUS masters would have the same flaw.
Do you mean that both modpoll
and ignition
gets 3.140625 for register 40001?
What do you get if you read the register just as regular bytes?
>>> struct.unpack('>HH', struct.pack('>f', 3.140625))
(16457, 0)
The question is why it reads 0 in register 40002?
Exactly, the first register is getting lost somewhere and I have no idea why.
[40001]: 0
[40002]: 16457
[40003]: 4059
[40004]: 16457
This is a common problem with modbus and depends on whether you are using base-0 or base-1.
Using your exact code, you are creating a code block starting at 40000, but you don't write the first register, you are writing 40001 first. Your zero is the fact that you are skipping that first register. If you look at the entire range you'll see every register you write has the 16457/4059 pairing, in that order, which can successfully decode to 3.1416 if you use a switch the bit order and start the scan one register later. Alternatively, you can start writing at 40000 instead of 40001, and I would recommend changing your > to < in your float conversion function. This will allow you to start scanning at 40001 in base-1, and use 32-bit float data type, and have 3.1416 show up. There are a number of ways to get the data to look correct and I can't say what it best for your application, but I can say this is not a bug with modbus-tk, but instead comes from your code allocating a block of registers, but not writing to the first one, compounded with base-0 vs. base-1 numbering, and bit order.
Thanks for the reply. A couple of observations and clarifications.
There is no standard for word/byte order or base 0/1 register addressing that I'm aware of. Prior versions of modbus-tk did not have this bad behavior. Regardless of how the address block is allocated it shouldn't matter which register is written first. Last, if the first register is half a word/byte off the subsequent four should also be wrong, and they're not.
Good information for general MODBUS troubleshooting but I'm not getting how it applies to this specific example. That said, I never claim to know everything so, if you have a corrected version of the test code I posted above could you please share it here?
If you don't want to change the data type on your master from LSR to MSR, then you need to do what I said above. "start writing at 40000 instead of 40001, and... change your > to < in your float conversion function". What does that look like:
def mb_set_float(addr, val):
j = struct.unpack('<HH',struct.pack('<f', val))
slave_1.set_values('ro', addr, j)
return val
if __name__ == "__main__":
try:
""" Initialize the MODBUS TCP slave """
server = modbus_tcp.TcpServer(address='0.0.0.0')
server.start()
slave_1 = server.add_slave(1)
slave_1.add_block('ro', cst.HOLDING_REGISTERS, 40000, 20)
print("Ready")
while True:
mb_set_float(40000, pi)
mb_set_float(40002, pi)
mb_set_float(40004, pi)
mb_set_float(40006, pi)
mb_set_float(40008, pi)
time.sleep(1)
except:
server.stop()
You can see the disconnect (where the zero value was coming from) in that you start polling and start the block at 40000, but don't start writing until 40001, so nothing is being written to 40000. To fix that, change where you start writing. I set up modscan so that it was viewing the data exactly as you copied it above, then I changed the code as described here and values all go to 3.1416. I believe this is the fix you are looking for that doesn't require changing anything on your master. Tested your command in modpoll and also worked. However, if you don't want to change your code and can change your master, This is the modpoll command using your base code that will get it to view properly : modpoll -m tcp -t 4:float -f -r 40002 -c 5 -o 3 192.168.x.x
note the -f
switch and where the starting register is.
Tested your modification to my example this morning, it's got the same bad behavior only different. It did inspire me to think about this from a new direction and find a solution.
The word order not specified in the MODBUS standard doesn't matter so long as it's consistent. I used the default word order for our MODBUS OPC master plugin which is the same defalt for modpoll. That's not the issue here.
The reason I created the block at 40000 and didn't write to the first register is our OPC (and modpoll) expects one-based, not zero-based addressing by default. Made that adjustment and everything lit up like a Christmas tree. Note the -0 flag.
To my understanding however you implement the zero/one base addressing in modbus-tk it shouldn't matter to the master so long as it knows what to expect. Apparently it does so that's what I think is the possible bug in the modbus-tk code.
Either way, this example works as expected.
#!/usr/bin/env python -u
import time
from math import pi
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp
import struct
def mb_set_float(addr, val):
j = struct.unpack('<HH',struct.pack('<f', val))
print("%02x %02x" % j)
slave_1.set_values('ro', addr, j)
#print("%02x %02x" % slave_1.get_values('ro', addr,2))
return val
if __name__ == "__main__":
try:
""" Initialize the MODBUS TCP slave """
server = modbus_tcp.TcpServer(address='0.0.0.0')
server.start()
slave_1 = server.add_slave(1)
slave_1.add_block('ro', cst.HOLDING_REGISTERS, 40001, 20)
print("Ready")
mb_set_float(40001, pi)
mb_set_float(40003, pi+1.0)
mb_set_float(40005, pi+2.0)
mb_set_float(40007, pi+3.0)
mb_set_float(40009, pi+4.0)
mb_set_float(40011, pi+5.0)
mb_set_float(40013, 0.0)
while True:
time.sleep(1)
except:
server.stop()
'''
modpoll -0 -1 -m tcp -t 4:hex -f -r 40001 -c 12 -o 3 192.168.5.36
modpoll 2.9 - FieldTalk(tm) Modbus(R) Polling Utility
Copyright (c) 2002-2010 proconX Pty Ltd
Visit http://www.modbusdriver.com for Modbus libraries and tools.
Protocol configuration: MODBUS/TCP
Slave configuration...: Address = 1, start reference = 40001 (PDU), count = 12
Communication.........: 192.168.5.36, port 502, t/o 3.00 s
Data type.............: 16-bit register (hex), output (holding) register table
Word swapping.........: Slave configured as big-endian float machine
Polling slave ...
[40001]: 0x0FDB
[40002]: 0x4049
[40003]: 0x87ED
[40004]: 0x4084
[40005]: 0x87ED
[40006]: 0x40A4
[40007]: 0x87ED
[40008]: 0x40C4
[40009]: 0x87ED
[40010]: 0x40E4
[40011]: 0x43F7
[40012]: 0x4102
modpoll -0 -1 -m tcp -t 4:float -r 40001 -c 8 -o 3 192.168.5.36
modpoll 2.9 - FieldTalk(tm) Modbus(R) Polling Utility
Copyright (c) 2002-2010 proconX Pty Ltd
Visit http://www.modbusdriver.com for Modbus libraries and tools.
Protocol configuration: MODBUS/TCP
Slave configuration...: Address = 1, start reference = 40001 (PDU), count = 8
Communication.........: 192.168.5.36, port 502, t/o 3.00 s
Data type.............: 32-bit float, output (holding) register table
Polling slave ...
[40001]: 3.141593
[40003]: 4.141593
[40005]: 5.141593
[40007]: 6.141593
[40009]: 7.141593
[40011]: 8.141593
[40013]: 0.000000
[40015]: 0.000000
'''
Hey, you are changing your polling parameters on me, don't blame my code. :P If you run the command you have in your original post modpoll -m tcp -t 4:float -r 40001 -c 5 -o 3 192.168.x.x
while running the code in my last post, you will get:
>modpoll -m tcp -t 4:float -r 40001 -c 5 -o 3 127.0.0.1
modpoll 3.4 - FieldTalk(tm) Modbus(R) Master Simulator
Copyright (c) 2002-2013 proconX Pty Ltd
Visit http://www.modbusdriver.com for Modbus libraries and tools.
Protocol configuration: MODBUS/TCP
Slave configuration...: address = 1, start reference = 40001, count = 5
Communication.........: 127.0.0.1, port 502, t/o 3.00 s, poll rate 1000 ms
Data type.............: 32-bit float, output (holding) register table
-- Polling slave... (Ctrl-C to stop)
[40001]: 3.141593
[40003]: 3.141593
[40005]: 3.141593
[40007]: 3.141593
[40009]: 3.141593
Which is I believe what you were looking for. If you run the commands you added in your last post such as the -0
switch, then yes, my code returns just zeros. However, that wasn't the command you originally posted so I apologize I didn't take that into account. I'm glad I got you on the right track, and that it's working for you now. Enjoy!
True, sorry I didn't mention that but that's how I spotted it. Try -c 8 instead. Reversing the endianness just moved the problem from the first to the last register. If you write different values and switch to -t 4:hex it really jumps out.
Hi, I'm using modbus-tk slave simulator for my project. I'm using the above code mentioned in the comment "blotsome commented on Jul 12, 2018". My code is working for values up to 125.5. If I give 126.5, the value gets truncate and when I fetch from other interfaces like web, SNMP the value is showing as 126.
Please help me with this. Thanks in Advance float.txt
Harish
I tried 126.5 and the value in your txt file 130.5 and got this:
Protocol configuration: MODBUS/TCP
Slave configuration...: address = 1, start reference = 286, count = 5
Communication.........: 127.0.0.1, port 502, t/o 3.00 s, poll rate 1000 ms
Data type.............: 32-bit float, output (holding) register table
-- Polling slave... (Ctrl-C to stop)
[286]: 126.500000
[288]: 0.000000
[290]: 0.000000
[292]: 0.000000
[294]: 0.000000
and
-- Polling slave... (Ctrl-C to stop)
[286]: 130.500000
[288]: 0.000000
[290]: 0.000000
[292]: 0.000000
[294]: 0.000000
so I updated your code to set values in both the ranges you say work and the ranges you say don't work:
while True:
mb_set_float(285, 124.5)
mb_set_float(287, 125.5)
mb_set_float(289, 126.5)
mb_set_float(291, 127.5)
mb_set_float(293, 128.5)
And scanned again:
Protocol configuration: MODBUS/TCP
Slave configuration...: address = 1, start reference = 286, count = 10
Communication.........: 127.0.0.1, port 502, t/o 3.00 s, poll rate 1000 ms
Data type.............: 32-bit float, output (holding) register table
-- Polling slave... (Ctrl-C to stop)
[286]: 124.500000
[288]: 125.500000
[290]: 126.500000
[292]: 127.500000
[294]: 128.500000
Just as we expect, so I dove down deeper, and looked at each register:
Protocol configuration: MODBUS/TCP
Slave configuration...: address = 1, start reference = 286, count = 10
Communication.........: 127.0.0.1, port 502, t/o 3.00 s, poll rate 1000 ms
Data type.............: 16-bit register, output (holding) register table
-- Polling slave... (Ctrl-C to stop)
[286]: 0
[287]: 17145
[288]: 0
[289]: 17147
[290]: 0
[291]: 17149
[292]: 0
[293]: 17151
[294]: -32768
[295]: 17152
We do see that for the values you say work, the first 16-bit register is not being used, but when we get higher up, it is being used. But where that start doesn't correlate to where you say you are seeing problems. My intuition is that there is nothing wrong with the python code here, but something wrong with how you are reading the data. Can you explain what you are doing to "fetch" these values, or how you use SNMP to fetch values?
In the example below I'm writing the same value to five holding registers encoded as a Modbus 32-bit float to a Modbus slave but the external master is not seeing the same value in all five registers, the first value is wrong. This happens in both Python2 and Python3 running under Raspbian Jessie.
I've been chasing this for a week and still don't have the slightest clue what's going on. Does anyone have any ideas?
TIA, Craig
This is the result from Modpoll, Industrial Automation's Ignition product shows the same.
If I query within the Python script it reports the correct value.