Codesys#Modbus TCP ライブラリを使ってみよう(Client編)

今回の記事ではCodesysのModbus TCP/IPライブラリを使用し同じネットワークのModbus Serverにアクセスします。ライブラリを使用することにアクセスするModbus Server、レジスタと数をダイナミックに変更できます。

こちらは今回記事で使用した機器です。

  • EXOR ex707M
  • JMOBILE V4.7
  • CODESYS V3.5 SP21

さ、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

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

FB

最初に今回記事で使用したFBを紹介します。

ClientTCP

ClientTCPはClientRequestが動作するための通信インフラストラクチャを提供するインスタンスです。

VAR_INPUT

Name

Type

Comment

xConnect

BOOL

サーバー(スレーブ)に接続します。

aIPaddr

ARRAY [0..3] OF BYTE

ETHサーバー(スレーブ)のアドレス。xConnectの立ち上がりエッジ時のみ読み込まれます。

uiPort

UINT

ETHサーバー(スレーブ)のポート番号。xConnectの立ち上がりエッジ時のみ読み込まれます。

tConnectTimeOut

TIME

接続タイムアウト(ミリ秒単位)。xConnectの立ち上がりエッジ時のみ読み込まれます。

udiLogOptions

UDINT

ロギングオプション。

VAR_OUTPUT

Name

Type

Comment

xConnected

BOOL

クライアント(マスター)がサーバー(スレーブ)に接続されています。

xError

BOOL

エラー

eErrorID

Error

エラーステータス

udiNumMsgSent

UDINT

接続以降に送信したリクエストメッセージの数。

udiNumMsgReply

UDINT

接続以降に受信したリプライメッセージの数。

udiNumMsgExcReply

UDINT

接続以降に受信した例外リプライメッセージの数。

udiNumMsgExcReplyIllFct

UDINT

接続以降に受信した例外リプライメッセージのうち、不正ファンクションを通知したものの数。

udiNumMsgExcReplyIllDataAdr

UDINT

接続以降に受信した例外リプライメッセージのうち、不正データアドレスを通知したものの数。

udiNumReplyTimeouts

UDINT

接続以降のリプレイタイムアウトの数。

udiNumReqNotProcessed

UDINT

接続以降、時間内に処理されなかったリクエスト(「リクエストスタベーション」)の数。

udiNumReqParamError

UDINT

パラメータエラーで開始されたリクエストの数(例:「Read Coils」→「コイル数」= 0)。

udiLastTransactionTime

UDINT

トランザクション時間(ms単位)。リクエストメッセージ送信からリプライメッセージ受信までの時間差。

ClientRequestReadHoldingRegisters

ReadHoldingRegisters クライアント要求 (FC03)を送信するFBです。

VAR_IN_OUT

Name

Type

Comment

rClient

Client

Clientへの参照。

VAR_INPUT

uiUnitId

UINT

リクエスト送信先のUnit-Id。

udiReplyTimeout

UDINT

リプライタイムアウト(μs単位)。リクエストメッセージ送信からリプライメッセージ受信までの最大許容時間。デフォルト50ms。

uiMaxRetries

UINT

「リプライタイムアウト」発生時のリクエスト最大リトライ回数。

uiStartItem

UINT

読み出す最初の「データアイテム」。

uiQuantity

UINT

読み出す「データアイテム」の数。ReadCoils / ReadDiscreteInputs:1〜2000、ReadHoldingRegisters / ReadInputRegisters:1〜125。

pData

POINTER TO ARRAY [0..0] OF UINT

結果データ配列へのポインタ。メモリは呼び出し元が確保する必要があります。

VAR_OUTPUT

Name

Type

Comment

xDone

BOOL

Ready条件に達しました。

xBusy

BOOL

操作実行中。

xError

BOOL

エラー条件に達しました。

eErrorID

Error

エラーステータス。

eException

ExceptionCodes

リクエスト例外コード。

uiRetryCnt

UINT

「リプライタイムアウト」発生時のリクエストリトライ回数。

ClientRequestWriteMultipleRegisters

WriteMultipleRegisters クライアントリクエスト (FC16)を送信するFBです。

VAR_IN_OUT

rClient

Client

Clientへの参照。

VAR_INPUT

Name

Type

Comment

xExecute

BOOL

立ち上がりエッジ:定義された操作を開始します。FALSE:Ready条件に達した後、定義された操作をリセットします。

udiTimeOut

UDINT

実行の最大動作時間(μs単位)。0:動作時間制限なし。

uiUnitId

UINT

リクエスト送信先のUnit-Id。

udiReplyTimeout

UDINT

リプライタイムアウト(μs単位)。リクエストメッセージ送信からリプライメッセージ受信までの最大許容時間。デフォルト50ms。

uiMaxRetries

UINT

「リプレイタイムアウト」発生時のリクエスト最大リトライ回数。

uiStartItem

UINT

書き込む最初の「データアイテム」。

uiQuantity

UINT

書き込む「データアイテム」の数。

pData

POINTER TO ARRAY [0..0] OF UINT

書き込みデータ配列へのポインタ。

VAR_OUTPUT

Name

Type

Comment

xDone

BOOL

Ready条件に達しました。

xBusy

BOOL

操作実行中。

xError

BOOL

エラー条件に達しました。

eErrorID

Error

エラーステータス。

eException

ExceptionCodes

リクエスト例外コード。

uiRetryCnt

UINT

「リプライタイムアウト」発生時のリクエストリトライ回数。

Implementation

Codesys Side

MAINプログラム

こちらは今回の記事のプログラムで、CFC言語で構築します。

VAR

こちらは今回記事のサンプルで定義した変数です。

PROGRAM OB1
VAR
fbClientTCP:ModbusFB.ClientTCP;
xConnect,xConnected,xError:BOOL;
aIPaddr:ARRAY[0..3]OF BYTE:=[192,168,251,42];
uiPort:UINT;
arreErrorID:ARRAY[0..9]OF ModbusFB.Error;
arreException:ARRAY[0..9]OF ModbusFB.ExceptionCodes;
arrxExecute,arrxDone,arrxBusy,arrxError:ARRAY[0..9]OF BOOL;
arrxWExecute,arrxWDone,arrxWBusy,arrxWError:ARRAY[0..9]OF BOOL;
arruiStartItem,arruiQuantity,arruiRetryCnt,arrWuiRetryCnt:ARRAY[0..9]OF UINT;
arrWeErrorID:ARRAY[0..9]OF ModbusFB.Error;
arrWeException:ARRAY[0..9]OF ModbusFB.ExceptionCodes;
xEnable: BOOL;
xReset:BOOL;
xInit :BOOL;
xSwap :BOOL;
arrewErrorID: INT;
END_VAR
VAR
fbClientRequestReadHoldingRegisters :ARRAY[0..9]OF ModbusFB.ClientRequestReadHoldingRegisters ;
fbClientRequestWriteMultipleRegisters :ARRAY[0..9]OF ModbusFB.ClientRequestWriteMultipleRegisters ;
uiUnitId:UINT;
arrOKCounter,arrErrCounter :ARRAY[0..99]OF WORD;
arrOKfbR_TRIG,arrfbErrR_TRIG,arrOKfbR_TRIG2 :ARRAY[0..9]OF Standard.R_TRIG;
arrWOKCounter,arrWErrCounter :ARRAY[0..99]OF WORD;
arrWOKfbR_TRIG,arrWfbErrR_TRIG :ARRAY[0..9]OF Standard.R_TRIG;
arrHoldingRegister_word :ARRAY[0..99]OF WORD;
arrHoldingRegister_Dword440001 :ARRAY[0..99]OF DWORD;
arrHoldingRegister_Real441001 :ARRAY[0..99]OF REAL;
arrHoldingRegister_Dword442001 :ARRAY[0..99]OF DWORD;
arrHoldingRegister_Real443001 :ARRAY[0..99]OF REAL;
arrHoldingRegister_Real443001_2 :ARRAY[0..99]OF REAL;
arrHoldingRegister_Dword444001 :ARRAY[0..99]OF DWORD;
arrHoldingRegister_Real445001 :ARRAY[0..99]OF REAL;

arrHoldingRegister_Dword442001R :ARRAY[0..99]OF DWORD;
arrHoldingRegister_Real443001R :ARRAY[0..99]OF REAL;
arrHoldingRegister_Real443001_2R :ARRAY[0..99]OF REAL;
arrwStep :ARRAY[0..9]OF WORD;
arrrStep :ARRAY[0..9]OF WORD;
END_VAR

初期化

このCFCプログラムは、初回起動時(xInit立ち上がり)に通信パラメータおよびModbusリード設定を一括初期化するブロックです。xInitの立ち上がりエッジでLabel001へジャンプし、初期化処理を実行します。初期化完了後はxInitをリセット(FALSE)して処理を終了します。

接続パラメータ

接続先IPアドレスは 192.168.251.42、ポートは502(Modbus TCP標準)です。

変数

設定値

内容

aIPaddr[0]

192

IPアドレス第1オクテット

aIPaddr[1]

168

IPアドレス第2オクテット

aIPaddr[2]

251

IPアドレス第3オクテット

aIPaddr[3]

42

IPアドレス第4オクテット

uiPort

502

Modbusポート番号(標準)

uiUnitId

0

ユニットID

Modbusリード開始アドレス(arruiStartItem)

インデックス

開始アドレス

[2]

0

[3]

1000

[4]

2000

[5]

3000

[6]

3100

[7]

4000

[8]

5000

Modbusリード数量(arruiQuantity)

インデックス

読み出し数

[2]

16

[3]

24

[4]

70

[5]

48

[6]

48

[7]

24

[8]

56

Modbus TCPサーバーへの接続管理

このCFCブロックは、Modbus TCPサーバーへの接続管理を担うセクションで、中心となるのはfbClientTCP(型:ClientTCP)のファンクションブロックで、Modbus TCPクライアントとして動作します。

また、xConnectedでxEnableを生成しています。これは接続が確立されている場合にのみ後段のModbus読み書き処理を有効化するためのイネーブル信号として機能します。

ClientRequestReadHoldingRegistersプログラム

このCFCブロックは、Modbus TCPサーバーからHolding Registerを読み出す処理と、その結果のカウント管理・次回実行トリガーを担うセクションです。また、読み出し成功・失敗のたびにカウンタをインクリメントし、通信品質の監視に活用します。 読み出しが完了(Done)またはエラー発生のいずれかで、かつリセット中でない場合にarxExecute[1]を再びONにすることで、Holding Registerの連続・繰り返し読み出しを実現しています。

ClientRequestReadHoldingRegistersプログラム(複数)

このCFCは、複数のHolding Registerブロックを順番に読み出すための順次制御と、各チャンネルの読み出し処理を組み合わせた構成です。

ステップ管理

複数のReadリクエストをステップ番号(arrStep[0])で順次切り替える制御を行っています。

ステップ

条件

動作

Step=0

arrxExecute[9] AND xEnable AND arrStep[0]=0

Read[9]を実行、完了でOKカウンタ加算・Step→1へ

Step=1

arrStep[0]=1

arrxExecute[5]をON

Step=2

arrStep[0]=2

arrxExecute[6]をON

Step=3

arrStep[0]=3

Step→0にリセット(arrrStep[0]=0)

R_TRIGで完了の立ち上がりを検出し、MOVEでステップを進めることで循環型の順次読み出しを実現しています。

読み出し

次にステップカウンタで複数のReadリクエストをリレー式に順次実行することで、1つのModbus TCP接続上で複数のアドレス範囲を効率よくポーリングする構成となっています。各チャンネルは異なるデータ型・格納先配列を持ち、用途別にレジスタマップが分割されています。

完了処理

  • 成功(arxDone[6]立ち上がり)→ arrOKCounter[6]+1、arrStep[0]→3にMOVE
  • 失敗(arrxError[6]立ち上がり)→ arrErrCounter[6]+1
  • Done or Error → arrxExecute[6]を再トリガー(xResetでキャンセル可)
[5]と[6]の役割分担

両チャンネルともReal型(浮動小数点)レジスタの読み出しを担っており、アドレス範囲の異なる2つのデータブロックを順次取得する構成となっています。

項目

Read[5]

Read[6]

格納先

arrHoldingRegister_Real443001

arrHoldingRegister_Real443001_2

完了後のステップ遷移

Step→2

Step→3

位置づけ

第1Realデータブロック読み出し

第2Realデータブロック読み出し

Holding Registerを書き込む

このCFCブロックは、Modbus TCPサーバーへHolding Registerを書き込む処理と、書き込み前のワードスワップ処理を組み合わせた構成です。またxSwapがONの場合、DWORDデータのワード順を入れ替えてから書き込みます。これはModbusのビッグエンディアン/リトルエンディアン変換に対応するための処理です。

ワードスワップ処理

xSwapがONかつ該当チャンネルの読み出し完了(arxDone)を条件として、スワップ関数を実行します。それはModbus TCPで取得したDWORDおよびReal型データは、デバイス間のエンディアン差異により上位・下位ワードが逆順になる場合があります。このブロックはxSwapフラグで一括制御することで、全チャンネルのエンディアン変換を柔軟にON/OFF可能な設計となっています。Real型については読み出し完了(arxDone)を条件に加えることで、未取得データへの誤適用を防止しています。

FC-fc_swapDWordsArray

この関数は、DWORD配列の各要素に対して上位・下位ワードを入れ替える処理を行います。Modbus TCPでは機器によってDWORDの上位・下位ワードの送信順が異なる場合があります。この関数は受信した全DWORD配列に対してワード順を一括反転することで、エンディアン差異を吸収するユーティリティ関数として機能しています。

FUNCTION fc_swapDWordsArray :BOOL;

VAR_IN_OUT
arrdw:ARRAY[*]OF DWORD;
END_VAR
VAR
_arrw:ARRAY[0..1]OF WORD;
_arrw2:ARRAY[0..1]OF WORD;
_i :DINT;
END_VAR


FOR _i:= LOWER_BOUND(arrdw,1) TO UPPER_BOUND(arrdw,1) DO
MEMUtils.MemCpy(pbyDest:=ADR(_arrw),pbySrc:=ADR(arrdw[_i]),SIZEOF(_arrw));
_arrw2[0]:=_arrw[1];
_arrw2[1]:=_arrw[0];
MEMUtils.MemCpy(pbyDest:=ADR(arrdw[_i]),pbySrc:=ADR(_arrw2),SIZEOF(_arrw2));
END_FOR;

ダウンロード

最後はプロジェクトをCODESYS Runtimeにダウンロードし、通信確認してください。

シェアする

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

フォローする