Something is rounding my float values in sitemap

(Ssalonen) #18

@fohdeesha Could you please try to read with uint 16bit data type to keep the test really simple?

The binding addressing is covered in the modbus binding wiki page. Essentially, the user configures the address that goes on the wire (zero-based index).

Quick glance through the code seems to indicate that the address are written as 16bit java short, the address is stored 32bit int int format. I could not find any other reference to writing this. 16 bit short should be OK I guess?

Regarding the floating point issues you experienced, that’s really weird and have not noted same behaviour. There’s really no “random” behaviour that I know in the implementation.

Generally speaking, perhaps you could use some other modbus server for testing purposes? Would diagslave (as documented in the wiki) work for you?

0 Likes

(Rossko57) #19

[quote=“ssalonen, post:18, topic:13704”]Quick glance through the code seems to indicate that the address are written as 16bit java short
[/quote]
mm, that is signed isn’t it? If the code (binding or serial libraries etc.) treats addresses greater than 32,767 as negative numbers, will that affect increments or comparisons? Although I can’t see any reason why they should process it at all; so far as the binding is concerned this is just two bytes to be placed on the wire.

And of course what I said before about the suspect area 28,709 being near this boundary is rubbish, doh.

Reading the same block of registers as uint16, uint32, float32 should be very enlightening.

0 Likes

(Jon Sands) #20

Do you mean to read the same floating point register (both registers that comprise it) as uint 16, or to read a different 16 bit register on the PLC? Just want to make sure I understand correctly and I will test when I get home. I tried reading another modbus device I had here with registers roughly as high and I had the same behavior.

The randomness in the errors confuses me too, if it were a simple math or reading error somewhere I would think that the error would be the same every read. But it indeed changes every poll while all other modbus softwares I use read the same value every time.

I can also give diagslave a try, but the documentation is sparse. Can I set a value and register in it to read from? I’m not sure how to test this bug unless I know ahead of time exactly what the floating point value should be, and setting it to a high register

0 Likes

(Ssalonen) #21

Yeah, there is no unsigned 16bit type in java so that is the way to do it, I believe. In bit level it should go well though, which is what we are interested in. See this SO answer for more details.

You are right that with raw comparisons it would break, a common gotcha in java code dealing bit level stuff. I have not found such code though.

Let’s hope the more simple test would open this issue more

0 Likes

(Ssalonen) #22

I would try reading the registers of intrest as uint16. We can then check the bits if they match what you expect.

Let’s try with the real server first, then use the diagslave if needed.

0 Likes

(Jon Sands) #24

OK, testing reading float32 register as uint16.

Register 28679 has a value of 3210.123291 (never changes) on PLC, float32, my other modbus programs retrieve this value as well

Openhab reading it as Float32: changes every poll, is always incorrect: http://i.imgur.com/Y8ndsHB.png

reading 28679 as uint16: Openhab reads 17736, and it never changes. progress!!

reading the other half of the float32 register, 28680 - returns a value that always changes :frowning: http://i.imgur.com/DdKGpOL.png

So it looks like the floating point math is OK, but it’s the modbus reading that’s the issue. I have no problem providing remote access to the openhab install/system to you devs if it helps you figure out the issue.

OpenHab has been amazing and is SO much nicer than AdvancedHMI that I am set on using it - so I will do anything required to get this bug fixed! Thanks again!

0 Likes

(Jon Sands) #25

Confirming the exact same erroneous behavior when reading from diagslave and not my PLC.

Ran diagslave in tcp mode, used an advancedHMI panel to set it’s register values. Read it back with CAS scanner and AHMI to ensure they were set correctly.

Then read from the diagslave instance with openhab. The values returned are incorrect when using high register numbers (for now I used the exact same register number, 28679 to keep it consistent). I can have the diagslave register hold the value 3210.123, but openhab just returns 3200. The higher the value, the farther off it is. For instance if I set the value lower to 200.9, openhab just returns 200. On lower numbers, it seems to return the number correctly, but not any decimal information

Now that this is easily reproducible with diagslave you guys should be able to test and see for yourselves. Thanks for the diagslave suggestion! You’ll need another modbus software to set the diagslave register values, advancedHMI is pretty easy in that regard. As I imagine using OpenHab to write values has the same error

0 Likes

(Jon Sands) #26

Well, I have some bad news guys - it seems the modbus implementation is broken regardless of the register number -

For a sanity check, I figured I’d set and read back some very low registers with diagslave, well within the original modbus specification of “40001 to 49999”.

For instance, reading register 05. in diagslave the register value is 1000.4, openhab just returns 1000. set diagslave to 3210.1234, openhab just returns 3200. Set diagslave to 5.8, openhab returns 5.7

Confirmed this behavior with both modbus binding versions, both 1.8.3 and 1.9. Now that it’s easily reproducible with diagslave hopefully you guys will have an easier time figuring it out, and hopefully the fact it happens to ANY register makes it easier to debug as well :slight_smile:

Surprised nobody has noticed this, I wonder how many openhab users out there are using the modbus binding and have no idea they’re getting erroneous data back since the error is so small on typical household number ranges

EDIT: ssalonen: I see on github you have a couple foobar2k tools: Nice! Foobar2000 is my favorite player of all time, been using it for years. small world

0 Likes

(Ssalonen) #27

Thanks for the testing this more!

Regarding your uint16 check, could you reproduce this with diagslave or similar? If possible describing exact steps what you are doing would help. It would be great if we can avoid having external devices in the test and use diagslave or similar. There is also more advanced 3rd party modbus client and server, ananas that might be of use.

I can have a look once we have the exact steps to reproduce the issue

Best
Sami

0 Likes

(Jon Sands) #29

Hey again! If you check my very last reply in this thread (sorry, I made several), you’ll see I tested with diagslave and openhab’s modbus binding returns incorrect values regardless of register number or device so it seems the bug is always present

The steps for you to reproduce it on your own system is:

step 1. - read any float32 register with openhab, using diagslave or similar

step 2. you have succesfully reproduced the bug. the higher the value stored in the register, the greater the error

if you’d like me to carry out more testing with diagslave like reading the float32 register as uint16, I can do that, but it’s now pretty reproducible on any system using diagslave

0 Likes

(Ssalonen) #30

Ok i will try to reproduce this and report back.

Sami

0 Likes

(Jon Sands) #31

Sweet! You’ll need something to write the values into diagslave, I used AdvancedHMI. I’m not sure of other free programs that allow the writing of modbus float values. I didn’t use openhab to set the register values since I assume the error also happens on write.

Try setting the value to something with a decimal point, or a high number like 3000. the error amount seems to be a % of the number, so only setting the value to 20 or something, openhab will succesfully retrieve 20. but setting it to 100.3, you’ll see the error clearly when openhab returns 100

If you need an application to set the diagslave register values and are on windows, tell me the IP of your diagslave instance and I can send you an advancedHMI windows executable compiled with the correct info to easily set registers

0 Likes

(Ssalonen) #32

@fohdeesha Unfortunately I cannot reproduce the issue. Perhaps the issues are caused by something with your hardware/software/devices?

Please find the steps I used:

  1. Install 1.8.3 runtime openhab
  2. Install modbus 1.9.0 SNAPSHOT (2016-09-07)
  3. Configure openhab.cfg as follows
    modbus:tcp.slave1.connection=127.0.0.1:55502
    modbus:tcp.slave1.type=holding
    modbus:tcp.slave1.start=0
    modbus:tcp.slave1.length=2
    modbus:tcp.slave1.valuetype=uint16
    # another slave reading the same registers as float32
    modbus:tcp.slave1float.connection=127.0.0.1:55502
    modbus:tcp.slave1float.type=holding
    modbus:tcp.slave1float.start=0
    modbus:tcp.slave1float.length=2
    modbus:tcp.slave1float.valuetype=float32
  1. Configure default.items as follows
	Number Item0 "Item0 [%d]" (ALL) {modbus="slave1:0"}
	Number Item1 "Item1 [%d]" (ALL) {modbus="slave1:1"}
	Number Item0float "Item0 [%f]" (ALL) {modbus="slave1float:0"}
	Number Item1float "Item1 [%f]" (ALL) {modbus="slave1float:1"}
  1. Start Modbus/TCP server ./diagslave -m tcp -p 55502
  2. Start openhab ./start_debug.sh
    6.a. If necessary, install python 2.7.
    6.b. Download Pollmb from http://mblogic.sourceforge.net/mbtools/mbpoll.html

Startup log from openhab

20:49:11.422 [INFO ] [runtime.busevents             :26   ] - Item1 state updated to 0
20:49:11.423 [INFO ] [runtime.busevents             :26   ] - Item0 state updated to 291
20:49:11.476 [INFO ] [runtime.busevents             :26   ] - Item0float state updated to 0.000000000000000000000000000000000000029938371747505134968578758682379069606944138399039104063426020019278439576737582683563232421875

Test A: Single register, 291 (uint16) or 0x0123 (hex)

pollmb/pollmb.py -h 127.0.0.1 -p 55502 -t 2 -f 16 -a 0 -q 1 -d 0123

20:49:49.529 [INFO ] [runtime.busevents             :26   ] - Item1 state updated to 0
20:49:49.529 [INFO ] [runtime.busevents             :26   ] - Item0 state updated to 291

Test B: Single register, 3000 (uint16) or 0x0BB8 (hex)

pollmb/pollmb.py -h 127.0.0.1 -p 55502 -t 2 -f 16 -a 0 -q 1 -d 0BB8
20:33:38.075 [INFO ] [runtime.busevents :26 ] - Item0 state updated to 3000

20:51:25.132 [INFO ] [runtime.busevents             :26   ] - Item0float state updated to 0.000000000000000000000000000000070874221953450279392459988287125325322202140654326285584829747676849365234375
20:51:25.192 [INFO ] [runtime.busevents             :26   ] - Item0 state updated to 3000

Test B: Two registers, 0x42C8999A (hex) = 100.3 (float)

pollmb/pollmb.py -h 127.0.0.1 -p 55502 -t 2 -f 16 -a 0 -q 2 -d 42C8999A

20:52:03.522 [INFO ] [runtime.busevents             :26   ] - Item0float state updated to 100.3000030517578125
20:52:03.582 [INFO ] [runtime.busevents             :26   ] - Item1 state updated to 39322
20:52:03.582 [INFO ] [runtime.busevents             :26   ] - Item0 state updated to 17096

Please note that 0x42C8999A matches the uint16 items as well (you can check these online)
0x999A (hex)= 39322 (uint16)
0x42C8 (hex) = 17096 (uint16)

In all the above cases the values remained constant after the write.

Extra: Documentation for pollmb here

0 Likes

(Jon Sands) #34

OK, I just tested following your steps exactly with pollmb and got the exact same results as you did - good so far!

However you may not like this or believe me, but please stay with me for now :slight_smile I wouldn’t make this up!

your command “pollmb/pollmb.py -h 127.0.0.1 -p 55502 -t 2 -f 16 -a 0 -q 2 -d 42C8999A” actually sets the register value to 100, not 100.3. I’m not sure if the pollmb script is broken, or if your hex is wrong, but when I used your exact pollmb command above to set both diagslave, and my actual plc just in case, reading the register back with four different programs and physical devices all returned 100. Openhab is the only application that reads that back as 100.3 - so the error is still there.

Now either CAS Scanner, AdvancedHMI, and my physical HMI modbus touchscreen panel are all wrong, or there’s something incorrect with that hex value or pollmb. This is incredibly strange!

If you have access to a windows machine, give this a try to read it back, it’s free - http://www.chipkin.com/products/software/modbus-software/cas-modbus-scanner/

this is what I get when reading the register after using “pollmb.py -h 127.0.0.1 -p 502 -t 2 -f 16 -a 05 -q 2 -d 42C8999A”

0 Likes

(Jon Sands) #35

This is adding to my post above, just confirmed there is something wrong with pollmb.py (to be fair it hasn’t been updated in nearly 3 years :stuck_out_tongue: )

after running “pollmb.py -h 127.0.0.1 -p 502 -t 2 -f 16 -a 05 -q 2 -d 42C8999A”

use this to read it back: http://www.modbusdriver.com/modpoll.html - it’ll tell you it’s been set to 100, not 100.3


root@web:~# ./modpoll -m tcp -t4:float -r 05 -c 2 -1 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 = 5, count = 2
Communication.........: 127.0.0.1, port 502, t/o 1.00 s, poll rate 1000 ms
Data type.............: 32-bit float, output (holding) register table

-- Polling slave...
[5]: 100.000000
[7]: 0.000000


this agrees with all my other hardware and software…pollmb.py and openhab are the only things that think it’s actually 100.3?

reading them back as uint16 shows that pollmb.py is not setting the first register in a way that is legal with the modbus spec? modpoll, CAS scanner etc all show the first register in the float pair as a value of 0

root@web:~# ./modpoll -m tcp -t 3 -r 05 -c 2 -1 127.0.0.1

Protocol configuration: MODBUS/TCP
Slave configuration...: address = 1, start reference = 5, count = 2
Communication.........: 127.0.0.1, port 502, t/o 1.00 s, poll rate 1000 ms
Data type.............: 16-bit register, input register table

-- Polling slave...
[5]: 0
[6]: 17096

EDIT AGAIN: well it seems I may have found the issue, pollmb.py and openhab are using an address offset of 1 and not 0 like everything else. if you use modpoll, you’ll see pollmb.py set to register 05 is actually writing the float pair to 06 and 07, one higher.

If you run “./modpoll -m tcp -t 3:hex -r 06 -c 2 -1 127.0.0.1” you’ll see the hex values that should be at 5 and 6 are at 6 and 7. However oddly when I compensate for this and try to read a float value starting at 6 with “./modpoll -m tcp -t4:float -r 06 -c 2 -1 127.0.0.1” it returns 0

what is going on!?!?!?!

EDIT 900: it seems pollmb.py and openhab are swapping the two register values used for float as well, that’s why I recieve an error trying to read it back. if you both compensate for the 1 offset and not 0 (so poll register 6), and tell modpoll the values are backwards by using the flag -f for “big indian” mode, it retrieves the correct value

root@web:~# ./modpoll -m tcp -t4:float -r 06 -c 1 -1 127.0.0.1 -f
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 = 6, count = 1
Communication.........: 127.0.0.1, port 502, t/o 1.00 s, poll rate 1000 ms
Data type.............: 32-bit float, output (holding) register table
Word swapping.........: Slave configured as big-endian float machine

-- Polling slave...
[6]: 100.300003

I’m going to go through my openhab.cfg, lower all my start offsets by 1, and change the mode to “float32_swap” and I guess that’ll fix it? This is the first software I’ve ran into that needed swapped values. What a journey!

0 Likes

(Rossko57) #36

I guess the previously observed ‘random’ number was due to reading an unexpected register that was being modified by some other process in your PLC?

Your comment earlier “On lower numbers, it seems to return the number correctly, but not any decimal information” was a big clue we all overlooked :frowning:

To be fair, the modbus binding Wiki does say it presumes big-endian-32bits. It’s come up before, both endian types must be supported (and are). Which is considered ‘normal’ is arbitrary, but once it has been selected you cannot change without breaking existing implementations,

That leaves the offset=1, I’m not sure I follow here? The “start” we give in openhab.cfg counts from zero, and should pass as-is into the transmitted protocol. Zero representing register 1, or to put it another way it is an offset not an address.
Modpoll parameter guide
-r # Start reference (1-65536, 100 is default)
Polimb parameter guide
-a Address (Modbus memory) default = 0
That’s not very explicit but I think polimb counts from zero and Modpoll from 1? This stuff is always a bugbear with Modbus.

Are you saying the binding adds an offset when dealing with 32-bit formats? So far as I can read the results, all seems to be working as expected when we account for different base addressing schemes?

0 Likes

(Jon Sands) #37

I agree this is seeming to be a giant misunderstanding from the beginning:) oops! Yes, it seems the “random error data” was because openhab was reading a half of a register for another sensor.

it seems the modbus specification specifies byte order for data within registers, but doesn’t actually outline any spec for the register orders of 32 bit float values, and it seems 90%+ of software and hardware out there uses little endian when it comes to multiple registers, which is why I was getting what looked like bad data when testing with 4 different softwares and 3 different hardware pieces (I actually can’t find anything else that uses big endian float other than openhab and pollmb.py). If I remember right even the arduino and raspberry pi main modbus libraries use little endian for float registers (could be wrong though I haven’t used a pi in a long time). this page has some good info - http://www.ccontrolsys.com/w/Common_Modbus_Protocol_Misconceptions

I agree changing the binding behavior endian-ness would be suicide and break all the currently running installations. I know the binding documentation explicitly states it uses big endian for float32, but I think for most people who are just casual users of modbus, a clue that this might be different compared to most hardware could really help. Perhaps a note in the binding documentation stating something like

The endian-ness for float32 registers is not specified within the modbus specification.
Most modbus speaking hardware uses little-endian for float32 registers.
OpenHab uses big endian. If you are receiving unexpected data back, try using the “float32_swap” value type.

I think a note like that could help a lot and prevent silly threads like this in the future :slight_smile: If the documentation is on github I can submit the added node via a pull request

As for the offset, I think you are correct, openhab is zero based and everything else being used was 1 based. I was so tired yesterday my math was off and I was thinking that everything else was 0 based so for openhab to be one register off meant is was -2 which would be wrong, but obviously that wasn’t it.

To end, accounting for all of this, reading all my devices and software with valuetype “float32swap”, and lowering openhabs offset value by 1 for everything to account for the 0/1 offset difference, I am now getting expected behavior and all float32 registers are being read exactly correctly. I would like to thank you rossko57 and of course ssalonen for entertaining my stupidity and spending the time to run through this and test. It helped!

As I said above I truly believe this thread and many others like it might be prevented if there was some addition to the modbus binding documentation using “plain speak” explaining the offset difference and endian difference in a way that “modbus casuals” would understand, like with real world examples - I’m totally willing to contribute that if the documentation is open for all to contribute to

0 Likes

(Rossko57) #38

It’s all a lesson in how simple things can cause non-obvious weirdness.

Siemens PLCs for example transmit big-endian words. Modicon, little-endian. In many cases it will depend on the often-long history of any given manufacturer. Openhab’s choice was made by the first OH user’s device needing 32-bit support.

I’m not sure you’ve seen the binding Wiki, which is really the “reference” documentation?


There is a section about 32-bit word swaps, maybe you could review it for content?
The ‘addressing’ comments could probably be expanded about zero-based offset.

I have read there are worse horrors, see “method 3” here
http://www.digi.com/wiki/developer/index.php/Modbus_Floating_Points

0 Likes

(Jon Sands) #39

That’s the wiki article I’ve used during setup, I’ve probably read it top to bottom 10 times (or so I thought). Reading it again I see almost verbatim the sentence I suggested above, so it seems I’m blind!

“If you get strange values using the int32, uint32 or float32 valuetypes then just try the int32_swap, uint32_swap or float32_swap valuetype, depending upon what your data type is.”

Has that always been there!?!? I swear, I’m not always this stupid. Like you said, simple errors - if it were just swapped bytes or wrong endian-ness on their own, I would have figured it out in a couple seconds, but when they combined I went down a rabbit hole thinking I was nuts. It doesn’t help that reading a float value with the wrong endian and a register error of +1, it gets the MSB and gives you a value that looks like it’s basically correct. Lucky me :slight_smile:

0 Likes

Modbus Energy Meter - reading float registers
(Rossko57) #40

It’s only been there since version 1.9 introduced byte swap couple of months ago (I think the second float32 user came along, with other-endianess)

It’s become a big Wiki, Modbus compatibility isn’t as simple as it looks. Sami has made great strides in making OH binding real-world workable, thankyou sir!

Do you think the address/offset/index 0<>1 business needs clarifying? It’s not supposed to be a Modbus tutorial, but that is a big pratfall.

0 Likes