Siemens#SLMP Clientを立ち上げ三菱PLCと通信しよう

こちらの記事はS7-1500 から FX5 に対して SLMP 通信を行い、デバイスを読み書きするための通信FB作成し、TCON / TSEND / TRCV を使った TCP 通信をベースに、デバイス種別・開始アドレス・点数をパラメータ化し、Read / Write をほぼ同じ構造で扱えるようにしています。

「とりあえず動く」ではなく、デバッグしやすく、再利用しやすい構成を意識した実装例としてまとめました。

さ、FAを楽しもう!

前書き

いつも私の技術ブログとYouTubeチャンネルをご覧いただき、心より感謝申し上げます。また、いまFullさん(full@桜 八重 (@fulhause) / X)と共に毎週水曜日の夜にお届けしている「高橋クリス」ラジオ番組を運営しています。

技術は独り占めせず、届けるもの

私たちは工場の生産技術や制御に関する技術情報を、ブログや動画などで無料公開しています。「知識は誰でもアクセスできるべき」という信念のもと、現場で役立つ具体的なノウハウやトラブル事例などを発信してきました。すべて無料で続けているのは、「知らなかったせいで困る人」を少しでも減らしたいからです。

また、もしあなたの現場で…

  • 「このPLCとデバイスの組み合わせ、ちゃんと動くのかな?」
  • 「EtherCAT通信でうまくいかない部分を検証してほしい」
  • 「新しいリモートI/Oを試したいけど社内に検証環境がない」

など、困っている構成や試してみたいアイデアがあれば、ぜひお知らせください。機器の貸出や構成の共有が可能であれば、検証し、記事や動画で発信します(ご希望に応じて匿名対応も可能です)。

支援のかたち

現在、私達の活動はほぼ無償で続けており、記事や動画の制作には、時間と検証環境の整備が必要です。この活動を継続的にコンテンツを提供するためには、皆様の温かいご支援が大変重要です。

メンバーシップ(ラジオの応援)

Fullさんとのラジオをより充実させるための支援プランです。

https://note.com/fulhause/membership/join

Amazonギフトリスト

コンテンツ制作に必要な機材・書籍をリストにしています。

https://www.amazon.co.jp/hz/wishlist/ls/H7W3RRD7C5QG?ref_=wl_share

Patreon(ブログ・動画活動への応援)

月額での小さなご支援が、記事の執筆・検証環境の充実につながります。

https://www.patreon.com/user?u=84249391

Paypal

小さな支援が大きな力になります。

https://paypal.me/soup01threes?country.x=JP&locale.x=ja_JP

知ってたら助かること、届けたいだけです

あなたの応援が、知識の共有をもっと自由で持続可能なものにしてくれます。これからもどうぞよろしくお願いします。

soup01threes*gmail.com

https://x.com/3threes2

技術はひとりじゃもったいない。

Reference Link

Beckhoff#TwinCAT3 TF6310でSLMP Clientを立ち上げ三菱PLCと通信しよう

Function Block

今回は、記事で使用するTCON・TSEND・TRCV・TDISCONを説明します。

TCON_IP_v4: 接続パラメータの構造

TCON_IP_v4 構造 は、S7-1200 V4.0 以降および S7-1500 の CPU において、TCP および UDP 通信接続のパラメータを割り当てるために使用されます。

パラメータ

Byte

パラメータ名

データ型

説明

0..1

InterfaceID

HW_ANY

ローカル側通信インターフェースの識別子(Ethernet など)。範囲:0~65535

2..3

ID

CONN_OUC

通信接続の識別番号。TSEND_C / TRCV_C / TCON で同じ ID を指定する。範囲:1~4095

4

ConnectionType

BYTE

通信方式の指定。11:TCP(0x0B)17:TCP(0x11)19:UDP(0x13)

5

ActiveEstablished

BOOL

接続開始方式。FALSE:受動接続(待ち)TRUE:能動接続(自分から接続)

6..9

RemoteAddress

ARRAY[1..4] OF BYTE

接続先の IP アドレス。例:192.168.0.1 → {192,168,0,1}

10..11

RemotePort

UINT

接続先(相手側)のポート番号。

12..13

LocalPort

UINT

自分側で使用するポート番号。

TCON: Establish communication connection

「TCON」命令を使用して通信接続を設定および確立します。接続が設定・確立されると、CPUによって自動的に維持および監視されます。「TCON」は非同期で実行されます。

ファームウェアバージョンV2.0以降のS7-1500-CPUでは、統合PROFINETインターフェースを介して「TCON」命令によりIPv4マルチキャスト通信用のアクセスポイントを設定できます。

CONNECT および ID パラメータに指定された接続データは、通信接続の確立に使用されます。CONNECT パラメータでは、可能な限り、プログラムエディタのインスペクタウィンドウにおける接続パラメータ割り当てで作成された事前定義構造体のみを使用してください。

接続を確立するには、REQパラメータで立ち上がりエッジを検出する必要があります。接続が正常に確立されると、DONEパラメータは「1」に設定されます。

接続プログラミング時のサポート

プログラムブロック内で通信用TCON、TSEND_CまたはTRCV_C命令を選択し、TCP、UDP、ISO-on-TCPまたはFDLタイプの接続にパラメータを作成して割り当てたい場合、接続パラメータ割り当てのサポートを利用できます。

TCPおよびISO-on-TCPとの接続

通信の両パートナーは「TCON」命令を呼び出して通信接続を設定・確立します。パラメータ割り当て時に、どちらのパートナーが能動的な通信エンドポイントで、どちらが受動的な通信エンドポイントかを指定します。

接続が中断した場合(例:回線切断や遠隔通信パートナー側の原因による)、アクティブなパートナーは設定済みの接続を再確立しようと試みます。この際、「TCON」を再度呼び出す必要はありません。ただし、これは「TCON」が一度正常に実行された場合(DONE = 1)にのみ適用されます。

「TDISCON」命令が実行された場合、またはCPUがSTOPモードに移行した場合、既存の接続は終了し、接続設定は解除されます。接続を再設定して確立するには、再度「TCON」を実行する必要があります。

パラメータ

パラメータ

区分

データ型

メモリ領域

説明

REQ

Input

BOOL

I, Q, M, D, L または定数

立ち上がりエッジで、指定された接続を確立するジョブを開始します。

ID

Input

CONN_OUC

I, Q, M, D, L または定数

割り当てられた通信接続の参照ID。有効範囲:W#16#0001 ~ W#16#0FFF※ TCONSettings で指定された ID はこの範囲外になる場合があります。

CONNECT

InOut

VARIANT

D

接続記述子へのポインタ。TCP/UDP 通信では TCON_IP_v4 や TCON_QDN 構造体を使用します。

DONE

Output

BOOL

I, Q, M, D, L

接続確立結果。0:未開始または処理中1:エラーなく正常終了

BUSY

Output

BOOL

I, Q, M, D, L

処理状態。0:未開始または完了済み1:処理中(新しいジョブは開始不可)

ERROR

Output

BOOL

I, Q, M, D, L

エラー状態。0:エラーなし1:エラー発生

STATUS

Output

WORD

I, Q, M, D, L

命令の詳細ステータスコード。

BUSY、DONE、ERROR パラメータ

FBは、BUSY、DONE、ERROR、STATUSパラメータで確認できます。

  • BUSYパラメータは処理状態を示します。
  • DONEパラメータはジョブが正常に実行されたかどうかを確認するために使用します。
  • RRORパラメータは「TCON」の実行中にエラーが発生した場合に設定されます。

エラー情報はSTATUSパラメータに出力されます。

「TCON」命令は、リモートパートナーへのアクティブな接続確立が失敗した場合、バージョン3.0でエラーメッセージを生成します。REQパラメータで立ち上がりエッジを生成し、接続を再確立してください。

BUSY、DONE、およびERRORパラメータ間の関係

以下の表は、BUSY、DONE、およびERRORパラメータ間の関係を示しています:

BUSY

DONE

ERROR

状態説明

1

0

0

ジョブ処理中。現在、命令が実行されています。

0

1

0

ジョブは正常に完了しました。

0

0

1

ジョブはエラーで終了しました。エラーの原因は STATUS パラメータに出力されます。

0

0

0

新しいジョブは割り当てられていません。

TSEND: Send data via communication connection

既存の通信接続を介してデータを送信するには、「TSEND」命令を使用します。TSENDは非同期で実行されます。

送信領域はDATAパラメータで指定します。これには送信するデータのアドレスと長さが含まれます。送信データにはBOOL型およびBOOL配列を除くすべてのデータ型を使用できます。

REQパラメータで立ち上がりエッジが検出されると、送信ジョブが実行される。

LEN パラメータを使用すると、送信ジョブで送信される最大バイト数を指定します。

TCP(ストリームプロトコル)でデータが転送される場合、「TSEND」命令は「TRCV」に送信されたデータの長さに関する情報を提供しない。

送信ジョブが完了するまで、送信データは編集してはならない。送信ジョブが正常に実行された場合、DONEパラメータは「1」に設定される。DONEパラメータの信号状態「1」は、送信データが通信相手によって既に読み取られたことの確認ではない。

有効のインタフェース

イーサネット:ファームウェアバージョン V4.0 以上の CPU S7-1200 および CPU S7-1500

PROFIBUS:システムデータタイプ TCON_FDL を使用する、バージョン V2.0 以降の CM 1542-5 を備えた S7-1500 の FDL 接続

パラメータ

パラメータ

区分

データ型

メモリ領域

説明

REQ

Input

BOOL

I, Q, M, D, L または定数

立ち上がりエッジで送信ジョブを開始します。

ID

Input

CONN_OUC

I, Q, M, D, L または定数

TCON で確立された通信接続の参照ID。有効範囲:W#16#0001 ~ W#16#0FFF

LEN

Input

UDINT

I, Q, M, D, L または定数

送信する最大バイト数。S7-1200:最大 8192 byteS7-1500:最大 65536 byte※ FDL 接続(CM1542-5)の場合は最大 240 byte

DATA

InOut

VARIANT

I, Q, M, D, L

送信データ領域へのポインタ。I/Oイメージ、ビットメモリ、DB、ローカルデータなどを指定可能。※ 構造体送信時は、送信側と受信側で同一構造である必要があります。

DONE

Output

BOOL

I, Q, M, D, L

送信結果。0:未開始または処理中1:エラーなく正常終了

BUSY

Output

BOOL

I, Q, M, D, L

処理状態。0:未開始または完了済み1:処理中(新しい送信は開始不可)

ERROR

Output

BOOL

I, Q, M, D, L

エラー状態。0:エラーなし1:エラー発生

STATUS

Output

WORD

I, Q, M, D, L

命令の詳細ステータスコード。

EN パラメータと DATA パラメータ

LEN = 0 の場合、DATA パラメータで指定された全データが送信されます。

LEN パラメータのバイト数が、DATA パラメータで定義された送信データの長さより大きい場合、STATUS パラメータにエラーコード 8088 が出力されます(STATUS パラメータの説明は後述)。

構造体(Struct)がDATAパラメータ経由で参照される場合、LENは構造体よりも短く設定できます。この場合、LENパラメータで指定された長さまでのデータのみが転送されます。

データ型STRINGおよびWSTRINGでは、パラメータLEN = 0の場合、全データが転送されます。LEN > 0の場合、長さは最大バイト数に、長さ情報を含む追加の2バイトを加えた値以上でなければなりません。

送信可能な最大バイト数はデバイスによって異なります。

最適化されたDBから構造化タグを使用する場合、構造化タグのアドレスはDATAパラメータに接続され、パラメータLEN = 0と指定する必要があります。

BUSY、DONE、ERROR パラメータ

ジョブのステータスは、BUSY、DONE、ERROR、STATUSの各パラメータで確認できます。

  • BUSYパラメータは処理中かどうかを示します。
  • DONEパラメータでは、ジョブが正常に実行されたかどうかを確認できます。
  • ERRORパラメータは「TSEND」の実行中にエラーが発生した場合に設定されます。

エラー情報はSTATUSパラメータに出力されます。

以下の表は、BUSY、DONE、およびERRORパラメータ間の関係を示しています:

BUSY

DONE

ERROR

状態説明

1

0

0

ジョブを処理中です。

0

1

0

ジョブは正常に完了しました。

0

0

1

ジョブはエラーで終了しました。エラーの原因は STATUS パラメータに出力されます。

0

0

0

新しいジョブは割り当てられていません(待機状態)。

TRCV: 通信接続を介してデータを受信する

既存の通信接続を介してデータを受信するには「TRCV」命令を使用します。「TRCV」は非同期で実行されます。

EN_R パラメータが値「1」に設定されている場合、データ受信が有効になります。受信データは受信領域に入力されます。受信領域の長さは、使用しているプロトコルバリアントに応じて、LEN パラメータ(LEN ≠ 0 の場合)または DATA パラメータの長さ情報(LEN = 0 の場合)で指定します。

データ受信中は、受信データの整合性を確保するため、DATAパラメータまたは定義済み受信領域を変更することはできません。

データの受信に成功した後、NDRパラメータは値「1」に設定されます。実際に受信したデータ量は、RCVD_LENパラメータで照会できます。

有効のインタフェース

イーサネット:ファームウェアバージョン V4.0 以上の CPU S7-1200 および CPU S7-1500

PROFIBUS:システムデータタイプ TCON_FDL を使用する、バージョン V2.0 以降の CM 1542-5 を備えた S7-1500 の FDL 接続

TRCVの受信モード

以下の表は、TRCV設定できる受信モードを示しています。注意するのは今回の記事ではAd-hocモードを使用します。

プロトコル種別

ADHOC パラメータ

受信エリアでのデータ有効条件

connection_type の値

LEN パラメータの扱い

TCP(Ad-hoc モード)

1(有効)

NDR が立つと、最低 1 バイト以上のデータが到着していれば即利用可能

16#11(10進:17)

受信済みのデータ量だけ読み込む。LEN で指定した最大長は超えない。LEN = 0(推奨):DATA で指定した受信エリア全体を使用

TCP(固定長受信)

0(無効)

LEN で指定した データ長をすべて受信後に利用可能

16#11(10進:17)

S7-1200:1 ~ 8192S7-1500:1 ~ 65536

ISO on TCP

LEN で指定した データ長をすべて受信後に利用可能

16#12(10進:18)

S7-1200:1 ~ 8192S7-1500:1 ~ 65536

FDL

LEN で指定した データ長をすべて受信後に利用可能

16#15(10進:21)

1 ~ 240

TCP (Ad-hoc mode)

TCP (Ad-hoc mode)はTCPプロトコルでのみ利用可能です。アドホックモードは「TRCV」命令を用いて動的長さのデータを受信するために使用します。

ADHOCパラメータに値「1」を割り当てることでアドホックモードを設定します。

データブロックを受信すると直ちに、「TRCV」命令が受信領域へ転送し、NDRを設定します。RCVD_LENには、このデータブロックに含まれるデータバイト数の情報が格納されます。RCVD_LENの最小値は1です。

TCP (Ad-hoc mode)使用時、標準アクセス方式のデータブロックには全てのデータ型を使用できます。

ただし、アクセスが最適化されたデータブロックの場合、許可されるデータ型はCPUの種類によって異なります:

  • S7-1200 CPUでは、最適化されたアクセスが可能なデータブロックには、BYTE配列または8ビット長データ型(CHAR、USINT、SINTなど)のみを使用できます。
  • S7-1500 CPUでは、すべてのデータ型を使用できます。

パラメータ

パラメータ

区分

データ型

メモリ領域

説明

EN_R

Input

BOOL

I, Q, M, D, L または定数

受信許可。TRUE の間、データ受信を有効にします。

ID

Input

CONN_OUC

I, Q, M, D, L または定数

TCON で確立された通信接続の参照ID。有効範囲:W#16#0001 ~ W#16#0FFF

LEN

Input

UDINT

I, Q, M, D, L または定数

受信エリアの長さ(バイト)。S7-1200:最大 8192S7-1500:最大 65536CM1542-5(FDL):最大 240※ DATA に最適化アクセスを使用する場合は LEN = 0 にする必要があります。

ADHOC

Input

BOOL

I, Q, M, D, L または定数

TCP 通信での Ad-hoc モード使用指定。TCP 以外では FALSE に設定します。

DATA

InOut

VARIANT

I, Q, M, D, L

受信データ格納エリアへのポインタ。構造体受信時は、送信側と受信側で同一構造である必要があります。

NDR

Output

BOOL

I, Q, M, D, L

新規データ受信通知。0:未受信/処理中1:新しいデータを受信

BUSY

Output

BOOL

I, Q, M, D, L

処理状態。0:未開始または完了済み1:受信処理中(新しい受信は開始不可)

ERROR

Output

BOOL

I, Q, M, D, L

エラー状態。0:エラーなし1:エラー発生

STATUS

Output

WORD

I, Q, M, D, L

命令の詳細ステータスコード。

RCVD_LEN

Output

UDINT

I, Q, M, D, L

実際に受信したデータ量(バイト数)。

LEN、DATA、およびRCVD_LENパラメータ

LEN = 0 の場合、受信データは DATA パラメータで指定された受信領域に保存されます。受信されたバイト数は RCVD_LEN パラメータで示されます。また、受信するデータの長さはDATAパラメータによって定義されます。受信領域(DATAパラメータ)は、TSENDによって送信されるデータと同じサイズに設定することを推奨します。

LENの値が0であり、送信データがDATA受信領域より小さいセグメントで転送された場合、以下の事項が適用されます。

  • 関連するTSEND命令が全データを送信するまでEN_Rを設定したままにしておくことを推奨します。
  • TSENDによって送信されたデータのサイズがDATA受信領域のサイズと等しくならない限り、STATUSは値7002を表示します。
  • 受信データ量がDATA受信領域のサイズと等しくなるまでEN_Rを設定しておく必要があります。
  • EN_Rをパルス入力する場合は、BUSY=0またはERROR <> 0となるまで継続する必要があります。

DATA受信領域のデータは、BUSYが0の値の場合にのみ有効である。

LENパラメータで指定された長さがDATAパラメータで受信したデータの長さより大きい場合、STATUSパラメータでエラーコード8088が出力される。

構造体(Struct)がDATAパラメータ経由で参照される場合、LENは構造体よりも短く設定できます。この場合、LENパラメータで指定された長さまでのデータのみが転送されます。

DATAパラメータが最適化されたアクセスを持つデータブロックを参照する場合、LENパラメータは「0」に設定する必要があります。基本データ型のデータ長が一致しない場合、データは受信されず、STATUSパラメータにエラーコード8088が出力されます。

STRINGデータ型がDATAパラメータ経由で参照される場合、LENパラメータで指定される長さは0または2以上でなければならない(LEN = 1は許可されない)。

WSTRINGデータ型がDATAパラメータ経由で参照される場合、LENパラメータで指定される長さは0または5以上でなければならない。

BUSY、NDR、およびERRORパラメータ

ジョブのステータスは、BUSY、NDR、ERROR、STATUSパラメータで確認できます。BUSYパラメータは処理状態を示します。NDRパラメータでは、ジョブが正常に実行されたかどうかを確認できます。ERRORパラメータは、TRCVの実行中にエラーが発生した場合に設定されます。エラー情報はSTATUSパラメータに出力されます。

以下の表は、BUSY、NDR、およびERRORパラメータ間の関係を示しています:

BUSY

NDR

ERROR

状態説明

1

受信ジョブを処理中です。

0

1

0

受信ジョブは正常に完了し、新しいデータを受信しました。

0

0

1

受信ジョブはエラーで終了しました。エラーの原因は STATUS パラメータに出力されます。

0

0

0

新しい受信ジョブは割り当てられていません(待機状態)。

TDISCON: 通信接続を終了する

TDISCON命令は、CPUから接続相手への通信接続を終了します。TDISCONは非同期で動作する命令であり、そのジョブ処理は複数の呼び出しにまたがって実行されます。接続を切断するジョブを開始するには、REQ = 1 でTDISCON命令を呼び出します。

TDISCONが正常に実行された後、TCONに指定されたIDは有効ではなくなり、送受信に使用できなくなります。

ジョブの状態は出力パラメータBUSYとSTATUSによって示されます。ここでSTATUSは非同期命令の出力パラメータRET_VALに対応します。

パラメータ

パラメータ

区分

データ型

メモリ領域(S7-1200)

メモリ領域(S7-1500)

説明

REQ

Input

BOOL

I, Q, M, D, L または定数

I, Q, M, D, L または定数

立ち上がりエッジで、指定した通信接続の切断ジョブを開始します。

ID

Input

CONN_OUC (WORD)

D, L または定数

I, Q, M, D, L または定数

切断対象となる通信接続の ID。有効範囲:W#16#0001 ~ W#16#0FFF※ TCONSettings で割り当てられた ID はこの範囲外になる場合があります。

DONE

Output

BOOL

I, Q, M, D, L

I, Q, M, D, L

切断処理結果。0:未開始または処理中1:エラーなく正常終了

BUSY

Output

BOOL

I, Q, M, D, L

I, Q, M, D, L

処理状態。1:切断処理中0:未開始または完了済み

ERROR

Output

BOOL

I, Q, M, D, L

I, Q, M, D, L

エラー状態。0:エラーなし1:処理中にエラー発生

STATUS

Output

WORD

I, Q, M, D, L

I, Q, M, D, L

エラーや処理結果の詳細ステータス。

BUSY、DONE、ERROR パラメータ

以下の表は、BUSY、DONE、およびERRORの関係を示しています。この表を使用することで、TDISCONの現在の状態や、接続の確立が完了したタイミングを認識できます。

BUSY

DONE

ERROR

状態説明

1

0

0

ジョブを処理中です。

0

1

0

ジョブは正常に完了しました。

0

0

1

ジョブはエラーで終了しました。エラーの原因は STATUS パラメータで確認できます。

0

0

0

命令には(新しい)ジョブが割り当てられていません。

Implementation

三菱FX5側

最初に三菱 FX5側の通信設定をします。

SLMP接続設定

今回の記事ではPORT 7501と7502を使用します。

シーメンス側

次はシーメンス側で通信プログラムを作ります。

IP設定

S71500のIPアドレスをFX5と同じネットワークに設定しましょう。

プログラミング

次はプログラムの部分です。

構造体

udtSLMPDeviceCode

udtSLMPDeviceCode は、SLMP 通信でアクセスするデバイス種別(D / M / X / Y / W)を

BOOL フラグで指定するためのデータ型です。つまり、「今どのデバイス領域を使いたいか」をプログラムから分かりやすく指定するための構造体という位置づけです。

SLMP で扱うデバイス領域を人間に分かる形で指定するためのスイッチ箱です。

FC

fc32BitDataTo4Bytes

こちらは32bit の数値を、下位から 1 バイトずつ取り出すFCです。

#b0 := #in.%B0;
#b1 := #in.%B1;
#b2 := #in.%B2;
#b3 := #in.%B3;

fcSLMPGetDeviceCode

それはBOOL で指定されたデバイス種別(D/M/X/Y/W)を、SLMP 仕様の「デバイスコード(BYTE)」に変換するためのFCです。

#fcSLMPGetDeviceCode := 16#0;

IF #udtSLMPDeviceCode.D THEN
#fcSLMPGetDeviceCode := #eDeviceCodeD;
ELSIF #udtSLMPDeviceCode.M THEN
#fcSLMPGetDeviceCode := #eDeviceCodeM;
ELSIF #udtSLMPDeviceCode.W THEN
#fcSLMPGetDeviceCode := #eDeviceCodeW;
ELSIF #udtSLMPDeviceCode.X THEN
#fcSLMPGetDeviceCode := #eDeviceCodeX;
ELSIF #udtSLMPDeviceCode.Y THEN
#fcSLMPGetDeviceCode := #eDeviceCodeY;
END_IF;

FB

次はSLMP読み書きのプログラムを作成します。

fbSLMPRead

これはMP 3E フレームを用いて、指定デバイス(D/M/X/Y/W)を指定点数分「周期的に読み出す」通信 FBです。

  • TCP 接続
  • フレーム生成
  • TSEND / TRCV を明示制御
  • エラー・タイムアウト・停止操作
入力インタフェース

変数

意味

xStart

通信開始トリガ

xStop

通信停止要求

xReset / xAutoReset

異常復帰制御

CONN_OUC

TCON 接続 ID

uiFirstDeviceOffset

読み出し開始デバイス番号

uiTotalDevices

読み出し点数

udtSLMPDeviceCode

D/M/X/Y/W 指定(BOOL)

出力インタフェース

変数

意味

xBusy

通信中フラグ

xError

異常発生

uiDoneCount

正常完了回数

uiErrorCount

エラー回数

iErrorID

エラー原因コード

iCurrentStep

現在の内部ステップ

入出力インタフェース

変数名

データ型

方向

内容 / 役割

udtTCON_IP_v4

TCON_IP_v4

InOut

TCP 通信の接続定義構造体。IPアドレス、ポート番号、Active/Passive 設定などを保持し、TCON FB にそのまま渡される。FB 内では編集せず、呼び出し元から注入される通信条件を使用する。

arrb

ARRAY[0..ebufferLength-1] OF BYTE

InOut

SLMP Read 応答から取得した 実データ格納バッファ。応答フレームのヘッダ(11 byte)を除いたデータ部のみがコピーされる。呼び出し元でそのまま解析・変換・表示に使用可能。

プログラム

こちらは各STEPの構造です。

Step

内容

0

初期化・待機

5

パラメータ妥当性チェック

10–15

TCON 接続

20

SLMP Read フレーム生成

30–33

TSEND 実行+送信監視

35–40

TRCV 実行+受信待ち

50

応答チェック

60

受信データ格納

70

次周期へ

100–105

切断処理

9000番台

異常系

#fbR_TRIG[0](CLK := #xStart);
#fbR_TRIG[1](CLK := #xStop AND #iStep > 0);
IF #fbR_TRIG[1].Q THEN
#xCycleStop := True;
END_IF;

CASE #iStep OF
0:
#xTCONReq := FALSE;
#xTSENDReq := False;
#xTRCVReq := false;
#xTDISReq := False;
#xCycleStop := False;
#xError := FALSE;
#xBusy := false;
#arrfbTONs[0](IN := False,
PT := T#0.1s);
#arrfbTONs[1](IN := False,
PT := T#0.1s);
IF NOT #fbTCON.ERROR AND NOT #fbTCON.BUSY
AND NOT #fbTDISCON.ERROR AND NOT #fbTDISCON.BUSY
AND NOT #fbTSEND.ERROR AND NOT #fbTSEND.BUSY
AND NOT #fbTCON.ERROR AND NOT #fbTCON.BUSY
AND #fbR_TRIG[0].Q
THEN
FOR #_uiLoop := 0 TO #ebufferLength DO
#arrbSendBuffer[#_uiLoop] := 0;
#arrbReceBuffer[#_uiLoop] := 0;
END_FOR;
#xBusy := True;
#iStep := 5;
END_IF;
5:
IF NOT #udtSLMPDeviceCode.D
AND NOT #udtSLMPDeviceCode.M
AND NOT #udtSLMPDeviceCode.W
AND NOT #udtSLMPDeviceCode.X
AND NOT #udtSLMPDeviceCode.Y
THEN
#iStep := 9005;
#iErrorID := 9005;
ELSIF #uiTotalDevices <= 0 THEN
#iStep := 9005;
#iErrorID := 9005;
ELSE
#iStep := 10;
END_IF;
10:
#arrfbTONs[0](IN := False,
PT := T#0.1s);
#xTCONReq := True;
IF #fbTCON.BUSY THEN
#iStep := 15;
END_IF;
15:
IF #fbTCON.STATUS=0 THEN
#iStep := 20;
END_IF;
20:
//50 00 00 ff ff 03 00 0c 00 10 00 01 04 00 00 64 00 00 a8 0a 00
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#arrbSendBuffer[0] := #e3EFrame;
//
#arrbSendBuffer[1] := 16#0;
//Network Number
#arrbSendBuffer[2] := 16#0;
//SubNetwork Number
#arrbSendBuffer[3] := 16#FF;
//Requested unit I/O number
#arrbSendBuffer[4] := 16#FF;
#arrbSendBuffer[5] := 16#03;
//Requesting Unit Station Number
#arrbSendBuffer[6] := 16#00;
//Requested data length Bytes
#arrbSendBuffer[7] := 16#0C;
#arrbSendBuffer[8] := 16#00;
//Monitor Timer
#arrbSendBuffer[9] := 16#10;
#arrbSendBuffer[10] := 16#00;
//Command
#arrbSendBuffer[11] := 16#01;
#arrbSendBuffer[12] := 16#04;
//SubCommand
#arrbSendBuffer[13] := 16#00;
#arrbSendBuffer[14] := 16#00;

// #arrbSendBuffer[15] := 16#64;
// #arrbSendBuffer[16] := 16#00;
// #arrbSendBuffer[17] := 16#00;
// #arrbSendBuffer[18] := 16#A8;
// #arrbSendBuffer[19] := 16#0A;
// #arrbSendBuffer[20] := 16#00;

//first number of Register
“fc32BitDataTo4Bytes”(
in := #uiFirstDeviceOffset,
b0 => #arrbSendBuffer[15],
b1 => #arrbSendBuffer[16],
b2 => #arrbSendBuffer[17],
b3 => #_arrbTempArray[0]
);
//device code
#arrbSendBuffer[18] := “fcSLMPGetDeviceCode”(udtSLMPDeviceCode := #udtSLMPDeviceCode);
//numbers
“fc32BitDataTo4Bytes”(
in := #uiTotalDevices,
b0 => #arrbSendBuffer[19],
b1 => #arrbSendBuffer[20],
b2 => #_arrbTempArray[0],
b3 => #_arrbTempArray[1]
);
#iStep := 30;
30:
#fbTSEND.LEN := 21;
#xTSENDReq := True;
IF #fbTSEND.BUSY THEN
#iStep := 33;
END_IF;
33:
#arrfbTONs[0](
IN := True,
PT := T#3s
);
IF #fbTSEND.STATUS=0 THEN
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#iStep := 35;
END_IF;
IF #arrfbTONs[0].Q THEN
#iStep := 9033;
#iErrorID := 9033;
END_IF;
35:
#xTSENDReq := False;
#xTRCVReq := True;
#arrfbTONs[0](
IN := True,
PT := T#1s
);
IF #fbTRCV.RCVD_LEN > 0 THEN
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#iStep := 40;
END_IF;
IF #arrfbTONs[0].Q THEN
#iStep := 9035;
#iErrorID := 9035;
END_IF;
40:
#xTRCVReq := false;
IF NOT #fbTRCV.BUSY THEN
#iStep := 50;
END_IF;
50:
//IF arrbReceBuffer[0]
IF #arrbReceBuffer[0] <> 16#D0 AND #arrbReceBuffer[9] <> 16#00 AND #arrbReceBuffer[10] <> 16#00 THEN
#iStep := 9050;
#iErrorID := 9050;
ELSE
#iStep := 60;
END_IF;

60:
FOR #_uiLoop := 0 TO #ebufferLength-11 DO
#arrb[#_uiLoop] := #arrbReceBuffer[#_uiLoop +11];
END_FOR;

#uiDoneCount := #uiDoneCount + 1;
IF NOT #xCycleStop THEN

#iStep := 70;

ELSE
#iStep := 100;
END_IF;
70:
#iStep := 20;
100:
#xTDISReq := True;
IF #fbTDISCON.BUSY THEN
#iStep := 105;
END_IF;
105:
IF #xCycleStop THEN
#iStep := 0;
ELSE
IF #xAutoReset THEN
#iStep := 0;
ELSE
IF #xReset THEN
#iStep := 0;
END_IF;
END_IF;
END_IF;

9005:
#iStep := 0;
9010..9999:
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#xError := True;
#iStep := 100;

END_CASE;

#fbR_TRIG[2](CLK := #xError);
IF #fbR_TRIG[2].Q THEN
#uiErrorCount := #uiErrorCount + 1;
END_IF;

#iCurrentStep := #iStep;


#fbTCON(
REQ:=#xTCONReq,
ID:=#CONN_OUC,
CONNECT:=#udtTCON_IP_v4
);

#fbTDISCON(
ID := #CONN_OUC,
REQ := #xTDISReq
);

#fbTSEND(
ID := #CONN_OUC,
REQ := #xTSENDReq,
DATA := #arrbSendBuffer
);

#fbTRCV(
ID := #CONN_OUC,
EN_R := #xTRCVReq,
LEN:=0,
ADHOC:=True,
DATA := #arrbReceBuffer

);

fbSLMPWrite

これはMP 3E フレームを用いて、指定デバイス(D/M/X/Y/W)を指定点数分「周期的に書き込み」通信 FBです。

  • TCP 接続
  • フレーム生成
  • TSEND / TRCV を明示制御
  • エラー・タイムアウト・停止操作
入力インタフェース

変数名

内容

xStart

BOOL

書き込み開始トリガ(立上り)

xStop

BOOL

書き込み停止要求

xReset

BOOL

エラー後の手動リセット

xAutoReset

BOOL

エラー後の自動リセット

CONN_OUC

CONN_OUC

TCON 接続 ID

uiFirstDeviceOffset

UINT

書き込み開始デバイス番号

uiTotalDevices

UINT

書き込み点数

udtSLMPDeviceCode

UDT

D/M/X/Y/W のいずれかを指定

出力インタフェース

変数名

内容

xBusy

BOOL

書き込み処理中

xError

BOOL

エラー発生

uiDoneCount

UINT

正常完了回数

uiErrorCount

UINT

エラー回数

iErrorID

INT

エラー識別コード

iCurrentStep

INT

現在の内部ステップ

入出力インタフェース

変数名

役割

udtTCON_IP_v4

TCON_IP_v4

TCP 接続定義(IP・Port・Active 等)

arrb

ARRAY[ ] OF BYTE

書き込みデータ本体(ユーザが用意する)

プログラム

こちらは各STEPの構造です。

Step

処理内容

0

初期化・待機

5

デバイス指定・点数チェック

10–15

TCON 接続

20

SLMP WRITE フレーム生成

30–33

TSEND 実行・送信監視

35–40

TRCV 応答待ち

50

応答フレーム検証

60

正常完了カウント更新

70

周期書き込み継続

100–105

切断処理

9000番台

エラー処理

#fbR_TRIG[0](CLK := #xStart);
#fbR_TRIG[1](CLK := #xStop AND #iStep > 0);
IF #fbR_TRIG[1].Q THEN
#xCycleStop := True;
END_IF;

CASE #iStep OF
0:
#xTCONReq := FALSE;
#xTSENDReq := False;
#xTRCVReq := false;
#xTDISReq := False;
#xCycleStop := False;
#xError := FALSE;
#xBusy := false;
#arrfbTONs[0](IN := False,
PT := T#0.1s);
#arrfbTONs[1](IN := False,
PT := T#0.1s);
IF NOT #fbTCON.ERROR AND NOT #fbTCON.BUSY
AND NOT #fbTDISCON.ERROR AND NOT #fbTDISCON.BUSY
AND NOT #fbTSEND.ERROR AND NOT #fbTSEND.BUSY
AND NOT #fbTCON.ERROR AND NOT #fbTCON.BUSY
AND #fbR_TRIG[0].Q
THEN
FOR #_uiLoop := 0 TO #ebufferLength DO
#arrbSendBuffer[#_uiLoop] := 0;
#arrbReceBuffer[#_uiLoop] := 0;
END_FOR;
#xBusy := True;
#iStep := 5;
END_IF;
5:
IF NOT #udtSLMPDeviceCode.D
AND NOT #udtSLMPDeviceCode.M
AND NOT #udtSLMPDeviceCode.W
AND NOT #udtSLMPDeviceCode.X
AND NOT #udtSLMPDeviceCode.Y
THEN
#iStep := 9005;
#iErrorID := 9005;
ELSIF #uiTotalDevices <= 0 THEN
#iStep := 9005;
#iErrorID := 9005;
ELSE
#iStep := 10;
END_IF;
10:
#arrfbTONs[0](IN := False,
PT := T#0.1s);
#xTCONReq := True;
IF #fbTCON.BUSY THEN
#iStep := 15;
END_IF;
15:
IF #fbTCON.STATUS = 0 THEN
#iStep := 20;
END_IF;
20:
//50 00 00 ff ff 03 00 0c 00 10 00 01 04 00 00 64 00 00 a8 0a 00
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#arrbSendBuffer[0] := #e3EFrame;
//
#arrbSendBuffer[1] := 16#0;
//Network Number
#arrbSendBuffer[2] := 16#0;
//SubNetwork Number
#arrbSendBuffer[3] := 16#FF;
//Requested unit I/O number
#arrbSendBuffer[4] := 16#FF;
#arrbSendBuffer[5] := 16#03;
//Requesting Unit Station Number
#arrbSendBuffer[6] := 16#00;
//Requested data length Bytes
#_uiSendDataTotalLength := #uiTotalDevices * 2 + 12;
“fc32BitDataTo4Bytes”(
in := #_uiSendDataTotalLength,
b0 => #_arrbTempArray[0],
b1 => #_arrbTempArray[1],
b2 => #_arrbTempArray[2],
b3 => #_arrbTempArray[3]
);
#arrbSendBuffer[7] := #_arrbTempArray[0];
#arrbSendBuffer[8] := #_arrbTempArray[1];
//Monitor Timer
#arrbSendBuffer[9] := 16#10;
#arrbSendBuffer[10] := 16#00;
//Command
#arrbSendBuffer[11] := 16#01;
#arrbSendBuffer[12] := 16#14;
//SubCommand
#arrbSendBuffer[13] := 16#00;
#arrbSendBuffer[14] := 16#00;

//first number of Register
“fc32BitDataTo4Bytes”(
in := #uiFirstDeviceOffset,
b0 => #arrbSendBuffer[15],
b1 => #arrbSendBuffer[16],
b2 => #arrbSendBuffer[17],
b3 => #_arrbTempArray[0]
);
//device code
#arrbSendBuffer[18] := “fcSLMPGetDeviceCode”(udtSLMPDeviceCode := #udtSLMPDeviceCode);
//numbers
“fc32BitDataTo4Bytes”(
in := #uiTotalDevices,
b0 => #arrbSendBuffer[19],
b1 => #arrbSendBuffer[20],
b2 => #_arrbTempArray[0],
b3 => #_arrbTempArray[1]
);

FOR #_uiLoop := 0 TO #ebufferLength-21 DO
#arrbSendBuffer[21 + #_uiLoop] := #arrb[#_uiLoop];
END_FOR;
#iStep := 30;
;
30:
#fbTSEND.LEN := 21 + #uiTotalDevices * 2;;
#xTSENDReq := True;
IF #fbTSEND.BUSY THEN
#iStep := 33;
END_IF;
33:
#arrfbTONs[0](
IN := True,
PT := T#3s
);
IF #fbTSEND.STATUS = 0 THEN
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#iStep := 35;
END_IF;
IF #arrfbTONs[0].Q THEN
#iStep := 9033;
#iErrorID := 9033;
END_IF;
35:
#xTSENDReq := False;
#xTRCVReq := True;
#arrfbTONs[0](
IN := True,
PT := T#1s
);
IF #fbTRCV.RCVD_LEN > 0 THEN
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#iStep := 40;
END_IF;
IF #arrfbTONs[0].Q THEN
#iStep := 9035;
#iErrorID := 9035;
END_IF;
40:
#xTRCVReq := false;
IF NOT #fbTRCV.BUSY THEN
#iStep := 50;
END_IF;
50:
//IF arrbReceBuffer[0]
IF #arrbReceBuffer[0] <> 16#D0 AND #arrbReceBuffer[9] <> 16#00 AND #arrbReceBuffer[10] <> 16#00 THEN
#iStep := 9050;
#iErrorID := 9050;
ELSE
#iStep := 60;
END_IF;

60:
//#arrb := #arrbReceBuffer;
#uiDoneCount := #uiDoneCount + 1;
IF NOT #xCycleStop THEN

#iStep := 70;

ELSE
#iStep := 100;
END_IF;
70:
#iStep := 20;
100:
#xTDISReq := True;
IF #fbTDISCON.BUSY THEN
#iStep := 105;
END_IF;
105:
IF #xCycleStop THEN
#iStep := 0;
ELSE
IF #xAutoReset THEN
#iStep := 0;
ELSE
IF #xReset THEN
#iStep := 0;
END_IF;
END_IF;
END_IF;
9005:
#iStep := 0;
9010..9999:
#arrfbTONs[0](
IN := False,
PT := T#3s
);
#xError := True;
#iStep := 100;

END_CASE;

#fbR_TRIG[2](CLK := #xError);
IF #fbR_TRIG[2].Q THEN
#uiErrorCount := #uiErrorCount + 1;
END_IF;

#iCurrentStep := #iStep;


#fbTCON(
REQ := #xTCONReq,
ID := #CONN_OUC,
CONNECT := #udtTCON_IP_v4
);

#fbTDISCON(
ID := #CONN_OUC,
REQ := #xTDISReq
);

#fbTSEND(
ID := #CONN_OUC,
REQ := #xTSENDReq,
DATA := #arrbSendBuffer
);

#fbTRCV(
ID := #CONN_OUC,
EN_R := #xTRCVReq,
LEN := 0,
ADHOC := True,
DATA := #arrbReceBuffer

);

fbCom2FX5

次はfbSLMPReadとfbSLMPWriteを呼び出すプログラムを作成します。

ネットワーク2

このネットワークでは、TCP 通信で接続する相手機器の IP アドレスを設定しています。TCON_IP_v4 構造体の RemoteAddress.ADDR[1]~[4] にそれぞれ 192・168・0・95 を代入することで、接続先 IP を 192.168.0.95 に指定しています。

ネットワーク3

このネットワークでは、TCP 通信の接続方式(ConnectionType)を設定しています。

TCON_IP_v4.ConnectionType に 16#11 を代入することで、通信方式を TCP(アクティブ接続) に指定しています。この設定により、PLC 側から相手機器へ接続を開始する TCP 通信が行われます。

ネットワーク4

このネットワークでは、TCP 通信で接続する相手機器のポート番号を設定しています。TCON_IP_v4.RemotePort に 7501 を代入することで、相手機器が待ち受けている TCP ポート番号 7501 を指定しています。この設定により、PLC は IP アドレスで指定した相手機器のポート 7501 に対して通信を行うようになります。

ネットワーク5

このネットワークでは、PLC が使用するローカル通信インタフェースを指定しています。

TCON_IP_v4.InterfaceId に 64(Local〜PROFINET_interface_1) を設定することで、PLC の PROFINET インターフェース 1 をTCP 通信に使用するよう指定しています。この設定により、PLC は指定した PROFINET ポートから相手機器との TCP 通信を行います。

ネットワーク6

このネットワークでは、TCP 通信に使用する接続 ID(Connection ID)を設定しています。

TCON_IP_v4.ID に 16#1 を代入することで、この通信接続を識別するための 接続番号 1 を指定しています。この ID は、TCON、TSEND、TRCV、TDISCON などの通信 FB で同じ接続を参照するための共通番号として使用されます。

ネットワーク7

このネットワークでは、PLC 側で使用するローカルポート番号を設定しています。

TCON_IP_v4.LocalPort に 7501 を代入することで、PLC が TCP 通信を行う際に使用する 送信元ポート番号を7501 に指定しています。この設定により、相手機器との通信はローカルポート 7501 ↔ リモートポート 7501の組み合わせで確立されます。

ネットワーク8

このネットワークでは、SLMPで使用するデバイス種別が正しく指定されているかをチェックしています。

ネットワーク9

このネットワークでは、SLMP通信で読み書きを開始するデバイス番号(先頭アドレス)を設定しています。具体的には、定数 100 をdbFX5Read.uiFirstDeviceOffset に MOVE で代入しています。つまりこれは、「SLMP通信は デバイス番号100番から 読み書きを始めます」と指定している処理です。

ネットワーク10

このネットワークでは、SLMP通信で扱うデバイスの個数(点数)を設定しています。具体的には、定数 10 をdbFX5Read.uiTotalDevices に MOVE で代入しています。つまりこれは、「先頭デバイスから 10点分 をまとめて読み書きします」という指定です。

ネットワーク11

このネットワークでは、dbFX5Read.udtTCON_IP_v4.ActiveEstablishedというフラグを 常にON にしています。つまり、PLC側から能動的(Active)に通信接続を開始するという指定です。

ネットワーク12

これは「今まで設定してきた通信条件を使って、SLMPの読込みFBを実行している本体部分」で、設定済みの通信条件を使って、SLMP読込みFBを起動し、FX5からデータを読み、その状態と結果をDBにまとめて返しています。

ネットワーク15

このネットワークでは、TCP 通信で接続する相手機器の IP アドレスを設定しています。TCON_IP_v4 構造体の RemoteAddress.ADDR[1]~[4] にそれぞれ 192・168・0・95 を代入することで、接続先 IP を 192.168.0.95 に指定しています。

ネットワーク16

このネットワークでは、TCP 通信の接続方式(ConnectionType)を設定しています。

TCON_IP_v4.ConnectionType に 16#11 を代入することで、通信方式を TCP(アクティブ接続) に指定しています。この設定により、PLC 側から相手機器へ接続を開始する TCP 通信が行われます。

ネットワーク17

このネットワークでは、TCP 通信で接続する相手機器のポート番号を設定しています。TCON_IP_v4.RemotePort に 7502 を代入することで、相手機器が待ち受けている TCP ポート番号 7502 を指定しています。この設定により、PLC は IP アドレスで指定した相手機器のポート 7502 に対して通信を行うようになります。

ネットワーク18

このネットワークでは、PLC が使用するローカル通信インタフェースを指定しています。

TCON_IP_v4.InterfaceId に 64(Local〜PROFINET_interface_1) を設定することで、PLC の PROFINET インターフェース 1 をTCP 通信に使用するよう指定しています。この設定により、PLC は指定した PROFINET ポートから相手機器との TCP 通信を行います。

ネットワーク19

このネットワークでは、TCP 通信に使用する接続 ID(Connection ID)を設定しています。

TCON_IP_v4.ID に 16#2 を代入することで、この通信接続を識別するための 接続番号 1 を指定しています。この ID は、TCON、TSEND、TRCV、TDISCON などの通信 FB で同じ接続を参照するための共通番号として使用されます。

ネットワーク20

このネットワークでは、PLC 側で使用するローカルポート番号を設定しています。

TCON_IP_v4.LocalPort に 7502 を代入することで、PLC が TCP 通信を行う際に使用する 送信元ポート番号を7502 に指定しています。この設定により、相手機器との通信はローカルポート 7502 ↔ リモートポート 7502の組み合わせで確立されます。

ネットワーク21

このネットワークでは、SLMPで使用するデバイス種別が正しく指定されているかをチェックしています。

ネットワーク22

このネットワークでは、SLMP通信で読み書きを開始するデバイス番号(先頭アドレス)を設定しています。具体的には、定数 100 をdbFX5Write.uiFirstDeviceOffset に MOVE で代入しています。つまりこれは、「SLMP通信は デバイス番号100番から 書きを始めます」と指定している処理です。

ネットワーク23

このネットワークでは、SLMP通信で扱うデバイスの個数(点数)を設定しています。具体的には、定数 10 をdbFX5Write.uiTotalDevices に MOVE で代入しています。つまりこれは、「先頭デバイスから 10点分 をまとめて書き込みします」という指定です。

ネットワーク24

このネットワークでは、dbFX5Write.udtTCON_IP_v4.ActiveEstablishedというフラグを 常にON にしています。つまり、PLC側から能動的(Active)に通信接続を開始するという指定です。

ネットワーク25

これは「今まで設定してきた通信条件を使って、SLMPの読込みFBを実行している本体部分」で、設定済みの通信条件を使って、SLMP書き込みFBを起動し、FX5もデータを書き込み、その状態と結果をDBにまとめて返しています。

OB1

最後はOB1から先程作成したFBを呼び出します。

ダウンロード

プロジェクトをCPUにDownloadします。

結果

こちらの動画から動作確認できます。

プロジェクトダウンロード

今回の記事のプロジェクトはGithubでDownloadできます。

https://github.com/soup01Threes/Siemens/blob/main/SLMP.zap20

シェアする

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

フォローする