Generating 8-Digit Batch Numbers in Siemens S7-300/S7-400 STL

David Krause11 min read
PLC ProgrammingS7-300Siemens
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

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 100 for the sequence counter and MD 104 for the assembled BCD batch number (adjust to project naming conventions).
  • Retentive flag/bit memory region sized to preserve MD 100 across 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
Bit layout of MD 104 after assembly: The high byte holds 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.

Validity window: DTB rejects values outside 0 … 99999999 (BCD) and sets 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:

  1. Network 1 — Edge detect & increment. An I 0.0---| |--- contact followed by a POS edge detector (LAD: ---|P|---) driving an ADD_DI block: MD100 := MD100 + 1.
  2. Network 2 — Compose header & year. Two WORD_TO_DWORD blocks feeding SHL_DWORD (shifts 24 and 16) then a WORD_OR_DWORD merger.
  3. Network 3 — BCD conversion & merge. DINT_TO_BCD_DW on MD100, then WORD_OR_DWORD with the header+year mask, stored in MD104.

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:

  1. Open HW Config → select the CPU → Properties → Retentive Memory.
  2. Enter the byte range starting at MB100 with the required length, e.g., from MB100 length 8 (covers MD100 and MD104).
  3. 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

  1. Compile and download FC1; clear MD100 and MD104.
  2. Open a watch table (VAT) on I 0.0, M 200.0, MD 100, MD 104.
  3. Force I 0.0 once; verify MD 100 increments to 1 and MD 104 reads 07 12 00 01 in hexadecimal (BCD) view.
  4. Repeat 9998 times; verify the 9999th edge produces 07 12 99 99.
  5. Cycle power; verify MD 100 retains the last value (retentive configured).
  6. Change CPU clock to 31-DEC 23:59:59 → 1-JAN of new year via SFC0/SFC1; verify the year byte in MW110 updates and the next batch number format is 07YY0001.
  7. Trigger overflow by forcing MD100 = 9998 then 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.

Back to blog