Sunday, April 11, 2021

使用 minicom 及 expect 自動化控制 TTY 設備

簡介
傳統來講,使用 expect script 可以幫助我們自動化一些繁瑣的工作,例如以互動方式登入 SSH,或者進行自動化軟件安裝及部署。那麼,如果我們想要用 expect 去控制 TTY 裝置(例如 UART to USB 開發板、數據機等等),又如何操作呢?以下介紹兩種方法,希望可以幫助大家,輕鬆透過 minicom,進行簡單的自動化。

有人會問,為什麼一定要用 expect 呢?我能先自行 echo command 去 /dev/ttyUSB0,然後再用一個 while loop 去觀察 /dev/ttyUSB0 的輸出,不就可以了嗎?但我發現,這種方法,將會製造一個「空窗期」:如果 tty 硬件十分快,它就有可能在 echo command 執行完成之後,以及在 while loop 開始執行之前,已經將結果輸出。這樣的話,之後才開始的 while loop,就會一直讀不到有 output,然後跌入 infinite loop 之中。理論上,我們可以透過 multi-thread,邊讀 TTY 內容,邊寫 TTY command 去解決這件事,但我覺得,這樣又實在太過小題大做。所以,使用 expect 就是最簡單的方法。

本來,使用 expect 去做 interactive scripting 是一件非常簡單的事情, 但奈何 expect 的文檔十分殘缺,而且上述問題又實在太多人忽略掉,單靠搜查文檔,其實很難去理解這件事。所以,我決定在此記下,希望對其他人也有所幫助。

---

方法一:使用 expect script 直接操控 minicom
這種方法最為直覺,你只需要使用 expect,就可以直接與 minicom 連接:

#!/usr/bin/expect
set timeout 10
exec bash -c "echo Hello World"
spawn minicom -D /dev/ttyUSB0 -b 9600 -C /tmp/minicom.log
send "ps"
expect "#"
send_to_user "completed ps command to ttyUSB0"

使用這種方法,expect 會持續監察 spawn 出來的 minicom 中的 stdout,並且會把 send 的內容傳送回 minicom 的 stdin 中,所以,它就能與 minicom 直接進行互動。然後,使用 exec 加上 bash,就能執行各種各樣非 expect 的 commands。這種情況,minicom 負責所有與 tty 裝置的內容收發。 

不過,如果你打算運行很多非 expect 的 commands 的話,這種方法就會變得很慢,並且難以理解。因為每次運行 bash commands,都要先 exec bash,然後再在 bash 中運行 command。

---

方法二:使用 bash 開啟 minicom,並透過查看 minicom 的 log 與 tty 裝置互動
這種方式較為間接,使用 bash 作為主力,expect 則成為其中一個 bash command:

#!/bin/bash
echo "Hello World"
minicom -D /dev/ttyUSB0 -b 9600 -C /tmp/minicom.log" &
PID_MINICOM=$#
expect -c '
    timeout 10
    spawn tail -Fn0 /tmp/minicom.log
    exec bash -c "echo ps > /dev/ttyUSB0"
    expect "#"
    send_to_user "completed ps command to ttyUSB0"
'
kill $PID_MINICOM

以上的代碼,bash 會先在背景開啟 minicom。接著,bash 會開啟一個 expect process,並且持續監察 minicom 的 log 輸出。如果 expect 想要與 tty 互動,由於 tail 的 stdin 並沒有任何作用,它將不能使用 send。所以,你需要把內容自行用 exec bash,再用 echo 把  command 內容 redirect (>) 進 /dev/ttyUSB0。

這種做法,雖然看起來比較複雜,但如果你打算使用 bash 作為為最主要的語言,它所產生的系統開銷會少很多(因為不需要頻繁開新 bash)。但反的來說,如果你的 script 打算主力運行 expect,並需要與 TTY 有很多互動的話,這種方法就比較緩慢:因為每一次 send TTY command,它也需要 exec 一個新的 bash。

---

小結
使用 expect 能使用非常短的語法,解決與 TTY device 互動所產生的各種問題。是次我分別介紹了以 expect 為主,以及用 bash 為主的兩種 expect script 寫法,以滿足不同情況的需要。

No comments:

Post a Comment