Tonino 咖啡烘焙色度檢測計 DIY – 軟體篇
話說我為了研究 Tonino 的 calibration 程序,決定來探勘一下它的 desktop application。這個 application 可以透過 USB 上模擬的 virtual serial port 跟 Tonino 溝通,控制 Tonino 做很多事,當然也包括 calibration。
跨平台
作者提供了三個平台的 binary 供使用者下載,分別是 Windows,macOS,以及 Ubuntu Linux。當初看到這 application 這麼厲害可以跨三個平台,我就覺得不妙,它絕對不是在各平台的 native development 環境下開發的,多半是搭建在 Java 或是 Python 這種跨平台的開發環境上。
到作者的 GitHub repository 上一看,Tonino-App 果然是用 Python 寫的。
我只用 Phython 寫過一些簡單的 script,做 Linux 的 server 管理,以及在 ESP8266 上跑過 MicroPython,但像這種有 UI 有底層 I/O 還有檔案系統存取的 desktop application 完全不在我的管區內。
先試試作者提供的,打包好的 binary 吧。
一路修改 firmware 都在 macOS 下的 Arduino IDE 做,所以我就抓了 macOS 版的 binary 回來。
Package 解開後,除了簡單的說明檔和一個儲存參數檔的目錄外,只有一個很巨大的執行檔,檔案大小 227MB ! 雖然我知道這種跨平台的程式 compile 成 binary 時為了要帶一大堆原來系統沒有的 runtime library,binary 都會很大,但這個 size 還是嚇到我了。macOS 版的 iTunes 的執行檔也不過 200MB。
無聲無息
Tonino App 打開後,我把跟小丹借來的 Tiny Tonino 插上 USB,Tonino App 下方的狀態列會偵測到有裝置插入,並顯示 “Connecting”,幾秒之後就會顯示連接上的裝置種類和它的 firmware 版本。
但當我插上自己做的 Classic Tonino 時,卻什麼事情都沒有發生。
沒有訊息,沒有顯示,什麼都沒有。
為了確定 Classic Tonino 真的有被系統認到,我開了 serial port 的 terminal 程式,看看對應的 virtual serial port 有沒有出現:
畫面選單中的 wchusbserial14430 就是 CH340 晶片造出來的 virtual serial port 名稱,在 macOS 下它其實就是 /dev/ 目錄下的 device I/O 檔名。
Classic Tonino 的 baudrate 是 115200bps,設定好 baud rate 之後,我在 terminal 裡打了一個 Tonino serial protocol 的指令: “TONINO”,這個指令是要 Tonino 表明身分,回報 firmware 版本。
Classic Tonino 有收到指令並回覆 firmware 版本是 1.1.6,所以 serial port 的通訊沒有問題呀。
那到底是什麼問題呢 ?
我對 macOS 底層的裝置和驅動程式管理並不熟,於是決定到 Windows 下試試看。
我把系統重開到 BootCamp 下的 Windows 10,裝了 Windows 版本的 binary。
No luck。Windows 下的 Tonino App 對我自己做的 Classic Tonino 一樣沒反應,但是對 Tiny Tonino 就沒有問題,可以找到裝置,也可以順利連上。
到底是什麼問題呢 ?
我進到裝置管理員裡去確認,對應的 serial port 有出現,而且不管插 Classic 還是 Tiny Tonino,serial port 都出現在同一個 COM port 號碼。
沒道理 Tiny 認得到,Classic 卻認不到呀。
唯一的不同,就是 Tiny Tonino 用的 USB-to-serial 晶片是 FTDI 的 FT232,但我自己做的 Classic Tonino 因為用的是淘寶來到 Arduino Nano,上面的 USB-to-serial 是大陸江蘇沁恆 WCH 的 CH340。這顆 USB-to-serial 晶片雖然不是出自名門大廠,driver 也沒有內建在各大作業系統中,但因為有大量的 Arduino Nano clone 來自大陸,它也算用量很大的一顆 USB-to-serial 晶片,而且在 Arduino IDE 下都工作得好好的,沒理由會讓 Tonino App 認不到啊。
CH340 模擬出來的 virtual serial port 在 Windows 的裝置管理員中叫做 “USB-SERIAL CH340″,而 FT232 的則叫做 “USB Serial Port”,這是我目前看到唯一的不同。
雖然覺得不太可能,但我稍微懷疑了一下 Tonino App 可能會挑 device name,所以我強迫裝置管理員把 FT232 的驅動程式掛到 CH340 上。雖然這樣晶片絕對不會動 (這兩顆都不走標準 USB CDM 的 class driver,各有各的驅動程式),但我可以試試 Tonino App 到底是不是真的挑裝置名字。Driver 換掉後,CH340 的 port 名稱真的變成 “USB Serial Port”。
但還是 no luck。Tonino App 沒反應就是沒反應。
柳暗花明
這時我心中的燈泡突然 “登“ 一下亮起來。有個聲音問我:該不會挑 VID/PID 吧 ?
我想到 Tonino App 是有 source code 可以看的,於是我就到 repository 裡去搜尋 PID 這個字串。 (根據經驗,如果搜尋 VID 的話也會搜到 “video” 這個字,但 “PID” 的鑑別度就很高,雖然我覺得 Tonino App 的 source code 裡不應該會有 “video” 這個字…)
果然我在 lib/serialport.py 這個檔案裡看到這段程式碼:
def getSerialPorts(self): # we are looking for # Classic Tonino: VID 403(hex)/1027(dec) and PID 6001(hex)/24577(dec) # Tiny Tonino: VID 403(hex)/1027(dec) and PID 6015(hex)/24597(dec) vid = 1027 # 403 (hex) # ClassicTonino model (0) classicToninoProduct = "VID_0403\+PID_6001" classicToninoPID = 24577 # 6001 (hex) # TinyTonino model (1) tinyToninoProduct = "VID_0403\+PID_6015" tinyToninoPID = 24597 # 6015 (hex)
沒想到作者這麼狠,直接把 FTDI 的 VID 寫死在程式碼中。
我以前用 FTDI 的晶片用很多,因此對它的產品很熟。0x0403 的確是 FTDI 的 USB VID,而 0x6001 是 FT232 和 FT245 的 PID,0x6015 則是 FT-X 系列晶片的 PID。
這下頭痛了,原來 Tonino App 只認 FT232 模擬出來的 virtual serial port,難怪我的 CH340 完全沒戲。
USB 裝置的 VID/PID 並非不能改,我以前用 FT2232 設計面板開發工具時,就把 FT2232 的 VID/PID 改成 Sony 自己的,以宣示主權。
但修改 VID/PID 除了要有原廠提供的工具外,晶片還要能記住非標準的 VID/PID。這種小顆的 peripheral 晶片一般不會用 embedded-flash process 製造,因此常見的做法是外掛一顆 EEPROM 來記錄這些有的沒有的參數。FTDI 的晶片如果要設定非預設的 VID/PID,就要外掛一顆 93C46 SPI EEPROM。
但我現在要面對的是 CH340,我得讓 Tonino App 以為 CH340 就是 FT232,因此要想辦法把 CH340 的 PID/VID 改成 FT232 的 PID/VID。
我找到 WCH 的網站,查了 CH340 系列晶片的 datasheet。WCH 也很猛,網站只有中文,連 datasheet 都只有中文的。難怪我在英文的 Arduino 的論壇上,看到一些老外在討論這些來自中國的 Arduino clone 或 NodeMCU clone 時,遇到 CH340 這顆 USB-to-serial 晶片他們就就投降。
還是 no luck。
我手上這片 Arduino Nano 用的晶片是最陽春的 CH340G,不支援客製化 VID/PID。沒戲。
看來我只能去改 Tonino App 了。
只能改程式了
想到要動這個用 Python 寫的 application 我就一個頭兩個大。
我雖然有一點點的 Python 使用經驗,但從來沒碰過這麼大的 Python application,而且它還用了 Qt 作為 GUI。我連它 runtime 需要裝些什麼東西都搞不清楚,更何況還要編譯。
幸好作者在 GitHub repository 上給的說明相當完整,除了三個平台各有各的 build script 外,Python 2 和 Python 3 也各有各的 build script,這樣組合之後就有六個 build script 了。
現在除了剪片和畫電路圖電路板外,macOS 是我的主要作業環境。為了避免不小心把它搞砸,我決定 build Windows 的版本。
這支 Python 程式真的用到不少有的沒有的 library,在 Windows 下除了 Python 本身外,還用了 Qt、PyQT、pySerial、NumPy、SciPy、Matplotlib… 等一大堆 Python library,以及 py2exe 編譯器和 NSIS 安裝檔打包器。
第一個問題就是,我該用 Python 2 還是 Python 3 來 build 呢 ? 雖然 Windows 的 build script 兩者都有,但作者在 Windows 的說明檔中列出來的 Python 版本是 2.7, 我決定先試 Python 2。
照著作者的說明依序安裝這些元件,其中有很多元件因為年代久遠,指定的版本已經沒有提供 Windows 的安裝檔了,用 PIP 也找不到指定的版本,只好抓 source 回來裝。還好這幾個需要這樣安裝的 library 都是純 Python source code 的 library,不需要 compile,不然光是搞 Visual C++ 或 GCC 的環境我就飽了。
裝好之後呢 ? 就 build 呀。
我在鍵盤上敲下 build-win,執行 build-win.bat。
然後就在這裡卡關卡了整整兩天。
先是一大堆看不懂的錯誤訊息,只知道 py2exe 因為某些我看不懂的原因,沒有造出 binary 檔,因此 NSIS 也沒有東西可以打包。
我試著去了解 py2exe 的錯誤訊息,看來是某些元件找不到、版本不對、參數對不起來之類的問題。我一度把整個環境清乾淨,換到 Python 3.6,並把其它所有的元件都裝到最新版。但事實證明,新不一定就好,換到 Python 3.6 之後錯誤訊息雖然不一樣,但數量可沒少。
我試著上 StackOverflow 去搜尋類似的錯誤訊息,但實在沒什麼頭緒,有幾個人因為 six 這個模組跟 py2exe 不對盤的關係遇到跟我類似的問題,但他們的解決方法對我卻無效。
我也試著向推廣 Python 不遺餘力 (其實根本就是傳教士般的熱忱) 的學弟 @yungyuc 求助。如果有 Python 的問題找他準沒錯,我內心深處相信,如果連他都解決不了那一定是原始碼有問題…
由於我一直被這些元件的版本和相依性困擾著,學弟建議我用 Anaconda 管理 Python 的環境試試,他也試著幫我在 macOS 的環境下 build 看看。
Anaconda 沒有好消息,原因是原作者預設 Python 2 的環境搭配的是 Qt4,但 Anaconda 裝起來的 Python 2.7 配的卻是 Qt5,讓 build script 一整個錯亂。我用 PIP 強迫把 Qt 降到 Qt4 也沒用。至於學弟那邊,他手動修改 build script 避開某些錯誤後,做出一個 305MB 的執行檔,但是不能跑。
這樣搞了整整兩天,這麼多元件安裝拔除安裝拔除,環境變數設了又改改了又設,我的 Windows 環境差不多被搞爛了,但還是一點線索都沒有。
就在我快要放棄時,腦中的燈泡突然又 “登” 一下亮了起來。
我到作者的 GitHub repository 上去檢查 Windows 版的 build script build-win.bat,以及它會呼叫到的另一個 script setup-win.py。我想知道它們的上一個版本是多久以前 commit 的,以及上一版到這一版中間究竟改了什麼東西。
我對 py2exe 真的一竅不通,這些 script 對我來說看起來都跟天書一樣,但我在檢查 setup-win.py 時,卻看到一段熟悉的程式碼:
我雖然看不懂它在幹嘛 (看起來是搜尋一些 path 然後把它加到一個list 裡之類的…) 但這段程式碼我看過千百遍了,他就是 setup-win.py 每次卡住的地方啊!!! 我每次執行 build-win.bat, 它 call 進 setup-win.py 後,最後一個錯誤訊息就停在第 86 行那個地方啊!!!
這一段是作者最後一次,也就是差不多兩個月前 commit 這個檔案時才加上去的,上一版的 setup-win.py 裡沒有這一段。於是我心裡冒出一個想法,如果時光倒流的話…
對,我用上一版的 setup-win.py 換掉了現在工作目錄中的檔案,然後…
建! 出! 來! 了! 打完收工!
我 build 出一個跟抓下來的安裝包一模一樣的檔案。作者也是用 Python 2.7 打包的,我之前花在安裝 Python 3.4 3.5 3.6 那些努力都是白做的啊啊啊啊啊~~~~~
於是我很快地改了 serialport.py 裡面的 VID/PID,再 build 一次 app。
看到左下角顯示連接上 Tonino 時,我真的快要流淚了!
接下來,我終於可以開始研究它怎麼做 calibration 了… 當然,那又是另外一個故事了。
Hihi 先生您好 我是一位烘豆師 最近在玩arduino
我仿造您的方式 用arduino 做出agtron機但也遭遇跟您所碰到的問題,想問你是否可跟您索取軟體的呢?另外我也想學程式語言 目前希望能從Arduino的程式開始 不知您有沒有推薦書或是一些方向可指引呢?
Hi 版主你好,
我嘗試DIY製作色度計也遇到了與您相同的問題,不知可否不吝分享您修改好可以認CH340的安裝包嗎?
感激不盡
買一顆新FT232的NANO的比較快吧XD,大大好厲害