今回の記事では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
技術はひとりじゃもったいない。
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
|
|---|
初期化
この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;
|
|---|
ダウンロード
最後はプロジェクトをCODESYS Runtimeにダウンロードし、通信確認してください。