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.
| 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).
| 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
| 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.
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:
| 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:
| 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
- 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).
-
Scale verification. With 12.000 mA injected, confirm
qFlow_m3hreads 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. -
Totalizer cross-check. Inject a constant 12.000 mA (150 m³/h) for 60 seconds. Expected
qTotal_m3after 60 s: 150 × 60/3600 = 2.500 m³ ± meter / AI tolerance. -
Integer round-trip. Verify
qTotal_m3 = qTotalRaw / scaleexactly. Any drift here indicates a missingROUNDor aREALmath path on the total. -
Retain test. Power-cycle the CPU with the totalizer non-zero. Confirm
qTotalRawrestores to the last pre-cycle value. -
Wire-break test. Open the loop (current drops to 0). Confirm
qWireBreakasserts 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 toiRawAI < 0. -
Long-run drift test. Run the batch to completion. Compare the final
qTotal_m3with the meter's local display and a reference totalizer (e.g., a Coriolis reference total or weigh-scale). Tolerance: ± meter accuracy specification.
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:
-
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_sfrom the OB cycle-time clock. The integer accumulator is forgiving of period jitter as long asiSamplePeriod_sreflects reality. - 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.
- 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.