この記事では、Modbus/TCP について以下の点を中心にまとめています。
- TCP 502番ポートを用いた接続の考え方
- ADU / PDU の構造とサイズ制限
- 各ファンクションコードにおけるデータ構成
- 1リクエストあたりの最大読み書き数の算出根拠
- OSI 7階層・TCP/IP 4階層との対応関係
仕様書を読んでも分かりづらかった部分を、
「なぜその数になるのか」「どの層の話なのか」
という観点で整理しています。
同じように Modbus を復習したい方や、
新人・後輩に説明する立場の方の参考になれば幸いです。
1. サーバーへの接続(Modbus/TCP)
- Modbus/TCP は TCP の 502 番ポートを使用する
- クライアント(PLC など)は、サーバー(製品)の IP アドレス + TCP 502 番ポートへ接続する
- サーバー側では、クライアントからの接続を許可するために、接続元アドレスのフィルタリング設定(詳細は補足をご覧ください)が必要
2. Modbus/TCP のパケット構造
Modbus/TCP では、以下のような リクエスト/レスポンス方式で通信を行う。(1 回のリクエストに対して、必ず 1 回のレスポンスが返される。)
- クライアント → サーバー:リクエスト
- サーバー → クライアント:レスポンス
※Modbus/TCP では、クライアントからのリクエストとサーバーからのレスポンスの両方に同じパケット形式を使用する。
用語整理(備考)
パケット(Packet)
通信でやり取りされるデータのかたまり。
パケット形式(Packet Format)
通信でやり取りするデータの並び方のルール。
2.1 ADU(Application Data Unit)の構成
- ADU内のすべての数値はビッグエンディアン形式
- 最上位バイトがアドレスの小さい側に配置される
3. Modbus の制御対象とアドレス空間
Modbus で扱う制御対象は、ビット数とアクセス権限によって4種類に分類される。
| 名称 | 操作単位 | アクセス | 通し番号 | アドレス範囲 |
|---|---|---|---|---|
| コイル (Coil) | 1 ビット | R/W | 00001~09999 | 0~9998 |
| インプット (Discrete Input) | 1 ビット | R | 10001~19999 | 0~9998 |
| ホールディング・レジスタ (Holding Register) | 16 ビット | R/W | 40001~49999 | 0~9998 |
| インプット・レジスタ (Input Register) | 16 ビット | R | 30001~39999 | 0~9998 |
製品制御における重要ポイント
- 製品制御は主にホールディング・レジスタ(読み書き)とインプット・レジスタ(読み取り)を使用
- 各制御対象は独立したアドレス空間を持つ
- ファンクション実行時は通し番号ではなくアドレスを指定する
3.1 1リクエストあたりの最大読み書き数
Modbus/TCP では、ADUとPDUの最大サイズに制限があるため、1回のリクエストで読み書きできる数に上限がある。
前提条件
(※「2.1 ADU(Application Data Unit)の構成図」に記載の通り、以下の条件とする)
1 バイト = 8 ビット
ADU最大サイズ:260 バイト
MBAP Header :7 バイト
PDU最大:253バイト
├ Function Code:1バイト
└ Data:最大252バイト
│
├ Read Coils / Discrete Inputs (0x01 / 0x02)
│ ├ リクエスト:4バイト
│ │ - Start Address (2)
│ │ - Quantity (2)
│ └ レスポンス:1 + N バイト
│ - Byte Count (1)
│ - Coil Data (N)
│
├ Read Holding / Input Registers (0x03 / 0x04)
│ ├ リクエスト:4バイト
│ │ - Start Address (2)
│ │ - Quantity (2)
│ └ レスポンス:1 + N×2 バイト
│ - Byte Count (1)
│ - Register Data (N×2)
│
├ Write Single Coil (0x05)
│ ├ リクエスト:4バイト
│ │ - Output Address (2)
│ │ - Output Value (2)
│ └ レスポンス:4バイト
│ - Output Address (2)
│ - Output Value (2)
│
├ Write Single Register (0x06)
│ ├ リクエスト:4バイト
│ │ - Register Address (2)
│ │ - Register Value (2)
│ └ レスポンス:4バイト
│ - Register Address (2)
│ - Register Value (2)
│
├ Write Multiple Coils (0x0F)
│ ├ リクエスト:5 + N バイト
│ │ - Start Address (2)
│ │ - Quantity (2)
│ │ - Byte Count (1)
│ │ - Coil Data (N)
│ └ レスポンス:4バイト
│ - Start Address (2)
│ - Quantity (2)
│
└ Write Multiple Registers (0x10)
├ リクエスト:5 + N×2 バイト
│ - Start Address (2)
│ - Quantity (2)
│ - Byte Count (1)
│ - Register Data (N×2)
└ レスポンス:4バイト
- Start Address (2)
- Quantity (2)
最大数の算出
Read Coils / Discrete Inputs (0x01 / 0x02)
レスポンスPDU構造:
Function Code(1) + Byte Count(1) + Coil Data(N) = 2 + Nバイト
- 2 + N ≤ 253 → N ≤ 251バイト
- ビット単位:251 × 8 = 2008ビット
- 仕様上の上限:2000ビット
Write Multiple Coils (0x0F)
リクエストPDU構造:
Function Code(1) + Start Address(2) + Quantity(2) + Byte Count(1) + Coil Data(N) = 6 + Nバイト
- 6 + N ≤ 253 → N ≤ 247バイト
- ビット単位:247 × 8 = 1976ビット
- 仕様上の上限:1968ビット
Read Holding / Input Registers (0x03 / 0x04)
レスポンスPDU構造:
Function Code(1) + Byte Count(1) + Register Data(N×2) = 2 + N×2バイト
- 2 + N×2 ≤ 253 → N ≤ 125.5
- 仕様上の上限:125レジスタ
Write Multiple Registers (0x10)
リクエストPDU構造:
Function Code(1) + Start Address(2) + Quantity(2) + Byte Count(1) + Register Data(N×2) = 6 + N×2バイト
- 6 + N×2 ≤ 253 → N ≤ 123.5
- 仕様上の上限:123レジスタ
まとめ表
| 操作 | 理論上の最大 | 仕様上の上限 | 制約要因 |
|---|---|---|---|
| Read Coils / Discrete Inputs | 2008ビット | 2000ビット | レスポンスPDU(251バイト) |
| Write Multiple Coils | 1976ビット | 1968ビット | リクエストPDU(247バイト) |
| Read Holding / Input Registers | 125レジスタ | 125レジスタ | レスポンスPDU(251バイト) |
| Write Multiple Registers | 123レジスタ | 123レジスタ | リクエストPDU(247バイト) |
4. サポートされるファンクション・コード
| ファンクション・コード | 値 | 説明 | 種別 |
|---|---|---|---|
| Read Coils | 0x01 | コイルの値を読み出す | オプション |
| Read Discrete Inputs | 0x02 | インプットの値を読み出す | オプション |
| Read Holding Registers | 0x03 | ホールディング・レジスタの値を読み出す | 必須 |
| Read Input Registers | 0x04 | インプット・レジスタの値を読み出す | 必須 |
| Write Single Coil | 0x05 | 1つのコイルに値を書き込む | オプション |
| Write Single Register | 0x06 | 1つのホールディング・レジスタに値を書き込む | オプション |
| Write Multiple Coils | 0x0F | 複数のコイルに値を書き込む | オプション |
| Write Multiple Registers | 0x10 | 複数のホールディング・レジスタに値を書き込む | 必須 |
製品制御において一般的に使用される主要ファンクション
- 0x03:ホールディング・レジスタの読み出し(事実上必須)
- 0x04:インプット・レジスタの読み出し(監視用途で使用)
- 0x10:複数ホールディング・レジスタの書き込み(制御用途で使用)
補足
1. フィルタリングについて
1.1 Modbus/TCP の特性
- 認証機能を持たない
- 暗号化を行わない
- TCP 502 番ポートを使用する
1.2 フィルタリングが必要な理由
- 接続制限を行わない場合、任意の機器から TCP 502 番ポートへ接続可能となる
- 誤操作、不正アクセス、想定外のPLCからの書き込みが発生する可能性がある
1.3 フィルタリングの考え方
- 機器(サーバー)側で接続元IPアドレスを指定する
- 許可したIPアドレス以外からの接続は受け付けない
1.4 フィルタリング設定の例
- 許可するIPアドレス:
192.168.1.10 - 上記以外のIPアドレスからの接続は拒否する
2. 通信階層とModbus/TCP
2.1 OSI 7階層モデル(理論)
OSI参照モデルは通信を説明するための理論モデルである。ISO(国際標準化機構)が策定し、ベンダーごとに異なる通信方式を共通の言葉で説明することを目的とした。
| 階層 | 名称 | 役割 |
|---|---|---|
| 7 | アプリケーション層 | 利用者が使う通信サービス(HTTP、FTP、Modbus等) |
| 6 | プレゼンテーション層 | データ形式の変換、暗号化、文字コード |
| 5 | セッション層 | 通信の開始・終了、会話の管理 |
| 4 | トランスポート層 | 信頼性のあるデータ転送、再送制御(TCP、UDP) |
| 3 | ネットワーク層 | 異なるネットワーク間の経路選択(IP) |
| 2 | データリンク層 | 同一ネットワーク内でのデータ転送制御 |
| 1 | 物理層 | 電気信号、ケーブル、物理的な接続 |
7階層に分けた理由
- 通信の役割を完全に分離し、責任範囲を明確にするため
- 各階層が独立して開発・変更できるようにするため
2.2 TCP/IP 4階層モデル(実務)
TCP/IPモデルは実装から生まれた実用的なモデルである。実際のネットワーク機器やOSはこの構造で動作している。
| 階層 | 名称 | 何をする層か | Modbus/TCP通信での具体的処理 | プロトコル例 |
|---|---|---|---|---|
| 4 | アプリケーション層 | 何をしたいか | Modbusコマンドの生成と解釈(ファンクションコード03:レジスタ読み取り等) | HTTP, SMTP, FTP, Modbus/TCP |
| 3 | トランスポート層 | 確実に届ける | TCP接続の確立(ポート502)、データの分割・再構成、到達確認、再送制御 | TCP, UDP |
| 2 | インターネット層 | どこに届ける | 送信元IP(PLC)→ 送信先IP(サーバー)へのパケット転送、ルーティング | IP, ICMP |
| 1 | ネットワークインターフェース層 | 物理的に流す | Ethernetフレームとして物理的にデータ送信、MACアドレス処理 | Ethernet, Wi-Fi |
4階層にまとめた理由
- OSI の 7・6・5 層はアプリケーション内部で自然に処理される
- OSI の 2・1 層はドライバレベルで一体として扱われる
- 実装に即したシンプルな構造の方が理解しやすい
2.3 OSI → TCP/IP 対応関係
2.4 サーバー・クライアント通信フロー
TCP接続の確立から切断まで
(3ウェイハンドシェイク + データ転送 + 4ウェイハンドシェイク)
フロー説明
| フェーズ | 処理 | 説明 |
|---|---|---|
| 接続確立 | ① SYN | クライアントがTCP接続を要求。 初期シーケンス番号(SEQ)を通知し、以降この番号からバイト単位で番号を振って送信することを宣言 例:SEQ=100の場合、「これから送る最初のデータの番号が100です」**と相手に知らせる |
| ② SYN + ACK | サーバーが接続を受諾。 自分の初期SEQを通知し、クライアントのSEQ+1をACKで確認 |
|
| ③ ACK | クライアントがサーバーのSEQ+1をACKで確認。 双方がSEQ/ACKを共有し、TCP接続が確立(3ウェイハンドシェイク完了) |
|
| データ転送 | ④ リクエスト | クライアントがModbus/TCPリクエストを送信。 TCPはデータを分割し、SEQを付与して送信 |
| ⑤ ACK | サーバーが受信したデータのSEQを確認し、次に受信したい番号をACKで通知 | |
| ⑥ レスポンス | サーバーがModbus処理結果をTCPで返送。 レスポンスにもサーバー側のSEQが付与される |
|
| ⑦ ACK | クライアントが受信完了をACKで通知。 再送が不要であることを確認 |
|
| 接続切断 | ⑧ FIN | クライアントが「これ以上送信するデータがない」ことを通知(FINはSEQを1消費) |
| ⑨ ACK | サーバーがFINを受信し、SEQ+1をACKで確認 | |
| ⑩ FIN | サーバーも送信完了を通知 | |
| ⑪ ACK | クライアントがFINを確認し、TCP接続が完全に終了(4ウェイハンドシェイク完了) |


