Writing a float to a Spyder-Plus

Subsea7

Member
Join Date
Mar 2021
Location
UK
Posts
48
Hi folks, I have a Spyder-Plus tension control module on a wire spooling machine. This is a new piece of kit. I can read data from it over Modbus using an MVI56E-MNETC Prosoft card, in a Rockwell CLX chassis. This works as expected.

The problem comes when I need to write a float to it for setting the tension setpoint. I create an entry in the MNETC card for its own register - 558 lets say - occupying 2 Modbus registers, then COP the float value into the MNETC.Data.Writedata[558] which should convert the float into IEEE 754 format and place two UINTS into register 558 & 559 (high word in the higher register as that's how the Spyder works if you don't swap-words at the MNETC end) yet it does not work; It seems to ignore the value sent, and when I look into the data in the controller tags for the MNETC write block, it (Studio / CLX PLC) has written the correct value to the higher register (the integer part of the IEEE 754 conversion) and the remainder of the floating point part + the exponent + mantissa part should occupy the lower register. But it contains a copy of the integer value itself, not even a converted one.

Say we want to send 16.5; the 559 register should contain 16772 and the 558 register should contain 0 - but it puts the -26214 in there where zero should be. I cobbled a python script together to do the IEEE 754 conversion from float to 32 bit binary so I could tell what the registers should hold and be able to check easily (the maths is long winded doing it by hand)

So the long and short is, why can I not say COP MyFLoat => MNETC.Data.WriteData[558] and have it work? I tried moving the float to a DINT first but that lops off the fractional part. If anyone has experience of writing a float (REAL) to a ProSoft MNETC card I would love to hear their thoughts.

This is what it should equate to in the registers of the PLC for the MNETC card: (IEEE 754 format)

16.5 as a 32-bit binary number: 0x41840000 (IEEE 754 format)
Hex representation as two 16-bit numbers: 4184 0000
Decimal representation as two 16-bit numbers: 16772 and 0

Instead, the registers hold 16772 and -26214 and the Spyder sees the value sent as an error of course - duly ignoring it - I don't understand why the PLC is doing this. Oh and for info, I am using Function 16 - Write Multiple Registers for a length of 2 registers (all registers in the MNETC are 16 bit each so need to use two) in the MNETC to the Spyder. I am certain the issue is the way the CLX PLC is handling the float to binary32 with the COP instruction.

Thanks.
 
Last edited:
A few possibilities, either or both of which could be the issue:
1) the word order is wrong
2) the addressing is "off by one"

Details

1) CLX is LSWord-first, so it will put the 16772 (1 sign bit plus 8 exponent bits plus high 8 mantissa bits) into the second INT on the CLX, and the 0 (low 16 bits of mantissa) into the first INT on the CLX. So if the Modbus addressing is correct (see (2) below), 16772 should end up in Spyder address 559 and 0 should end up in Spyder address 558. If the Spyder is also LSword-first, then God is in his heaven and all is right with the world. If the Spyder is MSWord-first, then the INTs (INT is equivalent to Word on this thought experiment), then the INTs will need to be swapped on the CLX before the transfer via Modbus. (I think you inferred the Spyder is LSWord -first, but I want to cover all the bases).

2) IIRC, Modbus addressing on the CLX is 1-based I.e. the starting address entered in the MSG Setup Screen has a range of 1 to 65536 (or to 65535?). The Spyder addressing is unknown, but the first word of the float is 558 is even, and floats usually start on a 32-bit i.e. 2-word boundary, which suggests (caveat: big assumption here) the Spyder starting address is also even i.e. zero i.e. the Spyder Modbus addressing is 0-based. Modbus has two addressing models: Modbus Data Model, which is 1-based; PDU (Protocol Data Unit) Model (bits on the wire L, which is 1 based. Manufacturers of Modbus devices (slaves or matters) choose which model to use when they publish their Modbus memory map. So if your Modbus MSG instruction uses a starting address of 558 on the CLX side, that could
- write the 0 to address 557 on the Spyder side,
- write the 16772 to address 558 on the Spyder side,
- and leave the contents of 559 unchanged on the Spyder side.

A lot of unknowns here, but if you are extracting the words in Python, e.g. import struct; print(struct.unpack('HH',struct.pack('f'.16.5))), then the rest is bits and bookkeeping, and I am sure you will figure it out.

The website modbus.org has technical documentation about the protocol; I would start there.
 
Last edited:
Oh, and to answer the original query "why can't I COP...", my response is "no reason, you are on the right track, but there are some implementation details that differ between the CLX and the Spyder that need to be sorted first.
 
Also note that, if the first attempt put the 16772 into the correct Holding Register, then the value would be within 1/8 of 16.5, no matter what value was in the other register. So the fact that that did not happen suggests the 16772 did not end up in the correct Holding Register, which further suggests the off-by-one hypothesis.
$ python
...
>>> import struct

>>> struct.unpack('f',struct.pack('hh',0,16772))
(16.5,)

>>> struct.unpack('f',struct.pack('hh',0,16773))
(16.625,)

>>> struct.unpack('f',struct.pack('hh',-1,16772))
(16.624998092651367,)

>>> struct.unpack('f',struct.pack('hh',-32768,16772))
(16.5625,)

>>> struct.unpack('f',struct.pack('hh',-26214,16772))
(16.575000762939453,)
If the -26214 ended up in the high word of the float, then the float would be negative, which may be why the Spyder rejects it.
>>> struct.unpack('f',struct.pack('hh',16772,-26214))
(-1.5949688341850516e-23,)

 
I looked up the Spyder-Plus manual (Spyder-Plus S1 is similar).

It has options to specify how the byte- and word-order of the Modbus PDUs are interpreted.

Byte order is needed for Modbus Masters from vendors that do not adhere to the MSbyte-first requirement of correct Modbus protocol; I am fairly certain the CLX will convert the individual 16-bit registers to MSWord-first before putting the data on the wire, so there should be no problem there.

The word order will be an issue that needs to be configured correctly, as the CLX is LSWord-first, and the Spyder-Plus default is MSWord-first. So

  • EITHER flip the Spyder-Plus to interpret incoming floats as LSWord-first,
  • OR swap the words (INTs) on the CLX before the data are transferred.
Also, you mention that the communications are handled by an MVI56E-MNETC ProSoft card, so that is another layer of configuration (including configuration of word order) that needs to be understood and dealt with. Also, your 558/559 addresses were Ethernet/IP addresses on the MNETC, so everything I said about odd and even there goes out the window. In fact, the Spyder addressing is 1-based, and floats start at both odd and even Modbus addresses. But you will still have to figure out what register addresses to use on the MNETC to get into the correct addresses on the MNETC.
 
Last edited:
Looking into it further, I think you should do everything Modbus-related on the MNETC,, as there are options to handle word- and byte-order there as needed. Rather than try to figure it out, it is usually quicker to go through all combinations until one works.

Also, have you tried the search terms MNETC and MODBUS on this forum? You are not the first.
 
BitBoy has beaten this to death and I know you're doing this right, but since you didn't type it in your description, I'll just put it here:

What did you put as the length parameter in the COP instruction? It's supposed to be the destination elements, so 2 in your case.

Edit: How does your company take it with you using their name here?
 
Last edited:
I wouldn't say obvious... -26214 is an odd bit pattern as a reset/default value, so I don't think this is the problem. Being a holding register, I wonder if it may be written from the other end?
 
Ok, so I know about word and byte swaps. I've been reading data from this card since 2017. Writing had not been required till now. The word order is LSW first for reads and it works. The documentation in the module actually says the read address space is 0-599 and write address space starts at 1000 to 1599 but the AOI in the Studio 5000 software only goes 0-599 for both readdata and writedata.

I think this is the issue but it can't be changed. Even when offline.

Oh and btw I don't work for SS7...I'm a contractor but I like the name.

Next, yes as said I used 2 for the length on the COP instructions. I now see the format of the words in the MNETC data structure is correct, the byte and word order are correct, but the AOI sets up the MNETC.Data.WriteData as INT[600] so the addresses in the CLX plc are 0-599 however the prosoft cards itself states all write addresses start at 1000 to 1599 so there's the issue..The MNETC cars is not processing the writes as the address.range is wrong, but in studio 5000 it is not possible to use address 1000 for the MNETC.Data.WriteData as it is addressed 0-599 in the data structure.

Anyone else hit this issue?
 
Last edited:
Page 100 of the mnetc manual says
Ladder logic subtracts 1000 from the value contained in word 249 to determine a block index. This b[l] ock index determines which 200-word block of data will be taken from the ReadData array and placed in the output image to be returned to the module.

So that offset of 1000 may be more or less invisible to the AOI.

Did you search the forum for "mnetc?"

Maybe the COP command cannot write to the mnetc array, but if you COP'ed the float to a buffer INT[2], you could then MOV the individual INTs from that buffer into the mnetc array.
 
Last edited:
Thanks buddy. Yeah I tried moving the float to a dint, intended to split into two ints, but it lops off the fractional part at the beginning.

Now what I've done is put a python script together to see what the 754 interpretation of the value would be, at first I got different numbers from the ints in the MNETC data structure in my ladder logic. However it seems that block stores all ints as signed, hence the negative values. Each half of a 32 bit float value sent becomes a 16 bit signed int. Weirder and weirder. So I altered the python to make two signed ints and lo & behold, the same numbers pop out as if I just cop the float straight to one of the array addresses with a length of 2. I have to look at that page 100 you quoted regards the addresses. Maybe I need to use 1558 in the MNETC card but 558 in the AOI tags.
 
What is this AOI you speak of? I thought that you'd just write directly into the mapped registers of the card?



I use a standalone card for Modbus-RTU in an application at work which is not the same, but reading through the manual seems close enough and I just write directly to the registers. Could that be the issue?



Additionally, you can also offset addresses from the variable array in the PLC into whichever registers matches the other device, so that shouldn't be an issue either.
 
Thanks buddy. Yeah I tried moving the float to a dint, intended to split into two ints, but it lops off the fractional part at the beginning.
Of course that would never work, MOV instructions will transfer interpreted values, not bits.
Now what I've done is put a python script together to see what the 754 interpretation of the value would be
This is summat like this:
import struct
MyFloat32 = 16.5

low_int,high_int = struct.unpack('Mhh',struct.pack('<f',MyFloat32))
print((low_int,high_int,))
or equivalent, and prints out "(0,16772)", yes?
Each half of a 32 bit float value sent becomes a 16 bit signed int. Weirder and weirder.
I don't understand why that seems weird; it is doing exactly what I think it should do. But then, I've been bit banging for a few more decades than some, perhaps.

I apologize for all of my earlier posts, because I assumed this was a Modbus problem and did not understand the role of the MNETC. So please allow me to start over.

The problem as originally stated is that

1) there is an instruction
COP MyFLoat MNETC.Data.WriteData[558] 2
where
- COP is the instruction that copies bits from a Source to a Destination, irrespective of the data types of that Source and that Destination
- MyFLoat is the Source: a 32-bit floating-point tag, currently with a test value of 16.5 (0x41840000), and is the source of the bit
- MNETC is a tag of UDT MNETCMODULEDEF
- MNETC.Data is a tag of UDT MNETCDATA, and is a member of the MNETCMODULEDEV UDT
- MNETC.Data.WriteData is an array INT[600], and is a member of the MNETCDATA UDT
- MNETC.Data.WriteData[558] is the Destination: a reference to the element at an offset of 558 INTs from the start of the INT[600] array tag MNETC.Data.WriteData, where the bits are to be written
- 2
is the number of elements INTs) of MNETC.Data.WriteData, starting at the destination, to be overwritten by the bits starting at MyFloat
2) When that COP instruction executes, it is expected that

  • the INT value 0 (0x0000) should be written to MNETC.Data.WriteData[558], and
  • the INT value 16772 (4184h) should be written to MNETC.Data.WriteData[559]
3) But what you are actually seeing is that

  • the INT value -26214 (E7BAh) is apparently being written to MNETC.Data.WriteData[558], and
  • the INT value 16772 (4184h) is apparently being written to MNETC.Data.WriteData[559]
Is that all correct now?

Please excuse me for going full-on pedantic here, but I find it a more efficient path to understanding what is going on, rather than going off half-****ed like I did earlier.

So if those are the symptoms, what I would do first is

  • Declare an INT[2] array tag wd558_2, and
  • on the same rung as the COP instruction, append the instructions
MOV MNETC.Data.WriteData[558] wd558_2[0] MOV MNETC.Data.WriteData[559] wd558_2[1]
So you would know what the COP instruction is writing when it executes. Because when you say MNETC.Data.WriteData[559] has -26214 displayed as its value, we do not know when that -26214 was written, and the MOV instructions would determine if it was the COP instruction that wrote the value, or if the -26214 value was written by some other event at a different time (PLC programming is primarily about time).

By the way, the INT[600] array tag MNETC.Data.WriteData is a buffer between the CompactLogix program memory (in the AOI) and the MNETC module device memory. How to examine or change the mapping of that array into the MNETC module device memory is described around Page 30 of the MNETC manual, and it is set in the [Edit - Module] dialog box*. So I think the 1000 offset you keep mentioning is a distraction; MNETC.Data.WriteData is a regular 600-element INT array as far as the PLC program is concerned.

* It might be useful to see a screenshot of that dialog box.
 
Last edited:

Similar Topics

I wrote some code in a compact logix using rs logix v.20.04 back in February. This code counts the number of pumps running a remote pump station...
Replies
12
Views
2,837
Hi, I'm having an issue in crimson 3.0 when I create a programme using a case statement referencing a fault word that each bit needs to change the...
Replies
5
Views
253
Hello all, I'm currently working on a servo motor linear positioning system (ball screw). I'm all set up regarding communication between my HMI...
Replies
1
Views
93
Hello All: I have a Windows 10 PC running a FTView ME Station 10.00. I have a .mer file version 6.00. It has been running well on it for a long...
Replies
1
Views
171
My R55 Ingersollrand is tripping on motor overload and im falling to see the trip history it is writing Acquarring texts
Replies
0
Views
134
Back
Top Bottom