Arduino Unoでtinygoをやってみた


はじめに

知人の手伝いで行った同人ハードウェアフェスの立ち話でtinygoなるものを聞きました。
golangをLLVMでCに変換してBluePill(STM32F103)や micro:bitなどnrfシリーズで動作するとか!

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

emgoというのもBluePillやnrfシリーズに対応してて、以前ちょっと触ってみたのですがご無沙汰なので、こっちをArduino Unoで試したいと思います。

環境構築

公式ページの手順に従い必要なものを入れます。

以下2つが必要になります。

  • Go 1.11+
  • LLVM 7 (for example, from apt.llvm.org
$ go version
go version go1.11 linux/amd64

go はもともと入っていたのでLLVMを入れます。
※ちなみにOSはUbuntu 18.04でやってます。

$ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install clang-7 lldb-7 lld-7
$ sudo apt-get install libllvm-7-ocaml-dev libllvm7 llvm-7 llvm-7-dev llvm-7-doc llvm-7-examples llvm-7-runtime

Arduinoで試してみるので以下を入れます

  • binutils (avr-objcopy) for flashing.
  • GCC (avr-gcc) for linking object files.
  • libc (avr-libc), which is not installed on Debian as a dependency of avr-gcc.
  • avrdude for flashing to an Arduino.
$ sudo apt-get install gcc-avr binutils-avr avr-libc
$ sudo apt-get install avrdude

必要なものがそろったらtinygoをセットアップします。

$ go get -u github.com/tinygo-org/tinygo
# github.com/tinygo-org/tinygo/loader
/go/src/github.com/tinygo-org/tinygo/loader/libclang.go:16:10: fatal error: clang-c/Index.h: No such file or directory
 #include <clang-c/Index.h> // if this fails, install libclang-7-dev
          ^~~~~~~~~~~~~~~~~
compilation terminated.

失敗しました。メッセージの通りにlibclang-7-devを入れて再チャレンジ

$ sudo apt-get install libclang-7-dev
$ go get -u github.com/tinygo-org/tinygo
$ go install github.com/tinygo-org/tinygo
$ which tinygo
/go/bin/tinygo

インストールできました。
Home > Getting Started > Usage をやってみます。

/tinygo/srcに移動して、tinygo run examples/testを打ってみます。

$ tinygo run examples/test/
Hello world from Go!
The answer is: 42
5 ** 2 = 25
3 + 12 = 15
fib(11) = 89
sumrange(100) = 5050
strlen foo: 3
map length: 2
map read: answer = 42
  answer = 42
  foo = 3
map length: 1
map read: data = 3
  data = 3
len/cap foo: 4 4
len/cap bar: 3 5
len/cap foo[1:2]: 1 3
foo[3]: 5
sum foo: 12
copy foo -> bar: 3
sum bar: 7
thing: foo
is int: 5
is byte: 120
is string: foo
is Thing: foo
is *Thing: foo
is *Thing: foo
is Doubler: 6
Stringer.String(): foo
Stringer.(*Thing).String(): foo
hello from function pointer: 5
deferring...
...run as defer 2
...run as defer 1
bound method: foo
thing inside closure: foo
inside fp closure: foo 3
lower to upper char: h -> H

tiny-goがあるフォルダに移動して、make gen-deviceを実行します。

$ cd /go/src/github.com/tinygo-org/tinygo
$ make gen-device
./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
lib/avr/packs/atmega/AT90CAN128.atdf
lib/avr/packs/atmega/AT90CAN32.atdf
lib/avr/packs/atmega/AT90CAN64.atdf
lib/avr/packs/atmega/AT90PWM1.atdf

省略

src/device/stm32/stm32l1xx.go
src/device/stm32/stm32l4x1.go
src/device/stm32/stm32l4x3.go
src/device/stm32/stm32w108.go
src/device/stm32/stm32l4x2.go
src/device/stm32/stm32l4x5.go
$

サンプル実行

サンプルを実行してみます。

$ tinygo flash -target=arduino examples/blinky1

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: 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 "/tmp/tinygo130840926/main.hex"
avrdude: input file /tmp/tinygo130840926/main.hex auto detected as Intel Hex
avrdude: writing flash (698 bytes):

Writing | ################################################## | 100% 0.12s

avrdude: 698 bytes of flash written
avrdude: verifying flash memory against /tmp/tinygo130840926/main.hex:
avrdude: load data flash data from input file /tmp/tinygo130840926/main.hex:
avrdude: input file /tmp/tinygo130840926/main.hex auto detected as Intel Hex
avrdude: input file /tmp/tinygo130840926/main.hex contains 698 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.10s

avrdude: verifying ...
avrdude: 698 bytes of flash verified

avrdude done.  Thank you.

おおお!!
GoのコードがArduinoに書き込まれました!!!
秒数を変えると点滅の速度が変わりました。

blinky1.go
package main

// This is the most minimal blinky example and should run almost everywhere.

import (
  "machine"
  "time"
)

func main() {
  led := machine.GPIO{machine.LED}
  led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
  for {
    led.Low()
    time.Sleep(time.Millisecond * 300)

    led.High()
    time.Sleep(time.Millisecond * 300)
  }
}

Goでマイコンを動かすのは新鮮ですね