参考資料#
各ブログサイトは章が欠けていたり、レイアウトが乱れていたり、時折誤りがあるため、参考にしないことをお勧めします。
ここに modbus 中国語サイトの資料があります
MODBUS プロトコル中国語版 / 英語版プレビューおよびダウンロード | Modbus IoT クラウドプラットフォーム
より権威のあるのは公式文書です
及び公式の他の文書は、ここで探してください
もちろん公式文書にもあまり満足していません。彼らの古代の設計構造を維持するために、各フレームセグメントの紹介が直感的でなくなっています。また、少量の誤植もあります。
以下は自分が書き直した有効な情報です。
また、私が実装したライブラリもあります
stbanana/modbusX: modbus プロトコルサポート (github.com)
プロトコルの特徴#
全体フレーム構造#
メッセージヘッダー | アドレスフィールド | 機能コード | データフィールド | チェックフィールド | |
---|---|---|---|---|---|
RTU | 1 バイト (スレーブ ID) | 1 バイト | n バイト不定 | 2 バイト (他のすべての内容の CRC-MB16) | |
TCP | 6 バイト (2 バイトトランザクション番号 + 2 バイトプロトコル識別子 - 全て 0+ 2 バイト後続バイトの総長 - スレーブ ID を含む) | 1 バイト (スレーブ ID) | 1 バイト | n バイト不定 |
メッセージヘッダーについては、公式文書の全体モデルではアドレスフィールドに分類されていますが、実際のバイトの総長にはスレーブ ID が含まれています。おそらく、初期のシリアルポートプロトコルの時代に全体モデルが定められ、新たに追加された modbusTCP は互換性のために混乱を招いています。
私はここで直接再分類しましたが、標準ではありませんが、私は喜んでいます。
レジスタ属性#
RW 属性 | bit ビット数 | |
---|---|---|
コイル (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 を埋めます。
[!NOTE]
最終出力状態 38-36 の応答バイトは、5 つの残りのビットをゼロで埋めます(高位端まで)。
リクエスト | 応答 | ||
---|---|---|---|
アドレスフィールド (スレーブ 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 を埋めます。
[!NOTE]
最終入力状態 218-213 の応答バイトは、2 つの残りのビットをゼロで埋めます(高位端まで)。
リクエスト | 応答 | ||
---|---|---|---|
アドレスフィールド (スレーブ 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 | レジスタ値高八位(108) | 0x02 |
レジスタ数量高 8 ビット | 0x00 | レジスタ値低八位(108) | 0x2B |
レジスタ数量低 8 ビット | 0x03 | レジスタ値高八位(109) | 0x00 |
チェック CRC 低 8 ビット | 0x74 | レジスタ値低八位(109) | 0x00 |
チェック CRC 高 8 ビット | 0x17 | レジスタ値高八位(110) | 0x00 |
レジスタ値低八位(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 | レジスタ値高八位(9) | 0x02 |
レジスタアドレス高 8 ビット | 0x00 | レジスタ値低八位(9) | 0x2B |
レジスタアドレス低 8 ビット | 0x03 | レジスタ値高八位(10) | 0x00 |
チェック CRC 低 8 ビット | 0xC1 | レジスタ値低八位(10) | 0x00 |
チェック CRC 高 8 ビット | 0xD7 | チェック CRC 低 8 ビット | 0xF3 |
チェック CRC 高 8 ビット | 0xF4 |
(0x05) 単一コイルを書き込む#
[!NOTE]
単一コイルを書き込む場合、出力値部分は 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 個のコイルを書き込むリクエストを送信します。
[!NOTE]
合計で 2 バイト (16bit) を書き込む必要があり、6 つの残りのビットをゼロで埋めます(高位端まで)。
コイルアドレス | 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 シリーズ#
とにかく modbusTCP は RTU に比べて前のメッセージヘッダーをラッピングしただけで、CRC チェックを削除しました👍。== なぜなら TCP 管理層のリンク伝送は非常に安定しているからで、シリアル伝送は不安定だからです ==
メッセージヘッダーのトランザクション番号は、ホスト(TCP クライアント)がリクエストを開始する際に継続的に加算され、スレーブ(TCP サーバー)がリクエストに応答する際には、処理しているリクエストを示すために同じトランザクション番号を返します。したがって、modbusTCP は生まれつきマルチフレームの連続送信をサポートしており、ホストは応答を誤解することはありません👍。
個人的には、このようなスムーズな通信プロトコルが好みです。|| 情報の正確性は物理リンク層に任せておけばよい、大雲大雲、チェックは依然として必要です ||
(0x01) コイルを読む#
ホストリクエスト#
トランザクション番号 | プロトコル識別子 | バイト総長 | アドレスフィールド | 機能コード | 開始アドレス | コイル数 |
---|---|---|---|---|---|---|
2 バイト | 2 バイト (全 0) | 2 バイト (後続バイト総長) | 1 バイト | 1 バイト | 2 バイト | 2 バイト |
スレーブ応答#
トランザクション番号 | プロトコル識別子 | バイト総長 | アドレスフィールド | 機能コード | バイト数 | コイル状態 |
---|---|---|---|---|---|---|
2 バイト | 2 バイト (全 0) | 2 バイト (後続バイト総長) | 1 バイト | 1 バイト | 1 バイト (計算コイル状態部分のバイト数) | n バイト |
例#
20〜38 アドレスのコイルデータを読み取るリクエストを送信します。とにかく、応答アドレスは低から高へ、最終バイトが不足している場合は高位に 0 を埋めます。
[!NOTE]
最終出力状態 38-36 の応答バイトは、5 つの残りのビットをゼロで埋めます(高位端まで)。
リクエスト | 応答 | ||
---|---|---|---|
トランザクション番号高 8 ビット | 0x00 | トランザクション番号高 8 ビット | 0x00 |
トランザクション番号低 8 ビット | 0x01 | トランザクション番号低 8 ビット | 0x01 |
プロトコル識別子 16 ビット (全 0) | 0x00 0x00 | プロトコル識別子 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 |
他のプロトコルについては省略します
他のプロトコルについては省略します
とにかく modbusTCP は RTU に比べて前のメッセージヘッダーをラッピングしただけで、CRC チェックを削除しました👍。
この文は Mix Space によって xLog に同期更新されました
元のリンクは https://www.yono233.cn/posts/shoot/24_7_26_modbus