學 Go 玩創客,TinyGo 初體驗(1):安裝環境並在 Arduino Uno/Nano 執行第一支程式

Photo by Fab Lentz on Unsplash

年 8 月,Arduino 官方網站登出了一篇文章〈TinyGo on Arduino〉(vMaker 有這篇文章的翻譯版:〈在ARDUINO上開發、運用TINYGO〉)。Go 語言(Golang)至今仍然比較小眾,但大家多少都會聽說它變得越來越熱門的消息,有人認為它說不定會是下一個 Python。這下它居然還能在開發板(微控制器)上跑,這對有接觸創客領域的人來說就頗令人好奇了。

TinyGo 是什麼,它又能做什麼呢?

一條迷你狗走天下

「我的初衷是:如果 Python 也能在開發板上跑,那 Go 當然就能在更低階的開發板上執行了。」

— — Ayke van Laëthem(TinyGo 的創建者)

想了解 TinyGo 的威力,不妨先來看上面這支影片。在這支影片裡,身為 TinyGo 共同開發者之一的 Ron Evans 先拿標準 Go 1.12 版編譯了個「Hello World」範例,然後用 TinyGo 0.9.0 版再編譯一次。前者得到的大小是 1100 KB,而後者僅有 11.6 KB。

完全相同的程式碼,怎麼會有如此懸殊的產出?

根據 Ron Evans 的說法,訣竅就在於寫一個新的編譯器,這個編譯器不會針對任何作業系統產生 runtime(畢竟微控制器沒有作業系統)。如此一來,剩下的「骨架」就非常小了。TinyGo 改用一個以 LLVM 撰寫的編譯器來編譯原始檔,藉此產生出一個小非常多的二進位檔案。於是,讓 Go 語言在開發板以及 WebAssembly (WASM) 的環境運作就變得可行。

也就是說,不若 MicroPython 是精簡版的 Python,TinyGo 基本上其實就是把 Golang 套用在一個新編譯器而已。當然,由於原始 Go 的套件有些跟 runtime 息息相關,所以它們是無法被 TinyGo 編譯的(不然就得等 TinyGo 寫出自己的版本)。此外,既然是設計給開發板,TinyGo 不僅有針對開發板設計的 machine package,此套件的內容還會因應開發板的種類而變化(使用者在編譯時必須指定要編譯的「target」)。

TinyGo 最有趣的特點,或許是其設計還受 MicroPython(應該說 CircuitPython,乃紐約的 Adafruit 公司修改自 MicroPython、替自家 SAMD21/51 系列開發板設計的版本)影響,所以許多硬體控制的功能就頗有 MicroPython 的風格。因此從語言上,TinyGo 似乎可說融合了 C 語言(Go 和 C,Java 很類似)和 MicroPython 的各自優點。

Photo by Riccardo Annandale on Unsplash

但就筆者而言,這還不是 TinyGo 最令人訝異之處。TinyGo 之所以值得注意,是因為它已經(試圖)支援種類眾多的開發板:

  1. 8 位元 AVR 處理器:ATmega328P、ATmega2560、ATtiny85
  2. 32 位元 ARM Cortex M0+ 家族:SAMD21、nRF51
  3. 32 位元 ARM Cortex M3:STM32F1
  4. 32 位元 ARM Cortex M4:SAMD51、nRF52、STM32F4

由於台灣鄰近中國之故,我們長年最容易接觸的就是廉價的 AVR 開發板,以及 ESP 家族。SAMD21/51 和 nRF52 近年似乎已是國外的推行主流,但因這邊得進口,在台灣很少人會用。顯而易見,TinyGo 主要的設計對象仍是 32 位元微處理器。

在 TinyGo 的支援列表中,甚至包括兩款智慧型手表,以及 2001 年的 Game Boy Advance,甚至是 Nintendo Switch。更多硬體已經加入陣營:來自對岸的 Seeeduino XIAO(使用 SAMD21,僅售 5 美元)和 Wio Terminal(SAMD51),極受歡迎的 ESP8266/ESP32 也在 0.15.0 版加入初步支援。誰知道呢?TinyGo 統一開發板的威力,也許將會比我們想像的更強大哩。

TinyGo 尚未成熟(但值得關注)

不過,我也得說,在寫這篇文的時候,TinyGo 離實用化其實還有一段距離。

儘管目前 TinyGo 名義上和 Arduino 有合作、並也成為 Google 官方贊助的專案,實際參與開發的人數仍只有不到 10 人。TinyGo 仍在經歷可觀的變動及改良,而且要做的不只是 TinyGo 本身,開發團隊也得發展諸如 TinyGo drivers(寫給各種感測器模組的 Go 驅動程式)以及 TinyFont(適用於各種螢幕的字型庫)之類的周邊功能。

Photo by Ian Schneider on Unsplash

目前最大的問題或許是,有太多類型迥異的開發板加入,開發者自己也不見得拿得到手,以致許多板子都有一部分功能尚未實作(比如欠缺類比輸入、PWM 輸出或 SPI 等)。TinyGo 能使用的標準 Go package 還在補充中,此外部分 TinyGo drivers 也有待改進 — — 例如,本文開頭連結的文章所提到的 Arduino Nano 33 IoT,雖然有其 ESP32 網路模組用的驅動程式,但功能仍很有限。

最後,TinyGo 必須借助外部燒錄工具才能將程式寫進某些開發板上,對一些板子來說有點麻煩。如果將來能像 Arduino IDE 一樣整合在一塊,那就更好了。

而當前支援 Go 語言的編輯器也不多,我目前是用 VSCode 加 Go 外掛來編輯原始檔,存檔後再用底下的終端機以指令手動上傳(出現錯誤就再改),另外開一個 Tera Term 來讀 UART 輸出結果。

所以,TinyGo 想在大多數開發板上達到 MicroPython 現有的實用化程度,或許還得等一陣子。不過,我們可以先來體驗看看如何撰寫並上載 Go 到台灣比較常見的幾種開發板 — — 包括我們最熟悉的 Arduino Uno 與 Arduino Nano。在後續的文章中,筆者還會介紹 BBC micro:bit 以及其他幾種板子的燒錄方法。

以下稱的 Nano 若無特別註明,指的就是較舊的 ATmega328 版 Nano 3.0。

透過開發板的互動性實作,其實或許更能刺激使用者學習 Go 語言的興趣。畢竟單純在電腦上學 Go,豈不是太無趣了?

環境安裝與設定

在 Windows 安裝 Go/TinyGo

1、下載和安裝 Go(比如 go1.16.5.windows-amd64.msi),安裝至 C:\。安裝程式應該會自動將 Go 的路徑加入你的系統 Path 變數。

2、下載 TinyGo(比如 tinygo0.19.0.windows-amd64.zip)和解壓縮到 C:\。

到控制台→系統→進階系統設定→環境變數(N)…然後在使用者或系統的 Path 變數加入路徑:

C:\tinygo\bin

3、現在打開命令提示字元,輸入

tinygo version

如果你看到以下結果,就代表路徑都正常:

tinygo version 0.19.0 windows/amd64 (using go version go1.16.7 and LLVM version 11.0.0)
Photo by Mariya Pampova on Unsplash

在 Linux 安裝 Go/TinyGo

1、安裝 Go

目前比較新版的 TinyGo 不再支援 Go 1.11 與更早版本,但像是 Ubuntu 和樹莓派能透過 apt 安裝的 Go 版本都比較舊,所以建議你安裝最新的 Go 版本。

同樣的,到 Go 網站下載你需要的版本。如果是 64 位元 Ubuntu 之類的就選下面列表中的 go1.16.7.linux-amd64.tar.gz,32 位元樹莓派系統則選 go1.16.7.linux-armv6l.tar.gz。

下載這個檔案到 /home/pi/Downloads/ 底下,然後執行以下指令(清空 /usr/local/go 然後將你下載的 Go 裝進去):

sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf /home/使用者名稱/下載/go1.16.7.linux-amd64.tar.gz

注意 Go 壓縮檔的路徑要改成你系統中的實際路徑。

2、下載和解壓縮 TinyGo:

使用 Ubuntu/Debian 系統:

sudo wget https://github.com/tinygo-org/tinygo/releases/download/v0.19.0/tinygo_0.19.0_amd64.debsudo dpkg -i tinygo_0.19.0_amd64.deb

若是樹莓派(使用 32 位元環境時)則得用以下指令:

sudo wget https://github.com/tinygo-org/tinygo/releases/download/v0.19.0/tinygo_0.19.0_arm.debsudo dpkg -i tinygo_0.19.0_arm.deb

目前沒有設計給 ARM64(aarch64)架構的版本。

實際網址及最新版本編號請參考 TinyGo 的 release 清單

4、加入 TinyGo 路徑:

確保回到 home 目錄,先打開 bashrc 文件

nano ~/.bashrc

然後在最後一行加入

export GOPATH=$HOME/goexport PATH=$PATH:$GOROOT/bin:$GOPATH/binexport PATH=$PATH:/usr/local/tinygo/bin

按 Ctrl+X 然後 Y 來存檔。

重開終端機來讓路徑生效,或者你可在終端機複製貼上這行執行一次,讓它現場生效。

5、最後在終端機輸入:

tinygo version

看到以下結果便代表路徑設置成功:(下面是我的樹莓派的結果,我自己用手動方式安裝最新的 Go)

tinygo version 0.19.0 linux/arm (using go version go1.16.7 and LLVM version 11.0.0)

更新 Go 與 TinyGo

將來當 Go 或 TinyGo 有更新時,照上面的方式安裝新版即可(在 Windows 要先刪掉舊的 tinygo 資料夾)。已經加入的路徑不用再重加一次。

設定 VSCode

筆者目前是在 Windows 上用 Visual Studio Code 來編輯 TinyGo 程式,不過由於我不是業界開發人士,所以並不熟 VS Code。但使用 VS Code 有很多好處,包括能依語法標示顏色、存檔時還會自動排版/import 套件等等,相當方便。

在 VS Code 安裝 Go 的 extension 後,它就能支援 Go 語言了。TinyGo 沒有直接支援,比如編輯器仍會顯示找不到 machine 套件,這是正常的,因為該套件在編譯時才會根據不同板子來產生對應的版本。我們會透過命令列工具來編譯跟上傳 TinyGo 程式。

不過 TinyGo 從 0.15.0 版發布後,他們在 VSCode 也有新增一個 plugin(tinygo),可以讓你選擇特定的開發板為目標。

安裝 AVR 上傳工具(for Arduino Uno/Nano)

為了能把編譯好的程式寫入 Arduino Uno 或 Nano,我們必須安裝額外的工具。這些其實在 Arduino IDE 內也有提供。

在 Windows 下,我個人的做法是下載這個人家打包好的 AVR-GCC 並將之解壓到 C:\。你同樣得將以下路徑加入 Path 系統變數(注意版本編號):

C:\avr-gcc-11.1.0-x64-windows\bin

Linux 比較簡單一點,只要執行下面這行指令即可:

sudo apt-get install gcc-avr avr-libc avrdude

編譯和上載 blinky 範例程式

Photo by Pavan Trikutam on Unsplash

在 Windows 檢查連接埠號碼

將你的 Arduino Uno 或 Nano 接上電腦。若在 Windows,到控制台→裝置管理員,看看你的板子是在連接埠的幾號 COM。以筆者的例子來說是 COM5。

通常同一個板子的 COM 號碼會固定。不過,也有的時候會改變。所以每次使用前最好還是確認一下。

另一個檢查 COM 號碼的快速方式是在 Windows 的命令提示字元視窗輸入 mode:

C:\Users\使用者> mode裝置 COM1 的狀態:
------------
傳輸速率: 1200
同位檢查: None
資料位元: 7
停止位元: 1
逾時: OFF
XON/XOFF: OFF
CTS 交握: OFF
DSR 交握: OFF
DSR 敏感度: OFF
DTR 電路: ON
RTS 電路: ON
裝置 COM5 的狀態: <--- 這個就是 Arduino Uno
-------------
傳輸速率: 0
同位檢查: None
資料位元: 0
停止位元: 1
逾時: OFF
XON/XOFF: OFF
CTS 交握: OFF
DSR 交握: OFF
DSR 敏感度: OFF
DTR 電路: OFF
RTS 電路: ON

在 Linux 檢查連接埠名稱

在 Linux 系統,連接埠通常會是 /dev/ttyACM0,也有可能是 /dev/ttyUSB0。如果真的不確定,在終端機用以下指令來查詢:

dmesg | grep tty

上傳 TinyGo 範例程式

打開命令提示字元(從 VSCode 的 Terminal 也可),輸入以下這行指令:

tinygo flash -target arduino -port COM5 examples/blinky1

「tinygo flash」 會執行編譯並上傳的兩步驟動作。參數 target 是開發板類型,「arduino」指的是 Arduino Uno;如果是 Nano 就改成 arduino-nano。port 是連接埠,examples/blinky1 是 TinyGo 提供的範例程式。

參數也可使用 =:

tinygo flash -target=arduino -port=COM15 examples/blinky1

若前面路徑都有設定正確的話,執行並稍待片刻,應該就能看到以下訊息:

avrdude: AVR device initialized and ready to accept instructionsReading | ################################################## | 100% 0.01savrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "C:\Users\Admin\AppData\Local\Temp\tinygo454908831\main.hex"
avrdude: writing flash (558 bytes):
Writing | ################################################## | 100% 0.15savrdude: 558 bytes of flash written
avrdude: verifying flash memory against C:\Users\Admin\AppData\Local\Temp\tinygo454908831\main.hex:
avrdude: load data flash data from input file C:\Users\Admin\AppData\Local\Temp\tinygo454908831\main.hex:
avrdude: input file C:\Users\Admin\AppData\Local\Temp\tinygo454908831\main.hex contains 558 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.13savrdude: verifying ...
avrdude: 558 bytes of flash verified
avrdude: safemode: Fuses OK (E:00, H:00, L:00)avrdude done. Thank you.

板子上的內建 LED 燈會開始閃爍。可以看到編譯的檔案大小為 558 bytes,而功能類似的程式在 Arduino 1.8.13 編譯出來則為 924 bytes。

上載自己的程式

建一個資料夾,在 VS Code 開啟該資料夾(File → Open Folder…),這即為你的專案工作區。然後在編輯器的檔案瀏覽視窗裡新增一個 main.go。最後點 Terminal → New Terminal 新開一個終端機,你會看到它已經切到資料夾的路徑。

在 main.go 內貼上下面的程式碼:

package main

import (
"machine"
"time"
)

func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
for {
led.High()
time.Sleep(time.Millisecond * 500)
led.Low()
time.Sleep(time.Millisecond * 500)
}
}

VS Code 應該會提示你安裝一系列工具,而且會在按存檔時自動格式化程式碼。但有時格式化會怪怪的,一直把字吃掉。這時你可按 Ctrl + Shift + P 然後輸入「Save Without Formatting」並點它,就能在不動到格式化之下存檔。

這其實就是前面的 blinky1 範例程式,這也是 TinyGo 的最基本範例。你可以試試看修改 time.Sleep 這行後面的數字,好改變內建 LED 燈的閃動速度。

要上載這支程式,在終端機輸入以下指令:(這裡我們改以 Arduino Nano 為例,位置是 COM10;請改成你自己板子的 COM port)

tinygo flash -target arduino-nano -port COM10 main.go

在本系列的下一篇中,我們將會以經典的 Arduino 創客主題來展示 Go 語言的一些基本特性,甚至擴及到一些其他的開發板(如 Arduino Nano 33 IoT 和 BBC micro:bit)。

當然,Arduino Uno/Nano 在運行 TinyGo 上仍有一些限制:

  1. 尚未實作 SPI 功能。
  2. PWM 只能調 duty cycle,還無法調頻率。
  3. 由於記憶體不夠,許多 TinyGo 驅動程式跑不動。(之後會講如何下載並使用這些驅動程式。)
  4. 由於記憶體不夠,Go 的 concurrency(可以平行跑兩個以上的程序)功能在 AVR 處理器上預設是關閉的。

不過許多腳位基本控制是可行的,而這就足夠拿來做不少應用了。

Photo by Kevin Schmid on Unsplash

I just like to write weird stuff that have very little to do with my actual work. My normal blog is https://krantasblog.blogspot.com.

I just like to write weird stuff that have very little to do with my actual work. My normal blog is https://krantasblog.blogspot.com.