← 所有文章

FIELD NOTES / 工業通訊與網路

序列ASCII框架解析_STX長度ETX與逾時重組

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

用自訂STX加三字元長度加ETX框架,練習串列分段重組、長度上限、逾時及不完整資料拒絕。

本文目錄

先寫清楚一筆訊息的邊界

串列接收不能把一次讀取當成一筆訊息。設備可能先送標頭,下一次才收到內容,也可能一次收到兩筆。本篇定義一個自訂ASCII教學框架,帶你依STX、長度及ETX重組;它不是Modbus ASCII,也不是三菱模組內建的專用格式。

本例格式為STX一byte、三個ASCII十進位長度字元、payload,最後ETX一byte。STX為02 hex,ETX為03 hex,長度只計payload bytes,允許1至128。payload僅允許20至7E hex的可列印ASCII,因此不能含02或03,也不接受換行。

內容HELLO共五bytes,完整訊息為02 30 30 35 48 45 4C 4C 4F 03,總長十bytes。三個長度字元是文字005,不是二進位數字0005;30 hex代表ASCII字元0,這是初學者最常混淆的地方。

設備採用其他長度欄位、UTF-8、逸出機制或校驗碼時,要另定框架版本,不能直接套本例。這個教學框架沒有校驗碼,僅能檢查格式,不能保證資料未損壞;正式通訊應採雙方已支援並文件化的協定。

以狀態機逐段收齊

解析器可分WaitStart、ReadLength、ReadPayload、ReadEnd四個自訂狀態。WaitStart找到02後進ReadLength;收滿三個字元才檢查每個是否0到9,轉成整數並核對1至128。不要收到第一個0就把長度當零而提早拒絕。

長度為5時,ReadPayload必須等待五個bytes,期間只把資料留在候選緩衝,不發布數值。收滿後進ReadEnd,下一個byte必須03。只有全部格式通過,才交付HELLO並回WaitStart處理後續資料。

用接收函式的測試替身依序提供02 30、30 35 48 45、4C 4C 4F 03。第一段只有部分長度,第二段有完整長度和HE,第三段才完成HELLO與ETX。預期前兩段交付零筆,第三段交付一筆;實體串列寫入次數不保證等於讀取次數。

一次讀到兩筆完整訊息時,解析迴圈在交付第一筆後繼續消費剩餘bytes。游標只前進到已處理邊界,不把整個接收buffer全部清空。若第二筆只收到STX及一個長度字元,保留它,等下一次追加。

每次診斷記錄狀態、期望長度、已收長度及交易世代。正式日誌避免無限保存payload,可保留受控長度的原始hex摘要;文字畫面可能不顯示02和03,因此排查邊界時一定要能查看bytes。

本例最大合法frame為1+3+128+1=133bytes,但接收buffer不能只按133推定足夠,因為一次讀取可能包含多筆。可逐段消費並限制未處理總量;滿載時明確回報容量事件,不能截去尾端後把前綴當完整frame。

長度不合法與逾時如何收尾

收到02 30 30 30表示長度000,本例拒絕;02 31 32 39表示129,也超過上限。若長度出現41 hex即字元A,應回LengthSyntaxError,不能把它當十六進位長度或偷偷換成零。先驗上限再配置空間,避免錯誤資料耗盡記憶體。

設定從STX開始計算的整體frame期限,例如500毫秒,並用同一本機單調時鐘量測。每收到一個byte不能無限延長總期限,否則錯誤來源每隔一段時間送一字就能永久占住解析器。必要時另加字元間期限,但兩者意義要分開。

例如0毫秒收到STX,100毫秒完成長度,300毫秒只收到HEL,500毫秒期限到。記錄FrameTimeout及expected=5、received=3,不補LO、不補零,也不交付HEL。所有候選內容先封存診斷摘要,再離開本次交易。

這個練習採保守錯誤政策:格式失敗或逾時後進入Fault,停止新交易,依雙方協定完成清理及重新同步才恢復。不能直接回WaitStart就聲稱所有晚到bytes都已隔離;晚回覆可能在新請求後到達,仍需明確回覆期限與關聯策略。

若產品另支援以STX重新同步,必須證明STX不會出現在合法payload並限制掃描與緩衝容量。本例已限制payload可列印ASCII,但重新找到STX仍只表示可能有新框架,後續長度與ETX仍須重新驗證,不能直接發布。

驗證內容與交易關聯

長度正確不代表內容可用。HELLO是文字,不是溫度;若業務格式要求TEMP=25.3,收到其他字串要在業務解析階段拒絕。先完成frame,再做ASCII解碼、欄位型別、單位與範圍檢查,不要在收到前幾個字時就更新HMI。

測試將最後03改成04,預期EndMarkerMismatch且零筆交付;將payload某byte改成80,預期PayloadNotAscii。若把HELLO中的E改成F,格式仍可能完全合法,這說明沒有校驗的框架無法單靠長度及ETX發現所有內容錯誤。

本例沒有交易識別欄位,因此不支援任意多筆同時未完成的請求。即使每次只送一筆,前次逾時後也不能立即重送並把下一個合法frame當成本次;相同格式的晚回覆可能無法分辨,需由實際協定提供時序或識別保證。

適用範圍是自訂協定解析教學。實作到Q系列、串列模組或HMI時,要先確認非程序通訊、接收buffer、終止碼、資料長度及錯誤旗標的實際支援方式。本篇不指定暫存器。

失敗排查依序查看原始hex、接收狀態、宣告長度、實收長度、期限與業務內容。畫面只顯示亂碼時,先確認串列參數和ASCII契約;只有偶爾少字時,再查分段處理、buffer容量及逾時,不要直接增加所有等待時間。

另測長度005但內容只送AB後立刻ETX。因payload禁止控制字元,這不是三byte內容的成功訊息,而是長度與內容衝突。相反地,長度003、ABC、ETX後接下一個STX,應交付ABC並開始下一筆,不能把下一個STX算入前一筆。

完成結果與練習

完成後,解析器面對標頭分段、payload分段與一次多frame,應產生相同的有效訊息序列。每個拒絕案例都有原因及長度紀錄,且任何不完整frame都不更新業務快照。這是本練習的驗收標準,不是已有實機通過的宣告。

練習將內容改成ABC。長度字元是003,完整hex為02 30 30 33 41 42 43 03,共八bytes。故意只送到42再逾時,應得到已收2、期望3,不能交付AB或ABC。

測試報告需保存輸入分段清單與每段後的狀態,確認改變分段方式仍得到相同交付結果。

問:三字元005是不是五個字元?答:它是三byte的長度欄,表示payload有五bytes;標頭及結尾不算在本例長度內。

問:一次read拿到完整HELLO就算成功嗎?答:還要核對STX、長度和ETX,以及業務內容;只看可見文字不足以確認框架。

問:逾時可以把buffer補滿零嗎?答:不可以,應拒絕並記錄不完整狀態;零是資料,不是恢復缺失bytes的方法。

問:這就是Modbus ASCII嗎?答:不是。本文為自訂STX加長度加ETX格式,實際Modbus ASCII有自己的邊界、編碼與LRC規範,不可混用。

參考:Python codecs官方文件:ASCII與嚴格解碼背景,本文框架是自訂設計。

參考:Modbus Serial Line V1.02:可對照Modbus ASCII既定格式,不能將本文STX框架當成Modbus。

延伸閱讀