読者です 読者をやめる 読者になる 読者になる

Raspberry PIを箱から出してGoアプリを動かす

Hello Raspberry!

8th gradeの長女(Mayaa)が最近いきなり電子工作に興味をもってブレッドボードなどを買ってきたので、対抗してRaspberry PIをはじめました。幸いにまだ臭いとかは言わないまでも、いずれ生ゴミ扱い仕掛けて来かねない思春期の娘に対して、父が一度はドヤ顔をする予定。

Raspberry PIを購入し、家にあるものかき集め

  • Raspberry PI 3 model B、今回唯一の購入品。Amazon.comで新品$36.91
  • micro SSD 4GBとSSDジャケット
  • iPad用アダプタ 12wとmini USBケーブル。2.4Aなので推奨2.5A以上というのを満たさないが問題ない
  • ルーターとLANケーブル、Comcastの引込み線が居間にあるうえ、Google OnHubなので有線ポートが一つしかないから設置条件が著しく制限されてるけど、一旦WiFi設定したら抜いて自室へ移動させるので問題ない
  • MacBook Pro。一世代前のものなのでSSDが直接挿せる
  • iPhone 5s。普段使いのスマホで、今回はOnHubを見るためのもの。

OS書き込み、起動

f:id:masataka_k:20161112111533p:plain

まずとにかくOSをダウンロードしてきて、SSDに書き込む。SSDが4GBしかないし、キーボードもモニターも余ってないのでヘッドレスで全部やることとして、GUIは諦めてRaspbianのJessie Liteの方にしました。MBPにジャケットを着せたmicro SSDを挿してOSインストールします。SSDはすでにFAT32でフォーマットしてあった。

Installing operating system images on Mac OS - Raspberry Pi Documentation

$ sudo diskutil unmountDisk /dev/disk2
$ sudo dd bs=1m if=~/2016-09-23-raspbian-jessie-lite.img of=/dev/rdisk2

SSDを抜き、Raspberry PI本体に挿す。LANケーブルとマイクロUSB電源ケーブルを接続すると、起動。

f:id:masataka_k:20161112112327j:plain

写真はインスタグラムにアップするために撮ったんだけど、実はこのときは書き込み失敗してたみたいでちゃんと上がってませんでした。書き込み成功すると勝手にマウントされるのでわかります。正常動作中は通電の赤LEDが常時点灯+動作状態表示の緑LEDが点滅します。

間違ってLinux PCでの作業ドキュメント読んでてddコマンドのパラメータでbs=4mとあったので1回目は間違ってそうしてましたがそれがいけなかったのか、それともフォーマットが悪かったのか。フォーマットしなおした後でbs=1mでの書き込みで成功しています。

SSHでアクセス

SSH using Linux or Mac OS - Raspberry Pi Documentation

iPhoneのOnHubアプリでIPを調べる。"raspberrypi"で見つかった。今回は192.168.86.107。

$ ssh pi@192.168.86.107
The authenticity of host '192.168.86.107 (192.168.86.107)' can't be established.
ECDSA key fingerprint is SHA256:C3eTyJeoxwm5/+PgxlEwFy7bQYUSHn4v3beRUGiWg8E.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.86.107' (ECDSA) to the list of known hosts.
pi@192.168.86.107's password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

WiFiを起こす

Setting WiFi up via the command line - Raspberry Pi Documentation

pi@raspberrypi:~ $ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

あらかじめ $ sudo wpa_passphrase P807NETWORK password でつくったnetwork設定ブレースを末尾にコピペ。P807NETWORKはウチのSSIDで、米国にくる前に住んでたマンションの部屋番号由来です。pskはところどころxでつぶしてます。

デフォルトではcountryがGBだったのでUSに変更。Raspberry PIはたしか英国発でしたね。

# /etc/wpa_supplicant/wpa_supplicant.conf
country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
    ssid="P807NETWORK"
    psk=70a7fc3xx0aa1f72f8xfa58xxcd7aa833xxx6023e4xx1d621119cb9d3e20bd81
}

反映。

pi@raspberrypi:~ $ sudo ifdown wlan0
ifdown: interface wlan0 not configured
pi@raspberrypi:~ $ sudo ifup wlan0

LANケーブルはもう抜いちゃう。

確認。

OnHubアプリでもう一回IPを探すと、同じ"raspberrypi"で今回は192.168.86.113にみつかりました。sshで再接続。

pi@raspberrypi:~ $ ifconfig
eth0      Link encap:Ethernet  HWaddr b8:27:eb:44:d1:0b  
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:149 errors:0 dropped:2 overruns:0 frame:0
          TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:25148 (24.5 KiB)  TX bytes:6210 (6.0 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

wlan0     Link encap:Ethernet  HWaddr b8:27:eb:11:84:5e  
          inet addr:192.168.86.113  Bcast:192.168.86.255  Mask:255.255.255.0
          inet6 addr: fe80::e987:c595:7e09:9dae/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2449 errors:0 dropped:25 overruns:0 frame:0
          TX packets:175 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:387234 (378.1 KiB)  TX bytes:24169 (23.6 KiB)

ストレージのほうも確認しておきます。。。が、何をどう見ればいいんでしょうか。。。うーんと、fdisk。

pi@raspberrypi:~ $ sudo fdisk -l
Device         Boot  Start     End Sectors  Size Id Type
/dev/mmcblk0p1        8192  137215  129024   63M  c W95 FAT32 (LBA)
/dev/mmcblk0p2      137216 7774207 7636992  3.7G 83 Linux

4GBのSDカードだから、おそらくいじる必要ないんじゃないかな。

netatalkでファイル共有する

ファイルをリモートで共有することで、ssh上で書き物するめんどくささを解消する。

pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install netatalk

あとはFinderで"移動" > "サーバへ接続..."のダイアログで、afp://192.168.86.113へ接続する。

f:id:masataka_k:20161112111440p:plain

時刻設定

/etc/ntp.conf を書き換え。http://www.pool.ntp.org/zone/us より、以下を書けといわれた。

# /etc/ntp.conf

(省略)

server 0.us.pool.ntp.org
server 1.us.pool.ntp.org
server 2.us.pool.ntp.org
server 3.us.pool.ntp.org

設定書き換えた後、ntpをリスタートする。

pi@raspberrypi:~ $ sudo service ntp restart

Go Raspberry!

まずは、Go SDKのディストロをダウンロードしてきて、MBPとRaspberry PIのファイル共有で転送します。ファイル大きくないし1個だからすぐ。

Downloads - The Go Programming Language

ファイルはarmv6lのフィックスが入ったファイル名のもの。最新1.7.3でもちゃんとありました。

pi@raspberrypi:~ $ sudo tar -C /usr/local/ -xzf ~/go1.7.3.linux-armv6l.tar.gz

展開はちょっと時間かかる。インストールは以上。

pi@raspberrypi:~ $ mkdir go
pi@raspberrypi:~ $ mkdir go/vendor
pi@raspberrypi:~ $ vi .bash_profile

Go関係の環境変数を設定します。

# .bash_profile
export PATH=/usr/local/go/bin:$PATH
export GOPATH=~/go/vendor:~/go

GOPATHはVendoring対応しときました。おもむろにgo getしてみようとしたら、gitがないって怒られた。

pi@raspberrypi:~ $ source .bash_profile
pi@raspberrypi:~ $ go version
go version go1.7.3 linux/arm
pi@raspberrypi:~ $ sudo apt-get install git
pi@raspberrypi:~ $ go get goji.io

予定通りきれいにgoji.ioのソース群がvendorフォルダの下に入ってくれました。

helloサーバー起動

Goji

goji.ioのトップにあるサンプルをそのままコピペしてgo runして。。。ブラウザから192.168.86.113:8000が見えない。接続が拒否された。。。sshで別に接続してローカルにcurlすると動く。。。あ、localhostの名前解決の問題か!

// ~/go/src/hello/hello.go
package main
import (
        "fmt"
        "net/http"
        "goji.io"
        "goji.io/pat"
)

func hello(w http.ResponseWriter, r *http.Request) {
        name := pat.Param(r, "name")
        fmt.Fprintf(w, "Hello, %s!", name)
}

func main() {
        mux := goji.NewMux()
        mux.HandleFunc(pat.Get("/hello/:name"), hello)
        http.ListenAndServe(":8000", mux)
}

上記のようにGoソースを書き換えて、ビルド。

pi@raspberrypi:~ $ go build hello
pi@raspberrypi:~ $ ./hello&
[1] 15120

最初のgoji.ioサンプルはhttp.ListenAndServeの引数で"localhost:8000"と指定しているからダメでした。上記のように":8000"とポート番号だけにしておけばOK。ブラウザからhttp://192.168.86.113:8000/hello/masatakaをたたくと、ちゃんとでてきます。

GPIO, PMW

GPIO: Models A+, B+, Raspberry Pi 2 B and Raspberry Pi 3 B - Raspberry Pi Documentation

Raspberry Pi with Gobot

GitHub - sarfata/pi-blaster: PWM on the Raspberry pi - done properly (in hardware, stable)

ブレッドボードとつなぐのはGPIOというもの利用するんですね。汎用目的入出力。ブレッドボードのキットの方に繫ぐための線がついていました。ドキュメントにLED光らせるのがあるので、始めるならここからかな。そして抽象化したライブラリのGobotと、GobotのインフラとしてのPI-Blasterでコントロールする。。。

pi@raspberrypi:~ $ sudo apt-get install autoconf
pi@raspberrypi:~ $ git clone https://github.com/sarfata/pi-blaster.git
pi@raspberrypi:~ $ cd pi-blaster
pi@raspberrypi:~/pi-blaster$ ./autogen.sh 
pi@raspberrypi:~/pi-blaster$ ./configure 
pi@raspberrypi:~/pi-blaster$ make
pi@raspberrypi:~/pi-blaster$ sudo make install

PI-Blasterのドキュメントにあるとおりに$ sudo apt-get install pi-blaster でインストールしてみようとしたら、一時的なのかわかりませんが"E: Unable to locate package pi-blaster"とエラー出しましたので、githubからクローンしてRaspberry上でmakeする方法としています。ドキュメントのExampleから、PI-Blasterは標準入力でPINに命令を送る仕組みと理解しました。そんなこんなで知らない単語として「PWM」というのが出てきたので調べると。。。

PWM(Pulse Width Modulation)とは、半導体を使った電力を制御する方式の1つです。オンとオフの繰り返しスイッチングを行い、出力される電力を制御します。

引用:PWMとは | 東芝 ストレージ&デバイスソリューション社

。。。気をとりなおして、Gobotをgo get。

pi@raspberrypi:~/pi-blaster$ cd ~
pi@raspberrypi:~$ go get -d -u github.com/hybridgroup/gobot/...
pi@raspberrypi:~$ go install github.com/hybridgroup/gobot/platforms/raspi

LEDチカチカ

// ~/go/src/blinker/blinker.go
package main
import (
        "time"
        "github.com/hybridgroup/gobot"
        "github.com/hybridgroup/gobot/platforms/gpio"
        "github.com/hybridgroup/gobot/platforms/raspi"
)

func main() {
        gbot := gobot.NewGobot()
        r := raspi.NewRaspiAdaptor("raspi")
        led := gpio.NewLedDriver(r, "led", "7")
        work := func() {
                gobot.Every(1*time.Second, func() {
                        led.Toggle()
                })
        }
        robot := gobot.NewRobot("blinkBot",
                []gobot.Connection{r},
                []gobot.Device{led},
                work,
        )
        gbot.AddRobot(robot)
        gbot.Start()
}

こちらのGobotページにあったサンプルをそのまま流し込みました。そしてビルド&実行。

pi@raspberrypi:~$ mkdir ~/go/src/blinker
pi@raspberrypi:~$ vi ~/go/src/blinker/blinker.go
pi@raspberrypi:~$ go build blinker
pi@raspberrypi:~$ ./blinker 
2016/11/12 01:41:39 Initializing Robot blinkBot ...
2016/11/12 01:41:39 Initializing connections...
2016/11/12 01:41:39 Initializing connection raspi ...
2016/11/12 01:41:39 Initializing devices...
2016/11/12 01:41:39 Initializing device led ...
2016/11/12 01:41:39 Starting Robot blinkBot ...
2016/11/12 01:41:39 Starting connections...
2016/11/12 01:41:39 Starting connection raspi...
2016/11/12 01:41:39 Starting devices...
2016/11/12 01:41:39 Starting device led on pin 7...
2016/11/12 01:41:39 Starting work...
^C2016/11/12 01:42:27 Stopping Robot blinkBot ...

Crrl+Cで止めました。きちんと動いているっぽいけど、LEDを接続まだしていないので見かけ変わらない。。。ここで長女登場でブレッドボードを組んでもらいました。

f:id:masataka_k:20161113192539j:plain

サンプルコードで、NewLedDriverの引数に渡されている「7」が物理pin番号の7。GPIOの7番ではない。この7番pinにプラス、39番pin(一番外側のground)にマイナスで、組んだ回路につなげて実行したら、プログラムどおりに1秒毎にトグルされてブレッドボードに刺したLEDがゆっくりとチカチカしました!

このとき注意ポイントは実行時にはsudoで実行すること。最初は知らずにしばらく沈黙してました。GPIO操作はルート権限。まあ、そうでしょうね。

電源オフ

pi@raspberrypi:~ $ sudo shutdown -h now

おー、久しぶりにこのコマンド叩いたよ!クラウド時代には絶対唱えないshutdown。しばらく基盤の緑LEDがチカチカしたのちに基盤の赤LEDのみ点灯となります。電源ケーブル抜いて、おしまい。

感想

想像以上に整備されちゃってて普通のLinux&普通のGoの世界まで速攻で来れちゃいました。今回程度のことなら容量もパワーも十分にあったのでRaspberry PIの上にSDKいれてgo getからアプリケーションビルドまでしましたが、クロスコンパイルする方が筋がよいかもしれません。

モニター&キーボードをつながないなら、Intel Edisonのほうが攻めてる感じになったかなと考えましたが、Raspberry PIのほうが価格が安く、それゆえかケースなどの周辺も充実してるんでOK。