S7 to SIMOTION IEEE 754 Float Conversion: Byte Array Method

David Krause14 min read
Motion ControlSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Problem Overview

Engineers integrating a Siemens S7-300/400/1500 PLC with a SIMOTION motion controller over PROFINET, PROFIBUS, or UDP/Ethernet frequently encounter a data-format barrier: the S7 sends floating-point data as raw bytes in IEEE 754 single-precision (REAL) format, and on the SIMOTION side the payload lands in a byte array rather than a typed REAL variable. The bytes themselves are perfectly valid IEEE 754 — the issue is purely a matter of how the SIMOTION runtime interprets the underlying memory layout when data arrives through a non-tag-bound channel (raw socket, UDP receive buffer, untyped I/O slot, or the older BigByteArray buffer used internally by SIMOTION communication functions).

The same conversion is required when the S7 publishes REALs through:

  • PUT/GET on S7 communication (fetch/write via FB14/FB15 on S7-400 or the S7-1500 PUT/GET instructions)
  • Open User Communication (TCP/UDP) on S7-1500 via TSEND_C/TRCV_C
  • PROFINET slot-based I/O with raw byte view enabled
  • PROFIBUS DP with PPO type 1–5 (parameter/process data) carrying REAL in the PZD area
  • SIMOTION-side _receive on a UDP socket populated from a S7-1500 broadcast

The diagnostic symptom is consistent: the data displays correctly as four bytes when viewed in hexadecimal, but assigning those bytes to a SIMOTION REAL or LREAL yields a value of 0.0, NaN, or a clearly wrong magnitude (typically a byte-order or sign-bit artifact).

Root cause: SIMOTION's strongly-typed ST/MCC/LAD/FBD editors do not implicitly reinterpret a ARRAY OF BYTE region as a REAL. A typed cast or a system conversion function is required. The S7 REAL type is already IEEE 754-compliant (per the STEP 7 data-type conversion documentation), so the bytes are correct — only the view is wrong.

IEEE 754 Single-Precision Format Reference

Both the S7-300/400/1500 and SIMOTION use the IEEE 754 binary32 representation for the REAL data type. The bit layout is identical; byte order is the only axis that varies.

Bit range Width Field Description
31 1 Sign (S) 0 = positive, 1 = negative
30–23 8 Exponent (E) Biased by +127; stored = exponent + 127
22–0 23 Mantissa (M) Fractional part; implicit leading 1 for normal numbers

Value formula: value = (-1)^S * 2^(E-127) * (1.M)

Encoded across four bytes in the order byte 0 = bits 31..24, byte 1 = bits 23..16, byte 2 = bits 15..8, byte 3 = bits 7..0. Siemens S7 and SIMOTION both use big-endian (network byte order) for REAL over communication by default, but PROFINET slot I/O on some hardware variants can deliver little-endian (LSB first) — verify with a known test value before committing code.

SIMOTION Data Types and System Functions

SIMOTION's ST compiler accepts the following floating-point types, all IEEE 754 compliant:

Type Size Format Range (approx.)
REAL 32 bit IEEE 754 binary32 ±3.402823E+38
LREAL 64 bit IEEE 754 binary64 ±1.797693E+308

Key SIMOTION system functions for byte-array conversion (all declared in the _iec or _standard library, available in SIMOTION SCOUT from kernel V4.1 onward and well-supported in V4.4, V4.5, and V5.x):

  • _BigByteArray_To_AnyType(destPtr, srcPtr, destType) — copies N bytes from a byte buffer into a typed destination, where N is inferred from destType. Works for REAL, LREAL, DWORD, INT, etc.
  • _AnyType_To_BigByteArray(destPtr, srcPtr, srcType) — inverse operation for sending typed values back to a byte stream.
  • _getBitByteWordDword and _putBitByteWordDword — granular 8/16/32-bit access to a byte array.
Kernel version note: Earlier SIMOTION kernels (V3.x and earlier) ship with reduced runtime libraries. If the function is missing, confirm the SCOUT project target is set to a controller with kernel V4.1+ and the appropriate lib is included in the LDBV/IEC project. The S7-1500↔SIMOTION data-type conversion guidance published in the STEP 7 (TIA Portal) V20 documentation set describes the equivalent conversions on the PLC side, and the same IEEE 754 byte layout applies on the SIMOTION side.

Prerequisites

  1. SIMOTION SCOUT (V4.4 or V5.4 recommended) installed, with a configured motion controller (e.g., SIMOTION D435, D445, C240 PN).
  2. STEP 7 (TIA Portal) or STEP 7 V5.x project with the source S7 CPU online and a published REAL tag (e.g., DB1.DBD0 as REAL).
  3. Configured PROFINET IO link between S7 and SIMOTION, OR a UDP/TCP socket pair (S7-1500 TSEND_C/TRCV_C ↔ SIMOTION _socket API).
  4. Know test value to validate byte order — e.g., S7 sends REAL#3.14 = 16#4048F5C3.
  5. SIMOTION program access to the destination REAL variable (axis data, TO data, or global VAR_GLOBAL).

Step-by-Step: Converting the Byte Array to a REAL

Method 1 — Direct Cast via _BigByteArray_To_AnyType (preferred)

This is the cleanest, kernel-supported approach and avoids hand-written byte manipulation. It requires the SIMOTION kernel to expose the function (V4.1+, available in _iec.lib).

// SIMOTION ST source
VAR_GLOBAL
    rxBuffer    : ARRAY[0..255] OF BYTE;    // populated by UDP/PN receive
    measuredPos : REAL;                     // target variable
END_VAR

VAR
    pBuffer : POINTER TO BYTE;
    pValue  : POINTER TO REAL;
    dummy   : DWORD;
END_VAR

// Method 1: typed copy (big-endian REAL on the wire)
pBuffer := ADR(rxBuffer[0]);
pValue  := ADR(measuredPos);
_BigByteArray_To_AnyType(pValue, pBuffer, TYPE_REAL);
// measuredPos now holds the IEEE 754 value as it was on the S7

The function copies exactly SIZEOF(REAL) = 4 bytes from pBuffer into pValue, reinterpreting the bits as a REAL. No swap, no shift, no manual assembly.

Method 2 — Manual Byte Assembly (fallback / kernel-agnostic)

If the runtime library does not expose _BigByteArray_To_AnyType, or if you are forced onto an older kernel, build the REAL manually. The procedure below assumes big-endian REAL on the wire (S7 default over S7-communication and PROFINET slot data).

// SIMOTION ST — manual REAL assembly from byte array (big-endian)
VAR_GLOBAL
    rxBuffer    : ARRAY[0..3] OF BYTE;
    measuredPos : REAL;
END_VAR

VAR
    dwordBits   : DWORD;
END_VAR

// Combine 4 bytes into one 32-bit word, then reinterpret as REAL
// Big-endian: byte[0] is MSB (sign + exponent), byte[3] is LSB (mantissa LSBs)
dwordBits := (DWORD)rxBuffer[0] << 24
           OR (DWORD)rxBuffer[1] << 16
           OR (DWORD)rxBuffer[2] << 8
           OR (DWORD)rxBuffer[3];

// Bit-level cast via a UNION-like alias:
// SIMOTION does not have a native UNION, but a POINTER cast achieves the same effect
IF ADR(dwordBits) <> 0 THEN
    // The IEC method: write the DWORD bits to memory, then cast the pointer
    measuredPos := REAL#0.0;          // initialize
    // Use a temporary DWORD alias over a REAL slot
    // (see Method 3 for a cleaner pattern)
END_IF;

The shift-and-OR pattern above produces a correct DWORD. To reinterpret it as REAL, you must use either an explicit copy via _getBitByteWordDword into a target, or a pointer alias (Method 3).

Method 3 — Pointer Alias (cleanest IEC-61131-3 pattern)

Create a 4-byte aligned buffer, write a DWORD into it, then read it back as a REAL through a POINTER TO REAL. This is endianness-explicit and kernel-portable.

VAR
    rawBytes : ARRAY[0..3] OF BYTE;       // shared 4-byte view
    pReal    : POINTER TO REAL;
    pByte    : POINTER TO BYTE;
    dwordVal : DWORD;
END_VAR

// --- receive path ---
// 1. Fill rawBytes from the fieldbus / UDP buffer
rawBytes[0] := rxBuffer[0];
rawBytes[1] := rxBuffer[1];
rawBytes[2] := rxBuffer[2];
rawBytes[3] := rxBuffer[3];

// 2. Build DWORD (big-endian, adjust for little-endian PN slots)
dwordVal := SHL_DWORD(BYTE_TO_DWORD(rawBytes[0]), 24)
          OR SHL_DWORD(BYTE_TO_DWORD(rawBytes[1]), 16)
          OR SHL_DWORD(BYTE_TO_DWORD(rawBytes[2]), 8)
           OR      BYTE_TO_DWORD(rawBytes[3]);

// 3. Reinterpret the DWORD bits as REAL
pByte  := ADR(rawBytes);
pReal  := ADR(dwordVal);   // alias same 4 bytes
// Or, more portably:
rawBytes[0] := DWORD_TO_BYTE(SHR_DWORD(dwordVal, 24) AND 16#FF);
rawBytes[1] := DWORD_TO_BYTE(SHR_DWORD(dwordVal, 16) AND 16#FF);
rawBytes[2] := DWORD_TO_BYTE(SHR_DWORD(dwordVal, 8)  AND 16#FF);
rawBytes[3] := DWORD_TO_BYTE(       dwordVal         AND 16#FF);

// Now alias rawBytes as REAL — using a temporary POINTER
// Note: SIMOTION allows pointer re-aliasing at runtime

For purely runtime-safe code without pointer aliasing, the recommended industrial pattern is to call _BigByteArray_To_AnyType with TYPE_REAL and let the system library do the cast.

Method 4 — Using _getBitByteWordDword for Bit-Grained Access

If you must avoid pointer aliasing entirely (some safety-certified SIMOTION builds restrict direct pointer use), use the system bit-access function:

VAR
    realVal : REAL;
    realBytes : ARRAY[0..3] OF BYTE;
END_VAR

// Copy first 4 bytes from incoming buffer
realBytes[0] := rxBuffer[0];
realBytes[1] := rxBuffer[1];
realBytes[2] := rxBuffer[2];
realBytes[3] := rxBuffer[3];

// Read 32 bits starting at byte 0 of realBytes, interpret as REAL
realVal := REAL#0.0;
realVal := _getRealFromByteArray(ADR(realBytes), 0);
// (where _getRealFromByteArray is a user FB wrapping _getBitByteWordDword
//  to fetch 32 bits and cast the resulting DWORD to REAL)

Wrap the call in a reusable function block so the pattern is consistent across the project.

Byte-Order (Endianness) Considerations

The IEEE 754 bits are identical — only the order of the four bytes on the wire changes. The S7 and SIMOTION defaults both lean toward big-endian for S7-communication-style protocols, but PROFINET slot I/O can present data either way depending on the device GSD and slot configuration.

  • PROFINET slot, default Siemens profile
  • Channel Default byte order Verify with test value
    S7 PUT/GET (FB14/FB15, S7-400) Big-endian REAL#1.0 = 16#3F800000
    S7-1500 PUT/GET Big-endian REAL#1.0 = 16#3F800000
    Big-endian (per PROFIdrive profile) REAL#1.0 = 16#3F800000
    PROFINET slot, third-party GSD Little-endian possible Check GSD; verify with 16#0000803F (LE) vs 16#3F800000 (BE)
    UDP/TCP, S7-1500 TSEND_C with raw REAL Big-endian (S7 stores MSB first in DB) Watch first 4 bytes in Wireshark
    PROFIBUS PPO type 5 (PZD = 10 words) Big-endian REAL#1.0 = 16#3F800000

    For a quick endianness sanity check, send REAL#1.0 from the S7 and look at the four received bytes. The IEEE 754 encoding of 1.0 is 0x3F800000.

    • Bytes arrive as 3F 80 00 00 → big-endian, no swap needed.
    • Bytes arrive as 00 00 80 3F → little-endian, reverse the byte order before the cast.

    PROFINET-Specific Procedure

    When the SIMOTION is the PROFINET IO controller and the S7 is the device (or vice-versa, with the S7 as controller and SIMOTION as I-Device), the REAL appears in a configured I/O slot.

    1. Configure the slot in HWCN / SCOUT with 4 bytes, type "input" or "output".
    2. Bind the slot to a SIMOTION I/O address (e.g., %IW0 for 16-bit, but for a 32-bit REAL use a DWORD alias %ID0).
    3. Reinterpret %ID0 as REAL via a POINTER alias, or copy the bits into a REAL variable using DWORD_TO_REAL (works on SIMOTION compilers that support the conversion instruction — V4.4+ confirmed).
    // SIMOTION ST — PROFINET slot, DWORD in I/O area reinterpreted as REAL
    VAR
        rawDword AT %ID0 : DWORD;    // 4 bytes from PN slot
        measuredVal : REAL;
    END_VAR
    
    measuredVal := DWORD_TO_REAL(rawDword);
    // Or, using the _iec library cast:
    // measuredVal := REAL#(rawDword);
    
    AT overlay caveat: The AT declaration overlays a variable in place. measuredVal := REAL#(rawDword); may not be a legal IEC typecast in all SIMOTION kernel versions. If the compiler rejects it, use _BigByteArray_To_AnyType with a temporary byte buffer, or use a POINTER TO REAL assigned to ADR(rawDword).

    UDP / Open User Communication Procedure (S7-1500 ↔ SIMOTION)

    1. On the S7-1500 side, configure a UDP connection with TSEND_C (or TCON + TSEND). Pack the REAL as four bytes using the Serialize instruction or a manual DB layout.
    2. On the SIMOTION side, declare a UDP socket via the _socket API, bind to a port, and call _receive asynchronously.
    3. On each receive, copy the first 4 bytes of the payload into a REAL using Method 1 above.
    // SIMOTION ST — UDP receive handler
    VAR_GLOBAL
        sockHandle : DWORD;
        rxBuffer   : ARRAY[0..1471] OF BYTE;
        rxLength   : DINT;
        fromAddr   : _SocketAddress;
        measuredPos: REAL;
    END_VAR
    
    // In a cyclic or interrupt task:
    IF _isReady(sockHandle) THEN
        rxLength := _receive(sockHandle, ADR(rxBuffer[0]), SIZEOF(rxBuffer), ADR(fromAddr));
        IF rxLength >= 4 THEN
            _BigByteArray_To_AnyType(ADR(measuredPos), ADR(rxBuffer[0]), TYPE_REAL);
        END_IF;
    END_IF;
    

    Verification

    1. Static value test: Set the S7 source REAL to 1.0. Confirm SIMOTION variable displays 1.0. Bytes on the wire should be 3F 80 00 00.
    2. Range test: Send 3.14, -3.14, 0.0, and a small value (e.g., 0.001 = 0x3A83126F). Confirm all values match within float precision.
    3. Edge cases: Send NaN (e.g., 0/0), +Inf, and the max REAL 3.402823E+38. SIMOTION should display NaN, +INF, and the max value respectively.
    4. Watchdog test: Drive the SIMOTION axis (if the REAL feeds a position/velocity setpoint) and confirm motion is smooth, with no glitches in the trace.
    5. Online monitor: In SCOUT, place a watchpoint on the target REAL, then toggle the S7 source between 0.0 and 100.0. The watchpoint value should update within one PROFINET cycle (typically 1 ms for IRT, 4 ms for RT).

    Troubleshooting Matrix

    Symptom Likely cause Fix
    REAL always 0.0 Bytes not yet written to buffer when _BigByteArray_To_AnyType is called Guard conversion with a buffer-valid flag; verify rxLength >= 4
    REAL = 0.0 even with valid bytes Pointer ADR() returns 0 (uninitialized array) Use a static VAR_GLOBAL or VAR_TEMP allocated array
    REAL = small positive garbage (~1E-38) Byte order swapped (little-endian received, no reverse) Reverse the 4 bytes before cast, or confirm GSD is big-endian
    REAL is correct magnitude but sign flipped Bit 31 (sign) misaligned due to 16-bit packing bug Verify no 16-bit WORD truncation between the S7 and SIMOTION
    REAL oscillates between two values Two PROFINET slots writing the same I/O address, or duplicate UDP listener Check slot mapping in HWCN and SCOUT; check socket bind uniqueness
    REAL = NaN in steady state Source REAL is NaN upstream; SIMOTION propagation is correct Trace back to the S7 source; clamp/limit upstream
    Conversion compiles but runtime halts _BigByteArray_To_AnyType not in linked library Add _iec.lib to LDBV project; confirm kernel V4.1+
    Compile error: function not found SIMOTION kernel older than V4.1 Upgrade firmware on the SIMOTION D/C/P; confirm via SCOUT device diagnostics
    Compile error: typecast not allowed Used REAL#(dwordVar) syntax; not supported in IEC ST for SIMOTION Use _BigByteArray_To_AnyType or POINTER alias
    Stale value, no update on S7 change Cyclic task running slower than PROFINET update; buffer overwritten before copy Use a separate receive buffer per cycle; confirm IPO/IPO2 task priority

    Performance and Timing

    • _BigByteArray_To_AnyType executes in single-digit microseconds on a SIMOTION D435 (kernel V4.5); no measurable impact at PROFINET 1 ms cycle.
    • Manual byte assembly via shift-OR is comparable (under 1 µs for 4 bytes on the same hardware).
    • For high-frequency motion loops (> 4 kHz), place the conversion in the IPO or IPO2 task, not the slower background task.

    Safety-Certified Builds

    SIMOTION F-variant controllers (e.g., SIMOTION D445-2 DP/PN F) restrict direct pointer use in the safety-related part of the program. Place all byte-array conversions in the standard user program, not in the safety task. Cross-check the F-program's view of the value via the safety I/O, not via the byte buffer directly. The S7-1500F side must use the Serialize/Deserialize instructions from the F-library to keep the payload integrity-checked end-to-end.

    Cross-Reference: S7 Side Encoding

    The STEP 7 (TIA Portal) data type documentation confirms the S7-1500 REAL is IEEE 754 single-precision, and the conversion instructions (REAL_TO_DWORD, DWORD_TO_REAL) operate on the same bit layout. When serializing a REAL for transmission over a TCP/UDP socket, the S7 stores it in DB byte order; the S7's Serialize instruction writes the bytes in big-endian (network) order by default. For PROFINET slot data, the byte order is dictated by the PROFIdrive profile (also big-endian for standard signal types).

    Field-Proven Patterns

    • Use a single FB_RxFloatToReal function block with a VALUE : REAL output and a BUFFER : POINTER TO BYTE input. This gives a uniform interface for every float received from the S7.
    • For double-precision, replace TYPE_REAL with TYPE_LREAL and the buffer slice to 8 bytes. The same _BigByteArray_To_AnyType call works.
    • If you have many REALs in a single frame, loop over an indexed buffer and call the conversion in a FOR loop. Expect ~2 µs per conversion on a D435.
    • Document the endianness assumption in the program header comment. Future maintainers will thank you.

    FAQ

    Does SIMOTION use the same IEEE 754 REAL format as the S7-300/400/1500?

    Yes. SIMOTION's REAL (32-bit) and LREAL (64-bit) are IEEE 754 binary32 and binary64, identical to the S7-300/400/1500. The data on the wire is already compatible — the only issue is reinterpreting a byte buffer as a typed variable.

    Which SIMOTION function should I use to convert bytes to a REAL?

    Use _BigByteArray_To_AnyType(destinationPtr, sourcePtr, TYPE_REAL) from the _iec system library. It is available on SIMOTION kernels V4.1 and newer. The function copies 4 bytes and reinterprets them as REAL — no manual byte assembly needed.

    The conversion result is 0.0 even though the bytes are correct — what went wrong?

    Most often the byte buffer was empty or a pointer returned 0 (uninitialized array). Verify the source buffer is populated before calling the conversion, guard with a rxLength >= 4 check on UDP, and confirm the buffer is declared as a static VAR_GLOBAL so ADR() returns a valid address.

    How do I detect little-endian vs big-endian REAL delivery on PROFINET?

    Send a known test value such as REAL#1.0 (IEEE 754 bits 0x3F800000) from the S7. Big-endian delivery shows bytes 3F 80 00 00 in the SIMOTION buffer; little-endian shows 00 00 80 3F. Reverse the byte order before the cast for little-endian sources.

    Can I avoid conversion entirely by binding the PROFINET slot directly to a SIMOTION REAL tag?

    Yes, if the PROFINET slot is configured as a typed signal (e.g., PROFIdrive standard telegram 1–110) and the SIMOTION-side I/O address is declared as REAL or as a DWORD that is then re-cast. For raw user-defined slots, use the _BigByteArray_To_AnyType approach described above.

    Back to blog