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.
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.
- Install a sensor at the escapement. The cleanest solution, but requires mechanical rework and is often not feasible on a retrofit.
- 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.
- 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.
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
- 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".
- Configure the CPU's PROFINET interface. Set a fixed IP address (e.g., 192.168.0.10) and enable "Use router" only if required.
- 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.
- Add the LGF library: "Options > Global Libraries > Open Library" and browse to the unzipped LGF folder. The library appears under "Global Libraries > LGF".
- 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";
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.
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
- 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.
- 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.
- 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.
- Force all 250 SHR bits FALSE. Set V_REJ_CMD TRUE once. EMPTY_CNT should increment; SC_CNT should not.
- 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.
- 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.