Scaling 4-20mA Flow Signals for Integer Totalization on S7 PLCs

David Krause14 min read
S7-1200SiemensTechnical 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

Scaling 4-20mA Flow Signals for Integer Totalization on S7 PLCs

A 0-300 m³/h flow transmitter on a 4-20 mA loop must be accumulated against a 20,000 m³ setpoint without losing meaningful precision over a full batching cycle. The naive approach — converting each 1-second sample to a REAL (IEEE 754 single-precision) and adding it to a REAL running total — works for short windows but introduces a measurable, accumulating error in long campaigns. This reference documents the math behind the error, the integer-scaling alternative, and working SCL code for the SIMATIC S7-1200 and S7-1500 platforms. The same methodology applies to S7-300/400 with minor block substitutions.

1. Problem Definition: Totalizing a 0-300 m³/h Flow to 20,000 m³

The signal chain is conventional:

  • Primary element: turbine, vortex, or magnetic flowmeter with a 4-20 mA analog output proportional to 0-300 m³/h.
  • Analog input: SIMATIC SM 1231 AI 4×13 bit (or AI 4×16 bit on the S7-1500) reads the loop current through a 250 Ω sense resistor, producing a raw count proportional to 0-27648.
  • Engineering conversion: the raw count is mapped to 0.0-300.0 m³/h, integrated to m³, and compared to the 20,000 m³ batch setpoint.
  • Sample period: 1 second (or 100 ms, depending on the application), invoked from a cyclic OB.

Per-sample increment at maximum flow:

dV_max = 300 m³/h × (1 s / 3600 s/h) = 0.0833 m³

Per-sample increment at minimum useful flow (4 mA = 0 m³/h, so 4.02 mA ≈ 0.6 m³/h):

dV_min = 0.6 m³/h × (1 s / 3600 s/h) = 1.667e-4 m³

Number of samples required to reach the 20,000 m³ setpoint at nominal 250 m³/h:

N = 20000 m³ / (250 × 1/3600) = 288,000 samples ≈ 80 hours

That is the order of magnitude where IEEE 754 single-precision starts to lose significance on the small addends.

2. Why IEEE 754 Single-Precision Fails for Long Totalization

An S7-1200 REAL is a 32-bit IEEE 754 single-precision value: 1 sign bit, 8 exponent bits, 23 mantissa bits (24 with the implicit leading 1). The mantissa represents roughly 7.22 decimal digits of precision. The smallest representable difference between two adjacent floats at magnitude X is eps(X) ≈ 1.19e-7 × X.

IEEE 754 single-precision granularity vs accumulator magnitude
Accumulator value (m³) ULP (m³) Relative error
1.0 1.19e-7 1.19e-7
100.0 7.63e-6 7.63e-8
1,000.0 6.10e-5 6.10e-8
10,000.0 6.10e-4 6.10e-8
20,000.0 1.22e-3 6.10e-8
100,000.0 7.81e-3 7.81e-8

Single-sample relative precision looks comfortable — about 6×10⁻⁸. The problem is not the single add; it is the cumulative quantization of the small addends. When the per-sample increment is 0.0833 m³ and the running total is 19,999.5 m³, the next add must round to the nearest representable float near 19,999.5, whose ULP is roughly 1.22e-3 m³. The small residual of each round-off persists, and a long sequence of small adds drifts the total by an amount proportional to √N × ULP for the worst case or N × ULP × flow% for the average.

Empirically, summing 0.0833 twenty thousand times into a REAL on a SIMATIC CPU yields totals in the 1660-1665 range when the analytically correct answer is 1666, with drift growing as the setpoint is approached. The drift is small in absolute terms (parts per million) but it is a hard floor: the REAL simply cannot represent the true total to the last m³ at the 20,000 m³ endpoint. A second, worse failure mode appears when intermediate sums underflow the mantissa at high values — the small increments (0.0833) become exactly representable only up to a certain total, after which they vanish into the gap.

3. The 4-20 mA Current Loop Signal Path

Reference: Fluke — What Is a 4-20 mA Current Loop?

The 4-20 mA loop is a series circuit: 24 VDC supply, transmitter, sense resistor (typically 250 Ω at the PLC AI), and return. The current is the process variable; cable resistance and moderate temperature changes do not corrupt the measurement because the signal is current, not voltage. Live-zero detection (current < 4 mA indicates loop fault, not zero flow) is the reason 4 mA — not 0 mA — is used as the lower range value.

The transmitter output stage typically uses a precision current-loop transmitter IC such as the Texas Instruments XTR117, which converts a 0-5 V DAC reference into a regulated 4-20 mA with ±0.05% of full-scale accuracy over the -40 °C to +125 °C range. A similar current loop is used in reverse by the PLC AI module to digitize the loop current: an internal 250 Ω resistor converts 4-20 mA to 1-5 V, and a delta-sigma ADC produces the raw integer count 0-27648 (S7 standard) or 0-32767 (legacy / some S7-300 AI8 modules).

Engineering-unit conversion from 4-20 mA loop
mA PLC raw (0-27648) Flow (m³/h)
4.000 0 0.0
8.000 6912 75.0
12.000 13824 150.0
16.000 20736 225.0
20.000 27648 300.0
3.600 -552 (under-range) FAULT (wire break)

The TIA Portal library blocks NORM_X and SCALE_X perform the linear conversion; the legacy FC105 / FC106 pair from the STEP 7 Standard Library do the same on classic S7-300/400. The result of either block is a REAL in engineering units; the question is what you do with it next.

4. The Integer-Scaling Strategy

Replace the per-sample REAL add with a 32-bit signed integer (DINT in Siemens terminology) add. The REAL is computed once per sample to drive the HMI display, but the cumulative total is maintained in raw integer counts with a fixed engineering-unit scale factor.

Pick the scale so that 1 integer count equals a useful engineering-unit increment. For a 20,000 m³ totalizer with 0.01 m³ resolution, use:

scale = 100 counts / m³      (1 count = 0.01 m³)
total_DINT_max = 20000 × 100 = 2,000,000 counts

This fits comfortably inside the 32-bit signed range (±2.147 × 10⁹). Even at 0.001 m³ resolution (scale = 1000), 20,000 m³ is 20,000,000 counts — still 1 % of the DINT range. The accumulation becomes a single DINT add per scan with no rounding at all.

Per-sample flow contribution in counts (1-second sample):

dCounts = ROUND( flow_m3_per_h × (1/3600) × scale )

For 250 m³/h and scale = 100:

dCounts = ROUND( 250 × 1/3600 × 100 ) = ROUND(6.944) = 7 counts per scan

Reaching 2,000,000 counts at 7 counts/s requires 285,714 seconds (79.4 hours), matching the analytical batch time. The accumulator can never drift: it is an integer counter.

5. Resolution vs. Range Trade-Off

Scale factor selection for 0-20,000 m³ totalizer
Resolution (m³/count) Scale (counts/m³) Total at end of batch (counts) DINT % used Min measurable flow @ 1 s sample (m³/h)
0.1 10 200,000 0.009 % 36.0
0.01 100 2,000,000 0.09 % 3.6
0.001 1000 20,000,000 0.93 % 0.36
0.0001 10000 200,000,000 9.3 % 0.036

The 'min measurable flow' column shows the smallest flow that produces ≥1 count per 1-second sample. Below that, the rate of change rounds to zero between scans and the totalizer stalls until the flow exceeds the threshold. For a 0-300 m³/h span with 4 mA = 0, 0.36 m³/h is the practical low cutoff at 0.001 m³ resolution — well below the meter's accuracy floor, so the resolution is appropriate.

Selection rule: pick the resolution to be ≥ 1/10 of the meter's stated accuracy class. A 1 % FS meter on 300 m³/h has an inherent ±3 m³/h uncertainty; 0.01 m³ resolution is over-spec. A 0.5 % Coriolis meter is meaningfully benefited by 0.001 m³ resolution.

6. S7-1200 / S7-1500 Implementation in SCL

The following Function Block (FB) encapsulates the scaling, integer accumulation, and a REAL view for the HMI. It targets TIA Portal V16+ on S7-1200 (Firmware 4.4+) and S7-1500 (Firmware 2.6+). The legacy equivalent for S7-300/400 STEP 7 V5.5 uses the same logic with FC105 replacing NORM_X/SCALE_X.

FUNCTION_BLOCK FB_FlowTotalizer
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
    iRawAI          : INT;      // 0-27648 from SM 1231 AI
    iReset          : BOOL;     // Rising edge clears accumulator
    iSamplePeriod_s : REAL := 1.0;
END_VAR
VAR_OUTPUT
    qFlow_m3h   : REAL;     // instantaneous engineering units
    qTotal_m3   : REAL;     // engineering-unit view (HMI)
    qTotalRaw   : DINT;     // integer accumulator (persisted)
    qWireBreak  : BOOL;     // AI under-range flag
END_VAR
VAR
    sLastTotal   : DINT;     // internal persistent total
    sScale       : DINT := 100;  // 0.01 m³ / count
    sFlowEU      : REAL;
    sRTrigReset  : R_TRIG;
END_VAR
BEGIN
    // --- Wire break detection (raw < 0 means < 4 mA) ---
    #qWireBreak := (#iRawAI < 0) OR (#iRawAI = 0 AND NOT #iReset);

    // --- Scale 0-27648 to 0.0-300.0 m³/h ---
    // TIA Portal intrinsic; same math as legacy FC105
    #sFlowEU := SCALE_X(
        MIN      := 0.0,
        VALUE    := NORM_X(
                       MIN := 0,
                       VALUE := INT_TO_REAL(#iRawAI),
                       MAX := 27648.0),
        MAX      := 300.0);
    #qFlow_m3h := #sFlowEU;

    // --- Integer accumulation: m³/s × scale = counts/sample ---
    IF NOT #qWireBreak THEN
        #sLastTotal := #sLastTotal +
            REAL_TO_DINT( #sFlowEU * (1.0/3600.0) * #iSamplePeriod_s * INT_TO_REAL(#sScale) );
    END_IF;

    // --- Reset edge ---
    #sRTrigReset(CLK := #iReset);
    IF #sRTrigReset.Q THEN
        #sLastTotal := 0;
    END_IF;

    #qTotalRaw := #sLastTotal;
    #qTotal_m3 := DINT_TO_REAL(#sLastTotal) / INT_TO_REAL(#sScale);
END_FUNCTION_BLOCK

Persistent storage: declare the instance DB of FB_FlowTotalizer with the Retain attribute on sLastTotal so a power cycle does not zero the batch. In TIA Portal, set the DB property Retain = Set in IDB and check the Retain column for that tag.

7. Cross-Platform Equivalents

The integer-scaling pattern is platform-agnostic. Implementation differences:

Integer totalizer equivalent by platform
Platform Integer type Scale block Add instruction Persistent tag
Siemens S7-1200/1500 DINT (32-bit signed) NORM_X / SCALE_X Standard + in SCL Retain attribute in instance DB
Siemens S7-300/400 DINT (32-bit signed) FC105 / FC106 ADD_DI in STL/SCL Retain in instance DB
Allen-Bradley CompactLogix / ControlLogix DINT (32-bit signed) SCL or ADD with scaling expression ADD instruction in ladder Tag with Retained checked
Beckhoff TwinCAT 3 DINT (32-bit signed) Function block from TcPlcUtilities Standard IEC + VAR RETAIN PERSISTENT
Codesys v3 (Wago, Eaton, others) DINT (32-bit signed) Inline arithmetic in ST Standard IEC + VAR RETAIN PERSISTENT
Schneider M340 / M580 DINT (32-bit signed) Analog scaling EF or ST Standard IEC + Retain attribute in DFB

Reference manuals: Siemens — S7-1200 Programmable Controller System Manual, Rockwell — Logix Designer / Studio 5000 Application Development.

8. Accuracy Budget and Error Sources

The integer totalizer's accuracy is bounded by the analog-input resolution and the meter, not by the CPU's floating-point engine. A budget for a 1 %-class turbine meter on a 13-bit AI:

Per-component error contribution to a 20,000 m³ batch
Source Magnitude Contribution to 20,000 m³ (m³)
Turbine meter repeatability ±0.5 % FS ±100
Turbine meter linearity ±0.5 % FS ±100
4-20 mA loop (XTR117 class) ±0.05 % FS ±10
SM 1231 AI 13-bit gain error ±0.3 % FS ±60
SM 1231 AI 13-bit integral non-linearity ±0.05 % FS ±10
Sample period quantization (1 s) ±1 s per scan ±0.0833 × N
Integer rounding (0.01 m³ res) ±0.005 m³/sample ±0.5 max
RSS total (excluding meter) — ±62

Errors dominated by the meter itself are intrinsic to the application. Replacing a turbine with a Coriolis meter typically drops repeatability to ±0.05 %, making the integer-scaling decision the difference between meaningful and meaningless precision at the third decimal place. Profibus-based Coriolis meters (e.g., Endress+Hauser Promass, Emerson Micro Motion) can deliver digital engineering units directly to the PLC over Profibus PA / DP-V1, bypassing the analog path entirely; reference Profibus & Profinet International (PI) for the profile specification.

9. Verification and Commissioning Procedure

  1. Loop check. Disconnect the field device, inject 4.000 mA, 12.000 mA, and 20.000 mA with a calibrated Fluke 754 or Beamex MC6 calibrator. Confirm the SM 1231 raw value is 0, 13824, and 27648 respectively (or 0, 16384, 32767 on legacy 15-bit modules).
  2. Scale verification. With 12.000 mA injected, confirm qFlow_m3h reads 150.0 ± 0.2. A 13-bit AI has an LSB of 27648/2¹³ ≈ 3.4 counts, so a tolerance of ±0.1 m³/h is achievable.
  3. Totalizer cross-check. Inject a constant 12.000 mA (150 m³/h) for 60 seconds. Expected qTotal_m3 after 60 s: 150 × 60/3600 = 2.500 m³ ± meter / AI tolerance.
  4. Integer round-trip. Verify qTotal_m3 = qTotalRaw / scale exactly. Any drift here indicates a missing ROUND or a REAL math path on the total.
  5. Retain test. Power-cycle the CPU with the totalizer non-zero. Confirm qTotalRaw restores to the last pre-cycle value.
  6. Wire-break test. Open the loop (current drops to 0). Confirm qWireBreak asserts and the accumulator freezes. The standard Siemens AI driver sets bit 7 of the quality byte for wire break; cross-check that diagnostic is wired to iRawAI < 0.
  7. Long-run drift test. Run the batch to completion. Compare the final qTotal_m3 with the meter's local display and a reference totalizer (e.g., a Coriolis reference total or weigh-scale). Tolerance: ± meter accuracy specification.
Safety: on flammable-service or custody-transfer applications, verify the totalizer is calibrated against a provable reference (e.g., a weigh tank of known volume) and that the meter's temperature/pressure compensation is enabled if reporting volume at reference conditions rather than line conditions.

10. Frequently Asked Questions

Why does the AI raw count go to a negative number on a Siemens SM 1231 when the loop is broken?

The SM 1231 13-bit AI module reports wire break as -32768 (or 0, depending on the diagnostic configuration). Negative raw values are the firmware's way of flagging under-range. Use the quality byte of the channel or the sign of the raw count to drive the qWireBreak flag, and freeze the accumulator on that condition to prevent the negative raw from corrupting the integer total. See the S7-1200 System Manual section on analog input diagnostics.

Can I use LREAL (64-bit double) instead of DINT to fix the precision issue?

Yes. The 52-bit mantissa of IEEE 754 double precision gives roughly 15.9 decimal digits, which is more than enough for a 20,000 m³ totalizer. The trade-off is CPU load: an S7-1200 takes roughly 4-6× longer to execute LREAL arithmetic than DINT. For high-rate sample loops (sub-100 ms) or large-scale installations with many totalizers, the integer approach scales better. Use LREAL only when sample rates are low and the engineering time to debug floating-point edge cases is more expensive than the CPU cost.

What scale factor should I pick for a 0-100 L/min flow with 0.1 mL resolution on a 10,000 L batch?

Use scale = 10,000 counts / L (0.1 mL = 0.0001 L = 1 count). The totalizer reaches 100,000,000 counts at end of batch, which is 4.7 % of the 32-bit signed range — well within DINT limits. For longer batches (multiple shifts) or higher resolution (0.01 mL), move to LREAL or to a 64-bit integer via the S7-1500 LWORD type. Reference: S7-1200 System Manual — supported data types.

How do I migrate a legacy FC105-based real totalizer to integer scaling without changing the HMI?

Keep the HMI tag mapped to a REAL in engineering units. Inside the FB, perform all accumulation in DINT and expose qTotal_m3 as a REAL derived from qTotalRaw / scale. The HMI sees an unchanged tag name and format; only the internal arithmetic changes. This is a code-only change with no HMI modification required, and it can be done block-by-block during a hot cutover if the instance DB is non-retain only after commissioning.

Does the integer approach work with HART or Profibus PA transmitters that report engineering units digitally?

Yes. When the transmitter delivers engineering units digitally (e.g., Endress+Hauser Promass 100 over Profibus PA), replace the NORM_X / SCALE_X chain with the cyclic value from the slot. The integer accumulation logic in the FB remains unchanged; only the input source changes. The Profibus PA profile for flowmeters delivers the volume increment directly in many implementations, which can be summed as a DINT in a single ADD without the per-sample flow-to-volume conversion. Reference: PI — PROFIBUS PA Profile for Flow.

11. Field-Commissioning Notes

Three caveats from deployment experience:

  1. Sample period drift. OB1 cyclic time on an S7-1200 is not guaranteed to be exactly 1 s; under heavy communication load it can stretch to 1.1-1.5 s. Use a hardware-driven periodic interrupt OB (e.g., OB30) at a fixed 1000 ms interval, or feed the FB a measured iSamplePeriod_s from the OB cycle-time clock. The integer accumulator is forgiving of period jitter as long as iSamplePeriod_s reflects reality.
  2. Scale-factor overflow with very high resolution. If you choose 0.0001 m³ resolution (scale = 10,000) and the batch exceeds 214,748 m³, the DINT overflows. For a 100,000 m³ batch, drop to scale = 1000 (0.001 m³ resolution) or use LREAL.
  3. Compensated vs. uncompensated volume. If the meter is configured for line-condition volume, temperature/pressure variations will appear in the total. If the application requires normal-condition (reference) volume, ensure the meter is delivering compensated volume to the analog output. Reference: Fluke — 4-20 mA current loop basics for the relationship between loop current and the engineering unit the transmitter chooses to report.

The integer-scaling approach is a low-effort, high-reliability change to the totalizer path. It eliminates an entire class of subtle floating-point issues at the cost of one DINT tag and a single ROUND per scan. For batching applications that have run for years on a REAL total and have always been 'a few liters off', the integer path is almost always the fix.

Back to blog