S7-1200 Scrap Counter Using a Shift Register: TIA Portal V14

David Krause18 min read
S7-1200SiemensTutorial / How-to
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-1200 Scrap Counter Using a Shift Register: TIA Portal V14 Implementation Guide

Counting scrap on a continuous-flow printing or packaging line is deceptively difficult when the reject valve can actuate with or without a product beneath it. A common approach in the field is to install a product-presentation sensor directly at the escapement, but on retrofit machines that sensor is often missing and only an upstream sensor is available. With up to 250 products in flight between the upstream sensor and the reject valve, a single Boolean cannot tell you whether a given valve actuation was "loaded" or "empty". The Siemens SIMATIC S7-1200 1215C running TIA Portal V14 has a perfect tool for this job: a fixed-length Boolean shift register driven by a product-present pulse train. This article walks through the theory, the Siemens LGF (Library of General Functions) shift-register block, a custom SCL implementation that requires no external library, an equivalent ladder-logic form, HMI mapping, and a complete commissioning and troubleshooting procedure.

1. Prerequisites and Hardware

Before writing a single line of code, confirm the following hardware and software are in place.

  • CPU: SIMATIC S7-1200 1215C, in any of the three variants (DC/DC/DC, DC/DC/RLY, AC/DC/RLY). Firmware V4.0 or later is required for the LGF block. See the S7-1200 System Manual (entry 109751634) for the exact variant order numbers (6ES7215-1AG40-0XB0, 6ES7215-1HG40-0XB0, 6ES7215-1BG40-0XB0).
  • Engineering: STEP 7 / TIA Portal V14, V14 SP1, or later. V14 SP1 ships the LGF library that matches the runtime behaviour described here. See the TIA Portal V14 SP1 release notes (entry 109740537).
  • Free memory: 100 KB of work memory and 1 MB of load memory is a safe target. A 250-bit shift register consumes 32 bytes of data; the surrounding DB and counter values are negligible.
  • Sensors and wiring: One 24 V DC PNP digital input for the upstream product-present sensor (S_PROD). One 24 V DC digital input for the reject-valve command (V_REJ_CMD). One 24 V DC digital output for the valve coil (V_REJ_OUT). On DC/DC/RLY and AC/DC/RLY CPUs, route the output through an interposing relay if coil current exceeds 2 A or if the coil is 110/230 V AC.
  • LGF library: The "LGF (Library of General Functions) for SIMATIC S7-1200 / S7-1500" is available from Siemens Support entry 109751634. Use the LGF version that matches your TIA Portal V14 SPx release.
A common pitfall on the 1215C is using the on-board relay outputs (Q0.0-Q0.1 on RLY variants) to switch DC valve coils directly. The mechanical relay is rated 2 A at 30 V DC and 0.5 A at 125 V DC, but its electrical life drops dramatically above 0.5 A. If the reject valve draws more than 0.5 A, switch to the DC/DC/DC variant and drive a 24 V DC interposing relay, or to a 24 V DC solid-state relay module mounted on the 1215C signal board.

2. Process and Signal Description

The mechanical layout is: products travel in single file from a feeder, pass a "product present" sensor (S_PROD), and continue up to 250 product-pitches downstream to the reject valve (V_REJ). The valve can be commanded to fire at any time, either by an operator pushbutton or by an automated vision system. If the valve fires with no product under it, the count is incremented incorrectly. The shift register solves this by remembering whether a product was seen recently.

2.1 Signal Map

Tag Direction PLC type Default address Description
S_PROD DI (PNP) Bool %I0.0 Upstream product-present sensor, 24 V DC sourcing
V_REJ_CMD DI Bool %I0.1 Reject-valve actuation request (operator button or quality logic)
V_REJ_OUT DO Bool %Q0.0 Reject-valve coil driver
SC_CNT Internal DInt %MD100 Validated scrap counter (loaded actuations only)
EMPTY_CNT Internal DInt %MD104 Empty actuation counter (diagnostic)
PROD_CNT Internal DInt %MD108 Total product pulses seen by S_PROD
SHR[0..249] Internal Array[0..249] of Bool DB1.DBX0.0 onwards 250-bit shift register, bit 0 = newest

2.2 Operating Principle

Every time S_PROD transitions from FALSE to TRUE (a product is detected at the upstream sensor), a single TRUE is shifted into bit 0 of the register and every existing bit moves one position toward the end of the array. The bit at position 249 is shifted out and discarded. Conceptually, bit 0 represents "a product was just seen", and bit 249 represents "a product was seen 250 pulses ago".

When V_REJ_CMD asserts, the program examines the shift register. If any of the 250 bits is still TRUE, a product is in flight between the sensor and the valve, and the actuation is counted as a real scrap event. If all 250 bits are FALSE, the valve fired with no product present and the event is logged as an empty actuation. The total product count is also incremented on every S_PROD pulse so that the scrap-to-throughput ratio (SCRAP_RATE = SC_CNT / PROD_CNT) can be displayed on the HMI.

3. Why a Shift Register Is the Right Tool

There are three practical ways to solve this problem on an S7-1200.

  1. Install a sensor at the escapement. The cleanest solution, but requires mechanical rework and is often not feasible on a retrofit.
  2. Use a TON (on-delay) timer. Reset the timer on every product pulse; the valve is "allowed" to count as scrap only when the timer is running. This works for a constant pitch and constant speed, but it breaks down on variable-speed lines and during line stoppages.
  3. Use a shift register indexed by product pitch. This is the only method that survives variable speed, line stops, and product accumulation in the buffer zone, because the register length is tied to a count of products, not to a clock period.

The shift register is essentially a First-In-First-Out (FIFO) buffer of fixed length. The 250-bit length covers the worst-case product distance in the user's application. A real FIFO of 250 BOOL entries is overkill because the consumer is the valve (which simply checks "any bit set"), but a fixed-length array with a shift-left operation is easier to read, easier to monitor online, and uses less memory than a true circular FIFO with head/tail pointers.

Do not attempt to use IEC timer IEC_Timer (TP, TON, TOF) blocks to back-calculate product position. Time is not the right domain for this problem: line speed varies, and any line stoppage will make the timer expire before the product reaches the valve, producing a false "empty" classification.

4. Memory Budget for the S7-1200 1215C

Resource Quantity used Notes
Work memory, user program < 8 KB One FB, one DB, one OB1, one cyclic OB
Work memory, user data ~ 200 bytes Shift register (32 B) + counters (12 B) + flags
Load memory < 200 KB TIA Portal V14 project overhead
Retainable data 44 bytes SC_CNT, EMPTY_CNT, PROD_CNT (mark DB "non-retain" or "retain" per policy)
CPU scan time (typical) 2-5 ms 250-bit copy loop adds < 0.1 ms on 1215C
Process image update 3 ms default Reducible to 1 ms for high-speed lines

The 1215C has 125 KB of integrated work memory (50 KB consumed by the operating system, leaving ~75 KB for user code and data). The shift register is 250 bits = 32 bytes, but TIA Portal rounds array storage to 4-byte boundaries, so the actual footprint is 32-36 bytes. The CPU is not the bottleneck; the I/O update time is. For lines running faster than 200 products per second, set the process image partition update for the relevant I/O to 1 ms (TIA Portal: Device Configuration > Digital Inputs > I/O Address > Process Image: PIP1, Update Time 1 ms).

5. TIA Portal V14 Project Setup

  1. Open TIA Portal V14 SP1 and create a new project. Add the S7-1200 1215C using "Add new device > Controllers > SIMATIC S7-1200 > CPU 1215C DC/DC/DC".
  2. Configure the CPU's PROFINET interface. Set a fixed IP address (e.g., 192.168.0.10) and enable "Use router" only if required.
  3. Enable the clock (TIA Portal: Device Configuration > Properties > Time of Day) and set the time zone. This is required if you plan to log scrap events with a timestamp.
  4. Add the LGF library: "Options > Global Libraries > Open Library" and browse to the unzipped LGF folder. The library appears under "Global Libraries > LGF".
  5. Create a new data block "DB_Scrap" with the structure shown in the signal map. Mark SC_CNT, EMPTY_CNT, and PROD_CNT as "Retain" if you want the totals to survive a power cycle. The shift register must remain "Non-retain": the bit pattern is meaningful only while the line is moving.

5.1 Data Block Layout

DATA_BLOCK "DB_Scrap"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
   STRUCT
      SHR : Array[0..249] of Bool;   // bit 0 = newest product
      SC_CNT : DInt;                  // validated scrap count
      EMPTY_CNT : DInt;               // empty actuation count
      PROD_CNT : DInt;                // total product pulses
      SC_CNT_EDGE : Bool;             // edge marker for HMI display
      EMPTY_EDGE : Bool;
   END_STRUCT;
END_DATA_BLOCK

6. Shift Register Implementation with the LGF Block

The LGF block LGF_ShiftRegister_BOOL is a self-contained function block that shifts a 1-D Boolean array by one position on every call. The LGF documentation is part of the library archive and is also mirrored in the Siemens Support entry 109751634. The block's interface is:

Port Direction Type Meaning
shift Input Bool Rising edge shifts the array by one
shiftValue Input Bool Value to insert at index 0
array InOut Array[*] of Bool The shift register itself
error Output Bool TRUE if the block detected an invalid configuration
status Output Word Error code (16#0000 = OK)

6.1 FB "FB_ScrapCount" Using the LGF

FUNCTION_BLOCK "FB_ScrapCount"
VERSION : 0.1
VAR_INPUT
      S_PROD : Bool;     // upstream product sensor
      V_REJ_CMD : Bool;  // reject valve command
END_VAR
VAR_OUTPUT
      V_REJ_OUT : Bool;          // valve driver output
      SC_CNT : DInt;             // validated scrap count
      EMPTY_CNT : DInt;          // empty actuation count
      PROD_CNT : DInt;           // total products seen
      error : Bool;
      status : Word;
END_VAR
VAR
      SHR_inst : LGF_ShiftRegister_BOOL;
      sProd_edge : Bool;
      vRej_edge  : Bool;
      SHR : Array[0..249] of Bool; // local 250-bit register
      i : Int;
      anyBitSet : Bool;
END_VAR
BEGIN
      // ---- 1. Product pulse: shift TRUE into bit 0 ----
      IF S_PROD AND NOT sProd_edge THEN
            sProd_edge := S_PROD;
            SHR_inst(
                  shift := TRUE,
                  shiftValue := TRUE,
                  array := SHR,
                  error := error,
                  status := status);
            PROD_CNT := PROD_CNT + 1;
      END_IF;
      IF NOT S_PROD THEN sProd_edge := FALSE; END_IF;

      // ---- 2. Valve command: classify and count ----
      V_REJ_OUT := V_REJ_CMD; // physical output mirrors the command
      anyBitSet := FALSE;
      FOR i := 0 TO 249 DO
            IF SHR[i] THEN anyBitSet := TRUE; END_IF;
      END_FOR;

      IF V_REJ_CMD AND NOT vRej_edge THEN
            vRej_edge := V_REJ_CMD;
            IF anyBitSet THEN
                  SC_CNT := SC_CNT + 1;
            ELSE
                  EMPTY_CNT := EMPTY_CNT + 1;
            END_IF;
      END_IF;
      IF NOT V_REJ_CMD THEN vRej_edge := FALSE; END_IF;

      // ---- 3. Shift every product pulse ----
      // (only one shift per pulse; the call above already shifts once)
END_FUNCTION_BLOCK

The block instance is dropped into OB1 with a multi-instance DB, and the inputs/outputs are wired to the I/O symbols defined in the PLC tag table. The LGF block handles all the array copy logic; the FB just classifies the actuation.

7. Custom SCL and Ladder Logic

If you cannot or do not want to import the LGF library, the same logic is implementable in 20 lines of SCL using a MEM_CP / MEM_CPY pattern. The LGF block internally uses MOVE_BLK to slide the array; a manual shift is just as fast on a 250-bit array.

7.1 Custom SCL Without LGF

// In OB1 cyclic interrupt (OB30) running at 10 ms, or in OB1
IF "S_PROD" AND NOT "sProd_edge" THEN
    "sProd_edge" := TRUE;
    // shift the array right by one; insert TRUE at bit 0
    "DB_Scrap".SHR[249] := FALSE;          // clear tail
    FOR "i" := 249 DOWNTO 1 BY 1 DO
        "DB_Scrap".SHR["i"] := "DB_Scrap".SHR["i" - 1];
    END_FOR;
    "DB_Scrap".SHR[0] := TRUE;
    "DB_Scrap".PROD_CNT := "DB_Scrap".PROD_CNT + 1;
END_IF;
IF NOT "S_PROD" THEN "sProd_edge" := FALSE; END_IF;

// Valve command classification
"anyBitSet" := FALSE;
FOR "i" := 0 TO 249 DO
    IF "DB_Scrap".SHR["i"] THEN "anyBitSet" := TRUE; END_IF;
END_FOR;

IF "V_REJ_CMD" AND NOT "vRej_edge" THEN
    "vRej_edge" := TRUE;
    IF "anyBitSet" THEN
        "DB_Scrap".SC_CNT := "DB_Scrap".SC_CNT + 1;
    ELSE
        "DB_Scrap".EMPTY_CNT := "DB_Scrap".EMPTY_CNT + 1;
    END_IF;
END_IF;
IF NOT "V_REJ_CMD" THEN "vRej_edge" := FALSE; END_IF;

"DB_Scrap".V_REJ_OUT := "V_REJ_CMD";
The SCL FOR loop runs from 249 down to 1 when shifting. Always shift "down to" rather than "up to" when inserting at index 0; otherwise you overwrite the data you are trying to read. The LGF block does the same thing internally but obscures the iteration direction from the user.

7.2 Ladder Logic Equivalent

A pure ladder implementation is feasible but cumbersome because the array shift requires explicit move instructions. The standard idiom is to use a multi-instance DB and the MOVE_BLK (move block) instruction to slide the bits. A representative segment is shown below.

Network 1: Product pulse edge detection
      |   S_PROD   ---|P|---   sProd_edge_set
      |   sProd_edge   sProd_edge_set

Network 2: Shift register slide (1..249)
      |   sProd_edge_set
      |   MOVE_BLK  SRC:=SHR[1]  COUNT:=249  DEST:=SHR[2]

Network 3: Insert new bit at SHR[0]
      |   sProd_edge_set   ---( S )---   SHR[0]

Network 4: Increment product counter
      |   sProd_edge_set   ADD 1   PROD_CNT

Network 5: OR-reduce the array to "anyBitSet"
      |   SHR[0]   SHR[1]   ...   SHR[249]   ---( OR 250 )---   anyBitSet

Network 6: Valve rising edge and classification
      |   V_REJ_CMD   ---|P|---   vRej_edge_set
      |   vRej_edge_set   anyBitSet   ---( ADD 1 )---   SC_CNT
      |   vRej_edge_set   NOT anyBitSet   ---( ADD 1 )---   EMPTY_CNT

The ladder form is easier for electricians to maintain but the 250-bit OR-reduction is unwieldy. A common simplification is to use SHR length 200 (5-Kbyte footprint) and check only the first 100 bits: this still validates a product presence window of 100 product pitches and halves the OR-reduction work. The choice depends on the physical distance between sensor and valve in product pitches.

8. HMI Tag Mapping and Operator Display

To make the count visible, expose the three counters to a TP700 Comfort panel or a WinCC Runtime Advanced view. The tag list is:

HMI tag PLC address Display format Notes
SC_CNT %DB1.DBD0 (optimized: "DB_Scrap".SC_CNT) Decimal, 10 digits Validated scrap count, retain
EMPTY_CNT %DB1.DBD4 Decimal, 10 digits Empty actuations, retain
PROD_CNT %DB1.DBD8 Decimal, 10 digits Total products, retain
SCRAP_RATE Computed Percent, 1 decimal SC_CNT / PROD_CNT * 100
SHIFT_FULL Computed Boolean, amber TRUE if all 250 bits are TRUE (overflow warning)

The SCRAP_RATE computation should be guarded against divide-by-zero: only compute when PROD_CNT > 0. A standard pattern in WinCC is to use a "Calculate" script in the HMI that triggers every 500 ms on the same tag refresh cycle.

TIA Portal V14's "Optimized block access" hides the absolute address of a DB variable. When wiring an HMI tag to an optimized DB, you must select the symbolic name ("DB_Scrap".SC_CNT) rather than the absolute address. The icon for an optimized-access variable in the PLC tag table is a small "S7" badge; the icon for standard access is a plain "%DB".

9. Edge Cases, Verification, and Commissioning

Every shift-register-based counter has the same set of failure modes. Run through this checklist before signing off the line.

9.1 Edge Cases to Test

  1. Line stoppage with product under the sensor. If the line stops and a product is stationary under S_PROD, the sensor stays TRUE continuously. The "rising edge" detection in the FB is what saves the count: only one TRUE is shifted in. Confirm by holding a piece of metal under the sensor for 5 seconds and verifying PROD_CNT incremented by exactly 1.
  2. Line running through the entire buffer zone. With 250 products in flight and 250 bits in the register, the register will be fully TRUE just before the last product reaches the valve. If the valve fires at that moment, the OR-reduction returns TRUE and the count is correct. The SHIFT_FULL alarm on the HMI lets the operator see this near-overflow condition.
  3. Sensor bounce. Optical and inductive sensors can produce a brief FALSE pulse during the rising edge. If bounce is observed, configure the digital input's input filter (TIA Portal: Device Configuration > Digital Inputs > Channel > Input Filter). The default 6.4 ms is too slow for lines > 100 products/s; drop to 0.8 ms or use the hardware de-bounce of the sensor itself.
  4. Sensor wiring failure (broken wire, short). The 1215C reports a broken wire on a digital input if the input filter is set high and the diagnostic interrupt is enabled. Add an "input error" OB (OB82) and latch an HMI alarm that resets SC_CNT to 0x7FFFFFFF, forcing the operator to acknowledge before counting resumes.
  5. Valve pulse shorter than scan time. If the operator double-taps the pushbutton faster than 2 ms, the program can miss a pulse. To guarantee detection, use the hardware interrupt capability of the 1215C: assign the V_REJ_CMD input to a hardware interrupt OB (OB40) and increment the counters there. This decouples the count from the OB1 scan rate.
  6. Replacing the shift register length. The array dimension is fixed at compile time. If the buffer-zone distance changes, edit the FB declaration and re-download. There is no runtime resize.

9.2 Commissioning Procedure

  1. Set the S7-1200 to STOP. Download the project. Verify in the TIA Portal "Online > Diagnostics > Memory" that work memory usage is below 70 percent.
  2. Switch to RUN. Force S_PROD FALSE and observe SC_CNT and EMPTY_CNT staying at 0. Force S_PROD TRUE once, then FALSE, and confirm PROD_CNT = 1.
  3. Force SHR[0] TRUE, SHR[249] TRUE, and all others FALSE. Set V_REJ_CMD TRUE once. SC_CNT should increment; EMPTY_CNT should not.
  4. Force all 250 SHR bits FALSE. Set V_REJ_CMD TRUE once. EMPTY_CNT should increment; SC_CNT should not.
  5. Remove all forces. Run the line at low speed (10 products/s) and have the operator fire the valve manually. Compare the HMI SC_CNT with a hand tally of actual scrap observed in the reject bin. Discrepancy should be zero.
  6. Run at full production speed for 30 minutes. Check that SHIFT_FULL never goes TRUE for more than one scan cycle. If it does, increase the array length or reduce the maximum line speed.

9.3 Troubleshooting Matrix

Symptom Likely cause Diagnostic Corrective action
SC_CNT always equals V_REJ_CMD pulses SHR is not being shifted (edge detection broken) Online monitor SHR[0..4] while feeding products; SHR[0] should pulse TRUE on each product Re-check the rising-edge logic on S_PROD
SC_CNT always 0, EMPTY_CNT counts every actuation Sensor is wired but not powered, or NPN instead of PNP Voltage on the input terminal; LED indicator on the 1215C Rewire to PNP sourcing; verify 24 V at the sensor
SC_CNT drifts above actual scrap Multiple products per S_PROD pulse (e.g., paired parts) Scope or high-speed counter on S_PROD vs. visual observation Use a hardware interrupt to capture every rising edge, or move S_PROD to a point where one product = one pulse
SC_CNT drifts below actual scrap SHR length is shorter than the actual product-pitch distance Count SHR bits at peak load; if all 250 are TRUE consistently, length is too small Increase the array length to 300 or 400 and recompile
EMPTY_CNT counts at line stop Operator holds the button while the line is stopped; SHR drains below 1 TRUE bit Watch the HMI in real time while pressing the button with the line stopped Suppress EMPTY_CNT logging when PROD_CNT is unchanged for 2 seconds (zero-speed interlock)
Counts reset on every power cycle DB is non-retain (default for new DBs) TIA Portal: DB properties > Attributes > Retain Set SC_CNT, EMPTY_CNT, PROD_CNT to "Retain"
TIA Portal download fails with "insufficient memory" Project exceeds 4 MB load memory on the 1215C TIA Portal: Project > Memory usage Remove unneeded libraries or use a 1215C with a memory card (6ES7954-8LF02-0AA0, 4 MB FEPROM)

10. Frequently Asked Questions

How many bits should the shift register have?

The number of bits must equal the worst-case product-pitch distance between S_PROD and V_REJ. If the gap can hold 250 products, use 250 bits. If the gap varies by product family, measure it for the largest product and round up by 10 percent. Going larger wastes a few bytes; going smaller causes empty actuations to be miscounted as scrap.

Can I replace the LGF block with a built-in SCL function on TIA Portal V14?

Yes. The custom SCL shown in Section 7.1 uses only standard FOR loops and array indexing that have been in TIA Portal since V11. It is functionally equivalent to the LGF block and saves the dependency on the external library. The LGF block is preferable when a maintenance team has standardized on it across machines.

What happens to SC_CNT, EMPTY_CNT, and PROD_CNT on a CPU restart?

By default, the DB is non-retain and all three counters reset to zero. To retain counts across power cycles, open the DB properties in TIA Portal, switch to the "Attributes" tab, and set SC_CNT, EMPTY_CNT, and PROD_CNT to "Set in IDB" and "Retain". The shift register itself must remain non-retain because its bit pattern is only meaningful while the line is moving.

Is there a way to use a hardware interrupt instead of polling S_PROD in OB1?

Yes, and it is recommended for lines above 200 products per second. Assign V_REJ_CMD to a digital input on the 1215C signal board or the CPU's on-board inputs, then in the device configuration attach it to a hardware interrupt OB (OB40). The shift-register update and the count increment run inside OB40, guaranteeing detection of pulse widths down to 100 microseconds.

Why does the FB use a local SHR array instead of the DB array directly?

Both are valid. The local array is faster because it lives in the FB's instance memory and is copied in a single block. The DB array is easier to monitor online from TIA Portal because the watch table can show all 250 bits at once. For a 250-bit array, the copy cost is negligible (less than 0.1 ms on a 1215C), so use the DB array if online visibility matters to your commissioning team.

Back to blog