Problem Statement: From Instantaneous Flow to Integrated Total
A flow transmitter with a 4-20 mA output delivers a signal proportional to the instantaneous volumetric or mass flow rate (for example, m³/h or kg/h). The operator, however, usually wants the cumulative quantity that has passed through the line since a defined start: shift total, batch total, daily total, or lifetime total. The conversion from a rate to a quantity is an integration:
Vtotal(t) = Vtotal(t0) + ∫t0t q(τ) dτ
On a discrete-time controller such as the SIMATIC S7-300, S7-400, S7-1200, or S7-1500, the integral is approximated by a Riemann sum. Implementing it correctly requires three engineering decisions:
- How the analog raw value (INT 0..27648) is scaled into engineering units. The classic STEP 7 block is FC105; in TIA Portal the equivalent is the pair NORM_X and SCALE_X.
- How the cycle time of the integrating task is obtained, and whether the integrator uses a fixed nominal Δt or a measured timestamp difference.
- How the integrator state is retained across power cycles and how overflow / rollover is handled.
Siemens documents this exact application in its Industry Online Support FAQ "How can you accumulate physical flow measurements (e.g. mass flow rate, flow velocity) to an overall value?" under entry ID 24000437. The technique below is the field-proven implementation of that reference.
Flow Measurement Signals: Analog Rate vs. Pulse Quantity
Before writing any code, classify the transmitter output. The integration math differs substantially between the two main cases.
| Signal type | Output characteristic | Engineering unit | Integration approach |
|---|---|---|---|
| Analog (4-20 mA / 0-10 V) | Proportional to instantaneous rate | m³/h, L/min, kg/h | Numeric accumulation: q(t) × Δt |
| Pulse / frequency | One pulse per fixed volume | pulses / unit volume | Event counting via K-factor |
| HART / Modbus register | Two registers: rate and device-side total | mixed | Read the device's pre-integrated total |
The pulse case is mechanically simpler because the transmitter itself performs the integration and the PLC only counts edges; this is also the most accurate method for custody transfer. The analog case, which is the focus of this article, requires the controller to compute the sum. Keyence's flow knowledge base defines the underlying terms: integrated flow is the cumulative value of flow used from the start to finish of measurement, while instantaneous flow is the value at a specific moment (see Keyence Flow Knowledge - Integrated Flow).
Prerequisites
- SIMATIC S7-300 (CPU 31x), S7-400, S7-1200 (CPU 12xx), or S7-1500 (CPU 15xx) with an analog input module (SM 331, SM 431, SM 1231, SM 1531) configured for 4-wire 4-20 mA. See the S7-1200 system manual 109741593 and the S7-1500 system manual 59191792.
- STEP 7 V5.x (classic) for S7-300/400, or TIA Portal V15..V18 for S7-1200/1500.
- Transmitter datasheet with: flow unit, full-scale (20 mA) value, lower-range value (4 mA), and output type.
- Defined scan time for the integration task. Use a cyclic interrupt OB (OB30 on S7-1500, OB35 on S7-300/400) with a configurable period instead of OB1, to make Δt deterministic.
- Retentive DB or retentive variable to survive CPU STOP/RUN transitions and power loss.
Scaling the Analog Input: FC105 vs. NORM_X / SCALE_X
The first non-trivial step is converting the raw AI value (0..27648 for the Siemens default unipolar range) into engineering units. The scaled value is the instantaneous rate q that feeds the integrator.
Classic STEP 7: FC105 "SCALE"
FC105 is a library function in the STEP 7 standard library under TI-S7 Converting Blocks. Calling convention:
CALL FC 105
IN := MW20 // raw INT 0..27648
HI_LIM := 1.000000e+02 // 100.0 m3/h at 20 mA
LO_LIM := 0.000000e+00 // 0.0 m3/h at 4 mA
BIPOLAR:= FALSE
RET_VAL:= MW22 // scaling error code
OUT := MD30 // REAL flow in m3/h
TIA Portal: NORM_X and SCALE_X
In S7-1200 and S7-1500, FC105 is replaced by the two-instruction pair NORM_X (normalize to 0.0..1.0) and SCALE_X (scale to engineering range). SCALE_X accepts REAL limits and produces a REAL output directly, which eliminates the need for a separate conversion step.
// NORM_X: 0..27648 -> 0.0..1.0
NORM_X(ENABLE := TRUE,
VALUE := "AI_raw",
MIN := 0,
MAX := 27648,
RET_VAL=> "norm_val");
// SCALE_X: 0.0..1.0 -> 0.0..100.0 m3/h
SCALE_X(ENABLE := TRUE,
VALUE := "norm_val",
MIN := 0.0,
MAX := 100.0,
RET_VAL=> "flow_m3h");
Integration Mathematics: Discrete-Time Implementation
The continuous integral V = ∫ q dt is approximated in the PLC by:
V(k) = V(k-1) + q(k) × Δt
where Δt is the time between two samples. Three Δt handling strategies exist:
| Strategy | Source of Δt | Accuracy | Recommended use |
|---|---|---|---|
| Fixed nominal | OB30/OB35 configured period (e.g., 100 ms) | ±5% under heavy CPU load | Lightly loaded, non-custody applications |
| Timestamp difference (SFC1 / RD_SYS_T) | Two system clock reads | ±0.1% | Recommended for all production systems |
| Counter-based Δt | Count OB passes; integrate every N | ±0.5% | When system clock unavailable |
Unit Conversion
If q is in m³/h and Δt is in seconds, the increment must include a time-unit conversion:
ΔV [m³] = q [m³/h] × Δt [s] / 3600 [s/h]
The constant 1/3600 must be present. Forgetting it is the single most common totalizer bug and produces a totalizer that runs 3600× too fast (or, if the inverse error is made, 3600× too slow).
Implementation: Classic S7-300/400 in STEP 7
Create a function block FB100 "Flow_Totalizer" with an instance DB (DB100) holding the integrator state in its STAT section. The integrator must be retentive so it survives a warm restart.
FB100 Interface
| Section | Name | Type | Comment |
|---|---|---|---|
| INPUT | i_Flow_m3h | REAL | Instantaneous flow from FC105 |
| INPUT | i_Dt_s | REAL | Measured Δt in seconds |
| INPUT | i_Reset | BOOL | Reset integrator to zero |
| INPUT | i_Hold | BOOL | Freeze accumulator |
| OUTPUT | q_Total_m3 | REAL | Cumulative volume in m³ |
| OUTPUT | q_Status | WORD | 16#0001 = overflow flag |
| STAT | s_Total | REAL | Retentive accumulator |
| STAT | s_OverflowCount | DINT | Retentive 109 m³ rollover counter |
In the instance DB properties, mark s_Total and s_OverflowCount as retentive (Non-Retain off). Configure the CPU's retentive area to include DB100.
STL Body
FUNCTION_BLOCK FB100
VAR
s_Total : REAL; // retentive
s_OverflowCount : DINT; // retentive
END_VAR
BEGIN
IF i_Reset THEN
s_Total := 0.0;
s_OverflowCount := 0;
ELSIF i_Hold THEN
// freeze
ELSE
// increment with unit conversion (m3/h -> m3)
s_Total := s_Total + i_Flow_m3h * i_Dt_s / 3600.0;
END_IF;
// rollover at 1.0e9 m3 (well below REAL precision floor)
IF s_Total > 1.0E+09 THEN
s_Total := s_Total - 1.0E+09;
s_OverflowCount := s_OverflowCount + 1;
END_IF;
q_Total_m3 := s_Total;
IF s_OverflowCount > 0 THEN
q_Status := W#16#0001;
ELSE
q_Status := W#16#0000;
END_IF;
END_FUNCTION_BLOCK
OB1 Wiring (Single-Call Variant)
CALL FB100, DB100
i_Flow_m3h := MD30 // output of FC105
i_Dt_s := MD40 // measured cycle time
i_Reset := M10.0 // from HMI reset button
i_Hold := M10.1 // from HMI hold button
q_Total_m3 := MD50
q_Status := MW52
For better accuracy, place the FB call inside OB35 so Δt is the OB35 period (e.g., 100 ms) and read the actual cycle time once per second via SFC1 "READ_CLK".
Implementation: S7-1200 and S7-1500 in TIA Portal
The TIA Portal version uses an FB written in SCL (Structured Control Language) for cleaner math. The logic is identical, but data-type handling is stricter and the RETAIN qualifier replaces the older "non-volatile DB" technique.
FUNCTION_BLOCK "Flow_Totalizer"
VAR_INPUT
i_Flow_m3h : REAL; // from SCALE_X
i_Dt_s : REAL; // measured delta time
i_Reset : BOOL; // rising edge resets
i_Hold : BOOL;
END_VAR
VAR_OUTPUT
q_Total_m3 : REAL;
q_Overflow : BOOL;
END_VAR
VAR RETAIN
s_Total : REAL; // retained across power cycle
s_Rollover : DINT;
END_VAR
BEGIN
IF i_Reset THEN
#s_Total := 0.0;
#s_Rollover := 0;
ELSIF NOT #i_Hold THEN
#s_Total := #s_Total + #i_Flow_m3h * #i_Dt_s / 3600.0;
END_IF;
IF #s_Total > 1.0e9 THEN
#s_Total := #s_Total - 1.0e9;
#s_Rollover := #s_Rollover + 1;
#q_Overflow := TRUE;
ELSE
#q_Overflow := FALSE;
END_IF;
#q_Total_m3 := #s_Total;
END_FUNCTION_BLOCK
Place the FB call inside the cyclic interrupt OB30 (typical period 100 ms). On S7-1500, OB30 is preferred for fast periodic tasks; OB1 remains free for slower I/O scans. Note the use of the RETAIN keyword in the VAR block: this is the TIA Portal mechanism for retentive storage and replaces the STEP 7 classic non-volatile DB property.
Delta-Time Measurement Patterns
Method 1 - Fixed OB Period (Simplest)
Configure OB30 on S7-1500 or OB35 on S7-300/400 to a known period. The integrator uses this constant. The downside is that the period is only nominal; actual jitter on a loaded CPU can be ±10%.
// inside the cyclic OB
#i_Dt_s := 0.1; // 100 ms period from OB30 configuration
Method 2 - Timestamp Difference (Most Accurate)
Read the system clock each pass and compute the actual Δt. Siemens blocks for this:
- S7-300/400 classic: SFC1 "READ_CLK" returning a DATE_AND_TIME structure.
- S7-1200/1500: RD_SYS_T (returns DTL) and DTL subtraction.
// S7-1500 timestamped integrator
VAR
s_t_last : DTL;
s_init : BOOL;
temp_t : DTL;
END_VAR
IF NOT s_init THEN
RD_SYS_T(RET_VAL := s_t_last);
s_init := TRUE;
ELSE
RD_SYS_T(RET_VAL := temp_t);
#i_Dt_s := DINT_TO_REAL(
TIME_TO_DINT(temp_t - s_t_last) / 1000);
s_t_last := temp_t;
END_IF;
Method 3 - Counter-Based Δt (Mid Accuracy)
Increment a DINT every OB cycle and integrate the total every time the counter reaches N. Use this if the OB period is too short for a timestamp read but you need a known quantity per integration step.
Pulse-Based Totalization
Many flowmeters expose a pulse output (one pulse = K cubic meters or liters). Counting pulses is robust against analog drift and is the preferred method for custody transfer. Siemens provides fast counters on most CPUs.
S7-1200 with HSC (High-Speed Counter)
- In device configuration, configure a high-speed counter on input I0.0 with counting mode "Count once".
- Assign a process image address, e.g., ID1000.
- Use the CTRL_HSC instruction to control the counter; the current count value (CV) is in ID1000.
- Compute the total: total = CV × K-factor (for example, 0.001 m³ per pulse).
// in cyclic OB
"total_m3" := "total_m3" + INT_TO_REAL(ID1000 - "last_count") * 0.001;
"last_count" := ID1000;
S7-1500 with TM Count / TM Pulse
For higher pulse rates (>1 kHz) or for two-channel metering, use a TM Count 2x module with "Count and Measure" mode. The TM module exposes totalizer registers directly, eliminating the need for PLC-side integration math.
Edge Cases and Field-Proven Caveats
1. Power Loss and Restart
If the integrator is not retentive, a STOP/RUN transition resets the total. Mark the storage tags as retentive in the DB, or use the S7-1500 RETAIN qualifier. For backup, mirror the integrator value to a non-volatile medium: use a recipe DB on S7-300/400, or use a DataLog written to a memory card on S7-1500.
2. REAL Precision Degradation
REAL (IEEE-754 single precision) has approximately 7 significant decimal digits. If the running total reaches 10,000,000 m³, the increment resolution degrades below 1 mL. Switch to LREAL (double precision) on S7-1500 if high precision is required at high total counts, or wrap the totalizer at a chosen rollover threshold.
3. Wrong Unit Conversion
The most common bug is mismatched time units (minutes in q, seconds in Δt) leading to totals off by a factor of 60, or 3600 if hours are involved. Always display both q and Δt in the HMI to verify consistency.
4. Scan Time Drift
If OB1 cycle time is used without compensation, the totalizer will drift under heavy communication load. Move the integrator call into a cyclic interrupt OB whose period is hardware-fixed.
5. Negative Flow (Bidirectional Meters)
Some meters report bidirectional flow (for example, reverse flushing or backflow). Subtract the absolute reverse flow from the forward total, or report two separate registers (forward total, reverse total). Do not simply use SIGN(q).
6. Out-of-Range Detection
If the AI shows >27648 or <0 (overrange), FC105 returns a status code and SCALE_X returns an error. Suppress the integrator increment when the quality is bad to avoid injecting garbage.
// suppress integration when AI quality is not "good"
IF "AI_flow".%Quality = 16#80 THEN
// integrate
END_IF;
7. Integrator Mode State Machine
The integrator has three valid states: Running, Hold, and Reset. The Reset action should be edge-triggered (rising edge only) to avoid repeated clears from a stuck input. The Hold action is level-triggered, so the operator can keep the total frozen for as long as required.
// edge-triggered reset
i_Reset_RTrig(CLK := i_Reset);
IF i_Reset_RTrig.Q THEN
s_Total := 0.0;
END_IF;
Commissioning Procedure
- Force the AI to 4 mA (0% flow) and verify FC105 / SCALE_X outputs 0.0.
- Force the AI to 12 mA (50% flow) and verify the output equals HI_LIM / 2.
- Force the AI to 20 mA (100% flow) and verify full-scale output.
- Set
i_Hold = TRUEon the FB. Verify the integrator is frozen. - Set
i_Reset = TRUEfor one OB cycle, then unset. Verify the integrator clears to 0.0. - Release the hold. Compare the integrator accumulation against a reference: weigh tank, calibrated volumetric vessel, or a calibrated pulse-counter reference.
- Run for 24 h at typical operating point. The drift between the PLC totalizer and the reference should be within the transmitter's accuracy specification (typically ±0.5% to ±1.0%).
Document the following so future modifications do not silently break the integrator:
- FB tag name, instance DB number, retention setting.
- OB used (OB1, OB30, OB35) and its configured period.
- Method of Δt acquisition (fixed, timestamp, counter).
- Unit conversion constants (for example, the 1/3600 divisor).
- Rollover threshold and overflow behavior.
- Reset procedure and authorization level.
Troubleshooting Matrix
| Symptom | Likely cause | Diagnostic | Fix |
|---|---|---|---|
| Totalizer drifts much faster than expected | Wrong time unit (q in m³/h but Δt in seconds without /3600) | Watch q, Δt, and increment in VAT; verify HMI units | Add or correct the /3600 divisor |
| Totalizer reads zero regardless of flow | Wrong MD/MW wiring; i_Reset stuck TRUE; i_Hold stuck TRUE | Monitor i_Reset and i_Hold in VAT | Correct wiring; verify HMI button logic |
| Totalizer resets on power cycle | Storage tags not marked retentive | Open DB properties / FB VAR RETAIN | Enable retentivity; verify CPU retentive range |
| Totalizer accuracy drifts over time | OB1 cycle time varies under load | Use RD_SYS_T to log min/max Δt | Move call into OB30/OB35 with fixed period |
| Negative total when flow goes negative | Bidirectional meter; integrator not absolute | Verify q sign during flow reversal | Split into forward and reverse totals |
| Total freezes at large value | REAL precision floor; LREAL not used | Inspect q_Total_m3 precision | Switch to LREAL on S7-1500 or use rollover |
| Large step changes in total | AI noise spikes (raw >27648 then back) | Inspect AI raw value in VAT | Add input smoothing or status qualification |
| Integrator runs in OB1 instead of OB30 | FB placed in main scan; Δt is wrong | Check OB call stack in online view | Move the FB call to the cyclic OB |
| Totalizer shows correct value but HMI shows wrong | HMI scaling or unit mismatch on the tag | Compare HMI tag and PLC tag in online | Correct HMI scaling field or use raw REAL |
Where can I find the official Siemens FAQ on flow totalization?
Siemens publishes "How can you accumulate physical flow measurements (e.g. mass flow rate, flow velocity) to an overall value?" in its Industry Online Support portal under the SIMATIC S7 topic. Search for entry ID 24000437 at support.industry.siemens.com. The FAQ includes a sample STEP 7 project for S7-300/400 with FB and OB35 integration.
Should I integrate in OB1 or in a cyclic interrupt OB?
Use a cyclic interrupt OB (OB30 on S7-1500, OB35 on S7-300/400). OB1 cycle time depends on user-program size and communication load, and its drift directly becomes integrator drift. A cyclic OB has a hardware-timer-fixed period, so the integrator is deterministic and the Δt can be measured with RD_SYS_T or assumed constant.
How do I scale a 4-20 mA flow signal in TIA Portal?
Use the instruction pair NORM_X (raw 0..27648 to 0.0..1.0) and SCALE_X (0.0..1.0 to engineering range). This replaces FC105 from STEP 7 classic. Both instructions are documented in the S7-1200 and S7-1500 system manuals at 109741593 and 59191792.
My totalizer resets after a power cycle. What is wrong?
The integrator state variable is not retentive. In STEP 7 classic, mark the instance DB as retentive in the CPU properties or set the variable as non-volatile. In TIA Portal, declare the variable with the RETAIN qualifier in the FB VAR block and ensure the CPU's retentive area covers it. Without this, s_Total is initialized to 0.0 on every cold restart.
How is flow integration different from pulse counting?
An analog flow signal carries an instantaneous rate; the PLC must multiply the rate by the elapsed time and accumulate. A pulse signal already encodes quantity: each pulse represents a fixed volume (the K-factor). Counting pulses is inherently accurate and immune to analog drift, which is why custody-transfer meters use pulse or HART totalized output instead of 4-20 mA.