この記事では私はBeckhoff IPC C6920とTwinCAT3を使って一つのEtherNet/IP Adapterの中に8つのAssembliesを構築し、Rockwell Compact GuardLogix L43S + 1768 ENBAT-A組み合わせのEtherNet/IP ScannerでEtherNet/IP ネットワークを構成します。
TwinCATは一つのIP Addressに最大8個のAssembliesを作成できるってことは、IPの節約になるだけではなく、複数のScannerから様々なデータにアクセスできます。たとえばAssemblies1はZone1、Assemblies2はZone2…のように。そして最後はRockwell側でST言語を使用しIOデータが一致してるかをCheckするプログラムも作成します。
では、始めましょう!
Thanks!
この記事が出来上がるのはベッコフ日本法人ベッコフオートメーション株式会社さまから機材を貸してくださったおかけです。誠にありがとうございます。
ベッコフ日本法人ベッコフオートメーション株式会社
IPC6920-005はベッコフ日本法人ベッコフオートメーション株式会社さまが貸してくださったものです。Beckhoff Automationは1980 年会社設立、PCベースの制御技術をベースにしたオープンオートメーションシステム導入の先頭に立つドイツ企業です。
ベッコフ日本法人ベッコフオートメーション株式会社は、2011年に横浜に本社、2017年に名古屋オフィスを設立しました。
こちらはベッコフ日本法人ベッコフオートメーション株式会社様のホームページです。
どうぞよろしくお願いします。
https://www.beckhoff.com/ja-jp/
Implementation-1
最初のImplementationはBeckhoff TwinCAT側で一つのEtherNet/IP Adapterで8つのAssembliesを作成します。各Assembliyは64 BytesのIn/outデータを占有します。
TwinCAT Side
まずTwinCAT Sideから構築します。
Choose Target
Projectをクリック>Choose Targetします。
いまTwinCATがIPCと繋がっているかを確認しましょう。
Add Ethernet/IP Adapter
I/O>Devices>Add New Itemします。
¥
EtherNet/IP>Ethernet/IP Adapter(Slave)を選び>Okします。
Ethernet/IP Networkで接続してるEthernet Interfaceを選択し>Okします。
Adapter Tabを開き、設定されたInterfaceが正しいかを再確認しましょう。
Sync Task
Sync Taskを開き、Special Sync Task Optionsを設定>Create new I/O Taskします。
Task名を入力>Okします。
Ethernet/IP Adapter用のTaskが作成されました。
Configure The box
次はEthernet/IP Adapter Connectionの設定を行います。
IP
Settings Tabを開き、8000:21、8000:22でIpとNetwork Maskを設定します。
アプリケーションに合わせて入力しましょう。
Append IO Assembly
次はIO Assemblyを追加します。Box>右クリック>Append IO Assemblyで新規Assemblyを追加します。
“Assembly 1(Input/Output)” は作成されました。
Add New Item-INPUT
Default上でTwinCATはInput/Outputデータも自動作成しませんので、手動でDataを作成しましょう。Inputs>右クリック>Add New Itemします。
変数名を入れ、WordをData Type設定>Create Array Typeします。
31 Wordsの配列が定義されました。
Add New Item-OUTPUT
Outputも同じようにデータを追加します。Outputs>右クリック>Add New Itemします。
変数名を入れ、Data Typeを Array[0..31]of WORD設定>Okします。
Output 変数が作成されました。
Add More Assembly!
次はAssemblyをCopyし、同じくBoxで貼り付ければOKです。(注意するのは1つのBoxは同時にMax8 Connectionsに制限されています)
Export EDS
Box1>右クリック>Export EDS FileでEDS FileをExportします。
Choose Yes.
Export 先を設定しましょう。
Done!
Add PLC
次はPLC>Add New Item.でPLCを追加します。
Standard PLC Project>AddでPLCを追加します。
ADD DUT
8つのAssembliesがProjectにあるので、DUTを作成しPLCの変数を定義したほうが手間がかかりません。右クリック>Add>DUTします。
DUT名を入力>Structureを選択>Openします。
DUTの中にProcess InputとProcess Outputのarray[0..31]変数とConnection状態を示すUDINTを定義します。それらの変数はAssemblyのInput/Output/ConnState変数と紐つけます。
TYPE DUT_Boxes : STRUCT InData AT %I* :ARRAY[0..31]OF WORD; OutData AT %Q* :ARRAY[0..31]OF WORD; ConnState AT %I* :UDINT; END_STRUCT END_TYPE |
ADD GVL
いまはGlobal Variable List(GVL)を作成し変数を定義します。
Boxesの中に8つのAssemblyがありますので、配列長8のDUT_Boxesの変数を定義します。
{attribute ‘qualified_only’} VAR_GLOBAL Boxes :ARRAY[1..8]OF DUT_Boxes; END_VAR |
PROGRAM
Main Program中にInput dataをそのままOutput DataにLoopbackするだけです。IOデータ一致性のCheckはRockwell側で行います。
PROGRAM MAIN VAR i,j :DINT; END_VAR FOR i:=1 TO 8 DO FOR j:=0 TO 31 DO GVL.Boxes[i].OutData[j]:=GVL.Boxes[i].InData[j]; END_FOR; END_FOR |
Build
Build>Build Solution でProjectをコンパイルします。
Link Variables
最後は変数をProcess Input/Outputと紐つけます。
Inputs-ConnState
まずはConnState変数をLinkします。その変数は該当するAssemblyのConnection Statusを示しています。
GVL内の1つ目の変数を選び>OK!
Inputs-data
次はInput dataをUser Programと紐つけます。GVLで定義した配列は100%Dataのサイズと同じなので、直接右クリック>Change Linkで一括設定できます。
Outputs-data
最後にOutput DataをUser ProgramとLinkしましょう。GVLで定義した配列は100%Dataのサイズと同じなので、直接右クリック>Change Linkで一括設定できます。
Activate Configuration
Activate ConfigurationでHardware ConfigurationをRuntimeにDownloadします。
OKで進みます。
ライセンスが足りないならYesでライセンス入力画面に切り替えます。
Magic Codeを入力>Okします。
OKでTwinCATを再起動しRun Modeに切り替えます。
Login
LoginでUser ProgramをRuntimeにDownloadします。
Yesで進みます。
Start
StartでProgramをRunします。
Rockwell Side
次はRockwell Sideに着手します。
Import EDS File
下記のLinkからEDS FileのImport方法を参考にしてください。
Add TwinCAT Adapter
Hardware ConfigurationにBeckhoff TwinCAT Ethernet/IP Adapterを追加します。
1768-ENBT/Aを右クリック>New Moduleします。
Beckhoffを検索します。
そのAdapterを選び>Createで作成します。
Box1はEthernet/IP Networkに追加されました。
Configure IP Address
先程設定したBeckhoff TwinCAT Etherenet/IP Adapterをクリックします。
Adapter名に名前を設定>IP Addressを入力しましょう。
Connection Tabを開き、いまAssembly1をExclusive ownerとして構築しています。
Configure Connections
General Tabに戻り、Change ボタンをクリックしConnectionを変更します。
Connectionの設定画面が表示されました。
Drop ListからAssembly2 Inputs and Outputs(Exclusive Owner)を使って2つ目のConnectionを作成します。
Done!新しいConnectionが作成されました。
他のConnectionも同じく作りましょう。
General Tabに戻ると、すべてのConnectionも構成されました。
Download
Communication >DownloadでプロジェクトをCPUにDownloadしましょう。
.
Downlaodで進みます。
しばらく待ちます…
Done!CPUをリセットRunします。
Result
RSLogix 5000からI/O OK のLEDは緑色になりました!つまりRockwell PLCとTwinCAT 間でEtherNet/IP 通信も確立しました!
Implementation-2
EtherNet/IP通信を確立したところで、次はRockwell側で簡単なラダーロジックでIOデータ転送したり、Connection Checkしたりしましょう。
Rockwell Side
Controller Tags
Controller Tagsを開くと、RsLogic5000はBox1の通信Tagをすでに自動作成してくれました。
Program
Now we can create some Ladder Program. Open the MainRoutine.
ラダープログラムを作成します。MainRoutineを開きます。
End Lineのところを右クリック>Add Rungで新しいRungを追加します。
A接点をRung0にDropします。
A接点がRung0に追加されました。
ConnectionFaulted 変数と紐付け、通信エラーが発生するとその変数がTrueになります。
次は通信エラーが発生したときTwinCATに転送するデータを0にリセットしたいので、“Move/Logical”からMOV命令をRung0にDropします。
Rung0の一番うしろにMove Blockが追加されました。
Source パラメータを0に入力し、DestパラメータをBox1_Ass1:O1:Data[4]に入力します。
次に、ConnectionFaultedはFalseするときTwinCATに与えを転送したいので、End Rungを右クリック>Add Rungで新しいRungを追加します。
Rung1が追加されました!
今度はB接点をRung1にDropします。
同じくConnectionFaulted変数と紐つけます。
もしConnectionFaultedがFalseであれば、TwinCATへの出力をずっと加算するようにしたいので、“Compute/Math”からAdd命令をDropします。
ADD Blockが追加され、Soruce AをAssign Box1_ASS1:O1で、Source Bは定数1、Destは Assign Box1_ASS1:O1:Data[4]に割り付けます。
Result
ProjectをCPUにDownloadし結果を見てみましょう。Connectionが確立すると変数はずっと加算されています。
TwinCAT側をみると、InData[0] の現在値もCycleで更新されていますね。
Implementation-3
最後はRockwell側でSTプログラムを作成、IO Dataの一致性をCheckしていきます。
Rockwell Side
Instruction Memo
これからは今回使用する命令のメモです。
TONR
TONR はリセット可能なDelay On Timerです。
Input Parameters
Name | Data Type | Description |
EnableIn | BOOL | Function Block:False=命令は実行せず、出力も更新しません。DefaultはTrueです。 Structured Text:命令を実行する。 |
TimerEnable | BOOL | True=Timerは時間をCount始めるDefaultはFalseです。 |
PRE | DINT | Timerの設定値。単位は1msec です。 |
Reset | BOOL | Timerをリセットする |
Output Parameters
Name | Data Type | Description |
EnableOut | BOOL | 命令は正常に実行されています。 |
ACC | BOOL | 現在の加算時間値(milliseconds.) |
EN | DINT | True=Timerは有効中 |
TT | BOOL | True=Timer実行中 |
DN | BOOL | Delay Timer ON |
Status | DINT | Status of the function block.Function blockの状態になります。Status.01:命令実行中にエラー発生Status.02:Timer設定値は無効 |
Flow
Example
TONR_01.Preset := 500; TONR_O1.Reset := reset; TONR_01.TimerEnable := limit_switch1; TONR(TONR_01) timer_state := TONR_01.DN; |
COP
これは三菱のBMOVと同じだと思っていただければと思います。
Parameters
Name | Data Type | Description |
Source | SINT/INT/DINT/REAL/structure | Copy元 |
Destination | SINT/INT/DINT/REAL/structure | Copy先 |
Length | DINT | number of Destination elements to copyCopyするデータのTotol長さ |
Example
COP(array_4[0],array_5[0],10); COP(timer_1,array_timer[5],1); |
Add New Routine
IO データを検証するプログラムはST言語で実装します。MainProgram>右クリック>New Routineをクリックします。
Routine名を入力し、TypeをStructured Text選び>Okします。
新しいRoutineが作成されました。
Change Routine
次はMainTaskの中の呼び出しのプログラムを変更します。
MainProgramを右クリック>Propertiesします。
元に”MainRoutine”がAssgined Routineのところに設定されていましたが、MySTProgramに変更しましょう。
MainのFieldからMySTProgramをそのまま選択すればOKです。
Done!
MySTProgram
次はプログラムを作成します。
Program Tags
こちらの変数はプログラム内で使用します。
- bCheck:Checkプログラムをトリガーする
- bError: True=IO Data不一致
- bOK:True=IO Data一致
- bResetTagValue:Output Dataをリセットする
- bSetTagValue:Output Dataをセットする
- iCounter:For loopで使用するCounter 変数
- iStep:the Case step制御用の変数
- MyTimer:IO DataをCheckするまでのDelay Timer
- Value:書き込みデータのベース
- TimeSetting:IO Data をCheckするのDelay時間
PROGRAM
プログラムはまずAdapter(TwinCAT)と接続状態を確認>出力データを書き込み>Delay>Input DataとOutput Dataは一致するかCheck>Loop Backだけです。
if not Box1_Ass1:I1.ConnectionFaulted and not Box1_Ass1:I2.ConnectionFaulted and not Box1_Ass1:I3.ConnectionFaulted and not Box1_Ass1:I4.ConnectionFaulted and not Box1_Ass1:I5.ConnectionFaulted and not Box1_Ass1:I6.ConnectionFaulted and not Box1_Ass1:I7.ConnectionFaulted and not Box1_Ass1:I8.ConnectionFaulted then ConnectionOK:=1; else ConnectionOK:=0; end_if; if TimeSetting <=0 then TimeSetting:=100; end_if; MyTimer.EnableIn:=1;MyTimer.TimerEnable:=iStep= 30 or iStep = 65;MyTimer.PRE:=TimeSetting; TONR(MyTimer); case iStep of 0: if ConnectionOK and not bError then iStep:=10; end_if; 10: bOK:=0; bSetTagValue:=1; iStep:=20; 20: if not bSetTagValue then iStep:=30; end_if; 30: if MyTimer.Dn then iStep:=40; end_if; 40: bCheck:=1; if bOK then iStep:=50; bCheck:=0; end_if; if bError then iStep:=999; end_if; 50: bResetTagValue:=1; iStep:=60; 60: if not bResetTagValue then iStep:=65; end_if; 65: if MyTimer.Dn then iStep:=70; bOK:=0; end_if; 70: bCheck:=1; if bOK then iStep:=10; bCheck:=0; end_if; if bError then iStep:=999; end_if; 999: bCheck:=0; bOK:=0; iStep:=0;end_Case; if bCheck and not bError then for iCounter:=4 to 67 do if Box1_Ass1:O1.Data[iCounter] <> Box1_Ass1:I1.Data[iCounter]then bError:=1; elsif Box1_Ass1:O2.Data[iCounter] <> Box1_Ass1:I2.Data[iCounter] then bError:=1; elsif Box1_Ass1:O3.Data[iCounter] <> Box1_Ass1:I3.Data[iCounter] then bError:=1; elsif Box1_Ass1:O4.Data[iCounter] <> Box1_Ass1:I4.Data[iCounter] then bError:=1; elsif Box1_Ass1:O5.Data[iCounter] <> Box1_Ass1:I5.Data[iCounter] then bError:=1; elsif Box1_Ass1:O6.Data[iCounter] <> Box1_Ass1:I6.Data[iCounter] then bError:=1; elsif Box1_Ass1:O7.Data[iCounter] <> Box1_Ass1:I7.Data[iCounter] then bError:=1; elsif Box1_Ass1:O8.Data[iCounter] <> Box1_Ass1:I8.Data[iCounter] then bError:=1; end_If; end_for; if not bError then bOK:=1; end_if; end_if; if bSetTagValue or bResetTagValue then for iCounter:=4 to 67 by 2 do if bSetTagValue then COP(iCounter,Box1_Ass1:O1.Data[iCounter],2); Value:=iCounter*10; COP(Value,Box1_Ass1:O2.Data[iCounter],2); Value:=iCounter*20; COP(Value,Box1_Ass1:O3.Data[iCounter],2); Value:=iCounter*30; COP(Value,Box1_Ass1:O4.Data[iCounter],2); Value:=iCounter*40; COP(Value,Box1_Ass1:O5.Data[iCounter],2); Value:=iCounter*50; COP(Value,Box1_Ass1:O6.Data[iCounter],2); Value:=iCounter*60; COP(Value,Box1_Ass1:O7.Data[iCounter],2); Value:=iCounter*70; COP(Value,Box1_Ass1:O8.Data[iCounter],2); end_if; if bResetTagValue then Value:=0; COP(iCounter,Box1_Ass1:O1.Data[iCounter],2); COP(iCounter,Box1_Ass1:O2.Data[iCounter],2); COP(iCounter,Box1_Ass1:O3.Data[iCounter],2); COP(iCounter,Box1_Ass1:O4.Data[iCounter],2); COP(iCounter,Box1_Ass1:O5.Data[iCounter],2); COP(iCounter,Box1_Ass1:O6.Data[iCounter],2); COP(iCounter,Box1_Ass1:O7.Data[iCounter],2); COP(iCounter,Box1_Ass1:O8.Data[iCounter],2); end_if; end_for; bSetTagValue:=0; bResetTagValue:=0;end_if; |
Result
TwinCAT3 Side
こちらはTwinCAT側の結果です。ConnState=0、つまり接続OKで、入力データは常に更新されています。
Rockwell Side
Rockwell側でも出力を更新しずつエラーがありません。
If Error..
もしIO Data不一致の場合、bError=Trueになり、Stepも0に戻ります。
Source Project
こちらのLinkから記事のプロジェクトをDownloadしてください。
https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT_EIP_Rockwell.tnzip