S7-414H Converting REAL Level Value to 12-Bit BOOL Pattern

David Krause17 min read
S7-400SiemensTechnical Reference
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

S7-414H Converting REAL Level Value to 12-Bit BOOL Pattern

This reference covers a recurring gateway task on the Siemens S7-414H redundant CPU: an upstream station publishes a 32-bit IEEE-754 REAL level value, the local CPU has to break that floating-point number down into 12 individual BOOL flags (Level_00...Level_11) plus a sign and three status bits, and the downstream station reassembles the bits into a code and converts the code into engineering units. The conversion looks trivial but in practice trips up engineers for three reasons: (a) the Siemens standard blocks FC105 / FC106 are linear and miss the low-end compression of the downstream curve, (b) the 12-bit width only addresses codes 0..4095, and (c) the H-system must run the conversion on both CPUs to avoid a stale pattern after a failover. The notes below give working SCL code, a bit-decomposition FC, the parameter set that makes UNSCALE behave correctly, and the OB/partition wiring required for an S7-414H redundant pair.

Bit significance convention used throughout: Level_00 = LSB (value 1), Level_11 = MSB of the 12-bit data field (value 2048). Sign, overflow, transmitter-fault and spare flags are outside the 12-bit data field and are reported as separate BOOLs. Total length is 16 bits.

1. I/O Contract and Bit Layout

The S7-414H is the gateway. The downstream contract is fixed: 16 discrete flags arrive at fixed positions and are reassembled into a code by the receiving PLC. The contract for the present application is:

Boolean tag Bit position Significance Used for
Level_00 Bit 0 (data LSB) 2⁰ = 1 Magnitude LSB
Level_01 Bit 1 2¹ = 2 Magnitude
Level_02 Bit 2 2² = 4 Magnitude
Level_03 Bit 3 2³ = 8 Magnitude
Level_04 Bit 4 2⁴ = 16 Magnitude
Level_05 Bit 5 2⁵ = 32 Magnitude
Level_06 Bit 6 2⁶ = 64 Magnitude
Level_07 Bit 7 2⁷ = 128 Magnitude
Level_08 Bit 8 2⁸ = 256 Magnitude
Level_09 Bit 9 2⁹ = 512 Magnitude
Level_10 Bit 10 2¹⁰ = 1024 Magnitude
Level_11 Bit 11 (data MSB) 2¹¹ = 2048 Magnitude MSB
Level_S Bit 12 Sign (1 = negative) Sign of engineering value
Mv_Ovf Bit 13 Measured-value overflow Status flag
Mv_Trf Bit 14 Transmitter fault Status flag
Mv_Sp Bit 15 Spare / reserved Status flag

Magnitude range with the 12 data bits: 0..4095 codes. The downstream station applies a non-linear conversion to map codes into engineering units. The exact mapping must be confirmed with the downstream station contract — see the calibration table in the next section.

2. Downstream Calibration Curve

Calibration table captured from the field, listing the engineering value displayed by the downstream station for each code assembled from the BOOL flags:

Code (decimal) Code (binary, B11..B0) Engineering value Code / value ratio
0 0000 0000 0000 0.000 m
16 0000 0001 0000 0.005 m 3200
32 0000 0010 0000 0.010 m 3200
64 0000 0100 0000 0.020 m 3200
128 0000 1000 0000 0.045 m 2844
256 0001 0000 0000 0.085 m 3012
512 0010 0000 0000 0.175 m 2926
1024 0100 0000 0000 0.350 m 2926
2048 1000 0000 0000 0.700 m 2926
3072 1100 0000 0000 1.050 m 2926
4096* 1 0000 0000 0000 1.400 m 2926
6144* 1 1000 0000 0000 2.100 m 2926
8192* 10 0000 0000 0000 2.800 m 2926
9368* 10 0100 1001 1000 3.200 m 2928
12288* 11 0000 0000 0000 4.200 m 2926
16384* 100 0000 0000 0000 5.600 m 2926

* Codes above 4095 require more than 12 data bits. Either the downstream contract reuses the same BOOL names with shifted significance (e.g. Level_00 becomes bit 2, etc.), or the receiving station applies its own internal scaling factor. This must be clarified with the downstream documentation before commissioning. For the analysis below, treat codes 0..4095 (engineering range 0..1.400 m) as the safe working window that fits the documented BOOL tags.

Code-to-value ratio analysis: from code 256 upward the ratio is a constant 2926 codes per metre, i.e. the relationship is linear. Below code 256 the ratio rises to 3200, indicating that the downstream display rounds small values to the nearest 0.005 m increment. The overall behaviour is therefore piecewise linear with a low-end discontinuity, not a true non-linear curve. UNSCALE can be made to work, but the engineer must choose between (a) accurate low-end at the cost of high-end scale (use the calibration table directly), or (b) linear scaling on codes ≥ 256 with degraded low-end resolution.

3. Why the Standard UNSCALE Block Initially Fails

FC106 UNSCALE from the STEP 7 Standard Library → TI-S7 Converting Blocks package applies a linear mapping of the form:

OUT = ((IN - LO_LIM) / (HI_LIM - LO_LIM)) × 27648

Used naively with LO_LIM = 0.0 and HI_LIM = 5.6 and an INT output, the result is off by a factor of about 4 (because the linear full-scale of 5.6 m maps to 27648, but the downstream station codes up to 16384 instead). The mistake usually diagnosed is "UNSCALE does not produce a 12-bit number." Two adjustments fix it:

  1. Replace the 27648 scaling constant with 16384 (the downstream full-scale) or with 4096 if the contract limits the magnitude to 12 bits.
  2. Set LO_LIM / HI_LIM to the engineering range actually used (0.0..1.4 for the 12-bit working window; 0.0..5.6 if the downstream contract supports 14 bits).
  3. Bias LO_LIM slightly positive (e.g. 0.005) to compensate for the downstream low-end rounding if sub-0.005 m accuracy is required.

Working UNSCALE call (linear, 12-bit, 0..1.4 m range):

CALL FC106
  IN     := DB_Level.Level_m       // REAL input, 0.0..1.4 m
  HI_LIM := 1.4                    // engineering full-scale
  LO_LIM := 0.0                    // engineering zero
  BIPOLAR:= FALSE                  // unipolar mode
  RET_VAL:= wFC106_RetVal          // WORD, error code (W#16#0000 = OK)
  OUT    := wCode                  // INT, 0..27648

Then re-scale wCode from the 0..27648 range to the 0..4095 range used by the BOOL decomposition:

// wCode is INT 0..27648; dCode12 is DINT 0..4095
dCode12 := DINT_TO_REAL(wCode) * 4095.0 / 27648.0;
dCode12 := REAL_TO_DINT(dCode12);
// Clamp
IF dCode12 < 0    THEN dCode12 := 0;    END_IF;
IF dCode12 > 4095 THEN dCode12 := 4095; END_IF;

This two-step (UNSCALE + re-scale) is what the original commissioning team eventually applied and it produces the correct codes for codes 0..4095. UNSCALE used in this way is not the problem; the problem was always the LO_LIM / HI_LIM parameter set.

4. Pure SCL Alternative (No Standard Library Call)

If FC106 is unavailable or undesirable (e.g. on a firmware variant where the Standard Library is not licensed), the equivalent logic fits in 20 lines of SCL:

FUNCTION FC_REAL_TO_CODE : VOID
TITLE  = 'REAL level m -> 12-bit code (UNSCALE equivalent)'
VERSION: '1.0'
VAR_INPUT
  Level_m  : REAL;     // engineering value
  Range_Hi : REAL := 1.4;
  Range_Lo : REAL := 0.0;
END_VAR
VAR_OUTPUT
  Code12   : DINT;     // 0..4095
  Ovr      : BOOL;     // out-of-range high
  Udr      : BOOL;     // out-of-range low
END_VAR
VAR_TEMP
  rSpan  : REAL;
  rNorm  : REAL;
  rScaled: REAL;
END_VAR
BEGIN
  rSpan := Range_Hi - Range_Lo;
  IF rSpan <= 0.0 THEN
    Code12 := 0; Ovr := TRUE; Udr := FALSE;
    RETURN;
  END_IF;
  rNorm := (Level_m - Range_Lo) / rSpan;
  IF rNorm < 0.0 THEN
    Code12 := 0; Ovr := FALSE; Udr := TRUE;
    RETURN;
  ELSIF rNorm > 1.0 THEN
    Code12 := 4095; Ovr := TRUE; Udr := FALSE;
    RETURN;
  END_IF;
  rScaled := rNorm * 4095.0 + 0.5;        // round
  Code12  := REAL_TO_DINT(rScaled);
  IF Code12 > 4095 THEN Code12 := 4095; END_IF;
  Ovr := FALSE; Udr := FALSE;
END_FUNCTION

This block compiles on STEP 7 V5.5 SPx for the S7-414H (CPU firmware V4.0.x or later) and produces identical output to FC106 with the correct parameter set.

5. Bit Decomposition of the Code

Once the 12-bit integer code is available, decompose it into the 12 individual BOOL tags. SCL version using shift-right and AND:

FUNCTION FC_Decompose12 : VOID
TITLE  = 'Decompose 12-bit DINT into 12 BOOLs'
VERSION: '1.0'
VAR_INPUT
  Code12 : DINT;
END_VAR
VAR_OUTPUT
  Level_00 : BOOL; Level_01 : BOOL; Level_02 : BOOL;
  Level_03 : BOOL; Level_04 : BOOL; Level_05 : BOOL;
  Level_06 : BOOL; Level_07 : BOOL; Level_08 : BOOL;
  Level_09 : BOOL; Level_10 : BOOL; Level_11 : BOOL;
END_VAR
BEGIN
  Level_00 := (Code12 SHR  0) AND 16#0001 <> 0;
  Level_01 := (Code12 SHR  1) AND 16#0001 <> 0;
  Level_02 := (Code12 SHR  2) AND 16#0001 <> 0;
  Level_03 := (Code12 SHR  3) AND 16#0001 <> 0;
  Level_04 := (Code12 SHR  4) AND 16#0001 <> 0;
  Level_05 := (Code12 SHR  5) AND 16#0001 <> 0;
  Level_06 := (Code12 SHR  6) AND 16#0001 <> 0;
  Level_07 := (Code12 SHR  7) AND 16#0001 <> 0;
  Level_08 := (Code12 SHR  8) AND 16#0001 <> 0;
  Level_09 := (Code12 SHR  9) AND 16#0001 <> 0;
  Level_10 := (Code12 SHR 10) AND 16#0001 <> 0;
  Level_11 := (Code12 SHR 11) AND 16#0001 <> 0;
END_FUNCTION

STL alternative for engineers who prefer ladder/text:

L     #Code12
T     #tmpCode       // copy to avoid mutating input
SET
SAVE
=     #Level_00      // bit 0 always 0 if Code12 even
A     #tmpCode.0
=     #Level_00
A     #tmpCode.1
=     #Level_01
A     #tmpCode.2
=     #Level_02
A     #tmpCode.3
=     #Level_03
A     #tmpCode.4
=     #Level_04
A     #tmpCode.5
=     #Level_05
A     #tmpCode.6
=     #Level_06
A     #tmpCode.7
=     #Level_07
A     #tmpCode.8
=     #Level_08
A     #tmpCode.9
=     #Level_09
A     #tmpCode.10
=     #Level_10
A     #tmpCode.11
=     #Level_11
CLR

Verify the assignment with the calibration table:

Engineering value Target code Expected BOOL pattern (B11..B00)
0.000 m 0 0000 0000 0000
0.085 m 256 0001 0000 0000
0.350 m 1024 0100 0000 0000
1.050 m 3072 1100 0000 0000
1.400 m 4095 1111 1111 1111

6. Status and Sign Bit Logic

The sign bit, overflow, transmitter-fault and spare bits are independent of the magnitude decomposition:

// Sign
Level_S := (Level_m < 0.0);
IF Level_S THEN
  // magnitude is stored as absolute value, downstream applies sign
  rMag := ABS(Level_m);
ELSE
  rMag := Level_m;
END_IF;

// Overflow: input range exceeded
Mv_Ovf := (Level_m > Range_Hi) OR Overflow_In;

// Transmitter fault: pass-through from upstream status
Mv_Trf := Trf_Fault_In;

// Spare: reserved, leave FALSE unless contract specifies
Mv_Sp  := Spare_In OR FALSE;
The Level_S bit must NOT be folded into the 12-bit magnitude. The downstream station reads it separately and applies it after the non-linear conversion. Folding the sign into the code produces wrong magnitude readings on negative values.

7. IEEE-754 Insight (Optional)

The incoming REAL value is a 32-bit IEEE-754 single-precision number with the layout: bit 31 = sign, bits 30..23 = 8-bit exponent (bias 127), bits 22..0 = 23-bit mantissa. If the upstream value is already in a fixed binary format (e.g. directly written from another PLC's code register), an AT overlay on a temporary variable lets the S7-414H read and modify the bits without conversion:

VAR_TEMP
  rIn    : REAL;
  dwView : DWORD AT rIn;       // alias to the same 32 bits
END_VAR

For the present gateway task the AT view is not strictly required — the conversion is engineering-value → code → bits. It is included here because (a) it is the standard Siemens technique for "bit manipulation on a REAL", and (b) it is useful for diagnostic displays that show the IEEE-754 sign and exponent for invalid-value detection. Reference for the format: IEEE Std 754-2019, "IEEE Standard for Floating-Point Arithmetic".

An off-line IEEE-754 calculator such as the h-schmidt.net float converter (h-schmidt.net IEEE-754) is handy during commissioning for cross-checking a suspect binary pattern, e.g. confirming that 16#40400000 decodes to 3.0.

8. S7-414H Redundancy Wiring

The S7-414H is an H-system: two CPUs of the same type run in parallel and a single master writes to the I/O. The downstream BOOL flags are typically written to a digital-output module (SM 422) or to a PROFIBUS DP / PROFINET slave. Three wiring rules must be observed:

  1. Run the conversion on both CPUs. STEP 7 does not mirror user-program output values automatically between the master and standby. The same FC chain (FC_REAL_TO_CODEFC_Decompose12 → status logic) must be present and called in the standby project. The standby's process image will then match the master's and a failover produces no glitch.
  2. Call from a time-triggered OB, not OB1. OB1 on the H-system runs once per cycle on the active CPU; after a failover the new master has the latest values only if the previous scan completed. Calling the FCs from OB35 (cyclic interrupt, 100 ms typical) on both CPUs guarantees that both have a fresh result before a possible failover.
  3. Use a Process Image Partition (PIP) for the DO word. Assign the 16 BOOL flags to a PIP and call SFC 31 UPDAT_PI / SFC 32 UPDAT_PO on the partition at the end of the OB35 that produces the values. This prevents the downstream station from reading a partially-updated pattern (e.g. Level_07..Level_00 updated but Level_11..Level_08 still old).

Reference for H-system programming: Siemens "S7-400H Fault-Tolerant Systems" system manual, available from Siemens Industry Online Support.

OB72 handling: In OB72 (redundancy-loss / failover), the new master continues with the latest process image. To prevent the downstream station from latching a stale value during the brief resync window, force Mv_Trf := TRUE for the first OB72 cycle so the downstream freezes its display until the resync completes.

9. Wiring to the Downstation

The 16 flags are typically placed in a shared DB consumed by the PROFIBUS DP slave or PROFINET device configuration:

DATA_BLOCK DB_Level_Out
STRUCT
  Level_00 : BOOL;     // offset 0.0
  Level_01 : BOOL;     // offset 0.1
  Level_02 : BOOL;     // offset 0.2
  Level_03 : BOOL;     // offset 0.3
  Level_04 : BOOL;     // offset 0.4
  Level_05 : BOOL;     // offset 0.5
  Level_06 : BOOL;     // offset 0.6
  Level_07 : BOOL;     // offset 0.7
  Level_08 : BOOL;     // offset 1.0
  Level_09 : BOOL;     // offset 1.1
  Level_10 : BOOL;     // offset 1.2
  Level_11 : BOOL;     // offset 1.3
  Level_S  : BOOL;     // offset 1.4
  Mv_Ovf   : BOOL;     // offset 1.5
  Mv_Trf   : BOOL;     // offset 1.6
  Mv_Sp    : BOOL;     // offset 1.7
END_STRUCT
END_DATA_BLOCK

Map this DB to a PIP and to a PROFINET slot / PROFIBUS slot using the HW Config of STEP 7. Ensure that the PIP is updated by SFC 32 UPDAT_PO at the end of the producing OB so all 16 bits change atomically.

10. Commissioning Procedure

  1. Download the project to both CPUs of the 414-4H. Confirm both CPUs are in RUN with synchronisation (no SF, BF or OB72 diagnostics).
  2. Force DB_Level.Level_m = 0.000 in the watch table of the master CPU. Confirm the downstream reads 0.000 m and that all of Level_00..Level_11 are FALSE.
  3. Force Level_m = 0.350. Confirm downstream reads 0.350 m and that only Level_10 is TRUE (binary 0100 0000 0000).
  4. Force Level_m = 1.050. Confirm downstream reads 1.050 m and that Level_10 and Level_11 are TRUE (binary 1100 0000 0000).
  5. Force Level_m = 1.400. Confirm downstream reads 1.400 m and that all 12 magnitude flags are TRUE (binary 1111 1111 1111 = code 4095).
  6. Force Level_m = 1.401. Confirm Mv_Ovf latches and that downstream indicates overflow.
  7. Force Trf_Fault_In = TRUE. Confirm Mv_Trf propagates.
  8. Pull the master CPU's power for 5 s. Confirm the standby takes over, the 16 flags remain unchanged across the failover (verify in PIP), and the downstream display does not glitch.
  9. Force a write of code 0 directly into DB_Level_Out from the watch table (bypass the FC) and confirm the downstream reads 0.000 m. Repeat with code 1024, 2048, 4095 to validate the PIP wiring.

11. Troubleshooting Matrix

Symptom Likely cause Fix
Downstream reads 1/4 of true value across the whole range UNSCALE used with default LO_LIM=0 / HI_LIM=100 or similar wrong range; output treated as INT but not re-scaled to 12 bits Set HI_LIM to engineering full-scale (1.4 m for 12-bit) and re-scale INT 0..27648 to INT 0..4095 before bit decomposition
Downstream reads true value at high end but wrong at low end Linear scaling with downstream's 0.005 m low-end rounding Apply a small positive LO_LIM bias (e.g. 0.005) or use a piecewise table
Downstream shows wrong value only after a failover Conversion runs only on master Call FCs from OB35 on both CPUs and assign output to PIP
One cycle of stale value after failover OB1 call, no PIP Move call to OB35 and use SFC 32 UPDAT_PO on the partition
Level_S bit TRUE causes wrong magnitude Sign bit OR-ed into the 12-bit field by mistake Keep Level_S as separate bit; do not fold into magnitude
Downstream reads 0.045 m when code 128 is forced Bit order reversed in the BOOL tags Swap Level_00 ↔ Level_11 in the assignment
Code 16384 reports compile error Type too large for 12 bits Confirm downstream contract; restrict to codes 0..4095 unless additional bits are provided
SQRT() throws exception at Level_m = 0 Math approximation used, not clamped Add IF Level_m <= 0 THEN dCode := 0 END_IF guard before SQRT

12. When to Use a Look-Up Table Instead

For plants that need sub-LSB accuracy over the full range, replace the FC chain with a 16-entry lookup table that uses the documented calibration points and linear interpolation between them. SCL template:

FUNCTION_BLOCK FB_LUT_ToCode
VAR CONSTANT
  N : INT := 16;
END_CONST
VAR
  tabCode : ARRAY[1..16] OF DINT := (0,16,32,64,128,256,512,1024,2048,3072,4096,6144,8192,9368,12288,16384);
  tabVal  : ARRAY[1..16] OF REAL := (0.000,0.005,0.010,0.020,0.045,0.085,0.175,0.350,0.700,1.050,1.400,2.100,2.800,3.200,4.200,5.600);
  idx     : INT;
  rFrac   : REAL;
  dCode   : DINT;
END_VAR
VAR_INPUT
  Level_m : REAL;
END_VAR
VAR_OUTPUT
  Code12  : DINT;
END_VAR
BEGIN
  // Locate bracketing segment
  idx := 1;
  WHILE (idx < N) AND (tabVal[idx+1] < Level_m) DO
    idx := idx + 1;
  END_WHILE;
  IF idx >= N THEN
    Code12 := tabCode[N];
  ELSIF Level_m <= tabVal[1] THEN
    Code12 := tabCode[1];
  ELSE
    rFrac := (Level_m - tabVal[idx]) / (tabVal[idx+1] - tabVal[idx]);
    dCode := tabCode[idx] + REAL_TO_DINT(rFrac * DINT_TO_REAL(tabCode[idx+1] - tabCode[idx]) + 0.5);
    Code12 := dCode;
  END_IF;
  // Clamp to 12-bit working range
  IF Code12 > 4095 THEN Code12 := 4095; END_IF;
END_FUNCTION_BLOCK

The lookup table delivers ±1 LSB accuracy across the whole engineering range and respects the downstream's piecewise-linear behaviour. The drawback is 32 constants of memory and an O(N) linear search; both are negligible on the 414-4H (instruction time < 50 µs for N=16).

13. Summary

For an S7-414H gateway that has to deliver a 12-bit BOOL-coded level to a downstream station, the correct sequence is: (1) take the incoming REAL from the upstream station, (2) scale it to a 12-bit integer code using FC106 UNSCALE with HI_LIM set to the engineering full-scale (1.4 m for the documented 12-bit window, 5.6 m if the downstream contract supports wider codes), (3) re-scale the INT output to 0..4095 if UNSCALE was configured for the 0..27648 INT range, (4) decompose the integer into 12 individual Level_00..Level_11 BOOL flags using shift-and-mask or bit-test assignment, (5) set Level_S, Mv_Ovf, Mv_Trf and Mv_Sp from the corresponding inputs and computed overflow flag, and (6) wire the 16-bit pattern to a PIP updated by OB35 on both CPUs of the H-system. The most common commissioning error is leaving UNSCALE's HI_LIM at a default value that does not match the downstream full-scale, which causes a constant-magnitude error of 50 % to 80 %.

FAQ

Why does FC106 UNSCALE give a wrong value at every code?

UNSCALE is linear and the most common configuration error is leaving HI_LIM at the default 100.0 or at the wrong engineering range. Set HI_LIM to 1.4 m for the 12-bit working window or 5.6 m for the 14-bit window, and re-scale the INT output from 0..27648 to the 0..4095 range used by the 12 BOOL flags before bit decomposition.

How many bits does the downstream station actually evaluate?

Codes 0..4095 fit in the 12 data bits (Level_00..Level_11). The calibration table extends to code 16384, which requires 14 bits. Until the downstream contract is clarified, restrict output codes to 0..4095 (engineering range 0..1.400 m with the linear UNSCALE scaling).

Do I need to run the conversion on both CPUs of the 414-4H?

Yes. The S7-414H does not automatically mirror user-program output values between the master and standby. Call the conversion FCs from OB35 (100 ms cyclic) on both CPUs and place the 16 output bits in a Process Image Partition updated by SFC 32 UPDAT_PO at the end of the OB.

What is the resolution at code 128 (engineering 0.045 m)?

Around code 128 the local resolution is approximately 0.000391 m per LSB (between codes 64 and 256). Below code 16 the resolution degrades sharply because the downstream display rounds to 0.005 m increments; use a positive LO_LIM bias of 0.005 if sub-0.005 m accuracy is required at the low end.

Can an AT-view be used to extract bits from a REAL value?

Yes. Declare rIn : REAL; and dwView : DWORD AT rIn; in a TEMP or STAT section. Bit 31 is the IEEE-754 sign, bits 30..23 the exponent, bits 22..0 the mantissa. This is useful for diagnostics but is not part of the engineering-to-code conversion, because the upstream REAL is already in metres rather than a raw binary code.

Is there an alternative to UNSCALE that handles the low-end rounding automatically?

Yes. Use a 16-point lookup table (FB_LUT_ToCode) with the documented calibration pairs and linear interpolation between them. The lookup delivers ±1 LSB accuracy across the full range and respects the downstream's 0.005 m low-end rounding. CPU load is < 50 µs per call on the 414-4H.

Back to blog