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.
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:
- Replace the 27648 scaling constant with 16384 (the downstream full-scale) or with 4096 if the contract limits the magnitude to 12 bits.
- Set LO_LIM / HI_LIM to the engineering range actually used (
0.0..1.4for the 12-bit working window;0.0..5.6if the downstream contract supports 14 bits). - 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;
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:
-
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_CODE→FC_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. - 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.
-
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_POon 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.
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
- Download the project to both CPUs of the 414-4H. Confirm both CPUs are in RUN with synchronisation (no SF, BF or OB72 diagnostics).
- Force
DB_Level.Level_m = 0.000in the watch table of the master CPU. Confirm the downstream reads 0.000 m and that all ofLevel_00..Level_11are FALSE. - Force
Level_m = 0.350. Confirm downstream reads 0.350 m and that onlyLevel_10is TRUE (binary 0100 0000 0000). - Force
Level_m = 1.050. Confirm downstream reads 1.050 m and thatLevel_10andLevel_11are TRUE (binary 1100 0000 0000). - 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). - Force
Level_m = 1.401. ConfirmMv_Ovflatches and that downstream indicates overflow. - Force
Trf_Fault_In = TRUE. ConfirmMv_Trfpropagates. - 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.
- Force a write of code 0 directly into
DB_Level_Outfrom 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.