Let's learn how to read UART by python.
UART 也是一種 Serial 通訊
最近有個測試項目要從產品的 debug port 讀取訊息,產品 debug port 藏在機殼內是 UART 介面,如果想多了解 UART 的基礎知識可以參考 帶你認識物聯網基礎 Serial 通訊(二)-- UART 篇 ,這裏簡單說 UART 跟 RS232/RS485 一樣都是 serial 通訊介面,在 Windows 電腦上以 com port 形式存在,通訊參數設定方式也是一模一樣。
首先在電腦上設定好 com port 通訊參數,就可以用 putty 連線。沒想到連線後後馬上看到不斷噴出的各種 debug 訊息,一噴好幾頁很難一眼就看到想要的訊息,用人工處理效率實在相當糟糕啊。當時我就在想,如果有個小程式可以幫我稍微整理這些 debug 訊息,至少調整下顯示內容應該很不錯。估計用 python 寫個小程式不需要花不了多少時間。
程式功能需求
我希望這隻小程式的基本功能是:- 有提供 Serial 通訊參數設定與開啟連線功能
- 通訊部分可以用正常人類容易使用的 ASCII String 方式來傳輸資料
- 最好有個簡單濾網功能讓我不必要的訊息排除
- 整理後要輸出訊息也要打上時間戳記方便追蹤
Python 的 Serial 通訊必要套件是 pyserial,最簡單的方式就是上網找一段 sample code 來改最快。拜豐富網路資源所賜,找到的這個 sample code 完成度相當高,我只需要把執行環境與 pyserial 套件先處理好,然後自己補上濾網功能跟打印時間戳記功能,前後幾分鐘就完成了這個小程式。
接下來,讓我們從程式碼來觀查值得學習或注意的地方,為了保持程式碼的完整性,就用註解來說明吧。
# 首先 pip install 安裝 pyserial 套件
# Python 的強大之處在於大量的軟體應用套件
# Serial 通訊套件沒意外都會選 pyserial
import serial
import time
# 建立 Serial 連線方式是用 pyserial 套件的 Serial 類別
# 生成實例的同時要把對應的通訊參數填上去
# 不知道怎麼填就去控制台把電腦對應 com 找出來
ser = serial.Serial(
port='COM6',
baudrate=115200,
timeout=1
)
print(f"Opening serial port {ser.name}...")
# 程式運行過程中會宣告很多變數,有 int, float, string..
# 這些變數的目的就是暫存程式碼運行時期的各種資料或運算狀態
# 這裏宣告變數 input_data 是用來存儲讀進來的內容
# 宣告變數最好加上 type hint 整數用 :int 浮點數用 :float..
# 字串用 :str,一目了然,IDE 也能正確識別物件變數提供操作函數
# 養成隨手用上 type hint 絕對是好習慣,當然也提高程式碼閱讀性
input_data : str = ""
# 變數也可以是複雜的陣列或是 key-value sets
# 這裏我用一個字串陣列來建立訊息濾網,負向表列不想顯示的訊息
# 當然濾網設計也可以用關鍵字或正向表列方式處理,端看需求來調整設計
filter_strings = ["No need to show"]
# 程式碼主體用 try-catch 可以讓程式發生意外發生時可以優雅的崩潰
# 程式碼的語法編寫錯誤編譯階段就可以被查找出來,執行階段就要靠例外處理
# 例如:能不能開啟一個通訊 port;能不能開啟一個要讀寫的檔案
# 這都是程式碼執行到當下才能知道的事
# 由於 try-catch 會捕捉執行例外與錯誤
# 當例外發生時就會進入各種 exception 來進行處理
# finally 則是無論程式碼是否有例外或錯誤,最後一定都要跑這段 code
try:
# 用 handle ser 打開連線
if ser.isOpen():
# 這裏我利用產品 clear 指令把舊的訊息先清除
# 也順便展示下如何寫入訊息到 serial port
# 字串在前面放個 b 是告訴 python 用 binary 方式處理
# 所謂 binary 就是 010100101 這種二進位的形態
# 為了方便閱讀,基本上都會轉成 16 進位的寫法
# BTW, clear 用 16 進制表示是 63 6c 65 61 72
ser.writelines(b'clear')
print("Serial port opened successfully.")
# Debug 最主要的目的從 debug console 持續讀取資料
# 要持續讀取資料需要一個無限迴圈
# 每次進入迴圈就讀取一筆資料來進行處理
while True:
# 先把讀回來的資料存放到 input_data 內
input_data = ser.readline().strip().decode('utf-8')
# 接下來跟濾網比對篩選掉不要的訊息,簡單粗暴
if input_data and not filter_strings.__contains__(input_data):
# 通過篩選的資料打上時間戳記輸出
# 字串前面加個 f 代表要格式化輸出
# 可以先用大括號加要輸出的變數組合來排版
print(f"{time.ctime()} > {input_data}")
# 有哪些 exception 或是 error 需要去查閱 user manual
# 就算一時間不知道也沒差,只要補上 exception 就「所有」例外狀況
# 懶人做法是等例外或錯誤發生的時候,有需要再來補就可以了
except serial.SerialException as e:
print(f"Serial port error: {e}")
except KeyboardInterrupt:
print("Exiting program.")
except Exception as e:
print(f"Unknow Excption: {e}")
# 前面提到 finally 是「一定會執行」的程式碼
# 所以當程式正常結束或例外處理完後,就是把連線關閉釋放記憶體
finally:
if ser.isOpen():
ser.close()
print("Serial port closed.")
結論
在測試領域相當重有基本程式設計能力非常重要,畢竟產品需要在測試過程中反覆執行重複動作,例如,用瀏覽器連上機器登入頁面輸入帳密登入系統;每個測試結束後對產品重置恢復原廠設定等等。這些手動測試很花時間更是考驗人性,同樣的動作只要連續做個三次就就令人開始心浮氣燥。所以只要能會一點簡單程式基本功,就可以把這些 routine 測試工作全部自動化,雙手解放後工作效率自然會大幅提升,在測試專業發展上也會比其他人很有優勢。


















