Just like other programming languages, the IEC 61131-3 programming standard provides many different data types which include both elementary and complex ones. A data type defines how much memory capacity is needed by a variable value and by that, the largest and smallest value in the variable.
1 Elementary data types (INT, REAL, BOOL)
The following (examples) simple data types are standard in any PLC controller:
All variables must have a data type. If a variable is given a value outside the minimum and maximum value range of the data type, a run time error may occur and consequently the PLC may stop the program execution. This may again lead to strange behavior when executing the program (the program may seem unstable).
A few PLC types provide more data types than the ones listed above. In general, it is recommended use only a few data types so that the PLC code can be copied in an easier way to other PLC types. Some special data types such as S7TIME, LWORD and ULINT cannot be used by all PLC types. This means that copying PLC code with special datatypes, or upgrading to a larger PLC, may take a lot of work and risk introducing errors to the code.
The three most used data types are BOOL, INT and REAL. The reason why INT is used more often than WORD is that INT provides the same amount of data as the bit-size in a PLC making it a fast data type. On the other hand, if REAL is used, the PLC will auto-generate underlying machine code as the PLC can only work with integers. The disadvantage of working with INT is when exchanging values between computers where e.g. one computer is a PLC running on a 16-bit operating system, and the second is running on a 64-bit operating system. The second computer could also be a small 8-bit computer (an embedded computer), used inside a sensor, a measuring instrument or equipment for analyzing process values.
When a variable is assigned a value (set to a value), the value is normally (by default) a decimal number. If the value is a binary number, 2# must be written in front of the number, and if it is a HEX number, 16# must be written in front of the number. E.g. 2#101 = 5 or 16#FF = 255.
When deciding on what data type to use for a variable, it is important to know its maximum value capacity. Normally an INT data type is used for counters. If INT is used as TACHO HOURS on a motor, the maximum value of an INT can be a problem. TACHO HOURS is a counter showing the total number of hours a motor has run, and is used to indicate when the service interval has ended, and motor service is required. If for example the motor runs 20 hours a day, and it has an expected life time of 10 years, the total counter value will be reached as follows:
Hours within 24 hours*days per year*year = 20*365*10 = 73,000 (hours)
The problem is that the variable cannot be contained in the data type INT, as INT has a maximum value of 32767. A double integer DINT must be used instead, or even better a DWORD data type as it is able to contain an even larger number.
DWORD may contain an integer value from 0 to 4.29 billion.
If an INT is used anyway, the variable will show: 7466 as the INT has two ‘overflows. An ‘overflow’ takes place every time the integer is higher than 32767 and at an ‘overflow’, the variable is reset to -32768 (which is the lowest value for INT).
2 User defined data types
It is possible to define more advanced and complex data types to save time when programming, and to obtain a better program structure. The data types are named user defined or derived data types and are declared within TYPE and END_TYPE.
There are three user defined data types: ENUM (Enumerated data type), which is a list of constant numbers. STRUCT (Structured data type) which group different variables in a structure. ARRAY contains a series of variables having same data type.
NOTICE
If an absolute beginner starts programming in a PLC, it is important to know that the derived data types are not necessary to use to make PLC programs work. Only start using derived data types when greater experience in PLC programming is gained.
The different user defined data types are explained in the following chapters.
3 Enumerated data type, ENUM
The enumerated data type ENUM contains a list of unique names. Names are listed in parentheses, and must be meaningful with regard to their purpose. The declaring begins with TYPE and ends with END_TYPE.
Example:
TYPE LightTYPE :
(RED, YELLOW, GREEN);
END_TYPE
The data type LightTYPE in the example above can either be RED, YELLOW or GREEN. LightTYPE could be used to control a traffic light, an operator signal lamp, a light tower (see picture) on a machine or as a status on a valve.
LightTYPE will always take one of the defined types: RED, YELLOW or GREEN.
An ENUM must be allocated a default value. A default value is required to ensure the right value during start-up (initialization). In the example below LightTYPE is initialized with the default value RED when the PLC is powered up:
TYPE LightTYPE :
(RED, YELLOW, GREEN):= RED;
END_TYPE
The PLC-compiler (program which converts the ST program code to the PLC machine code) automatically associate a number to each text in the ENUM. The numbers are indexed starting from 0. This means that: RED = 0, YELLOW = 1 and GREEN = 2. The automatic numbering of the ENUM types is necessary as a CPU can only work in numbers. This also explains the ENUM data type name as ENUM (enumeration) can be translated to ‘automatic numerical order’. This ENUM is used because it is easier for the programmer to remember text instead of a number.
It is possible to define a fixed value for each name instead of automatic:
TYPE LightTYPE :
(RED:= 10, YELLOW:= 20, GREEN:= 30) := RED;
END_TYPE
The disadvantage of using ENUM is that all numbers are positioned in a continuous order (indexed). If new names are added in the middle of the sequence, the index is disrupted which will cause issues when ENUM variables are exchanged between more PLCs or computers, as all devices must be updated with the new PLC code at the same time.
Examples of use: Below are two variables, MotorLamp and Lamp, both having the data type LightTYPE:
Lamp:= MotorLamp; | //Here is Lamp set to red |
MotorLamp:= LightTYPE.GREEN; | //Set MotorLamp to green |
Lamp:= MotorLamp; | //Here is Lamp set to green |
ENUM creates a better structure, but ENUM is not possible in all PLC types.
4 Structured data type, STRUCT
A structured data type, STRUCT, is a composite data type used to group more datatypes in a class/object. The structured data type is declared by using the key words TYPE, STRUCT and END_STRUCT.
Each variable in a STRUCT needs to have a name followed by a colon, and then the data type. Note that the declaring is ended by a semicolon.
Below a STRUCT is shown called Motor, containing four variables which are all related to a motor. Speed (Motor speed), Temperature (measurement inside the motor), Voltage (Power supply for the motor) and AlarmStatus:
TYPE Motor : | //Example 1 STRUCT | ||
STRUCT | |||
Speed | : INT; | //Actual speed of the motor [RPM] | |
Temperature | : REAL; | //Temperature inside the motor [C] | |
Voltage | : REAL; | //The voltage of the motor [V] | |
AlarmStatus | : BOOL; | //Alarm if TRUE else FALSE | |
END_STRUCT; | |||
END_TYPE |
Note that comments are written after each variable which accurately describe the functionality of the variable to the reader of the PLC program. Furthermore, a unit is written in square brackets because the unit of different variables is often not known. For example, the speed of a motor could be measured in RPM (revolutions per minute), the frequency in Hz (Hertz) or in percentages (0 to 100%).
When the variable is declared, comment lines are also used to describe the behavior of the variable, as this is not always obvious or logical; e.g. the AlarmStatus where it is not clear whether the alarm goes off when the variable is TRUE or FALSE.
Some PLC types do not use text, as in the example above, when declaring a STRUCT; instead they are declared (written) in a list and therefore the key words: TYPE, STRUCT, END_STRUCT or END_TYPE will not appear to the reader.
A structured data type may contain one or more other derived data types. This can be seen in the example below:
TYPE Valve : | //Example 2 STRUCT | ||
STRUCT | |||
DisplayColor | : LightTYPE; | //User defined TYPE | |
ValveState | : BOOL; | //Can be TRUE (open) //or FALSE (closed) |
|
Pressure | : REAL; | //Pressure in [Bar] | |
END_STRUCT; | |||
END_TYPE |
In example 2 above the data type Valve consists of tree variables:
DisplayColor, ValveState (status of the valve: open or closed) and Pressure. The variables Pressure and ValveState use the standard data types REAL and BOOL, while the variable DisplayColor uses the data type LightTYPE.
Example of a portable tank containing chemicals (IBC tank):
TYPE TankType : | //Example 3 STRUCT | ||
STRUCT | |||
Liters : REAL | := 1000; | //Default tank size | |
LevelSensor | : REAL; | //Sensor at bottom | |
LevelSwitch | : BOOL; | //Float switch at bottom | |
END_STRUCT; | |||
END_TYPE |
Many variables in a PLC program can easily become confusing. Variables belonging to the same component (object), the same domain, or the same mode of operation may advantageously be grouped in a STRUCT. Grouping variables makes it easier and quicker to set up and maintain many identical components. This method of programming is called Object Oriented Programming (OOP), and is often used when writing computer programs.
If a variable with the data type STRUCT is to be transferred to a function the variable scope must be set to VAR_IN_OUT within the function.
5 Collection of values with same data type, ARRAY
An ARRAY is a structure, which can store a collection of values with the same data type. The values are located side by side in memory which means that it is simple to work on. An ARRAY always has a fixed length which cannot be changed during the execution of the program. An ARRAY can be set up and indexed by several dimensions.
You can write ARRAY programming code quickly, and it provides a good programming structure. The challenge is getting the values in and out of the ARRAY.
An ARRAY is also called a multi elementary data type.
Below example show an ARRAY, SpeedArray, which contains 6 positions of the data type INT. To declare the 6 positions, use ARRAY followed by square brackets including start end position number, separated by two dots as shown below:
VAR SpeedArray :
ARRAY [1 .. 6] OF INT;
END_VAR
The first value in the array is located in position no. 1 and the last in position no. 6. The name for the ARRAY in this example is Speed, which is added to the text Array, so that any person working on the PLC code will easily know that an ARRAY is used.
SpeedArray is a one-dimensional ARRAY and can be used where a collection of many values with same data type is positioned in one long row. Examples include:
An ARRAY can be used with all data types, including STRING, STRUCT or functions.
A two-dimensional ARRAY can be used on e.g. a parking lot (car park), stock rack, a graph, a bar chart or a pivot table and can be set up as follows:
VAR Racking
ARRAY [1 .. 5, 1 .. 3] OF INT;
END_VAR
A three-dimensional ARRAY is defined as follows:
VAR PackOnPallet
ARRAY [1 .. 5, 1 .. 4, 1 .. 3] OF REAL;
END_VAR
Used e.g. for packages on a pallet (palletizing) or positions in a warehouse.
If you look at a three-dimensional ARRAY as an X, Y and Z system of coordinates, the values from the above-mentioned example can be grouped as follows:
X = 1 til 5, Y = 1 til 4, Z = 1 til 3.
The total amount of positions in PackOnPallet ARRAY is: 5*4*3 = 60 pieces. So this ARRAY contains 60 positions (elements).
An ARRAY can be defined with an index starting point of 0. The ARRAY below contains 4 positions as position 0 (zero) and position 3 are included when counting the number of positions in the array. It results in a more stable program when arrays start from 0, because the array index pointer remain uninitialized (not given an start value):
VAR MyArray1D
ARRAY [0 .. 3] OF INT;
END_VAR
Insert a single value in an ARRAY
In the below example the value 5 is inserted at position 4 in the one-dimensional ARRAY SpeedArray:
SpeedArray [4] := 5;
Values can be inserted in the three-dimensional ARRAY PackOnPallet as follows:
PackOnPallet [1, 1, 1] := 12.1;
PackOnPallet [5, 1, 3] := 43.9;
PackOnPallet [1, 4, 2] := 23.5;
For inserting multiple values in a 3D ARRAY.
Get a value from an array
In this example shows how to get a value from a one-dimensional ARRAY. The value is located at position 2 in the array MyArray1D and copied to the variable Var1:
Var1 := MyArray1D [2];
//Contents of Var1 is 12
Get a value from a three-dimensional ARRAY is carried out as follows:
A value is transferred (copied) to the variable Var3 with the value of 43.9:
Var3 := PackOnPallet [5, 1, 3];
//Contents of Var3 is 43.9
IMPORTANT: You must not assign (copy) a value to positions outside of the ARRAY. If assigning a value to for example, position no. 10 in an ARRAY containing only 6 positions, the PLC can stop the program execution (Run Time Error). This is a common error/mistake when using arrays for your code. To avoid assigning values to positions outside the ARRAY use an IF statement as shown below:
Index:= 4; //Insert 5 at position 4
IF Index > 0 AND Index <= 6 THEN
SpeedArray [Index] := 5;
END_IF;
In the previous example, the low bound and upper bound of the array are used directly. This can be a disadvantage when the array is used inside a function or the lower and upper bounds have to be changed. Therefore, the built-in standard functions LOWER_BOUND and UPPER_BOUND can be used.
The two functions return the bound of the array and can be used as shown below:
Index:= 4; //Insert 5 at position 4
IF Index >= LOWER_BOUND (SpeedArray, 1)
AND Index <= UPPER_BOUND(SpeedArray, 1) THEN SpeedArray[Index]:= 5;
END_IF;
Direct and indirect addressing
For direct addressing in an array, numbers are used to retrieve the content at a particular location in an array and for indirect addressing, a variable is used:
//Indirect addressing is using a variable
Index:= 4; //Index is an INT or a WORD data type
SpeedArray[Index]:= 5;
//Direct addressing when a number is used directly
SpeedArray[4]:= 5;
Data collection
An array is perfect for collecting data (data log) in a PLC controller. This code collects the number of items produced from a machine:
The array is configured as shown:
VAR
DataCollect ARRAY [7 .. 16] OF WORD;
END_VAR
The array saves data from a range from 7 to 16 which corresponds to the period during which the machine produces items. From 7 o’clock to 8 o’clock the machine produces 15 items, between 8 o’clock and 9 o’clock the machine produces 29 items, etc.
By collecting the number of items produced, it is easy to find out the periods in which the machine has produced the most and the fewest items. The data collected confirms how efficient the production is during different time periodes.
An element in an array can only save one value. This example shows how an array can contain multiple values, and how constant values are saved in an array.
The example is a warehouse rack, controlled by a robot:
1 | Starting point (homing position) |
2 | Distance, X-axis for each row |
3 | Distance, Y-axis for each shelf |
4 | Pallet with 1 box in location 3,1 |
5 | Pallet with 2 boxes in location 2,2 |
6 | Pallet with 4 boxes in location 4,3 |
Numbers at X-axis (2) and Y-axis (3) are predefined numbers to inform the robot how far to go (distance) to place an item on a certain row and shelf in the warehouse rack. Each axis uses an encoder to determine where a location is. An encoder is a sensor that sends a pulse for distance traveled, and when the correct number of pulses is received, the robot stops at the correct location.
Each location in the warehouse rack contains several values and therefore a STRUCT is created as shown to the right:
To add more values, add additional code lines under the Weight variable.
TYPE LocationTYPE
STRUCT
NoOfBox : WORD;
Weight : REAL;
END_STRUCT
END_TYPE
To handle the warehouse rack, the following variables are created:
VAR CONSTANT
StockSizeX: INT := 4; //Size x of the Stock
StockSizeY: INT := 3; //Size y of the Stock
StockEncodeX: ARRAY[1.. StockSizeX] OF INT := [235, 370, 505, 640]; //Encoder X values
StockEncodeY: ARRAY[1.. StockSizeY] OF INT := [0, 213, 355]; //Encoder Y values
END_VAR
VAR
Stock: ARRAY[1.. StockSizeX, 1.. StockSizeY] OF LocationTYPE; //Location is a STRUCT
END_VAR
Inserting values for the pallet located at (6) is done like this:
Stock[4, 3].NoOfBox := 4; //Located at StockEncodeX[4] and StockEncodeY[3]
Stock[4, 3].Weight := 1210.25; //Set weight
This example is based on a depalletizer:
A conveyor belt can have different states
and these are grouped in an ENUM:
TYPE ConveyorState :
( NONE, STOP,
RUN_CW, // Run clock wise
RUN_CCW, // Run counter clock wise
ALARM) := STOP; // Default set to stop mode
END_TYPE
By default the state is set to STOP to avoid the conveyor belt running unintentionally when the PLC is powered up.
The variables for a conveyor belt are grouped into a STRUCT:
TYPE ConveyorTYPE :
STRUCT
State | : ConveyorState; //State/mode |
Speed_m_s | : REAL; //Conveyor speed in [m/s] |
Size | : INT; //Conveyor size, 40 or 60 |
END_STRUCT
END_TYPE
Declaration of the conveyor belt variables and code examples are shown below:
VAR | |||
M1 | : ConveyorTYPE; | //Single conveyor | |
ConveyALL | : ARRAY [4..6] OF ConveyorTYPE; | //All conveyor | |
END_VAR |
//Start M1 single conveyor
M1.State:= ConveyorState.RUN_CW;
//Set size of conveyor 5
ConveyALL[5].Size:= 40;
//Start conveyor 5
ConveyALL[5].State:= ConveyorState.RUN_CW;
//Copy all variables from conveyor 5 to conveyer 6.
ConveyALL[6]:= ConveyALL[5]; // Conveyor 6 is a copy of conveyor 5