異常フレーム长什么样
Modbus 的異常な応答和普通の反応只有一位之差。マスター发出リクエスト后,スレーブ把機能コード的最高位置 1,后面跟一个单バイト的異常コード。,フレーム就结束了。ないデータ区。
正常フレーム vs 異常フレーム的構造对比:
Request: 01 03 00 00 00 01 — 读 1 号站,レジスタを保持する。 0x0000
普通の反応: 01 03 02 12 34 — 02=データBytesの数,0x1234=读回的值
Exception Response: 01 83 02 — 83=0x03|0x80,02=Exception Code 0x02
上面这个例子:function code 0x03 的最高位置 1 后变成 0x83。Exception Code 0x02 代表「Illegal data address」——説明 0x0000 这アドレスはこちら。在该スレーブ里不存在。
多看一眼 CRC verification:不管普通の反応まだ異常な応答,CRC 都是駅からの住所开始算到最后一个データバイト(不含 CRC 自己)。上面我故意没写 CRC,实际フレーム末尾还有两バイト単位。。
RTU 和 TCP 的異常フレーム有区别,差异在封装层。TCP 的 MBAP 头里有一个 2 bytes的「Length」field,这个長さ包含了ユニット标识符 + function code + Exception Code。而 RTU 靠 3.5 文字静默时间来分隔フレーム。协议层不一样,但関数コードと異常コード。的语义完全一样——这是 Modbus 最干净的设计之一。
还有一个细节容易被忽略:異常な応答的フレーム長さ是固定的。对機能コード 0x01~0x06,異常な応答永远只有 3 バイト単位。(address + function code|0x80 + Exception Code),CRC 另算。对 0x0F 和 0x10 这类多操作機能コード,異常な応答也是 3 byte。スレーブ必要なし。告诉你「哪一部分出错了」,只告诉你「整个リクエスト被拒了」。这个设计很粗暴,但也很简单——マスター異常コードを受信后自己想办法重试或报错。
シリアルポート层面的时序也有讲究。Modbus RTU 规定スレーブ必须在收到リクエスト后的指定时间内开始レスポンス——但標準并ない硬性规定这个时间的具体值。实践中多数スレーブ的応答時間在 5~50ms 之间。超过 1 秒受信しなかったレスポンス(或者受信しなかった異常フレーム),マスター应该认为スレーブ応答なし。——这是タイムアウト,ではない異常コード。。很多工控软件把「No Response」和「Exception Response」混在一起报,给你一种デバイス戻る了異常コード。的错觉。实际上スレーブ根本没回任何东西。区分这两种情况对トラブルシューティング問題至关重要——没レスポンス是链路层或デバイス层的問題,異常コード。是スレーブアプリケーション層在告诉你「我受信しました但我不干」。
下面逐个拆解 7 种標準異常コード。。每个都配了真实报文和现场案例——ではない拿 Wireshark 仿真抓的,是产线、变电站、水泵房里实实在在遇到的。
0x01 — 違法な機能コード
スレーブサポートなし。マスターリクエスト的機能コード,或者スレーブ当前ステータス下该機能コード被無効了。
你给一台サポート 03/06/16 的標準 Modbus デバイス发 0x08 诊断機能コード,大概率收到 0x01。因为诊断機能コード是可选的,很多廉价 I/O モジュール根本没実装。
报文サンプル
Request: 02 08 00 00 AA 55
Exception Response: 02 88 01
0x08 = 诊断,子码 0x0000 = ループバックテスト,data 0xAA55。スレーブ直接回 0x88(0x08|0x80) + 0x01。
案例 1:台達 DVP 系列 PLC サポートなし。诊断機能コード
台達 DVP-SX2 系列,固件版本 V3.8。项目中做 RS-485 线路质量测试,用 Modbus Poll 的 Test Center 发 0x08 诊断,结果例外コードを返す 0x01。翻了 DVP 的手册確認:DVP 只実装了 01/03/05/06/0F/10 六个機能コード,0x08 不在其列。想要诊断线路质量只能用 0x01 Reading coil或者 0x03 读レジスタ,経由応答時間和连续成功率间接判断。这个事情告诉我们,诊断機能コード虽然写进了 Modbus 標準,但ではないすべての厂商都买账。
案例 2:シーメンス S7-1200 做 Modbus Server 时サポートなし。リードコイルを読む
シーメンス S7-1200 从 TIA Portal V14 开始サポート Modbus TCP Server。但它的実装只是把 DB 块マッピング到レジスタを保持する。(0x03/0x06/0x10),コイル 0x01/0x05/0x0F 压根没做。跟トップマシンデバッグ的时候,SCADA 用 0x01 去读デバイスステータス字,S7-1200 直接甩回一个 0x81 + 0x01。解決办法:把コイルアドレスマッピング到レジスタを保持する。区段,トップマシン改用 0x03 读。
案例 3:周波数変換器运行时禁止写参数
インバーター MD500 周波数変換器,実行状態の状態下尝试用 0x06 写频率给定レジスタ 0x2000。戻る 0x01。ではない周波数変換器サポートなし。 0x06 function code,而是当前ステータス不允许写。インバーター的手册里管这叫「运行禁止写入」,但协议层用的就是標準異常コード。 0x01。这种语义扩展在很多国产デバイス上都能见到——用標準異常コード。表达厂商自定义的限制条件。
案例 4:Modbus Plus 特有的機能コード在 TCP ゲートウェイ上被拒
一台老旧的 Modicon Quantum PLC,経由 BM85 网桥做 Modbus Plus to Modbus TCP 的转换。Modbus Plus 有自己的一套扩展機能コード(0x14~0x18 用于网络管理),但 BM85 网桥只透传標準 Modbus function code。トップマシン不小心发了 0x14 读 PLC 统计信息的リクエスト,网桥在アプリケーション層チェック后直接戻る 0x81+0x01。注意这里例外コードを返す的是ゲートウェイ,ではない PLC——PLC 可能サポート 0x14,但ゲートウェイサポートなし。。网桥日志里ない这个機能コード的解析路由,直接拒绝了。ゲートウェイ做機能コードセキュリティ过滤的情况在实际项目里很常见,尤其是有ファイアウォール功能的工业ルータの設置。
0x02 — Illegal data address
スレーブサポート该機能コード,但リクエスト的開始アドレス + 数量超出了スレーブ的有效アドレス範囲。这是オンサイトデバッグ碰到最多的異常コード。,ない之一。
关键点:マスター软件里填的アドレス和协议层实际送信的アドレス有 offset。Modbus protocol规定,アドレス从 0 开始编号。你在 Modbus Poll 里填的 40001(1-based レジスタを保持する。),协议层发的是 0x0000。アドレス計算逻辑弄反了,轻则读到エラーデータ,重则触发 0x02。
PLC/SCADA アドレス表示: 40001 (1-based, レジスタを保持する。)
协议层 PDU address: 0x0000 (0-based)
报文サンプル
Request: 01 03 00 64 00 0A — 读 0x0064 = 400101,读 10 个
Exception Response: 01 83 02
スレーブ的有效レジスタを保持する。範囲是 40001~40090(即 PDU address 0x0000~0x0059),リクエスト的開始アドレス 0x0064 超了,直接 0x02。
案例 1:Schneider TM3 扩展モジュールアドレス不够
Schneider Modicon M221 挂了两块 TM3 扩展。設定里第一块 TM3DI16 占 %IW0~%IW1,第二块 TM3AI4 占 %IW2~%IW5。トップマシン SCADA 用 0x04(read input registers)去读 %IW10,スレーブ戻る 0x02。原因很简单——M221 只分配了 %IW0~%IW5,%IW10 不在物理 I/O マッピング里。SoMachine 里看一眼 I/O マッピング表就明白了。这种問題属于設定和实际硬件不マッチング,ではない协议缺陷。
案例 2:Modbus Poll address 40001 和 400001 的坑
一个新手エンジニア用 Modbus Poll 点表里抄的アドレス是 40001,Modbus Poll 实际发的是 0x0000,读到的是デバイス第一レジスタを保持する。,没問題。但后来换了另一个国产组态ソフトウェア,那个软件把 40001 解析成了 4000 + 1 的オフセットの量,实际发的是 0x0FA0。スレーブ根本ない那么多レジスタ,戻る 0x02。同一份点表、同一个スレーブ,不同的マスター软件アドレス约定不一样,结果就不一样。这ではない Modbus protocol的問題,是工具链各玩各的约定导致的。建议直接使用する。 PDU address(0-based)跟同事沟通,别说什么 4 区 5 区,那都是 Modicon 的老黄历。
案例 3:複数読み込み1つのレジスタ时末尾アドレス越界
一台 Modbus 電気メーター,レジスタを保持する。 0x0000~0x003F(64 Registers)。マスターリクエスト读 0x0038 起 10 Registers(0x0038~0x0041)。スレーブチェック:最后一アドレスはこちら。 0x0038 + 9 = 0x0041 > 0x003F,直接扔 0x02。很多スレーブ在做アドレス合法性チェック时,会先算起始 + 数量是否在範囲内,只要任何一アドレスはこちら。越界就整包拒绝。ではない一1つのレジスタ越界戻る部分データ——Modbus 標準ない部分レスポンス的概念。
案例 4:ショップ型入力和コイルアドレス混淆
这个坑在用了 Modicon 老式 5 ビット·アドレス标注的公司里特别常见。1xxxx 是ショップ型入力(読み取り専用位),0xxxx 是コイル(読み書き位)。トップマシン组态里の設定了アドレス 10001 来读デバイスステータス,但实际该スレーブ把デバイスステータス放在了コイル区(0xxxx 对应 0x01 function code)。トップマシン用 0x02(read discrete inputs)去リクエストアドレス 0x0000,スレーブ里 0x0000 在ショップ型入力区确实不存在——ショップ型入力总共有 8 路(address 0x0000~0x0007)但那 8 路已经分配给了其他 DI。スレーブ戻る 0x02。换个機能コード或者换アドレスはこちら。都能解決,但前提是你得知道スレーブ的アドレスマッピングテーブル长什么样。不少工控エンジニア直接把すべての离散信号全マッピング到コイル区或全マッピング到レジスタを保持する。里,省得用户搞混——但前提是你手里的スレーブ允许你这么做。
0x03 — Illegal data value
アドレス有效、機能コード有效,但リクエスト体中携带的データ值不在允许範囲内。
这是一个容易被忽略的異常コード。。很多时候你会先怀疑アドレス設定错了,但实际是写的值越界了。
报文サンプル
Request: 01 06 00 10 FF FF — write single register,address 0x0010,值 0xFFFF
Exception Response: 01 86 03 — function code 0x06|0x80 = 0x86,Exception Code 0x03
なぜか。 0xFFFF(65535)非法?因为那1つのレジスタ是 0~10000 的工程值範囲。65535 在 16 位表示上是合法的,但在业务逻辑上不合法。
案例 1:写 EEPROM 参数超出設定範囲
オムロン E5CC サーモスタット,register 0x0103(PID 比例带)。该参数的有效範囲是 0.1~999.9,以 0.1°C 为単位存储,即レジスタの値 1~9999。トップマシン下发 0x0000(即 0.0°C),スレーブチェック后发现值小于下限 1,戻る 0x03。手册上写了範囲,但トップマシン代码里偷懒没做边界チェック。调了半小时才发现是写值超範囲——这种低级エラー每个人都会犯,重要的是记住先翻手册再看異常コード。。
案例 2:对読み取り専用レジスタ执行書き込み操作。
ABB ACS580 周波数変換器,register 0x2104(实际转速反馈,読み取り専用)。トップマシン试图用 0x06 写入 1500(想手动给转速值),スレーブ戻る 0x03。0x2104 这アドレスはこちら。本身存在,function code 0x06 也被サポート,但该アドレス在スレーブ内部被标记为読み取り専用属性。「Illegal data value」ここで的含义是「你往一个不接受写入的アドレス写了データ」——ではない值本身非法,是写这个动作非法。Modbus protocol specification里,0x03 的措辞是「Value is not allowed」,给了スレーブ実装自由度。许多厂商就把読み取り専用保护归到 0x03 下。
案例 3:多レジスタ写 —— 部分字段值非法
用 0x10 一括書き込みき込み 5 Registers,前 4 个值没毛病,No. 5 1つのレジスタ要求值在 0~100,你写了 200。一部のスレーブ会チェックすべてデータ后才回复,5 个全合法就执行,任何一个不合法就整体拒绝戻る 0x03。一部のスレーブ则逐个チェック、发现第一个不合法就拒绝。规范没说必须按哪种方式,各家的実装不一样。建议你对自己用的デバイス做一下边界测试,心里有数。
案例 4:Modbus 電気メーター的需量复位レジスタ权限控制
シュナイダー PM800 系列パワーメーター,register 0x2F01(需量复位命令)只能写入特定的控制字组合(0x1234 或 0x5678)。トップマシン误写了 0x0001 试图清需量,仪表戻る 0x03。手册上明确写了:复位命令レジスタ的有效值是 0x1234 和 0x5678,其他任何值都触发 0x03。这个设计是为了防止误操作——毕竟某些レジスタ写错了ではない报错那么简单,是真的会改参数。
还有一个与 0x03 相关的陷阱:function code 0x06 単一レジスタの書き込み时,もし该レジスタ是 32 位值的低 16 位,只写低 16 位而高 16 位未初期化,スレーブ可能戻る 0x03。比如 AB ACS580 的参数 0x1000(32 位浮点),你必须用 0x10 一次写 2 Registers,不能拆成2つ 0x06。分开写的话スレーブ会在第一个 0x06 时就报 0x03——因为它知道这アドレスはこちら。是 32 位对齐的,不接受 16 位書き込み操作。。
0x04 — Substation equipment malfunction
スレーブ在処理リクエスト的过程中发生了不可恢复的内部エラー,导致命令无法执行。这个異常コード。告诉你「ではない你的リクエスト有問題,是我自己坏了」。
0x04 是现场最让人紧张的異常コード。。0x01~0x03 你改改設定就能解決,0x04 意味着可能要去车间里爬梯子了。
报文サンプル
Request: 03 03 00 20 00 04 — 读 4 レジスタを保持する。
Exception Response: 03 83 04
同一个リクエスト,十分前还正常戻るデータ,现在开始持续戻る 0x04。硬件故障的可能性大了。
案例 1:Modbus Sensors EEPROM 写入失敗
一台昆仑海岸 JWSK-6 温湿度トランスミッタ,Modbus RTU Interface。远程改了デバイスアドレス从 0x01 to 0x05,写 EEPROM 的过程中供电抖动了一下。写入失敗,デバイス固件检测到 EEPROM checksum 不マッチング,进入了 fail-safe モード。之后すべての機能コードリクエスト(不管 0x03 读まだ 0x06 写)すべて戻る 0x04。查了手册確認:该センサーは在 EEPROM 自检失敗后会锁死通信,只能物理電源オフと再起動恢复出厂設定。这属于固件保护机制——比让あなたは読んだ。エラー值强。
案例 2:サーモスタット探头ショート导致 AD 转换異常
RKC CB100 サーモスタット,热电偶入力端因为配線端子进水ショートショートショート。。AD 转换芯片读到的是溢出值,固件判定センサーは故障,然后すべての 0x03 读 PV 值的リクエスト都戻る 0x04。这个 0x04 ではない Modbus 通信芯片坏,是センサーは前端故障経由固件的異常传递机制反映到了协议层。换了个热电偶、擦干端子,0x04 消失了。
案例 3:扩展 I/O モジュール掉线
Siemens ET200SP 経由接口モジュール做 Modbus TCP Server。一个 DI モジュール因为背板连接器接触不良掉线了。对于掉线モジュール的レジスター·アドレス,スレーブ统一戻る 0x04。但其他正常モジュール的レジスター·アドレス読み取り正常。这説明スレーブ内部的故障絶縁做得好——坏一块不影响全局。
案例 4:模拟量入力モジュール过压保护触发
研华 ADAM-4117 模拟量入力モジュール,量程設定 0~5V。某个通道意外接入了 12V 信号,モジュール内过压保护电路启动,该通道进入故障ステータス。すべての 0x04 读该通道值的リクエスト戻る 0x04,設定文件表示エラーフラグ位置位。拔掉过压信号、重新上电后恢复。这个 0x04 的根因在物理配線,不在通信プロトコル。トラブルシューティング时先把线甩了,再用仿真信号测——不过我记得 ADAM-4117 有时要软复位,单靠重新上电不够。
0x05 — Confirm(ACK)
0x05 ではないエラー。它是スレーブ对マスター说:「受信しました,正在做,你先别催。」
標準措辞是「Acknowledge」。スレーブ已经接受了リクエスト,但処理需要较长时间(比如写 Flash、执行自整定),先回一个 0x05 让マスター知道连接没断,后续処理完成后再経由普通の反応或ステータス位告知结果。
这是 7 个標準異常コード。里最特殊的一个——它是唯一不表示問題的異常コード。。
报文サンプル
Request: 01 06 0F A0 00 00 — 写レジスタ 0x0FA0 = 0x0000,触发参数保存
Exception Response: 01 86 05
案例 1:周波数変換器参数写入 EEPROM
台達 VFD-M 周波数変換器,写 0x2000(命令レジスタ)= 0x0010 触发「参数写入 EEPROM」Operation。EEPROM 擦写周期在 10~30ms 左右。スレーブ收到命令后先回 0x86 + 0x05,表示「知道你要保存参数,正在写 Flash」。大约 20ms 后可以再次ポーリング命令レジスタ,读到 0x0000 表示写入完成。もし在 0x05 之后 50ms 内ない完成,一部の周波数変換器会タイムアウト回退。
案例 2:温度コントローラー PID 自整定
オムロン E5CC,写 0x0101(AT 执行/Stop)= 0x0001 启动自整定。这个操作需要几分。E5CC 先回 0x05,然后自整定过程中可以正常読み取り PV 和 SV 值,只是 AT 标志位为 ON。自整定完成后,AT 自动 OFF。トップマシン需要在收到 0x05 后启动タイムアウト计时器,不要一直在那死等——一部のデバイス的自整定能跑 30 分。
案例 3:ゲートウェイ后端的スレーブレスポンス慢
Modbus TCP 转 RTU ゲートウェイ(比如 MOXA MGate MB3170),マスター経由ゲートウェイ读一个 9600bps 低速バス上的スレーブ。マスター发了读 125 1つのレジスタ的リクエスト,ゲートウェイ把这个リクエスト转发到 RTU バス,但因为データの量大、ボーレート低,RTU スレーブ需要 200ms+ 才能回完データ。ゲートウェイ在收到完全 RTU レスポンス之前,先给 TCP マスター回一个 0x05 占位——这就是ゲートウェイ常见的「pending」処理方式。Modbus TCP 规范里其实ない明确规定 0x05 在这种场景下的用法,但很多ゲートウェイ厂商就这么干了。
一个需要注意的设计细节:0x05 触发后,マスター侧的待機中策略。不应立即重发同样的リクエスト——这会让スレーブ收到重复命令。正确的做法是:ポーリング一个ステータスレジスタ(もしスレーブ提供了)或者设立タイムアウト计时器,タイムアウト后发新的查询命令。一部の SCADA 驱动(比如 Kepware 的 Modbus 驱动)内置了 0x05 的処理逻辑,一部の则直接报タイムアウト。もし你自己写 Modbus 驱动,0x05 的処理逻辑是必做项。
0x06 — 駅から忙しい。
スレーブ现在太忙了,処理不了你的リクエスト。命令被接受了但无法执行,マスター应该稍后重试。
0x05 和 0x06 違いはありません:0x05 是「受信しました,在処理,等结果」;0x06 是「现在没空,你过会儿再来」。0x05 意味着スレーブ已经开始执行命令了,0x06 意味着スレーブ根本没开始执行。
报文サンプル
Request: 01 06 20 00 07 D0 — 写レジスタ 0x2000 = 0x07D0(2000)
Exception Response: 01 86 06 — 忙,没空処理
案例 1:写完参数立即读
写入一个需要写 EEPROM 的参数后,10ms 内立即发下一个リクエスト。slave Flash コントローラーは还没释放バス,直接回 0x06。很多エンジニア在スクリプトのデバッグ里几毫秒一个リクエスト连续发,スレーブ根本来不及処理。ではないデバイス慢,是你太快了。Modbus ないフロー制御机制,マスター需要自己控制节奏——写完 Flash 相关レジスタ后,留 30~50ms 再发下一个読み書きリクエスト。
案例 2:高速ポーリング导致低速デバイスバッファオーバーフロー
用 Modbus Poll 以 20ms 間隔ポーリング一台 9600bps 的 RTU Temperature & Humidity Sensor。刚启动时正常,运行一分后开始断続的な出现 0x06。9600bps 传输一バイト単位。约 1ms,一个最小读リクエスト+レスポンス来回大概 30~40ms。20ms 的ポーリング間隔已经小于デバイス的最小レスポンス周期。スレーブシリアルポート受信バッファ。被塞满了,它在忙着丢弃溢出的フレーム,没精力処理新リクエスト。把ポーリング間隔调到 100ms 就好了。换成 115200bps 能进一步缩短,但很多工控デバイス最高只サポート到 38400bps 甚至 19200bps。
案例 3:多マスター同时アクセス一个スレーブ
一个 RS-485 バス上的 Modbus RTU slave,同时接了 PLC 和トップマシン SCADA 2つマスター。2つマスター之间ない协调,各自以 500ms 和 300ms 間隔ポーリング。碰撞概率上来了——2つリクエストフレーム重叠,スレーブ收到的是乱码。一部のスレーブ在检测到フレームエラー后会忽略,一部のスレーブ会消耗时间做エラー処理,导致刚クリア受信バッファ。时下一个マスター的フレーム就到了。还没準備して受信新フレーム,只能回 0x06。RS-485 多マスター的冲突処理,建议上硬件フロー制御或在マスター侧做互斥锁,别指望スレーブ自己Got it.
案例 4:スレーブ電気の初期化期间收到リクエスト
主に。 Modbus スレーブ上电后有几十毫秒到几秒的初期化时间。在这个窗口里发リクエスト,スレーブ的 Modbus protocol栈还没準備して。一部のスレーブ直接不回(No Response),一部のスレーブ回 0x06。这个行为跟具体的固件実装有关。丹佛斯 VLT FC302 周波数変換器上电后大约 2 秒内会戻る 0x06,初期化完了。后恢复正常。もし你的 SCADA 启机就去駅から読む。,头几个リクエスト可能收到 0x06——在トップマシン代码里加个上电延迟或者对 0x06 做自動再試行(間隔 500ms,最多 3 次)就能规避。
0x06 和 0x05 很容易搞混,这里说清楚:0x05 是「処理中です。你的リクエスト」,処理完了会回正常データ。0x06 是「没空処理你的リクエスト,リクエスト被丢弃了」。マスター收到 0x05 应该待機中,收到 0x06 应该重试。もし把 0x06 当成 0x05 処理——等着等着就タイムアウト了。
0x0A — Gateway path unavailable
这个異常コード。のみゲートウェイ场景出现。ゲートウェイ和下游スレーブ之间的通信出了問題——ゲートウェイ本身没問題,但它后面的デバイス连不上了。
Modbus 標準定义的措辞是「Gateway Path Unavailable」——无法建立到目标デバイス的路径。这里的目标デバイスではないアドレス 0x01 的スレーブ本体,而是ゲートウェイ内部的「路由」到下游スレーブ的路径。
报文サンプル
Request: 01 03 00 00 00 01 — 経由ゲートウェイ读下游スレーブ
Exception Response: 01 83 0A — ゲートウェイ:后面那个スレーブ没レスポンス
案例 1:シリアルサーバー后端的スレーブ掉线
USR-N510 シリアルサーバー做 Modbus TCP 转 RTU ゲートウェイ,下面挂了 3 台 Modbus RTU Sensors(address 0x01、0x02、0x03)。0x02 スレーブ因为电源モジュール烧了,完全掉线。マスター経由ゲートウェイ读 0x02 スレーブ时,シリアルサーバー尝试在 RS-485 バス上广播リクエスト,等 500ms タイムアウト受信しなかったレスポンス,然后在 TCP 侧给マスター回了 0x0A。0x01 和 0x03 スレーブ的通信完全正常——ゲートウェイ本身活着,只是某一条路径断了。
案例 2:ゲートウェイの構成了エラー的下游参数
MOXA MGate MB3170,設定下游为 9600bps、8N1。但实际 RS-485 バス上的デバイス是 19200bps、8E1(even parity)。ゲートウェイ按照 9600bps 发リクエストフレーム,スレーブ收到的是乱码,不レスポンス。ゲートウェイタイムアウト后戻る 0x0A。这种問題在有多家的デバイス混装的 RS-485 バス上尤其常见——不同厂商的デフォルト通信パラメータ不一样,项目初期不统一调一遍就会踩坑。
案例 3:Modbus TCP 级联ゲートウェイ
一个大型分布式场景:中心 SCADA → Modbus TCP 主ゲートウェイ → 光纤环网 → 就地子ゲートウェイ → RS-485 slave。主ゲートウェイ到子ゲートウェイ之间的光纤断了,主ゲートウェイ对子ゲートウェイ下面すべての駅から。的リクエスト都戻る 0x0A。这时候トラブルシューティング思路是逐级 ping:先確認主ゲートウェイ到子ゲートウェイ的 TCP connect,再確認子ゲートウェイ到 RS-485 バス,最后才是シリアルデバイス本身。
厂商自定义異常コード。
除 7 种標準異常コード。,很多厂商在 0x80~0xFF 区段定义了私有異常コード。。这ではない违反標準——Modbus 规范允许厂商扩展。但你的トップマシン代码もし只认识 01~0A,碰到 0x90 就会报「Unknown Exception」或者直接丢フレーム。
一些常见厂商扩展:
| 厂商 | 自定义異常コード。 | 含义 |
|---|---|---|
| シーメンス S7-1200/1500 | 0x80 | Modbus Server DB 块未初期化 |
| インバーター AM600 | 0x81 | 参数锁定(需要先写解锁レジスタ) |
| 台達 AS 系列 | 0x8B | 機能コード在当前 PLC 运行モード下被禁止 |
| 部分国产サーモスタット | 0x90~0x9F | 参数検証の失敗(写值与 EEPROM 不符) |
| Schneider M221 | 0xF0 | 固件サポートなし。的レジスタエリア |
这些異常コード。ない统一標準,得翻各家手册。一部の厂商在手冊的 Modbus 通信章节会列出完全的異常コード。表,一部の藏在附录里,还一部の干脆不写——你只能 debug 抓到之后凭经验判断。
有个坑:部分国产 PLC 在遇到 0x02 条件时也会回 0x03(不区分違法な住所。和不正なデータ値),因为它们内部的異常処理把「アドレス不存在」和「值不合法」归为同一个エラー路由。你按標準预期是 0x02,实际收到 0x03——别较真,对着手册的レジスタマッピングテーブル查就是了。
还有一个容易被忽略的:CPU 停机モード下的異常コード。行为。很多 PLC 在 STOP ステータス下仍然运行 Modbus protocol栈,但行为不同。シーメンス S7-1200 做 Modbus Server 时,CPU 从 RUN 切换到 STOP,读到已マッピング的 DB 块戻る 0x04(「機器の故障」),而ではない 0x01。这个语义很微妙——PLC 没坏,但在 STOP モード下 DB データ不可用。シュナイダー M221 在 STOP ステータス下则仍然レスポンス Modbus Request,只是データ不更新。三菱 FX5U 做 Modbus Server 时,STOP ステータス下直接不レスポンス任何リクエスト,トップマシン看到的是タイムアウト,ではない異常コード。。同一套 SCADA 系统要适配不同ブランド PLC 的 STOP 行为,驱动层面就得做差异化処理。
厂商扩展異常コード。还经常被用于セキュリティ场景。比如某些サポート Modbus Security(基于 TLS)的ゲートウェイデバイス,会在认证失敗时戻る自定义異常コード。 0xE0~0xEF 表示セキュリティ层拒绝。这ではない Modbus 標準定义的,但确实存在。もし你的 Modbus TCP 通信突然开始收到 0xE1,ではないプロトコルスタック解析错了,是ゲートウェイ在告诉你「TLS 握手没过」。
用 Wireshark 抓異常フレーム
不管 RTU まだ TCP,Wireshark 都能直接解析 Modbus protocol。前提是你的抓包点在正确的网络位置。
TCP Scenarios
Modbus TCP 走 502 ポート,Wireshark デフォルト就解析。在过滤器栏入力 `modbus`,異常フレーム会表示为红色背景。
展开異常な応答フレーム,看这几个关键字段:
Modbus/TCP
Transaction Identifier: 1
Protocol Identifier: 0
Length: 3 ← 注意这个長さ
Unit Identifier: 1
Modbus
Function Code: 131 (0x83) ← 83 = 03 | 0x80,Wireshark 直接表示了
Exception Code: 2 (Illegal Data Address)
`Length: 3` 是 MBAP 头里の長ささ字段,它 = Unit Identifier(1) + Function Code(1) + Exception Code(1) = 3 byte。普通の反応这个值会更大(因为后面有データ)。只看長さ就能初步判断は、異常な応答まだ普通の反応——異常な応答的 TCP 载荷通常只有 3 byte。
RTU Scenarios
RTU 抓包需要用 RS-485 转 USB 変換器です。搭一个监听节点。Wireshark 对 RTU 的サポート不如 TCP 好,需要手動指定ポート設定(Baud rate、チェック等)。抓到的異常フレーム形式和 TCP 类似,但ない MBAP 头:
01 83 02 xx xx — address 01,function code 83,Exception Code 02,CRC xx xx
RTU 抓包里ない Transaction ID,你只能靠时间戳来判断リクエスト和レスポンス的对应关系。デバッグ复杂的多スレーブ RTU バス时,建议在 Wireshark 里按 Modbus アドレス过滤,把每个スレーブ的トラフィック分开看。
Wireshark 的 Modbus 解析器从 3.x 版本开始サポート異常コード。的中文表示。在 Preferences → Protocols → Modbus 里勾选 Exception 的解析选项。しかし注意点:Wireshark 只能解析標準異常コード。 01~0A,厂商自定义码会表示数字而非説明文本。
还有一个 Wireshark 小技巧:用 `modbus.exception_code` 做表示过滤器。`modbus.exception_code == 2` 过滤出すべての 0x02 異常フレーム。`modbus.exception_code >= 1 && modbus.exception_code <= 10` 过滤すべての標準異常。在现场トラブルシューティング时,先看全局统计——Statistics → Protocol Hierarchy → Modbus,能看到異常フレーム占通信总量的百分比。もし異常フレーム超过 10%,你的設定大概率有問題。もし只有偶发的 0x06,那是时序問題。もし持续 0x04,准备换デバイス。
Modbus Poll 里的異常提示
Modbus Poll 是最もよく使われるデバッグツール。它的異常信息直接表示在窗口底部ステータス栏,长这样:
Modbus Exception Response
Function: 3, Exception: 2 (Illegal Data Address)
第一眼看到红字别慌,先看 Function What is it,再看 Exception What is it。機能コード告诉你哪个操作报错了,異常コード。告诉你なぜか。。
open Display → Communication,能看到完全的收发报文。異常フレーム用红色标记,点开看元の十六进制:
Tx: 01 03 00 00 00 01 84 0A
Rx: 01 83 02 C0 F1
`Rx` 的第二byteは 0x83(=0x03|0x80),第三バイト 0x02 就是異常コード。。`C0 F1` 是 CRC。Modbus Poll 帮你解析出来了,但学会自己看元のフレーム是一项基本功——上线部署后你不会永远有 Modbus Poll 可以用。
同一个リクエスト连续报 0x02,先别改アドレス,先確認你的開始アドレス和数量是否在スレーブ手册列出的有效範囲内。很多时候是アドレス計算逻辑错了,ではない你アドレス写错了。尤其是从 1-based レジスター·アドレス切换到 0-based PDU アドレス的时候——这个坑基本每个新手都要踩一次。
デバッグ方法论:異常コードを受信后的トラブルシューティング清单
现场出了異常コード。,按下面这个顺序走——ではない標準操作流程,是老エンジニア的血泪经验。
**最初のステップ:確認是哪个スレーブ、哪个機能コード、哪个異常コード。**
因为现场通常不止一台スレーブ。用 Modbus Poll 或 Wireshark 抓元の报文,拿到駅からの住所 + function code + 異常コード。这三个数。もし你是用脚本在测,加一行 `print(hex(response[0]), hex(response[1]), hex(response[2]))`,别只看日志里的「通信失敗」。
**第二のステップ:異常コード。カテゴリ——软件原因まだ硬件原因?**
0x01 / 0x02 / 0x03 大概率是你的リクエスト有問題。查設定、查アドレスマッピング、查データ範囲。0x04 / 0x0A 大概率是スレーブ或链路有問題。先電源オフと再起動スレーブ试试——もしそうなら 0x04 变正常了,説明是デバイス内部故障恢复,但不代表根本的な原因已解決。0x06 是时序問題,调慢ポーリング間隔。
**第三のステップ:絶縁变量**
同一台マスター,换一个駅からの住所试试。通信可能信 → 問題在原スレーブ。不通信可能信 → 問題在マスター端或バス。同一个駅からの住所,用 Modbus Poll 试试。通信可能信 → 你的トップマシン代码有問題。不通信可能信 → スレーブ或链路有問題。这是最も一般的な的二分トラブルシューティング法,但很多人跳过了这一步,直接怀疑硬件坏了。
**第四のステップ:看手册**
異常コード。出来了,翻スレーブ手册的 Modbus 通信章节。一部の厂商会列出すべての可能戻る的異常コード。和触发条件。もし手册ない異常コード。表,找アドレスマッピングテーブル,手动对照你的リクエストアドレスが有効な範囲内かどうか。很多现场的「通信故障」其实是你读了一个保留アドレス或者只アドレスを書く。。
**5番目のステップ:抓包**
用 Wireshark 或者シリアルポートモニタリング工具抓元の报文。看的是:マスター到底发出了什么?スレーブ到底回复了什么?有ない CRC Error?有ないフレーム不完全?很多时候你看日志以为发出去了 01 03 00 00 00 01,实际抓包发现ボーレート错了スレーブ当噪音扔了。不要相信你的代码日志,要相信抓包工具。
**6番目のステップ:替换测试**
换一台同型号的新スレーブ接上去,同样的リクエスト結果を見る。。もし新デバイス正常,原デバイス返異常コード。——デバイス硬件故障。もし新デバイス也返同样的異常コード。——你的リクエスト有問題。もし手头ない备用デバイス,换一个确定能正常工作駅からの住所来测通信链路。链路的嫌疑排除了,問題就在特定デバイス上。
**第七步:もしまだ搞不定**
把元の报文(hexadecimal)、スレーブ型号和固件版本、你的マスタープラットフォーム信息发给スレーブ厂商的 FAE。别发日志截图,发元の报文——FAE 看元の报文比看你的説明准得多。もし FAE 也不回,去 modbus.cn 论坛贴元の报文求助。社区的デバッグ经验有时比厂商手册还靠谱,因为手册是理论,社区是血泪。
**补充:自己写 Modbus 驱动时的異常コード。処理模板**
もし你在写トップマシン的 Modbus マスター驱动(用 C/Python/Node.js 等),異常フレーム処理至少要カバー这几种情况:
1. チェックレスポンス的第一个byteは否等于リクエスト駅からの住所(アドレス不Match = ではない给你的レスポンス,丢弃) 2. 機能コード的 bit 7 是否为 1(为 1 则提取異常コード。,为 0 则正常解析データ区) 3. Exception Code 0x05 特殊処理:不抛出異常,进入待機中ステータス,ポーリングステータスレジスタ或タイムアウト重发 4. Exception Code 0x06 特殊処理:延迟 50~100ms 后重试,最多重试 3 次 5. 其他異常コード。:记录日志、通知上位层进行エラー処理(告警、重试、切换备用链路等) 6. 厂商自定义码 0x80~0xFF:需要デバイス型号相关的字典来解析,否则按 Unknown Exception 処理
エラー的做法是把すべての異常コード。都当作「通信失敗」然后重试——0x02 重试一万次也是 0x02,アドレス错了就是错了。正确的做法是根据異常コード。カテゴリ采取不同策略。这部分逻辑写好了,你的驱动就能适配市面 90% 的 Modbus スレーブデバイス。剩下的 10% 是那些不按標準例外コードを返す的厂商——它们用普通の反応替代異常な応答,把エラー信息塞在レジスタのデータ里。遇到这种デバイス只能翻专用手册。
異常なスピードチェック表
| Exception Code | Name | 一句话解释 |
|---|---|---|
| 0x01 | 違法な機能コード | スレーブサポートなし。你发的機能コード,或当前ステータス不允许 |
| 0x02 | Illegal data address | リクエスト的アドレス在スレーブ的レジスタマップ里不存在 |
| 0x03 | Illegal data value | アドレス存在但写的許容範囲外の値或写了読み取り専用レジスタ |
| 0x04 | Substation equipment malfunction | スレーブ内部硬件或固件出了不可恢复的错 |
| 0x05 | Confirm (ACK) | ではないエラー,スレーブ在処理长耗时操作,等着 |
| 0x06 | 駅から忙しい。 | スレーブ现在没空処理,稍后重试 |
| 0x0A | Gateway path unavailable | ゲートウェイ到下游スレーブ通信不可,或設定不マッチング |
| 0x80~FF | 厂商自定义 | 翻各自的手册,语义通信不可用 |
这张表建议打印出来贴在デバッグ电脑旁边。现场出異常コード。不用掏手机搜,看一眼表就知道大概方向。具体的诊断方法,回到上面每个異常コード。的案例里找——すべての案例都是现场碰过的,ではない编的。
Leave a Reply