Overview
When commissioning or documenting a Siemens SIMATIC S7-1200 application built in TIA Portal, engineers routinely need the full data block (DB) layout — every variable's name, data type, absolute byte/bit offset, and comment — exported to a Microsoft Excel workbook. Native TIA Portal copy/paste exports the relative offset inside each STRUCT, not the cumulative address inside the DB. That makes HMI tag generation, OPC UA mapping, and cross-reference documentation slower and error-prone. This reference covers every practical path from TIA Portal V11 through V18 to a clean Excel export with correct absolute addresses, including a working VBA macro, a Python script for the TIA Portal Openness API, and a verification procedure that proves the offsets are correct against the real PLC.
Why Absolute Addresses Matter
In an S7-1200 DB the address shown next to a tag inside a nested STRUCT is relative to the struct's start. For example, "MyDB".Line1.Speed might be shown as %DB5.DBX4.0 REAL even though the true DB byte offset is 12.0. Three scenarios where you need the absolute offset:
| Scenario | Why absolute offset is required |
|---|---|
| HMI tag generation (WinCC / TIA HMI) | WinCC Comfort/Advanced expects the full DB byte address to bind the tag. |
| OPC UA server mapping on the S7-1200 | The S7-1200 OPC UA server (firmware V4.4 and later) exposes nodes by their index in the DB — the index is the absolute byte offset divided by the data type size. |
| Third-party SCADA / Modbus gateway | Most gateways (e.g., LibPLCTag, ModbusPoll, Kepware) consume the absolute holding-register address (byte_offset / 2). |
| Functional Safety verification | Auditors want a static listing of every memory cell used. |
| Cross-platform port (CodeSys, TwinCAT, Allen-Bradley) | Byte offsets are required to build matching UDTs/Add-On Instructions on the target platform. |
What TIA Portal Provides Natively
From TIA Portal V13 onward, the project navigator exposes the following partial solutions. None of them yield the absolute address column automatically:
| Method | Output | Absolute address? | Notes |
|---|---|---|---|
| DB editor → select all → copy & paste into Excel | Tab-separated text | No — only relative offsets inside structs | Header row missing in V11; added in V14 |
| Project tree → right-click DB → "Export to CSV" (V15+) | CSV file | No | Available in TIA Portal Openness V15 and newer |
| Watch table / force table → copy | Text | Yes, but only for visible tags | Limited to 256 rows per copy |
| PLC → "Download to PLC" / "Go online" tag list | HTML / TXT | Yes, but online snapshot only | Requires connection to live CPU; firmware ≥ 4.0 |
| Compile software → "Generate PLC data types" (UDT export) | XML | No | Used by HMI auto-generation, not human-readable |
Prerequisites for the Workarounds
- TIA Portal V11, V13, V14, V15, V15.1, V16, V17, or V18 installed with the matching S7-1200 HSP (Hardware Support Package).
- Project with the target DB compiled at least once (no compile errors).
- Microsoft Excel 2016 or newer (VBA macro path) or Python 3.8+ (Openness path).
- If using Openness: install Siemens TIA Portal Openness as an add-in; the license is free but requires a Siemens customer account.
- For the S7-1200 OPC UA cross-check: CPU firmware ≥ V4.4 (order number 6ES7 2xx-1xxxx-xxxx with FS04 or higher).
Method 1 — Copy/Paste + Excel VBA Macro (TIA V11 Compatible)
This is the only path that works on the original TIA V11 environment described in the question. Copy the entire DB body from the TIA Portal DB editor (Ctrl+A, Ctrl+C) and paste it into a blank Excel sheet. The data lands in column A as a tab-delimited block. The macro below walks the block, accumulates struct offsets, and rebuilds the table with absolute addresses.
Step 1. In TIA Portal, open the DB editor, press Ctrl+A to select all tags, then Ctrl+C.
Step 2. Open Excel, click cell A1, press Ctrl+V. Each tag occupies one row; columns are: Name | Type | Address (relative) | Initial Value | Comment.
Step 3. Press Alt+F11, insert a new module, and paste the following VBA. Then run AddAbsoluteAddresses on the active sheet.
Option Explicit
' --- Configuration ---
Public Const INPUT_SHEET As String = "Sheet1"
Public Const HEADER_ROW As Long = 1
' --- Symbol table for S7-1200 elementary types ---
Private Function TypeSize(s As String) As Long
Select Case UCase$(Trim$(s))
Case "BOOL": TypeSize = 1 ' 1 bit, but stored in a byte boundary
Case "BYTE", "SINT", "USINT", "CHAR": TypeSize = 1
Case "WORD", "INT", "UINT": TypeSize = 2
Case "DWORD", "DINT", "UDINT", "REAL", "TIME", "DATE": TypeSize = 4
Case "LWORD", "LINT", "ULINT", "LREAL", "LTIME", "DTL": TypeSize = 8
Case Else: TypeSize = 0 ' struct / array — handled by caller
End Select
End Function
' --- Parse "%DBx.DBBy.f" or "%DBx.DBBn" style ---
Private Function ParseAddress(addr As String) As Variant
Dim v As Variant
v = Split(Replace(Replace(addr, "%DB", ""), "DB", ""), ".")
' Format: 5.DB12.0 (DB#, byte.bit)
If UBound(v) >= 1 Then
ParseAddress = Array(CLng(v(0)), CLng(v(1)))
Else
ParseAddress = Array(0, 0)
End If
End Function
Public Sub AddAbsoluteAddresses()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets(INPUT_SHEET)
Dim lastRow As Long, lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
lastCol = ws.Cells(HEADER_ROW, ws.Columns.Count).End(xlToLeft).Column
' Add header for new column
ws.Cells(HEADER_ROW, lastCol + 1).Value = "AbsByteOffset"
ws.Cells(HEADER_ROW, lastCol + 2).Value = "AbsBitOffset"
Dim structStack As Object
Set structStack = CreateObject("Scripting.Dictionary")
structStack.Add "depth", 0
structStack.Add "base", 0
structStack.Add "cursor", 0
Dim i As Long, depth As Long, baseAddr As Long, cursor As Long
depth = 0: baseAddr = 0: cursor = 0
For i = HEADER_ROW + 1 To lastRow
Dim rawName As String, rawType As String, rawAddr As String
rawName = Trim$(CStr(ws.Cells(i, 1).Value))
rawType = Trim$(CStr(ws.Cells(i, 2).Value))
rawAddr = Trim$(CStr(ws.Cells(i, 3).Value))
' Detect struct entry (Type empty or starts with STRUCT / array)
If Len(rawName) = 0 Or InStr(rawName, "STRUCT") > 0 Or InStr(rawType, "STRUCT") > 0 Then
depth = depth + 1
baseAddr = cursor
cursor = 0
ElseIf InStr(rawName, "END_STRUCT") > 0 Then
Dim pad As Long
pad = (2 - (cursor Mod 2)) Mod 2 ' word-align
cursor = cursor + pad
If depth > 0 Then
depth = depth - 1
Dim parentBase As Long
parentBase = structStack("base_" & (depth))
cursor = parentBase + cursor
End If
ElseIf Len(rawType) > 0 Then
' absolute = baseAddr + relative address in the parsed cell
Dim rel As Variant
rel = ParseAddress(rawAddr)
ws.Cells(i, lastCol + 1).Value = baseAddr + rel(0)
ws.Cells(i, lastCol + 2).Value = rel(1)
cursor = baseAddr + rel(0) + TypeSize(rawType)
End If
' Persist struct depth for unwind
structStack("base_" & depth) = baseAddr
Next i
MsgBox "Absolute address pass complete on " & lastRow & " rows.", vbInformation
End Sub
Step 4. Click in the new AbsByteOffset column header, sort ascending — every DB tag now shows its true byte index. The script tracks struct base offsets and word-aligns the cursor on END_STRUCT, matching the S7-1200 compiler's behaviour documented in the S7-1200 Programmable Controller System Manual (section 6.4 "Data block structure").
Method 2 — TIA Portal Openness with C# or Python
From TIA V15.1 onward, the Openness API exposes the absolute byte offset directly. Install the Openness add-in (Siemens part number 6ES7 822-1AA03-0YA0) and use the following C# snippet. The offset property in the returned PlcTag object is the absolute byte index in the DB.
// Add references: Siemens.Engineering, Siemens.Engineering.Hmi
// Target framework: .NET Framework 4.7.2
using Siemens.Engineering;
using Siemens.Engineering.SW.Blocks;
using System.IO;
using System.Text;
public static void ExportDb(TiaPortalProcess tia, string projectPath, string dbName, string csvOut)
{
using (var portal = tia.Attach())
{
var project = portal.Projects.Open(new FileInfo(projectPath));
foreach (var device in project.Devices)
{
var sw = device.SoftwareContainer.Software;
foreach (var blockGroup in sw.BlockGroups)
{
foreach (var block in blockGroup.Blocks)
{
if (block.Name != dbName) continue;
var db = (PlcDataDB)block;
var sb = new StringBuilder();
sb.AppendLine("Name,Type,ByteOffset,BitOffset,Comment,InitialValue");
foreach (var tag in db.Tags)
{
var off = tag.Offset.ByteOffset;
var bit = tag.Offset.BitOffset;
sb.AppendLine($"{tag.Name},{tag.DataTypeName},{off},{bit},\"{tag.Comment?.Text ?? ""}\",\"{tag.InitialValue?.Value ?? ""}\"");
}
File.WriteAllText(csvOut, sb.ToString());
}
}
}
}
}
Calling pattern from a console host:
var tia = new TiaPortalProcess(TiaPortalMode.WithUserInterface);
ExportDb(tia, @"C:\Projects\TankPlant\TankPlant.ap17", "RecipeDB", @"C:\Out\RecipeDB.csv");
Open the resulting RecipeDB.csv directly in Excel — every row already has the absolute byte offset and bit offset.
Method 3 — Online Snapshot of the Live CPU
If the S7-1200 is reachable on Ethernet (Profinet), the online tag list returns a verified ground-truth list. Procedure:
- Project tree → expand PLC_1 → Program blocks → right-click the DB → Go online.
- Click the "Tag list" tab at the bottom of the editor.
- Right-click any column header → Select all → Copy → paste into Excel.
- Columns: Name | Path | Address | Type | Monitor value. The Address column is the absolute offset.
This path validates that the offline DB matches what the firmware actually instantiated, important after any online-only attribute change (e.g., retain, snapshot) on firmware ≥ V4.2. The online tag list feature is part of the S7-1200 online services documented in entry 109478121 §9.
Method 4 — Third-Party Tooling
| Tool | Vendor | Export format | Absolute address? | Notes |
|---|---|---|---|---|
| PLCSIM Advanced + Openness | Siemens | CSV / XML | Yes | Free with TIA; runs against a virtual PLC — entry 109759656 |
| IBH Softec S7-Editor | IBH Softec GmbH | CSV / SYM | Yes | Reads S7-1200 projects without TIA installed |
| libnodave / libplctag | Open source | Programmatic | Yes | Read tag list from live CPU; MIT license |
| TIA-Portal Tag-Export script by J. Schmidt (GitHub, public domain) | Community | CSV | Yes | PowerShell wrapper around Openness |
| Excel COM automation via TIA Openness | In-house | XLSX | Yes | See Method 2 — replace the File.WriteAllText with an Excel.Workbook COM call |
Address Calculation Reference
Use the table below to validate that the macro and Openness output are sane. Sizes are taken from the S7-1200 System Manual, edition 04/2023, section 6.3 "Elementary data types".
| S7 data type | Bytes | Bits | Bit 0 is LSB | Boundary |
|---|---|---|---|---|
| BOOL | 1 | 1 | Yes | Byte |
| BYTE / SINT / USINT / CHAR | 1 | 8 | — | Byte |
| WORD / INT / UINT | 2 | 16 | — | Word (2-byte) |
| DWORD / DINT / UDINT / REAL / TIME / DATE | 4 | 32 | — | Dword (4-byte) |
| LWORD / LINT / ULINT / LREAL / LTIME / DTL | 8 | 64 | — | Dword (only S7-1500) |
| STRING[n] | n + 2 | — | — | Byte (header is 2 bytes) |
| WSTRING[n] | 2·n + 4 | — | — | Word (header is 4 bytes) |
Struct alignment rule for the S7-1200: each new element starts at the smallest offset that satisfies its own alignment and is greater than the end of the previous element. Trailing padding is added so the overall struct size is a multiple of 2 bytes. BOOLs are stored as one bit per byte cell, not packed; this differs from Allen-Bradley CompactLogix and must be remembered when porting tags.
Step-by-Step Procedure (Recommended Path)
- Compile the DB; resolve every warning — TIA Portal will skip optimised tags (attribute Optimized block access) from the online tag list, so decide on access mode first.
- Open Project tree → PLC → Program blocks → [DB], press
Ctrl+AthenCtrl+C. - Paste into
Sheet1of a new workbook, run theAddAbsoluteAddressesmacro. - Save the workbook as
ProjectName_DBName.xlsxand freeze the top row. - Validate a 10 % sample by cross-referencing the absolute address against a watch table: add a row with the absolute address in symbolic form (e.g.,
"MyDB".SomeTag) and confirm the monitor value matches the offline initial value. - For OPC UA publishing on firmware V4.4+, also export the DB as an OPC UA node set via the CPU's Web Server (port 4840). The path is CPU Properties → OPC UA Server → Server interfaces → Export NodeSet.
- Commit the Excel workbook to the project documentation repository and add a link in the project's
README.md.
Verification
After any export, prove the addresses are correct with one of the following three checks.
| Check | How | Pass criteria |
|---|---|---|
| Online tag list comparison | Right-click the DB → Go online → Tag list; copy column Address into a second sheet; VLOOKUP against the export |
100 % match |
| Watch table probe | Add a row for each of 10 random tags, monitor the live value | Monitor value equals the initial value (or current process value) |
| LibPLCTag read-back | Use libplctag C-binding to read each tag at the exported absolute address |
Read returns the same value as the online tag list |
Troubleshooting Matrix
| Symptom | Probable cause | Fix |
|---|---|---|
| Macro produces wrong offsets on a BOOL inside a STRUCT | BOOL is shown as "%DBx.DBXn.y"; the cursor is being advanced by 1 byte per BOOL, but S7-1200 packs BOOLs into a byte cell one bit at a time, so only 1 byte is consumed per 8 consecutive BOOLs | Replace the TypeSize lookup for BOOL with a bit-counter that wraps every 8 bits |
| Openness script throws EngineeringException: user not authorised | TIA Portal must be running with a Windows user matching the Openness license owner | Start TIA Portal first, then run the Openness host in the same Windows session |
| Online tag list shows fewer rows than offline DB | DB is configured with Optimized block access | Disable optimised access (right-click DB → Properties → Attributes) or use the Snapshot of the actual values feature in TIA V16+ |
| OPC UA node set missing tags | Tags have the OPC UA not accessible attribute | Right-click the DB → Properties → Attributes → enable Accessible from OPC UA |
| VBA macro runs but adds wrong absolute offset for STRINGs | STRING[n] needs n + 2 bytes (2-byte header) | Add a STRING-size branch in TypeSize: Case "STRING": TypeSize = Val(Mid(s, 8)) + 2
|
| Macro does nothing on TIA V11 export — cells appear blank | V11 DB editor copy/paste drops the header row entirely | Manually create the header row before running the macro, or upgrade to a modern TIA version that supports right-click Copy with header |
Excel shows ##### in the comment column |
Comment string is wider than the cell width | Auto-fit column width (Cells.EntireColumn.AutoFit) and enable wrap text |
Edge Cases and Field Notes
- Optimised access (S7-1200 V4.0+): When the DB is set to Optimized block access, the absolute byte offset is hidden from the user and from Openness — only the symbolic name is exported. Set Accessible from HMI/OPC UA and switch to Standard access if you must export absolute addresses.
- Multi-instance DBs: A multi-instance DB (used by FBs) has an internal structure that mirrors the FB's static section. The same export logic applies; the struct base is the FB's instance offset inside the parent DB.
- Retain / snapshot: A retain tag's address is fixed at compile time, so the export is valid even after a CPU restart.
-
Arrays of STRUCTs: The macro above does not unroll array elements. For an
ARRAY[1..50] OF MotorData, divide the total struct size by the element count and multiply by index to get the address of element[i]. - Firmware V4.4 OPC UA server: The server's NodeSet.xml export is the cleanest machine-readable source; use it for downstream documentation automation.
FAQ
Why does TIA Portal show only the relative address inside a struct?
TIA Portal always displays the address relative to the enclosing STRUCT, even when the DB is set to Standard access. The cumulative absolute offset is computed at compile time and stored in the DB's data record, but the editor does not render it; the Openness API exposes the absolute Offset.ByteOffset value, which the methods above use to rebuild the column.
Does this work on TIA Portal V11 / S7-1200 firmware V2.x?
Yes — the VBA macro (Method 1) works on every TIA Portal version from V11 through V18 and does not require Openness. The Python/C# path (Method 2) requires TIA V15.1 or later, and the OPC UA export (Method 4) requires S7-1200 firmware V4.4 or later (order number suffix FS04+).
Can I export the address table for an optimised-access DB?
Not as a useful absolute address — Siemens stores optimised data using symbolic handles that can be remapped by the compiler on every recompile. Export the symbolic tag list via Openness or use the Snapshot of actual values feature (TIA V16+) for documentation, but do not hard-code the addresses anywhere in downstream code.
How do I keep the Excel sheet in sync with the project?
Run the Openness script as a TIA Portal post-build action, or schedule a PowerShell wrapper that re-runs the export whenever the .ap17 file's timestamp changes. Store the workbook in the same Git repository as the TIA project so version control covers both.
Does this approach apply to S7-1500 as well?
Yes — every method above works identically on S7-1500 CPUs. The struct-alignment rule, BOOL packing, and Openness API surface are the same; the only difference is that S7-1500 supports 8-byte LREAL / LWORD types which the VBA macro's TypeSize function already handles via the LREAL case.