PyPortal で TinyGo プログラミングを始めよう

この記事では、以下の環境で Windows 10 環境で TinyGo + Pyportal を試した時のメモをまとめます。

* Windows 10 Home / Pro 64bit
* TinyGo 0.14
* PyPortal

個別に記載しない限りは Windows 10 を対象としたメモであることに注意。
また、基本的には PyPortal (および feather-m4) でしか確認していませんが、他のボードも大差ないと思います。

## 最初に

### メモを書くきっかけ

TinyGo でマイコン遊びをしようと思ったがまとまった情報が少なかった。
基本的には TinyGo 専用ボードというのはないので、書き込み方法含め不明点が多かった。
同僚が Windows 10 + Adafruit Trinket M0 で TinyGo デビューしようとして躓いていた。
ボード毎に異なる部分もあり L チカまですんなり進むようにサポートしたいという思いがあった。

### TinyGo とは

マイコンや WASM などの Small Places 向けの Go Compiler です。
ここでは Go の文法でマイコン開発ができるもの、という程度の認識でよいです。

* [TinyGo - Go on Microcontrollers and WASM](https://tinygo.org/)

### PyPortal とは

Adafruit 社の 3.2 インチカラー液晶タッチディスプレイ (320x240) な IoT ディスプレイマイコンボード。
基本的には Arduino や CircuitPython での開発が想定されているが、 TinyGo でも 0.12 でサポートされた。
ESP-WROOM-32 が搭載されていて Wi-Fi 経由で何かをしたり、等も可能。

マイコンは Microchip 社の ATSAMD51J20 (Arm Cortex-M4 120MHz) が搭載されている。

* [Adafruit PyPortal - CircuitPython IoTディスプレイ](https://www.switch-science.com/catalog/5741/) - スイッチサイエンス
* [Adafruit PyPortal - CircuitPython Powered Internet Display](https://www.adafruit.com/product/4116) - オフィシャル
  * [Overview | Adafruit PyPortal - IoT for CircuitPython](https://learn.adafruit.com/adafruit-pyportal) - オフィシャル Overview

![4116-00.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/cc42dc34-4921-332c-cc9d-530df0d2e328.jpeg)


### TinyGo 対応ボード


オフィシャルで対応が謳われているものについては以下に列挙されている。
AVR / RISC-V / Xtensa / ARM Cortex-M などがサポートされている。
現時点では ARM 系が開発の中心となっているので、新しく買うなら ARM 系のボード (※) で試すのが良いはず。
※ATSAMD21 / ATSAMD51 / nRF51822 / nRF52840 等
※クロックは高め (64MHz 以上) の方が楽に遊べる

あとは、 avr や stm32 などは openocd / jlink 等が使える必要があるので、書き込み用の何かを持っていない人は以下のいずれかが始めやすいと思います。

```
circuitplay-bluefruit
circuitplay-express
clue_alpha
feather-m0
feather-m4
feather-nrf52840
hifive1b
itsybitsy-m0
itsybitsy-m4
metro-m4-airlift
microbit
pybadge
pygamer
pyportal
reelboard
trinket-m0
wioterminal
xiao
```

* https://tinygo.org/microcontrollers/

2021/12/31 追記)
以下に画像付きで TinyGo 0.21 時点の対応ボード一覧を記載しました。
ディスプレイがあったり、 LED 沢山なものが個人的にはモチベーション維持しやすいです。

* https://sago35.hatenablog.com/entry/2021/11/20/112002

### なぜ Pyportal を選んだのか

PIC16F あたり？からデビューした世代なので、楽ができるオールインワンなボードが欲しかったから。

* 本体だけでそれなりに遊べそう
  * ブレッドボードを使わなくてもいい
* ネットワークにそのままつながる
* TinyGo 0.12 で新規サポートされた
* [FOSDEM 2020 (2020/02) のデモ](https://fosdem.org/2020/schedule/event/iottinygo/) でも使われていた
  * 画面描画部分の TinyGo のソースが手に入る
* CPU (ATSAMD51) が Adafruit Feather M4 等の他の TinyGo ターゲットでも使われている

とはいえ、好きなのを買うのが良いと思います。
引っかからずに進むためには、ドキュメントが多いボードを使うのが安全です。
引っかかるのも経験だし勉強になるのも、そういう考え方も OK です。

### サンプルコード

最初にどういうコードをビルドして試せばよいか悩むと思います。
自分も悩みました。

とりあえず以下を書き込むことで、基板上の LED を点滅させることができます。
動作確認が出来たら、反転周期を切り替えてみる等をしてみると良いです。

以下のソースコードは、 TinyGo のオフィシャルにあるサンプルになります。
PyPortal 環境では、液晶の裏側の赤 LED が machine.LED として設定されています。
他のボードでも動く (ように machine.LED とかがボード毎に設定され続ける) と思います。

ビルド方法と書き込み方法は後述。

```go
package main

import (
    "machine"
    "time"
)

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

        led.High()
        time.Sleep(time.Millisecond * 1000)
    }
}
```

## TinyGo の始め方

### TinyGo のインストール

最新版の `tinygo.*.windows-amd64.zip` をダウンロード＆解凍し `C:\tinygo` 以下に配置します。

* https://github.com/tinygo-org/tinygo/releases

その後、環境変数 PATH に `C:\tinygo\bin` を追加します。

```
$ set PATH=C:\tinygo\bin;%PATH%%
```

実際にはファイルの場所はどこでも良いので適宜好きな場所に読み替えても OK です。
とにかく `tinygo` の実行体 (Windows だと `tinygo.exe`) に PATH が通ってればよいです。

なお、上記では Windows Native 版を紹介しましたが Docker ベースでも開発可能です。
ただし、特別な理由がない限りは Native 版が良いと思います。

### ビルド方法

Windows Native 版の TinyGo を使っても良いですし、 docker 版を使っても良いです。
それぞれの実行方法は以下の通り。

#### ビルド方法 (Windows Native 版 TinyGo)

```
tinygo build -o app.uf2 -target pyportal [PATH TO YOUR PROGRAM]
```

### マイコンへの書き込み方

#### Windows Native 版 TinyGo を使って書き込む

以下の例では、再ビルドしてマイコンへの書き込みまで実施します。

```
tinygo flash -target=pyportal [PATH TO YOUR PROGRAM]
```

もしも複数のマイコンを接続している等でうまく書き込みができない場合はポートを指定してください。

```
tinygo flash -port COM4 -target=pyportal [PATH TO YOUR PROGRAM]
```

![devmgmt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/8cb14f5f-60a4-8a1b-b1bc-cc46af5b73b5.png)

#### マウントされたドライブに `*.uf2` ファイルをコピーして書き込む

実行後すぐに crash してしまうようなプログラムを書いてしまった場合は、この方法で復帰できます。

リセットボタンをダブルクリックするとブートローダーにとどまるようになります。

マウントされたドライブに作成した `*.uf2` ファイルをコピーすることで書き込みができます。
書き込み後は自動的にリセットして、書き込んだファイルの動作を行います。
例えば以下のようなコマンドで書き込みすることができます。

```
$ copy app.uf2 D:
```

## 参照資料等

### マイコンボード毎の TinyGo のドキュメント

以下にあります。

* https://tinygo.org/microcontrollers/

PyPortal については以下にあります。

* https://tinygo.org/microcontrollers/pyportal/
* https://tinygo.org/microcontrollers/machine/pyportal/
  * https://github.com/tinygo-org/tinygo/blob/release/src/machine/board_pyportal.go


### 周辺デバイスのドライバ

TinyGo 全般用として、様々な周辺デバイス用のドライバが以下あります。

* https://github.com/tinygo-org/drivers

例えば PyPortal の液晶は ILI9341 TFT Display なので以下を使うと簡単に画面描画できます。

* https://github.com/tinygo-org/drivers/tree/release/ili9341

PyPortal 用のものについてのまとめは以下。

* ATSAMD51J20 : Arm Cortex-M4
    * https://www.microchip.com/wwwproducts/en/ATSAMD51J20A
    * One Full-Speed (12 Mbps) Universal Serial Bus (USB) 2.0 interface
    * 120MHz / 1MB ROM / 256KB RAM
* ILI9341 : Display 3.2" 320 x 240 color TFT
  * 画面描画周り
    * https://github.com/tinygo-org/drivers/tree/release/ili9341
    * https://github.com/tinygo-org/drivers/tree/release/examples/ili9341/pyportal_boing
      * 
![ili.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/1af6c849-38cb-4a22-ff35-3db641de7733.jpeg)
  * タッチパネル周り
    * https://github.com/tinygo-org/drivers/tree/release/touch
    * https://github.com/tinygo-org/drivers/tree/release/examples/touch/resistive/pyportal_touchpaint
      * 手書きができる Demo
      * ![touch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/5b694319-0715-bed3-5803-82f227c03cfb.jpeg)
  * ライブラリ類
    * https://github.com/tinygo-org/tinydraw
      * Line や Circle といった描画系メソッドの Demo
      * ![tinydraw.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/ca3d6033-a879-1b6c-eedd-dbfbedf1188a.jpeg)
    * https://github.com/tinygo-org/tinyfont
      * いくつかのフォントを表示する Demo
      * build に数十秒かかるので気長に待つ
        * ビルド時間を短縮したい場合は、必要なフォントに対応する *.go のみで構成する新しいパッケージを作る事
      * ![tinyfont.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/5bb8ee24-9106-d5e2-ce47-5816714d6ca8.jpeg)

* ESP-WROOM-32 : WiFi / Bluetooth v4.2
    * WiFi
        * 最低限の通信が可能
        * https://github.com/tinygo-org/drivers/tree/release/wifinina
        * https://github.com/tinygo-org/drivers/tree/release/examples/wifinina/webclient
            * ssid と pass を設定するだけで動く
        * https://github.com/tinygo-org/drivers/tree/release/examples/wifinina/http-get
            * http.Get() や http.Post() 等が使用できます
    * Bluetooth
        * (未確認)
* speaker
  * speaker につながっているのは machine.PA02
  * https://github.com/tinygo-org/drivers/tree/release/buzzer
  * https://github.com/tinygo-org/drivers/tree/release/examples/buzzer
* ALS-PT19 : light sensor
    * Data Sheet : https://www.adafruit.com/product/2748
* ADT7410 : 温度センサー
    * Data Sheet : https://www.analog.com/jp/products/adt7410.html
    * https://github.com/tinygo-org/drivers/tree/release/adt7410
    * https://github.com/tinygo-org/drivers/tree/release/examples/adt7410
    * A1 / A0 が GND につながっているので I2C アドレスは 0x48
    * machine.I2C0 を用いて通信する
* NeoPixel : 本体裏のフルカラー LED
    * https://github.com/tinygo-org/drivers/tree/release/ws2812
    * https://github.com/tinygo-org/drivers/tree/release/examples/ws2812
* microSD
    * https://github.com/tinygo-org/drivers/tree/release/sdcard
    * ファイルシステムも下記に記載の tinyfs にて実装済み
    * https://github.com/tinygo-org/drivers/tree/release/examples/sdcard/console
    * https://github.com/tinygo-org/drivers/tree/release/examples/sdcard/tinyfs
        * Windows 等から FAT32 フォーマットした microsd カードを読み込むことができます
* 8MB flash
    * Data Sheet : http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf
    * 以下の flash.NewQSPI() が使える
        * 1 byte 単位で書き込み可能だが、 bit 単位では 1 → 0 しか出来ない
        * このため、任意の値に書き換えるには事前に block / sector / chip 単位での消去が必要
    * https://github.com/tinygo-org/drivers/tree/release/flash
    * https://github.com/tinygo-org/drivers/tree/release/examples/flash/console/qspi
    * ポート設定は以下の通り
        * machine.PB11, // CS
        * machine.PB10, // SCK
        * machine.PA08, // D0
        * machine.PA09, // D1
        * machine.PA10, // D2
        * machine.PA11, // D3
    * ファイルシステムは以下にて作成中
        * https://github.com/tinygo-org/drivers/issues/45
* plug-in ports for I2C
    * コネクタから GND / VCC / SDA / SCL の順で線出し
    * 初期状態で VCC は 5V となっているが、ジャンパーにより 3.3V に変更可能
    * machine.I2C0 を用いて通信する
    * 適合するコネクタ付ジャンパワイヤは以下
        * https://www.switch-science.com/catalog/5889/
    * 実際に動かしてみた例は以下にあります
        * [TinyGo で goroutine + pin interrupts を使う](https://qiita.com/sago35/items/75537e78f06c86a5a77b)
* 2 x analog/digital pins
    * コネクタから GND / VCC / Dx_UNSAFE の順で線出し
    * 初期状態で VCC は 5V となっているが、ジャンパーにより 3.3V に変更可能
    * ポート設定は以下の通り (D3 および D4 という名前でも定義されている) で、どちらも PWM 有効
        * machine.PA04 or machine.D3 // D3_UNSAFE
        * machine.PA05 or machine.D4 // D4_UNSAFE
    * 適合するコネクタ付ジャンパワイヤは以下 (ただし、 VCC と GND が逆なので入れ替える等の対策が必要)
        * https://www.switch-science.com/catalog/2421/

### 日本語情報リンク

* [TinyGoで始める組み込みプログラミング - 144Labグループ開発者ブログ](https://tech.144lab.com/entry/tinygo)
* [TinyGo Tutorial - GolangRdyJp](http://golang.rdy.jp/2019/12/17/tinygo/)
* [TinyGoで組み込み開発を始めよう!!](https://nobonobo.github.io/presantations/tinygo2/)
* [Arduino Unoでtinygoをやってみた - Qiita](https://qiita.com/sat0ken/items/6bc889c3c7989a29a7c8)
* [TinyGo 0.14 で遊べるマイコンボード一覧 - sago35の日記](https://sago35.hatenablog.com/entry/2020/08/05/074905)
* [TinyGo で組込開発を始めよう!! 0.14 ver](https://docs.google.com/presentation/d/1KCM_mWaeO-Hg5FZSb-6P8w8hIblnaJz7aB0zXwUsbtQ/edit#slide=id.p) - [Umeda.go 2020 online](https://umedago.connpass.com/event/179814/) で発表した資料
  * [↑ の発表動画](https://youtu.be/hZJVa-7LL9I)
* [Wio Terminal で TinyGo プログラミングを始めよう](https://qiita.com/sago35/items/92b22e8cbbf99d0cd3ef)

## 雑多なメモ／困りごと／制限事項など

* math.Abs() や float64 を返す関数を使うとフリーズする
    * 2020/02/25 追記) Windows Native 0.12 および docker の tinygo/tinygo だとフリーズする
    * 2020/02/25 追記) tinygo/tinygo-dev であればフリーズしない
    * 2020/05/22 追記) windows 版でも以下の URL 内の enableCortexM4FPU() すればフリーズしなくなります
        * https://github.com/tinygo-org/tinygo/issues/944#issuecomment-625037019
    * 2020/05/23 追記) [Windows 版の TinyGo で SAMD5x CPU の浮動小数点演算でクラッシュする問題への対策](https://qiita.com/sago35/items/7db93dbdcfe098993edd)
    * 2020/08/07 追記) TinyGo 0.14 で修正されました
* goroutine は LLVM coroutine を使ってエミュレートしている
* goroutine は GOMAXPROX = 1 時の動きに近い
    * time.Sleep() や chan 操作時などに異なる goroutine に切り替わる
* ~~Windows 10 だと USB-CDC が動いてなさそう~~
    * ~~本来は `print()` とか `println()` とかからシリアルポートに書き込める感じ~~
    * ~~デバイスマネージャーで COM ポートとして認識しない~~
        * ~~ここが解決したら `ResetProcessor()` が動くようになってリセットボタンのダブルタップが不要になりそう~~
    * TinyGo 0.13 で修正されました
* ~~tinygo flash する時、 taget に従ったファイルが読み込まれる (target/pyportal.json) が、 flash-method が msd なので本当は flash-1200-bsp-reset は false が正しいはず~~
    * ~~この部分が (少なくとも windows 環境に対しては) 間違っているので公式ドキュメント通りに書き込めず -port 指定が必要となっている~~
    * flash-1200-bps-reset で boot に遷移させてから msd (uf2 を単にマスストレージデバイスにコピー) でやるのが正しい模様
    * 2020/08/07 追記) `Windows` および `Windows + 非英語圏` では書き込めない問題がありましたが TinyGo 0.14 で修正されました

## 変更履歴

2020/02/25 追記)
以下の Windows 版 tinygo 0.12 だと PyPortal で float 演算をしようとするとフリーズする。
現時点では docker の tinygo/tinygo-dev を使用する事で回避できる。
以下の本文中の記載は、随時 tinygo/tinygo-dev での確認結果をベースとしたものに書き換えていきます。

* tinygo version 0.12.0 windows/amd64 (using go version go1.13.1)

2020/04/16 追記)
tinygo 0.13 がリリースされましたが、 Windows 版では SAM D5x マイコンで float 演算をしようとするとフリーズする。
今の所は、 docker の tinygo/tinygo-dev (0.13 ベースになっています) を使うと回避できます。
Windows で SAM D5x マイコンを使っている場合は、当面は docker ベースで開発を続ける必要があります。

* SAM D5x based boards crash when using floating point operations
  * https://github.com/tinygo-org/tinygo/issues/944

2020/05/04 追記)
以下について記載を追加

* ESP-WROOM-32 : WiFi
* 8MB flash
* plugin ports for I2C
* 2 x analog/digital pins

2020/05/22 追記)
雑多なメモに、 windows 版 tinygo で FPU を使ったときにフリーズする問題への対策を記載。
plug-in ports for I2C の使用例を追記。

2020/05/23 追記)
各所に [Windows 版の TinyGo で SAMD5x CPU の浮動小数点演算でクラッシュする問題への対策](https://qiita.com/sago35/items/7db93dbdcfe098993edd) へのリンクを追記。
マイコンへの書き込み方 2 を追加。
TinyGo 対応ボード に、デバッガ等が無くても導入可能なボード一覧を追記。
変更履歴 (のうち古いもの) を末尾に移動。

2020/06/08 追記)
浮動小数点演算を使うとクラッシュする問題については、 FPU を使わない側に明示的に指定する変更が入りました。
0.14 に含まれるのでリリースされた後は普通に使用できるようになります。
FPU を使う側にすると、 goroutine 使用時の stack 使用量が増え切り替えも遅くなるとのこと。


2020/07/19 追記)
microsd の driver を作成した (まだ作成中ですが最低限動作する) のでリンクを追加。

## docker 関連の記述のバックアップ

TinyGo 0.14 では不要かもしれませんが、以下は記録として残します。

### ビルドの前準備

> 2020/08/07 追記)
> TinyGo 0.14 は go modules に対応しているため、この手順は必要無いかもしれません。
>
> 2020/05/23 追記)
> この vendoring の手順は、 docker 版のみに必要な手順です。
> windows 版であれば基本的には必要ありません。

ビルドに必要な依存ライブラリ等は事前に vendoring しておきます。
~~この時の Go は Go1.13 ではなく、また Go1.12 の最新版ではなく少し古めのものである必要があります。~~
Go 1.14 をインストールしておくと良いです。

その後、開発ディレクトリにて `go mod vendor` を実行します。

```
set GO111MODULE=on
go mod init
go mod vendor
```

正しく実行できると、 import している依存ライブラリが `./vendor` 以下に配置されます。
例えばあるプロジェクトだと以下のようになります。
vendor 以下に `tinygo.org/x/drivers` 以下のファイルが追加されているのが分かります。

```
./go.mod
./go.sum
./touch.go
./vendor/modules.txt
./vendor/tinygo.org/x/drivers/ili9341/ili9341.go
./vendor/tinygo.org/x/drivers/ili9341/parallel_atsamd51.go
./vendor/tinygo.org/x/drivers/ili9341/README.md
./vendor/tinygo.org/x/drivers/ili9341/registers.go
./vendor/tinygo.org/x/drivers/LICENSE
./vendor/tinygo.org/x/drivers/touch/point.go
./vendor/tinygo.org/x/drivers/touch/resistive/fourwire.go
```


### ビルド方法

Windows Native 版の TinyGo を使っても良いですし、 docker 版を使っても良いです。
それぞれの実行方法は以下の通り。

#### ビルド方法 (docker 版 tinygo/tinygo)

[TinyGo Tutorial - GolangRdyJp](http://golang.rdy.jp/2019/12/17/tinygo/) に色々記載があるので参照すると良いです。

簡単に書くと以下でビルドします。
Windows 10 Home 等で Docker Toolbox on Windows を使っていて、 Docker Quickstart Terminal (bash) で実行する分には以下が実行可能です。

```
docker run -it --rm -v $PWD:/go/src/github.com/user/repo \
-w /go/src/github.com/user/repo \
-e GOPATH=/go \
tinygo/tinygo tinygo build -o app.hex -target pyportal .
```

ただし、 Windows 10 Pro + Docker Desktop + cmd.exe だったりすると以下のようになります。
※カレントディレクトリが %GOAPTH%/src/github/com/user/repo である場合

```
docker run -it --rm -v %GOPATH%/src/github.com/user/repo:/go/src/github.com/user/repo \
-w /go/src/github.com/user/repo \
-e GOPATH=/go \
tinygo/tinygo tinygo build -o app.hex -target pyportal .
```

更には、 Docker Toolbox on Windows を使っているけど cmd.exe で実行したい場合は上記だとうまく動かず下記のように実行する必要があります。
試した感じでは Docker Toolbox on Windows の docker の場合は `/c/Users` のように `/` 始まりかつ小文字のドライブレターにする必要があります。

```
docker run -it --rm -v /c/Users/USER/go/src/github.com/user/repo:/go/src/github.com/user/repo \
-w /go/src/github.com/user/repo \
-e GOPATH=/go \
tinygo/tinygo tinygo build -o app.hex -target pyportal .
```

正直覚えにくいし面倒だと思いましたので、 `tinygo-docker` というヘルパーコマンドを作りました。
良かったら使ってみてください。
Windows Native 版 TinyGo と限りなく同じやり方で `tinygo` を `tinygo-docker` に変更すればビルドできます。
ただし docker 経由なので flash は出来ません。

* https://github.com/sago35/tinygo-docker

```
tinygo-docker build -o app.uf2 -target pyportal .
```

![tinygo-docker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63649/e5a82e17-9d73-05f2-ff9b-c407273a3f96.gif)

2020/02/25 追記)
上記では、 `tinygo/tinygo` を使いましたが、 `tinygo/tinygo-dev` を指定することで最新の開発版を使うことができます。
手元の環境において Windows TinyGo 0.12 + PyPortal だと float 演算でマイコンがフリーズしてしまいますが、 tinygo/tinygo-dev だと動きました。
以下のようしてビルドを実行します。

```
docker run -it --rm -v $PWD:/go/src/github.com/user/repo \
-w /go/src/github.com/user/repo \
-e GOPATH=/go \
tinygo/tinygo-dev tinygo build -o app.hex -target pyportal .
```

tinygo-docker では以下のように設定できます。
引数 `--docker-image` の位置は tinygo-docker の直後に指定する事。

```
tinygo-docker --docker-image tinygo/tinygo-dev build -o app.uf2 -target pyportal .
```