The stop means a REAL outside the DWORD range 0 to 4294967295 reached REAL_TO_DWORD. Fix the slave's documented word and byte order at one point in the master, prove it at two known values, then guard the operand so bad frames hold the last valid value instead of stopping the PLC. Move the bits with a union when the next device wants the IEEE 754 pattern, not the number.
The stop is the honest part. REAL_TO_DWORD converts a number, and the destination type holds 0 to 4294967295. When mis-ordered bytes from a Modbus slave are decoded as a REAL outside that range, the conversion result is undefined and your runtime stops the controller with an FPU overflow. Fix it in two moves: set the master to assemble the slave's documented word and byte order, then guard the operand so a bad frame holds the last valid value instead of stopping the PLC. When the next device wants the floating-point bit pattern instead of the number, move the bits with a union and skip the conversion.
Why the conversion stops the controller
REAL_TO_DWORD is a numerical conversion, not a bit copy. It takes the value of the REAL and tries to express it as an unsigned 32-bit integer, whose range is 0 to 4294967295. The vendor's online help states that an operand outside the target range gives a result that depends on the processor, which makes it undefined. On your runtime that undefined conversion raises a floating-point overflow and the controller stops. The instruction did not break; the operand was garbage.
A REAL arriving over Modbus is 32 raw bits in two 16-bit registers, packed per IEEE 754 single precision: 1 sign bit, 8 exponent bits, 23 fraction bits. Join the two words in the wrong order, or swap the bytes inside a word, and the sign, exponent and fraction all change. The decoded value can come out enormous, negative, or not a number at all. Spend 10 minutes tracing how the 32 bits split across the two 16-bit registers before touching code.
Symptoms and what each points to
- Stop with FPU overflow on the conversion line. The decoded REAL is negative or above 4294967295. Bytes swapped inside the 16-bit register words are the usual cause, because that moves bits into the sign and exponent fields.
- Decoded value of 0.0 while the sensor reads live. The two register words are exchanged; a small positive value then assembles as a near-zero denormal. No stop, no alarm, wrong data.
- Value flips sign or jumps decades while the process barely moves. The master reads the wrong register pair, or words from two adjacent measurements are mixed.
- Value tracks the process by a constant wrong factor. Scaling applied twice, or not at all, in the map. A map problem, not a byte-order problem.
- NaN or an infinity where a number belongs. The assembled bits form a non-finite pattern: wrong layout, or the pair holds status flags instead of data.
- No reply from the slave at all. That is transport and addressing, one step earlier. Work through Modbus RTU master no-reply and address errors before touching any decode.
Check the data path from slave to operand
- Read the slave's register map: register address, declared data type, scaling, and the documented word and byte order. The map is the source of truth; the master configuration is a copy of it.
- Compare the master's start address and register count with the map. Documents often number registers from 1 while the protocol address is one lower, and that off-by-one selects a neighboring value.
- Capture the two 16-bit register words at the master, before any decode, next to the value shown on the slave's local display.
- Assemble the 32 bits by hand in the map's documented order and compare with what the application decoded.
When the raw words match the map, transport is good. Stop adjusting network settings and work on the layout.
Fix the word and byte order at one point
Two places can reorder bytes: the layout setting in the master channel, and any swap written in the application code. Enable exactly one. A swap in the driver plus a swap in the code cancel out today and double up the day one side changes. Use the channel setting when the driver offers one, and keep the application working on decoded REAL values. The discipline is the same one behind serial command byte encoding for display control: define the byte order once, at the boundary.
Prove the layout at two known values with clearly different magnitudes, one near the bottom of the range and one near the top. A byte-order error corrupts every value the same way, so one point proves the order; the second point catches a wrong register pair.
Guard the operand before the conversion
The layout fix covers the frames you have seen. The guard covers the one you have not. Convert only when every test passes:
- the master reports good communication status for that request;
- the value equals itself, a test a NaN fails;
- the value is at or above 0.0;
- the value is at or below a finite process limit, which also rejects both infinities.
Then convert, store the result as the last valid value, and move on. On any failure, hold the last valid value and set a bad-quality flag for the alarm logic:
IF xDataGood AND (rValue = rValue) AND (rValue >= 0.0)
AND (rValue <= rProcessMax) THEN
dwValue := REAL_TO_DWORD(rValue);
dwLastGood := dwValue;
xQuality := TRUE;
ELSE
dwValue := dwLastGood;
xQuality := FALSE;
END_IF
Set rProcessMax to the engineering range of the measurement, not the type limit. For a temperature tag spanning 0.0 to 150.0 °C, the limit is 150.0 °C. The next section gives the reason the limit must stay away from the type limit, and the exact numbers.
Where the guard limit must sit
Single precision carries 24 significand bits, so it represents every integer exactly only up to 2^24 = 16777216 counts. Above that, representable values are multiples of growing powers of two, and near 2^32 the spacing is 256 counts. Two consequences:
- The literal
4294967295.0is stored as 4294967296.0. A guard writtenrValue <= 4294967295.0therefore accepts 4294967296.0, and that conversion is out of range again. - The largest single-precision value that converts cleanly is 4294967040, one spacing step of 256 counts below the type limit.
Set the process limit at or below 16777216 counts and the comparison is exact in REAL. If the count can genuinely exceed 16777216, run the limit comparison in LREAL, which stores 4294967295 exactly, and convert the value that passed.
Move the bits with a union instead
Some interfaces want the encoding, not the number: a downstream device that will reinterpret the 32 bits as a REAL, or a logger storing the raw pattern. A union is built for that. Declare a type with a REAL member and a DWORD member in one shared storage location, write the REAL member, read the DWORD member. Nothing is converted, so there is no range to violate and no overflow to raise. The same shared-storage technique is the standard way to split a DWORD into its four bytes with a union when a protocol wants the bytes one at a time.
A union moves bits without checking them, so the value tests from the guard section still apply to the REAL before anything computes with it.
Example: 25.0 °C with swapped bytes stops the PLC
The case: a temperature slave ships its measurement as a REAL in two consecutive holding registers, the application decodes it and converts a copy to a DWORD counter interface, and the controller stops with FPU overflow.
Inputs:
- Slave register map: measurement in two consecutive 16-bit holding registers, documented as high word first, most significant byte first inside each word.
- Known test value from the slave's local display: 25.0 °C.
- Second test value near the top of the engineering range: 150.0 °C.
- IEEE 754 single precision: 1 sign bit, 8 exponent bits, 23 fraction bits.
- Destination range for the DWORD: 0 to 4294967295.
Rule: a normal number is stored as 1.f x 2^(e-127), with the sign in the leading bit.
Arithmetic:
- 25.0 °C = 1.5625 x 16 = 1.1001 binary x 2^4. The exponent is 4 + 127 = 131, binary 10000011. The stored bits are 0 10000011 10010000000000000000000, which is 0x41C80000. The slave sends the words 0x41C8 and 0x0000.
- Assembled in the documented order, 0x41C80000 decodes back to 25.0 °C. REAL_TO_DWORD returns 25, inside range, no stop. The second point, 150.0 °C, decodes to 0x43160000 and returns 150, also inside range, so the register pair is right too.
- With the master set to exchange the two words, the application assembles 0x0000 before 0x41C8 and reads 0x000041C8. The exponent field is zero, so the value is a denormal: 0x41C8 = 16840, and 16840 x 2^-149 is about 2.4 x 10^-41, effectively 0.0 °C. The conversion returns 0. No stop, no alarm, wrong answer.
- With the bytes swapped inside each word instead, 0x41C8 becomes 0xC841 and the pattern is 0xC8410000. Sign bit 1, exponent 10010000 binary = 144, fraction 0.5078125, so the value is -1.5078125 x 2^17 = -1.5078125 x 131072 = -197632.0. That is below 0 °C-equivalent range and outside 0 to 4294967295, so the conversion is undefined and the controller stops with FPU overflow.
Result: one wrong layout stops the PLC, the other fails silently at 0.0 °C. Only the raw-word capture tells them apart.
Decision: set the master channel to the map's order, high word first and most significant byte first, then confirm the decode returns 25.0 °C at the bench point and the right value at the 150.0 °C point near the top of the range. Keep the guard in place after that: convert only finite values from 0.0 up to the process limit, and hold the last valid value on any failure.
Prove the fix at commissioning
- Hold the slave at a known stable value and record the two 16-bit register words at the master.
- Confirm the decoded REAL matches the slave display at two values with clearly different magnitudes, for example 25.0 °C and 150.0 °C.
- Break the data on purpose, with the process outputs inhibited so nothing acts on the held value: unplug the slave or force a wrong layout. Confirm the output holds the last valid value, the bad-quality flag sets, and the controller keeps running.
- Restore the link and confirm the value returns and the flag clears.
- If the DWORD travels onward, decode it at the receiving end and compare with the source.
If the number feeds a plant screen, decide what it shows during an outage: a held value with a visible quality flag is what a read-only process display should show, and the same rule belongs on any shop-floor screen fed from the same tag.
Recommendation: fix the layout first, at one point, and prove it at two known values such as 25.0 °C and 150.0 °C; then keep the guard in the code so a future bad frame holds the last valid value between 0.0 and the process limit instead of stopping the controller. Switch from REAL_TO_DWORD to a union only when the next device wants the IEEE 754 bit pattern, not the number.
Questions
Does assigning the REAL straight to a DWORD variable avoid the conversion?
No. An assignment or a MOVE between REAL and DWORD performs the same numerical conversion with the same 0 to 4294967295 range, so the stop simply moves to the assignment line. Implicit conversion follows the same rule as the explicit operator. Only a union reads the shared storage as the other type without converting anything.
The decoded value sits at 0.0 while the sensor reads live. Is that the same fault?
Same family, different symptom. When the two register words are exchanged, a small positive value assembles as a near-zero denormal and the conversion returns 0 with no stop. A guard passes that value, because 0 is inside every range. Capture the raw words and put them beside the slave display value; a word exchange is obvious in the raw capture and invisible in the converted result.
What upper limit should the guard compare against?
Your process limit, not the type limit. Single precision stores every integer exactly only up to 16777216, and the literal 4294967295.0 is stored as 4294967296.0, so a guard written against the type limit can admit an out-of-range value. Keep the limit at or below 16777216 and the REAL comparison is exact, or run the comparison in LREAL. The largest single-precision value that converts cleanly is 4294967040.
Does the union path need its own guard?
It needs value checks, not range checks. Nothing is converted, so nothing overflows, but the bits can still hold a NaN or a bad frame. Apply the same test list as the conversion path minus the type limit: communication status, the self-equality test for NaN, and the engineering range of the measurement, before any arithmetic or control logic uses the value.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.