Disciplined I/O Mapping in PLC Programs

Lack of PLC tag organization eats time. This engineering brief demonstrates best practices using a Siemens PLC and ET 200SP distributed I/O (the structure applies to any PLC). We will use UDT, DB, and mapping so that I/O is encapsulated and then access using convenient dot notation e.g., #IO_Remote.In.xSW1.


This article is part of the DigiKey Field Guide for Industrial Automation

Location: Program It → Data Structures (UDT/DB)
Difficulty: :classical_building: System Integrator — difficulty levels explained
Author: Aaron Dahlen | MSEE | Senior Applications Engineer, DigiKey
Last update: 06 Mar 2026


Figure 1: An S7-1200 (Gen 2) and an ET 200SP both installed on industrial breadboards.

What’s wrong with the conventional way of handling I/O?

Plenty!

And I’ll answer with a simple question.

Do you know what tags are being used for each block of PLC code?

If we use the simplified textbook approach, we have no idea what the block is connected to. We have failed to use formal parameters to pass data into and out of a block. Instead, we find sideways smuggling of tags (implicit global dependencies). For example, we pull tags such as xSW1 and xMotor from the left-hand tree and drop them into the program. The computer science professor would be mortified by this indiscriminate use of global tags.

Global variables appear harmless. However, they create bad habits and the technique doesn’t scale. In fact, we will spend more time bouncing between screens and chasing tag-based bugs than we do actually programming.

Bottom line: Unstructured programming isn’t just messy, it costs downtime. Think of your last program where you chased signals all over the POUs. With the DB-centric approach every input and output is visible. No more guessing what went into or came out of a block.

Encapsulation

The solution is to follow the Siemens Programming Styleguide guidelines. Specifically, rule DA005 which addresses formal parameters:

Data exchange within the PLC with FBs or FCs is always carried out via the block interface (input, output or in/out parameters).

Demonstration Project

The hardware shown in Figure 1 is used to showcase the encapsulation technique. This is an intermediate PLC project featuring a Siemens CPU 1214C and a collection of ET 200SP distributed IO components as described here.

Overview

The encapsulation is completed in multiple steps:

  • Symbolically name the I/O from within a UDT

  • Instantiate the UDT within a DB

  • Generic naming of screw-terminal I/O in a PLC tag table

  • Mapping the generic I/O names to the symbolic names within a DB

  • Passing the DB (by reference) to each block

Symbolic Names Within a UDT

The symbolic names are contained within a UDT. Figure 2 presents an example for the ET 200SP. Note that this is a hierarchical system with structures for input and output. This is important for the dotted notation e.g., #IO_Remote.In.xSW1.

Figure 2: Symbolic names within a UDT.

Instantiation of the UDT Members Within a DB

The UDT from the previous step describes the container. It is not a container itself. Consequently, we cannot directly assign screw-terminal hardware to the UDT blueprint. However, we can instantiate the container within a DB as shown in Figure 3.

Note that this is a structure of structures. The top level IO_Remote tag is the address that will be passed to the various blocks. We will pass this pointer (by reference) to the InOut interface of each block.

Figure 3: Instantiation of UDT members within a DB

Generic Naming of Screw-Terminal I/O

Part of the PLC tag table for the ET 200SP modules are included as Figure 4. In this example, generic names have been given to each tag to reflect the physical address. The first line shows that physical %I2.0 has a symbolic reference of I2_0.

Figure 4: PLC tag table for the ET 200.

Mapping Symbolic Names to Hardware

In this step we map the DB to the physical hardware.

The mapping is conducted once every program scan. That’s one block and one scan inside Main [OB1]. Everything else uses symbolic names through the DB.

Figure 5 is arguably the cornerstone of this document. It shows how the hierarchical DB tags are linked to the program’s InOut interface.

Listing 1 shows how the tags are used by the actual program. In this example each input tag is updated and each output tag is sent to the associated hardware. The resulting dot notation provides a clean self documenting description of each Boolean tag.

Figure 5: Interface for the mapping program (emphasis InOut).

#Remote.In.xSW1 := "I2_0";
#Remote.In.xPBGreenPress := "I2_1";
#Remote.In.xPBRedPress := "I2_2";
#Remote.In.xCR1Closed := "I2_3";
#Remote.In.xCR2Closed := "I2_4";
#Remote.In.xCR3Closed := "I2_5";

"Q2_0" := #Remote.Out.xPLGreen;
"Q2_1" := #Remote.Out.xCR1;
"Q2_2" := #Remote.Out.xCR2;
"Q2_3" := #Remote.Out.xCR3;

Listing 1: Mapping of Symbolic names to the physical hardware.

Passing Tags to Blocks

The final step is illustrated in Figure 6 where we pass the hierarchical DB tag(s) to each program. In this example Main [OB1] has two blocks including the mapping and control blocks.

Every block that uses the I/O sees the same DB structure. No global variables and no surprises.

Note that the InOut interface for the control program is identical to the mapping interface described in Figure 5. In all cases we use the self-documenting dot notation tags.

Figure 6: View of Main [OB1]

Next Steps

  • Multi Instance: As a thought experiment, modify the code for a PLC controlling 10 identical ET 200SP. Explore how the IO data could be encapsulated in an array of structs with an index for each ET 200SP instance.

  • Test bench: Since the modules are clean (not directly connected to hardware) with clean interface boundaries, we can construct test benches to develop and harden the code. The Siemens scope tool may be used to examine the DB contents.

  • Library: Structured, tested, and hardened code fits naturally into a larger library.

  • Message Buffering: Explore a closely related technique for buffering messages as described in this article

How to Break the Structure

At this point we have a highly structured interface between program blocks. All tags are explicitly defined at the blocks interface.

Think of this as a DB-based contract for I/O binding. It’s a robust interface that requires programming discipline.

The danger is to revert back to our old ways and sideways smuggle global variables into the blocks.

The Siemens Styleguide has something to say about this:

Mixing both types of access in one block is not permitted.

Don’t do it!

Breaking the contract produces a system that is worse than an unstructured one. The clear local I/O contract reverts to a soup of tags while lying about being structured.

Sincerely,

Aaron

:books: Continue Exploring Industrial Control Systems

If this discussion was helpful, you may also want to explore:

:world_map: DigiKey Navigation

:japanese_symbol_for_beginner: Related Foundational Articles

About This Author

Aaron Dahlen, LCDR USCG (Ret.), is a Senior Applications Engineer at DigiKey in Thief River Falls. His background in electronics and industrial automation was shaped by a 27-year military career as both technician and engineer, followed by over a decade of teaching.

Dahlen holds an MSEE from Minnesota State University, Mankato. He has taught in an ABET-accredited electrical engineering program, served as coordinator of an electronic engineering technology program, and instructed military technicians in component-level repair.

Today, he has returned to his home in northern Minnesota, completing a decades-long journey that began with a search for capacitors. Read his story here.