PLCNEXT#Start as Modbus TCP/IP Server x TwinCAT/Codesys Client

この記事ではPLCNEXT AXC-F-2152 ControllerからMODBUS-TCP/IP Serverを立ち上げ、CodesysとTwinCAT3+TF6250からModbus TCP/IP Clientを実装しデータをやりとりします。PLCNEXT EnginneringではModbus TCP/IPのライブラリがDefaultインストールされていないのでPLCNEXT StoreからDownloadすることができます。

どうぞよろしくお願いします。


Library Download

下記のLinkにアクセスします。

https://www.plcnextstore.com/world/app/1443

Loginをクリックします。

Username や Passwordし、Loginをします。

Choose your Version

LibraryはダウンロードするVersionが赤枠で設定できます。

Prices

もちろん、無料のものがあれば有料なLibraryもあります。

Library?Function Extension?Runtime?

LibraryというのはPLCNEXT EngineerにImportできるFileです。ImportされたLibraryはFunction Blockとしてプロジェクト内で使用できます。

Function Extension/Runtimeは直接PLCNEXT Runtimeにインストールするもので、場合によりインストールしたあとに設定Fileを修正する必要があります。

Supported Devices

そしてSupportできるDeviceにもList-upされています。

Manual

LibraryのインストールやFunction Block説明のManualもDownloadできます。

Change Logs

そして各Versionのメイン変更も記載しています。

Download

Loginしたら、Installのところがクリックできなくなります。

DownloadをクリックしLibraryをDownloadします。

ライセンスに同意し>Downloadします。

Zip FileがDownloadしました。

解凍すると、DocumentsとFilesがあります。

Function Block

LibraryのすべてのFunction Blockを紹介するのは無理ありますので、今回記事で使うFunction Blockだけ紹介します。

MB_TCP_Server

こちらのFunction Blockを使用することによりPLCNEXT AXC F2152 Cotnroller内でModbus TCP/IP Serverを立ち上げることができます。


VAR_INPUT

xActivate BOOL 立ち上げ=Block有効たち下げ=Block無効
xAcknowledge BOOL 立ち上げ=Error MessageをClearする(でもBlock自体は初期化しない)
xAutoAck BOOL True=Errorを自動AckするErro発生するとwDiagCodeとwAddDiagCodeが1Cycle出力する
xUDP BOOL Communication Protocol選択。False=Modbus-TCP,True=Modbu-UDP
strBindIp STRING SeverのSocketIP、文字列です。Example:”192.168.1.1”Emptyや0.0.0.0ならSystemが自動的適切なEthernet Adapterを選択する。
uiBindPort UINT ServerのPort番号を設定します。0=Systemをランダムで選ぶ。注意するのはModeにより変わります。UDP=使用しないTCP=Port指定、1つ以上のServerが存在する場合Portを被らないようにしてください。
strDestIp STRING ClientのIPを指定する。TCP=0.0.0.0は任意のClientUDP=使用しない
uiDestPort UINT ClientのPortを指定するTCP=0なら任意のPortUDP=使用しない
tReconnectDelay TIME 再接続の時間間隔です。
tTimeout Time Timeoutです。

VAR_OUTPUT

xActive BOOL False=Block実行してないTrue=Block実行中
xConnected BOOL TCPの場合:True=Clientと接続するUDPの場合:True=MB_TCP_SocketがConnectionを開通した
xErrorBOOL1=Errorあり
wDiagCodeWORDError発生ときの情報1
wAddDiagCodeWORDError発生ときの情報2
udtDiagMB_TCP_UDT_SER_DIAGError発生ときの情報3

VAR_INOUT

arrHoldingRegisters MB_TCP_ARR_W_0_65535 長さ65535のWord配列、Holding registersです。Function Codes3,6,16,23でアクセスできます。
arrInputRegisters MB_TCP_ARR_W_0_65535 長さ65535のWord配列、Input registersです。Function Codes4でアクセスできます。
arrInputsMB_TCP_ARR_X_0_65535長さ65535のBit配列、Discrete inputsです。Function Codes4でアクセスできます。
arrCoilsMB_TCP_ARR_X_0_65535長さ65535のBit配列、coilsです。Function Codes1,15でアクセスできます。

PLCNEXT Side

ではDownloadしたLibraryをProjectにImportし、Modbus TCP/IP Serverを立ち上げるプログラムを作成します。

Reference Link

http://soup01.com/ja/category/plcnext/

Add Library

Components>Libraries>右クリック>Add User Libraryします。

PLCNEXT StoreからダウンロードしたLibraryを選び>Openします。

Components>Programmingを開くとMdobus_TCP_XXのFolderが出てきます。

そのXXはライブラリのバージョンです。

.

Program

LibraryをImportしたところで、次はModbus TCP/IP Serverを立ち上げたいと思います。

Variables

以下の変数を追加します。

この4つはModbusのRegister変数です。0-65535の固定長さだそうです。

VariablesType
arrCoils MB_TCP_ARR_X_0_65535
arrHoldingRegisters MB_TCP_ARR_W_0_65535
arrInputRegistersMB_TCP_ARR_W_0_65535
arrInputsMB_TCP_ARR_X_0_65535

Code

Modbus TCP/IP Serverを起動するFunction Blockを呼び出します。

Server側がエラー発生した場合xActivateをFalseし>xErrorがFalseになったらまたxActivateをTrueし、Modbus TCP/IP Serverを立ち上げる簡単Loopです。

myServer(
    xActivate:=xActivate
    ,xAcknowledge:=xAcknowledge
    ,xAutoAck:=xAutoAck
    ,xUDP:=FALSE
    ,strBindIp:=’192.168.1.11′
    ,uiBindPort:=502
    ,strDestIp:=’0.0.0.0′
    ,uiDestPort:=0
    ,tTimeout:=T#2s
    ,tReconnectDelay:=T#1s
    ,arrHoldingRegisters:=arrHoldingRegisters
    ,arrInputRegisters:=arrInputRegisters
    ,arrCoils:=arrCoils
    ,arrInputs:=arrInputs
    ,xActive=>xActive
    ,xConnected=>xConnected
    ,xError=>xError
    ,wDiagCode=>wDiagCode
    ,wAddDiagCode=>wAddDiagCode
    ,udtDiag=>udtDiag
    );
   
    r1(CLK:=myServer.xError);
    if r1.Q THEN
        count:=count+1;
        xActivate:=False;
    END_IF
       
if  not xActivate and not xError THEN
          xActivate:=True;
  END_IF


  //write the value
  arrInputRegisters[0]:=16#EF02;
  arrHoldingRegisters[1]:=16#ABEC;

  //
  arrInputs[1]:=TRUE;
  arrInputs[2]:=TRUE;


Play with Codesys

最初にプログラムなしのCodesysから検証していきたいと思います。

Reference Link

Add Modbus-TCP Client

Modbus TCP/IP MasterとModbus TCP/IP Slaveを追加します。

Point1 – IP

Codesys Runtime実行してるMachineのIPを同じSubnetに設定してください。

Point2 – Master update time/Auto update

もしつながってTimeoutなど頻繁に発生したら、Response timeout(ms)やSocket timeout(ms)を調整してください。

Auto=ReconnectのCheck-boxを入れるとCodesys Runtimeが自動的に再接続します。

Point3 – Client Connection Setup

もちろんModbus TCP/IP Server側のIP(今回の記事はPLC NEXT AXC F 2152 Controller)、

あおtはUnit ID、TimeoutとアクセスPortを設定してください。

Point4 – Modbus slave Channel setup

Slave ChannelにInput Registers/Multiple Registers/Discrete Inputs/Multiple Coilsを作成します。

Point5 – Slave I/O Mapping

もちろんUser Programの変数と紐付けを忘れずに。

Program

プログラムは難しくないので、単なるいくつかのBytesを与え変更し、両方も確認するだけです。

VAR

PROGRAM PLC_PRG
VAR
data:WORD;
Modbus_Function16:ARRAY[0..99]OF WORD; //Q
Modbus_Function4:ARRAY[0..99]OF WORD; //I
Modbus_Function2:ARRAY[0..12]OF BYTE; //I
Modbus_Function15:ARRAY[0..12]OF BYTE; //Q
END_VAR

Code

Modbus_Function16[0]:=16#1234;
Modbus_Function16[99]:=16#4567;

Modbus_Function16[10]:=Modbus_Function4[0];
Modbus_Function16[11]:=Modbus_Function4[1];

Modbus_Function15[0].0:=TRUE;
Modbus_Function15[0].7:=TRUE;

Modbus_Function15[1].1:=Modbus_Function2[0].1;
Modbus_Function15[1].2:=Modbus_Function2[0].2;

Result

ProjectをDownloadしClientとServerが接続してるかを確認します。

そしてPLCNEXT EngineeringとCodesys IDEで両方も現在値を確認しましょう。

Play with TwinCAT

Reference Link

Beckhoff#TwinCAT3 TF6250 Modbus-TCP Function

FlowChart

制御の流れはすごく簡単です。Connectionパラメタを初期化>Holding Regisiterに与えを書き込む>Input Registerの現在値を読み込む>Inputsを読み込む>Coilsを書き込む>また最初にから始まるLoopです。

途中でエラーが出たらErrorのStepに飛び込み、Resetしたらまた最初にからやり直す。


Program

FB_MBWriteRegs・FB_MBReadInputRegs・FB_MBReadInputs・FB_MBWriteCoilsを使用しModbus TCP/IP Serverをアクセスデータをやり取りします。

VAR

PROGRAM MAIN
VAR
//Function Blocks
MB_WriteRegs :FB_MBWriteRegs; 
MB_ReadInputRegs :FB_MBReadInputRegs;
MB_ReadInputs :FB_MBReadInputs;
MB_WriteCoils :FB_MBWriteCoils;

//Send.Receive Buffer
ArraMB_Reads :ARRAY[0..99]OF WORD;
ArraMB_Write :ARRAY[0..99]OF WORD;
ArrayMB_ReadsInput :ARRAY[0..99]OF WORD;
ArrayMB_ReadInputCoils :ARRAY[0..99]OF WORD;
ArrayMB_WriteCoils :ARRAY[0..99]OF WORD;

//System Flags
bReset :BOOL;
bError :BOOL;
iStep :INT:=0;
w0,w1,w2,w3 :WORD;
b0,b1,b2,b3 :BOOL;
wErrorWord :WORD;
wErrorID :UDINT;
i:INT;

//Connection Parameters
sIPAddr :STRING(15);
nTCPPort :UINT;

END_VAR

VAR CONSTANT
ciStepModbusConfigurationInit :INT:=0;
ciStepModbusWriteRegisiters :INT:=10;
ciStepModbusRegInputRegisters :INT:=20;
ciStepMdobusReadInputs :INT:=30;
ciStepModbusWriteCoils :INT:=40;
ciStepModbusError :INT:=8000;
END_VAR

Code

CASE iStep OF
ciStepModbusConfigurationInit:
//IP and Port
sIPAddr:=’192.168.1.11′;
nTCPPort:=502;
//Error Flag Reset
wErrorID:=0;
wErrorWord:=0;
bError:=FALSE;
bReset:=FALSE;
//Function Execute Flags
MB_WriteRegs.bExecute :=FALSE;
MB_ReadInputRegs.bExecute :=FALSE;
MB_ReadInputs.bExecute :=FALSE;
MB_WriteCoils.bExecute :=FALSE;
//Init
MB_WriteRegs();
MB_ReadInputRegs();
MB_WriteCoils();
MB_ReadInputs();
//Step Jump
IF NOT MB_WriteRegs.bBusy
AND NOT MB_ReadInputRegs.bBusy
AND NOT MB_ReadInputs.bBusy
AND NOT MB_WriteCoils.bBusy
THEN
iStep:=ciStepModbusWriteRegisiters;
END_IF

ciStepModbusWriteRegisiters:

//Write the Regisiters
ArraMB_Write[0]:=ArraMB_Write[0]+1;
ArraMB_Write[1]:=ArraMB_Write[1]-1;
ArraMB_Write[98]:=ArraMB_Write[98]+1;
ArraMB_Write[99]:=ArraMB_Write[99]-1;
MB_WriteRegs.bExecute:=TRUE;
//Function Blocks
MB_WriteRegs(
nUnitID:=16#34
,nMBAddr:=300
,nQuantity:=100
,cbLength:=SIZEOF(ArraMB_Write)
,pSrcAddr:=ADR(ArraMB_Write)

);
//Result
IF NOT MB_WriteRegs.bBusy THEN
IF MB_WriteRegs.bError THEN
iStep:=ciStepModbusError;
ELSE
FOR i:=0 TO 99 DO
ArrayMB_ReadsInput[i]:=16#00;
END_FOR
iStep:=ciStepModbusRegInputRegisters;
END_IF
END_IF

ciStepModbusRegInputRegisters:
//Flags
MB_ReadInputRegs.bExecute:=TRUE;
//Function Blocks
MB_ReadInputRegs(
nUnitID:=16#35
,nMBAddr:=200
,nQuantity:=100
,cbLength:=SIZEOF(ArrayMB_ReadsInput)
,pDestAddr:=ADR(ArrayMB_ReadsInput)
);
//Result
IF NOT MB_ReadInputRegs.bBusy THEN
IF MB_ReadInputRegs.bError THEN
iStep:=ciStepModbusError;
ELSE
w0:=ArrayMB_ReadsInput[0];
w1:=ArrayMB_ReadsInput[1];
w2:=ArrayMB_ReadsInput[98];
w3:=ArrayMB_ReadsInput[99];
FOR i:=0 TO 99 DO
ArrayMB_ReadInputCoils[i]:=16#00;
END_FOR
iStep:=ciStepMdobusReadInputs;
END_IF
END_IF
ciStepMdobusReadInputs:
//Flags
MB_ReadInputs.bExecute:=TRUE;
//Function Blocks
MB_ReadInputs(
nUnitID:=16#36
,nQuantity:=100
,nMBAddr:=0
,cbLength:=SIZEOF(ArrayMB_ReadInputCoils)
,pDestAddr:=ADR(ArrayMB_ReadInputCoils)
);

//Result
IF NOT MB_ReadInputs.bBusy THEN
IF MB_ReadInputs.bError THEN
iStep:=ciStepModbusError;
ELSE
b0:=ArrayMB_ReadInputCoils[0].0;
b1:=ArrayMB_ReadInputCoils[0].1;
b2:=ArrayMB_ReadInputCoils[0].2;
b3:=ArrayMB_ReadInputCoils[0].3;
FOR i:=0 TO 99 DO
ArrayMB_ReadInputCoils[i]:=16#00;
END_FOR
iStep:=ciStepModbusWriteCoils;
END_IF
END_IF
ciStepModbusWriteCoils:
//Flags
MB_WriteCoils.bExecute:=TRUE;
ArrayMB_WriteCoils[0].0:=TRUE;
ArrayMB_WriteCoils[0].2:=TRUE;
ArrayMB_WriteCoils[0].4:=TRUE;
ArrayMB_WriteCoils[0].6:=TRUE;
ArrayMB_WriteCoils[0].8:=TRUE;
ArrayMB_WriteCoils[1]:=16#FFFF;
//Function Blocks
MB_WriteCoils(
nUnitID:=16#38
,nQuantity:=100
,nMBAddr:=0
,cbLength:=128
,pSrcAddr:=ADR(ArrayMB_WriteCoils)
,cbLength:=SIZEOF(ArrayMB_WriteCoils)
);
//Result
IF NOT MB_WriteCoils.bBusy THEN
IF MB_WriteCoils.bError THEN
iStep:=ciStepModbusError;
ELSE
iStep:=ciStepModbusConfigurationInit;
END_IF
END_IF
ciStepModbusError:
bError:=TRUE;
IF MB_WriteRegs.bError THEN
wErrorWord:=1;
wErrorID:=MB_WriteRegs.nErrId;
ELSIF MB_ReadInputRegs.bError THEN
wErrorWord:=2;
wErrorID:=MB_ReadInputRegs.nErrId;
ELSIF MB_ReadInputs.bError THEN
wErrorWord:=3;
wErrorID:=MB_ReadInputs.nErrId;
ELSIF MB_WriteCoils.bError THEN
wErrorWord:=4;
wErrorID:=MB_WriteCoils.nErrId;
END_IF;
IF bReset THEN
iStep:=ciStepModbusConfigurationInit;
END_IF;
END_CASE

Result

両方の与えを確認しデータ一致しているかをCheckしましょう。

Source Code

ここでPLCNEXTとTwinCATのProject filesを一式ダウンロードできます。

https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT%20ProjectModbusTCP_Client_withPLCNEXT.zip

Footer_Basic

Please Support some devices for my blog

Amazon Gift List

Find ME

Twitter:@3threes2
Email:soup01threes*gmail.com (* to @)
YoutubeChannel:https://www.youtube.com/channel/UCQ3CHGAIXZAbeOC_9mjQiWQ

シェアする

  • このエントリーをはてなブックマークに追加

フォローする