S7-1500 Optimized DB Pointer: SCL Array & HMI Tag Solution

David Krause11 min read
SiemensTechnical ReferenceTIA Portal
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

1. Problem Overview: Scaling Beyond the Optimized-DB Model

On a SIMATIC S7-1500 programmed with TIA Portal V14 (and subsequent releases), the standard "one Function Block + one Instance DB per motor" pattern breaks down when a machine scales to several hundred drives. A representative scale-out looks like this:

Resource Value (typical 572-motor project) Notes
Motor FBs 572 Each with its own instance DB
Comfort HMI screens 572 One per motor faceplate
Tags per screen ~7 State, command, fault, current, hours, etc.
Raw HMI tag count ~4,000 Exceeds the 2,048 tag ceiling

The instinctive fix is to declare a pointer P#ptr_DB_HMI, swap it inside a CASE block to point at Motor1_DB, Motor2_DB, ... MotorN_DB, and let the HMI ride on a single mirrored DB. The plan collapses the moment the motor DBs are flagged Optimized — the S7-1500 CPU refuses symbolic pointer arithmetic on optimized blocks because the load memory layout no longer carries a fixed offset table the firmware can decompose into byte/bit/word slices at scan time.

Engineer's rule: Symbolic pointer / ANY / Variant / P# access on DBs requires the "Non-optimized block access" attribute. Optimized blocks are accessed only by fully qualified symbolic names. This is the root conflict of the entire pattern.

2. Why Optimized DBs Reject Pointer Operations

An optimized DB stores each tag at whatever offset the compiler chooses, ordered for fastest access and minimum RAM footprint. The compiler is free to re-pad, re-align, and reorder elements between compilations. There is no published offset map the user can dereference with P#DBxx.DBXy.z — and there cannot be, because the offsets are not stable.

Concretely, three symptoms appear when you try pointer work on optimized DBs:

  1. Compiler error 1A89 / 8025 — "The data type of the actual parameter is not compatible with the data type of the formal parameter" — when an ANY pointer is passed into an FB that uses an optimized instance.
  2. Compiler error 8022 — "The variable does not exist or the area of the variable is invalid" — when an indirect DB number is used with a symbolic tag.
  3. Load memory growth without execution benefit — symbolic-only access forces the HMI to read every tag by name, multiplying the OPC UA / S7comm round-trips.

The fix is not to de-optimize the motor DBs (that destroys scan-time performance and symbolic trace). The fix is to introduce a structure the compiler can index natively: an Array DB.

3. The S7-1500 Array DB — the Native Solution

The Array DB is a DB whose only data view is a typed array of a UDT. It is created in one step (Project tree → Add new block → Data block → Array DB) and exists only on the S7-1500/1500T CPU family; the S7-1200 cannot host it. Inside the DB you see a single line such as:

[Array_DB_1].Motor[0..571]
     UDT "Motor_type"

From this point, the compiler exposes the array as a true indexed structure. To address a specific motor you write:

// Symbolic indexed access — no pointer needed
#stMotor := "dbMotors".Motor[iIdx];
"dbMotors".Motor[iIdx].bRun := TRUE;

Because the index iIdx is a runtime variable, the same code path serves all 572 motors. The 572 separate instance DBs are gone; you have one DB whose length scales linearly.

Performance fact: On a CPU 1515-2 PN, indexed access into an array of UDTs runs at the same scan-time cost as direct symbolic access on an optimized instance. The optimization is preserved because the compiler still knows the array element size at compile time and produces a base-address + (index × stride) instruction.

4. The THIS Pointer — Multi-Instance Style Without FBs

When you drag an element of an Array DB into an SCL editor, the syntax does not show the path. Instead it shows:

// Inside FC_MotorLogic, "Motor[0]" was dropped into code:
#stLocal := THIS.Motor;            // (FBD/FUP) or
THIS.RampDone := TRUE;              // boolean slice

THIS is the array-element reference for the duration of the call. It behaves like the implicit INPUT of an FB instance: the block is "self-aware" of which element is active. This is the cleanest way to write a single motor logic routine that the HMI (or any caller) can address by index without ever constructing a pointer.

Style Pointer required? Works on optimized DB? Recommended for 500+ motors?
FB + instance DB per motor No Yes No (DB explosion)
Single DB + P# pointer switching Yes No No
Array DB + indexed access No Yes Yes
Array DB + THIS reference No Yes Yes (cleanest)

5. UDT as the Single-Tag Wrapper for the HMI

Comfort Panels and WinCC Runtime Professional will not bind to an ARRAY[..] OF UDT directly — the HMI tag editor exposes only scalar tags. The work-around used in TIA V14 (and unchanged in V15.1, V16, V17) is to wrap the array in a UDT and bind the HMI to a single tag whose data type is that UDT. At runtime the HMI's VBScript or the panel's dynamic dialog references the field by symbolic path.

Define UDT_Motor as the full motor record (state, command, fault word, current, hours, etc.). The Array DB then becomes ARRAY[0..571] OF "UDT_Motor". The HMI gets one tag: HMI_tag = dbMotors.Motor[iHmiIdx] where the index is re-mapped every screen change.

Tag-count math: If each faceplate consumes 7 fields and the panel allows switching the source index, you collapse 4,000+ tags down to a single UDT tag. The 2,048 tag ceiling on a Comfort Panel is no longer the bottleneck — the index variable is.

6. The 2,048 HMI Tag Limit — How to Live With It

The Comfort Panel firmware (WinCC V14, part of TIA Portal V14) enforces a hard cap of 2,048 tags per connection on the smaller 4"–9" panels and 4,096 on the 10"–22" models. Exceeding the cap produces the runtime alarm "Tag limit exceeded — connection X" and tags are dropped in the order they were declared. Strategies:

  1. UDT collapse — bind one tag per UDT instance; the panel uses the index variable to pick which instance.
  2. Screen-level multiplexing — open one motor faceplate, drive its iIdx tag from a navigation list, refresh the UDT on every index change.
  3. Split across panels — install two KP/TP panels, each binding half the array. Useful when the array exceeds the HMI's own element-count cap.
  4. Promote to WinCC Professional / Unified — Unified Comfort panels (since V16) lift the cap and expose the array index directly; the legacy workaround becomes unnecessary.

7. SCL Code Generation From a Spreadsheet

When the dispatch logic — 257+ CASE branches, or a 572-element IF iIdx = ... ladder — is too long to type, generate it. Build a 3-column spreadsheet:

A — Constant (yellow) B — Reference (orange) C — SCL output
0 Motor_0 0: stMotor := "dbMotors".Motor[0];
1 Motor_1 1: stMotor := "dbMotors".Motor[1];
2 Motor_2 2: stMotor := "dbMotors".Motor[2];
... ... ...
571 Motor_571 571: stMotor := "dbMotors".Motor[571];

Formula in column C (Excel/LibreOffice syntax):

=A2 & ": stMotor := \"dbMotors\".Motor[" & A2 & "];"

Copy column C, paste into the SCL editor. A 572-row dispatch is generated in one operation. The same technique produces a 572-row faceplate tag list for the HMI: each line binds HMI_Faceplate[i].Tag to dbMotors.Motor[i].Field. Maintenance of the motor list becomes a spreadsheet problem, not a TIA Portal problem.

Migration tip: Before converting 572 instance DBs to a single array DB, export the tag list from the existing project (Project tree → PLC → Export tag list) so the spreadsheet can be cross-checked. Siemens' "STEP_7_V14_new_functions.pdf" highlights array DBs as a V14 feature — only the S7-1500/1500T supports them in that release.

8. Step-by-Step Refactor (TIA V14, S7-1500)

  1. Inventory the data. Open the project, list every per-motor tag, group them into a single UDT named UDT_Motor. Mark bit fields, INT, REAL, and DATE_AND_TIME explicitly — implicit typing in the UDT saves space.
  2. Create the Array DB. Project tree → Add new block → Data block → "Array DB" type. Choose UDT_Motor as the array element, set [0..571]. Leave the "Optimized block access" attribute enabled.
  3. Replace per-motor FBs. Convert the motor FB into a stateless FC that receives UDT_Motor by IN_OUT. The FC's internal state moves into the array element itself. The single FC replaces 572 instance DBs.
  4. Generate dispatch code. Use the spreadsheet trick above to build the CASE iIdx OF block in the OB1 (or OB82-grade cyclic OB if the cycle time requires it).
  5. Re-bind the HMI. In the HMI tag editor, delete the 4,000+ per-motor tags. Create one tag of type UDT_Motor pointing to dbMotors.Motor[iHmiIdx]. Add a separate INT tag iHmiIdx for the index; drive it from the navigation list.
  6. Enable "Multiplex tag" on the HMI connection. TIA V14 introduces this property on the HMI connection: it lets the panel switch the symbolic source of a UDT tag at runtime by writing the index variable. Without it the tag is statically bound to Motor[0].
  7. Compile and download. Compile the PLC first (so the HMI gets the new tag list), then compile the HMI. The Online > Compare function will report the diff in tag count — a 95% reduction is typical.

9. Verification and Diagnostics

After the refactor, validate three things before sign-off:

  1. PLC cycle time. Open Online > Diagnostics > Cycle time. With 572 motors in a single array, expect the cycle time to decrease 10–30% because the compiler no longer has to resolve 572 separate instance DB contexts. If cycle time climbs, an indirect dispatch is looping — look for a FOR that calls the motor FC inside another FOR.
  2. HMI refresh rate. On the Comfort Panel, open the "System diagnostics → Performance" screen. The "Tags / second" counter should remain at or above the previous rate; if it drops, the multiplex tag is being re-pointed more often than the screen-redraw budget allows.
  3. Optimized-block attribute. In the project tree, right-click the array DB, Properties → Attributes. Confirm "Optimized block access" is ticked. If the compiler silently demoted it to non-optimized (often after a bulk copy-paste from a S7-1200 source), the pointer ban is back and scan time suffers.
Symptom Likely cause Fix
Compiler 1A89 / 8025 on ANY pointer DB attribute was reset to non-optimized Re-tick "Optimized block access" in DB properties
Runtime "Tag limit exceeded" on HMI Multiplex tag not enabled Connection properties → "Multiplex tag" = Yes
All faceplates show Motor[0] data iHmiIdx tag not bound to a write-capable PLC tag Bind iHmiIdx to a real PLC INT, write from navigation list
Array DB option greyed out CPU is S7-1200, not S7-1500 Use ARRAY[..] OF UDT inside a standard DB on S7-1200
Cycle time doubled after refactor FC copies the whole UDT on every call Pass UDT by IN_OUT (reference) instead of IN/OUT-by-value

10. Field-Proven Caveats

  • Move-block-variant is not a substitute for a pointer. MOVE_BLK_VARIANT (and its predecessor BLK_MOV) copies memory, it does not redirect HMI bindings. It is useful for mirroring but does not solve the index-routing problem.
  • VBScript on the panel is a last resort. WinCC Comfort's VBS can iterate an HMI-side array, but the data still has to come from the PLC by tag. The round-trip cost defeats real-time monitoring; reserve VBS for non-critical navigation logic.
  • Security-rated motors lose their instance DB. If a motor is part of a F-runtime / F-CPU safety program, the safety FB is required to own its instance. The Array-DB pattern applies to the standard user program only; safety instances remain one-per-motor by design (SIL 2/3 audit reasons).
  • Watch the array upper bound. An array of 572 UDTs of 64 bytes each occupies ~36 KB of load memory. The S7-1500 CPU 1511 has 150 KB of work memory for data — comfortable, but on the CPU 1511-1 PN with bit-oriented safety adders, always check the memory budget after the refactor.
  • TIA V14 → V15+ migration. The Array-DB concept survives all subsequent TIA Portal releases unchanged. The "Multiplex tag" property was promoted to a first-class option in V16 and is now under "HMI tag → Settings → Multiplexing".

Why does my S7-1500 reject a P# pointer to a DB marked as optimized?

Optimized blocks do not expose a fixed offset table — the compiler is free to re-pack the data on every build. Symbolic pointer / ANY / P# access requires "Non-optimized block access"; the runtime cannot compute a stable base address otherwise. Convert the design to an Array DB or accept a non-optimized DB and its scan-time penalty.

What is the maximum number of elements in a Comfort Panel HMI tag list?

WinCC Comfort V14 caps internal tags at 2,048 on 4"–9" panels and 4,096 on 10"–22" panels. Each external tag is also counted. The fix is to wrap a UDT and bind a single multiplexed tag, then re-point the tag's index from a navigation list — typical project size drops from 4,000+ tags to a handful.

Can the S7-1200 host an Array DB?

No. The Array DB was introduced in TIA Portal V14 exclusively for the S7-1500 / 1500T CPUs. On the S7-1200, declare ARRAY[0..n] OF "UDT_Motor" inside a standard global DB — the compiler emits the same indexed-access code path, but you lose the array-DB specific diagnostics.

How does the THIS pointer differ from a P# pointer inside an FC?

THIS is a compile-time, array-element reference bound the moment you drag a tag into the editor. It needs no P# arithmetic, no ANY marshalling, and works on optimized blocks. A P# pointer is a runtime address built from area, byte, and bit offsets; the runtime cannot build it for optimized data because the offsets are not published.

Is the multiplex tag property available in TIA Portal V14?

It was added in V14 SP1 and is configured on the HMI connection ("Properties → Multiplex tag = enabled"). On earlier V14 builds you must use a separate INT tag and a VBScript event to re-point the symbolic tag, which is more fragile and slower at runtime.

Back to blog