FREE GUIDE

Reduce Logic & Save Coding Time: 4 Hacks to Level Up Your Arrays in TIA Portal

plc structure tia portal Aug 28, 2023

 

Are you a PLC programmer, already familiar with the basics of using arrays in TIA Portal but not sure what's really possible? 
Are you interested in minimizing redundant logic and saving program development time?

 

Then keep on reading my friend.  In this blog post, I will share with you 4 things you might not know - FOUR application hacks - that are going to make your programming life easier when working with arrays in TIA Portal.  So if you're already experienced using arrays in PLC applications, or you're just about to start and not sure what is possible, then this post is for you. 

And you want to keep reading all the way to the end, because I'll be sharing a free downloadable sample application with all array examples used in this post.

 

Why use arrays?

Arrays are widely used by PLC programmers in various automation applications due to the advantages they offer. Here are 3 key advantages why you should consider using arrays in your PLC programming:

 

  • EFFICIENT DATA STORAGE
    Arrays allow you to store multiple values of the same data type in a single data structure. This way of data storage helps save memory space, which can be crucial in resource-constrained PLC systems.
  • SIMPLIFIED LOGIC
    When dealing with multiple instances of similar data, such as shift registers or temperature logs, arrays simplify the programming logic. You can program loops in your PLC application to iterate through array elements, reducing the need for redundant logic.
  • MODULAR PROGRAMMING
    Arrays encourage modular programming practices. You can create functions or function blocks that operate on arrays, enabling code reuse and easier troubleshooting.

 

Arrays are particularly useful when dealing with repetitive tasks involving similar data types and structures and that's why I love to use them in my own PLC projects.

 

In today's post, you will learn:

  • How to store structured data efficiently in registers using arrays
  • How to improve reusability of code by creating functions that can handle arrays of any size and type
  • How to simplify PLC logic by accessing array elements indirectly (vs. explicitly)
  • How to save programming time by declaring multi-instance arrays

 

Arrays are commonly used in PLC programming across various industries nowadays - learning more about them could turn out to be a valuable investment of your time. So let's begin because there's a lot to cover!

 


HACK 1 - STORE STRUCTURED DATA EFFICIENTLY USING ARRAYS


Many of you likely know about arrays created from basic data types, like an array of Booleans, Integers, Reals, etc..

 

Example of arrays built from simple data types.

 

But did you also know that you can construct arrays using user-defined data types (UDTs)?  Let's have a look at the example below.

 

Example of a user-defined data type.

 


In this example, I've created a user-defined data type named "UDT_Shiftreg_Data" designed to encapsulate data - a collection of variables - for a single position within a shift register.  In my field, I frequently deal with intricate shift registers, often more complex than the one shown here. Please remember that the provided example is intentionally simplified for educational purposes.

In this data type, I've included a 'Product_ID' string variable, a 'Batch_Number' integer, and two status bits: 'Product_Ok' and 'Product_Reject'. The concept behind the status bits is straightforward: when a product undergoes proper processing, the 'Product_Ok' bit is activated by the application. Conversely, if a product encounters processing issues, the 'Product_Reject' bit is activated.

Now in the next step, we use this UDT to build an array.  As you might know from the teachings in my PLC and HMI courses, global data structures which are used in different parts of a PLC application, should always be created in global data blocks (DB).  So for this purpose, I have created a DB called "Shiftreg - Data", where I have declared 3 shift register arrays - 'Process_A', 'Process_B', and 'Process_C'.  Each of these process arrays is defined as Array[start..end] of the "UDT_Shiftreg_Data" data type.

 

Example of arrays using user-defined data types.

 

For each process, every collection of data values (ID, batch number, process bits) is efficiently stored at a specific position of that array register.  Every position in the register contains the exact same variables and in the same order.  If more variables were to be added to the data type "UDT_Shiftreg_Data", the array would be automatically updated as well (NOTE - requires a software compilation of the application). If you need more data positions, simply increase the last element in the array declaration.

Now there's a couple of interesting observations I'd like to share with you about the above example.  Firstly, an array in TIA Portal does not necessarily need to start with element "0".  Unlike other platforms - like Rockwell for example - where an array can only start with element "0", TIA Portal gives you more control by letting you choose your starting element.

In the example above, Process_A is starting with array element "0", Process_B starts with element "1" and Process_C begins with array element "100".

 

TIP - I'd recommend declaring your first array element as "0", unless you have a good reason not to.  If you have e.g. an array of months, then it would make more sense to use 'Array[1..12] of INT' instead of 'Array[0..11] of INT' because '1 to 12 months' makes more sense then '0 to 11 months'.

 

Another thing I'd like to mention here, is the option to enable the Retain property for each array.  In the example above, the Retain property for each shift register array is turned on.  When dealing with shift register arrays and a bustling production line, preserving shift register data through a PLC power cycle (e.g. due to a power outage in the factory) becomes crucial. If shift register data is missing after a power cycle, the customer most likely has to scrap all products currently in the production line, leading to a lot of wasted time and a big pile of product waste.  My advice?  Give the retain property of arrays some serious thought, and when in doubt - enable this option - it's your safest bet.

 

Now let's have a look at a couple of clever ways to pass arrays through to function blocks.

 


HACK 2 - REUSE & REDUCE CODE BY CREATING FUNCTIONS THAT CAN HANDLE ARRAYS OF ANY SIZE AND TYPE


In this section we're going to have a look at how we can optimally pass arrays through to functions (FCs) and function blocks (FBs), while at the same time reducing the amount of logic needed.

First off, if you have large data structures - e.g. arrays, UDT structures, large strings - that you want to pass on to your function, you should always use the InOut of the block.  The reason is that passing variables through the InOut of the block interface does not require any extra resources from the PLC.  The data is just passed through (this is called 'Call-by-Reference').  If you would use the Inputs or Outputs area of the block interface to get data into our out of your function (='Call-by-Value'), the data is first copied to the internal memory (instance DB) of the function block.  This requires extra resources from the PLC.

 

TIP - To avoid using unnecessary resources, always connect large data structures to the InOut area of your function or function block.

 

Now let's take the following example.  Let's say we want to build a function block "FB - Array Info" that can do some basic operations on an array.  In order to keep code redundancy to a minimum, ideally we would build just 1 function block that can handle different array sizes.  Are you following me here?

In example 1 below, I have declared an InOut array with a pre-defined start(0) and end(50) element and a pre-defined data type "UDT_Shiftreg_Data".

 

Example 1 - Declaring a fixed array limit with a pre-defined data type.

 

The advantage of this approach is that it is immediately clear what data is expected here.  This function block will only accept arrays with size 0 to 50 of the data type "UDT_Shiftreg_Data", so it will only accept the Process_A array.  Unfortunately, this is also its weakness - ONLY arrays that fit this exact description can be attached to the InOut of this block.  It is not possible to use Process_B (array size 1 to 150) or Process_C (array size 100 to 299) with this function block.  You would have to build a second function block for Process_B, and a third one for Process_C.  Since all 3 function blocks would contain identical code (apart from the InOut declaration of the array), this method of explicitly declaring array limits can lead to redundant code and ineffective programming.

 

Now let's take a look at example 2 below. 

 

 Example 2 - Declaring a variable array limit with a pre-defined data type.

 
In this second example, we have declared the InOut array with variable limits (= undefined start and end elements - indicated with Array[*]) and a pre-defined data type "UDT_Shiftreg_Data". Using this approach, ANY array size of the data type "UDT_Shiftreg_Data" can be passed on to this FB.  As long as the data type matches, the array can be used inside the function block.  With this type of declaration, we only need 1 function block for Process_A, Process_B, and Process_C resulting in no redundant code and a streamlined application

 

Now there's a couple of very interesting instructions you can use with arrays of variable limits inside an FB.

  • LOWER_BOUND - This instruction outputs the low limit (=first array element) of the array
  • UPPER_BOUND - This instruction outputs the high limit (=last array element) of the array

 

The screenshot below illustrates how both instructions are used for Process_B (Array[1..150] of "UDT_Shiftreg_Data").

 

Reading out the first / last element of an array using the LOWER_BOUND and UPPER_BOUND instructions.

 

The function block in the example above calculates as well the total number of elements in the array using the first and the last element.

Keep in mind that the LOWER_BOUND and UPPER_BOUND instructions only work with Array[*] type of declarations.  These instructions are available for S7-1200 and S7-1500 type controllers.

 

So how can we take this one step further?  The examples above showcased how you can pass variable size arrays through to the same function block.  But there is still one caveat here - the data type needs to be correct.  In our example, only arrays of data type "UDT_Shiftreg_Data" can be used.  What if we want to build a function block that can process arrays of ANY size and ANY data type? Is there a solution for this..?

Yes there is.  Take a look at the example below.  Here I have created a function block named "FB - Array Info Any" and I have declared an 'iq_stArray_Any' variable using data type VARIANT.

 

Using arrays of unknown size and data type.

 

The data type VARIANT allows you to assign an array of any size and type to the InOut of the block.  In the example below, I connected Array[0..50] of data type "UDT_Shiftreg_Data" to the InOut of the first block call and Array[5..2800] of data type INT to the InOut of the second block call.

 

Assigning variable size arrays with different data types to two calls of the same block.

 

The disadvantage of using the VARIANT declaration is that - inside the function block - the array structure is missing.  You get a VARIANT data type, and with this data type you can for example not access individual elements of the array.  There are only limited operations you can do on arrays using this method.

 

So now you're asking: "Why should I implement this method at all if I can not manipulate arrays inside my FBs?"

 

There is one specific application of this method that I use a lot in my PLC programming, and I think you could benefit from it as well.  It is reading out the total number of elements for an array of any size or type.  This is possible using the 'CountOfElements' instruction.

 

Determining the number of array elements for a random array size and type.

 

TIP - I recommend using the 'IS_ARRAY' instruction to check if the VARIANT tag points to a tag of the ARRAY data type.  If this is TRUE -> Enable the use of the 'CountOfElements' instruction. 

 

The 'CountOfElements' instruction uses a VARIANT input to determine how many ARRAY elements a tag - to which the VARIANT points - has.  The result of the query is returned via the RET_VAL output.

The example below showcase the use of this function to determine the total number of elements of Array[1..199] of data type 'IEC_TIMER'.  The result of the query is '199'.

 

A multi-use function block that outputs the total number of elements for an array of any size or type.

 

So if you're a PLC programmer who frequently use arrays in your applications, I would recommend building 1 function block that outputs the total number of elements for any array size or type.  I typically check for array size, number of elements in the beginning of my array logic or sequence.  Then afterwards, I use the result for indexing arrays and looping operations - and this take us to the next point. 

 


HACK 3 - SIMPLIFY YOUR LOGIC BY ADDRESSING ARRAYS INDIRECTLY


As mentioned before, an array consists of a number of elements.  These elements are defined in size by a low and high limit declaration.  E.g. in the screenshot below, the 'Process_A' variable is declared as an Array[0..50] which means the array has a low limit of '0' (=the first element) and a high limit of '50' (=the last element).

 

Example of an array with low limit '0' and high limit '50'.

 

Each element of this array can be accessed EXPLICITLY by entering the element number between the brackets [ ] of the array . Below is an example of transferring the batch number of array element '10' to a local variable using a MOVE instruction.

 

Reading out the value of an array element using explicit addressing.

 

Now there's a second way of reading out that same batch number, and that's by using INDIRECT addressing.  The idea here is that you use an index tag (typically INT or DINT) instead of an explicit number to access an element of the array.  Important here is that the the index tag value has to be within the low and high limits of your array (check out the previous point on how to read out those limits).

The example below showcases this method of indirectly addressing array elements.  First, the value '10' is transferred to the index tag.  Afterwards, the batch number corresponding to element position '10' (=value of the index tag) is transferred to a local batch number tag.

 

Reading out the value of an array element using indirect addressing.

 

This method of indirectly addressing elements of an array is especially powerful when used together with program loops.  Loops are a very efficient programming tool when performing repetitive task resulting in fewer lines of code.  This is especially true for loops using arrays, because logic and instructions can easily iterate through all elements of the array using an index tag.

Let's see how this works by having a look again at one of the previous examples used in this post (picture below).  Here we used the "FB - Array Info Shiftreg" block to read out the first element, the last element, and the total number of elements of the 'Process_A' array.

 

  

Reading out the array information for 'Process_A'.

 

Now that we know the first and last element of the array, we can set up a loop that iterates through each array element, starting with the first element, and ending with the last element.

The logic in the example below is used to count the number of rejected products in the 'Process_A' array.
 
In the first network (Network 2), the loop variables are initialized.

  • the index (diIndex) is declared (=first position for the loop)
  • the last position (diLast) for the loop is declared
  • the count (diCount) is set to zero

In the second network (Network 3), the loop is executed.  Every element of the array is being processed, and whenever a product reject has been found (xProduct_Reject = TRUE) -> the counter is updated.

 

A function block that returns the number of array elements for any type of array.

 

As long as the index (diIndex) is less than the last element of the array (diLast), we increase the index by '1' and return to the beginning of the loop.
As soon as the index is greater or equal than the last element of the array, the program will jump out of the loop and continue with the next network.

In older PLC generations, loops were rather unpopular due to the heavy load on the CPU of the PLC.  Newer generations of PLC processers however are so powerful, that loops are used more frequently in PLC applications.  The big advantage of loops together with arrays are that you get a highly compacted block of logic with no redundant code.

I've almost reached the end of this post, but before finishing up, I'd like to share with you one more array skill that can save you programming time.  

 


HACK 4 - SAVE TIME BY DECLARING MULTI-INSTANCE ARRAYS


Using this final array skill in TIA Portal, you can save time by reducing the number of multi-instance declarations.  In the example below, the same function block "FB - Array Info Any" is being called 3 times.  For this purpose, I have declared 3 separate multi-instances in the Static area of the block interface:

  • instance_Array_Info_Example_1
  • instance_Array_Info_Example_2
  • instance_Array_Info_Example_3

 

Calling an FB 3 times using 3 independently declared multi-instances.

 

So how can we simplify this using arrays?  TIA Portal gives you the possibility to declare an array of multi-instances. The advantage is that you'll only need 1 declaration, instead of a single declaration for each multi-instance.

This solution is shown in the example below, where I have created Array[1..3] of the instance "FB - Array Info Any".

 

Calling an FB 3 times using a single declared multi-instance array.

 

Honestly, I haven't personally used this feature a lot, but I believe it could significantly save time when you need to call the same function block multiple times within a parent function block.

 


So that's it for this post on arrays!

I hope you got a better understanding of the possibilities of using arrays in TIA Portal.  By adding these 4 array hacks to your PLC programming, you can save time, reduce logic, and store data more efficient.

Now as promised, here is a link to the array application used for all examples in this post:

DOWNLOAD THE ARRAY APPLICATION 
(you'll need to remove the .zip from the filename first before you can retrieve it in TIA Portal)

 

Thanks for reading, I hope you enjoyed it!
-Hans

FREE GUIDE

Grab my '5 Simple Steps to Drastically Improve your PLC Program Structure in TIA Portal' and start building better PLC applications TODAY.

Where should I send you the guide?

When you signup, we'll be sending you further emails with additional free content. You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.