240
TwinCAT Tutorial http://www.contactandcoil.com 1 TABLE DE CONTENT Chapter 1: Introduction…………………………………………………………………………………...2 Chapter 2: Quick Start………………………………………………………….……………………….... 5 Chapter 3: Structuring PLC Data…………………………………………….…………….……………47 Chapter 4: Persistent Variables…………………………………………………………………….…...59 Chapter 5: Structuring PLC Logic………………………………………….……………………………79 Chapter 6: Multiple Virtual PLCs…………………………………….…………..………………………88 Chapter 7: Ladder Logic Editor………………………………………….……………………………..110 Chapter 8: Writing your own Functions and Function Blocks……..……………………...……………126 Chapter 9: Structured Text…………………………………………...………………………………..139 Chapter 10: Building an HMI in .NET………………………………….………………………………151 Chapter 11: Introduction to Motion Control………………………….…………………..……………..162 Chapter 12: Introduction to TwinSAFE.………………………….…………...………………………..187 Chapter 13: The Scope View……………………………………….…………………………………216 Chapter 14: Part Tracking……………….……………………………….………...…………………. 229

TwinCAT tutorial - Beckhoff

Embed Size (px)

Citation preview

Page 1: TwinCAT tutorial - Beckhoff

TwinCAT Tutorial http://www.contactandcoil.com

1

TABLE DE CONTENTChapter 1: Introduction…………………………………………………………………………………...2

Chapter 2: Quick Start………………………………………………………….………………………....5

Chapter 3:Structuring PLC Data…………………………………………….…………….……………47

Chapter 4:Persistent Variables…………………………………………………………………….…...59

Chapter 5:Structuring PLC Logic………………………………………….……………………………79

Chapter 6:Multiple Virtual PLCs…………………………………….…………..………………………88

Chapter 7:Ladder Logic Editor………………………………………….……………………………..110

Chapter 8:Writing your own Functions and Function Blocks……..……………………...……………126

Chapter 9: Structured Text…………………………………………...………………………………..139

Chapter 10:Building an HMI in .NET………………………………….………………………………151

Chapter 11: Introduction to Motion Control………………………….…………………..……………..162

Chapter 12: Introduction to TwinSAFE.………………………….…………...………………………..187

Chapter 13:The Scope View……………………………………….…………………………………216

Chapter 14:Part Tracking……………….……………………………….………...………………….229

Page 2: TwinCAT tutorial - Beckhoff

Chapter 1: Introduction

2

1 – IntroductionIn the control system industry we’re used to slow progress. PLCs lag behind PC technology by

ten years or more, and we pay an outrageous premium for PLC hardware compared to the

technology sitting on our desktop in our offices.

Beckhoff has always pushed PC-based control systems based on commodity hardware like Intel

x86 processors and Ethernet chips. With TwinCAT 3, their flagship automation software now

supports multi-core processing, which leaves traditional PLC technology so far behind it’s not

even funny.

On all new TwinCAT 3 PLC programs I start, I set the task to run every 0.5ms by default, and I

could run most of them much faster. Not only does the PLC logic execute every 0.5ms, but the

EtherCAT I/O bus for a reasonably large machine can also run at this speed. When you compare

this with a traditional PLC with typical logic scan times (plus I/O refresh times) in the tens of

milliseconds, you can see that TwinCAT 3 with EtherCAT is far better suited to any high speed

process than your typical PLC.

If you think scan times don’t matter, consider a conveyor line running at around 1 m/s (completely

reasonable) and you have to fire an output as a specific point on the conveyor passes a given

location (measured by an encoder). The difference between a 1ms reaction time and a 10ms

reaction time is the difference between 1 mm and 10 mm accuracy.

The TwinCAT 3 solution also offers many advantages over the traditional PLC/HMI/Laptop

combination we’re familiar with:

The development environment runs on the same machine as the PLC logic, so you don’t need

a separate laptop. Going online is as simple as opening the development environment and

clicking the Go Online button.

The HMI runs on the same machine as the PLC, so HMI-to-PLC communication responds

incredibly fast. Transferring data between the TwinCAT 3 runtime and the HMI is as fast as a

Page 3: TwinCAT tutorial - Beckhoff

Chapter 1: Introduction

3

memory copy instruction. I’ve been able to transfer hundreds of kilobytes of data from a PLC

array to a .NET application in a fraction of a second.

If you’re into writing your own HMI software (for example in .NET), the driver for the protocol

(called ADS) is included for free.

The TwinSAFE safety editor is integrated into the development environment, so mapping

signals between your safety program and your PLC program is trivial, and safety inputs can

also be double-mapped both to the safety program, and to your PLC program for monitoring

and alarming.

You can use source control applications like git, mercurial or subversion, and they can even

integrate with the development environment.

Not only does TwinCAT 3 offer outstanding performance, but you can download it, install it, and

try it out indefinitely for free! When you try to activate your first TwinCAT 3 PLC program, you’ll

be prompted to generate a 7 day trial license. When that 7 day trial license expires, if you’re not

done testing, you can just generate a new 7 day trial license without reinstalling. Obviously you

wouldn’t be able to run this in a production environment if you had to do this every 7 days, but the

trial is a fully functional version of TwinCAT 3. Want to try something on your test bench before

you commit to implementing it in production? Just install the trial version on an old PC and test it

out.

I’ve been a control system programmer for over 15 years, and I’m also a .NET programmer, but

I’m a ladder logic programmer first and foremost. If you’re a control system programmer in North

America, you’re likely familiar with Allen-Bradley’s (or Rockwell Software’s) RSLogix family of

control software. There is no doubt that RSLogix has set the standard for ladder logic editors, and

to be completely honest, no other editor comes close to the user-friendliness of their development

environment, and I’m including TwinCAT 3.

Beckhoff is a German company and ladder logic just doesn’t seem to be popular on their side of

the pond. TwinCAT 3’s ladder editor is a major improvement over the one in TwinCAT 2, but it

still doesn’t have the polish you’d be used to if you’re coming from the RSLogix world. I guarantee

you’ll be frustrated the first time you try it, but that’s why I’m writing this tutorial. In my experience,

programming ladder logic in TwinCAT 3 can be just as productive as in RSLogix, and it doesn’t

Page 4: TwinCAT tutorial - Beckhoff

Chapter 1: Introduction

4

need to be painful. Also, keep in mind that TwinCAT 3 is very new and Beckhoff continues to

support and improve it.

If you’re familiar with RSLogix 5000, you may find some of my examples familiar. I’ve tried to

organize my TwinCAT 3 projects in a way that’s similar to how most RSLogix 5000 programmers

would organize their projects. That made the transition easier for me, and I hope it will for you too.

Page 5: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

5

2 – Quick StartThis “Quick Start” is actually rather long, but it’s going to take you through downloading, installing,

configuring, programming, building, activating, going online, forcing, and even making online

changes to a TwinCAT 3 PLC program. When you’re done you will have a basic understanding

of the TwinCAT 3 system and how it works.

Prerequisites

TwinCAT 3 can run on a plain-vanilla copy of Windows 7. As of June 2016, Beckhoff says

TwinCAT 3 is fully supported on Windows 10 (with TwinCAT version 3.1.4020.0).

One of Beckhoff’s main product lines is industrial PCs and if you buy their industrial PCs, you’ll

get a discount on the TwinCAT 3 license. Also, the license cost scales with the processing power

of the PC. Their most expensive license tier is for “3rd party hardware”, which means any PC you

didn’t buy from them. I have no opinion on whether you should buy a Beckhoff industrial PC or a

commercial PC. One thing to keep in mind is that Beckhoff’s top of the line industrial PC won’t

have quite as much processing power as the newest commercial PCs on the market today, so if

you’re really concerned about performance, you’ll probably go with a commercial PC. On the other

hand, industrial PCs are hardened against tougher environmental conditions like dirt and

temperature, and they come in nicer form factors for mounting inside of a control panel. They also

take a 24V input rather than running from the AC mains. Whatever you do, make sure you invest

in a good UPS as well.

BIOS Settings

If you use a 3rd party PC, you may have to change the following settings in the BIOS:

Turn off Hyper-Threading (Intel Core-i7 specifically)

Turn on Intel Virtualization Technology Extensions (VT-x), which is required for the 64-bit

version of TwinCAT 3

Page 6: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

6

Optional: I/O

Whether or not you’re hooking up to a legacy system, or starting fresh, I highly recommend using

an EtherCAT I/O bus. The performance is phenomenal compared to any other bus on the market

today, including any other Ethernet-based technology, and even SERCOS. The price is also quite

reasonable because the technology is based on commodity Ethernet hardware.

If you do want to go with EtherCAT, you can’t just use any Ethernet card in the PC as your bus

master. It has to be an Intel chip, as that’s the only chipset that Beckhoff’s EtherCAT bus master

seems to support. While you can buy a compatible card from Beckhoff, I’ve also had success with

commercial off-the-shelf Ethernet cards. Your mileage may vary. For a list of compatible Ethernet

chips, do a Google search for beckhoff ethercat compatible cards or visit the following

URL:http://infosys.beckhoff.com/english.php?content=content/1033/tcsystemmanager/reference

/ethercat/html/ethercat_supnetworkcontroller.htm

As of this moment, an Intel Gigabit CT PCI-E Network Adapter EXPI9301CTBLK is a reasonably

priced commodity card that works well.

If you want to try TwinCAT 3 without any hardware attached, you can do that too, and you won’t

need an EtherCAT master at all.

If you do happen to need to interface to legacy I/O buses like DeviceNET, rather than installing a

DeviceNET bus master card in the PC itself, I highly recommend starting with an EtherCAT bus

to an EtherCAT bus coupler such as an EK1100, and then buying one of the many different

EtherCAT-to-whatever bridges that Beckhoff sells, as this is usually less expensive in the short

run, and lets you expand your system with lower cost (and higher performance) EtherCAT I/O in

the future.

Optional: Visual Studio 2010 Professional

If you’re already a .NET programmer then you may already have Visual Studio 2010 Professional

installed. If that’s the case, TwinCAT 3 will actually install as an extension to VS2010. If you don’t

have it installed, don’t worry; TwinCAT 3 will install the Visual Studio Shell instead.

Page 7: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

7

You may wonder if you can use the free versions of Visual Studio such as Visual C# 2010 Express

edition or Visual Basic .NET 2010 Express Edition. While you can have them installed, and you

can use them to write programs that communicate with the TwinCAT 3 runtime, TwinCAT 3 won’t

install as an extension to these products.

Downloading TwinCAT 3

The TwinCAT 3 installer is free to download, but it requires registration. Beckhoff periodically

releases new versions, so to get the very latest, go to their product web page:

http://www.beckhoff.com/english.asp?download/tc3-downloads.htm

Click the TE1xxx | Engineering link under the Software section. Click on the TwinCAT 3.1 –

eXtended Automation Engineering (XAE) link. Note that XAE includes the runtime (XAR) as well.

Once you register you’ll receive an email with download instructions. Download the .zip file and

extract it to some temporary directory.

Installing TwinCAT 3

In the directory where you unzipped the downloaded TwinCAT 3 archive, locate the “exe” file and

double-click it.

Follow the directions. Use the default options. When it prompts you about the Visual Studio 2010

shell, check the box to install it:

Page 8: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

8

You will have to restart before it completes.

Create Your First TwinCAT 3 Project

Now that you have TwinCAT 3 installed, check in your system tray for a new icon:

Just for reference, the color of the icon (blue) means the TwinCAT 3 runtime (PLC/motion

controller/etc.) is in Config mode. That’s similar to “program mode” on an Allen-Bradley PLC.

When the runtime is started the icon will be green. Red means it’s stopped.

Right-clicking on the icon will display a system menu:

Page 9: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

9

Select TwinCAT XAE from the system menu. (The “E” stands for “Engineering.” That’s the

programming environment.)

After the splash screen you’ll see the Visual Studio Start Page:

Click the New Project… link (highlighted) on the left side of the page. That will display the New

Project dialog:

I’ve only installed TwinCAT 3 without installing Visual Studio 2010 Professional, so you can only

see one project type: TwinCAT XAE Project. If you have Visual Studio 2010 Professional installed,

you’ll see all the regular project types, plus this new TwinCAT XAE Project type. Make sure you’ve

Page 10: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

10

selected this type. Then give your project a suitable Name (see the highlighted field above). When

you change the project name, the solution name will change to match. For our purposes they can

be the same name. I chose “TwinCAT 3 Tutorial.” Click OK.

This could take a minute. It’s building a new TwinCAT 3 project from a template. When it’s

complete you’ll see a screen like this:

All of the files for this project are organized in the Solution Explorer on the right. Here’s a brief

explanation of each section:

The top node in the tree is the “Solution.” This is actually a Visual Studio construct, not a TwinCAT

3 object. In Visual Studio, a Solution is a group of Visual Studio Projects and an associated “build

order.” In our case there’s only one Visual Studio Project: TwinCAT 3 Tutorial. If you’re using

Visual Studio 2010 Professional, you would be able to add additional projects under the solution,

such as a C# or VB.NET project (perhaps an HMI or a data collection application?). This would

be convenient simply because you could keep everything in one place and even use the

integrated source control add-ins to manage everything in one environment.

Under the project you have:

SYSTEM: this section manages the runtime including the licenses. You can configure how

many PC processor cores to use, which PLC or motion tasks run on each core, and what

percentage of each core’s processing power to allocate to each task. Note that it’s possible to

connect to runtimes on other computers, but for now we’ll only be concerned with the local

runtime.

Page 11: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

11

MOTION: this is where you add motion control tasks, and assign axes to each task. These

tasks do the work of closing the control loops for you. Your PLC programs typically interface

with the motion tasks using standard motion function blocks and the motion tasks do the heavy

lifting of controlling the actual servo drives, etc. When you commission a new axis (setting up

your motion parameters like speed, acceleration, deceleration, etc., you interact directly with

the objects under the MOTION section of the tree).

PLC: this is where you add PLC projects. Each PLC project lets you define variables (memory

assignment) and PLC logic.

SAFETY: if you’re using an EL6900 Safety PLC slice or the new TwinCAT 3 safety runtime (a

certified safety PLC that runs on the PC itself), you can edit the safety logic here. This is handy

because you can create connections between the PLC and the Safety logic for monitoring and

status.

C++: one of the big new features or TwinCAT 3 is the ability to write C++ code that executes

directly in the runtime.

I/O: this is where you configure your I/O network, and then create your mappings between all

of the different modules. For example, if you define an input in the PLC module, you have to

map it to a physical input in the I/O module.

TwinCAT 3 Solution File Structure

One of the interesting things about TwinCAT 3 compared to other PLC programming

environments is that it stores the “solution” in a collection of files and directories rather than in a

single monolithic file. If you open the directory where you asked it to create your solution, you’ll

see the following:

Page 12: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

12

The TwinCAT 3 Tutorial.sln file is your “solution” file, and it corresponds to the top level node in

your solution explorer in the right hand side of your TwinCAT XAE window. The TwinCAT 3

Tutorial folder corresponds to the TwinCAT 3 Tutorial project under the solution. If you double

click on that folder, you’ll see a file called TwinCAT 3 Tutorial.tsproj:

If you’re inquisitive, you can open this file with Notepad (or any text editor) and look at the

contents. You’ll see that it’s just an XML file. When you start to add a PLC program or a Safety

program in the editor, you’ll then see new folders created here, and the TwinCAT 3

Tutorial.tsproj file will include references to these child projects.

Adding a PLC Project

In TwinCAT 3, a “PLC” is like a “virtual” PLC. You can run multiple virtual PLCs on a single

computer. This might actually be useful if you had a single PC controlling multiple machines. Each

machine could be controlled by a single virtual PLC, which might be a good way to manage the

complexity of a large system. You can also separate virtual PLCs by CPU core, so if your scan

time started to get too high, you could break out part of that logic and run it in a separate virtual

PLC and schedule the task that runs that second PLC on a different CPU core. Two virtual PLCs

can still send data back and forth by mapping outputs on one PLC to inputs on the other PLC,

and vice-versa.

The simplest case is to create a single PLC project. Let’s do that now. Right-click on the PLC

node in the Solution Explorer (highlighted):

Page 13: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

13

From the context menu that appears, select Add New Item… to display the following wizard:

Enter a name for your new virtual PLC in the Name box (highlighted above). I chose “PLC1” but

you may want to pick a more descriptive name, like the name of the machine or cell that this PLC

will control.

Standard PLC Project is the default template, so leave that selected and click the Add button in

the bottom right. TwinCAT 3 will build a new PLC project for you from a template. This could take

a few seconds to a few minutes depending on the speed of your computer. When it’s complete,

you’ll see your new PLC in the Solution Explorer:

Page 14: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

14

It gets a little confusing to talk about the “PLC Project” now because there are so many nodes in

the tree, so from now on I’ll call the nodes by their name: PLC1 or PLC1 Project.

Most of the interesting parts are under the collapsed PLC1 Project node, so click on the triangle

next to that node to expand it:

Let’s go through them one at a time to explain their meaning:

External Types: you should never have to go in here, so don’t worry about it.

References: this is where you add references to external libraries (either ones that are included

in TwinCAT 3, such as motion control libraries, or ones that you write yourself, which we’ll

explain later).

DUTs, GVLs, POUs, and VISUs: these are just convenience folders that are created by

TwinCAT 3, and it’s where you’re supposed to create your Data Unit Types, Global Variable

Lists, Program Organization Units, and Visualizations, respectively. What do those mean?

o Data Unit Type (DUT): this is similar to a structure or a user-defined type in other

programming languages. It allows you to group a few pieces of data together into a single

composite piece of data.

Page 15: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

15

o Global Variable List (GVL): as the name suggests, this is a list of variables (memory

locations) that will be accessible from everywhere in this PLC (but not other PLCs) and can

be accessed from your HMI.

o Program Organization Unit (POU): contains logic (such as ladder logic, function block

diagram logic, structured text, etc.) and associated local variables that are only accessible

from inside that logic. There are 3 types of POUs: Programs, Functions, and Function

Blocks, which will be explained later.

o Visualization (VISU): these are your HMI screens. TwinCAT 3 has a built-in HMI system

that takes advantage of the fact that you’re already running TwinCAT 3 on a computer with

a screen. This is an optional add-on (sometimes called a “supplement”). You have to pay

extra for the visualization license if you want to use it in a production machine, but in my

experience it’s a reasonable price. Note that you can also use 3rd party HMI/SCADA

systems with TwinCAT 3, and you can write your own .NET program. The 3rd party

solutions typically use OPC for communication, and .NET programs use Beckhoff’s free

DLL that lets you communicate directly with the runtime using their proprietary ADS

communication library.

When you created the PLC project, TwinCAT 3 automatically created the first POU for you (a

Program called MAIN). It did this so that it could automatically create a Task for you. To explain

what this means, I’ve expanded a few more nodes in the Solution Explorer tree:

Page 16: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

16

I’ve highlighted the important nodes, above. Under SYSTEM > Tasks, there is a new Task

called PlcTask. A Task is what gets scheduled to run on the TwinCAT 3 runtime (that is, you can’t

run a Program directly, you have to create a Task, and the Task then runs your Program). The

Task has certain attributes, like how often to run. Double-click on the PlcTask node under

the SYSTEM > Tasks node, and you’ll see this properties page for the Task:

Page 17: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

17

Note the two highlighted fields above next to the Cycle ticks field. By default the task is configured

to run once every 10 cycle ticks, and that corresponds to once every 10 ms. That means the

TwinCAT 3 runtime itself is actually configured to “wake up” once per millisecond, and every 10

times it wakes up, it executes this Task. You can then configure what this Task does when it runs.

TwinCAT 3 has already done this by linkingPlcTask to the MAIN program in PLC1. That means

our program called MAIN in PLC1 will be executed once every 10 ms.

Configuring the Real-Time

All of the editing we’ve been doing in TwinCAT 3 XAE is a normal Windows program, and

Windows is a general-purpose operating system, not a real-time operating system. The runtime

component of TwinCAT 3 (called XAR) has to run under real-time conditions so that it won’t be

pre-empted by other tasks on the computer while it’s busy executing machine control logic.

The TwinCAT 3 runtime pre-empts normal Windows programs by running in “ring 0” execution

mode. In Windows there are only two execution modes: ring 0 and ring 3. Ring 0 is “kernel” mode,

Page 18: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

18

which is where all drivers and some internal windows code runs, and ring 3 is “user” mode, which

is where normal windows programs run.

The runtime registers a timer interrupt to force execution of the runtime at a regular interval (called

the clock tick). Interrupt routines run under ring 0, or kernel mode, so the runtime has access to

the full resources of the computer. The runtime then executes Tasks, and each Task can do things

like run PLC Programs, do Motion Control, interface with the EtherCAT I/O bus, or manage

communications with the HMI. These interrupts pre-empt ring 3 (user-mode) programs so the

runtime always gets priority over normal Windows programs, like the TwinCAT 3 editor or your

HMI.

To configure the real-time behavior of TwinCAT 3, double-click on the SYSTEM > Real-

Time node in the Solution Explorer (highlighted):

That will display the Real-Time configuration window:

Page 19: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

19

Notice that the “Available CPUs” shows only one CPU and it’s allocated to Windows. This is

actually incorrect, because my computer is a Core i3 with 4 logical cores. Click the Read from

Target button to make TwinCAT 3 read the actual configuration from the PC:

Page 20: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

20

Now it correctly shows 4 cores. The cores are listed in the top list box, numbered 0, 1, 2, and 3.

This configuration allows you to allocate cores between Windows, the TwinCAT 3 runtime, or both

(or neither). I have found that mixing Windows and TwinCAT 3 on the same core might cause

problems (at least in earlier versions of TwinCAT 3), so if you have a 4-core machine, I

recommend allocating 2 cores for Windows and 2 cores for TwinCAT 3 runtimes. Changing this

allocation requires a reboot, so save everything first.

Warning: only use the core isolation feature on Windows 10 if you have build 4020.0 or

later. The “core isolation” feature is not compatible with Windows 10 in earlier versions.

As far as I have been told, prior to build 4020.0, TwinCAT 3 won’t be able to see the cores

set to “Other” and the only way to fix it is to use the “Reset this PC” feature of Windows

10.

Page 21: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

21

If you have Windows 7, to change the core allocation, first click the Set on target button and the

following window will pop up:

Here, we just set the number of cores available to Windows, and the remainder will be available

to the TwinCAT 3 runtime. Change the number 4 to a 2, and then click the Setbutton:

Click OK to the confirmation dialog box:

Then click OK to the Reboot question (save and close everything except TwinCAT 3 first):

Page 22: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

22

Hang on while the system reboots. Once it has rebooted, open the TwinCAT 3 solution again

(either by double-clicking on the TwinCAT 3 Tutorial.sln file wherever you saved it, or by opening

TwinCAT 3 XAE again and selecting it from the Recent Files list.

Now double-click on the SYSTEM > Real-Time node in the Solution Explorer again, and the real-

time configuration window will look like this:

Page 23: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

23

Notice that CPUs 0 and 1 still say “Windows” but cores 2 and 3 now say “Other”. Now we want to

configure the TwinCAT 3 runtime to use cores 2 and 3. Under the RT-CPUcolumn, check the

checkboxes next to cores 2 and 3, and uncheck the checkbox next to core 0:

Notice that the CPU Limit column showed 80% next to core 0 when it was checked but shows

100% next to cores 2 and 3. That’s because when the TwinCAT 3 runtime has to share a core

with Windows, we have to limit how much CPU time it can use, so that Windows still has some

time to run user programs. However, when the runtime has a core all to itself, it can use to up

100% of the CPU time on that core.

The lower grid allows you to configure which tasks run on which core. Currently there are only 2

tasks: PlcTask and PlcAuxTask. The first is the task that was created when we created the PLC1

project. The second is an automatically created “housekeeping” task. For now, both are set to run

on core 2, which is a bit wasteful of core 3, but in most real applications there’s a good chance of

Page 24: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

24

using two real-time cores. If your application has any motion control, it’s typical to run the motion

control task on a separate core, and if you had more than one virtual PLC, you can choose to run

them on different cores to balance the load.

Next, check the Base Time columns in the grids above. By default, the TwinCAT 3 runtime “wakes

up” every 1 millisecond and checks if it needs to execute any Tasks. You can optionally change

the Base Time in the upper grid. 1 millisecond is actually the slowest option. I find that 0.5 ms

(500 us) works quite well for most PLC tasks. Note that this is much faster than you’ll be used to

if you come from a more traditional PLC brand.

In the lower grid, you can see that even though PlcTask is running on core 2 with a 1 ms base

time, it only runs once every 10 Cycle Ticks, so it effectively only runs once every 10 ms. I normally

set this to 1 Cycle Tick so the PLC task would run every time the runtime wakes up. Note that

your I/O refresh is tied to the speed of the Task using the I/O. That means if you set

your PlcTask to run every 1 ms, then any I/O mapped to that Task also has to refresh once every

1 ms. The EtherCAT I/O bus is actually fast enough to do this even with a rather large number of

devices on the bus.

To change the PlcTask so that it runs every 1 ms, double click on the SYSTEM > Tasks >

PlcTask node in the Solution Explorer and change the Cycle ticks from 10 to 1:

Page 25: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

25

Edit the PLC LogicAdd a Global Variable List

Let’s assume we’re going to program a grinding machine. Perhaps there’s a grinding wheel that

needs to be turned on and off via a motor starter. Let’s start by declaring some variables for our

PLC to interface with the outside world.

Under the PLC1 project, right click on the GVLs folder (highlighted):

From the context menu that appears, choose Add > Global Variable List…

A dialog box will appear where you can enter a name for your new Global Variable List. Let’s

enter a suitable name for this list, like Grinder:

Page 26: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

26

Then click the Open button. This will do 2 things: it will add a new Global Variable List in

the GVLs folder, and it will open the new Grinders GVL for editing. Your PLC1 project will now

look like this in the Solution Explorer:

Page 27: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

27

…and the main editing area will now have a window open for editing the GVL:

An empty Global Variable List isn’t very useful, so let’s add some variables. You’ll have to type

the following exactly correctly between the VAR_GLOBAL and END_VARlines:

Note that I’ve created 3 variables, all of which are of type BOOL. The first variable

(GrindingWheelMS) will represent the output for the motor starter that turns on the grinding

wheel. The grinding wheel will run as long as this variable is on. The other two variables represent

the inputs for start and stop pushbuttons (PBs) for the grinding wheel. We haven’t actually

declared them as real outputs or inputs yet, but we’re going to come back to that later.

A BOOL value in TwinCAT 3 can take on two values: TRUE and FALSE. These variables can

be used anywhere in the PLC program. It’s possible to create two Global Variable Lists that both

have a variable of the same name. In that case, if you used that variable name in your program,

when you tried to build your PLC project, the compiler will complain that the name is “ambiguous”.

In that case you will have to prefix the variable name with the name of your Global Variable List.

Due to this possibility, I always use the fully-qualified name (including the Global Variable List

name) whenever I use a global variable in my logic, and I will do that in the rest of this tutorial.

This is optional (and unnecessary) if you only have one GVL.

Adding a Ladder Logic Program

Now that we have some variables to control, let’s write some logic to control them. Right click on

the POUs folder in the Solution Explorer and choose Add > POU…

Page 28: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

28

In the Add POU dialog that pops up, enter a name for the new Program

(RunGrindingWheel), choose Program under Type, and at the bottom under

Implementation Language, choose Ladder Logic Diagram (LD):

Then click the Open button. Your new Program will show up under the POUs folder in the Solution

Explorer, and the Program will be opened for editing in the main window:

Page 29: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

29

The editing screen is split into two parts. At the top is your list of variable declarations. These are

similar to the variable declarations in a Global Variable List, but any variables declared here can

only be used within this Program. It’s a useful place to declare variables for storing temporary

values or helper coils. In order to write a simple start/stop rung, we won’t need to declare any

local variables.

The bottom part of the screen is where you write the ladder logic. The numeral “1” indicates that

it has already added a blank rung #1 for you. Let’s start by adding a coil to this rung. You can right

click on the rung and choose Insert Coil from the context menu, or you can click the coil icon on

the ladder logic toolbar at the top of the screen:

If you don’t see that toolbar, it might be hidden. Try right clicking on the blank toolbar spaces at

the top of the screen and selecting the TwinCAT PLC FBD/LD/IL toolbar.

A third option is to click on the Toolbox vertical tab on the far left of the screen and drag and drop

the coil icon from the toolbox over to the empty rung. Any of those options will result in a new coil

being added to your empty rung:

Notice that the coil has three question marks over it. This is where you have to enter the name of

the variable you want to assign to this coil. Type the fully-qualified name of the grinding wheel

motor starter variable into this field: Grinder.GrindingWheelMS:

Page 30: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

30

Now let’s add the start button. The easiest way is to click on the rung right where the small tick-

mark is in the center of the rung, and then right-click and choose Insert Contact from the context

menu. Now you will see a contact inserted on the left of the rung:

Then enter the fully-qualified variable name for the start push button:

Grinder.StartGrindingWheelPB:

That will turn the motor starter on when the start pushbutton turns on, but of course it won’t stay

on unless we add a seal-in circuit. To add a branch around the start button contact, select the

contact, right click, and choose Insert Contact Parallel (below) from the context menu:

Edit the three question marks over the new parallel contact and change it to

Grinder.GrindingWheelMS so that the coil seals itself in:

Page 31: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

31

Now the motor starter will stay on after the start pushbutton input turns off, but it will keep running

forever. Finally let’s add the stop pushbutton into the circuit as a normally closed contact. Select

the section of rung between the branch on the left and the coil on the right (where the small tick

mark is). Right click on this section of rung, and choose Insert Negated Contact from the context

menu:

Finally, replace the three question marks above the normally closed contact with the fully-qualified

variable name of the stop pushbutton: Grinder.StopGrindingWheelPB:

Finally, our start/stop circuit is complete! Remember to click the Save All button on the toolbar (it

looks like a stack of three floppy disks).Calling the Ladder Logic Program from MAIN

There’s one last thing we have to do before we can run our program. When the PlcTaskruns, it

will only execute the MAIN program, not our new RunGrindingWheelprogram. To get our

program to execute every 1 ms, we need to “call” it from the MAINprogram. Double click on

the MAIN program in the POUs folder of the Solution Explorer:

Page 32: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

32

That will open the program in the Structured Text editor:

This looks very similar to the RunGrindingWheel program when we first opened it, but in

this case, everything in the bottom is in Structured Text (ST) language instead of Ladder Diagram

(LD). If you want, you can delete the MAIN program and replace it with a new MAIN program

that uses Ladder Diagram. You have to remember to link thePlcTask to this new MAIN program

because when you delete the old one it will lose the link.

However, since the MAIN program’s only job is to call other programs (in our example), it’s simple

enough to do this in Structured Text. Change line 1 of the MAIN program to the following:

Page 33: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

33

That is, it’s just the name of the program you want to call, following by an open and a closed

bracket, and finally a semicolon. All statements in Structured Text end in a semi-colon. You could

add other program calls below this one on new lines if you wanted. The program will call them

once, in sequence, every time it runs (which is once every millisecond).

Downloading and Going OnlineBuild the PLC Project

It’s a good idea to “build” the PLC project before you try to download it. This will tell you if you

have any syntax errors or misspelled variable names. In the Solution Explorer, right click on

the PLC1 Project node and choose Build from the context menu:

This could take a minute while it compiles the PLC project. At the bottom of the screen, there’s a

tab called Output, and it will show you the progress of the compiler:

Page 34: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

34

Take a look at the last line to see if there were any errors. You want it to say “1 succeeded” and

“0 failed”. If it failed to build, you should look for error messages earlier in the output messages.

Double-clicking on the error message should take you to the site of the error, or close to it.

Activate the Configuration

Now that our PLC project compiled successfully, the next step is to “activate the configuration”.

What this does is:

1. Stop the TwinCAT 3 runtime (all running programs will halt, and I/O will turn off)

2. Apply the real-time configuration to the runtime (number of cores, tasks, etc.)

3. Copy the compiled program(s) to the runtime

4. Apply the “mapping” configuration (we will discuss mapping later)

5. Restart the TwinCAT 3 runtime in run mode

Note that when the runtime starts, it won’t automatically load the PLC program unless we “activate

it as the boot project”, and it won’t start executing the PLC program unless we configure it to

“autostart the boot project”. Do these two things by right clicking on the PLC1 node in

the Solution Explorer:

Page 35: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

35

…and then choose Activate Boot Project… from the context menu, and then do it again and

choose Autostart Boot Project.

Now go to the top menu and choose TwinCAT > Activate Configuration from the menu. You will

have to confirm this action with the following dialog box:

Click the OK button. Since we haven’t purchased a runtime license for this machine, it’s going to

ask us if we want to generate “trial licenses”:

Page 36: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

36

Click the Yes button. Then it will ask you to enter a captcha-type randomly generated group of

characters to prevent you from automating the generation of trial licenses. Enter the letters in the

text box underneath exactly as they appear (yours will be different than mine):

Then click the OK button. This generates a “trial” license which will expire after 7 days. Don’t

worry too much about exceeding this 7-day limit though, as you can just generate another 7-day

license when that happens. You will have to purchase an actual license only when you deploy to

a production system.

After generating the trial license, it will ask you if you want to restart in run mode:

Click the OK button. You should see the TwinCAT icon in the system tray (if it’s visible) turn from

blue (config mode) to red (stopped) and then finally to green (running). If that happens, the

TwinCAT 3 runtime is running in run mode on your PC!

If you look at the PLC1 node in the Solution Explorer window, you will also notice that it now

displays a green rectangle, indicating that the PLC program is running:

Page 37: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

37

Go Online

Like most other modern PLCs, TwinCAT 3 supports online debugging. To see this in action, start

by double-clicking on the RunGrindingWheel program so that it’s open in the editor’s main

window. Then select PLC > Login from the main menu:

That puts the system into “online” mode and you can view the current state of the logic. Power is

represented by the blue lines, and the status of the contacts and coils are represented by the blue

rectangles in the middle of each. As you can see above, the

Grinder.GrindingWheelMS coil is currently off, as are the start and stop pushbuttons.Write Values to Variables (Online)

Even though the pushbuttons and motor starter variables aren’t connected to anything physical,

the code is running. We can watch the state of the motor starter variable change by writing new

values to the pushbutton variables. Let’s start by simulating someone pressing the start

pushbutton. To do that, we’re going to write the value TRUE

to Grinder.StartGrindingWheelPB. The first step is to queue a value to write. Hover

your mouse over the center of the Grinder.StartGrindingWheelPB contact and

double-click the small rectangle in the center:

Next to the contact you can now see a queued write value (“TRUE“). You can change the queued

write value to FALSE by double-clicking it again, and remove the queued write value entirely by

double-clicking it a third time. When you’re ready to write the queued values to the PLC variables,

Page 38: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

38

choose PLC > Write values to all online applications from the drop-down menu at the top of the

screen. You will immediately see the state of the rung change to reflect the new value:

As you can see, simulating the start pushbutton turning on caused the logic to turn on

the Grinder.GrindingWheelMS motor starter variable. Now let’s simulate releasing the

start pushbutton by writing the value FALSE to the start pushbutton:

…and write the values by choosing PLC > Write values to all online applications from the drop-

down menu at the top of the screen:

You can see that the motor starter variable (coil) stayed on. To finish the example, let’s simulate

someone pressing the stop pushbutton by writing the value TRUE to the

Grinder.StopGrindingWheelPB contact:

Page 39: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

39

Writing the value then turns off the motor starter coil:

Writing a value to a variable is a one-time operation, so if you tried to write the valueTRUE to the

motor starter coil, it won’t appear to have any effect, because 1 millisecond later, the program

logic will execute and change the value back to FALSE. If you want to override the logic, you

need to use the “force.”

Force Variables (Online)

Forcing a variable is similar to writing a variable, except that the force overrides the logic of the

program. For instance, we can force the motor starter coil to TRUE even though the logic is trying

to keep it turned off. Start by double-clicking on the small rectangle in the middle of the motor

starter coil:

You can see that we’ve queued the value TRUE, just like if we were going to do a variable write.

However, in this case we’re going to choose PLC > Force values to all online applications from

the menu at the top of the screen:

Page 40: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

40

Forced variables are indicated with a small “F” in a red box next to the contact or coil that is forced.

These variables will stay forced until you choose PLC > Unforce all values on all online

applications from the menu at the top of the screen. You will also be asked if you want to unforce

existing forces if you logout (go offline).

Using the TwinCAT PLC Toolbar

We’ve been going through the PLC menu at the top of the screen for all of the online functions,

but there is also a toolbar that can give you quick access to these functions. It looks like this:

If you don’t see it, try right right-clicking on the toolbar area near the top of the screen and making

sure that the TwinCAT PLC option is checked. If it’s already checked and you can’t see it, then

you might have too many toolbars on one line, and that would force it to shrink the toolbar to

almost nothing, like this:

If that’s the case, drag and drop the shrunk toolbar onto a new empty toolbar line and it will expand

fully for you.

Making Online Changes to PLC Programs

TwinCAT 3 supports online changes to PLC program logic and variables.

TwinCAT 3 does not support online changes to the mapping between your PLC program and

your physical I/O, so if you want to add or remove I/O, or you want to change the mapping between

your PLC I/O and your physical I/O, you have to use TwinCAT > Activate Configuration from the

top menu, and you will then have to restart the runtime, which will restart your PLC programs.

This will cause your machine to stop.

The process for making an online change to a PLC program is:

1. Make sure you are offline (logged out).

2. Edit your PLC program logic and variables.

Page 41: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

41

3. Login (at the prompt, select Login with online change).

4. If you’ve edited a variable (for instance, changed the type), it will warn you that the variable

will be moved to a new memory location, and ask if you want to continue. This is safe to do

unless you have an external program (like an HMI or a data collection system) reading or write

variables in the PLC, in which case you may want to shut them down before continuing.

5. Click Yes when it asks if you want to “update the boot project” (if you say No then your changes

will be lost if you restart the runtime or reboot the computer).

Your changes will be applied without stopping the PLC program, and without any interruption to

the machine.

Now let’s make a change to our logic. Stop buttons on machines are sometimes wired using a

normally closed contact instead of a normally open contact (so the input is on when nobody is

pushing the button and the input turns off when someone pushes it). The reason for this is so that

the machine will stop if the wiring to the stop button is disconnected (and it also allows you to

chain stop buttons in series). Let’s change our grinding wheel start/stop logic to work with a

normally closed stop pushbutton. First, make sure that you are offline. Choose PLC > Logout from

the menu if you haven’t done so already. Here is the original logic:

Left-click to select the contact in the middle (Grinder.StopGrindingWheelPB):

TwinCAT 3 has a handle shortcut for changing a normally closed contact to a normally open

contact, or vice-versa: just press the slash key (“/”). Now your logic will look like this:

Page 42: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

42

Login to the PLC by choosing PLC > Login from the menu. TwinCAT 3 will notice that the logic

has changed and will ask you what you want to do:

Leave Login with online change selected and click OK. Next it will ask you if you want to update

the boot project:

Click Yes. After that you will be online with the PLC, and your changes will be applied:

Page 43: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

43

Notice that the Grinder.StopGrindingWheelPB contact is now a normally open

contact, meaning the input has to turn off to stop the grinding wheel.

Designating PLC Inputs and Outputs

When we built our example PLC program above, we didn’t actually define which variables were

inputs and which were outputs, so as far as the runtime was concerned, all of the variables were

internal variables and the PLC didn’t interact with any physical I/O.

Designating a variable as an input or output only makes it available for mapping to a physical

input or output during the mapping phase (after we’ve configured our I/O devices, which will be

covered later).

We designate a variable as an input or output by modifying the variable declaration. Here is the

original list of variables in the Grinder Global Variable List:

First let’s designate the GrindingWheelMS variable as an output. Change the variable

declaration to the following:

Page 44: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

44

As you can see, I’ve added AT %Q* directly after the variable name. The Q means output. You

can designate the other two variables as inputs like this:

This is the same, but I’ve replaced the Q with an I meaning “input.” I know this is a weird syntax,

but it’s a throwback to processors where you could (or had to) explicitly map variables to an input

or output memory area. In this case, the asterisk means “I don’t care where you put it, just put it

in the input or output map accordingly.”

Now you have to “build” the PLC project to see these variables show up on the PLC’s input or

output map. Right-click on the PLC1 Project node in the Solution Explorer and choose Build from

the context menu:

After the project is compiled, you will now see two new nodes under the PLC1 Instance node:

Page 45: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

45

Expand the inputs and outputs by clicking on the small triangle next to each node:

Now you can see the variables that you designated as inputs and outputs are available here for

mapping. Normally you will map these variables to physical I/O, but they can also be mapped to

I/O in the motion control task, the Safety program, or even to I/O from another PLC. That’s how

you would be able to interlock two PLC programs together virtually without running any physical

wiring. The runtime takes care of copying the data between each task, or between tasks and

physical I/O.

Page 46: TwinCAT tutorial - Beckhoff

Chapter 2: Quick Start

46

Beware that if you change the name or type of a variable that has been designated as an input or

output, TwinCAT 3 will unmap that variable from any existing mappings, and will only give you a

rather easy-to-miss message in the output window when the PLC project builds. When you

activate the configuration and restart, you may be left wondering why your output won’t turn on,

or why your start button isn’t working. Note that when you’ve mapped a variable, the icon will

change to include a small arrow, so you can tell which ones are mapped:

(All I did here was map the PLC output Grinder.GrindingWheelMS back to PLC

input Grinder.StartGrindingWheelPB, which is a rather silly thing to do in real life.)

Whew! That concludes the Quickstart chapter of this tutorial! You’ve now covered all the basics

of downloading, installing, configuring, programming, going online, forcing, and making online

changes to your PLC program in TwinCAT 3. That’s a lot of new information, but I hope it gives

you a good enough introduction to feel like you can experiment and play around with the system

some more. When you’re ready, you can move forward in the tutorial with more advanced topics.

Page 47: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

47

3 – Structuring PLC DataTwinCAT 3 gives you a lot of options about how to organize the data (whether Boolean, integer,

floating point, string, or user defined data) in your PLC project. As with any PLC project, you want

your data structures to follow naturally from the actual machine it’s controlling. I think we can all

agree that Infeed.Conveyor.Running is easier to understand than bit[328].

Global vs Local Variables

If you’re coming from the RSLogix 5000 world then you’re probably already familiar with the

concept of global vs. local variables. A ControlLogix has “controller scope” and “program scope”

tags. Essentially “controller scope” tags are global (because every program can see them) and

“program scope” is local because only the program they’re defined in can see them. It’s also pretty

common to come across PLC projects where all the tags are declared in “controller scope”.

TwinCAT 3 also has global variables and local variables. Global variables live in “Global Variable

Lists” and Local variables live inside programs, function blocks and functions (a.k.a “POUs”). As

for what should go where, that’s ultimately your decision, but here are the rough guidelines I use:

Inputs and Outputs: Global

HMI Buttons and Indicators: Global

HMI Alarms: Global

Faults: Global

Interlocks: Global

Everything Else: Local

What do I mean by “Interlocks”? When you divide up your program, you typically divide it into

modules that physically match your machine, so you might have a program for the Infeed

Conveyor, one for the Pick & Place, and one for a Robot. Most of your Infeed Conveyor logic

shouldn’t care about the Pick & Place logic, except that you might want to prevent the Infeed

Conveyor from running if the Pick & Place isn’t out of the way of the conveyor. In that case, you

probably want to create a global PickAndPlace.ClearOfInfeedConveyor variable.

Page 48: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

48

The Pick & Place logic is responsible for setting this using a coil, and the Infeed Conveyor logic

uses it to prevent running the conveyor. This is an “interlock” signal and since two different

programs need to access it, then it’s a good candidate for a global variable.

You’ll also notice that everything accessible from an HMI is also global. This is just my preference.

You don’t have to do that… an HMI can access local variables, but since an HMI-accessed

variable is kind of like an interlock, I like to give them Global status. When someone is reading or

modifying your PLC logic they’ll tend to make the assumption that Local variables are only

accessed, well… locally. That means when they want to make a change, they’ll only check for

usages of the variable within that local scope. Checking the HMI for references to that variable is

much harder. That’s why I suggest making HMI-accessed variables Global and also indicating

with a comment that they are accessed from the HMI.

Global Variable Lists

When you created your PLC project in TwinCAT 3, the wizard automatically creates a

GVLs folder:

“GVL” stands for “Global Variable List.” It’s Beckhoff’s intention that you should put all your global

variable lists in the GVLs folder, but I think that’s incorrect. The organization and structure of your

PLC project should mimic the physical and logical organization of your machine. Let’s continue

Page 49: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

49

our example of a machine with an infeed conveyor followed by a pick & place. If those are two

major components of the machine, then that’s a logical way to break down our logic and data.

Let’s create two folders under our project called InfeedConveyor and PickAndPlacerespectively.

Start by right clicking on the PLC Project node and selecting Add -> New Folder from the context

menu. That will create a new folder called NewFolder1 and you can change the name

to InfeedConveyor:

You can do the same to add a PickAndPlace folder:

I want to stop here and talk about folder ordering. First of all, TwinCAT 3 will order the folders and

items in a folder alphabetically. Secondly, TwinCAT 3 has a bug where it will only re-order them

after you save your project, close the solution and open it again. Initially they show up based on

the original folder name, which was NewFolder1 (hopefully this is fixed in a later version). Since

the folders represent the major components of our machine, we want the ordering to flow logically

Page 50: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

50

from the way the machine works. If the machine works by parts being introduced to the machine

on the infeed conveyor, and are then processed by the pick & place, then it makes the most sense

to have the InfeedConveyor folder come before PickAndPlace folder. It just so happens that “I”

comes before “P” in the alphabet, but this is only a happy coincidence. That’s why I suggest

prefixing each folder with a number so you can enforce a logical ordering:

I used two digit numbers because that will allow it to sort correctly from 01 through 99. Don’t worry

about needing to insert one later as renumbering even 10 or 20 folders doesn’t take very long.

Now let’s add a Global Variable List for the infeed conveyor. Right click on

the01_InfeedConveyor folder and select Add -> Global Variable List… from the context menu.

That will display the Add Global Variable List dialog. Enter InfeedConveyorin

the Name text box and click the Open button:

Page 51: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

51

Now you’ll see an empty global variable list in the editor:

Now we have to imagine some global variables that our imaginary conveyor might have. I think it

needs an output to turn on the motor (we’ll call that “Run”), a through-beam sensor across the

end of the conveyor to sense when a part reaches the pick position (we’ll call that the “NoPart”

input) and maybe this conveyor needs to signal to the pick & place that the conveyor is stopped

(we’ll call that the “Stopped” interlock). Here’s what a global variable list with those 3 variables

would look like:

Page 52: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

52

Let’s look at the first variable:

NoPart AT %I* : BOOL; (* Through-beam sensor *)

The line starts with the variable name (NoPart). Typical variable naming rules apply, so you

can’t start the name with a number, and you can’t have spaces or special characters in the name,

except an underscore. Here I’ve used the PascalCase way of writing the variable, by sticking

capitalized words together. You don’t have to use PascalCase, but you should pick a consistent

way of naming your variables and stick with it.

After the variable name is an optional element that defines the variable as an input or output (AT

%I*). The “I” means input and a “Q” means output. Adding this element will force the variable to

show up in the PLC project’s I/O map which makes it available for linking to physical I/O. Inputs

are set by the input scan before your logic runs, and outputs are set by your logic, but copied to

the physical outputs during the next output scan. (Technically the input and output scans happen

at the same time.)

A colon (:) separates the declaration of the variable from the variable type (BOOL). Here are the

common types you’ll use, though there are more:

BOOL: A boolean value (TRUE or FALSE)

BYTE: An 8-bit unsigned integer value (0 to 255) aka USINT

SINT: An 8-bit signed integer value (-128 to 127)

WORD: A 16-bit unsigned integer value (0 to 65,535) aka UINT

INT: A 16-bit signed integer value (−32,768 to 32,767)

DWORD: A 32-bit unsigned integer value (0 to 4,294,967,295) aka UDINT

DINT: A 32-bit signed integer value (−2,147,483,648 to 2,147,483,647)

ULINT: A 64-bit unsigned integer value (0 to 18,446,744,073,709,551,615), new in TwinCAT

3

Page 53: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

53

LINT: A 64-bit signed integer value (-9,223,372,036,854,775,808 to

9,223,372,036,854,775,807), new in TwinCAT 3

REAL: A 32-bit floating point value

LREAL: A 64-bit floating point value

STRING: An ASCII null-terminated string, default 80 characters

STRING(n): An ASCII null-terminated string, with max length “n” characters

TIME: A 32-bit time value (typically used in timers)

A note on how much memory each one takes: for anything that says “8-bit”, “16-bit”, “32-bit” or

“64-bit” then the memory usage is 1 byte, 2 bytes, 4 bytes, or 8 bytes respectively. A BOOL could

be represented by a single bit, but takes a full byte (this is for performance reasons). There is also

a rarely used data type called BIT which uses a single bit, and can have the values 0 and 1, but

this is not equivalent to a BOOL. ASTRING takes 81 bytes (80 for the characters and 1 for the

null terminator) and a STRING(n) takes n+1 bytes.

Beckhoff’s Infosys site has more information about TwinCAT 3 Data Types.

After the data type, there is a terminating semi-colon (;). The compiler stops reading here.

After the semi-colon you can put an optional comment ((* Through-beam sensor *)).

There are two ways to insert comments: you can enclose them in bracket-asterisks, as I did, or

you can preface them with a double-slash (//). Note that comments will show up as tool-tips

when you hover your mouse over any use of this variable in your logic, so they are helpful.

The other thing that shows up in a tool-tip over a variable is the data type (BOOL, INT, etc.).

Long before we had helpful features like this in our IDE, programmers invented a system for

embedding the type of the variable in the variable name called Hungarian Notation. You will often

see TwinCAT code examples, even in the official documentation, that use Hungarian Notation

(such as bIsTRUE : BOOL;). This is now discouraged thanks to smarter and more helpful

editors. In general you should avoid Hungarian Notation in your TwinCAT 3 programs.

When you refer to global variables in your logic, you should use the fully qualified name of the

variable (including the Global Variable List name) such as InfeedConveyor.Run.

Technically it will let you omit the Global Variable List name and just use the variable name (Run).

This is a throw-back to TwinCAT 2 where there was only one Global Variable List. You don’t want

Page 54: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

54

to run into a situation where it gets confused with another global variable called Run in another

Global Variable List (OutfeedConveyor.Run would be an obvious one). If you happen to

do this, the compiler will give you an error telling you that you have an ambiguous variable name

and it will force you to use the fully qualified name. I suggest using fully qualified names for all

global variables.

Adding Structure(s)

A conveyor is a pretty simple piece of equipment and I doubt you’d want to break up the structure

of the InfeedConveyor Global Variable List into smaller pieces, but for a larger part of the

machine you might want to. For instance, the pick & place might have two positions (Pick and

Place). Rather than having variables such asPickAndPlace.InPickPosition it’s a bit

nicer to havePickAndPlace.Pick.InPosition (I realize some of the reasons why it’s

nicer might not be obvious at this point, but trust me).

You can accomplish this with TwinCAT 3’s Structure, which is a kind of Data Unit Type, a.k.a.

“DUT”. In the RSLogix 5000 world, this is called a “UDT” meaning “user defined type.” Let’s start

by creating a Structure for the pick & place’s Pick position. Begin by right clicking on the

02_PickAndPlace folder and selecting Add -> DUT…, which will open the Add DUT dialog

window. Enter PickAndPlace_Pick in the Name text box. Make sure the Structure radio

button is selected in the Type group box. Then click the Open button:

Page 55: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

55

That will generate a new (empty) structure in the editor window:

Any variables we add here will show up prefixed with PickAndPlace.Pick in their fully

qualified name. Add an InPosition variable like this:

Page 56: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

56

Now let’s create a new PickAndPlace Global Variable List. Right click on the

02_PickAndPlace folder, use Add -> Global Variable List… and after creating it, add this variable:

Now we’ve created a variable called PickAndPlace.Pick.InPosition. Note that you

can also define inputs and outputs inside your structure (by adding AT %I* orAT

%Q* respectively).

Arrays

It’s often advantageous to declare a numbered list of variables, and for this we traditionally use

arrays. TwinCAT 3 has full support of arrays of any type. The syntax for declaring an array variable

looks like this:

To use this array in your logic, it would look like this:

ExampleGlobalVariableList.SomeArrayVariable[1]. That would be the

first element. The last element in the array would be

ExampleGlobalVariableList.SomeArrayVariable[10].

Note that the syntax ARRAY[lower..upper] OF allows you to define any lower or upper

bounds you like. Unlike a language like C, your lower bound doesn’t have to be zero. The total

number of elements in the array is upper - lower + 1.

You can also define multi-dimensional arrays like this:

Page 57: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

57

You could access the first element of the array like this:

ExampleGlobalVariableList.SomeArrayVariable[1,50] and the last

element asExampleGlobalVariableList.SomeArrayVariable[10,55].

Caution: when some programmers graduated from RLogix 500 to RSLogix 5000 and were first

introduced to tags, they just didn’t “get” it. They wanted their nice safeB3:0/5 or whatever.

Since the concept of tags was confusing, they just created a big controller-scope array tag

called bits and used bits[0], bits[1], bits[2], etc. in their logic. It was a bad idea,

but doing the same thing in TwinCAT 3 is a really bad idea. At least in RSLogix 5000 you could

add a useful comment for each element of the array but there is no method of doing that in

TwinCAT 3 (because you’re not supposed to do that anyway). Your TwinCAT 3 program would

be completely incomprehensible.

I would also like to caution you against using arrays for things that aren’t naturally numbered, or

which aren’t a good fit for fixed numbers. For instance let’s say you have an auto sequence of

steps like:

1. Run conveyor to sensor

2. Pick part from conveyor

3. Place part on fixture

4. Return pick & place to home

Perhaps you would like to implement that auto sequence using the Step pattern. Good idea. Now

you might think you want to create an array of variables for your steps, such

as PickAndPlace.AutoSequence.Step[1]. Having done this in the past, I now think

it’s a bad idea. It’s very common to have to modify your auto sequence to add or remove steps in

the future. What if between your pick and place step you wanted to add a measurement step? It’s

not so easy to add a step 2.5 when you’ve declared them as an array, and renumbering the steps

in all your logic can be a real pain. I think it’s better to create variables

like PickAndPlace.AutoSequence.PickStepand PickAndPlace.AutoSeq

uence.PlaceStep. Then you can add and remove steps, or even re-order them, without

cursing yourself later.

Page 58: TwinCAT tutorial - Beckhoff

Chapter 3: Structuring PLC Data

58

Conciseness and Readability

You might notice that some of my variable names above end up very long. I agree that’s a

problem. On the one hand, since the comments for each variable only show up if you hover your

mouse over the variable, you shouldn’t make your variable names too cryptic or it will be hard for

someone to understand your logic. On the other hand, if you make them too long, you can’t fit

enough of your ladder logic on the screen at once, and the logic itself becomes difficult to follow.

There’s a trade-off to make in conciseness vs. readability.

I suggest making short codes or acronyms for your top level machine elements. For example, you

could abbreviate InfeedConveyor as IFC and PickAndPlaceto PP. When someone

looks at your PLC project, they will still see 01_InfeedConveyor and 02_PickAndPlace as two

folders, and inside each there will be Global Variable Lists IFC and PP respectively. Since you’re

only doing this at the top level of your program, and it’s fairly easy for someone to see the

relationship of the full name to the abbreviation, I think this is a good way to shorten your fully

qualified variable names without sacrificing much readability.

For the lower level variable names, make an effort to pick names that are still concise but convey

the meaning to the reader. Yes, it takes practice and there’s no perfect answer, but effort really

counts here.

Page 59: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

59

4 – Persistent VariablesTraditional PLCs offer various forms of data persistence (so your PLC memory isn’t lost when the

PLC loses power). Some use battery-backed SRAM, which maintains power to all or a portion of

the memory even when power is removed from the PLC. Some use flash-memory technologies.

Since TwinCAT 3 runs on PC hardware, and PC memory isn’t battery-backed up, it has a different

mechanism for storing persistent variables.

By default, variables in TwinCAT 3 are not persistent. If you restart the runtime, or your PC loses

power, then when your PLC program restarts the variables will revert to their default state. This

is fine for many situations (we generally want our outputs to start in the off state anyway).

However, there are times when we need to store machine state such as part presence or process

information and we don’t want to lose it when the PC reboots. TwinCAT 3 allows you to explicitly

define variables that are “persistent” for this purpose.

In this chapter I’ll explain how to create persistent variables, how TwinCAT 3 saves and restores

them, and how to write a program to periodically save them to a file in case of a crash or power

loss.

Declaring Persistent Variables

For this example, I created a new Global Variable List called “PersistentExample”. Note that

persistent variables don’t need to be global (they could be defined in a program or a function

block, for example). By default a Global Variable List only has one section of declarations

(called VAR_GLOBAL):

Anything declared inside of the VAR_GLOBAL section is not persistent. However, we can

declare a section to be persistent like this:

Page 60: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

60

Adding the PERSISTENT keyword after VAR_GLOBAL tells TwinCAT 3 that any variables in

this section should be written to persistent storage when the runtime shuts down, and restored

from persistent storage when the runtime restarts (the data is actually stored to a file on disk, and

I’ll explain more about the details later). I’ve declared two integer variables (one persistent and

one non-persistent) as examples, and I’ve initialized them with default values (5 and 3

respectively).

Now let’s login to the PLC, and apply this as an online change. You will now see an online view

of this Global Variable List:

As you can see, both variables have been created in the PLC, and they have the values 5 and 3,

respectively. Now I’m going to write new values to them. In the Prepared value column, enter the

values 10 and 6, like this:

Page 61: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

61

Now choose PLC > Write values to all online applications to overwrite the variable values in the

PLC memory:

As you can see, the values have changed. Now we’re going to restart the runtime and see what

happens. First choose PLC > Logout from the menu and then choose TwinCAT > Restart

TwinCAT System to initiate a runtime restart. You will be asked to confirm:

Click OK to restart in Run Mode. You will see the TwinCAT icon in the system tray briefly turn red,

and then turn back to green. When that’s done, go back online by choosing PLC > Login from the

menu:

You can see that the persistent variable retained the value of 10 that we wrote to it, but the non-

persistent variable reverted to the initialization value of 3 that we defined in the variable

declaration. The written value of 6 was lost.

Page 62: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

62

How Persistence Works

TwinCAT 3 stores the persistent variables in a file on the computer’s hard drive. The sequence of

when this happens is critical to understand to prevent you from losing your valuable persistent

data. When you shut down the TwinCAT 3 runtime, here is how it saves your variables:

1. The PLC program stops executing

2. The runtime reads all of the persistent variables and writes them to a file

3. The runtime stops

The persistent data file is stored in the C:\TwinCAT\3.1\Boot\Plc folder, and it will be

named Port_851.bootdata. Each PLC instance on a runtime has an “ADS port number”.

The default port numbers start at 851. You can change the port number, but that’s not important

right now.

When TwinCAT 3 restarts, here is how it restores your variables:

1. The runtime loads the PLC program (including the default values of all variables)

2. The runtime looks for a persistent data file on the hard drive, and if it finds one, it restores the

saved values to the PLC variables

3. It deletes the persistent data file from the hard drive

4. The PLC program starts executing

If, for some reason, the runtime can’t find the persistent data file, then all of your persistent

variables will be reset to their default values. This is actually very easy to demonstrate because if

the runtime doesn’t get a chance to shutdown cleanly, it won’t write the persistent data file to the

hard drive. Here are some ways that this can happen:

The PC loses power without doing a Windows shutdown

The runtime crashes (this shouldn’t happen, but it can happen)

The persistent data file can become corrupted

The recommended way to install a TwinCAT 3 system is to use a PC with some kind of battery

backup, like an Uninterruptible Power Supply (UPS). You must connect the PC to the UPS (via

USB cable, etc.) and use the software provided with the UPS to make sure that Windows does a

Page 63: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

63

clean shutdown if the UPS loses power for any appreciable length of time. However, this isn’t

good enough.

Protecting Your Persistent Data

Even if you use a UPS to provide Windows enough time to do a clean shutdown, there are still

many ways that you can lose your persistent data (such as the UPS failing, the hard drive failing,

or a runtime crash). Unless you can afford to lose the persistent data, you must do more to protect

it.

While your PLC program is running, the only up-to-date copy of your persistent data is the actual

values of the variables in your PLC memory. The persistent data file is only written when the

runtime shuts down cleanly, and it deletes the persistent data file after reading it when the runtime

restarts.

The first step in protecting your persistent data is to periodically write out the persistent data to

disk while the program is running. Thankfully TwinCAT 3 provides a function block

called WritePersistentData that does exactly this. You can manually instruct the

runtime to write out the persistent data file from within the PLC program.

Create a Save Persistent Variables Program

In this section I’ll describe how to write a generic program that handles saving the persistent

variables regularly while your program is running. This program will make use of a function block

called WritePersistentData that’s provided with TwinCAT 3 in the Tc2_Utilities library.

This library wasn’t referenced automatically when you created the PLC program, so you’ll have

to add a reference before you can use it. Start by finding the References folder under the PLC1

Project:

Page 64: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

64

There are already 4 libraries referenced. These references were added when you created the

project, and they provide some basic function blocks like timers, counters, and string functions.

To add a reference to the Tc2_Utilities library, right-click on the References folder and

choose Add Library… from the context menu. That will display the Add Library window:

Now click on the System node (highlighted above) to expand it so you can see the system

libraries:

Page 65: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

65

Click on Tc2_Utilities (highlighted above) and then click the OK button. The window will close and

you will now see the Tc2_Utilities library added under the References node in the Solution

Explorer:

If you want to see a list of what is contained in a library, double-click on that library name

in Solution Explorer and it will open the Library Manager window:

Page 66: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

66

Select a library in the top portion of the window, and you’ll be able to browse the contents of the

library in the bottom left corner. With the Tc2_Utilities library selected, expand the POUs folder

on the bottom left and expand the TwinCAT PLC folder beneath that to find

the WritePersistentData function block. Click on the name of the function block to

display a picture of the block on the right side:

Page 67: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

67

You can click on the Documentation tab above the picture of the block to see more detailed

information about the inputs and outputs:

Page 68: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

68

Now we’re going to create a new Program POU called “SavePersistentVariables.” Start by right-

clicking on the POUs folder in the Solution Explorer and choosing Add > POU…from the context

menu. Enter the program name in the Add POU window and choose Program as the type

and Ladder Logic Diagram as the implementation language:

Page 69: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

69

…then click Open to create the program. You will now see the empty

SavePeristentVariables program in the ladder logic editor:

Page 70: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

70

Since WritePersistentData (from the library) is a function block, that means it has local

state (stored in internal variables) and we have to allocate space for those state variables by

declaring an “instance” of the function block as a variable in our program. (Note that the main

difference between a Function and a Function Block is that a Function doesn’t have any internal

state, so you can use a Function without first declaring an instance of it as a variable). If you’ve

ever used a timer or a counter function block in another PLC, you will be familiar with the idea of

allocating memory space for each instance of a timer or counter. Declare the function block

variable as a local variable of the program, like this:

This is just like declaring a BOOL variable. WritePersistentDataFB is the name of the

variable, and WritePersistentData is the type. In this case, the type is a data structure

that the function block needs to store its internal state.

To add the function block to rung 1, right click on the rung and choose Insert Empty Box… from

the context menu. Your rung will now look like this:

The three question marks inside the box are where you type the function block name, and the

three question marks above the box are where you type the variable (or “instance”) name. Go

ahead and type WritePersistentData inside the box and

WritePersistentDataFB above it:

Page 71: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

71

The editor recognizes the function block name and automatically adds the required input and

output parameters to the block. We can start by filling in the NETID, PORT, and TMOUT inputs

like this:

The NETID is an empty string (just two single quotes), which means that the AMS Net ID is the

local target (i.e. the PC it’s currently running on). The PORT is 851 because that’s the ADS port

number of the PLC1 program.

The TMOUT input is set to 10 seconds, which is a maximum time we will allow for the operation

before the function block will abort and set the ERR and ERRID outputs. Notice the format we

used for specifying the time. This is a syntax defined in the IEC 61131-3 standard. It allows you

to specify durations like this:

T#10ms (10 milliseconds)

T#5000ms (5000 milliseconds, or 5 seconds)

T#5s (5 seconds)

T#1m (1 minute)

T#1h30m (1 hour and 30 minutes)

T#2d1h (2 days and 1 hour)

Page 72: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

72

The WritePersistentData function block initiates a save of the persistent data when

the START input transitions from off to on (also called a “rising edge”). The BUSY output will turn

on immediately and will stay on until an acknowledgement has been received (or the timeout

expires). When the BUSY output turns off, if there was an error then the ERR output will turn on

and the ERRID output will indicate the ADS error number (do a Google search for “ADS Return

Codes” to see the list).

Ideally we should save the persistent variables immediately after the PLC program starts, and

then at regular intervals after that. We’re going to use two on-delay timers, one to trigger the initial

write and a second to trigger the subsequent writes. TwinCAT 3 includes an on-delay function

block called TON. It’s included in the standard library, which is already referenced. First you have

to create the variables to hold the timer instance data:

Declare two timers called StartTON and IntervalTON. Now we want to insert a rung to

put the first timer on (note that in TwinCAT 3, rungs are called Networks because the ladder

diagram editor is also the function block diagram editor, and function block diagram programs are

divided into networks). Right-click on the existing rung and choose Insert Network from the

context menu. The existing rung will become rung 2, and a new blank rung will be inserted as

rung 1:

Page 73: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

73

Now right-click on rung 1 and choose Insert Empty Box from the context menu. Inside the new

box, where the three question marks are, type TON and press enter. Then where the three

question marks are above the box, type StartTON and press enter. Your rung #1 should look

like this:

The IN input of the timer controls when the timer is timing. The PT input is the “preset time” which

is how long the timer will run before turning on the Q output. The ET output gives you the current

“elapsed time” which is how long it has been since the IN input turned on. We want this timer to

start timing right away, so replace the three question marks above the contact that feeds

the IN input with the constant value TRUE. We want this timer to fire very shortly after the PLC

starts, so enter the time constantT#1ms in place of the three question marks next to

the PT input. That will make the timer Q output turn on one scan after the PLC starts (I did this to

make sure the WritePersistentData function block will see a transition from off to on).

Since we don’t need the ET output, right click on the timer and choose Remove unused FB call

parameters from the context menu. That will remove both the Q and ET outputs. Now your timer

rung should look like this:

The timer’s output Q will transition from FALSE to TRUE 1ms after the program starts. Use the

output to drive the WritePersistentData function block’s START input (highlighted):

Page 74: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

74

Now we need to create another timer that will fire repeatedly after a certain interval. Insert another

rung (between rungs #1 and #2), add another TON function block, this time

using IntervalTON as the timer instance. Set the preset time to 1 minute. We want this timer

to start after the initial startup timer is complete, and we want it to reset every time

the WritePersistentData function block is busy:

Now modify rung #3 (the WritePersistentData rung) so that both timers will trigger the

function block’s START input. Begin by selecting the StartTON.Q contact:

Now right click on the contact and choose Insert Contact Parallel (below) from the context menu:

Page 75: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

75

Then replace the three question marks over the new parallel contact with the output of the second

timer (IntervalTON.Q):

Finally, there remains one problem. The StartTON.Q variable will be on almost all the time,

but we’re only interested in the rising edge. TwinCAT 3 allows you to change a contact from a

normally open contact into a rising edge or falling edge (also called “differentiate up” and

“differentiate down”) by selecting the contact and pressing the “P” or “N” keys, respectively. Select

the StartTON.Q contact and press the “P” key:

Page 76: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

76

Finally, we don’t want to use the ERR and ERRID outputs. Rather than making them disappear,

just delete the three question marks next to those outputs and the compiler will just leave them

disconnected for now. Eventually you should consider using the ERR output to create an alarm

to notify someone that there’s a problem saving the persistent variables. Here is the complete

program:

Page 77: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

77

Here’s how it works:

1. The StartTON timer fires almost immediately when the PLC starts, and that gives a single

pulse to the START input.

2. During the initial write process, the BUSY output is on, which prevents IntervalTON from

running.

3. When the initial write is complete, BUSY turns off and then IntervalTON starts running.

4. One minute later the IntervalTON timer completes and turns on the Q output. This

triggers the START input again, which makes the BUSY output turn on, which

resets IntervalTON until the write is complete.

5. When the write completes, we go back to step 3.

Page 78: TwinCAT tutorial - Beckhoff

Chapter 4: Persistent Variables

78

Before this program will run, you have to call it from your MAIN program. The Quick Start chapter

explained how to call other programs from your MAIN program so I won’t repeat that here.

Remember to login with an online change to start running this new program.

When you execute this code, you can watch the C:\TwinCAT\3.1\Boot\Plcfolder and

see the Port_851.bootdata file created, and then see the file timestamp update every

minute after that.

Now we have a copy of the persistent data on the hard drive that is at most about 1 minute old.

That will protect us if there is a runtime crash or the UPS stops working but it still doesn’t protect

us from a corrupt file or a hard drive that stops working. To make sure you can recover from these

scenarios, you need to make backups of the persistent data file at regular intervals to another

computer.

To restore a backup of your persistent data file:

1. Restart your runtime in Config mode

2. Replace the .bootdata file with the backed up one

3. Restart your runtime in Run mode

Note that in the event of a crash, the restored persistent variables might be up to 1 minute old.

That’s still a problem for part presence data, but at least you won’t lose all your setpoints. The

persistent data saved when the runtime does a clean shutdown is the primary method for

protecting the consistency of your persistent data. That means a good UPS connected to the PC

with the USB cable and running the UPS software so it will do a clean shutdown in the event of a

power loss is absolutely necessary for every production system using TwinCAT 3.

Page 79: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

79

5 – Structuring PLC LogicTwinCAT 3 gives you a lot of flexibility in organizing your PLC logic. In this chapter I’ll explain how

to make use of this flexibility to organize your logic in a way that makes it easier for someone

reading your program to find what they need.

The MAIN Program

When you create your first TwinCAT 3 PLC project, the wizard will helpfully create a

MAIN program for you:

The MAIN program is called by the PlcTask task on a schedule defined in the PlcTask

configuration. This is the point at which your program now has control of the runtime. From

within MAIN, you have to call other programs that are part of your PLC project.

Page 80: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

80

Note that the MAIN program is inside of a POUs folder. This is only Beckhoff’s default output

generated by its wizard. Right now you could move the MAIN program into the GVLs folder

and PlcTask would still call it without a problem.

Folders

In my programs I delete the DUTs, GVLs, POUs and VISUs folders and then I create my own

folder structure that mimics the structure of the machine. For instance, if I was programming a

multi-zone conveyor system, I would create folders called Zone01, Zone02, etc., and if I was

programming a rotary assembly cell I would create folders such

as RotaryTable, Station01, Station02, etc. Then, inside each folder I would put both the data

structures and logic related to that part of the machine. There’s no need to separate the data from

the logic in separate folders:

Note: the folders sort themselves alphabetically. It just so happens that “R” comes before “S” so

it all worked out nicely above, but usually you want to enforce an ordering scheme. In that case,

you can add a numerical prefix, such as “01_” to the beginning of every folder name to make them

show up in the order you want. Also note that the current version of TwinCAT 3 doesn’t seem to

Page 81: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

81

sort properly as you add new content. To get it to sort properly, save everything, close the solution,

and open it again.

You can also put folders inside of folders. For instance, if you have a zone-based conveyor system

inside of the infeed side of your automation cell, you can put all of the zone folders inside of

an Infeed folder.

Programs

Within your new folder structure, add programs to control each logical element or function of the

machine. For instance in a zone-based conveyor system, it’s typical for each zone to have a

“drive” which is the motor that turns the rollers and a “stop” which is an air-actuated plunger that

pops up to prevent a pallet from moving past a point, and drops down out of the way to allow the

pallet to go forward. In a system like that, I would expect to see logic like this:

Always try to imagine someone trying to troubleshoot this machine. In a normal factory there will

be a maintenance team that handles the maintenance on several different machines. Nobody will

Page 82: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

82

know any particular machine to the depth that you know it while you’re programming it. An

operator might call them because there’s a pallet stuck in zone 2 and it won’t leave. If they open

up the project above, they can quickly navigate to the Zone02 folder, open

the Zone02_Stop program and find the output that drops the stop. If the output is on but the

stop isn’t dropping, then they know there’s something wrong on the mechanical or electrical side

with the stop, but if the output isn’t on in the program, then they can start working their way

backwards through the logic trying to figure out why the program isn’t turning the output on.

You might wonder why you wouldn’t create a single function block and call it 3 times, one for each

zone. Good question. Remember that even though the conveyor system appears to have 3 zones

and they all look the same, you should expect the logic for each zone to be slightly different.

Remember that these are 3 separate physical parts of the machine and even though we might

like to imagine that they’re identical, they’re not. If zone 1 is where material gets introduced to the

line, then it likely has some kind of interface logic for interlocking with whatever is feeding it, such

as a light curtain or a robot. Similarly the 3rd zone might need to be interlocked with some kind of

pick-and-place equipment. If you create a function block that can handle the logic for all 3 zones,

then you’ve complicated the logic for no real gain.

It’s better to see if you can pull out components of each zone that are necessarily identical (such

as the stop) and create a function block for just that component. However, even then I would

caution you that these are 3 separate physical stops and you should expect the logic to have to

work slightly differently for each one, even if it’s something as simple as a timer. Also, it’s common

that someone in maintenance might need to bypass some condition in the logic either to recover

from an unforeseen problem or to bypass a broken sensor until it can be replaced. It’s much more

difficult to do this if all 3 components share the same logic, since any bypassing code will likely

affect all 3 stops, when in reality you only want to bypass a single sensor. Ultimately it’s your

decision, but I suggest defaulting to the copy/paste method rather than the function block method

in most cases.

Page 83: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

83

Calling Program from MAIN

After you’ve created your programs, you need to “call” them once-and-only-once from

your MAIN program. Here is what your MAIN program would look like in the conveyor example

above:

Remember that there is only one “instance” of each program (unlike a function block like a timer

where there can be more than one instance). That means there is only one copy of the local

variables declared inside of a program. If you call a program twice, you are executing the logic a

second time, and it’s going to affect your local program variables and any global variables

referenced by that program. That’s almost certainly not what you want. Someone reading your

project will assume that every program is called once, and typically in the order that the programs

are listed in the solution explorer, so please work hard to make the order that they show up and

the order that you call them in match. If they need to be called in a certain order, please rename

your programs and folders so that the ordering is the same.

Every time you create a new program you have to remember to call it from MAIN. If you don’t,

the logic won’t execute and this can be difficult to debug. Thankfully, TwinCAT 3 will give you a

hint that you made this mistake. As an example, I’m going to comment out the line where I

call Zone02_Stop from the MAIN program:

Page 84: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

84

The two forward slashes turn the line into a comment, which means anything after the slashes is

ignored by the compiler. Now, right click on the PLC1 Project node in the Solution

Explorer window and click Build from the context menu. After building, TwinCAT 3 will gray out

any POUs that aren’t “linked,” which generally means any that aren’t called by

your MAIN program:

Page 85: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

85

I highlighted the grayed out node with a red arrow above. Note that you can defeat this helpful

feature by forcing the compiler to link it even if it’s not called. Click on the grayed out node, and

then look at the Properties window:

If you change the Always Link property from False to True, and then build the project, the node

will no longer be grayed out. When you run the program it still won’t call this program, so you may

wonder why this feature exists. Well, if the program isn’t linked, then that means the compiler

didn’t even try to build it. There are certain cases where you might be trying to write some new

logic and you don’t want to call it yet, but you do want to check if there are any compile errors. In

that case you can set the Link Always property to True, build the project, and any compile errors

will show up in the error list at the bottom of the screen. Just remember to change the property

back to false so you will remember that you’re not calling this logic yet.

Program Variables

Each program has a variable declaration section at the top of it for local variables. Any variable

that is only referenced by this program should go in the program’s local variables, not in a Global

Variable List. That helps someone reading your program understand the “scope” of a variable.

Global variables have more impact across your whole project and might even be referenced by

your HMI program, but local variables should only be used by your program and modifying them

should only have a local effect.

Note: In the latest version of TwinCAT 3, you can actually read and write program local variables

from the HMI. I advise against doing this, but if you do, please make sure that you at least

comment these variables in your program to let other people know that they’re accessed by the

Page 86: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

86

HMI. Even global variables accessed by the HMI need to be commented. That’s because it’s easy

to find all the places where a variable is used in your program: just change the variable name and

do a build. You’ll get compile errors at every reference (and there’s also a cross reference feature

too). Unfortunately there’s no simple way to find places where that variable is referenced in the

HMI, so make sure you make it obvious.

In addition to normal local variables, you can add a PERSISTENT variable section inside the

variable declarations of a program, just like you would in a Global Variable List. The variables in

a VAR PERSISTENT block will be saved and restored through a shutdown and a restart of the

PLC runtime, just like persistent variables in a Global Variable List:

Less is More

How much logic should you put in a single program? My general rule of thumb is “as little as you

can get away with.” You want all of the logic in a program to be “tightly coupled.” That is, all of the

logic in a single program should be highly related to the other logic in there. Conversely, you want

the logic in different programs to be “loosely coupled.” That is, ideally you want all the logic in a

program to stand on its own without interacting with the logic in other programs at all.

Obviously this isn’t always possible, but in practice you want to reduce the coupling between

programs. Since the coupling between programs takes place through global variables, this means

you want to separate your logic in such a way that the interfacing signals between programs are

few and simple. If you have a conveyor feeding a pick-and-place robot,

then PickAndPlaceClearOfConveyor is a reasonable global variable that’s set in the

pick-and-place logic and used in the conveyor logic. The conveyor logic shouldn’t “care” how that

Page 87: TwinCAT tutorial - Beckhoff

Chapter 5: Structuring PLC Logic

87

signal is created (that’s the job of the pick-and-place logic). It just needs to know that it’s clear so

the conveyor can move.

In practice, I try to keep my programs between 1 and 12 rungs long, but that’s certainly not a hard

and fast rule. Logic that needs to be longer should be longer. However, if your program logic really

starts to grow, see if it’s really doing two things that could be separated out into two programs

where just one or two global variables could handle the interaction between those two programs.

Page 88: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

88

6 – Multiple Virtual PLCsIn the Windows Server world, the big trend in the last 10 or so years has been server virtualization.

That means we take one big beefy server and we run many “virtual servers” on that hardware.

One physical server takes on the role of email server, file server, database server, etc., and we

can logically think of each server as a separate entity because we can login to each one, see its

disk drives, install programs, etc., but each virtual server shares the resources of the parent

physical server. This revolution in the data center has also made its way into PLCs.

The TwinCAT 3 runtime running on a PC is kind of running a “virtual” PLC. That PLC runs in “ring

0” of Windows and handles all the tasks that a traditional PLC would, like scanning the I/O, and

solving logic. TwinCAT 3 lets you create multiple virtual PLCs and run them all in the same

TwinCAT 3 runtime. You might wonder why you’d want to do this. Here are some reasons…

Reason 1: Make use of Multiple Cores

The primary reason touted by Beckhoff is that this is how you make use of more than one core in

those fancy new multi-core CPUs we can buy now. Modern CPUs in the Intel Core-i3, i5, and i7

series have 4 physical cores in the CPU, and each one can be executing a different program at

the same time. However, the fundamental scheduled unit in Beckhoff, the “task”, can only run on

a single core.

Even though the processor power available in a single core is much more than in a traditional

PLC, EtherCAT is so fast that you may also be pushed to run your logic faster. In case you run

out of CPU time, you now have the option of breaking your system up into multiple tasks that run

on separate cores. In a typical system with a PLC and a motion controller, you can already do

this by running your PLC task on one core and the motion control task on another. However, you

still have the option of splitting your PLC logic into multiple virtual PLCs and running each one on

a separate core so you can balance the loads.

Reason 2: Separation of I/O

Let’s say you have a really big system with lots of I/O. The more I/O you have, the longer it can

take to scan the whole bus. Some day you might have a case where you want to scan a small

Page 89: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

89

portion of the bus very quickly (a high speed input, for example) but the rest of the I/O can be

scanned at some more leisurely pace. The I/O scan matches its rate to the PLC task tied to it.

That means you can accomplish multiple I/O scan rates by making one virtual PLC to handle your

high speed I/O and another to handle the rest of it. TwinCAT will handle scanning just that portion

of the I/O that each PLC needs for its I/O map.

Reason 3: Flexibility

In addition to allowing you to balance CPU load and I/O scan time, supporting multiple virtual

PLCs also gives you the flexibility to move a virtual PLC to an entirely different physical machine.

Imagine you have three separate production cells (let’s call them OP10, OP20, and OP30). These

form a production line where parts are transferred from one operation to the next. Currently the

cells are beside each other in your facility, but they operate independently. Operators run parts

through OP10, and they may or may not take the parts directly to OP20, or they might store them

for a while and run them as batches. The point is that in the future the production department

wants flexibility. They may want to install a transfer system to tie all the cells together, or they may

want to batch the parts into bulk bins, possibly moving the cells away from each other physically.

In today’s environment you may even see one of the operations contracted out to a lower tier

supplier entirely, including moving the cell (say OP10) to their facility.

When you build and install this machine, if you separate the operations onto 3 virtual PLCs, you

have the flexibility to run all 3 on a single physical PC (saving money). Later, when they want to

move OP10 to another location, or even another facility, you just have to move the PLC and I/O

for OP10 over to another PC (possibly a new PC, or possibly an existing PC in the new location).

This is just a configuration change and a matter of plugging the EtherCAT cable for OP10 into a

different EtherCAT master.

Reason 4: Modularity and Code Re-use

We often try to design our machines with re-usable components. If you’re a machine builder and

the machines you sell have an optional feature, such as an auto-pallet-changer on a CNC mill,

why ship that logic on a machine where the customer didn’t order it? If you use a separate virtual

Page 90: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

90

PLC to control your pallet changer, then you just don’t include that virtual PLC on machines that

don’t have it. You might also be able to offer your pallet changer as an add-on for other CNC mills,

perhaps ones you didn’t even build, by running your virtual PLC on a low-cost shoebox style

TwinCAT 3 box without the overhead of an industrial PC.

What if you already have a bunch of TwinCAT 3 machines in your facility and you want to add a

new plant-wide data collection system to all of them? Create a single virtual PLC project that

handles the data collection and run it on all your TwinCAT 3 machines. That’s a lot simpler than

modifying the logic on every single machine, and it keeps the data collection logic nice and

isolated from the machine control logic. The machine control virtual PLC would interface with the

data collection virtual PLC over mapped I/O which creates a nice clean interface between the two.

Example: Create Multiple PLCs

Start by creating a new TwinCAT XAE project called TwinCATMultiplePLCs:

In this example we’re going to imagine two separate physical work cells called OP10 and OP20

each controlled by a separate virtual PLC. There will be a single physical EtherCAT master

(though you could connect each work cell to its own physical EtherCAT master if you want). At

first these two PLCs won’t communicate with each other, but we’ll add interlocks between them

at a later stage of the example.

Let’s start by creating the I/O. Under the I/O node, right click on Devices and select Add New

Item… from the context menu. That will open the Insert Device dialog. Select EtherCAT Master as

the new device and enter EtherCAT in the name box:

Page 91: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

91

It will now try to attach this EtherCAT master to a physical device on your PC. If none exists

(because you haven’t installed one), just click Cancel:

Now you will see your new EtherCAT master in the Solution Explorer:

Page 92: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

92

Right click on the EtherCAT master and choose Add New Item… from the context menu. That will

display the Insert EtherCAT Device dialog:

Page 93: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

93

It will helpfully auto-select the EK1100 head module, which is what we typically want.

Enter OP10 as a name for this device in the name box. This will be the head module for the I/O

in the OP10 panel. Click OK. Now follow the same procedure again, but this time add an EK1100

with the name OP20. You will see these two new devices under your EtherCAT master:

Page 94: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

94

Now under each head module we want to create a single input card. Right click on

theOP10 device and choose Add New Item… from the context menu. The dialog will auto-select

the last thing you selected. We want to add an EL1008 input card. The fastest way to select it in

the list is to use the Search box. Type el1008 in the search box:

Page 95: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

95

Enter a name for this card as well. I chose OP10 Inputs here, but you could choose something

like OP10 In01 if you expect to add more cards. Click OK. Now follow the same procedure but for

the OP20 device, to add a new card called OP20 Inputs. Your I/O should now look like this:

Page 96: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

96

Now let’s add our virtual PLCs. Right click on the PLC node in the Solution Explorer and

choose Add New Item… from the context menu. From the dialog that pops up, choose Standard

PLC Project, enter OP10 in the name box, and click the Add button:

Wait for the wizard to create your new PLC project. Now repeat the process and create a second

PLC project called OP20. You should see your two PLC projects under the PLC node:

Page 97: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

97

Now we need to create some inputs in our PLCs. I’ve already covered adding global variable lists

earlier, so I’m only going to explain the broad steps here.

1. Create a Global Variable List in the OP10 PLC called OP10

2. Create a Global Variable List in the OP20 PLC called OP20

3. Add a single BOOL input to each Global Variable List

4. Right click on the OP10 Project node and choose Build from the context menu

5. Right click on the OP20 Project node and choose Build from the context menu

Now each PLC project will have an input for linking to the physical I/O:

Page 98: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

98

Now go back down to your I/O. Expand the OP10 Inputs device (the EL1008 card) and then

expand the Channel 1 node underneath that:

Page 99: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

99

Underneath the Channel 1 node you’ll see a single node called Input. Right click on the

Input node and choose Change Link… from the context menu. Now you’ll see the Attach Variable

Input dialog:

Select the OP10.Input01 variable in the tree and click the OK button. Now you’ve “mapped”

the PLC input to the actual physical I/O. Repeat this procedure to map

theOP20.Input01 variable to the OP20 Inputs Channel 1 physical I/O.

Page 100: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

100

Now go up to the OP10 node under PLC, right click on it and choose Activate Boot Project…

Then do the same with the OP20 node under PLC. Also right click on the OP20 node and make

sure that Auto start Boot Project is checked (the OP10 one should already be checked by default).

Since this is a new project we’ve created, we have to configure our Real-Time again. Under

the SYSTEM node, double-click on the Real-Time node to show the real-time settings:

Click the Read from Target button (highlighted above). Assuming you already completed the

Quick Start section earlier, then it should read the following configuration:

There are 2 cores assigned to Windows and 2 cores assigned as “Other.” Under the RT-

CPU column, check CPUs 2 and 3, and uncheck CPU 0:

Page 101: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

101

Now if you try to Activate Configuration to start your PLCs, it won’t work because the EtherCAT

master isn’t linked to a physical Ethernet card. You can try it if you want, but you’ll just get an error

message when it tries to start the runtime in run mode. To get around this, right click on

the EtherCAT master node and choose Disable from the context menu. Now you can go to

the TwinCAT menu (at the top of the screen) and choose Activate Configuration from the drop

down menu. Click through the dialog boxes, just like in the Quick Start, earlier. When it asks if

you want to restart in run mode, say Yes.

Going Online

You can still go online with each PLC, but it’s important to understand that you go online with

each PLC separately. That means when you login with an online change, you’re making the

change to that PLC only. There’s no way to simultaneously make online changes to 2 PLCs at

the same time (however that should rarely ever be a problem).

ADS Ports

By default, the first PLC listens on ADS port 851. You can see this by double-clicking on

the OP10 node. Subsequent PLCs are assigned in order, so the OP20 PLC uses ADS port 852,

and so on. This is important if you make use of the free ADS DLL to communicate with the PLC(s)

from a Windows program.

Running the PLCs on Separate CPU Cores

In the example above, there’s still only one task (called PlcTask) that executes both PLCs.

Currently both PLCs are running on CPU 2 (i.e. core 2). If we want to run the two PLCs on

separate CPU cores (say cores 2 and 3) then we need to have two PLC tasks.

Under the SYSTEM node in the Solution Explorer, under the Tasks node double-click on

the PlcTask node. In the name box, change the name of the task to OP10. Now right click on

the Tasks node and choose Add New Item… from the context menu. In the Insert Task dialog,

enter OP20 as the new task name, and click OK.

Page 102: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

102

That will add a new OP20 node under Tasks. Double-click on the OP20 node and change the

priority to 21. By default, new PLC tasks are assigned a priority of 20. Two tasks can’t have the

same priority, so while we’re leaving the OP10 task as priority 20, we’re assigning the OP20 task

to a priority of 21.

Now under the OP20 Project node, find the PlcTask node:

Notice that it’s still linked to the original OP10 task. Right click on the PlcTask node and

click Assign to Task from the context menu. That will display the Assign new Task dialog box:

Page 103: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

103

The only other option is the OP20 task, so click OK.

Go back under the SYSTEM node in the Solution Explorer and double-click on the Real-

Time node:

Notice that both the OP10 and OP20 tasks are listed in the bottom grid, but both are still assigned

to CPU (core) 2. In the RT-CPU column of the bottom grid, in the OP20 row, click the drop-down

box and change it to CPU 3:

Page 104: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

104

Now choose Activate Configuration from the TwinCAT menu at the top of the screen, and restart

in run mode when it asks you. Now the OP10 PLC is running on CPU (core) 2 and the OP20 PLC

is running on CPU (core) 3.

Click on the Online tab:

The two left-hand graphs are for CPU 2, and the right-hand graphs are for CPU 3. The top graphs

show how much of the CPU you’re using (0% in this case because we didn’t run any logic). The

bottom graphs show how the latency between when the timer interrupt fires (every 1 ms in our

example) and when the task actually gets to run.

Page 105: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

105

Connecting Virtual PLCs

In the example above, each PLC operates independently of the other. OP10 is blissfully unaware

of the existence of OP20 even though both share an EtherCAT master, and could even share an

input mapped from the same physical input.

What if we want to connect these two PLCs together so they can send signals back and forth?

This is as simple as defining an input variable in one PLC, an output variable in the other (of the

same type) and linking them together the same as if you were linking these variables to physical

I/O.

For example, in OP10‘s Global Variable List define a variable like this:

ToOP20 AT %Q* : BOOL;

Then in OP20‘s, define:

FromOP10 AT %I* : BOOL;

Right click on each of OP10 Project and OP20 Project and select Build from the context menu.

Now link the output from OP10 to the input from OP20:

Right click on the OP10.ToOP20 PLC output (highlighted) and choose Change Link…from the

context menu. Select OP20.FromOP10 in the Attach Variable dialog. Click OK.

Select Activate Configuration from the TwinCAT menu and say yes to restart in run mode.

Page 106: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

106

Now the OP10 PLC can send a signal to OP20. You can open both Global Variable Lists, go

online with each one, and set the output variable to TRUE on the OP10 side and watch it change

to TRUE on the OP20 side.

Sharing a Common Library

It’s normal to have a list of functions or function blocks you’d like to use in all your programs.

Rather than copying the logic manually into each PLC project, TwinCAT 3 offers the ability to

create a library and reference it in your other projects.

Typically you would create this library in another separate solution, but I like to have everything

in one solution if I can. Start by right clicking on the PLC node in the Solution Explorer and

choosing Add New Item… from the context menu. In this case select Empty PLC Project and give

it the name CommonLibrary. Click Add.

The next thing you want to do is right click on the new CommonLibrary node and choose

Disable from the context menu.

Now let’s add a function. Right click on the CommonLibrary Project node and select Add-

>POU from the context menu. In the dialog, set the name to DegreesToRadians,

select Function as the type, REAL as the return type, and choose Structured Text (ST) as the

language:

Page 107: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

107

Then click Open. The formula for converting degrees to radians is to multiply by pi and then divide

by 180. The mathematical constant PI is defined in a TwinCAT 3 library, which we will have to

reference. Under CommonLibrary Project, right click on the References node and choose Add

Library from the context menu. Expand the System section in the tree and choose

the Tc2_System library and click OK.

Now define the DegreesToRadians function:

Page 108: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

108

Note that there’s an input (Degrees), and one line of logic.

Before we can save it as a library, we have to set some properties. Right click on the

CommonLibrary Project node and choose Properties from the context menu. Now you’ll see the

properties window. You have to fill in the 3 bolded fields: Company, Title, and Version:

To access this function in the OP10 and OP20 PLCs, we first have to save this as a library, install

it in the local library manager, and then reference it in both PLCs. Start by right-clicking on

the CommonLibrary Project node and choosing Save as library and install… from the context

menu. It will ask you to save this as a .library file somewhere on disk. It really doesn’t matter

where you save it, as it will install it in the library manager immediately afterwards.

Now you can go under the OP10 Project node, right click on References and choose Add

library… from the context menu. Your new library will show up under (Miscellaneous). Select it

and click OK. Do the same thing under the OP20 Project node.

Now you can use the DegreesToRadians function in both virtual PLCs. If you want to modify

the CommonLibrary then you have to remember to right click on the CommonLibrary Project node

and choose Save as library and install… from the context menu. The library manager will reload

Page 109: TwinCAT tutorial - Beckhoff

Chapter 6: Multiple Virtual PLCs

109

it in both of your PLC projects. You’ll still have to login with an online change on the actual PLC

to send the new logic to the runtime.

That concludes this section on multiple virtual PLCs. This is an advanced feature of TwinCAT 3,

but it’s useful in many real-world applications.

Page 110: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

110

7 – Ladder Logic EditorIn TwinCAT 3, the ladder logic editor shares a lot of functionality with the function block diagram

editor. In some ways, the function block editor is just the ladder logic editor without contacts and

coils. All of the function blocks available in the function block diagram editor are also available in

the ladder logic editor, so I consider the ladder logic language to be a super-set of the function

block diagram language.

Ladder logic, however, requires the concept of a “rung”, which is just power (represented as

a BOOL signal) entering from the left, and connecting all of the language elements together within

the rung (a.k.a “network” in TwinCAT 3).

The EN Input and ENO Output

Here is what the ADD function looks like in the function block diagram editor:

Here is what it looks like in the ladder logic editor:

Notice that the editor added the EN input and the ENO output automatically. The EN input is an

enable input. The instruction is only executed if the EN input is true. Here is a

disabled ADD instruction (the EN input is FALSE), and you can see that the ADD instruction

isn’t being executed because the Result is still 0:

Page 111: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

111

While the EN input is true, the instruction executes:

Please note that the contact before the ADD instruction is generally unnecessary if you want it to

execute all the time:

The ENO output is for continuing the rung to the right, so you can chain multiple instructions

together like this:

However, once you start using longer variable names, doing this can cause rungs that are very

wide and don’t wrap very nicely, so as a matter of ladder logic programming style, a rung like this

should generally be split into two rungs:

Page 112: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

112

While the novice programmer often assumes that a program with fewer rungs is better, the only

thing that should guide your choice is readability. One good rule of thumb is that any instruction

that modifies a variable should be the end of a rung. Ladder logic programmers implicitly assume

that inputs are on the left and outputs are on the right. In the example above, the ADD instruction

modifies the Result variable, so we should stop the rung there and continue the logic in a new

rung.

The Rung as RValue and LValue

Take a look at this typical rung made up of only contacts and coils:

I highlighted a vertical line with a red circle around it. The vertical line separates two parts of the

rung: the RValue and LValue. These names are actually confusing because the RValue is

everything to the left of the vertical line, and the LValue is everything to the right of it. Why is this

backwards? In a traditional programming language like C, consider a statement like this:

int a = 5 + 3;

Page 113: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

113

Everything on the left of the equals sign is the LValue and everything on the right is the RValue.

The LValue is the part being modified (the result of the assignment) and the RValue is the

expression being evaluated. It’s just that in ladder logic, this is reversed. The assigned variable

(the coil) is on the right and the expression (the inputs) are on the left.

What does this mean in practice? Basically it means that everything on the left of the vertical line

will be evaluated first, and then everything on the right will be assigned. Only coils can go on the

right, and coils can’t go on the left.

That might seem a bit restrictive if you’re coming from Allen-Bradley, where you can put a coil

instruction anywhere you like. In practice, this is only a minor adjustment to your thinking.

Automatic Conversion Between Function Block Diagramand Ladder Logic

One interesting feature of TwinCAT 3 is that it can convert between function block diagram and

ladder logic. If you copy the rung above and paste it into the function block diagram editor, you

get this:

Interestingly, if you take this ADD instruction in function block diagram:

…and then you paste it into the ladder logic editor, you’ll get this:

Page 114: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

114

Why didn’t it include an EN input and an ENO output when it converted it to ladder logic? As I

said, the ladder logic language in TwinCAT 3 is a super-set of the function block diagram

language. The ladder logic editor can support any valid function block diagram construction.

If you right click on a rung and choose Insert ADD Box from the context menu, you’ll get

an ADD instruction with the EN input and ENO output. However, if you right click (typically on an

empty rung) and select Insert Empty Box from the context menu, you’ll get a box without

the EN input and ENO output. Then you can just type the name of the function you want at the

top of the box:

However, this presents a problem. The ladder logic editor won’t allow you to connect the output

of the ADD instruction to a variable. You can insert another empty box and use a GT (greater

than) instruction to compare it, which gives you a BOOL output, which you can then connect to a

coil. What you should be able to do is right click and choose Insert Assignment, but this option

isn’t enabled in the ladder logic editor. I actually believe this is a bug in the editor, and that it might

be fixed in a future version. In the meantime, you can actually build your network in the function

block diagram editor, copy it, and paste it into the ladder editor, and it will compile and run just

fine.

Blocks with Variable Numbers of Inputs

Some blocks allow you to add extra inputs. For example, an ADD function can have 3 or more

inputs:

Page 115: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

115

To add an input, right click on the block and select Append Input from the context menu.

Contacts and Coils

Let’s take a look at the ladder logic specific instructions in more detail.

The Coil : Here is the coil instruction:

The coil instruction does one thing: it assigns the result of the RValue expression (the result of

the logic on the left) to the coil variable.

In other PLCs, the coil instruction does a second thing: during program startup (sometimes

referred to as the pre-scan), it sets the variable to FALSE. This is an important property of coils

in ladder logic: they are expected to default to off after a program start.

In TwinCAT 3, persistence is controlled by whether or not you declare a variable as a persistent

variable. In real life, coils turn off when they lose power, so an experienced ladder logic

programmer (or an electrician) looking at a rung like this:

Page 116: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

116

…will expect the Run coil to drop out if the program restarts (particularly due to the machine

being turned off and back on again). If you make the Run variable persistent, then you will break

this expectation. There is a principle of programming called The Principle of Least

Astonishment. Don’t make a coil variable persistent because it breaks this principle. Use a

Set/Reset instruction pair instead (explained later).

Similarly, as a matter of programming style, do not use the same variable in two different coil

instructions. The editor and compiler will allow you to do this, but it’s considered bad ladder logic

style. If you find that you have logic that requires you to do this, consider writing that logic in

structured text language instead. Ladder logic should mimic physical relays whenever possible,

and using the same variable in two coil instructions breaks this mental equivalency.

The Negated Coil

If you have a regular coil and you select it and press the slash key (“/”), it will change it into a

Negated Coil:

This is an example of an instruction that was probably included because it was easy to add, but

should never have been included in the language. It’s easy to explain what this does: during

program execution, if the rung input condition is TRUE, it sets the variable to FALSE, and if the

input condition is FALSE then it sets the variable to TRUE. The problem is that there’s no real-

world counterpart to a negated coil.

Logically this:

Page 117: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

117

…is equivalent to this:

The latter two-rung logic is the preferred form. Please avoid use of the negated coil

instruction. One of the purposes of ladder logic is to allow electricians to debug it. The negated

coil is not intuitive to an electrician and even an experienced ladder logic programmer will frown

when they see it.

Set and Reset

While a coil should revert to “off” when the program is restarted, it’s sometimes preferable to

remember the state of a variable through a program restart. This is the purpose of Set and Reset

instructions:

In the physical world, we would use a latching relay. The latching relay has two inputs called Set

and Reset (or Latch and Unlatch). Energizing the Set input will move an internal solenoid to

change the relay to the “on” position. A spring mechanism will hold it in this position, so it will

remember its state even after power is removed. Likewise, energizing the Reset input will move

the solenoid the other way, to the “off” position.

Page 118: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

118

Note that in TwinCAT 3, to get the variable to retain its state, you will have to make it a persistent

variable. The important thing to note is that when a ladder logic programmer sees a pair of

Set/Reset instructions, they will expect this to be a persistent variable, so please don’t break this

expectation.

Normally Open and Closed Contacts

The Normally Open Contact:

…sets the rung output condition to the logical “and” of the rung input condition and the state of

the contact’s variable. In the physical world, a normally open contact is a switch that only allows

power to pass if the coil variable is in the “on” state. The normal state of a coil is off (de-energized)

which means a normally open contact is normally “open” (i.e. not conducting).

The Normally Closed Contact:

…sets the rung output condition to the logical “and” of the rung input condition and the

negated state of the contact’s variable. In the physical world, a normally closed contact is a switch

that only allows power to pass if the coil variable is in the “off” state.

The TwinCAT 3 ladder logic editor has a handy shortcut: to toggle a contact between normally

open and normally closed, but select it and press the slash (“/”) key.

Positive and Negative Transitions

TwinCAT 3 supports Positive and Negative Transition contacts:

Page 119: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

119

What these instructions do is generate a one-scan pulse on a positive or negative transition of

the given variable, and then “and” the pulse with the rung input condition. In other PLCs this type

of instruction is sometimes referred to as a “differentiate up/down”, “rising/falling edge”, or a “one-

shot rising/falling” instruction. It’s important to note that these TwinCAT 3 instructions generate

pulses based on the transitions of the variable (SomeInput1 in this example) and not based

on the rung input condition. (There are built-in function blocks called R_TRIG and F_TRIG that

will look for transitions on their inputs.)

Internally, the Positive and Negative transition contacts operate by implicitly storing a copy of the

variable and comparing the current value against the value from the last scan to determine if a

transition occurred.

It’s worth noting that the internal variable copy seems to be initialized to FALSE on a program

start. This means that if you use a Positive transition contact with a variable that is

already TRUE when the program starts, the instruction will generate a pulse on the first program

scan. This may be unexpected.

For instance, if you have a persistent variable, and used a Positive transition instruction on it, and

it was TRUE when the program shut down and restarted, most programmers wouldn’t expect the

Positive transition instruction to generate a pulse when the program started. Unfortunately you

need to be aware of this and program accordingly. If it’s important, you can use the built-

in R_TRIG function block and make the function block instance itself persistent. This will save

the state of the internal memory variable through the program shutdown and restart.

For some added fun, the Negative transition instruction doesn’t behave this way because the

internal state is initialized to FALSE so when it’s used on a variable that’s already FALSE on a

program restart, the instruction doesn’t see a transition.(Ultimately I think this is a bug, and that

the Positive transition instruction’s internal variable should have been initialized to TRUE to avoid

Page 120: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

120

this inconsistent and surprising behavior. It’s possible that Beckhoff may “fix” it in the future, but

they might be hesitant because it could constitute a breaking change in a few programs.)

Using Function Blocks in Ladder Logic

In TwinCAT 3, a function block has inputs, outputs, and internal state. The obvious example is a

timer or a counter. TwinCAT 3 has two timer instructions: a delay-on timer (TON) and a delay-off

timer (TOF). Here’s an example of a TON instruction:

What this does is create a variable (SomeInput1TMR.Q) where the on is delayed by 200 ms.

The timer will still turn off immediately when SomeInput1 turns off. On the other hand, a TOF

instruction turns on immediately when the input turns on, but delays turning off:

A delay-on timer is more common, but a delay-off timer is typically used when you have a fast

pulse (like a quick button-push or brief sensor) and you want to elongate that into a longer, more

consistent pulse width.

What differentiates a function block from a function is that a function block can store state

internally. In the case of a timer, the obvious internal state is the Elapsed Time (ET) output. Since

function blocks store internal state, TwinCAT 3 needs somewhere to put it, so you have to declare

a variable. The variable is entered above the function block:

Page 121: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

121

You have to declare this function block variable just like any other variable in your program:

I find that most function blocks are declared in the program variables, not in global variables,

though there are some exceptions. Where you declare them is up to you and the needs of your

application.

In addition to timers, you can also declare counters. Here is a count-up counter (CTU):

The count-up timer will start at zero (the CV output means current value) and will add one to the

current value on each rising edge of the count-up (CU) input. The counter will turn on the Q output

if the CV value is greater than or equal to the PV value. However, please note that the counter

will continue counting even after the Q output is on. This is different than counters in some other

PLCs. Turning on the RESET input causes theCV output to change to zero and the counter won’t

respond to the CU input. Note that if you set PV to zero, the counter still functions but the Q output

will always be on.

The opposite of the count-up timer is the count-down timer (CTD):

Page 122: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

122

As you can see, instead of a RESET input, the count-down timer has a LOAD input. When

the LOAD input turns on, it sets the current value (CV) output to the value of the preset (PV)

input. Each rising edge on the count-down (CD) input will decrement one from the CV output

value. The counter will count down to zero, but won’t continue counting below zero (it can’t

because the CV output is an unsigned variable). The Q output will turn on if the CV output is zero.

Note that both the CTU and CTD counters are 16-bit counters (WORD variable), so their max

values are 65535. You can, of course, write your own counter function blocks if you need one that

can count higher.

Note that TwinCAT 3 also offers the up/down counter (CTUD) which is a combination of

the CTU and CTD counters:

As you can see, you can RESET the counter (to zero), LOAD the counter (to the persistent value)

and count it up or down. The counter is “reset dominant” meaning if both

the RESET and LOAD inputs are on at the same time, the RESET input takes priority. Like the

count-up counter, it can happily count above the preset value, but like the count-down counter it

can’t count down below zero. The QU output turns on if CV is greater than or equal to PV, and

the QD output turns on if CV equals zero.

The Many Faces of the MOVE Function

The simple MOVE function:

Page 123: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

123

It copies the value on the left to the variable on the right. The input on the left can be any

expression:

Here’s a useful case where you have an angle in degrees and you want to take the COS of it,

which expects an angle in radians:

…which is equivalent to this:

The latter is probably more readable since COS is what you’re “doing” and the expression in the

input is just a conversion. Here’s another typical use of the MOVE function to do unit conversions:

Page 124: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

124

Note that a purist would say that the value 25.4 needs to be declared in a constant, like

INCH_TO_MM. Personally, for well-known conversions, I find that using the value 25.4 is simple

and readable. Obviously you shouldn’t be using 3.14 for the value of PI because (a) it’s

wrong(er) than value available in the built-in constant and (b) the constant is shorter and more

readable.

If you really don’t want to use a constant, consider creating a function, like in_to_mm(). This

is both readable, short, and precise.

It’s also a very good idea to put the unit name in your variable name, such as

Length_mm or Length_inch. In the United States and many facilities in Canada the

operators will still want to see distances shown in inches, but Beckhoff motion control and servo

products all use mm exclusively, so this type of conversion is commonplace. Putting the units in

the variable name will help you keep track and make errors more obvious.

Variable Type Conversions

TwinCAT 3 has a full suite of built-in unit conversion functions. Some conversions can be done

implicitly, like converting from an integer value to a floating point value, but you will still get a

compiler warning. To get rid of the warning, or to do a conversion from floating point to integer,

use an explicit conversion, like this:

Page 125: TwinCAT tutorial - Beckhoff

Chapter 7: Ladder Logic Editor

125

Summary

The ladder logic editor in TwinCAT 3 is a powerful super-set of the function block diagram editor.

It allows you to mix standard contacts and coils with the entire library of built-in function blocks,

plus any new functions or function blocks you write. Remember to always focus on writing

readable logic. In particular, make sure you’re writing logic that an electrician can read, follow,

and understand. The ability for an electrician to understand ladder logic is one of the main reasons

that we write in ladder logic to begin with. Always keep that in mind.

Page 126: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

126

8 – Writing your own Functions and Function BlocksThere are three types of Program Organization Units (POUs):

Programs

Function Blocks

Functions

When you use the wizard to create a new TwinCAT 3 PLC project, it will create a program for you

called MAIN. TwinCAT 3 also comes with several libraries full of Function Blocks and Functions

for you to use (the most common being timers, counters, and math functions like addition and

subtraction).

Let’s start by examining the differences between Functions and Function Blocks. The basic

difference is that a function block can hold state, and a function cannot. For instance, a counter

function block has to “remember” the value of the counter internally, and it must also remember

the previous value of the increment input because it needs to detect a rising edge. To the

programmer using a function or a function block, this means that an instance of a function block

needs to be declared as a variable and referenced when you call it.

Another difference is that a function always returns a value. The ADD function returns a number,

for instance. You have to declare a return type for a function when you define it. Most functions

only return one value, but you can define a function that has more outputs.

Here is an example of an ADD function:

Here is an example of a delay-on timer (TON) function block:

Page 127: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

127

I’ve highlighted the variable above the TON block. As you can see, to use a function block you

have to declare the variable to hold the internal state, but to use a function, you don’t. This means

a function is easier to use, but the functionality is limited to what it can compute from the current

inputs of the function.

A program is like a function block (it has internal state), but there is only one global copy of each

program, so you can’t create “instances” of programs like you can with a function block.

You can’t use a function block (like a timer) inside of a function, because the timer’s state will be

re-initialized very time the function is called. TwinCAT 3 will let you do it, but the timer won’t work.

Build a Maintenance Counter Function Block

TwinCAT 3 includes counter function blocks in the standard library, but these have some

limitations. The primary limitation is that the internal counter uses a 16-bit WORD variable, so it

can only count up to 65535. What if we wanted to create a maintenance counter for our machine

that recorded the number of parts produced. It’s easy to imagine a machine that could produce

millions of parts in its lifetime. A counter with a 32-bit value (DWORD) would solve this.

Since this is a maintenance counter, we also want to make this value persistent so it doesn’t reset

to zero when the program is restarted. We can do this by making the instance variable of the timer

persistent, but we can also do it by making the internal counter persistent (which makes all

instances of this counter persistent automatically).

Start by right clicking on the folder where you want to add your function block (typically a folder

called Common, or Helpers, or Utilities, etc.). Then choose Add->POU… from the context menu.

In the Add POU dialog, enter MaintenanceCounter as the function block name and

choose Function Block under Type. Finally, make sure Ladder Logic Diagram (LD) is selected

under Implementation Language:

Page 128: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

128

Click Open to create the function block:

Page 129: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

129

Now define the inputs and outputs of the function block. I copied these from the count-up (CTU)

function block, but I changed the PV and CV values from WORD (16-bit) to DWORD (32-bit)

variables:

Now let’s add internal variables. Since we want these variables to be persistent, we need to

change the variable declaration from VAR to VAR PERSISTENT. Then add two variables, as

shown:

Page 130: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

130

The RisingEdge variable is actually an instance of another function block, called an

R_TRIG. This is a function block that detects a rising edge on an input. R_TRIG is actually

defined in the IEC-61131-3 programming language standard for automation systems. Internally it

“remembers” the last state of the input value, and by making it persistent, it will remember that

value even through a power cycle or program restart.

The CurrentValue variable is just the internal representation of the CV output value. At the

end of our function block, we’ll copy the CurrentValue to CV. Again, we’re making it

persistent so the value won’t be reset on a power cycle or program restart.

Now add the ladder logic for the MaintenanceCounter implementation:

In rung 1, the R_TRIG function block detects a rising edge on the CU input. In rung 2, it adds 1

to the counter value on the rising edge. Rung 3 zeros the counter on a reset, and since it executes

after the ADD instruction, the RESET input will be “dominant”. That is, if the RESET input is on

Page 131: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

131

while the CU input turns on, the RESET input will keep the counter at zero. Rung 4 copies the

internal value to the output, and rung 5 sets the Q (counter done) output based on

the CV and PV values.

I’m sure you can see some other interesting possibilities. For instance, if you needed a counter

that stopped counting when the current value reached the preset value, you could easily make

one. I’ll leave that as a homework exercise for you.

Build a Function to Convert Celsius to Fahrenheit

Most industrial sensors will report the temperature in Celsius, but some US customers might want

their machine to have set points entered in Fahrenheit. This is a good application for a function

because the output (temperature in Fahrenheit) can be computed directly from the input

(temperature in Celsius).

Start by right clicking on the folder where you want to add your new function. Select Add-

>POU... from the context menu. In the Add POU dialog, enter

CelsiusToFahrenheit as the Name, choose Function under Type, and make sure to

enter REAL in the Return type box. Make sure Ladder Logic Diagram (LD) is selected in

the Implementation language drop down:

Page 132: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

132

Click Open to create the empty function:

Page 133: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

133

Note that the return type (REAL) is defined after the function name at the top. This is actually an

implicitly declared output variable, where the variable name is the name of the function itself. At

some point in the function you have to set this implicit variable to some value.

Now declare an input variable (TemperatureInCelsius):

The formula for converting from Celsius to Fahrenheit is to multiply by 9, divide by 5, and then

add 32. Since we’re doing this in ladder logic, let’s break it down into 3 steps and use an internal

(temporary) value. Add a variable called Temp:

Remember that any variables declared in a function will be re-initialized to their default values

(typically zero or false) at the beginning of each call to the function. If you’re familiar with other

languages like C/C++, BASIC, or Pascal, these are like local variables.

Now create the ladder logic to compute the temperature in Fahrenheit:

Page 134: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

134

In rung 1, it takes the input temperature, multiplies by 9 and stores the result in the temporary

variable. Rung 2 takes the temporary result and divides by 5, storing it back into the same

temporary location. Rung 3 takes the result of rung 2 and adds 32, returning it as the result of the

function.

Note that a purely mathematical function like this is better implemented in the Structured Text

language, but this is a good introductory example of a function with local variables implemented

in Ladder Logic. We can revisit this in a later section on Structured Text.

The VAR_IN_OUT Variable Type

In the examples above, you can see three types of variable declarations: inputs (VAR_INPUT),

output (VAR_OUTPUT) and local (VAR). There is another optional block you can add,

called VAR_IN_OUT. In other languages like C/C++, etc., this other type would be called “pass

by reference”.

With the normal inputs and output types, the value of the input is copied into the function or

function block and copied out of it into an output. For a BOOL or INT type, this happens very fast

and it also prevents a function block from inadvertently modifying an input value (which would

Page 135: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

135

normally be unexpected by someone using a function block). However, there are cases where

you want a function block to do something to a variable, or you don’t want to incur the overhead

of copying a very large variable structure into a function block. In these cases you can use

the VAR_IN_OUT type.

As an example, look at this function block that increments a number:

You would call it like this:

Notice that the Number input has a little two-directional arrow next to it indicating that it’s an

in/out variable.

Also note that we still had to declare a variable for this function block, even though the function

block itself doesn’t have any internal state. If you were the user of this function block, it might be

Page 136: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

136

a bit annoying. We can actually use a trick to fix this. Instead of declaring Increment as a

function block, we can declare it as a program:

A program’s internal state is global, but in this case it has no internal state. That means you can

use it like this:

Note that you have to add the VAR_INPUT and VAR_IN_OUT blocks manually, since the

wizard that creates a program POU only adds the VAR block.

Using a VAR_IN_OUT block is an advanced topic, but it does come up enough that you need

to be aware of it. For instance, let’s say you had a long list of measurements stored in an array

and you wanted to compute the average. Copying the array into function could be an expensive

operation (causing a higher than necessary scan time), but passing a reference to the array by

declaring it as an in/out variable is as fast as passing an integer.

As a matter of understanding, when you create a function block and you declare a variable for it

to represent the internal state (like a timer variable), that internal state variable is implicitly being

Page 137: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

137

passed to the function block by reference, which is why the function block can read and write to

that variable.

Usage

The overall structure of your automation project should be broken up into programs, with one

program representing one functional unit of your machine. The more you break it up into logical

pieces, the easier it will be to find the part of the logic you’re looking for. Average programs might

have 5 to 15 rungs of ladder logic, but this isn’t a hard and fast rule, and you should group your

logic however it makes the most sense for someone reading through your logic. Try to keep

related things together and unrelated things apart.

Use functions and function blocks to build the “domain specific language” of your application. Use

descriptive names for your functions and function blocks that clearly describe what they do. If you

see the same 5 rungs of logic repeated over and over, that’s a good indication that perhaps you

should be looking at creating a function or a function block (but only if it improves readability).

Functions and function blocks are particularly useful to contain identical bits of logic that are likely

to change. Putting logic like that in a single place makes it easy to change. However, just because

two bits of logic are identical now doesn’t mean they aren’t likely to diverge in the future. This is

actually quite common in automation programming. You might have a conveyor system with 5

identical zones, but the customer may someday want to add some feature to zone 4 that you can’t

anticipate. Making a ConveyorZone function block seems like a great idea at the beginning,

but may be a bad decision in the long run.

Making these kinds of decisions can only be informed by experience. As a general rule, I

don’t think machine control logic (e.g. contacts and coils that make the machine start and stop)

belongs in a re-used function or function block, but common elemental operations like logging an

event, computing common formulas, or communicating with some piece of hardware should be

contained in a re-usable POU. That’s because the physical and electrical configuration of the

machine is likely to change on a piece-by-piece basis, but the way you log events, or compute an

Page 138: TwinCAT tutorial - Beckhoff

Chapter 8: Writing your own Functions and Function Blocks

138

average, is only likely to change simultaneously across your entire project (or is unlikely to change

at all).

Note that functions and function blocks can access global variables too. For example, I would

expect a function block that logs an event to access a global event array where the event history

is kept. Accessing physical inputs is also fairly common. Be careful not to do something

unexpected, such as setting a physical output (each output should be driven by exactly one rung

in a program somewhere, not in a function block).

Finally, since each POU can be implemented in a different programming language, using

functions and function blocks is a great way to write parts of your project in a language that’s more

appropriate to the task at hand.

Page 139: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

139

9 – Structured TextTwinCAT 3 includes all five IEC-61131-3 languages: Ladder Diagram, Structured Text, Function

Block Diagram, Sequential Function Chart, and Instruction List. If you’re coming from the Allen-

Bradley world then obviously Ladder Diagram is going to be your most comfortable language, but

I expect you’ll also want to make use of Structured Text. In fact, Beckhoff themselves typically

present Structured Text as the go-to language for programming in TwinCAT 3.

I prefer writing most of my programs in Ladder Diagram for the obvious reasons: ease of

troubleshooting, and the ability of electricians to go online with the program and debug it.

However, we can’t forget that old adage, “use the right tool for the job,” and there are times when

Structured Text is the right tool, and Ladder Diagram is not.

Structured Text has similarities to Pascal or BASIC (at least after they removed the concept of

line numbers from BASIC). The most applicable feature of Structured Text for us are LOOPs.

The FOR Loop

Imagine for a moment that you have an array of a thousand REAL data values and you want to

compute the average of those values. The formula is pretty simple: just add them up and divide

by 1000. Obviously this presents some difficulty in Ladder Diagram, but in Structured Text, we

can just use a FOR loop.

Start by creating a new function. Call it AverageOf1000 and make sure you select a function

with the return type of REAL, and Structured Text (ST) in the Implementation Language drop-

down box:

Page 140: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

140

Click Open. Now you’ll have an empty Structured Text function:

Page 141: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

141

We could pass the array in as an input, but if you remember from the last section, that would

mean copying the entire array every time this function is called, which could negatively impact the

scan time. It’s better to pass large data structures like this by reference, which means we declare

it as a VAR_IN_OUT variable:

Next declare some local variables: one to store the sum of the values, and another to be an index

to hold where we’re pointing to in the array.

Now we can write our logic, which consists of a FOR loop and a division operation:

Page 142: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

142

On line 1 it initializes the value of variable Sum to 0. Note that the := operator means

assignment. It computes the expression on the right (the RValue) and stores it in the variable on

the left (the LValue). Also note that each statement ends with a semi-colon. This is important and

you’ll get a syntax error if you don’t include it (the exception is the semi-colon at the end of line 4,

which is optional, but frequently included in many Structured Text examples).

Lines 2 and 4 define the FOR loop. Line 2 defines a loop index variable (called Index in this

case), followed by an assignment symbol (:=). This means the Index variable will take on the

values from 1 to 1000 and BY 1 means it will count by 1. The lines between 2 and 4 are what

will be executed with each value of Index.

If you were to watch the runtime execute this logic, what you’d see is (roughly):

Set Index to 1

Execute line 3

Set Index to 2

Execute line 3

Set Index to 3

Execute line 3

Set Index to 4

Execute line 3

Set Index to 5

Execute line 3

Set Index to 999

Execute line 3

Set Index to 1000

Execute line 3

As you can see, loops can have a significant impact on scan time, especially as the number of

iterations becomes high. If you’re running TwinCAT 3 on a modern PC, then 1000 iterations isn’t

too bad, but executing a million iterations on a 2 GHz PC is likely going to take a minimum of 0.5

milliseconds, and that’s without doing anything in the loop. You have to be aware of this and

Page 143: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

143

program accordingly. If you’re averaging the list of the last 100 sensor readings, don’t even worry

about it, but if you’re doing math-heavy computation on thousands of data points, be aware that

it might be too much work to do in one scan time.

Line 5 takes the Sum and divides by 1000, assigning the result to the return value of the function.

Note that I added a decimal point to the value 1000.0 and I did this to remind the reader that

I’m dealing with floating point numbers here. This is a style choice. You don’t have to do it.

The WHILE Loop (and IF/THEN/ELSE Blocks)

Another type of loop is the WHILE loop. Instead of executing a fixed number of times like

a FOR loop, it can execute as long as some condition is true. For instance, let’s say we want to

find the first index in an array where the value is greater than some value:

Page 144: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

144

The purpose of this function is to search an array of 1000 values and return the first index where

the value is greater than some Threshold. If it doesn’t find any values greater than

the Threshold then it returns 0, which is an invalid index.

Line 1 initializes a Boolean flag, Found, to FALSE. Since this is a function, it’s not really

necessary because the value would be initialized to false every time you call the function, but if

this was a function block, then you’d want to include that line because the value would be retained

from call to call.

Line 2 initializes the Index variable to the first array index (1). Lines 3 and 9 define the

WHILE loop. Lines 4 through 8 will be executed repeatedly as long as the expression in line 3

Page 145: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

145

returns true. As you can see, we loop until either we find it, or the Index passes the upper bound

of the array.

Lines 4 through 8 comprises an IF/THEN/ELSE block. If the expression in line 4 is true, then

it executes line 5. If the expression on line 4 is false, then it executes line 7 instead.

To demonstrate how this works, assume the values in the array are 25, 50, 75, 100, 125, etc. Also

assume Threshold is 80. We would expect the function to return a value of 4. Here’s how the

function executes:

Line 2 sets Index to 1

Line 3 evaluates to true because Found is false and Index is 1

Line 4 evaluates to false (25 is not greater than 80)

Line 7 sets Index to 2

Line 3 evaluates to true because Found is false and Index is 2

Line 4 evaluates to false (50 is not greater than 80)

Line 7 sets Index to 3

Line 3 evaluates to true because Found is false and Index is 3

Line 4 evaluates to false (75 is not greater than 80)

Line 7 sets Index to 4

Line 3 evaluates to true because Found is false and Index is 4

Line 4 evaluates to true (100 is greater than 80)

Line 5 sets Found to true

Line 3 evaluates to false because Found is true

Line 11 evaluates to true

Line 12 sets the return value of the function to 4 (because Index has the value 4)

While this is a perfectly reasonable function, there are also some problems with it.

First of all, the scan time is quite variable. The worst case scan time is when the value isn’t found,

and it returns 0. In that case it iterates through the entire array. In the best case it returns 1.

Variable scan times can lead to problems if the worst case is never tested, or if you have a lot of

functions like this and there’s some diabolical case where all of them have to execute the worst

case on the same scan, and you exceed your allowable scan time.

Page 146: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

146

Secondly, the logic is complex. Some of you might be laughing at me for saying that. If you’re a

PC programmer writing code in C or BASIC then the function above is actually quite simple, yet

in PLC programming we have an abnormal emphasis on simplicity. We want logic that

is obviously correct when we look at it, and the above function isn’t obviously correct unless you

give it a significant amount of analysis. To analyze it you really have to “play computer” and walk

through at least 2 different scenarios: one where the value is found, and one where it’s not found.

Earlier in this section I talked about expecting electricians to go online with our programs and do

troubleshooting. An electrician can understand Ladder Diagram, and with a little bit of work they

can probably understand the FOR loop example above, but there are going to be a lot of people

who won’t be able to understand this example of a WHILE loop with IF/THEN/ELSE blocks.

If you believe these people don’t have any business going online with a PLC, then suggest you

should change your attitude. Automation is a team sport and we have no room on the team for

big egos.

Use the simplest logic you possibly can (not the shortest). If the machine you’re programming

has 10 motors, don’t try to write the motor start/stop logic in Structured Text with a FOR loop.

Don’t even make a function block and re-use it 10 times. Just write 10 different programs in Ladder

Diagram and copy the logic. Sure they might share some common logic, like

an OkToRunMotors coil that gets set in another program. Remember that these are 10

physically different motors and the conditions for starting and stopping them are likely to change

over time. Recognize that and keep the logic separate.

On the other hand, Structured Text is the right tool for the event-logging and recipe-handling logic

of a program. An electrician logging into the PLC to understand why a motor isn’t starting isn’t

going to be concerned with the event-logging module. Structured Text is also the right tool for

manipulating data, such as a scan received from a barcode scanner or an RFID reader. Complex

math is also more easily expressed in Structured Text.

Using the right tool for the job means taking more than the problem itself into account. Make sure

you take the capabilities of your team and the customer’s capabilities into account too.

Page 147: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

147

Don’t Loop on an Input

A novice programmer will write this:

Notice how we’re looping on an input. An input is a real-world physical input. It only changes when

an I/O scan happens. When the runtime executes this logic, it will enter the loop and potentially

never exit, and none of the rest of your program will execute again. The machine will appear to

freeze, outputs will stay in their last state, and bad things will happen. Simply, if you’re using an

input as the conditional in a WHILE loop, then you don’t have a good understanding of how the

PLC runtime works, and you need to stop and go back to the beginning.

Most PLCs work by reading the physical inputs into memory, running the program logic, and

copying the new values of the outputs to the actual physical outputs (that’s a simplification and

not true of all PLCs, but it’s a good model to start with), and then doing it again and again. The

amount of time it takes to do all that is your scan time, and we want the scan time to be as short

as possible. Causing the program to enter a loop that waits for an input to turn on will essentially

stop the program. In some cases it will also prevent the I/O scan from happening, so it’s

impossible for that input to change state again. The machine will freeze forever.

Ladder Diagram doesn’t give you the option of shooting yourself in the foot like this, but Structured

Text does. Stay away from infinite loops.

Mixing Ladder Diagram and Structured Text

I’ve shown you how you can write programs, functions, and function blocks in Structured Text,

but sometimes it’s nice to add a little Structured Text in the middle of your Ladder Diagram

program. It turns out that a program can include something called an Action (which is like a mini

local sub-program that you can call from your program) and the Action can be written in a different

implementation language than the parent program.

Page 148: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

148

To add an Action, right click on an existing (Ladder Diagram) program and choose Add-

>Action… from the context menu. All you have to enter is a Name and choose an Implementation

Language. Choose Structured Text. The new Action will show up in the Solution Explorer under

your program.

The Action has access to all the declarations (inputs, outputs, and local variables) of the parent

program or POU. You can call the Action like any other program: just add a block and enter

the Action name.

String Functions

String functions can be used in both Ladder Diagram and Structured Text, but when you start to

do complicated string manipulation then I suggest moving into Structured Text because it can be

easier to understand.

Here are your typical string functions and what they do:

LEN(s) – returns the number of characters in string s

LEFT(s, n) – returns the n left-most characters from string s, or returns s if n > LEN(s)

RIGHT(s, n) – returns the n right-most characters from string s, or returns s if n > LEN(s)

MID(s, n, p) – returns n characters from string s, starting at position p (first character

number is 1, not 0)

CONCAT(s1, s2) – returns strings s1 and s2 joined (concatenated) together

INSERT(s1, s2, p) – returns a new string formed by inserting s2 into s1 at position p

DELETE(s, n, p) – the opposite of MID, returns string s with the n characters starting

at position p removed

REPLACE(s1, s2, n, p) – combines DELETE and INSERT – removes n

characters from s1 starting at position p, and replaces them with s2

FIND(s1, s2) – returns the position of string s2 in string s1, or 0 if not found, and is case

sensitive

Page 149: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

149

You can, of course, create your own string functions. For instance, it might be useful to have a

different replace function that takes a 3 strings: a string to search, a string to find, a string to

replace all instances of the found string with:

Notice how the variables are declared as T_MaxString instead of STRING…

STRING Limitations

Since variables are statically allocated in TwinCAT 3, when you define a STRING variable you

have to declare the length. Implicitly this is 80 characters, and it uses up 81 bytes of memory (80

for the data and one byte for a null terminator). Strings are limited to a length of 255 characters.

There is a specific type called T_MaxString which is an alias for STRING(255).

Be careful because TwinCAT 3 will silently truncate a string to the maximum defined length of the

destination string when you do an assignment.

When you make your own string functions, you should use T_MaxString as the variable type

to make sure they work with any string passed to them. If you don’t, the input and output variables

will be silently truncated to the length you specify.

Page 150: TwinCAT tutorial - Beckhoff

Chapter 9: Structured Text

150

Conclusions

Structured Text is a powerful tool. In some PLCs, like the Allen-Bradley ControlLogix line, you

have to pay extra for the Structured Text editor, but with TwinCAT 3 you get it for free. (Actually,

you get the Ladder Diagram editor for free too…)

With great power comes great responsibility. Use your new powers wisely and sparingly. When

programming PLCs, the first priority is correctness and the second priority is readability. Nobody

gets points for writing fewer lines of code. Remember that.

Page 151: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

151

10 – Building an HMI in .NETBeckhoff offers a paid HMI add-on for TwinCAT 3. That add-on is reasonably priced and offers

basic functionality such as buttons, indicators, alarms, and more. There are also 3rd party HMI

solutions that are compatible with TwinCAT 3, and there’s an OPC-UA server available for

TwinCAT 3, so any HMI, SCADA or historian system that supports OPC-UA should be compatible

with TwinCAT 3.

I’m going to describe an alternative. TwinCAT 3 comes with a free DLL that allows you to

communicate from a Windows application directly to the PLC over Beckhoff’s ADS protocol. Since

you can download a free version of Visual Studio from Microsoft (called the Express Edition, or

more recently Community Edition), this is an interesting way to add an HMI to your system without

paying additional software costs. Additionally, unlike packaged HMI solutions with their pre-

defined set of features, .NET’s functionality is comparatively unlimited. An application written in

.NET can do anything an HMI package can do, and more.

I can’t teach you VB.NET or C# in this short tutorial. There are so many excellent resources

available online for learning VB.NET or C# that adding anything here would be superfluous, so

I’m going to assume that you already know some .NET programming. If so, you probably already

understand why using .NET to build your HMI would be a better alternative than paying for a 3rd

party solution (in your case, at least).

After you’ve installed TwinCAT 3, the ADS DLLs are located in C:\TwinCAT\AdsApi.

The .NET DLLs are located in the .NET sub-folder. Pick the version corresponding to the .NET

version that you’re using. If you’ve downloaded Visual Studio Express or Community edition

version 2010 or better, then you’ll want the DLL for version 4.

Page 152: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

152

Simple Example: a Button

In your TwinCAT 3 PLC project, create a Global Variable List called MyGVL, and inside that

create a BOOL variable called MyBoolVariable. Go ahead and do that now, then activate

the configuration and go online so you can see the value of that variable.

Assuming you have Visual Studio 2010 Express or better installed, open it and use the new project

wizard to create a Windows Forms project. I find the Windows Forms graphical editor to be more

“HMI-like”. Of course you could also develop your HMI in WPF, but the concepts will be similar.

In the Solution Explorer window, under Solution->HmiTest->References, right click on

References and choose Add Reference… from the context menu. Choose the Browse tab, and

navigate to the C:\TwinCAT\AdsApi\.NET\v4.0.30319 folder, and select

the TwinCAT.Ads.dll file. Click OK.

The new project wizard should have created a new form for you called Form1. Open that form

in the forms editor, and drag a button from the Toolbox onto the form (button1). Double-click

on the button to create a click event handler (button1_Click).

Put this in the click event handler:

private void button1_Click(object sender, EventArgs e){

using (var client = new TwinCAT.Ads.TcAdsClient()){

client.Connect(851);client.WriteSymbol("MyGVL.MyBoolVariable", true,

reloadSymbolInfo: true);Thread.Sleep(1000);client.WriteSymbol("MyGVL.MyBoolVariable", false,

reloadSymbolInfo: true);}

}

This isn’t a very practical example, but it’s a simple introduction to the ADS DLL. When you click

the button, it creates a new ADS client. By default, this client will connect to the TwinCAT 3 (or

TwinCAT 2) runtime on the local machine (but there’s an optional parameter to Connect which

will make it connect to a remote node). You have to specify a port number. The default port

number of the first PLC instance is 851 for TwinCAT 3 (it was 801 for TwinCAT 2). If you’re running

multiple virtual PLCs in your system, the second would be 852 and so on.

Page 153: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

153

The WriteSymbol method looks up a variable by name, and then writes the given value to

that variable. In this case, we write true, wait 1000ms and then write false.

You should be able to run your new Windows Forms program and click the button. If you have

the TwinCAT 3 solution open in the background and you’re online with the Global Variable List,

you’ll see the value of the variable change to TRUE for 1 second, and then change back

to FALSE.

This example is simple, but it has several problems. First, creating a new client object every time

is simple but inefficient. Second, the port shouldn’t be specified in each button click handler. Third,

the variable name is entered twice and is entered in the client event handler, when really it should

be a property of the button. Fourth, just writing true, pausing for 1 second, and writing false

actually freezes the HMI during that second, and is just a bad way to do it. We should be using

the MouseDown and MouseUp events instead (to write a true and a false value, respectively).

In order to handle cases where the PLC runtime is stopped, we really want to block reads and

writes because it can really slow down the HMI waiting for timeouts to expire. We also want to fail

gracefully if the variable doesn’t exist (either because of a typo or because the variable name

changed on the PLC side and someone forgot to update the HMI).

A Better Architecture

The requirement to use a single ADS client, and the requirement to block communications when

the PLC isn’t responding, and to report on bad variable names implies the need for a centralized

communication manager object, or at least one object for each virtual PLC.

The requirement for a variable name to be associated with each button or indicator implies the

variable name should be a property of the button or indicator control.

I suggest creating custom UserControls for a standard momentary button and a standard

indicator, each with custom properties to define the variable name and the indicator colors in the

case of the indicator. At the beginning of your program, you instantiate a new communication

manager object, and then register all of the buttons and indicators with the communication

manager. This can be done fairly painlessly with a few lines of code. Then, adding a new button

Page 154: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

154

or indicator is as simple as dragging and dropping one of your custom user controls from

the Toolbox onto your form and setting the variable name in the property page.

Example Communication Manager

You can create a custom MomentaryButton by creating a new class and inheriting

from Button:using System.Windows.Forms;using System.ComponentModel;

namespace HmiTest{

class MomentaryButton : Button{

[Description("The BOOL Variable in the PLC to write to"),Category("PLC")]

public string VariableName { get; set; }}

}

Here I’ve taken the regular windows forms Button control and added a custom property

called VariableName. When you drag a MomentaryButton out of your Toolbox onto

your form, you’ll be able to edit the new VariableName property in the property page. Note

that you’ll have to build your project to get it to show up in theToolbox.

You can create an Indicator by inheriting from Label:

using System.Windows.Forms;using System.ComponentModel;using System.Drawing;

namespace HmiTest{

[ToolboxItem(true)]class Indicator : Label{

public Indicator(){

this.AutoSize = false;this.Width = 100;this.Height = 25;this.TextAlign = ContentAlignment.MiddleCenter;this.BorderStyle = BorderStyle.FixedSingle;this.OnColor = Color.Green;this.OffColor = Color.DarkGray;

}

[Description("The BOOL Variable in the PLC to read from"),

Page 155: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

155

Category("PLC")]public string VariableName { get; set; }

[Description("Color when the Variable is TRUE"),Category("PLC")]

public Color OnColor { get; set; }

[Description("Color when the Variable is FALSE"),Category("PLC")]

public Color OffColor { get; set; }}

}

Here I’ve customized a bit more by adding a default height and width, aligning to center, setting

a border and turning off the auto-size feature. In addition to a VariableName property, I’ve

also added OnColor and OffColor properties, which will control the background color of

the indicator when the value of the variable is TRUE or FALSE, respectively.

Most of the functionality is in the new CommunicationManager class:

using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Windows.Forms;using TwinCAT.Ads;

namespace HmiTest{

class CommunicationManager : IDisposable{

private readonly int port;private readonly TcAdsClient client = new TcAdsClient();private readonly List<Action> pollActions

= new List<Action>();private readonly Dictionary<string, DateTime> readWriteErrors

= new Dictionary<string, DateTime>();private bool connected;private DateTime? lastErrorTime = null;

public CommunicationManager(int port){

this.port = port;}

public void Poll(){

foreach (var action in this.pollActions){

action();}

}

Page 156: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

156

public bool IsConnected{

get { return this.connected; }}

public ReadOnlyCollection<string> GetReadWriteErrors(){

var result = this.readWriteErrors.Keys.OrderBy(x => x).ToList();

return result.AsReadOnly();}

public void Register(Control control){

if (control == null) return;if (control is MomentaryButton){

this.register(control as MomentaryButton);}else if (control is Indicator){

this.register(control as Indicator);}

}

private void register(MomentaryButton momentaryButton){

momentaryButton.MouseDown += (s, e) =>{

this.doWithClient(c =>{

c.WriteSymbol(momentaryButton.VariableName,true,reloadSymbolInfo: true);

}, momentaryButton.VariableName);};

momentaryButton.MouseUp += (s, e) =>{

this.doWithClient(c =>{

c.WriteSymbol(momentaryButton.VariableName,false,reloadSymbolInfo: true);

}, momentaryButton.VariableName);};

}

private void register(Indicator indicator){

this.pollActions.Add(() =>{

this.doWithClient(c =>{

if (string.IsNullOrWhiteSpace(

Page 157: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

157

indicator.VariableName)){

return;}bool value = (bool)c.ReadSymbol(

indicator.VariableName, typeof(bool),reloadSymbolInfo: true);

indicator.BackColor = value? indicator.OnColor: indicator.OffColor;

}, indicator.VariableName);});

}

private void doWithClient(Action<TcAdsClient> action,string variableName)

{this.tryConnect();if (this.connected){

try{

action(this.client);this.readWriteSuccess(variableName);

}catch (AdsException){

readWriteError(variableName);}

}}

private void tryConnect(){

if (!this.connected){

if (this.lastErrorTime.HasValue){

// wait a bit before re-establishing connectionvar elapsed = DateTime.Now

.Subtract(this.lastErrorTime.Value);if (elapsed.TotalMilliseconds < 3000){

return;}

}try{

this.client.Connect(this.port);this.connected = this.client.IsConnected;

}catch (AdsException){

connectError();}

Page 158: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

158

}}

private void connectError(){

this.connected = false;this.lastErrorTime = DateTime.Now;

}

private void readWriteSuccess(string variableName){

if (this.readWriteErrors.ContainsKey(variableName)){

this.readWriteErrors.Remove(variableName);}

}

private void readWriteError(string variableName){

if (this.readWriteErrors.ContainsKey(variableName)){

this.readWriteErrors[variableName] = DateTime.Now;}else{

this.readWriteErrors.Add(variableName, DateTime.Now);}

}

public void Dispose(){

this.client.Dispose();}

}}

Yes, it’s a little beefy for a class, but it does quite a bit, including managing the connectivity to the

PLC and recording read/write errors for missing or misspelled variable names. Here’s how you

use the CommunicationManager in your form:

First, add a System.Windows.Forms.Timer to your form (in the forms designer) and

rename it to tmrPoll. I left the Interval at 100 ms, which is the default.

Then add a readonly instance of CommunicationManager as a form member

variable, and add the following in the form’s constructor:

using System.Windows.Forms;

namespace HmiTest{

public partial class Form1 : Form

Page 159: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

159

{private readonly CommunicationManager communicationManager

= new CommunicationManager(851);

public Form1(){

InitializeComponent();

foreach (var control in this.Controls){

this.communicationManager.Register(control as Control);

}this.tmrPoll.Tick += (s, e) =>

{this.communicationManager.Poll();

};this.tmrPoll.Start();

}}

}

Once you’ve done that, then you can drag and drop your new MomentaryButtons

and Indicators on your form, set

the VariableName, Text, OnColor andOffColor properties, and you’re done.

There are also some troubleshooting features. If you want to display whether the connection to

the PLC is “alive”, then use a timer to query the

connectionManager.IsConnected property and update something on the screen to

indicate it’s connected or not. You could also log the disconnection to a file, etc. You can also get

a list of read/write errors by calling connectionManager.GetReadWriteErrors().

Here’s an example of a button click event handler that will call the function, take the result, and

display it in a message box:

private void button1_Click(object sender, EventArgs e){

var readWriteErrorVariableNames= this.communicationManager.GetReadWriteErrors();

var readWriteErrors = string.Join(Environment.NewLine,readWriteErrorVariableNames);

if (string.IsNullOrWhiteSpace(readWriteErrors)){

MessageBox.Show("None");}else{

MessageBox.Show(readWriteErrors);

Page 160: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

160

}}

That’s a little crude, but it illustrates the idea.

More Ways to Read and Write Data

For more .NET examples on reading and writing PLC variables, see the following Beckhoff

information page:

https://infosys.beckhoff.com/english.php?content=../content/1033/tcsample_labview/html/tcsam

ple_labview_overview.htm&id=

These sections are particularly useful:

Reading Arrays

Writing Structures

Reading and Writing Strings

Reading and Writing Time and Date Variables

Some Things to Note

Here are some lessons I’ve learned:

If the HMI is running on the same PC as the runtime, ADS is very fast.

Try to keep the number of reads per second to less than 200 if on the same PC.

The amount of data you get in a single read doesn’t seem to affect performance much, so

reading an array or a large structure is very efficient.

If an indicator can’t be seen, there’s no reason to poll the value or update it. If you build your

HMI as a bunch of tab pages, before you go to read an indicator’s value, check to see if the

parent tab page is visible. If not, skip it.

The CommunicationManager above isn’t thread safe.

Page 161: TwinCAT tutorial - Beckhoff

Chapter 10: Building an HMI in .NET

161

I have reason to believe the TwinCAT.Ads.dll is not thread safe. If you need a multi-

threaded HMI, write a thread safe wrapper for the client, and serialize all calls with locks.

Further Thoughts

This chapter only scratches the surface of what you can do with .NET and TwinCAT 3 working

together. Obviously .NET has strong capabilities for connecting to databases, accessing files and

network resources, and communicating with many other peripherals. TwinCAT 3 is a fast real-

time system with a large library of industrial hardware to choose from. The ADS DLL joins the

performance of TwinCAT 3 with the versatility of .NET programming.

As you can see, adding more custom controls is fairly simple. You can easily add gauges, bar

graphs, and other graphical indicators to display data from the PLC. You can use array and

structure reads to implement event logging, alarms, and historians, and store that data in a local

database, or a database on a server in your data center. You can report on that data with the

Microsoft Chart control, or the Microsoft Report Viewer. You can have the HMI send emails when

critical events happen.

Customers are calling for more and more connectivity between the automation system and the

ERP and MES systems. Data and connectivity are big topics. TwinCAT 3’s seamless integration

with .NET is a primary advantage over other automation solutions.

Page 162: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

162

11 - Introduction to Motion ControlMotion Control is a big topic. Motion Control refers to the use of servo (and stepper) motors in

your system. A servo requires a motor and a position feedback device such as a resolver or an

encoder, and it controls the position of the motor using a feedback control system.

Motion Control systems are not new, but they have a parallel lineage to PLCs. Since around 2000

we’ve seen more and more convergence of Motion Control and PLC systems, but they are still

separately packaged modules within the system. In TwinCAT 3 they are separately licensed

modules. The PLC system is licensed as TwinCAT PLC, and the motion control system is called

TwinCAT NC. The latter is licensed depending on how many “axes” (i.e. servo motors) your

system needs. The base package is 10 axes, and you can add more axes for more money.

The “NC” in TwinCAT NC means “numerical control”. It’s the same NC as “CNC machines”, which

can mill (or cut) away material from a block of steel with accuracies better than a thousandth of

an inch. In fact Beckhoff originally got its start in motion control systems, so TwinCAT 3 includes

one of the better motion controller systems available today.

The TwinCAT NC module is just a basic motion controller that gives you “point to point” motion.

That is, each axis is controlled independently. If you need your axes to work in a co-ordinated

way, such as in a 3-axis CNC mill where you need to follow a complex path in 3 dimensions, then

you need “interpolated motion”. TwinCAT 3 offers this as an add-on to TwinCAT NC, called

“TwinCAT NC I”. This add-on actually includes a built-in G-code interpreter. G-code is the

language that CNC mills (and FDM-type 3D printers) use to instruct the motion controller on the

path to follow to create a finished part.

Industrial robots, such as those from ABB, Fanuc, Motoman, etc., are actually controlled by

interpolated motion controllers internally. It’s no co-incidence that Fanuc is both a leading supplier

of industrial robots and CNC mill controllers. The technology is similar. In fact TwinCAT 3 does

support kinematics packages for industrial robots, and you can even purchase a Codian Delta

Page 163: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

163

Robot through Beckhoff and use TwinCAT NC I with a delta robot kinematics package to control

the robot directly from TwinCAT 3.

Since motion controllers have evolved from their origins in CNC milling and industrial robots, the

sequential nature of their programming (in the form of G-code or proprietary robot programming

languages) has never fit well with ladder logic programming, and gives rise to what I call

the Ladder Logic/Motion Controller Impedance Mismatch.

In PLC programming it’s typical to think in terms of discrete outputs. Turning on an output can do

many things, such as turning on a pneumatic valve to apply air pressure to a cylinder, turning on

a motor to run a saw or a pump, or turning on a heater to control the temperature of a fluid. In all

of these cases you can think of a discrete output meaning “try to do X.” For instance, “try to extend

this cylinder,” or “try to start motor,” or “try to increase temperature.” We normally have feedback

mechanism like a proximity switch, or a level or temperature sensor to tell us that our action

succeeded. This try/confirm idea naturally gives rise to ladder logic programming patterns like

the Five Rung Logic Block and the Step Pattern. Implicit in the Five Rung logic is the idea of “when

is it safe to perform (and continue performing) this action?”

Motion controllers, on the other hand, are designed to follow a strict set of sequential instructions,

with little consideration of how to recover when things go wrong. Consider the idea of a weld

robot. It waits for a part to be loaded, follows a path (series of points) to move the weld tool into

position, clamps the welding tool, fires the welder, unclamps, and follows another path back to

the home position. The linear nature of the sequence makes this particularly easy to program, but

what if someone opens the gate and removes the part while the robot is moving in towards the

part? The robot can’t continue the program because the part is no longer there. However, the

check for part presence would have been done at the beginning of the program. That means you

need to insert a second part presence check right before you clamp the welder, and if it’s not

present then skip over the weld and jump to the move-out sequence. Unfortunately this means

the robot recovers by continuing in towards the missing part. The “right” thing would be to move

back out immediately, but programming a way for the robot to find its way back away from the

part without crashing into an obstacle can be quite difficult and complicated. That’s why many

industrial robots are tended to by operators who frequently have to enter the cell with a teach

Page 164: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

164

pendant and recover by manually “jogging” the robot safely around obstacles back to a known

home position.

If the motion was done by a PLC controlling a sequence of air cylinders, the recovery logic would

be comparatively simple: just retract them in the right sequence. Typically your “safety” rung in

your Five Rung logic block already contains the logic about when it’s “safe” to retract those

cylinders without crashing, so if you give them all the trigger signal to retract, they’ll automatically

retract in the right order.

When a new control system programmer comes out of school, they always want to program the

industrial robots because they look so cool. However, I always tell them, “the robots are stupid

but PLCs make them smart.” For a PLC to make a robot smart, it needs to know the current robot

(or motion controller) position. If you don’t know where the robot is, how can you program a smart

recovery path? Unfortunately, getting the current robot position out of the robot and into the PLC

can be difficult at best, and nearly impossible with some older robot controllers. Thankfully, with

TwinCAT 3, the motion controller and the PLC are operating in the same runtime, so the PLC

automatically has access to all motion controller information, which makes your job a lot easier.

Creating a Motion (NC) Task

To add a motion controller to your TwinCAT 3 project, go into the Solution Explorer and find

the MOTION node directly below the SYSTEM node. Right click on the MOTION node and

choose Add New Item… from the context menu. That will pop up the Insert Motion

Configuration dialog box. Choose NC/PTP NCI Configuration and click Ok.

You can only insert one motion controller. The motion controller automatically defines a task and

that task can be scheduled under the SYSTEM->Real-Time node just like your PLC tasks.

Typically this should be a high priority task (higher than your PLC tasks) and should run often,

such as once every one or two milliseconds, even if your PLC only runs once every 10

milliseconds. This task handles all communication with the servo drives (even for drives from

manufacturers other than Beckhoff) and provides a generalized “axis” interface for your PLC to

deal with. It can also close the position feedback loop for some less expensive drives that don’t

Page 165: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

165

offer a position loop, synchronize multiple axes in an interpolated system, and perform kinematic

transformations in the case of industrial robots. It even has a G-code interpreter.

Adding A Servo Drive and Axis

Beckhoff offers a really good line of EtherCAT servo drives in their AX5000 range. It’s also

advantageous to purchase a Beckhoff servo motor so you know they’re compatible (and it makes

the configuration simpler). In order to size your servo motor you need to start with the performance

requirements of your mechanical system (such as load inertia, required rates acceleration,

deceleration, speed, and expected duty cycle. I suggest taking this information to your local

Beckhoff sales representative and they can help you to size the motors and drives accordingly.

When sizing the motor, optimal performance is typically achieved when the load inertia and motor

inertia are approximately equal, though in practice this is sometimes impossible. Other

mechanical considerations will also factor in: for instance, a belt drive can sometimes give you

higher top speeds, but at the expense of lower acceleration and deceleration (due to the belt

flexing), so adding more torque won’t help if your mechanical system can’t handle it.

Beckhoff also offers an interesting line of lower cost servo drives that can connect right into the

terminal strip of an EK1100 head module. These are limited to around 48V and 4A max, and the

position loop is closed in the motion control task rather than in the drive itself, but for lower

performance smaller applications these can offer significant cost savings. There is also a line of

similar terminal slices for stepper motor control, which offers an even lower cost alternative, if

your application doesn’t need servo-class performance.

In Solution Explorer, under the I/O node, go to the point in your I/O tree where you’re going to

attach the drive (or you can just do a bus scan too). When you add the drive (manually or with a

scan), it will say, “EtherCAT drive(s) added. Append linked axis to NC-Configuration“? If you

choose Yes, it will add an axis object under the NC-Task in the MOTION node, and it will link the

drive I/O under that axis to the new drive under the I/O node. Typically you would

answer Yes when programming a system for the first time.

Here’s what it looks like after I’ve added an AX5118 drive (single axis 18 amp):

Page 166: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

166

I highlighted the new drive and the new axis. Note that you can (and should) rename the drive

and axis to match the real name of your axis in your electrical drawings.

Note that if you expand the Axis->Drive node you’ll see that it’s automatically linked some I/O to

the physical I/O under the actual drive:

Page 167: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

167

You never have to touch these mappings yourself, even if you’re connecting the drive to the axis

manually. Double-click on Axis 1 under the Axes node, and go to the Settings tab. You’ll see

where the drive I/O is linked to the axis:

If you click the Link To I/O… button, it’ll give you a list of drives available for linking.

Configuring the Drive

Double-click on the drive under the I/O. All of the configuration is done under the Drive

Manager tab. The first thing you want to do is setup the motor and feedback. If you’ve purchased

Page 168: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

168

a Beckhoff motor, this is simple. In the tree, go to Channel A->Configuration->Motor and

Feedback. Click the Select motor button on the right:

That will open the Select a Motor dialog box. You will need the motor part # from the motor

nameplate, or from the motor ordering information. TwinCAT 3 includes a datasheet for each

motor, so it knows all the motor characteristics and feedback characteristics. Select your motor

and click OK.

TwinCAT 3 will then display a dialog box titled, Power supply and extra settings for:

Page 169: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

169

This is a “gotcha”. Most people would think this means you should specify what voltage we’re

supplying to the drive, which in the United States would be 480V, 3 phase, 60 Hz. However, since

I specified a Beckhoff motor and it was designed for Europe, I actually must keep the voltage set

to 400V or I get an error (the voltage has to match the motor voltage). This was several TwinCAT

3 versions ago, so I don’t know if anything has changed since then, so it might be a good idea to

check with your Beckhoff representative about this. Of course, these are still power supply

settings (relating to the incoming power) so if you set the voltage to 400V with 10% tolerance, and

connect it to 480V, the drive will fault because the input voltage is too high. Since the drive (and

apparently the motor) is actually 480V tolerant, the “correct” way to handle this situation is to

select Other settings, and then manually edit the U+rng setting to 30.0% (this is the maximum

value allowed). This will allow your voltage to go up to 520V which is an 8% over-voltage

allowance based on 480V. This means the drive will actually work from 360V up to 520V. Here

are the settings:

Note: in my latest project (spring of 2016) I was able to specify 480V for all the servo motors, so

it looks like Beckhoff has updated the software to fix this problem since I wrote it.

Click OK.

Note that if you’re in Canada, the typical industrial voltage is 600V. Unfortunately this means you’ll

have to use a step-down transformer to use these drives. Since there are also many other devices

that can run off 480V, it’s typical to step-down to just 480V instead of 400V, and use the settings

shown above.

Page 170: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

170

After setting the power supply settings, it will ask if you want to set the scaling and parameters

now. If this is your first time adding a motor to this drive, say OK, but if you’ve already modified

these parameters and you’re just selecting the motor again (or selecting a new motor), you’ll

probably want to say Cancel.

If you click OK, then it will display the Scaling and NC parameters node in the drive manager

tree:

Make sure you set the Feed constant (highlighted). Assuming you’re driving a linear axis of some

kind (rail or ball screw), this is the distance that the linear axis moves in one revolution of the

motor. As soon as you set it, click the Set NC Parameters button. This will set the default NC

parameters (below).

You need to set reasonable motion limits for the axis (this is done in the Axis node under Axes,

on the Parameters tab). Make sure you do this before trying to move your axis. This will

depend on your application. For instance, it sets Maximum Velocity to 100% of the maximum

motor speed. However, the mechanical limitations of your machine may not be able to tolerate

that speed. You have to calculate that maximum linear speed your system can handle, and set

this parameter accordingly. Similarly, set reasonable limits for Manual Velocity (a.k.a. “jogging”).

Page 171: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

171

The automatically calculated values are typically much higher than you would want during system

testing and start-up. Calibration Velocity is used during the homing routines.

Finally, setup the digital I/O on the drive. Not all drives have Digital I/O, but most at least offer

inputs for limit switches. These are important, so let’s take a short detour on the topic of axis limits.

An axis can have up to 3 sets of limits:

1. Software Limits

2. Hardware Limit Switches

3. Hard Stops

Software Limits are software-only limits on the axis travel. They are practically free, but they’re

also the least useful. First of all, software limits are only useful after the axis has been calibrated

(homed), so they won’t protect you during system startup, or during the homing operation.

Secondly, software limits depend on the motor’s encoder value, so it won’t catch certain classes

of failures such as a shaft coupler break, a belt break or a tooth skip on a belt. You set your

software limits in the Axis node under Axes, on the Parameters tab:

Page 172: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

172

Hardware Limit Switches are physical limit switches that sense the axis has moved beyond the

expected range of motion. Ideally these should be mounted to sense the carriage position (after

the belt, if you have one, or after the shaft coupler or anything else likely to break). Typically these

are normally closed switches, proximity sensors, or through-beam type sensors. The state of the

sensor should be “on” when the axis is in the “ok” range, and “off” if it exceeds the limit. This

protects you against a cut wire or blocked sensor. They need to be positioned far enough away

from the end of travel to allow the axis to decelerate from maximum speed to a stop (or almost to

a stop) before hitting the Hard Stop.

The Hard Stop is a physical limit on the axis. Ideally this needs to be able to take the impact of

the axis hitting it at full speed, though this is rarely possible on heavier loads. Note that shock

absorbers can be installed as well. If you’re wondering why the Hard Stop needs to be able to

take an impact at full speed, consider the worst possible scenario: a power outage, or some kind

of motor cabling fault while the axis is moving at top speed and near the end of the axis travel.

The drive won’t be able to decelerate the axis, even when it trips the hardware limit switch, so the

only option left is a crash. Thankfully this is rare, but it should be designed against if possible.

Your specific choice of axis limits is application-dependent. However, on a typical ball-screw or

belt-drive type axis I suggest the default should be normally closed Hardware Limit Switches,

placed at a reasonable deceleration distance from a physical Hard Stop capable of stopping the

axis in the event of a full speed impact. Given that scenario, I think Software Limits are

unnecessary. As always, work with your mechanical designer(s) to find a reasonable solution for

your application.

An AX5000 series drive has optional I/O that you can configure for your application, including

positive and negative overtravel limit switches. In the drive’s Device Manager tab, under

the Device->Digital I/O node in the tree, see the Digital I/O settings box. In the AX5000 series,

find parameter P-0-0401 and expand it:

Page 173: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

173

Expand Positive limit switch and set the following 3 parameters:

Configuration

Limit switch reaction

Input number

Configuration and Input number are self-explanatory. For the Limit switch reaction, note that

Beckhoff drives categorize events into 3 categories: Class 1 Diagnostic (fault), Class 2 Diagnostic

(warning), and Class 3 Diagnostic (status), a.k.a. C1D, C2D, and C3D.

Simply, C1D is a “drive shut down” error, kicks the drive out of OP mode into ErrSafe-OP mode,

and requires you to execute a drive reset command to clear the error (from the PLC, this is

accomplished with an FB_SoEReset). This seems rather harsh for an overtravel limit fault,

which really isn’t an internal drive, motor, or feedback error. I lean towards using a C2D warning,

with an E-Stop. Ultimately it’s up to you to choose a reasonable configuration. Does a Hardware

Limit Switch fault in your application indicate something internal has malfunctioned?

For selecting E-Stop vs. Axis halt, this refers to programmable error reactions. In the Drive

Manager tab, go to the Channel A->Configuration->Error reaction / drive Halt node in the tree:

Page 174: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

174

Here you can set your deceleration rates in the event of an E-Stop or Drive Halt error reaction.

Online Mode and Jogging

If you happen to have your I/O and drive (with motor) connected right now, you can actually

activate the configuration and jog the motor back and forth with the built-in manual controls.

Assuming you’ve activated the configuration, in Solution Explorer, go to MOTION->NC Task 1

SAF->Axes and double-click on the axis node. Now go to the Online tab:

Page 175: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

175

First you have to enable the axis. This means turning on the feedback loops within the drive, and

turning on torque (i.e. current) to the motor. Note that when you do this, you will be causing motion,

so make absolutely sure that no dangerous motion can result before you do this. To enable

the axis, click the Set button inside the Enabling group box. That will display the Set

Enabling dialog box:

Click the All button. That’s a shortcut to enable the controller, the forward direction, the backward

direction, and set the speed override to 100%. If successful, the drive will be enabled and

the Online tab will look like this:

Page 176: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

176

Now to test the axis, you can use the manual slow buttons (F2 and F3) at the bottom to “jog” the

axis. When you do that, the motor should move, and the position number at the top of the screen

should change to indicate the actual position.

If you have the limit switches installed, this is a good time to test them out. Jog towards a limit

and make sure the axis responds appropriately when it reaches the sensor.

If your axis is hooked up to the actual load, now is the time to do some initial “tuning” of the axis.

Most tuning is simply choosing appropriate maximum values for acceleration, deceleration, and

jerk on the axis Parameter tab. It’s sometimes helpful to setup a repeating back-and-forth motion

while you try changing the parameters.

Using the axis Functions tab, you can create a Reversing Sequence:

Page 177: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

177

Remember that your axis hasn’t been “homed” (a.k.a. “referenced”) yet, so the positions are

based on where the axis was when it started up. Make sure that you can safety move to the

desired positions without hitting an end stop before you start the reversing sequence. Start with

a shorter move and move it up from there.

You want to modify the acceleration, deceleration, and jerk parameters so that you get a fast

response without unacceptable overshoot. Remember that at no time should you exceed the

maximum velocity and torque capabilities of your mechanical system.

TwinCAT 3 comes with a really good oscilloscope-like tool called the TwinCAT 3 Scope. You

create one by adding a TwinCAT 3 Measurement project under the solution in the Solution

Explorer. I won’t go into the Scope now, but it’s a useful tool when trying to optimize a motion

axis.

If you want to play around with the axis Online tab but you don’t have the physical I/O or drive

connected, don’t worry. If your axis isn’t linked to the drive, or if you disable the drive, then

TwinCAT 3 treats it as a simulated axis. You can do everything you would do with a normal drive

Page 178: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

178

and motor, and TwinCAT 3 will simulate an actual position that’s equal to the commanded

position. This is really useful for simulating your system offline before you start it up in the field.

Manually Releasing a Brake

Some servo motors come with an optional motor brake. Some systems may have an external

brake. In both cases the drive itself controls the brake output directly, energizing it to release the

brake mechanism. The drive makes sure that torque is applied to the servo motor before releasing

the brake, so the axis doesn’t fall or move unexpectedly.

In some cases you want to be able to manually release the brake for maintenance tasks. Before

explaining this procedure, you must understand that this is situation-specific. If there’s any

residual force on the axis, or if the axis carries a vertical load and will fall under gravity if you

release the brake, then uncontrolled motion will occur when you release the brake. It is your

responsibility to make the system safe before you release the brake. This might including

blocking a vertical axis mechanically so it can’t fall, or releasing stored potential energy such as

air before unlocking the brake.

The manual brake release function is in the Drive Manager tab of the physical drive node in the

I/O list. In the tree, go to Channel A->Service Functions->Manual brake control. There you have

the options for Automatic, Force lock and Force unlock.

Linking the Axis to the PLC

We’ve already seen how the NC task’s axis node links to the physical drive. Now we need to link

the axis to the PLC. The first thing you have to do in your PLC is add a reference to

the Tc2_MC2 library. In Solution Explorer, go to PLC->PLC1->PLC1 Project and right click on

the References node. (That assumes your PLC project is called PLC1.) Click Add library… from

the context menu to open the Add Library dialog. You can find the Tc2_MC2 library under Motion-

>PTP in the tree.

Next you need to define a variable of type AXIS_REF in your PLC project (typically in a global

variable list):

Page 179: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

179

Now build your PLC project (right click on the PLC1 Project node in Solution Explorer and

choose Build from the context menu).

Now the Axis1 variable will be available for linking to the axis in the NC task. Double-click on

the axis under the Axes node in the NC task and go to the Settings tab:

Click the Link To PLC… button to open the Select Axis PLC Reference dialog:

Page 180: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

180

Select the line that says MyGVL.Axis1 and click OK. Now you’ve linked the axis to your PLC.

This actually creates I/O linking between the PLC task and the axis in the NC task:

Make sure you activate this configuration before continuing.

Enabling the Axis

You use the MC_Power function block to enable an axis from the PLC:

Page 181: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

181

In this example, set EnableAxis1 to TRUE and if the function block is successful,

the Axis1Enabled coil will be set to TRUE. If it won’t enable, the MC_Power function block

has diagnostic outputs: Error and ErrorID. If you do get an error, you can find more

information about the error codes by searching the internet for TwinCAT NC Error Codes.

Jogging the Axis

Use the MC_Jog function block to jog the axis from the PLC:

Page 182: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

182

Make sure you choose sensible values for Velocity, Acceleration,

Deceleration and Jerk.

You would normally connect JogForwardPB and JogBackwardsPB to physical button

inputs or momentary buttons on your HMI. The axis will jog as long as you hold down on the

button. Note that in almost all cases you should condition the

JogForward and JogBackwards inputs so that they only work if you have the machine

in manual mode.

Also note that this block can also perform incremental jog operations (where you move a specific

amount with each button push). To implement incremental jog, change the Mode input

to E_JogMode.MC_JOGMODE_INCHING and set the Position input to the amount

you’d like to move with each button press. If you want to implement both modes and give the

operator the ability to switch between modes, make sure you only use one MC_Jog block and

just change the Mode input based on the operator’s selection.

Page 183: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

183

Homing the Axis

Use the MC_Home function block to home (a.k.a. “reference”) the axis to a known position:

Similar to jogging, you should condition the Execute input with your manual mode signal, so

the axis can only be homed if the machine is in manual mode.

When you execute the home function by pressing the HomePB momentary button, the axis will

start moving towards the home sensor. When the sensor turns on, it will stop and move off the

sensor slowly to record the exact encoder raw value when the input turns off. Once it turns off,

the axis position will change to the position given in the Position input of the block.

In order to make this work, it’s best to implement the home sensor as a normally open switch

positioned near the end of the travel. It needs to be built mechanically so that the sensor state is

“on” if you’re on one side of it and “off” if you’re on the other side. The logic of MC_Home uses

the initial sensor state to determine which direction to start moving to look for the sensor “edge”.

You can change the direction and speeds of the homing sequence in the Parameter tab of

the axis. I suggest setting the speed towards the sensor reasonably high, and the speed off the

sensor to be slow, as this will allow it to find the sensor quickly, but still obtain good accuracy

when finding the sensor edge.

Page 184: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

184

Reading the Axis Status

If you want to know whether the axis has been homed, or if you want any kind of information

about the axis status, use the MC_ReadStatus function block:

This block can be called constantly (once per scan) and the resulting variable

AxisStatus contains a lot of interesting information about the state of the axis right now. Also,

once you’ve called this function, it also updates the MyGVL.Axis1.Status variable with the

same value, which you can use anywhere in your logic.

Moving the Axis to an Absolute Position

Now that the axis is referenced, you can command it to move to an absolute position with

the MC_MoveAbsolute function block:

Page 185: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

185

Make sure that MoveCommand is conditioned with the

MyGVL.Axis1.Status.Homed status signal. You don’t want to try to move to an

absolute position if you haven’t homed the axis yet.

When the block sees a rising edge on the Execute input, it will initiate a move to the

given Position using the given motion parameters

(Velocity,Acceleration, Deceleration, and Jerk). Note that turning off

theExecute input does not stop the motion. You must use MC_Halt or MC_Stopto do

that.

The reason is that when the rising edge happens on Execute, it pre-computes a desired motion

profile for the axis and the NC task starts feeding this commanded position to the drive over the

time period of the motion profile. The only way to change this motion profile is by executing a

different function block. For instance, you can execute another MC_MoveAbsolute block to

“change your mind” on the fly and send the axis to another final position. The NC task will compute

a blended motion profile to move the axis to the new position. You could also execute

a MC_Stop function block which will re-compute the motion profile to bring the axis to a stop.

What this means is that your PLC program must “remember” what you’ve commanded the axis

to do so that you can take appropriate action by issuing new commands if the situation changes.

Page 186: TwinCAT tutorial - Beckhoff

Chapter 11: Introduction to Motion Control

186

For instance, if someone opens a guard door, you may be allowed a short amount of time where

you have to decelerate to a stop under power before the safety system will drop out power to your

drive. This is the responsibility of the PLC programmer to make sure this action happens. The

only action the safety system can take is removing power, it can’t enforce a deceleration.

Conclusion

Obviously motion control is a big topic, and I’ve only introduced the subject and how it relates to

TwinCAT 3 here. I hope this will be enough to get your axes moving and head you in the right

direction. Please understand that motion control is complicated and requires a lot of experience.

If you’re stuck on some technical detail, don’t be afraid to contact Beckhoff’s technical support. In

my experience they’re very knowledgeable and helpful.

Page 187: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

187

12 – Introduction to TwinSAFEWhen we talk about “Safety” in industrial automation, we’re talking about specific functions of our

machines, equipment and our processes related to the safeguarding of people. In most places

there are legal requirements for machine safeguarding, and those regulations are generally based

on national and international standards (e.g. CSA Z432-04 in Canada), but can vary between

regions and nations. It is generally the role of a Professional Engineer to at least review and stamp

the design of a safety system, though this can vary between jurisdictions.

Safety systems concern themselves with the control of energy. All sources of energy and all stored

energy in a system must be accounted for and controlled. This includes electrical, pneumatic, and

hydraulic sources, and includes potential energy stored in suspended loads, springs, compressed

air, and kinetic energy of moving parts. Safety systems work by interrupting sources of energy

and controlling potential or kinetic energy. Safety systems also involve proper mechanical

guarding of a machine so that operators can’t come in contact with dangerous areas of the

machine while sources of energy are connected or potential energy is uncontrolled.

A properly designed safety system consists of process, mechanical, electrical, and (increasingly)

software elements that combine to form a safe working condition for machine operators and

maintenance personnel. The demands on the reliability of the electrical and software elements

are strong enough that ordinary electrical components and PLCs are unacceptable for use. In

general (and this depends on specific cases) electrical and software components must be

designed in such a way that any single component failure won’t lead to a loss of safety function,

and the failure is detected and reported, and prevents further operation of the machine until it’s

repaired.

Take, for example, an electrical relay. A typical relay is unsuitable for use in a safety system for

at least 2 reasons:

1. The relay can fail (typically by contact welding) in the “on” position resulting in the loss of its

capability to interrupt a source of electrical energy.

Page 188: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

188

2. We typically detect failure of a relay by monitoring a normally closed contact. Depending on

the construction of the relay, the failure may not be detected if the normally closed contact isn’t

mechanically linked to the normally open contacts.

In order to reliably interrupt an electrical energy source in a way that a single component failure

doesn’t cause a loss of safety function, and the fault is detected, you typically use two force guided

contacts relays. The use of two solves the first problem of single point of failure, and the use of

force guided contacts solves the second problem of reliably monitoring the relay’s state.

You can conveniently purchase a combination of two force guided contacts relays in this

configuration, but it requires an external monitoring system to check for proper function. You can

also purchase a “safety relay” with the two force guided contacts relays and the monitoring

function all built-in.

Safety PLCs such as the Beckhoff EL6900 are capable of providing the monitoring function.

Importantly, input devices such as e-stop buttons, gate switches, light curtains, and safety mats

are also constructed with redundant components and must be monitored for correct operation,

and the EL6900 can provide monitoring of these components as well.

As you can imagine, there are special requirements placed on the design of a Safety PLC. Just

like any other device in a safety system, the failure of any component in the Safety PLC mustn’t

result in the loss of the safety function, and it must be detected. This includes both electrical and

software components. The designs are then certified by 3rd party certifying bodies before they’re

fit for use in a safety system.

The TwinSAFE module of TwinCAT 3 is the programming software for Beckhoff’s Safety PLC.

The TwinSAFE program you write is executed by the EL6900 module and is separate from your

traditional PLC program. Safety monitoring devices (i.e. “safety inputs”) are connected

through EL1904 cards and safety output devices (i.e. “safety outputs”) are connected

to EL2904 cards, both of which are certified safety devices. The communication between the

EL6900 and EL1904/EL2904 cards is carried over the normal EtherCAT I/O network. This is

possible because the communication uses a special Fail-safe over EtherCAT (FSoE) protocol.

The FSoE protocol is compatible with any EtherCAT master that supports slave-to-slave

Page 189: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

189

messaging/mapping. Each FSoE device can monitor the status of the communication channel

itself and can revert to a safe state (i.e. “off”) if the communication is lost.

Aside: it’s worth noting that the FSoE protocol seems to work by the EtherCAT master routing

data between the EL6900 card and the EL1904/EL2904 cards. This is happening inside the

TwinCAT 3 real-time, which is typically running inside ring 0 of a Windows PC. The question

arises: what could a malicious person who gained remote access to a TwinCAT 3 PC do to the

safety system? The safety logic itself is supposedly password protected, but it’s not clear to me if

the authentication is done inside the EL6900 or in the client, or if the authentication is programmed

flawlessly (in most real-world situations the answer is usually “no”).

In my opinion, a malicious person who gained remote access to the PC could likely (but not

certainly) modify the safety logic in the EL6900, but could almost certainly override the EtherCAT

master’s handling of the FSoE data and perform a man-in-the-middle attack by making each

safety component think the communication link was still working but feed false data to one or

more devices, such as telling the EL2904 cards to turn on an output when it shouldn’t. Therefore

a robust safety system design needs to take this into account and take reasonable steps to

prevent remote and/or unauthorized access to the TwinCAT 3 PC. In most cases you, as a control

system integrator, are not an expert in IT systems and certainly not an expert in IT security.

Realizing this is step 1, and consulting a knowledgeable professional is step 2. While no system

is 100% secure, there are industry best-practices that should be followed.

Adding the EL6900, EL1904, and EL2904 Cards

The most basic TwinSAFE system needs the Safety PLC, an input card, and an output card.

These are added to your I/O bus in the typical way. Here I’ve created a small test system with an

EtherCAT master, an EK1100 head module, and the three safety cards:

Page 190: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

190

Adding a TwinSAFE Project

Right click on the SAFETY node in the tree in Solution Explorer and choose Add New Item… from

the context menu. That will pop up the Add New Item – TwinSAFE dialog:

Make sure TwinCAT Default Safety Project is selected and enter a name for your safety project

in the Name box (highlighted). Click Add. Now you’ll see the TwinCAT 3 Safety Wizard:

Page 191: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

191

Enter your name as the Author and a descriptive Internal Project Name. Click OK. After a moment

the wizard will create your new safety project:

Linking the TwinSAFE Project to the Cards

Expand the MySafety Project node so you can find the Target System node:

Double-click on the Target System node so you can view the EL6900 properties:

First you have to link it to the physical device (the one you added in the I/O tree). Click the link

button (circled in red above). You will see the Choose Physical Terminal for Mapping dialog:

Page 192: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

192

Select the EL6900 node at the bottom of the tree (highlighted) and click OK.

Each safety device in a TwinSAFE system has a unique FSoE address, and this has to be set

with dip switches on the physical card. This is set in binary. Refer to the card’s documentation for

how the switches map to binary values (it’s not obvious by looking at the card, unfortunately).

The FSoE-Address field in the EL6900 properties page (highlighted above) has to match the dip

switch setting on the card.

You connect the safety project to the EL1904 and EL2904 I/O cards by creating “Alias Devices”

for them. In the Solution Explorer tree, below the Target System node, expand

the TwinSafeGroup1 node and then expand the Alias Devices node inside the group. Right-click

on the Alias Devices node and choose Import Alias-Device(s) from I/O-configuration from the

context menu. That will open the Select from I/O tree dialog:

Page 193: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

193

Click the Select All button and then click the OK button. It will try to read the dip switch (FSoE)

addresses from each card when you import it. If you haven’t activated the configuration with the

I/O in the tree, you will receive error messages telling you that you have to manually update the

FSoE addresses for each alias.

Now both cards are imported as alias devices:

Setting Input Card Properties

Double-click on the Safety In 1 alias device node to edit the EL1904 properties:

Page 194: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

194

Notice the FSoE Address box at the top. This must match the dip switch settings on the EL1904

card. Let’s assume that the input (EL1904) card has an FSoE address of 2, so set that value to

2.

Now take a look at the 8001 FS Sensor Test and 8002 FS Logic of Input Pairs sections. Safety

inputs are often wired in pairs, and there are different kinds of safety inputs. In all cases the

system has to provide protection against short circuits and crossed channels. It does this by use

of test pulses. The EL1904 can generate its own test pulses, but it can also be configured to

accept test pulses generated by output signal switching devices (OSSD) such as light curtains or

solid state gate switches. These parameters are used to configure what type of safety input the

card should expect.

There are two very common configurations. The first is the default configuration, as you can see

in the diagram above. That is, you have generation and testing of test pulses turned on

(8001:01 and 8001:02 set to True) and you have 8002:01 set to single logic channel 1/2. This

configuration is appropriate for all dry contact circuits, such as E-Stop buttons, mechanical gate

switches, and the normally closed feedback signals from a safety relay. The EL1904 card provides

a terminal that sources power to the dry contact circuit, and it automatically inserts test pulses

Page 195: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

195

and expects to see those same test pulses on the input. Each test pulse source is offset in time,

so it can detect if someone crosses the circuits, or if the circuit is shorted to 24V or 0V.

The second common configuration is when you already have a device that outputs OSSD pulses

on its outputs, so the EL1904 doesn’t need that feature. In that case, you turn off the test pulses,

and configure the logic to expect OSSD pulses:

Note in the setting it refers to “asynchronous analysis”. Most OSSD devices should be outputting

the test pulses out of phase with each other, so the pulses never overlap. If they do, it’s considered

a fault, because that’s how the EL1904 detects crossed wires. However, there are a few OSSD

devices that don’t force their pulses to be asynchronous. Ideally you should avoid these devices,

but in some cases they still exist, so if you want to use them you’ll have to use the any pulse

repetition OSSD, sensor test deactivated setting. This allows the test pulses to occur

simultaneously.

There is a fourth setting called short cut channel 1/2 is no module fault. The EL1904

documentation is rather vague on the use of this setting, but indicates it’s needed for “safety

mats”. I’m not clear on what this setting does, and I suggest contacting Beckhoff before using it

for anything other than a safety mat.

Page 196: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

196

In this example I’m going to describe an E-Stop function, so we can leave the default settings in

the card.

Setting Output Card Properties

Double-click on the Safety Out 1 alias device node to edit the EL2904 properties:

Note that I already set the DIP switch value (a.k.a. FSoE address) to 3.

The EL2904 output cards have 4 configuration parameters:

8000:01 Standard outputs active allows you to link a PLC output to a safety output, and the card

will automatically “AND” the PLC output value with the safety logic output value. This allows the

PLC to override an output by turning it off. In this case, the safety logic is responsible for when

it’s allowed to turn on, but the PLC controls the actual timing. In my opinion you should leave this

set to False and not use this feature. First of all, it’s going to make the logic more obscure, and

you can achieve the same effect with an AND block inside the safety logic itself. Secondly, if you

want to perform monitoring of the output in your safety logic to make sure it switches on and off

when you tell it to (via a feedback circuit) then you can’t do that if the safety logic doesn’t have

access to the PLC signal. Therefore it’s generally better to do this all in the safety logic, not using

this “standard outputs” feature.

8000:02 Current measurement active is for monitoring a broken circuit. With this feature enabled,

when the output is on, the card expects to see between 20mA and 500mA of current, otherwise

the card will fault. Obviously if the device you’re turning on doesn’t draw the right amount of

Page 197: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

197

current, you’ll have to turn this feature off. In my experience, the feature doesn’t work very well

anyway and seems to trigger falsely. That could be because the brand of relay we were using

isn’t compatible with the current measurement being used. I suggest trying it, but beware that it

might not work.

8000:03 Testing of outputs active is for monitoring crossed circuits, and makes use of test pulses.

The card creates a train of test pulses on the 4 card outputs and makes sure that (a) the pulses

don’t overlap and (b) the pulses are all different lengths varying from 300 to 800 us. These should

be short enough not to interfere with the operation of a mechanical relay. In general this feature

should be activated, except in cases where you are interfacing with a device that can’t tolerate

test pulses. (Note that setting8000:02 also activates test pulses, so you would have to set both

settings to False.)

8000:04 Error acknowledge active controls how the card recovers from a detected output error.

By default, you have to cycle power to the card to recover (typically by pulling the card out of the

rack). If you change this setting to True, then you can reset it through the normal safety error

acknowledgement signal configured in the TwinSAFE logic group.

Ultimately I’m trying to explain how TwinSAFE works, but I can’t make your decisions for you. It’s

your responsibility to understand the impacts of these settings and design your system to meet

the safety requirements of your specific application.

The TwinSAFE Group

When you created your TwinSAFE project, the wizard automatically created a TwinSAFE “group”.

The groups are a way to logically separate your safety programs. For instance, let’s say you have

two physically separate work-cells both controlled by the same TwinSAFE EL6900 safety PLC.

Since they’re logically two distinct machines, it makes sense to separate the logic into two

separate TwinSAFE groups.

The purpose of the groups is to manage error reactions. When a safety malfunction is detected,

such as an output card fault, a communication fault with one of the cards, a discrepancy between

the two channels of a two-channel input device, or a detected failure of a safety relay via a

normally closed feedback circuit, then the entire group enters a faulted state, all of the outputs

Page 198: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

198

revert to their “safe” state (i.e. “off”), and the fault must be acknowledged with the error

acknowledgement signal. In the case where you have two physically separate machines

controlled from the same safety PLC, it makes sense to separate them into two groups so that a

safety device malfunction on one machine doesn’t cause the other machine to stop.

The TwinSAFE Group Properties

When the wizard created your TwinSAFE group, it also created the first alias, called

ErrorAcknowledgement. This is supposed to be linked to a PLC output or a standard input (such

as a pushbutton) and used to acknowledge safety group errors. Linking standard variables is

always a 2-step process: first you have to link from inside the logic to the alias, and then from the

alias to the PLC or physical I/O.

Start by opening the safety application called TwinSafeGroup1.sal by double-clicking on it in

the Solution Explorer tree. That opens a blank TwinSAFE application:

Not very exciting, I admit. The trick is, you now have to right click on the background and

choose Properties from the context menu. This opens the properties tool window:

Page 199: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

199

You have to link the ErrAck signal (highlighted) to the ErrorAcknowledgement alias variable that

the wizard created for you. Click on the spot shown by the red circle, above, and you should see

a “…” button. Click on that button to open the Choose Alias Port dialog box:

Page 200: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

200

Select the highlighted node (Channel 1 under ErrorAcknowledgement). Now the properties will

show that you’ve linked the signal:

You should also link the ComErr (communication error) and FbErr (function block error) outputs.

These are diagnostic outputs which tell you that there’s an error either in a connection (from the

EL6900 to one of the EL1904/EL2904 cards) or in a function block (such as a two-channel

discrepancy error), respectively. Start by creating two output aliases. Right click on the Alias

Devices folder under the TwinSafeGroup1 folder in the Solution Explorer tree and select Add-

>New Item… from the context menu. That will open the Add New Item dialog:

Page 201: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

201

Select Standard from Installed Templates in the top left corner. Select 1 Digital Output

(Standard) in the center box, and then enter a name (ComErr.sds) in the Name box. Finally, click

the Add button. Now you’ll see the ComErr standard output alias device added:

Repeat the process to add an FbErr output alias device:

Page 202: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

202

Now go back to the TwinSAFE Application Properties tool box and link the ComErr and

FbErr outputs to your new aliases:

Now we have to link these standard input and output aliases to the PLC. I’ve created a GVL in

my PLC project with the following 3 variables defined:

Page 203: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

203

Right click on your PLC project and build it, so that you can link these variables. Standard input

and output alias variables can only be linked from the alias itself. Double-click on

the ErrorAcknowledgement.sds alias in the Solution Explorer tree:

Click on the link button, and link it to the MyGVL.ErrorAcknowledgement output from

the PLC. Now do the same thing for the ComErr and FbErr aliases to connect them to your PLC.

Whew! Now we’re ready to start writing some safety logic.

Writing Safety Logic

Double-click on the TwinSafeGroup1.sal safety application in the Solution Explorer tree. What

you see is a blank safety application:

The wizard automatically created the first “network” for you. The safety program is written in the

function block diagram (FBD) language. The program is broken up into “networks” (think of them

like rungs in a ladder logic program). Start by renamingNetwork1 to EStop. Click on the

existing network name to select it and start typing the new name, and press enter when complete.

Now you need to start adding function blocks to the network. Available safety function blocks are

in your Toolbox window. Use View->Toolbox from the main menu:

Page 204: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

204

Now click and drag the safeEstop function block (highlighted) and drop it in your network:

Page 205: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

205

The safety function blocks are certified safety function blocks. The combination of the function

blocks and the hardware platform (the EL6900) makes them suitable for use in a safety

application.

This block can monitor up to 4 two-channel e-stop circuits. If any of the EStopInN safety inputs

turn off, then the EStopOut safety output will turn off immediately and

the EStopDelOut safety output will turn off after a programmable delay (see the Delay

Time (ms) box). To turn the EStopOut and EStopDelOut outputs back on, the function

block needs to see all enabled EStopInN inputs on, and needs to see a 0->1->0 transition on

the Restart input.

Page 206: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

206

The block has optional EDM1 and EDM2 “electronic device monitoring”

inputs. EDM1corresponds to the EStopOut output and EDM2 corresponds to the

EStopDelOut output. If EDM1 is enabled, then the EDM1 feedback input must be on prior to

the 0->1->0 transition on the Restart input. Assuming the EStopOut output is connected

through an EL2904 output card to turn on a pair of force-guided relays, then the normally-closed

contacts of both relays should be connected in series to the EDM1 input of the function block. In

the event that one of the force guided relays fails to switch off, this feature will prevent

the safeEstop block from restarting, preventing the system from operating.

Start by adding a two-channel e-stop circuit to inputs EStopIn1 and EStopIn2. Start by

single-clicking on EStopIn1 to select it, then right click on it and select Properties from the

context menu:

Note that EStopIn1 and EStopIn2 are an “input pair” and can be configured to monitor both

inputs together. That’s what we want in this case, so in the Channel Interface property, select

the Two-Channel setting. Now under the Assigned Variable Name property, type in a descriptive

variable name for this variable. I suggest EStopChannelA:

Page 207: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

207

These changes are reflected on the function block as well:

Page 208: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

208

As you can see, the EStopIn1 and EStopIn2 inputs now have lines under them indicating

they are activated, and there’s also connecting lines between them inside the “AND” block with

100 indicating the block will monitor that the two inputs switch within 100 ms of each other. If they

don’t, this is considered a function block error, so it will turn on the Error output, and it’s also a

group error so all outputs in the group will turn off and you will need to send a 0->1->0 transition

on the ErrAck group input to reset.

Now configure the EStopIn2 input with variable name EStopChannelB. Similarly,

configure the Restart input with a variable name of EStopReset, theEDM1 input with

variable EStopFeedback, the EStopOut output with variable EStopMCR (“MCR”

meaning “master control relay”), and finally the Error output with variable EStopError:

Page 209: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

209

Link the TwinSAFE Variables

We declared the variables in our safety program, and now we have to link them to our Alias

Devices. There should be a tab at the bottom of your screen called Variable Mapping. If you can’t

find it, try going to View->Other Windows->Variable Mapping. This is where you link your safety

program variables to aliases:

Clicking the “…” button in the right-most Alias Port column brings up the Choose Alias Port dialog

box, just like when you linked the ErrAck, ComErr, and FbErr group I/O to the Alias

Devices earlier. In this case you’re going to be linking the safety variables to the EL1904 and

El2904 safety I/O cards. Go ahead and link all but theEStopError variable to the Alias

Devices:

The EStopError variable is a diagnostic signal, not a real safety output. We really just want

to send that signal back to our PLC so we can display a fault message or show it on the HMI.

First, create another standard output variable alias device called EStopError.sds (the same as

Page 210: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

210

you did for ComErr.sds and FbErr.sds previously). Second, use the drop-down box in the Data

Type column to change the data type from safeBool to Bool (a safeBool can only be

linked with safety I/O and a Bool can only be linked with standard variables). Third, link

the EStopError variable to the EStopError.sds standard output alias:

Finally, you’ll need to create another input variable in your PLC (EStopError AT %I* :

BOOL;), build your PLC project, double-click on the EStopError.sds alias, and use the link button

to link it to the PLC I/O.

Downloading to the EL6900

Start by doing a safety “verification”. This is like a “build” or “compile” in that it checks for errors.

In the case of TwinSAFE, it checks for things like inputs or outputs not connected to alias devices,

or inputs on function blocks that are enabled and not connected to variables. From the menu at

the top, select TwinSAFE->Verify Safety Project (Project Level). If successful, it will display the

message Verification Process succeeded in the status bar at the very bottom of the window.

If you don’t have any hardware connected, then this is as far as you can go. Everything else

requires you to have a working EtherCAT master with all your I/O connected and communicating.

This requires a typical “activate configuration” as well.

If you do have the hardware connected, select TwinSAFE->Verify Complete Safety Project

(Project and Hardware Level) from the main menu. This does a similar verification but it also

Page 211: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

211

checks that the DIP switches on the physical I/O cards match the settings in the alias devices,

etc.

If that verification is successful, then you can select TwinSAFE->Download Safety Project from

the main menu. That’s going to pop up a dialog box asking you for 3 things:

1. Username

2. Serial number

3. Password

The default user name is Administrator. The serial number refers to the EL6900’s serial number.

You can find it on the card itself, or you can also find it by double-clicking on the Target

System node under the safety project in the Solution Explorer tree. The default password

is TwinSAFE (note that it’s case sensitive). Once you put the system into production, this

password should be changed and kept in a safe place, typically with the safety audit

documentation.

Once you enter these fields and continue, it will then display a download screen where it

downloads the function blocks to the EL6900. When the download is complete, you have to enter

the password one more time to activate the download, which allows the EL6900 to run the safety

program.

Going Online

First, go online with your PLC project and look at the signals from the safety PLC. You should see

that you have a communication error (or ComErr). To reset the communication error, it needs

to see a 0->1->0 transition on the ErrorAcknowledgement signal.

Second, you can go online with the safety PLC. Double click on the safety group to display the

safety program, and then select TwinSAFE->Show Online Data of Safety Project from the main

menu. Now you’ll be able to monitor the online value of the variables, and see the state of the

function blocks. There is also a small window or tab at the bottom of your screen called Safety

Project Online View. Once you’re online, open this window and you can get more information

about diagnostic signals from each connection and each function block.

Page 212: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

212

Additional Function Blocks

The EL6900 supports more than just the safeEstop function block. There is a document

published by Beckhoff listing all the function blocks. The document is titled “Function Blocks for

TwinSAFE Logic Terminals”, and is available from the TwinSAFE Product Documentation page.

Connections Between Function Blocks and Networks

You can connect an output on one function block to an input on another function block by just

clicking and dragging a connecting line between them. For example:

If you have a hard time getting your mouse to start the link or end the link, then try using the zoom

feature to zoom in first, and that will make it easier to hover your mouse over the right link spot.

This is even more important if you have a single output branching out to multiple inputs.

Let’s say you have a function block in one network and you want to connect an output from that

block to an input on a function block in another network. You can do this by typing in a fully

Page 213: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

213

qualified address of the other point using the syntax:

Network.FunctionBlockName.InputName. For example:

Monitoring in the PLC

TwinSAFE provides the reliability you need to meet the safety requirements of your machine, but

you still need to make the operator interface user-friendly. Typically this means bringing status

signals into your PLC and using that to create faults and alarms. I’ve already described how to

bring the error signal from a function block out to an alias device and link it to a PLC variable. You

can also link PLC I/O directly to the safety I/O in the I/O tree in the Solution Explorer. For instance,

here’s where you can link a PLC input to an EL1904 safety input:

Page 214: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

214

Here’s how you link a PLC output to an EL2904 safety output:

Page 215: TwinCAT tutorial - Beckhoff

Chapter 12: Introduction to TwinSAFE

215

Conclusion

That’s it! Obviously safety is a big topic and TwinSAFE is necessarily complicated by its stringent

requirements. Overall, the TwinSAFE system can be a cost-effective way to implement a safety

system, and it provides excellent integration into the rest of the PLC control system which makes

monitoring and diagnostics much simpler.

Page 216: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

216

13 – The Scope ViewIn this section I’m going to introduce you to one of the most powerful tools in your debugging

arsenal: the TwinCAT 3 Scope View. This is a software-based digital oscilloscope that comes free

with TwinCAT 3.

Create a Test PLC Project

First, create a PLC project for testing. I’m going to describe a simple scenario that generates a

sine wave at a reasonably high frequency. Follow these instructions (assuming you’ve read the

rest of the tutorials and you’re familiar with how to do this):

1. Create a new TwinCAT 3 solution

2. Use the wizard to create a standard PLC project (called PLC1)

3. Setup the SYSTEM->Real-Time node (hint: “Read from Target“)

4. Setup SYSTEM->Tasks->PlcTask to run at 1 ms

5. Remove the structured text MAIN program and create a new ladder logic MAIN program

6. Create a global variable list called GVL

Here’s the contents of the GVL:

Here’s the MAIN program:

Page 217: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

217

Now build and activate the configuration. Go online and make sure you can see it running.

Add a TwinCAT Measurement Project

The TwinCAT 3 Scope View is built into the Visual Studio 2010 shell. You use it by first creating

a new “TwinCAT Measurement Project.” In the Solution Explorer, go to the top level (Solution)

node and right click on it. Select Add->New Project… from the context menu. That will open

the Add New Project dialog:

Page 218: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

218

Make sure you have TwinCAT Measurement selected in the upper left under Installed Templates.

Then select Empty Measurement Project in the middle panel, and give it a descriptive name in

the Name field. Click the OK button to create the project. Now you’ll see the new project under

your solution node in the Solution Explorer:

Add a Scope to your Measurement Project

Now you have to add a Scope. There are two basic scope types: YT and XY. A YT scope plots

some variable on the vertical Y axis and time along the horizontal (X) axis. An XY scope plots two

variables against each other, one on the X and one on the Y axis. In our case we want an YT

scope.

Page 219: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

219

In the Solution Explorer, right click on your new measurement project and select Add->New

Item… from the context menu. That will open the Add New Item dialog window:

With TwinCAT Measurement selected in the upper left under Installed Templates, select Scope

YT Project in the middle panel, add a descriptive name for your scope in the Name box, and

click Add. Now you’ll see a new scope node under your measurement project in the Solution

Explorer:

Page 220: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

220

Adding Variables to your Axis

Start by adding the GVL.SinOfAngle variable to the Axis that was created by the wizard.

Right click on the Chart->Axis node under your scope in the Solution Explore rand choose Target

Browser from the context menu. That will display the Target Browser window:

At first it will show the ROUTES node, and under it you should see your PC name (where the red

box is in the figure above). Click on your PC name, expand Port_851, click on the GVL node, and

you will see SinOfAngle in the right pane. Double-click on SinOfAngle and it will add that variable

to your Axis. Then close the Target Browser window. Now you’ll see SinOfAngle added to

your Axis under the Solution Explorer:

Record Data

Make sure you have the scope window visible by double-clicking on the scope node in

the Solution Explorer. Now look for the TwinCAT Measurement buttons in your toolbar:

Page 221: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

221

The button on the left is the Record button. The button in the middle is the Stop button. Click the

Record button, wait a few seconds, and click the Stop button (and you’ll have to click an OK button

on a dialog after clicking the Stop button). Now you’ll have some data to look at:

If you want to zoom in on the data, hover your mouse over the point where you’d like to zoom,

and use the mouse wheel to zoom in (forward on the mouse wheel zooms in, backwards zooms

out). If you don’t have a mouse wheel, look for the Zoom X button on the chart toolbar. Moving

left/right is as simple as clicking, holding, and dragging the mouse left or right.

Page 222: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

222

Measuring using Cursors

You can take measurements from the scope using X and Y cursors. Let’s say I want to measure

the sine wave period, which is the time between one positive (rising) zero crossing to the next

positive zero crossing. First we need to create two X cursors. In the Solution Explorer, under

the Chart node, find the Cursor node. Right click on the Cursor node and select New X Cursor.

Do this twice. You’ll see the two cursors listed under the Cursor node:

Notice that the C cursor is green and the C 01 cursor is blue (you can barely see it in the icon).

You’ll also see new green and blue X cursor lines in the chart itself:

Page 223: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

223

Now click and drag the green cursor to the first positive zero crossing of the sine wave, and click

and drag the blue cursor to the second positive zero crossing:

Page 224: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

224

Now right-click on the C node (the actual cursor under the Cursor node) in the Solution

Explorer again and select Cursor Window from the context menu. That will display the

Cursor window:

Page 225: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

225

You can see the relevant details of each cursor in the C and C 01 columns. Now click the Delta

button (circled in red). That displays another column showing the difference between the two

cursors:

Here we can see the time difference between the two cursors is about 360ms, which makes sense

since the input angle is changing by 1 degree every scan, and each scan is 1ms long.

Page 226: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

226

Adding More Axes

The sine wave has a Y range of -1.0 to +1.0 in amplitude. Suppose we wanted to compare this to

the input angle (in degrees) which ranges from 0.0 to about 360.0 degrees. Plotting them on the

same graph would be difficult because the amplitudes are so dissimilar.

To scope variables with different amplitudes, simply add more axes. Right click on the Chart node

in the Solution Explorer and click New Axis from the context menu. That will add a new axis

called Axis(1). You can rename these axes if you like:

Looking at your chart window, you’ll now see two Y-axes on the left side.

Now right click on the new Axis(1) node and select Target Browser from the context menu. This

time use the Target Browser window to add the Port_851->MAIN.->Angle_deg variable to your

new axis:

When you do this, it has to delete the captured data and it asks if you want to save the data to a

file first. Just say “no”. Close the Target Browser window.

Page 227: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

227

Now use the Record and Stop buttons to record a few seconds of data again:

The chart looks a little more cluttered with two axes overlaid like this. Zooming in helps a little.

However, I generally prefer using “stacked Y axes.” You can enable this feature in the chart

properties. Right click on the Chart node in the Solution Explorer and choose Properties from the

context menu. Now scroll down to the bottom of the Properties window and look for the Stacked

Y-Axes property. Set this property to True. Now your axes will appear “stacked”:

Page 228: TwinCAT tutorial - Beckhoff

Chapter 13: The Scope View

228

I also find stacked axes useful when scoping Boolean variables because I find it easier to see

which ones are off and which ones are on at any given point in time.

Conclusion

The Scope View is a powerful tool for troubleshooting irksome transient conditions or measuring

high speed signals. Once you know the basics, the interface is fairly user-friendly and best-of-all

the tool is included for free with TwinCAT 3.

Page 229: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

229

14 – Part TrackingPart tracking is a topic that’s applicable to all PLC makes and models, so I’m going to start by

discussing the properties of a generalized discrete part tracking system, and then explain how

that can be implemented in TwinCAT 3.

Properties of a Generalized Part Tracking System

A good discrete part tracking system should:

Track the presence or absence of a part at each location

Work even with imperfect part sensing

Track the work done to each part

Works regardless of mode (even in manual mode)

Optionally track other metadata such as lot # or serial #

Be resilient to abnormal operation such as faults or loss of power

Allow the operator to recover from an abnormal operation, such as by removing a part from

the systemUsing a Structure to Track a Part in TwinCAT 3

A good way to implement part tracking in TwinCAT 3 is to use a Structure. Imagine a welding

machine where the operator places a part into the machine, it welds the part, and then the part is

automatically ejected either into the good part bin, or into a reject chute. A

reasonable Part structure might look like this:

Part tracking data needs to survive the reboot of the controller PC, so we need to declare this as

a persistent variable. Here I’ve created a new variable called WelderPart in a persistent

section of a global variable list:

Page 230: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

230

Tracking Part Presence

We presumably have a sensor or some other method to detect part presence. Perhaps an

operator has to place a part in the fixture, we detect the part with a sensor, and then they have to

push a button to initiate the cycle. When this happens we need to set the Present bit and

generate a new serial #.

To generate a new serial #, we need a persistent LastSerialNumber variable:

Now create a new ladder logic Part Tracking program called TrackWelderPart:

Page 231: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

231

The first rung creates a “pulse” bit, sometimes called a “one-shot”. This is a bit that’s only on for

one scan. The reason this is only on for one scan is because in the second rung, we use a Set

coil to set the WelderPart.Present bit, of which we have a normally closed contact in the

first rung.

The third and fourth rungs are responsible for incrementing the serial # and assigning it to the

part tracking variable.

Notice so far that taking the part out and putting it back in (or a momentary failure of the part

present sensor) won’t cause the part tracking to change. The part is still considered present, and

the serial # is fixed.

Page 232: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

232

Tracking Weld Status

The weld status is tracked presumably by feedback from the welder. Most welders operate by the

PLC sending a weld trigger signal, and the welder responding with a WeldOK or

a WeldFault signal. If that’s the case, we can track weld status like this:

Again we’re creating a “pulse” bit that’s only on for one scan, called WeldedPulse. Notice also

that the first three conditions on the first rung could be moved over to the program that fires the

welder, and it could be used to create a coil called OkToWeld. You could then

use OkToWeld here in place of those three contacts. This logic will prevent the welder from

welding the same part twice, which is very important.

It’s also important to realize that this logic works equally well in both automatic or manual mode.

As long as the welder fires correctly with a part present then we’ll track the weld status. This is

useful both for testing and to prevent mis-tracking the part.

Tracking Part Removal

There are three ways this part can leave the welder:

1. It can be ejected into the good part bin

2. It can be ejected into the bad part chute

Page 233: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

233

3. An operator can remove it

I like to create a separate pulse for each event so that I can use that pulse bit for other tasks like

part counters and logging events.

Let’s assume we have a Part Ejector cylinder and cylinder that toggles the chute between the

good bin and rejected part chute position. Ejecting the part with the toggle in the good position

sends it to the good part chute, and ejecting the part with the toggle in the bad position sends it

to the bad part chute.

When removing the part from the part tracking memory, it needs to clear the entire structure.

Thankfully there’s an easy way to do this. Declare a constant of type Part and use

a MOVE instruction to copy the empty structure over to of the part tracking variable:

Somewhere else in your logic, you need to make sure that the ejector only extends if the chute

position matches the part status, so the part tracking should have the Welded

and WeldOK bits set in order to eject in the good position. Don’t let it eject a bad part into the

good part bin even in manual mode.

Page 234: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

234

Recovering From Abnormal Conditions

Let’s assume the operator removed the part by hand. You have some options. One way to handle

it is to just accept that the part was removed. I like to use a timer just to “debounce” the sensor a

little bit:

However, unless manual removal is part of the normal process, I don’t like to automatically accept

a part removal. I prefer creating a cycle stop fault, and then having the operator press a button to

manually remove the part tracking data:

Page 235: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

235

This is a nice handshake with the operator. The machine is saying, “I know you removed the part,

and now I know that you know that I know that you removed a part.” In any relationship,

communication is key.

If you happen to have a situation where the part can literally fall out, like a vacuum gripper on a

robot, you typically monitor the vacuum feedback to make sure the part is still attached. In that

case, if the vacuum feedback turns off while you’re supposed to have a part, that’s a pretty serious

fault (typically an immediate-stop fault) because it detects a dropped fault. In that case, you need

to get the operator to confirm the removal of the part because they need to remove the part from

the machine (wherever it dropped). It’s also possible there’s something wrong with the gripper.

Tracking a Part From Station to Station

Machines often have automated transfer equipment such as conveyors, pick & place equipment

or material handling robots to move parts around from station to station within the work cell.

Imagine the case where a conveyor is moving the part from station 1 to station 2. Assume there’s

Page 236: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

236

a sensor at station 1 and station 2. We could use a persistent array to track the part at each

station:

The logic to move a part from station 1 to station 2 might look like this:

Page 237: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

237

Part Tracking in Rotary Tables

Rotary tables present an interesting challenge. A rotary table is a table that can continuously

rotate in either direction. It has some number of part nests, and it moves parts around a cell by

indexing the entire table forward one index distance so all the parts are moved from the current

station to the next station in one move.

If you use an indexing table (where you extend a cylinder or run a motor until a sensor turns on)

then you can usually get away with using logic like the conveyor-type logic above, except that you

index all the stations simultaneously. This is no problem, just remember to index the last station

first and the first station last, so you don’t overwrite the next station.

However, rotary tables can be more complicated than this. What happens if the operator is in

manual mode and indexes a 4 position rotary table 4 times? Shouldn’t the original parts be back

in their original locations? Does that mean you index the last station into the first station too? Also,

what about servo-type rotary tables that run in reverse or index to half-station positions?

The problem is that we’ve modeled the situation wrong. The parts are always fixed in a certain

nest, and the table position determines which station they’re at. It’s more correct to create

a PartInNest tracking array:

(I deliberately added an index 0 at the beginning to make the part tracking logic simpler later, as

you’ll see. Nest 0 is an invalid nest, meaning no nest at station.)

Then you need to use the table position to determine which nest number is at each station (or 0

if no nest). In some cases this is done with an array of binary proxes (one prox indicates you’re in

some position, and a set of other proxes indicate which one you’re in, so you’d need 2 to indicate

4 stations, 3 to indicate up to 8 stations, etc.). In other cases you have a servo with an angle

Page 238: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

238

position, and you use the angle position to figure out which nest is at each station. For now just

imagine a function that returns an integer (nest number) for a given station:

This is simplified logic. It assumes that if the proxes show off,off then nest 1 is at the OP10 station,

and if it shows off/on then nest 2 is at the OP10 station, etc. You would have to customize it for

your machine and for each station. This is just an example. Note that if no nest is in position it

sets the nest number to 0.

Now there’s more than one way to do this. You could use

NestInStation[NestNumberAtOP10].Present everywhere in your OP10 part

tracking logic but the variable name starts to get really long, and it’s a bit complicated for an

electrician to understand “indirect addressing”. Here’s another alternative… create a program that

takes a Part as a VAR_IN_OUT, and call it like this:

Page 239: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

239

We’re doing something a little tricky here. We’re passing the actual nest part data by reference,

meaning the OP10 part tracking program can read and write into that variable. Inside the program

it looks like this:

The logic is straightforward, the variable name lengths are reasonable, and you don’t have to

worry about which nest is at the OP10 station because that’s handled by the outer program. Note

that you have to use the InAnyStationPRX input to determine if you’re actually in a station,

or else you’re looking at index 0.

By tracking the part in the nest, you don’t have to shift data around with MOVE instructions when

you index the table, and you have the option of displaying the part data on the HMI both by station

and by nest. You can even display the part tracking by nest when the table isn’t in an actual station

position.

Page 240: TwinCAT tutorial - Beckhoff

Chapter 14: Part Tracking

240

Conclusion

This has been a brief introductory tutorial to generalized part tracking in TwinCAT 3. In my

experience you can build an arbitrarily large part tracking system based on these few simple

concepts.