Overview
Many third-party fieldbus gateways (HMS Anybus, Prosoft, etc.) expose a fixed-length process-data area - commonly 256 bytes per direction - to a Siemens S7-1200 or S7-1500 controller. The SCL program must then pick, decode, and convert the bytes that carry payload (e.g. ASCII status, packed DINT, vendor-specific protocol fields) into usable PLC tags.
This reference covers three production-grade methods to bring those 256 bytes into an SCL ARRAY[*] OF BYTE (or CHAR) and to convert selected slices to STRING and DINT values:
- POKE_BLK - runtime copy from the I/O process image into a non-optimized DB.
- UDT mapping - symbolic, direct access to the gateway I/O area (preferred for native Siemens and HMS PROFINET/Slave gateways).
-
AT overlay - on optimized blocks (the S7-1500 default), overlay the bytes with a
STRUCTorARRAYat the same memory location.
STRING length in S7-1200/S7-1500 (254 payload + 2-byte header = 256 bytes total). If a single value may exceed 254 characters, it cannot be held in a single STRING and must be split across multiple strings or moved to a WSTRING / ARRAY OF WCHAR. See the Siemens S7-1200 character and string data types reference.Prerequisites
| Item | Requirement |
|---|---|
| Controller | SIMATIC S7-1200 (FW 4.2+) or S7-1500 (FW 1.8+ recommended, 2.9+ for ARRAY[*] bounds) |
| Engineering | TIA Portal V15.1 or later (V17 / V18 recommended for current firmware) |
| Gateway | HMS Anybus X-gateway / Communicator or compatible PROFINET / PROFIBUS slave exposing 256 bytes of input process data |
| GSD file | Installed in TIA Portal (Options → Manage General Station Description Files) |
| Block knowledge | SCL syntax, optimized vs. non-optimized block access, AT construct |
| License | STEP 7 Professional (required to edit SCL sources) |
Gateway Data Layout and I/O Addresses
After the GSD is installed, the gateway appears under Devices & Networks as a PROFINET IO device. Its input slot defines the byte length; for a 256-byte gateway you will see one slot of type Input with length 256. The I/O addresses assigned by TIA look like %IB256 … %IB511 for inputs and %QB256 … %QB511 for outputs (offset depends on the slot configuration; verify in the device properties, IO tags tab).
Two important points noted in field use:
- Third-party gateways do not expose the slot's I/O tags as symbolic names inside the Properties → IO tags page. Selecting the gateway in the project tree and opening the Details view (or the Device view → Inspector window → IO tags) shows the address range and the start offset. Use the start offset and length to build a UDT that exactly covers 256 bytes.
- Always confirm with the gateway's user manual (e.g. HMS Anybus X-gateway installation guide) which sub-range is cyclic process data vs. acyclic parameter data. Only cyclic data is updated every bus cycle and is safe to read into an ARRAY for fast SCL processing.
Method 1 - POKE_BLK into a Non-Optimized DB
POKE_BLK is the classic approach when the user mandates a single SCL block that ingests the entire 256-byte input area and then extracts fields. It requires a non-optimized data block (a hard requirement - the function writes by absolute byte offset).
Step 1 - Create a Non-Optimized DB
- Add new Data Block → name
DB_Gateway_Buffer. - Right-click the DB → Properties → Attributes → clear the check Optimized block access.
- Declare a single tag:
Buffer : ARRAY[0..255] OF BYTE;(starts at offset 0.0). - Compile and note the absolute start address from the DB properties (e.g.
DB200.DBX0.0).
Step 2 - Call POKE_BLK in SCL
// FB "FbGatewayDecode" - called in OB1 / cyclic task
VAR
arrInputBuffer : ARRAY[0..255] OF BYTE; // staging area, OPTIONAL
sStatus : STRING; // 254-char max
diValue : DINT;
END_VAR
// 1. Copy 256 bytes from process image (%IB256) into non-optimized DB
IF NOT POKE_BLK(
area_src := 16#81, // 0x81 = inputs (process image)
db_src := 0, // 0 for PII, DB number for DB
byte_src := 256, // start byte in PI
area_dst := 16#84, // 0x84 = data block
db_dst := 200, // DB200 = DB_Gateway_Buffer
byte_dst := 0, // offset 0.0 in DB200
count := 256) THEN
// POKE_BLK returns BOOL - handle error
"dbAlarm".bPokeError := TRUE;
RETURN;
END_IF;
// 2. Slice the bytes (e.g. status at offset 8, length 32)
// Direct symbolic access is preferred on the non-optimized DB
sStatus := '';
FOR #i := 0 TO 31 DO
sStatus[1 + #i] := CHAR_TO_STRING("DB_Gateway_Buffer".Buffer[8 + #i])[1];
END_FOR;
// (Production code should use the IEC STRNG libraries instead of byte-by-byte copy)
// 3. Convert 4 ASCII digits at offset 16 to DINT
diValue := STRING_TO_DINT(
CHAR_TO_STRING("DB_Gateway_Buffer".Buffer[16]) +
CHAR_TO_STRING("DB_Gateway_Buffer".Buffer[17]) +
CHAR_TO_STRING("DB_Gateway_Buffer".Buffer[18]) +
CHAR_TO_STRING("DB_Gateway_Buffer".Buffer[19]));
Method 2 - Direct UDT Mapping (Recommended)
Symbolic I/O access is the cleanest approach and is what Siemens documentation recommends.
Step 1 - Build a 256-byte UDT
// UDT "UDT_Gateway_256"
TYPE UDT_Gateway_256 :
STRUCT
abData : ARRAY[0..255] OF BYTE; // 256 bytes total
END_STRUCT;
END_TYPE
For a structured payload (e.g. one ASCII status + one packed DINT + one float), declare the UDT with named fields so the SCL can address them symbolically. Example for an HMS Anybus status payload of length 256:
TYPE UDT_Gateway_Status :
STRUCT
abHeader : ARRAY[0..7] OF BYTE; // protocol header
sNodeId : STRING[32]; // offset 8, 34 bytes total
diCycleTime : DINT; // offset 42, 4 bytes
rVoltage : REAL; // offset 46, 4 bytes
rCurrent : REAL; // offset 50, 4 bytes
abTail : ARRAY[54..255] OF BYTE; // vendor-specific
END_STRUCT;
END_TYPE
STRING[32] occupies 34 bytes (2-byte header + 32 data), not 32. Likewise DINT/REAL require 4-byte alignment on non-optimized blocks. On optimized blocks the compiler reorders fields for word alignment automatically. Always verify the byte layout against the gateway user manual and use a PLC tag table watch window to confirm offsets before scaling up.Step 2 - Map the UDT to the IO Address
- Open PLC tags → Default tag table and add a new tag:
ibGatewayInwith data typeUDT_Gateway_256and address%IB256(start of the 256-byte input slot). - For the output direction, add
qbGatewayOutwith the same data type at%QB256. - Compile the hardware configuration and download to the CPU.
Step 3 - Use the UDT in SCL
// Symbolic, type-safe access - no POKE_BLK needed
VAR
stGw : "UDT_Gateway_256"; // instance for code-side work
END_VAR
#stGw := "ibGatewayIn"; // snapshot inputs (single MOV)
// Now read fields symbolically
IF #stGw.sNodeId = 'NODE-007' THEN
"dbCtl".diLastCycle_ms := #stGw.diCycleTime;
END_IF;
Method 3 - AT Overlay for Optimized Blocks
When the surrounding code must live in an optimized FB (the S7-1500 default) and you only have a byte index to work with, use the AT construct to re-interpret the input area as an array or a struct without copying memory.
FUNCTION_BLOCK "FbDecode256"
VAR
// 256 raw bytes from process image
abRaw : ARRAY[0..255] OF BYTE AT %IB256;
// Overlay the SAME memory as a struct
stPayload : "UDT_Gateway_Status" AT %IB256;
END_VAR
BEGIN
// Read fields directly - no copy, no POKE_BLK
"dbCtl".diCycle_ms := #stPayload.diCycleTime;
"dbCtl".rVoltage := #stPayload.rVoltage;
END_FUNCTION_BLOCK
Key rules for AT overlays:
- Both variables must reference the same absolute address or the same identifier.
- Lengths must match exactly - a 256-byte UDT cannot overlay a 200-byte ARRAY.
- On optimized FBs the Address column in the block interface must be visible (right-click header → show) and the AT must be on a
VAR_TEMP,VAR, orVAR_IN_OUTof the same block - not across blocks.
Selecting the Destination Area: TEMP vs Static vs Non-Optimized DB
Field experience strongly prefers non-optimized static or UDT-mapped symbolic I/O over VAR_TEMP for the 256-byte staging area. Reasons:
| Location | Advantages | Disadvantages |
|---|---|---|
| VAR_TEMP | Local, no symbol table pollution, no retentivity concerns | Re-initialized every block call - cannot be inspected live; cannot be bound to HMI; POKE_BLK cannot write here |
| Non-optimized DB (static) | POKE_BLK can write by absolute byte offset; HMI-visible; survives scan | Disables optimized access; no symbolic read in newer TIA wizards |
| Optimized DB (static) | Symbolic, type-safe, no offset management | No POKE_BLK; field-by-field must match vendor protocol |
| Direct AT on process image | Zero-copy, fastest, no extra DB | Address is fixed at compile time; cannot be remapped without FB re-compile |
For a single 256-byte input that is consumed by one FB and not remapped at runtime, the direct AT method (Method 3) is the leanest. If the buffer must persist across cycles, copy it into a static VAR or into the same UDT in a DB with Optimized block access enabled.
Converting the Bytes to STRING and DINT
ASCII substring to STRING
The TIA standard library ships CHARS_TO_STRING (IEC) and the legacy STRNG family. A clean, bounds-checked copy of 32 bytes from offset 8 into a STRING[32] looks like:
// Copy offset 8, length 32 from gateway to sStatus (STRING[32])
"LIB_String".Chars_To_Strg_26(
String := #sStatus,
Chars := #stPayload.sNodeId, // symbolic - already a STRING
pChars := 0,
Count := 32);
When the source is still raw bytes (no UDT field), the IEC-standard idiom is to CHAR-cast each byte and concatenate. Avoid per-byte STRING assignment in long loops - it produces quadratic string copies. Use the Chars_To_Strg FB (Blocks library → String + Char) for any payload over 16 bytes.
ASCII digits to DINT
For a 4-byte ASCII representation (e.g. '0042') use the IEC STRING_TO_DINT conversion. The function returns 0 on invalid input - guard the result:
IF #sStatus = '' THEN
#diValue := 0;
ELSE
#diValue := STRING_TO_DINT(#sStatus);
IF ENO = FALSE THEN
#diValue := 0;
"dbAlarm".bConvertError := TRUE;
END_IF;
END_IF;
Raw 4-byte big-endian to DINT
Many gateways return numeric values as big-endian bytes, which is the opposite of S7 native little-endian. Swap bytes before assigning:
// Convert 4 big-endian bytes at offset 16 to DINT
#diValue := SysByteSwap_BEtoLE_DINT(
BYTE_TO_DWORD(#abRaw[16]) * 16#1000000 + // byte 0 << 24
BYTE_TO_DWORD(#abRaw[17]) * 16#10000 + // byte 1 << 16
BYTE_TO_DWORD(#abRaw[18]) * 16#100 + // byte 2 << 8
BYTE_TO_DWORD(#abRaw[19])); // byte 3 << 0
Testing the 256-Byte Input in TIA Portal
Hardware-in-the-loop testing with the real gateway present is best, but it is rarely convenient during software bring-up. Use one of the three approaches below.
Approach A - PLCSIM (S7-1500 only)
- Start PLCSIM (V16+ supports the S7-1500 process image and PROFINET IO simulation).
- Add a virtual PROFINET device and configure a 256-byte input slot.
- Drive the input bytes with a watch table forced on a small simulator block.
Approach B - Watch Table with Force
- Open Watch and force tables → create a new table.
- Add
"ibGatewayIn".abData[0]…"ibGatewayIn".abData[255](or use the Modify / Force pattern with a 256-elementARRAY). - Select all 256 rows, set Modify value to a known test pattern (e.g.
16#01,16#02, … cycled), apply, and observe the UDT fields.
Approach C - Force M-Flag Mirror for Early Code Bring-up
To validate the SCL logic before the gateway is on the network, mirror the 256 bytes into a M-flag area and have the SCL read from the mirror. Once the gateway is on-line, switch the pointer back to the real input area. This is the most reliable way to unit-test the decode path.
// Pseudo-code for bring-up test switch
IF "dbCfg".bUseTestMirror THEN
#stPayload AT %MB1024 : "UDT_Gateway_Status";
ELSE
#stPayload AT %IB256 : "UDT_Gateway_Status";
END_IF;
Verification Checklist
- CPU goes to RUN; PROFINET diagnostics shows the gateway as Connected, No faults (or PROFIBUS slave in data exchange).
- Watch table shows
ibGatewayIn.abData[0..255]updating at the bus cycle rate. - All 256 bytes are accounted for in the UDT (no overlap, no gaps). Use Cross-reference to find stray absolute I/O accesses.
- STRING slices contain the expected ASCII (no leading NULs, no truncation past the field's max length).
- Numeric conversions produce the expected values; sweep test values from 0 to max.
- Cycle-time impact: the SCL block must complete well inside the OB1 / cyclic task budget (typical budget < 5 ms for a 256-byte decode on an S7-1516).
- Retentivity: if the buffer lives in a DB, decide whether the data must be retentive; set the Retain property accordingly.
Troubleshooting Matrix
| Symptom | Likely Root Cause | Fix |
|---|---|---|
| SF / BF LED on, gateway not in data exchange | Wrong GSD, wrong device number, cable/port issue | Re-install GSD; verify device name & IP match the project; check PROFINET diagnostics buffer |
| Inputs always 0 | Watch table is on symbolic tag but actual I/O is at a different start byte | Confirm slot start offset in device properties; align the UDT base address |
| POKE_BLK returns FALSE / BOOL stays FALSE | Destination is in an optimized DB, or count > available bytes | Disable optimized access on target DB; verify count and the source/dest byte ranges |
| Garbled characters in STRING | Length field of the STRING is wrong, or the source is binary not ASCII | Re-initialize the STRING header (use the String standard library FBs); verify source encoding with watch table |
| Numeric value is byte-swapped | Endianness mismatch between gateway and S7 | Apply byte swap (see snippet above); confirm in gateway manual |
| Cannot see IO tags in gateway property window | Third-party gateway does not export symbolic tag info | Open the device's Details view for the address range; build a UDT manually |
| Compile error: AT requires same identifier | AT and target variable have different addresses | Point both AT declarations at the same absolute address or use the same source identifier |
| STRING is truncated to 1 character | String was declared without a max length and defaults were not set | Declare as STRING[254] for the largest expected payload, or use WSTRING for Unicode |
FAQ
Can POKE_BLK write into an optimized DB on S7-1500?
No. POKE_BLK writes by absolute byte offset and is rejected by the optimized-block memory protection. Use a non-optimized DB or, preferably, switch to symbolic UDT access or an AT overlay.
Why don't I see I/O tags for my HMS Anybus gateway in TIA Portal?
Third-party gateways typically do not publish a symbolic tag list in the GSD. Open the gateway in the project tree and check the Details view (or the Inspector window → IO tags) to see the address range. Build a matching 256-byte UDT and bind it to that start address.
What is the maximum STRING length on S7-1200/S7-1500?
A STRING can hold 254 user characters plus a 2-byte header (max length + current length), for a total of 256 bytes. For longer ASCII or Unicode payloads, use a WSTRING, an ARRAY OF CHAR, or split the data across multiple strings.
Should the 256-byte buffer be in VAR_TEMP or in a DB?
Avoid VAR_TEMP for a 256-byte buffer that must be inspected from a watch table or HMI. Put the buffer in a static DB (optimized or non-optimized) or - best - map the gateway's process image directly via UDT / AT and read fields symbolically.
How do I test the decode logic before the gateway is on the network?
Mirror the 256 bytes into an M-flag area, point the SCL at the mirror using an IF / ELSE on a test-mode flag, and force the mirror with a watch table. Once the gateway is live, switch the pointer to the real %IB area and verify with on-line diagnostics.