← 所有文章

FIELD NOTES / 工業通訊與網路

二進位協定的大小端如何用已知封包驗證

工業通訊與網路作者:站長預估 5 分鐘閱讀

用0x1234與0x12345678逐層核對byte順序、跨word順序與型別,避免以倍率修補端序錯誤。

本文目錄

先取得已知值與原始位元組

通訊已有回應,讀回數值卻巨大或忽正忽負,先不要修改倍率。先取得設備文件、封包中的原始位元組與一組可確認的已知值,再驗證端序。本篇從十六位整數進到跨兩個暫存器的三十二位資料,讓你知道每次交換究竟改了什麼。

大端表示多位元組數值的高位元組先出現,小端則是低位元組先出現。這是資料格式的約定,不是網路品質好壞,也不是畫面左邊的數字一定比較重要。先定義偏移、長度、型別、端序和單位,再解碼同一段位元組。

準備一個隔離的測試來源或設備核准的唯讀診斷值。本例選0x1234作十六位已知值,因為兩個位元組不同;若只用0x0000、0xFFFF或0x1111,交換後看起來一樣,無法分辨設定是否正確。不要為了測端序任意改生產設定。

紀錄欄位至少包含來源、請求識別、封包方向、資料偏移、原始十六進位、型別與解碼規則版本。原始資料保留不變,解碼結果另列。若測試工具先自動換過字序,還要另取得未解碼視圖,避免把已轉換的資料再次交換。

適用於可取得原始資料的二進位協定。TCP提供位元組串流,不替應用定義整數端序;串列協定也各有規格。PLC記憶體內部排列、網路線上排列與工具畫面的顯示格式不能直接畫上等號,應逐層對照。

十六位元先驗證一個word

假設來源值是0x1234,也就是十進位4660。若線上資料依大端傳送,依序為12、34;重建值為0x12乘256加0x34,得到18乘256加52,結果4660。請同時寫出十六進位與十進位,避免把12誤當十進位十二。

同樣的兩個位元組12、34,若用小端重建,會得到0x3412,也就是13330。這不是倍率錯誤;兩個值的比例沒有通用縮放意義。不能用4660除以13330當修正係數,因為下一個輸入的誤差比例就會改變。

換一個已知值0x0102,正確大端重建是258,小端誤讀是513。兩個不同已知值都符合相同端序,才比單點更有說服力。再測0x0001和0x0100,可看出哪個位元組對應低八位,並確認沒有讀錯偏移。

Modbus應用規格定義地址與資料項目使用高位元組先傳。十六位暫存器0x1234的兩個資料byte應為12、34。這不代表設備自訂的三十二位Float跨兩個暫存器排列也已被同一條規則完整定義;跨word還要看設備資料表。

本步完成後應能以兩組已知值證明每個word內的byte順序。如果資料偏移抓錯一個byte,即使輪流試大端小端也可能都不對。先核對長度、功能碼和回覆資料起點,再判斷是否真的需要交換。

三十二位再分byte順序與word順序

以0x12345678為三十二位已知樣式,把四個byte命名為A=12、B=34、C=56、D=78。文件若指定高word先、每word高byte先,線上排列是ABCD,也就是12 34 56 78。這個例子的四個byte皆不同,可同時辨識多種排列。

若只交換兩個十六位word,排列變CDAB,也就是56 78 12 34;若只交換每個word內兩個byte,變BADC,也就是34 12 78 56;若兩種都交換,則為DCBA,也就是78 56 34 12。把四種寫成映射,不要只寫模糊的「反轉」。

正確解成無號三十二位整數時,0x12345678是305419896。驗收以原始byte重組回這個位元樣式,再按型別解讀;不要挑一個落在工程範圍內的結果就宣布正確。多種錯誤排列也可能碰巧產生看似合理的數值。

若資料實際是IEEE binary32浮點,1.0的樣式為0x3F800000。在ABCD排列下是3F 80 00 00;先按已知規則重組32位,再解讀浮點。不能將整數1065353216以數值轉型成浮點而期待得到1.0,數值轉換與位元重新解讀是兩件事。

用浮點作測試時,1.0含有重複零byte,辨識力有限。最好搭配文件已知的另一個非對稱樣式或設備診斷常數,確認映射。不可用沒有已知真值的製程溫度四處試排列,最後選最順眼的那個當協定規格。

有號性與其他錯誤分開排查

端序正確後才判型別。同一個0xFFFF,無號十六位是65535,有號二補數是−1;交換byte仍是FFFF,端序調整無法解決有號性錯誤。同樣地,工程倍率0.1或BCD編碼是另外的解讀規則,要有各自的來源文件。

測試來源的身份也要確認,尤其透過閘道時需區分上游封包與下游設備資料。閘道可能已完成映射;紀錄擷取位置,才能避免把兩側不同排列誤認成同一條線的格式矛盾。

跨兩個word的資料也可能在讀取途中更新,形成一半新、一半舊。這種不一致快照不是端序;若固定已知值正確、動態值偶發跳變,查來源更新原子性、讀取範圍與版本握手。不能看到跳值就再多加一次word交換。

逐層驗收:先確認資料偏移和長度,再看單word byte順序,再看跨word順序,再看有號或浮點型別,最後才套工程單位。每一步保存中間結果,可以精確指出錯誤發生在重組、型別還是倍率。

例如已知0x1234讀成13330,先查byte交換;FFFF顯示65535但預期−1,查有號性;原始253正確但工程值2.53,查重複倍率;固定資料正常、更新瞬間錯,查一致性。不同症狀應對應不同檢查,不以單一交換開關包辦。

修改映射後,用原始封包重新解碼,再核對設備唯讀結果。保存變更前後的規則版本與預期值,讓歷史資料不被新規則靜默改寫。本文的數值為算例。

完成結果與常見問題

交付的映射表應清楚寫出每個欄位的偏移、byte數、word順序、byte順序、型別與倍率,並附至少兩個已知值的原始資料及結果。若文件仍缺跨word排列,就列為待確認;不能以推測取代設備契約。

問:TCP是不是一律大端?答:TCP首部有自己的格式,應用負載的欄位排列則由應用協定規定,不能從使用TCP就推定資料端序。

問:Modbus高byte先傳,Float的兩個word一定高word先嗎?答:不能直接推定。請查設備對該三十二位資料的暫存器映射與word順序。

問:0xFFFF拿來測交換是否可靠?答:不可靠。交換後不變,應用0x1234和其他非對稱已知值,並確認有號性是另一項設定。

問:讀到合理溫度就算驗證成功嗎?答:還不夠。應以已知真值、原始byte及固定規則比對,並排除偏移、型別、倍率與更新不一致。

參考:Modbus Application Protocol V1.1b3 §4.2:地址與資料項目的byte編碼。

參考:Python struct:byte order、size與alignment的明確格式;僅作離線解碼參考。

延伸閱讀