Generating 8-Digit Batch Numbers in Siemens S7-300/S7-400 STL
Production lines on SIMATIC S7-300 and SIMATIC S7-400 controllers frequently require an 8-digit batch identifier conforming to a fixed schema such as 07YYNNNN — two fixed header digits (here 07), the two-digit calendar year, and a four-digit rolling sequence from 0001 to 9999. The reference implementation below uses STEP 7 STL bit-shifting and the DTB (Double Integer to BCD) conversion to assemble the value, store it as a 32-bit word, and emit it for human-readable trace or HMI display. This article dissects every STL statement, allocates bit-precise tag addresses, then provides SCL, LAD, and FBD equivalents, year-rollover and retentive-counter engineering, and a commissioning checklist.
1. Batch Number Schema
The target batch number format and range are:
| Field | Position | Length | Source | Example |
|---|---|---|---|---|
| Header | Byte 3 (high byte) | 1 digit | Constant W#16#7 | 07 |
| Year | Byte 2 | 2 digits | Manual constant or SFC1 date |
12 (year 2025 → 25) |
| Sequence | Bytes 1..0 (low word) | 4 digits | Incrementing counter MD100 |
0001 … 9999
|
An example range is 07120001 through 07129999. A counter wrap-around from 9999 back to 0000 requires explicit handling to avoid duplicate batch IDs within a calendar year.
2. Prerequisites
- STEP 7 V5.5 SP2 or later programming tool with the optional S7-SCL package installed.
- SIMATIC S7-300 (CPU 314 or higher recommended) or SIMATIC S7-400 CPU with firmware supporting FP, SLD, OD, DTB, and
L L#…literal syntax. Consult the STEP 7 STL/LAD/FBD Programming Reference for instruction availability per CPU. - A digital input wired to a manual "next batch" push-button or sensor, e.g.,
I 0.0. - Free marker double-word
MD 100for the sequence counter andMD 104for the assembled BCD batch number (adjust to project naming conventions). - Retentive flag/bit memory region sized to preserve
MD 100across power cycles. Configure the CPU's retentive settings in HW Config → CPU Properties → Retentive Memory.
3. Memory Layout and Tag Allocation
| Address | Symbol | Type | Purpose | Retention |
|---|---|---|---|---|
| I 0.0 | iBatchTrigger | BOOL | Edge-triggered increment source | Non-retentive |
| M 200.0 | mEdgeFlag | BOOL | Edge memory bit for FP | Non-retentive |
| MD 100 | mSeqCounter | DINT | 1 … 9999 rolling sequence | Retentive |
| MD 104 | mBatchNumber | DWORD (BCD) | Assembled 8-digit batch (DTB) | Non-retentive |
| MW 110 | mYearBCD | WORD (BCD) | Two-digit year BCD | Non-retentive |
07, the next byte the year (e.g., 25), and the low word holds the four-digit BCD sequence (e.g., 0001). Reading MD 104 as BCD equals 07250001 exactly as required.4. STL Reference Implementation
The canonical solution originating from the field uses one positive-edge detection, one increment, one shift-and-OR assembly, and one BCD conversion. The full source for an FC block named FC_BATCH follows:
FUNCTION FC 1 : VOID
TITLE = 'Generate 8-Digit Batch Number (07YYNNNN)'
VERSION : 0.1
BEGIN
// -------------------------------------------------------------------
// 1) Detect rising edge of manual / sensor trigger
// -------------------------------------------------------------------
A I 0.0 // iBatchTrigger (NO push-button)
FP M 200.0 // edge memory bit
JCN out // jump if no rising edge
// -------------------------------------------------------------------
// 2) Increment rolling sequence counter MD100 by 1
// Range managed externally as 1 .. 9999
// -------------------------------------------------------------------
L L#1 // load 32-bit long constant 1
L MD 100 // load previous counter value
+D // DINT addition: ACCU1 + ACCU2
T MD 100 // store counter back (DINT)
// -------------------------------------------------------------------
// 3) Compose high 16 bits: '07' << 24 + YY << 16
// W#16#7 shifted left 24 places = bits 24..27 of doubleword
// W#16#12 (sample year 2012) shifted left 16 places
// OD merges the two halves bit-wise OR
// -------------------------------------------------------------------
L W#16#7
SLD 24 // shift left doubleword
L W#16#12 // replace with current year constant
SLD 16 // shift left doubleword 16 places
OD // ACCU1 OR ACCU2 -> header + year mask
// -------------------------------------------------------------------
// 4) Merge sequence counter and convert header+year to BCD
// -------------------------------------------------------------------
L MD 100 // load DINT counter
DTB // convert low word to BCD
OD // OR to assembled header+year mask
T MD 104 // store as DWORD BCD: 07250001
out: NOP 0 // scan-end safe point
END_FUNCTION
4.1 Instruction-by-Instruction Walk-through
| Mnemonic | Operation | Effect on ACCU |
|---|---|---|
| A I 0.0 | AND, scan input bit | Sets RLO from input |
| FP M 200.0 | Edge positive detect | RLO = TRUE on 0→1 transition only |
| JCN out | Jump if RLO = 0 | Skips increment if no new edge |
| L L#1 | Load 32-bit long constant | ACCU1 = 16#0000_0001 |
| L MD 100 | Load double word | ACCU1 ← ACCU2; ACCU2 = MD100 |
| +D | Add double integer | ACCU2 := ACCU2 + ACCU1 (DINT) |
| T MD 100 | Transfer to MD100 | MD100 := ACCU2 (DINT) |
| L W#16#7 | Load 16-bit hex | ACCU1-low = 16#0007 |
| SLD 24 | Shift left doubleword | 16#0007 → 16#0700_0000 |
| L W#16#12 | Load year hex | ACCU1-low = 16#0012 |
| SLD 16 | Shift left doubleword | 16#0012 → 16#0012_0000 |
| OD | OR doubleword | 16#0712_0000 (header+year mask) |
| L MD 100 | Load DINT counter | ACCU2 = DINT seq |
| DTB | Double Integer → BCD | Converts low 16 bits of ACCU2 |
| OD | OR doubleword | Combines BCD seq with header+year |
| T MD 104 | Transfer batch no. | MD104 := 16#0725_0001 (BCD) |
5. Why DTB Works for Display
DTB converts a 32-bit signed integer into its binary-coded decimal representation: each decimal digit occupies 4 bits with values 0…9 (1010 through 1111 are invalid BCD digits). Reading MD 104 byte-by-byte yields 0x07 0x25 0x00 0x01, which is the BCD-encoded number 07250001. This is the form expected by WinCC, ProTool/Pro, HMI tags configured as BCD-16/BCD-32, and printable strings on most operator panels.
OV (overflow) / OS bits. Sequence must never exceed 9999; otherwise, switch to a 9-digit schema or treat overflow as a separate "batch exceeded" flag (see Section 10).
6. SCL Equivalent (STEP 7 V5 / TIA Portal)
For modern TIA Portal projects (S7-300/S7-400 with PROFINET firmware, or the S7-1500 successor), SCL is far more maintainable. The SCL reproduction of the same logic is:
FUNCTION "FC_BatchNumber" : Void
{ S7_Optimized_Access := 'FALSE' }
VAR
iTrigger : BOOL; // I0.0 hardware input edge
mEdge : BOOL; // M200.0 edge memory
seqCtr : DINT; // MD100 retentive DINT
batchNo : DWORD; // MD104 BCD output
yearVal : WORD; // W#16#12 or BCD from SFC
END_VAR
BEGIN
IF (iTrigger AND NOT mEdge) THEN // rising edge
seqCtr := seqCtr + 1;
END_IF;
mEdge := iTrigger;
IF (seqCtr > 9999) THEN
seqCtr := 1; // rollover
END_IF;
// Compose header+year mask and OR with BCD conversion
batchNo := SHL_DWORD(W#16#7, 24)
OR SHL_DWORD(WORD#16#12, 16)
OR DINT_TO_BCD_DWORD(seqCtr);
END_FUNCTION
The SCL form avoids the pitfalls of implicit ACCU shuffling and is visible to reviewers without STEP 7 STL training.
7. LAD / FBD Representation
For installations where STL is disallowed by the customer's coding standard, the equivalent ladder segments are:
-
Network 1 — Edge detect & increment. An
I 0.0---| |--- contact followed by a POS edge detector (LAD:---|P|---) driving anADD_DIblock:MD100 := MD100 + 1. -
Network 2 — Compose header & year. Two
WORD_TO_DWORDblocks feedingSHL_DWORD(shifts 24 and 16) then aWORD_OR_DWORDmerger. -
Network 3 — BCD conversion & merge.
DINT_TO_BCD_DWonMD100, thenWORD_OR_DWORDwith the header+year mask, stored inMD104.
The LAD/FBD representation is functionally identical but loses two to four µs of OB1 scan time per execution on a CPU 315-2 DP, and triggers caution regarding execution order: order-dependent ORs must use the dedicated segment sequence shown above.
8. Year Source: Manual Constant vs. SFC1 (READ_CLK)
The sample uses hard-coded W#16#12 for 2012. Best practice couples the year to the CPU clock with SFC1 (READ_CLK) so that a year boundary (e.g., 2025 → 2026) automatically updates the batch number prefix:
CALL SFC 1
RET_VAL := MW 12 // return code (0 = OK)
OUT_DATE := MD 16 // DATE_AND_TIME structure
// Extract YEAR byte; DT field byte 0 = year (1990..2089)
L MB 16 // year byte (BCD!)
SRD 4 // shift right 4 bits
SLW 4 // clear lower nibble
L MB 16 // year byte again
OW // combine high & low nibble
T MW 110 // mYearBCD (WORD)
If the operator configures or synchronises the CPU clock via NTP, LAN, or via SFC0 (SET_CLK) from a superordinate master, batch numbers then track the actual calendar automatically.
9. Retentive Counter Engineering
By default MD 100 is volatile. After a power cycle the controller would restart at 1, producing duplicate batch numbers. To prevent this:
- Open HW Config → select the CPU → Properties → Retentive Memory.
- Enter the byte range starting at
MB100with the required length, e.g., fromMB100length 8 (covers MD100 and MD104). - Compile and download the hardware configuration.
For S7-400 power retentive flags may be insufficient if the CPU uses battery-buffered RAM; verify with SFC 51 (SZL_ID W#16#0132) on rack status.
10. Wrap-around and Year-Boundary Handling
The minimal snippet does not enforce limits. Industrial-grade behaviour requires:
-
Sequence limit (1…9999): When increment results in 10000, reset to 1 and raise
mSequenceOverflow. Otherwise DTB sets overflow and the batch ID becomes garbage. -
Year boundary: At year change (detected by comparing current SFC1 year byte with stored
mYearBCD), reset the counter to 1 to prevent collision across years.
L L#1
L MD 100
+D
L L#9999
>D // result > 9999?
JCN ok
T MD 100 // overflow caught, store
L 1
T MD 100 // reset to 1
S M 210.0 // mSequenceOverflow alarm
BEA // block end, halt further work
ok: T MD 100
11. Trace Output and HMI Wiring
MD 104 is in BCD. Configure the HMI tag as BCD unsigned 32-bit if the WinCC flexible / TIA Comfort panel expects a binary-coded display string. For raw decimal display on an HMI configured for unsigned 32-bit integer, convert with BCD_DW_TO_DW prior to display to avoid decimal interpretation errors.
For operator-triggered logs (WinCC Audit or ProAgent), feed MD 104 into a STRING formatter block that prepends a leading zero when the BCD-encoded year byte is below 10 (e.g., year 2005 → 05).
12. Verification & Commissioning Checklist
- Compile and download
FC1; clearMD100andMD104. - Open a watch table (VAT) on
I 0.0,M 200.0,MD 100,MD 104. - Force
I 0.0once; verifyMD 100increments to 1 andMD 104reads07 12 00 01in hexadecimal (BCD) view. - Repeat 9998 times; verify the 9999th edge produces
07 12 99 99. - Cycle power; verify
MD 100retains the last value (retentive configured). - Change CPU clock to 31-DEC 23:59:59 → 1-JAN of new year via SFC0/SFC1; verify the year byte in
MW110updates and the next batch number format is07YY0001. - Trigger overflow by forcing
MD100 = 9998then click twice; verify reset to 1 and alarm flag set.
13. Troubleshooting Matrix
| Symptom | Likely Cause | Diagnostic Step | Remedy |
|---|---|---|---|
MD104 shows 07120000 after every edge |
DTB operand ordering wrong; OR applied before BCD | Single-step STL in online monitor | Reorder: load counter, then DTB, then OD with mask |
| MD104 jumps by 2 each press | DI is bouncing or FP flag double-rising | Oscilloscope on I0.0; check FP timing | Add 50 ms input debounce in OB100 or use IEC Timer |
| MD104 displays garbage after power cycle | MD100 not retentive | HW Config → Retentive Memory | Configure retention range to include MD100 |
| Year byte shows wrong digit (e.g., 5 instead of 25) | Used SFC1 but did not merge BCD nibbles | Inspect year byte in VAT in BCD view | Implement the SRD/SLW/OW merge shown in Section 8 |
| Overflow flag (OV) set in STL | DTB received value > 9999 | Check MD100 bound logic | Clamp MD100 mod 9999 before DTB |
| HMI shows "????" instead of batch number | BCD/DINT mismatch on tag | Check PLC tag data type in HMI | Change to "BCD32" or convert with BCD_DW_TO_DW |
| Sequence resets to 1 immediately | Edge detected twice (re-trigger during cycle) | Lengthen scan OB1 vs. process time | Move counter logic to OB35 cyclic interrupt |
14. Variant: Decadal Prefix and 5-Digit Sequence
Some MES systems require a 5-digit rolling sequence (07YYNNNNN) for lines exceeding 9999 batches per year. The same shift/OR pattern applies with an extra slot:
L W#16#7
SLD 24 // header
L W#16#25 // current year (BCD ready)
SLD 16 // year
OD
L MD 100 // counter (0..99999)
DTB
OD
T MD 108 // 9-digit batch 07 25 00 001
Note: BCD has a hard ceiling of 99 9999 9999 per DWORD, which fits well within 32-bit. For sequences beyond 99 999 999 999, switch to a printable ASCII string assembled char-by-char using BTD and a 12-byte array.
15. Compatibility Notes
| Platform | Firmware | STL Instructions Used | Notes |
|---|---|---|---|
| S7-300 (CPU 314-3, 315-2, 317-2) | V2.x and later | FP, SLD, OD, DTB | All instructions supported |
| S7-400 (CPU 412-2, 414-3, 416-3) | V5.x and later | FP, SLD, OD, DTB | All instructions supported |
| S7-1500 (replacement) | Firmware ≥ V2.0 | DTB replaced by INT_TO_BCD / DINT_TO_BCD | Use TIA Portal SCL form (Section 6) directly |
| S7-1200 | Firmware ≥ V4.2 | DTB unavailable | Use STRING construction in SCL |
| ET 200S IM151 | Firmware ≥ V6.0 | Limited STL support | Use LAD/FBD only |
16. FAQ
Why use DTB instead of direct BCD arithmetic?
DTB converts a binary counter into a printable BCD format readable by HMI panels and trace tools without further conversion; the alternative is to manage each decimal digit separately with masks, doubling code size and introducing off-by-one errors.
How do I prevent duplicate batch numbers after power loss?
Configure MD 100 as retentive in HW Config (CPU Properties → Retentive Memory) so the counter survives power cycles. On S7-400 confirm battery status; on S7-300 a capacitor-backed retentive area is automatic if enabled.
What happens if the sequence reaches 10000?
DTB signals an overflow (status word bit OV) and the BCD result becomes invalid. Add the reset-to-1 guard shown in Section 10 before calling DTB, then raise an alarm flag for operator visibility.
Can I read the year automatically from the CPU clock?
Yes. Call SFC1 (READ_CLK), extract byte 0 of the DATE_AND_TIME structure (BCD year 1990–2089), then merge the two BCD nibbles into a single WORD before shifting it into the batch mask.
Is there a TIA Portal STL equivalent?
TIA Portal no longer recommends STL. Use SCL (Section 6) with SHL_DWORD, OR, and DINT_TO_BCD_DWORD to reproduce identical behaviour, or call the legacy STL FC from a wrapper in TIA Portal's "STEP 7 V5 compatible" blocks.