Edition 4 of IEC 61131-3 has been available online since May 2025. In the following post, I will briefly explain the most important differences between Edition 3 and Edition 4.
I was unable to test the new features of Edition 4 as I did not have a development environment that supported the new functions of Edition 4 at the time of the comparison. I can therefore only offer a general overview of the differences. I used IEC 61131-3 (Edition 3.0) 2013-02 and IEC 61131-3 (Edition 4.0) 2025-05 for the comparison. However, if you want or need to familiarise yourself more intensively with IEC 61131-3, there is no getting around the standard.
The first impression
When comparing the tables of contents, it is noticeable that in Edition 3 the appendix begins on page 221, while in Edition 4 the appendix begins on page 241.
A new addition is the 6-page chapter 6.9 Synchronisation of concurrent execution. The corresponding sub-chapters reveal that this is about mutexes and semaphores.
Chapter 7.2 Instruction List (IL) is no longer included in Edition 4. This means that IL is no longer part of IEC 61131-3, but this does not necessarily mean that IL is no longer offered by manufacturers. Each manufacturer can continue to decide for themselves whether they want to offer IL in their development environment or not.
Octal literals
In addition to the integer literals (e.g. -43), the floating point literals (e.g. -43.8), the binary literals (e.g. 2#1101_0110) and the hexadecimal literals (e.g. 16#E26B), there are also octal literals (e.g. 8#267) in Edition 3. The octal literals are no longer supported by Edition 4. In Edition 3, the octal literals were already labelled as deprecated.
USTRING and UCHAR data types
With the two new data types USTRING and UCHAR, character strings are now also supported in which each character is encoded according to UTF-8.
While a WSTRING / WCHAR is encoded as UTF-16 and always requires 2 bytes per character, UTF-8 encodes each character with a sequence of 1 to 4 bytes.
UTF-8, or Unicode Transformation Format – 8 bit, is backwards compatible with 7-bit ASCII, as the first 128 characters in UTF-8 and ASCII are identical in the English character set. This means that every valid ASCII character string is also a valid UTF-8 character string, so that most programmes written for ASCII can be easily adapted to UTF-8.
UTF-8 is particularly widespread in Internet applications. Both the Internet Mail Consortium (IMC) and the Word Wide Web Consortium (W3C) recommend the use of UTF-8 as a standardised encoding.
USTRING and UCHAR literals are specified as follows:
x : USTRING := USTRING#'ABC';
x : USTRING := U#'ABC';
y : UCHAR := UCHAR#'A';
y : UCHAR := U#'A';
LEN_MAX and LEN_CODE_UNIT
The character string functions are described in chapter 6.6.2.5.12. The functions LEN_MAX and LEN_CODE_UNIT are new additions.
LEN_MAX gibt die maximale Länge des Strings zurück:
VAR
x : STRING[100] := 'Hello';
y : WSTRING[100] := WSTRING#'Hello';
z : USTRING[100] := USTRING#'Hello';
END_VAR
a := LEN_MAX(x); // a = 100
b := LEN_MAX(y); // b = 100
c := LEN_MAX(z); // c = 100
LEN_CODE_UNIT returns the number of code units occupied by the string. For the data types STRING and WSTRING, LEN_CODE_UNIT returns the same value as for LEN. With USTRING, however, the value of LEN_CODE_UNIT can be greater than that of LEN.
a := LEN_CODE_UNIT(x); // a = 5
b := LEN_CODE_UNIT(y); // b = 5
c := LEN_CODE_UNIT(z); // c = 5
As the strings in this example only contain ASCII characters, LEN_CODE_UNIT returns the same value for all three data types.
Specification of characters by character code
In a string or character literal, any character can be specified by the character code. This is specified in hexadecimal notation after a $ in curly brackets:
VAR
x : STRING := '${9}'; // Control character for tabulator
y : WSTRING := WSTRING#'${2211}'; // Character Σ
z : USTRING := USTRING#'${1F579}'; // Emoji ‚Joystick’
END_VAR
The previous support of character codes with fixed lengths (e.g. ‘$0A’ or “$00C4”) is still supported.
Note: For the representation of the character code, I would have liked the usual coding for the respective number system to be used, i.e. 16#1F579 instead of just 1F579. Even if it is usual to specify character codes in hexadecimal, it is easier to recognise in which number system the character code was specified.
Unfortunately, the Char Map under Windows only allows the entry of character codes with a maximum of 4 digits. The function Insert -> Symbol in Word is much more helpful in finding the right character code:

The string and character literals are described in chapter 6.3.3 Character string literals.
Data type conversion
Figure 11 in chapter 6.6.1.6 Data type conversion provides an overview of the implicit and explicit data type conversions. Compared to Edition 3, there are some adjustments and extensions here.
Edition 4 defines some explicit data type conversions that are not included in Edition 3:
- LWORD_TO_BOOL, DWORD_TO_BOOL, WORD_TO_BOOL and BYTE_TO_BOOL
- UINT_TO_WCHAR and USINT_TO_CHAR
- WSTRING_TO_WCHAR
- WCHAR_TO_UINT
Some explicit data type conversions have been changed to implicit data type conversions:
- STRING_TO_WSTRING
- CHAR_TO_WCHAR
Some implicit and explicit data type conversions have been added for the USTRING and UCHAR data types:
- UDINT_TO_UCHAR and DWORD_TO_UCHAR
- USTRING_TO_WSTRING, USTRING_TO_STRING and USTRING_TO_UCHAR
- UCHAR_TO_UDINT, UCHAR_TO_LWORD, UCHAR_TO_DWORD, UCHAR_TO_USTRING, UCHAR_TO_WCHAR and UCHAR_TO_CHAR
- WSTRING_TO_USTRING and STRING_TO_USTRING
- CHAR_TO_UCHAR
Data type conversion between strings and ARRAY OF BYTE
The functions for data type conversion between strings and ARRAY OF BYTE are new additions. These are described in chapter 6.6.2.5.8 Data type conversion between string types and array of bytes:
- STRING_TO_ARRAY_OF_BYTE, WSTRING_TO_ARRAY_OF_BYTE and USTRING_TO_ARRAY_OF_BYTE
- ARRAY_OF_BYTE_TO_STRING, ARRAY_OF_BYTE_TO_WSTRING and ARRAY_OF_BYTE_TO_USTRING
TRUNC was removed
The TRUNC function is defined as a typed and overloaded function. In the typed notation, the source data type and the target data type are specified in the function name, e.g. LREAL_TRUNC_DINT(x), while in the overloaded notation, only the target data type is specified, e.g. TRUNC_DINT(x). The source data type is determined from the transferred parameter.
Edition 3 still had the notation without specifying a data type definiert, i.e. TRUNC(x). Here the source data type always had to be ANY_REAL and the target data type always ANY_INT. This notation was already labelled as deprecated in the edition 3 and has now been removed from edition 4.
Properties
Edition 4 (finally) defines properties. Properties are part of a CLASS, FUNCTION_BLOCK or INTERFACE. Setter and getter methods are declared so that the value of a property can be set or read. The declaration is similar to that of a method. However, the keywords PROPERTY_GET and PROPERTY_SET are used instead of the METHOD keyword.
PROPERTY_GET PUBLIC nFoo : INT
;
END_PROPERTY
PROPERTY_SET PUBLIC nFoo : INT
;
END_PROPERTY
If setter and getter methods are defined, the data type must be the same for both methods.
The same access specifiers can be used for properties as for methods, i.e. INTERNAL, PRIVATE, PROTECTED and PUBLIC. The keywords ABSTRACT and FINAL are also possible. If no access specifier is specified, the property is PROTECTED.
If a property is only to be readable, the setter method can be omitted or set to PRIVATE.
A property can be used like an instance variable:
VAR
fbFoo : FB_Foo;
x : INT := 10;
END_VAR
fbFoo.nFoo := x; // Write to the property
x := fbFoo.nFoo; // Read from the property
Program code is stored in the setter and getter methods to manage the value of the property internally.
FUNCTION_BLOCK FB_Foo
VAR PRIVATE
_nFoo : INT; // Backing variable
END_VAR
PROPERTY_GET PUBLIC nFoo : INT
nFoo := _nFoo;
END_PROPERTY
PROPERTY_SET PUBLIC nFoo : INT
_nFoo := nFoo;
END_PROPERTY
END_FUNCTION_BLOCK
The properties are described in chapter 6.6.5.11 Properties.
Assertions
In chapter 6.6.2.5.17 Assertions, Edition 4 defines the ASSERT function, which has the input parameter IN of type BOOL and no return value. ASSERT is used to check the validity of expressions or variables during development.
If IN is TRUE when called, the function has no effect. However, if IN is FALSE, ASSERT should ‘inform’ the developer of this. Each manufacturer (of the development environment) can individually define what the message looks like.
Once development is complete, calling ASSERT no longer has any effect, regardless of the value of the input parameter IN. How this is communicated to the development environment can be determined by the manufacturer.
Synchronization of concurrent execution
Chapter 6.9 Synchronization of concurrent execution has been added in Edition 4 and defines objects and functions to synchronize program code that has been split into several tasks. Synchronization is required, for example, if two parallel programs access a common variable, whereby one program also changes this variable.
Mutexes and Semaphores are defined in Edition 4 to make it easier to synchronize programs running in parallel.
I will only briefly introduce the functions that define Edition 4. A detailed description of these concepts would be too extensive at this point, but could be the subject of a separate post.
Mutex
Mutexes are usually used to model critical sections. A mutex can be locked and unlocked.
Both an object-oriented approach and an approach based on functions are defined. The object-oriented approach is based on the MUTEX function block. This has the following methods:
- UNLOCK
- LOCK
- TRYLOCK
MUTEX also has the properties LOCK_COUNT and OWNER. OWNER is used to store a value that identifies the task.
In the approach based on functions, the corresponding functions are available:
- MUTEX_UNLOCK
- MUTEX_LOCK
- MUTEX_TRYLOCK
Semaphore
Here too, both an object-oriented approach and an approach based on functions are defined. The SEMA function block was defined for the object-oriented approach. This has the methods:
- RELEASE
- ACQUIRE
- TRY_ACQUIRE
The following functions have been defined accordingly:
- SEMA_RELEASE
- SEMA_ACQUIRE
- SEMA_TRY_ACQUIRE
Support of BCD numbers discontinued
In future, BCD numbers will no longer be supported. In Edition 4, the following functions are marked as deprecated:
- IS_VALID_BCD
- BCD_TO_out and in_BCD_TO_out
- TO_BCD_out and in_TO_BCD_out
Conclusion
My personal highlight is the support of mutexes and semaphores. In times when CPUs have more and more cores, appropriate tools must also be provided to distribute a program across several tasks.
I would describe the remaining changes as general care and maintenance. It’s good that things are also removed from the standard if they are no longer relevant. This includes, for example, the removal of IL as and the omission of octal literals.
Some points have also been defined in more detail, for example the question of whether ARRAY[1..1] and ARRAY[1..0] are permitted and how they behave (chapter 6.4.4.5.1).
Otherwise, I don’t see the differences as extensive as between Edition 2 (published in 2003) and Edition 3 (published in 2013).
However, I also miss some points. These include the implicit enumeration, optional parameters for methods and functions, as well as the option to overload methods.