Overview
Reading bulk data from a PROFINET IO device on a SIMATIC S7-1200 or S7-1500 returns a contiguous block of bytes (for example, a 56-byte input area exposed as devicename~56_bytes_I_1). When the project only declares the input as a flat HW_IO address or as an ARRAY[..] OF DINT, the programmer loses the ability to symbolically address individual bytes, words, or bits inside the payload. This article documents four field-proven techniques to recover granular access: bit/byte slice notation on a DWORD tag, casting the I/O address to a UDT, telegram-based symbolic I/O assignment, and consistent block reads using the GETIO instruction. Each method is mapped to a specific TIA Portal workflow and to a STEP 7 firmware capability baseline (CPU firmware V4.4 for S7-1200, V2.0+ for S7-1500).
Prerequisites
- STEP 7 (TIA Portal) V16 or later. V20 is recommended for the most current GETIO documentation set; the GETIO instruction itself has been available since V14 SP1 on S7-1500 and since V14 on S7-1200 firmware V4.1+. Reference: GETIO: Read all inputs of a submodule (S7-1200, S7-1500).
- S7-1200 CPU with firmware V4.4 or higher, or S7-1500 CPU with firmware V2.0 or higher. Slice access (
%B0..%B3) on aDWORDrequires the S7-1500 or S7-1200 firmware V4.0+ instruction set. - Configured PROFINET IO device in the device view, with at least one submodule assigned to a slot. The device GSD file must be installed in TIA Portal so that the I/O length (e.g., 56 bytes input, 0 bytes output) is visible.
- Available PLC tags table, plus a data block of type UDT if the UDT-based method is used.
- Optional: PROFINET IO Connector V1.1 documentation for advanced scenarios involving third-party IO Connector stacks. Reference: PROFINET IO Connector V1.1 PDF (Siemens Support, attachment 109793251).
HW_IO data type identifies a hardware input by its I/O address. A tag of type HW_IO cannot store process data; it is only an alias. To read data, the tag must be cast to BYTE, WORD, DWORD, ARRAY OF BYTE, or a UDT at the point of consumption.Method 1: Byte Slice Access on a DWORD (%Bx)
On S7-1200 (FW V4.0+) and S7-1500, a tag declared as DWORD exposes four byte slices %B0, %B1, %B2, and %B3, plus sixteen bit slices %X0..%X15. This is the fastest path to granular reads when the input length is small and fits inside one or more DWORD tags.
- In the PLC tag table, add a tag named
ioDword_0of data typeDWORDand assign its address to the start of the PROFINET input range, e.g.%ID500. - In the program, read the entire DWORD with
L %ID500/T "ioDword_0"or use symbolic reference directly:"ioDword_0" := "devicename"~56_bytes_I_1[0];(the slice and cast are implicit in SCL). - Access individual bytes with SCL slice syntax:
// SCL example — direct slice access on a DWORD
"ioByte_0" := "ioDword_0".%B0; // bits 0..7
"ioByte_1" := "ioDword_0".%B1; // bits 8..15
"ioByte_2" := "ioDword_0".%B2; // bits 16..23
"ioByte_3" := "ioDword_0".%B3; // bits 24..31
For a 56-byte payload (14 DINTs), declare ioDword_0 through ioDword_13 and assign addresses %ID500..%ID552 step 4. Each DWORD yields 4 byte slices and 16 bit slices without any additional MOVE or serialization.
Method 2: Cast the I/O Area to a UDT
Casting the PROFINET input area to a UDT is the most maintainable long-term solution. The UDT documents the slot layout (which byte carries status, which carries encoder position, etc.) and lets the programmer reference fields symbolically throughout the project.
Step-by-step
- Open the PLC data types editor in TIA Portal and create a new UDT named
UDT_PN_Devicewith the exact field layout of the 56-byte input. Example for a generic 14-DINT payload:
TYPE UDT_PN_Device :
STRUCT
StatusWord : WORD; // bytes 0..1 (IW500)
Padding : ARRAY[0..1] OF BYTE; // bytes 2..3
EncPosition : DINT; // bytes 4..7 (ID504)
EncSpeed : DINT; // bytes 8..11 (ID508)
Torque : DINT; // bytes 12..15 (ID512)
Diagnostics : ARRAY[0..3] OF DINT; // bytes 16..31
UserData : ARRAY[0..7] OF DINT; // bytes 32..63 (only first 24 used)
END_STRUCT
END_TYPE
- In the PLC tag table, add a tag named
pnInputof data typeUDT_PN_Deviceand assign its address to the start of the PROFINET input range:%IB500. TIA Portal accepts the start address even when the tag type is a STRUCT; subsequent fields are mapped at%IB500 + offsetautomatically. - Reference fields symbolically anywhere in the project:
"pnInput".EncPosition,"pnInput".UserData[3]. - If the device also has outputs, create a second UDT
UDT_PN_Outputsand assign it to the output range%QB500.
UDT_PN_Device. The address stays at the original offset, and all UDT members are mapped relative to that offset. This is the same incremental path Siemens describes in the S7-1500 system manual, and it mirrors the AT-overlay pattern familiar from STL/S7-300 programming.Method 3: Telegram-Based Symbolic I/O Assignment
If the PROFINET device supports a configurable telegram (for example, Siemens SINAMICS drives use telegrams 1, 2, 3, 5, 6, 7, 9, 110), TIA Portal can map the telegram to a UDT directly. This is the cleanest method when a standard telegram is available because the GSD file ships the structure definition.
- In the device view, click the PROFINET device and open the device configuration.
- Select the submodule that represents the telegram slot. Assign the standard telegram number from the drop-down (e.g., Telegram 105 for SINAMICS S120 free telegram configuration).
- In the I/O addresses tab, note the assigned input start address (e.g.,
%IB500) and the length. - Create a PLC tag of type
UDT_SINAMICS_T105(Siemens provides this UDT in the SINAMICS library), assign it to%IB500, and reference fields symbolically:"drive".SetpointSpeed.
| Method | Best For | Required Firmware | Symbolic Access | Consistent Read |
|---|---|---|---|---|
| %Bx slice on DWORD | Small payloads (≤16 bytes), ad-hoc debugging | S7-1200 FW V4.0+ / S7-1500 FW V2.0+ | Yes (via tag) | No — read is non-atomic across DINTs |
| UDT cast on I/O | Stable, documented device layout | S7-1200 FW V4.0+ / S7-1500 FW V2.0+ | Yes | No — UDT is read word-by-word |
| Telegram UDT | Drives and devices with PROFINET telegrams | S7-1200 FW V4.2+ / S7-1500 FW V2.0+ | Yes (Siemens UDT) | Partial — depends on telegram mapping |
| GETIO / DPRD_DAT | Consistent submodule snapshot, large payloads | S7-1200 FW V4.1+ / S7-1500 FW V1.0+ | Via destination UDT | Yes — atomic over the entire submodule |
Method 4: GETIO for Consistent Submodule Reads
Both DPRD_DAT (read) and DPWR_DAT (write) operate on a contiguous I/O area. When the application needs a consistent snapshot of the entire submodule (for example, to read a 56-byte input atomically with respect to OB1 cycles), use GETIO from the Extended Instructions. GETIO — Read all inputs of a submodule (S7-1200, S7-1500).
// SCL — GETIO usage on a PROFINET submodule
#retVal := GETIO(
LADDR := 500, // logical base address of the submodule
ID := 0, // submodule identifier (0 = whole station)
LEN := 56, // length in bytes
RECORD := "dbInputs".inputArray, // destination DB of type ARRAY[0..55] OF BYTE
#busy := #busyFlag,
#error := #errorFlag,
#status := #statusWord);
// After GETIO returns TRUE (done), cast the byte array back to a UDT for symbolic access
"ioStruct".StatusWord := "dbInputs".inputArray[0..1];
"ioStruct".EncPosition := DWORD_TO_DINT( DWORD_FROM_BYTES("dbInputs".inputArray[4],
"dbInputs".inputArray[5],
"dbInputs".inputArray[6],
"dbInputs".inputArray[7]) );
Key parameters
-
LADDR: Logical base address of the PROFINET IO submodule. For the example device at
%IB500, this is500. Note thatLADDRis aWORD; always pass the integer literal or anINTtag, not a hex string. -
ID: Hardware identifier of the submodule. When the entire station is read, set
ID := 0. For partial reads, use the system constant generated by TIA Portal under PLC > System constants > ... > Head station. -
LEN: Length in bytes. The maximum transfer length per
GETIOcall is 1024 bytes; for larger submodules, split into multiple calls with differentIDvalues. -
RECORD: Destination tag. Must be a fully qualified tag of type
ARRAY[..] OF BYTEin a non-optimized DB, or a UDT-compatible buffer in a standard DB. Optimized DBs are allowed in S7-1500 FW V2.0+ but require theRETENTIVEattribute to be off and the DB to be configured as non-process-image.
Address Arithmetic: Locating Byte N Inside a Submodule
Byte-N inside a submodule at base IBbase is IBbase + N. For the 56-byte example with a telegram at IB500:
| Symbolic Name | Address | Notes |
|---|---|---|
| Byte 0 (first byte of submodule) | %IB500 | Bit 0 of first byte |
| Byte 1 | %IB501 | |
| Byte 4 (start of first DINT) | %IB504 | Equivalent to %ID504 / %ID501 (alignment-dependent) |
| Byte 5 | %IB505 | High byte of %IW502 (little-endian) |
| Byte 55 (last byte) | %IB555 | Final byte of 56-byte payload |
Siemens S7-1500 stores multi-byte values in little-endian order, so the first byte of a DINT is the least significant byte (LSB) and is placed at the lowest absolute address. A debugger that monitors IB504..IB507 will see the value of ID504 in the order LSB, LSB+1, LSB+2, MSB.
Comparison of the Four Methods
| Criterion | %Bx Slice | UDT Cast | Telegram UDT | GETIO |
|---|---|---|---|---|
| Setup effort | Very low | Medium | Low (if Siemens UDT exists) | Medium |
| Code clarity | Medium | High | High | High |
| Read consistency | None (slice reads are not atomic across 4 bytes) | None (UDT members update per OB1 cycle) | Partial (depends on telegram mapping) | Full (entire submodule in one call) |
| Performance (overhead per scan) | Negligible | Negligible | Negligible | One system call per cycle (~5-20 µs on S7-1516) |
| Recommended use | Diagnostic, ad-hoc | Production code, fixed layout | Drives with standard telegrams | Large submodules, safety-relevant reads |
Step-by-Step Implementation (Recommended Path)
The recommended long-term pattern combines a UDT for symbolic access with GETIO for consistent reads. The procedure below assumes a 56-byte PROFINET input at %IB500.
-
Create the input UDT. In PLC data types, add
UDT_PN_Inputwith the device-specific layout. Use a tool like a PROFINET frame capture or the device's manual to confirm field offsets. -
Create the target DB. Add
DB_PN_Buffercontainingbuffer : ARRAY[0..55] OF BYTE;andparsed : UDT_PN_Input;. Mark the DB as non-optimized (S7-1500) to allow absolute addressing, or as optimized with Accessible from HMI/OPC UA unchecked if the controller supports it. - Call GETIO in OB1 (or a cyclic OB). Use the parameters shown in the snippet above. Wire the DONE, ERROR, and STATUS outputs to evaluate success; on ERROR, evaluate STATUS per the table below.
- Parse the byte array into the UDT. After DONE, copy the buffer into the UDT. For known DINTs at fixed offsets, use the conversion pattern:
"parsed".EncPosition := DWORD_TO_DINT(
SHL_DWORD(BYTE_TO_DWORD("DB_PN_Buffer".buffer[7]), 24) OR
SHL_DWORD(BYTE_TO_DWORD("DB_PN_Buffer".buffer[6]), 16) OR
SHL_DWORD(BYTE_TO_DWORD("DB_PN_Buffer".buffer[5]), 8) OR
BYTE_TO_DWORD("DB_PN_Buffer".buffer[4]) );
-
Reference fields symbolically. Use
"DB_PN_Buffer".parsed.EncPositionin all subsequent code. The buffer is the only place where raw bytes live; the parsed UDT is the source of truth. -
Repeat for outputs. Use
SETIOfor consistent output writes; see the related TIA Portal instruction set.
Verification
- Compile and download the project to the CPU. The TIA Portal online view should show the device as OK in the diagnostics view.
- Open the watch table PN_IO_Watch and add tags
"DB_PN_Buffer".buffer[0..55],"DB_PN_Buffer".parsed.EncPosition, and"DB_PN_Buffer".parsed.StatusWord. - Toggle a known input on the PROFINET device (e.g., force a value in the device's web server or commissioning tool) and confirm that both the raw byte and the parsed UDT field update on the same OB1 cycle.
- Force the controller to STOP and back to RUN. Confirm that
GETIOcontinues to refresh RECORD without an error code in STATUS. - Disconnect and reconnect the PROFINET cable to trigger a station failure and recovery. Verify that STATUS returns 0x0000 on the next successful
GETIOcall.
Troubleshooting Matrix
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Compiler error "Address %IB504 overlaps tag | Two tags assigned to the same byte | Delete the redundant tag or shift the UDT start address |
| GETIO returns STATUS = 0x80A1 | Invalid LADDR (station not configured or wrong base) | Verify the device's logical base address in the device view |
| GETIO returns STATUS = 0x80B1 | LEN longer than configured submodule | Reduce LEN or check the GSD-defined submodule length |
| GETIO returns STATUS = 0x80C3 | Resource bottleneck (busy) | Wait for DONE before next call; do not retrigger in the same OB1 |
| Slice value flickers between 0 and a constant | Process image is updating mid-cycle; the slice read is not atomic | Switch to GETIO for a consistent snapshot, or move the slice access into OB1 with a snapshot DB |
| UDT fields show wrong endianness | Manual assumed big-endian, S7 stores little-endian | Swap byte order in the DINT conversion, or use SHL/SHR with the documented order |
| Hardware fault LED on the device after wiring change | Submodule mismatch (slot has a different module than the GSD defines) | Re-scan the topology in the device view; confirm the configured order matches the physical order |
DINT is not atomic across the array. The PROFINET update is performed once per PROFINET cycle (typically 1 ms), but the application may read partial updates. For deterministic behavior, always use GETIO into a buffer DB and then parse the buffer.Frequently Asked Questions
Can I read a single byte directly from a PROFINET IO submodule without copying the whole input range?
Yes. Declare a BYTE tag (or a DWORD tag with %Bx slices) and assign its address to the byte you need, e.g., %IB504 for the 5th byte of a 56-byte input area starting at IB500. The I/O address is mapped to a process image that updates once per PROFINET cycle; no extra copy is required.
Why does my UDT declared at %IB500 not show the right field offsets?
The most common cause is that the UDT was created with the wrong field order or that the start address was assigned before the UDT type was set. TIA Portal maps STRUCT members relative to the tag's start address, so an offset of 1 inside the UDT places the second field at %IB501. Verify offsets against the device manual and recompile the project.
What is the difference between DPRD_DAT and GETIO for PROFINET?
DPRD_DAT reads a contiguous I/O area defined by LADDR and LEN but does not guarantee consistency across the entire area; each word can be updated independently. GETIO reads the entire submodule in a single PROFINET cycle and writes a consistent snapshot into the destination RECORD, which is the recommended method for data larger than 4 bytes.
Do I need a PROFINET telegram to use GETIO or a UDT cast?
No. A telegram is only required for the symbolic Siemens UDT mapping on drives and similar devices. GETIO works on any PROFINET IO submodule regardless of telegram, and a UDT cast on %IB..%Ixx works for any device whose input area is mapped into the process image.
What firmware is required for the %Bx slice notation on a DWORD?
S7-1200 requires firmware V4.0 or later, and S7-1500 requires firmware V2.0 or later. Earlier firmware does not support the %B0..%B3 slice access on a DWORD tag, and the symbol will be flagged in the compiler as an unknown attribute.