Beckhoff PLC development
We have the training videos for TwinCAT 2 and 3 stored in ByManufacturer.
Starting a project
- Create a new project
- Create a route to the PLC
- Add a PLC (Standard PLC Project)
- Setup IO
- Create global variables
- Scan for IO devices
- Make sure TwinCAT is in configuration mode
- Build the solution
- Link the Inputs and Outputs from the Instance to the IO
This [video](https://www.youtube.com/watch?v=95hl-0hIuCQ) gives a good run down of getting started with the CX7000 range.
[Jakob Sagatowski](https://www.sagatowski.com) has a good [Youtube](https://www.youtube.com/playlist?list=PLimaF0nZKYHz3I3kFP4myaAYjmYk1SowO) series that looks at TwinCAT 3 as well as how to apply standard software development techniques such as [Unit Testing](https://www.youtube.com/watch?v=FnXY6MA3axw&list=PLimaF0nZKYHz3I3kFP4myaAYjmYk1SowO), [Version Control](https://www.youtube.com/watch?v=1g6eYnlzKtA&list=PLimaF0nZKYHz3I3kFP4myaAYjmYk1SowO&index=15) and [Continuous Integration](https://www.sagatowski.com/posts/cicd_with_twincat_part_one/)
[Source Control project files](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_sourcecontrol/18014398915785483.html&id=576503262143878121)
## IO
You need to define memory locations for the IO that you are interested in. Then [link](https://youtu.be/zQwiBeDbDcM?si=lTbqBenXogr9H9kW&t=1814) the memory locations to the IO devices. [Linking](https://youtu.be/zQwiBeDbDcM?si=pNOob-F6LDpR-z7W&t=1657) EtherCAT data.
Infosys explanation for [AT declarations](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/11948825611.html&id=)
```
VAR
// Use AT%I* for an input
bCycleTrigger AT%I* :BOOL;
// and AT%Q* for an output
bMisfeedAlarm AT%Q* :BOOL;
END_VAR
```
## Unit Testing
An explanation of [Test Driven Development (TDD)](https://youtu.be/FnXY6MA3axw?si=64CfuKrNoQGZBT36&t=170). In TwinCAT there is an external library for unit testing called [TcUnit](https://tcunit.org/#/), here is an explanation of [how it is used](https://youtu.be/FnXY6MA3axw?si=slPp3qppXd6cVH1D&t=472).
Test Suite is a group of tests that are related to each other. Each Test Suite is a Function Block that inherits its properties and functionality from TcUnit. Each test is implemented as a Method on the Test Suite function block. The tests that are in the Test Suite are run by a Test Runner, which collects the test results and presents them. Use an assertion method to test whether your function under test is working correctly.
Best approach is to move any code that you want to test over to its own library. This allows you to have the tests executed separately to your main programs execution.
Key points from an example
- [Move code to be tested into its own library](https://youtu.be/FnXY6MA3axw?si=0qZekN2zwuTUdhwu&t=1060)
- [Create a new PLC project (Untitled1)](https://youtu.be/FnXY6MA3axw?si=M4Y8rVEZYF-IHSph&t=1082)
- [Rename](https://youtu.be/FnXY6MA3axw?si=luRqyPf3Ze4uVQFy&t=1085) the MAIN program to PRG_TEST and put it in a TEST folder
- [Copy](https://youtu.be/FnXY6MA3axw?si=xZ1Vs0JQQr_rX5Ef&t=1103) the Function Blocks over from the original project
- [Create a stub](https://youtu.be/FnXY6MA3axw?si=dqIj9VLxJ1mH4Joz&t=1199) of new code (the interface but not the implementation code)
- [Download and Install TcUnit](https://youtu.be/FnXY6MA3axw?si=HsL8kyjhUTReVCzT&t=1419)
- [Create a TestSuite](https://youtu.be/FnXY6MA3axw?si=fg8xUqYZEt9P02b4&t=1517). This is a Function Block that EXTENDS FB_TestSuite
- [What do we want to test?](https://youtu.be/FnXY6MA3axw?si=xsJV9How-UY6Bn5F&t=1619) Create a Method for each test
- [Assert](https://youtu.be/FnXY6MA3axw?si=sFJLUesT8vG29f6B&t=1966) a condition to test that code functions correctly.
- To [execute](https://youtu.be/FnXY6MA3axw?si=RwtCcIbfVdCWjIpG&t=2852) the tests we need to
- Call all of the test methods from the [body of the TestSuite](https://youtu.be/FnXY6MA3axw?si=X5EiHdQKiIVqB-Vf&t=2865)
- [Instantiate](https://youtu.be/FnXY6MA3axw?si=3WsPn3f1oezG2TO9&t=2881) the Test Suite in the the PRG_TEST code.
- and call the test runner, [TcUnit.RUN();](https://youtu.be/FnXY6MA3axw?si=gO17IXw34GfPvkXx&t=2901) from the PRG_TEST code.
- Run the code by [activating](https://youtu.be/FnXY6MA3axw?si=Ah9bHE-SpLVeGoXX&t=2957) the project
## Runtimes
There is a runtime on the PLC but you can also use runtimes on the development machine (as a way to test code before installing on the PLC). This can be difficult though with modern laptops due to issues with Hyper-V and virtualisation. One way around this is to install the [TC1700 Usermode Runtime](https://www.beckhoff.com/en-au/products/automation/twincat/tc1xxx-twincat-3-base/tc1700.html).
Note: to use the TCP IP library with the Usermode Runtime you need to add a configuration file to the system. The details are in section "5.8.2 TF6310 | TwinCAT 3 TCP/IP" of the Technical Documentation [PDF](https://www.beckhoff.com/en-en/download/723561088)
## Instructions
[Flow Control](https://youtu.be/pnLgRNb09qE?si=yiLeaMGtYA_HdQvk) instructions are used to branch the execution of code based on booleans
## [Variables](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/11982693643.html&id=) and Constants
Variables go at the top of a file, in their own section. There are two views available, text or table.
```
FUNCTION_BLOCK FB_Something
VAR_INPUT
bExecute : BOOL := FALSE;
END_VAR
VAR_OUTPUT
bBusy : BOOL := FALSE;
END_VAR
VAR
barCodeData : ARRAY[1..MAX_BUFFER_COUNT] OF ST_CartonDetails;
END_VAR
VAR CONSTANT
MAX_BUFFER_COUNT : UINT := 1000;
END_VAR
VAR_INST
foo : BOOL := TRUE;
END_VAR
```
[VAR](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528755083.html&id=) is for variables within function blocks (Local Variables)
[VAR_INPUT](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528760459.html&id=) is where input parameters are specified
[VAR_OUTPUT](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528765835.html&id=) is where output parameters are specified
VAR CONSTANT read only variables.
[VAR_INST](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528798091.html&id=) is for creating instance variables within Methods.
[VAR_IN_OUT](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528771211.html&id=) pass through parameter in a function block
VAR_GLOBAL
[VAR_STAT](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2528787339.html&id=)
### Conditional Statements
such as [IF ELSE](https://youtu.be/pnLgRNb09qE?si=_q5zdbaEYJYDR6of&t=60)
```
IF condition1 THEN
ELSEIF condition2 THEN
ELSE
END_IF
```
of [CASE](https://youtu.be/pnLgRNb09qE?si=fyJppUmDJdyN0gsc&t=153) statements
```
CASE variable OF
x :
// Block of code
y :
// Block of code
z :
// Block of code
ELSE
// Block of code
END_CASE
```
Strings are not able to be used as the variable for CASE statements.
## Loops
### [FOR](https://youtu.be/pnLgRNb09qE?si=WXWcK7uYA7MVc32i&t=275)
```
VAR
nSum : INT;
nCounter : INT;
aArray : ARRAY[1..5] OF INT;
END_VAR
============================================
FOR nCounter := 1 TO 5 BY 1 DO
nSUM := nSUM + aArray[nCounter];
END_FOR
```
### [WHILE](https://youtu.be/pnLgRNb09qE?si=7eYP4KjFm-I6UxzL&t=335)
```
VAR
nSum : INT;
nCounter : INT;
aArray : ARRAY[1..5] OF INT;
END_VAR
============================================
WHILE nCounter < 6 DO
nSum := nSum + aArray[nCounter];
nCounter := nCounter + 1;
END_WHILE
```
## Data Types
### Variables
[Naming Conventions](https://alltwincat.com/2019/02/11/plc-naming-conventions/)
[TwinCAT 3 Data Types](https://infosys.beckhoff.com/english.php?content=..%2Fcontent%2F1033%2Ftc3_plc_intro%2F2529388939.html&id)
### Type Conversions
[Type Conversions](https://youtu.be/JSFvSu8uB9U?si=7UCyW5pVslw_g9M7&t=2167)
[Type Coversion Functions](https://infosys.beckhoff.com/english.php?content=..%2Fcontent%2F1033%2Ftc3_plc_intro%2F3998090635.html&id=5012109936154414215)
```
VAR
nOne_Usint : USINT; // 1 byte
nOne_Uint : UINT; // 2 bytes
END_VAR
=================================
nOne_Uint := nOne_Usint + 5; // Ok, because UINT is bigger than USINT
nOne_Usint := nOne_Uint + 5; // Compiler error, because USINT is smaller than UINT
nOne_Usint := UINT_TO_USINT(nOne_Uint) + 5; // Type conversion removes the upper byte
```
[Changing the base for Integer Literals](https://youtu.be/JSFvSu8uB9U?si=3gPcJVmqxaJrGLnJ&t=387)
`10#179 = 2#1011_0011
[Accessing bits](https://youtu.be/JSFvSu8uB9U?si=YDCEhantSHwZFGup&t=477) of a variable
`nSomeVariable : BYTE := 2#1011_0011;
`nSomeVariable.0 = 1
`nSomeVariable.1 = 1
`nSomeVariable.2 = 0
`nSomeVariable.3 = 0
#### Strings
Most functions that take strings have a size limit of [255 characters](https://youtu.be/JSFvSu8uB9U?si=7qj69Mz7kxCEdWrQ&t=561)
### [Pointers](https://youtu.be/JSFvSu8uB9U?si=Lf59MOHo0JU_n0jE&t=1395)
Pointers are a variable with an address
### [References](https://youtu.be/JSFvSu8uB9U?si=bXSocKRP89W1DFTI&t=1580)
References are like an Alias to a variable
## Grouping Types
### Array
[Arrays](https://youtu.be/JSFvSu8uB9U?si=imD6zcd6XM_13K2U&t=1952) are a collection of the same data type. [Infosys](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8825257611.html&id=)
```
aOneArray : ARRAY[1..5] OF INT;
aInitialised : ARRAY[1..5] OF INT := [10, 20, 30, 40, 50];
iValue := aOneArray[3];
aOneArray[4] := 55;
```
Arrays can start an end at any index number
```
aZeroIndexed := ARRAY[0..4] OF LREAL;
aNegativeIndexed := ARRAY[-1..0] OF STRING;
```
Arrays can be [multi-dimensional](https://youtu.be/JSFvSu8uB9U?si=mWhY2pe50OGRjtgI&t=2044). There are two different ways to declare and access multi-dimensional arrays
```
a2DArray : ARRAY[0..2, 0..3] OF REAL;
a2DArray[1,3];
aAnother2DArray : ARRAY[0..2] OF ARRAY[0..3] REAL;
aAnother2DArray[1][3];
```
#### Variable Sized Arrays
The ability to define [variable sized arrays](https://stefanhenneken.net/2016/09/27/iec-61131-3-arrays-with-variable-length/) has been added to [TwinCAT IEC 61131-3](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8825257611.html&id=).
The array has to be defined as a VAR_IN_OUT at the moment.
There are LOWER_BOUND(array, dimension) and UPPER_BOUND(array, dimension) functions for working out the size of the array.
```
FUNCTION F_CalcSum1DimArray : LREAL
VAR_IN_OUT
arrData : ARRAY[*] OF LREAL;
END_VAR
VAR
nIndex : DINT;
END_VAR
======================================
F_CalcSum1DimArray := 0;
FOR nIndex := LOWER_BOUND(arrData, 1) TO UPPER_BOUND(arrData, 1) DO
F_CalcSum1DimArray := F_CalcSum1DimArray + arrData[nIndex];
END_FOR
```
### Enum
[Enumerations](https://youtu.be/JSFvSu8uB9U?si=Rh_fhtH-V2V7SRWf&t=930) create a set of named constants.
```
TYPE E_Colour :
(
Red,
Green,
Blue
);
END_TYPE
```
By default enums are backed by an INT. So the above type would be Red = 0, Green = 1, Blue = 2. You can explicitly set the type and the value for the enumerations if required.
```
VAR
eLightTower : E_Colour;
END_VAR
============================
eLightTower := E_Colour.Red
```
#### [Local Enumerations](https://youtu.be/pnLgRNb09qE?si=FzNOSr3UfcbpACMV&t=730)
are used for creating enumerations that are only accessible from within the function block in which they are defined.
### Structures
[Structures](https://youtu.be/zB_2x0NPMGc?si=RaaPoz14YPLegu5V&t=55) are used for grouping data of different types.
```
TYPE ST_Event :
STRUCT
eEventType : E_EventType;
eEventSeverity : TcEventSeverity;
nEventIdentity : UDINT;
sEventText : STRING(255);
dtTimestamp : DATE_AND_TIME;
END_STRUCT
END_TYPE
```
Use dot syntax to access the different components of a structure
```
eEvent := ST_Event;
eEvent.sEventText;
```
You can create inline structures using () characters
```
input : ST_CartonDetails := (
barcode1 := '9341259000153',
text1 := 'JONES BROS FAMILY FARMS',
text2 := '700g x 12 pk',
text3 := '(Free Range)',
bestBeforeDuration := 7,
barcode2 := '90341259000153'
);
```
## Code Organisation
### Functions
[Functions](https://youtu.be/zB_2x0NPMGc?si=ROEP7jKbMGEnEL0C&t=253) are used to reuse blocks of code that are completely dependent on their inputs. Add a POU to [create](https://youtu.be/zB_2x0NPMGc?si=WErhsZF0b6VD45vj&t=477) a new function.
```
FUNCTION Celsius_To_Farenheit : REAL
VAR_INPUT
fCelsius : REAL;
END_VAR
Celsius_To_Fahrenheit := fCelsius * 1.8 + 32.0;
```
To call a function
```
rTemp = Celsius_To_Fahrenheit(fCelsius := 25.0);
```
[Multiple outputs](https://youtu.be/zB_2x0NPMGc?si=XlPOsCBFVUu0lH81&t=340) are also possible in TwinCAT
```
FUNCTION Greater_Smaller
VAR_INPUT
nNumber1 : INT;
nNumber2 : INT;
END_VAR
VAR_OUTPUT
nGreater : INT;
nSmaller : INT;
END_VAR
IF nNumber1 > nNumber2 THEN
nGreater := nNumber1;
nSmaller := nNumber2;
ELSE
nGreater := nNumber2;
nSmaller := nNumber1;
END_IF
```
To use this function we would need to define variables for storing the answers
```
PROGRAM MAIN
VAR
nReturnGreater : INT;
nReturnSmaller : INT;
END_VAR
Greater_Smaller(nNumber1 := 5,
nNumber2 := 10l
nGreater => nReturnGreater,
nSmaller => nReturnSmaller);
```
Functions that modify their inputs are also possible using [VAR_IN_OUT](https://youtu.be/zB_2x0NPMGc?si=XhasZNjwhAVbG61S&t=407) variables.
Variables can be [passed by value or](https://youtu.be/zB_2x0NPMGc?si=MNPQn2AH2ETyVPT5&t=500) [by reference](https://youtu.be/zB_2x0NPMGc?si=x9oAbhmj5KcB9G_c&t=1157) in TwinCAT.
- value = copy,
- reference = original
Pass by value when the parameter is cheap to copy (small memory size)
Pass by reference when the parameter is expensive to copy
### Function Blocks
[Object oriented](https://youtu.be/kd3NY7-IHRk?si=oDUIuFBQI4elZ7hf&t=181) data types. [Functions Blocks](https://youtu.be/kd3NY7-IHRk?si=p5y-PAZBhtYZ_Ntr&t=382) are equivalent to Classes.
[Interfaces](https://youtu.be/phpFd2UIFwo?si=AiBKguSUF3_NDHmS&t=89) are equivalent to generics from swift (Polymorphism). Blueprint of what a function block needs to do (but not how to do it).
[Dependency Injection](https://youtu.be/phpFd2UIFwo?si=Mw2f1azqqQoFD0YV&t=1129)
#### Initialisers
In TwinCAT code that you want run during the initialisation of the Function Block can be put into the [FB_Init method](https://youtu.be/phpFd2UIFwo?si=rHNV5YLziQLg76t6&t=1229). This method will be called implicitly when the function block is initialised.
Providing [input parameters](https://youtu.be/pnLgRNb09qE?si=jB7auxpjSahjm19V&t=983) when instantiating a function block
```
fbFileOpen : FB_FileOpen := (sPathName := 'C:\some_file')
```
#### Methods
[Access Modifiers](https://youtu.be/kd3NY7-IHRk?si=vODax1sAhdpccfQi&t=1628)
To [return a value](https://youtu.be/kd3NY7-IHRk?si=w5sSAJ4uElfeTIl0&t=2075) (from a method or function) you simply assign a value to the name of the block of code you are calling. So if the method is called "IsEventBufferFull" the way to return a value from within the method would be
```
IF bufferIndex >= MAX_BUFFER_SIZE THEN
IsEventBufferFull := TRUE;
ELSE
IsEventBufferFull := FALSE;
END_IF
```
### State Machines
Beckhoff use a very basic design
```
VAR
nState : INT;
END-VAR
========================
CASE nState DO
10:
// State 1
nState := 20;
20:
// State 2
nState := 10;
ELSE
// If there is no defined code for the state number (something went wrong)
END_CASE
```
An [improvement](https://youtu.be/pnLgRNb09qE?si=MVWgrtdcu55knahU&t=608) is to use a [local enumeration](https://youtu.be/pnLgRNb09qE?si=FzNOSr3UfcbpACMV&t=730) to name the states
```
VAR
state : (IDLE, RUN, WAIT, SLOW, STOP, ERROR);
END_VAR
=======================
CASE state DO
IDLE :
state := RUN;
RUN :
state := WAIT;
WAIT :
state := SLOW;
SLOW :
state := STOP;
STOP :
state := IDLE
ERROR :
IF reset THEN
state := IDLE;
END_IF
END_CASE
```
### Attributes
Appear to be sort of like macros?
- Used to allow [enums to be automatically convertible to strings](https://youtu.be/pnLgRNb09qE?si=uW0Eylo9zyDXV42T&t=1981)
## TwinCAT Libraries and Utilities
[Bekchoff Documentation](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/41891384434359666059.html&id=)
[Managing dependancies](https://sagatowski.com/posts/managing_twincat_libraries/) is important with libraries. By default any installed libraries will be set to use the most recent version. This means that you may not be able to generate an identical binary if returning to the project after some time (or with a different engineering environment). The answer is to make sure that the libraries have specific versions selected in your project.
### Tc2_Standard
#### [TON](https://youtu.be/n94JY-5-0jg?si=7F1-JY88osUF30t5&t=112) Timer On Delay
```
VAR
fbTon : TON := (PT := T#3s);
bBool : BOOL;
END_VAR
=======================================
fbTon(IN := bBool);
fbTon.Q
```
#### [TOF]() Timer Off Delay
```
VAR
fbTof : TOF := (PT := T#3s);
bBool : BOOL;
END_VAR
=======================================
fbTof(IN := bBool);
fbTof.Q
```
#### [R_TRIG](https://youtu.be/n94JY-5-0jg?si=mWu5z5MUWdLhuiSd&t=1146) Rising Trigger
```
VAR
fbRTrig : R_TRIG;
bBool : BOOL;
END_VAR
=======================================
fbRTrig(CLK := bBool);
fbRTrig.Q
```
#### [F_TRIG](https://youtu.be/n94JY-5-0jg?si=iPvxEcq-ROshnN8M&t=1244) Falling Trigger
```
VAR
fbFTrig : F_TRIG;
bBool : BOOL;
END_VAR
=======================================
fbFTrig(CLK := bBool);
fbFTrig.Q
```
#### [CONCAT](https://youtu.be/n94JY-5-0jg?si=ODuLiYSEBNfklEBw&t=1738) Concatenate two STRINGs
```
VAR
sString1 : STRING(255) := 'Hello ';
sString2 : STRING(255) := 'World!';
sResultString : STRING(255);
END_VAR
=======================================
sResultString := CONCAT(STR1 := sString1, STR2 := sString2);
```
## Tc2_Utilities
### [FB_MemRingBuffer](https://youtu.be/1s5O8srtDTM?si=DVVWYkI2Vhdsadab&t=225)
FIFO structure for holding data.
#### [Profiler](https://youtu.be/1s5O8srtDTM?si=rraNcatPDB1Ec7SC&t=1928)
This function block is used to measure the execution time of PLC code.
```
VAR
fbProfiler : Profiler;
stProfilerData : PROFILERSTRUCT;
END_VAR
=======================================
fbProfiler(START := TRUE, RESET := TRUE);
// Block of code to measure
fbProfiler(START := FALSE);
stProfilerData := fbProfiler.DATA;
```
#### [NT_StartProcess](https://youtu.be/1s5O8srtDTM?si=brQKo45EipyVr6W1&t=2466)
Function block for starting a Windows process or application.
```
VAR
fbNtStartProcess : NT_StartProcess := (NETID := '127.0.0.1.1.1', TMOUT := T#5s);
END_VAR
=======================================
fbNtStartProcess(PATHSTR := 'C:\Windows\Notepad.exe',
DIRNAME := 'C:\Temp',
START := TRUE);
```
## Libraries
How to freeze libraries for projects https://alltwincat.com/2018/03/01/managing-twincat-libraries/
### Custom Libraries
[Create](https://youtu.be/rWWPWuUYFbg?si=51M2XwLvEvEKaQEH&t=259) a project that is just for the code for the library. Use the XAE Project.
- Clean up the project file. Remove Motion, C++, Safety.
- Copy and paste your function blocks and code into the library.
- Use a POU just for testing.
- [Define a library](https://youtu.be/rWWPWuUYFbg?si=rQSaoLfRgGmVnqWB&t=473)
- Contextual menu for the PLC Project in the Solution Explorer. Select Properties
- Enter the details about the library.
- Released flag locks the library until you update the version number
- Can add a namespace. Use the library name
- Add Global version structure. Creates a file that includes the version details.
[Compile](https://youtu.be/rWWPWuUYFbg?si=vs3IAGrx4pbj_nqs&t=653)
- Contextual menu for the PLC Project in the Solution Explorer. Select "Save as library and install"
- It asks you for a location to store the library file but then immediately moves it to the TwinCAT 3 library installation folder. (C:\TwinCAT\3.1\Components\Plc\Managed Libraries)
[Using the library](https://youtu.be/rWWPWuUYFbg?si=Hk2ouIxXCtYKCNru&t=960)
- In your machine program project you need to [create a reference](https://youtu.be/rWWPWuUYFbg?si=LT85_3RlSNP0OdDj&t=727) to the library.
- Solution Explorer -> References -> Contextual Menu -> Add Library
- By default it shows up in Miscellaneous.
[Categories](https://youtu.be/rWWPWuUYFbg?si=0G4JlC1wY72-f43x&t=785)
- Download the categories file from [github](https://github.com/sagatowski/TwinCAT-LibraryCategories). For EI libraries use the file in ByManufacturer/Beckhoff/EIBarcodesTcLibraryCategories.libcat.xml
- Modify it to add Categories and sub categories (So EI and then Barcode scanners)
- Generate a [GUID](https://www.guidgenerator.com/) (UUID)
- Add the file to the library properties window
Organising
How to provide an updated version?
- Increment the version number in the PLC Project Properties
- Recompile
## TwinCAT Functions
Are a way of extending TwinCAT. They need to be downloaded from Beckhoff and installed on your computer. Some are already pre-installed by default. Add them to your project by adding the libraries as has been described above.
Available Catgories
- TF1xxx TC3 System
- TF2xxx TC3 HMI - User interface library using HTML5, CSS and JS
- TF3xxx TC3 Measurement - Scope, power analyser
- TF4xxx TC3 Controller - PID, Filters
- TF5xxx TC3 Motion - Motor controls and CNC
- TF6xxx Connectivity - Fieldbuss's
- TF7xxx Vision - Cameras for quality control
- TF8xxx Industry Specific - Building automation, Wind Turbines
#### Connectivity
TF6020 TC3 JSON data interface
TF6100 TC3 OPC-UA
[TF6250 TC3 Modbus TCP](https://youtu.be/FOC0rUeECDs?si=hVOweM1q89NEJhGm&t=414)
TF6270 TC3 Profinet RT Device (Siemens Fieldbus)
TF6300 TC3 FTP Client
## FieldBus
### EtherCAT
In config mode you can bring up the contextual menu for "Devices" (in IO) and then select ["Scan"](https://youtu.be/zQwiBeDbDcM?si=El7v16i0-7Flg-9r&t=1150). It should find all of the EtherCAT interfaces, then you can let it search for boxes (devices on the network). You might need to install .ESI files in order for the program to correctly map out the memory that is required to be transferred.
### Other FieldBuss's
[Video](https://youtu.be/zQwiBeDbDcM?si=fnSE4ItMR-az1-YQ&t=1772)
## Data Persistance
### CSV files
[TwinCAT 3 Datalogging or Recipe via CSV Reading and Writing](https://www.youtube.com/watch?v=SImFETapSXg)
The example is in the Beckhoff Infosys documentation system under TwinCAT 3 / TE1000 XAE / PLC / PLC Libraries / Tc2_Utilities / Samples / [Example: Writing/Reading of CSV file](https://infosys.beckhoff.com/english.php?content=../content/1033/tcinfosys3/index.html&id=). There is a sample project that can be [downloaded](https://infosys.beckhoff.com/content/1033/tcplclib_tc2_utilities/Resources/803601163.zip).
The downloaded project comes in the form of an archived project file (.tpzip). To use it we need to create a new project in XAEShell and then import it as the PLC. [Instructions for using .tpzip archived projects](https://youtu.be/SImFETapSXg?si=SGBoMzgkPsGrUMkw&t=372)
Jakob Sagatowski also has CSV writing as part of his tutorial. It starts off with
- [Opening a file](https://youtu.be/pnLgRNb09qE?si=a7yUo01PkVQPy_s7&t=598)
-
## TCP/IP UDP
[TF6310](https://www.beckhoff.com/tf6310) is used for general purpose networking. There is an example project on [Github](https://github.com/Beckhoff/TF6310_Samples). The [UDP](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/9007199338979083.html&id=) project has 3 helper pages for [setting up the PLC project](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/84233611.html&id=), [explaining the PLC code](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/84238091.html&id=) and [getting the .Net test program running](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/84241035.html&id=). The [API description](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/84117899.html&id=8368536050982972780) gives an overview of the function blocks that are needed for getting networking working. The complete list for the API is [here](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6310_tc3_tcpip/1436387851.html&id=5737259533555290925).
- https://github.com/Beckhoff/TF6310_Samples/
## Time and Duration
[How to measure a duration](https://stackoverflow.com/questions/67599108/twincat-how-to-measure-program-execution-time) between events in a program. [Another example](https://infosys.beckhoff.com/english.php?content=../content/1033/cx7031/10675338251.html&id=)
```
PROGRAM PLC_PRG
VAR
tStart: TIME; (* Time program start *)
tWork : TIME; (* Execution time *)
END_VAR
(* First line of main program *)
tStart := TIME();
// Your program here
(* Last line of your program *)
tWork := TIME() - tStart;
END_PROGRAM
```
You can also use [LTIME](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/12189021579.html&id=) to get a higher resolution (nano-seconds). [TIME](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/12189021579.html&id=) has a milli-second resolution.
# Human Machine Interface (HMI)
There are a couple of different HMI options for Beckhoff PLCs. There is a builtin visualisation stack that is fairly limited. Or there is a HTML5 based solution that is the current recommendation.
[Demonstration](https://beckhoff-au.teachable.com/courses/1070403/lectures/22804238)
[Tutorial](https://hellotwincat.dev/twincat-hmi-your-first-project-project-upload-server-config/)
You can [Auto-Start](https://infosys.beckhoff.com/english.php?content=../content/1033/te2000_tc3_hmi_engineering/6813194891.html&id=) the HMI client using a web browser. You can also start in kiosk mode using Chrome so that the HMI takes up the entire display.
### Tx2000 (HTML5))
Need to install TE2000 HMI Engineering and TF2000 HMI Server.
# TwinSAFE
[Applications Guide](https://assets.euautomation.com/uploads/parts/pdf/el1904.pdf)
## Delivering a Project
https://alltwincat.com/2020/11/02/handling-different-versions-of-twincat/
- [Pin the TwinCAT software](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_remote_manager/9007202409368843.html&id=2353451714920715042)
- Lock the libraries using the [placeholders](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/4189532299.html&id=)
-
# Examples
## Jakob Sagatowski
- [Structures and Functions](https://youtu.be/zB_2x0NPMGc?si=gh_b4SUATqiRFsD5&t=744) Update an events timestamp from system time
- [Function Blocks](https://youtu.be/kd3NY7-IHRk?si=i7XMDAdAwm5_3Ws6&t=1098) Continuation of the event logger
- [Interfaces](https://youtu.be/phpFd2UIFwo?si=s_wsdTuX-U2VyjXn&t=618) Storing the events persistently
- [Instructions](https://youtu.be/pnLgRNb09qE?si=mIGpxcqFDCW6_-uw&t=385) Changing the storage from XML to CSV
- [Timers (TON / TOF)](https://youtu.be/n94JY-5-0jg?si=nIp6DOPdk0m-WQlQ&t=379) New project, not an extension of the examples above
- [Triggers (R_TRIG / F_TRIG)](https://youtu.be/n94JY-5-0jg?si=boK7jPj5mrwzh9vm&t=1325)
- [Profiler](https://youtu.be/1s5O8srtDTM?si=nTYDUHF86JmeZHJ1&t=2000) Function block for timing the execution of PLC code
- [NT_StartProcess]() Running ping and getting the response back into the PLC program
- [Modbus](https://youtu.be/FOC0rUeECDs?si=42nRigvf9v2ytrzJ&t=578) Creating a program that talks to a simulated Modbus server
- [Unit Testing](https://youtu.be/FnXY6MA3axw?si=3IWfGIwX4NIugMUS&t=857) Using the CSV Event Logger from earlier examples.
## Resources
https://github.com/benhar-dev/twincat-resources
[Instructions for using .tpzip archived projects](https://youtu.be/SImFETapSXg?si=SGBoMzgkPsGrUMkw&t=372)
[Electrical Automation Hands On Videos](https://www.youtube.com/playlist?list=PL762SfoJC-e_zInKf3ZRgoa1jLl10deYb)