参考資料
各ブログサイトは章立てが不十分だったり、レイアウトが乱れていたり、誤りも混ざっていたりするので、参考にしないほうがいい。
ここに Modbus 中国語サイトの資料がある
MODBUS プロトコル中国語版/英語版プレビュー・ダウンロード | Modbus IoT クラウドプラットフォーム
より権威があるのは公式ドキュメント
MODBUS Application Protocol 1 1 b
ほかの公式ドキュメントもここから探せる
もちろん公式ドキュメントにも正直満足していない。古い設計構造を維持しているせいで、各フレームのフィールド説明が直感的でなく、誤植も少しある。

以下に、自分で書き直した有用な情報を載せる。
あと、自分で実装したライブラリもある。
プロトコルの特徴
フレーム全体の構造
| メッセージヘッダー | アドレス部 | 機能コード | データ部 | チェックサム部 | |
|---|---|---|---|---|---|
| RTU | 1バイト(スレーブID) | 1バイト | nバイト(可変) | 2バイト(他すべてに対して CRC-MB16) | |
| TCP | 6バイト (2バイトのトランザクションID+ 2バイトのプロトコルID=すべて0+ 2バイトの後続バイトの長さ=スレーブIDを含む) | 1バイト(スレーブID) | 1バイト | nバイト(可変) |
このメッセージヘッダーは、公式ドキュメントの全体モデルではアドレス部に分類されているが、実際はバイト長の値にスレーブIDが含まれている。おそらく、初期はシリアルプロトコルしかなかった時代に全体モデルが決められてしまい、後から追加された Modbus TCP が互換性のために無理やり合わせたんだと思う。
ここでは勝手に分類し直した。非標準だけど、気にしない。
レジスタの属性
| R/W属性 | ビット数 | |
|---|---|---|
| コイル(0x01) | 読み書き可 | 1ビット |
| 離散入力レジスタ(0x02) | 読み取り専用 | 1ビット |
| 保持レジスタ(0x03) | 読み書き可 | 16ビット |
| 入力レジスタ(0x04) | 読み取り専用 | 16ビット |
全体モデル
ソフトウェアでメモリ構造を抽象化し、各アドレスに異なる意味のデータを格納する。FPGA で SRAM を模擬するのと同じ考え方だ。ただし、各レジスタアドレスが必ずしも16ビットとは限らず、1ビットの場合もある。属性は RO/RW である。
RTU シリーズ
(0x01) コイルの読み出し
マスター要求
| アドレス部 | 機能コード | 開始アドレス | コイル数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | バイト数 | コイル状態 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 1バイト(コイル状態部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
例
例として、アドレス20〜38のコイルデータの読み出しを要求する。応答はアドレスの低い順に並び、最後のバイトが足りない場合は上位ビットを0で埋める。
最終的な出力状態 38-36 の応答バイトでは、残り5ビットを0で埋める(上位端まで)。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x01 | 機能コード | 0x01 |
| 開始アドレス上位8ビット | 0x00 | バイト数 | 0x03 |
| 開始アドレス下位8ビット | 0x13 | 出力状態 27-20 | 0xCD |
| 出力数上位8ビット | 0x00 | 出力状態 35-28 | 0x6B |
| 出力数下位8ビット | 0x13 | 出力状態 38-36 | 0x05 |
| CRC下位8ビット | 0xA9 | CRC下位8ビット | 0x42 |
| CRC上位8ビット | 0xC8 | CRC上位8ビット | 0x82 |
(0x02) 離散入力レジスタの読み出し
マスター要求
| アドレス部 | 機能コード | 開始アドレス | 離散入力数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | バイト数 | 離散入力状態 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 1バイト(離散入力状態部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
例
例として、アドレス197〜218の離散入力レジスタの読み出しを要求する。応答はアドレスの低い順に並び、最後のバイトが足りない場合は上位ビットを0で埋める。
最終的な入力状態 218-213 の応答バイトでは、残り2ビットを0で埋める(上位端まで)。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x02 | 機能コード | 0x02 |
| 開始アドレス上位8ビット | 0x00 | バイト数 | 0x03 |
| 開始アドレス下位8ビット | 0xC4 | 入力状態 204-197 | 0xAC |
| 入力数上位8ビット | 0x00 | 入力状態 212-205 | 0xDB |
| 入力数下位8ビット | 0x16 | 入力状態 218-213 | 0x35 |
| CRC下位8ビット | 0xB8 | CRC下位8ビット | 0x22 |
| CRC上位8ビット | 0x39 | CRC上位8ビット | 0x88 |
(0x03) 保持レジスタの読み出し
マスター要求
| アドレス部 | 機能コード | 開始アドレス | 保持レジスタ数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | バイト数 | 保持レジスタ状態 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 1バイト(保持レジスタ状態部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
例
例として、アドレス108〜110の保持レジスタデータの読み出しを要求する。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x03 | 機能コード | 0x03 |
| 開始アドレス上位8ビット | 0x00 | バイト数 | 0x06 |
| 開始アドレス下位8ビット | 0x6B | レジスタ値上位8ビット(108) | 0x02 |
| レジスタ数上位8ビット | 0x00 | レジスタ値下位8ビット(108) | 0x2B |
| レジスタ数下位8ビット | 0x03 | レジスタ値上位8ビット(109) | 0x00 |
| CRC下位8ビット | 0x74 | レジスタ値下位8ビット(109) | 0x00 |
| CRC上位8ビット | 0x17 | レジスタ値上位8ビット(110) | 0x00 |
| レジスタ値下位8ビット(110) | 0x64 | ||
| CRC下位8ビット | 0x05 | ||
| CRC上位8ビット | 0x7A |
(0x04) 入力レジスタの読み出し
マスター要求
| アドレス部 | 機能コード | 開始アドレス | 入力レジスタ数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | バイト数 | 入力レジスタ状態 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 1バイト(入力レジスタ状態部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
例
例として、アドレス9〜10の入力レジスタデータの読み出しを要求する。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x04 | 機能コード | 0x04 |
| 開始アドレス上位8ビット | 0x00 | バイト数 | 0x06 |
| 開始アドレス下位8ビット | 0x6B | レジスタ値上位8ビット(9) | 0x02 |
| レジスタ数上位8ビット | 0x00 | レジスタ値下位8ビット(9) | 0x2B |
| レジスタ数下位8ビット | 0x03 | レジスタ値上位8ビット(10) | 0x00 |
| CRC下位8ビット | 0xC1 | レジスタ値下位8ビット(10) | 0x00 |
| CRC上位8ビット | 0xD7 | CRC下位8ビット | 0xF3 |
| CRC上位8ビット | 0xF4 |
(0x05) 単一コイルへの書き込み
単一コイル書き込みでは、出力値は FF 00 なら ON、00 00 なら OFF のみ有効で、それ以外の値は不正。
マスター要求
| アドレス部 | 機能コード | 出力アドレス | 出力値 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | アドレス | 出力値 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
例
例として、アドレス173のコイルを ON に書き込む要求。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x05 | 機能コード | 0x05 |
| レジスタアドレス上位8ビット | 0x00 | レジスタアドレス上位8ビット | 0x00 |
| レジスタアドレス下位8ビット | 0xAC | レジスタアドレス下位8ビット | 0xAC |
| レジスタ値上位8ビット | 0xFF | レジスタ値上位8ビット | 0xFF |
| レジスタ値下位8ビット | 0x00 | レジスタ値下位8ビット | 0x00 |
| CRC下位8ビット | 0x4C | CRC下位8ビット | 0x4C |
| CRC上位8ビット | 0x1B | CRC上位8ビット | 0x1B |
(0x06) 単一保持レジスタへの書き込み
マスター要求
| アドレス部 | 機能コード | 保持レジスタアドレス | レジスタ値 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | 保持レジスタアドレス | レジスタ値 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
例
例として、アドレス2の保持レジスタに 0x0003 を書き込む要求。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x06 | 機能コード | 0x06 |
| レジスタアドレス上位8ビット | 0x00 | レジスタアドレス上位8ビット | 0x00 |
| レジスタアドレス下位8ビット | 0x02 | レジスタアドレス下位8ビット | 0x02 |
| レジスタ値上位8ビット | 0x00 | レジスタ値上位8ビット | 0x00 |
| レジスタ値下位8ビット | 0x03 | レジスタ値下位8ビット | 0x03 |
| CRC下位8ビット | 0x2C | CRC下位8ビット | 0x2C |
| CRC上位8ビット | 0x0B | CRC上位8ビット | 0x0B |
(0x0F) 複数コイルへの書き込み
マスター要求
| アドレス部 | 機能コード | 開始アドレス | 設定数 | バイト数 | 設定値 | チェックサム部 |
|---|---|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 1バイト(設定値部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | 開始アドレス | 設定数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
例
例として、アドレス20から始まる10個のコイルを書き込む要求。
書き込みには合計2バイト(16ビット)必要で、残り6ビットは0で埋める(上位端まで)。
| コイルアドレス | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | — | — | — | — | — | — | 29 | 28 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 対応値 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
仕様に従って埋めると、実際の設定値部分は 0xCD 0x01 になる。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x0F | 機能コード | 0x0F |
| 開始アドレス上位8ビット | 0x00 | 開始アドレス上位8ビット | 0x00 |
| 開始アドレス下位8ビット | 0x13 | 開始アドレス下位8ビット | 0x13 |
| 設定数上位8ビット | 0x00 | 設定数上位8ビット | 0x00 |
| 設定数下位8ビット | 0x0A | 設定数下位8ビット | 0x0A |
| バイト数 | 0x02 | CRC下位8ビット | 0x24 |
| 設定値(アドレス27〜20) | 0xCD | CRC上位8ビット | 0x09 |
| 設定値(アドレス29〜28) | 0x01 | ||
| CRC下位8ビット | 0x72 | ||
| CRC上位8ビット | 0xCB |
(0x10) 複数保持レジスタへの書き込み
マスター要求
| アドレス部 | 機能コード | 開始アドレス | 設定数 | バイト数 | 設定値 | チェックサム部 |
|---|---|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 1バイト(設定値部分のバイト数) | nバイト | 2バイト(CRC-MB16) |
スレーブ応答
| アドレス部 | 機能コード | 開始アドレス | 設定数 | チェックサム部 |
|---|---|---|---|---|
| 1バイト | 1バイト | 2バイト | 2バイト | 2バイト(CRC-MB16) |
例
例として、アドレス34から始まる4つの保持レジスタを書き込む要求。
| 要求 | 応答 | ||
|---|---|---|---|
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x10 | 機能コード | 0x10 |
| 開始アドレス上位8ビット | 0x00 | 開始アドレス上位8ビット | 0x00 |
| 開始アドレス下位8ビット | 0x22 | 開始アドレス下位8ビット | 0x22 |
| 設定数上位8ビット | 0x00 | 設定数上位8ビット | 0x00 |
| 設定数下位8ビット | 0x04 | 設定数下位8ビット | 0x04 |
| バイト数 | 0x08 | CRC下位8ビット | 0x61 |
| 設定値上位8ビット(34) | 0x00 | CRC上位8ビット | 0xC0 |
| 設定値下位8ビット(34) | 0x40 | ||
| 設定値上位8ビット(35) | 0x00 | ||
| 設定値下位8ビット(35) | 0x24 | ||
| 設定値上位8ビット(36) | 0x00 | ||
| 設定値下位8ビット(36) | 0x01 | ||
| 設定値上位8ビット(37) | 0xBF | ||
| 設定値下位8ビット(37) | 0x52 | ||
| CRC下位8ビット | 0x5F | ||
| CRC上位8ビット | 0xCC |
異常応答フレーム
| アドレス部 | 機能コード | 異常コード | チェックサム部 |
|---|---|---|---|
| 1バイト | 1バイト(機能コード+0x80) | 1バイト | 2バイト(CRC-MB16) |
| 異常コード | 名称 | 意味 |
|---|---|---|
| 0x01 | 不正な機能コード | 受信した要求の機能コードが許可されていない操作である。機能コードがサポートされていないか、あるいはエラー状態で要求を処理しようとしている可能性がある。 |
| 0x02 | 不正なデータアドレス | 要求に含まれるデータアドレスが許可されていないアドレスである。特に、開始アドレスと転送長の組み合わせが無効。たとえば、レジスタを100個持つコントローラでは、開始アドレス96・長さ4の要求は成功するが、開始アドレス96・長さ5の要求は例外コード0x02になる。 |
| 0x03 | 不正なデータ値 | 実質的にはデータ部が不正であることを意味する。たとえば、データ部の長さが不正、または書き込み/読み出し対象のレジスタ数がデータ部と一致しない場合など。なお、レジスタに想定範囲外の値が書き込まれたり、実際の書き込みに失敗したことを意味するわけではない(その場合は0x04)。 |
| 0x04 | スレーブデバイス障害 | サーバー(スレーブ)がレジスタに対して要求された操作を実行する際にエラーが発生した場合。たとえば、レジスタに想定範囲外の値が書き込まれた場合など。 |
| 0x05 | 確認応答 | 実際にはエラーではなく、時間のかかるコマンドを受信したことを示す。要求を受け取り、処理を開始したことを表す。 |
| 0x06 | スレーブデバイスビジー | 時間のかかるコマンドを処理中でビジー状態である。(スレーブが空いたら、このエラーを引き起こした要求を再送すべき) |
| 0x08 | メモリパリティエラー | 記録ファイルを読み出そうとしたが、メモリ内でパリティエラーが検出された。 |
| 0x0A | ゲートウェイ経路利用不可 | ゲートウェイで使用され、要求を処理するための入力ポートから出力ポートへの内部通信経路をゲートウェイが割り当てられないことを示す。通常はゲートウェイの設定ミスまたは過負荷を意味する。 |
| 0x0B | ゲートウェイターゲットデバイス応答失敗 | ゲートウェイで使用され、ターゲットデバイスから応答が得られなかったことを示す。通常はデバイスがネットワーク上に存在しないことを意味する。 |
TCP シリーズ
要するに Modbus TCP は、RTU に比べると先頭のメッセージヘッダーを追加しただけで、CRC チェックを省いたプロトコルだ👍。TCP層のリンク伝送はすでに安定しており、シリアル伝送は安定していないからだ
メッセージヘッダー内のトランザクションIDは、マスター(TCPクライアント)が要求を発行するたびに増えていく。スレーブ(TCPサーバー)は応答を返すとき同じトランザクションIDを返すため、どの要求に対する応答なのかが分かる。だから Modbus TCP は複数フレームの連続送信が当然できて、マスターが応答を取り違えることもない👍。
個人的には、こういう流れるような通信プロトコルのほうが好きだ。||情報の正確性は物理リンク層に任せればいい……なんてね、やっぱりチェックサムは必要だ||
(0x01) コイルの読み出し
マスター要求
| トランザクションID | プロトコルID | バイト長 | アドレス部 | 機能コード | 開始アドレス | コイル数 |
|---|---|---|---|---|---|---|
| 2バイト | 2バイト(すべて0) | 2バイト(後続バイトの長さ) | 1バイト | 1バイト | 2バイト | 2バイト |
スレーブ応答
| トランザクションID | プロトコルID | バイト長 | アドレス部 | 機能コード | バイト数 | コイル状態 |
|---|---|---|---|---|---|---|
| 2バイト | 2バイト(すべて0) | 2バイト(後続バイトの長さ) | 1バイト | 1バイト | 1バイト(コイル状態部分のバイト数) | nバイト |
例
例として、アドレス20〜38のコイルデータの読み出しを要求する。応答はアドレスの低い順に並び、最後のバイトが足りない場合は上位ビットを0で埋める。
最終的な出力状態 38-36 の応答バイトでは、残り5ビットを0で埋める(上位端まで)。
| 要求 | 応答 | ||
|---|---|---|---|
| トランザクションID上位8ビット | 0x00 | トランザクションID上位8ビット | 0x00 |
| トランザクションID下位8ビット | 0x01 | トランザクションID下位8ビット | 0x01 |
| プロトコルID 16ビット(すべて0) | 0x00 0x00 | プロトコルID 16ビット(すべて0) | 0x00 0x00 |
| バイト長上位8ビット | 0x00 | バイト長上位8ビット | 0x00 |
| バイト長下位8ビット | 0x06 | バイト長下位8ビット | 0x06 |
| アドレス部(スレーブID) | 0x01 | アドレス部(スレーブID) | 0x01 |
| 機能コード | 0x01 | 機能コード | 0x01 |
| 開始アドレス上位8ビット | 0x00 | バイト数 | 0x03 |
| 開始アドレス下位8ビット | 0x13 | 出力状態 27-20 | 0xCD |
| 出力数上位8ビット | 0x00 | 出力状態 35-28 | 0x6B |
| 出力数下位8ビット | 0x13 | 出力状態 38-36 | 0x05 |
他のプロトコルについては省略する
他のプロトコルについては省略する
要するに Modbus TCP は、RTU に比べると先頭のメッセージヘッダーを追加しただけで、CRC チェックを省いたプロトコルだ👍。