Go Advent Calendar の12月23日目担当 @yanolab です。 qiita の投稿は初めてだ。。。
23日目の記事では、Golang + Raspberry Pi + LPS331AP(I2C気圧センサーモジュール)で気圧・温度を測定してみたので紹介したいと思います。
個人的な Golang の好きなところにクロスコンパイルがすごく容易というものがあります。マルチプラットフォームで動くツールをよく書く私としてはものすごく助かります。また、 Golang は ARM 用バイナリ出力をサポートしているので、 開発は汎用のIAマシンでできます。さらに、Golang でコンパイルしたものは libc にすら依存しないという男気っぷりを発揮しているのでデプロイも対象マシンにコピーするだけ。と、簡単です。簡単すぎます。
最近では Raspberry Pi の購入は比較的容易なようです。 Amazon でも買えますね。LPS331AP は秋月 で買えます。センサーモジュールはいろいろあるんですが、I2C通信ができるものが Raspberry Pi から扱うのが容易なのでおすすめです。
Raspbery Pi でセンサーモジュールとやりとりを行うためには I2C が簡単です。Raspberry Pi とLPS331APモジュールのつなぎ方等はここなどを参照するとよいかと思います。いろいろ記事がありますので、そこはがんばってくださいwセンサーと Raspberry Pi が I2C でやりとりできれば準備完了です。
本題
Golang の記事なので、今回は Golang から I2C 通信を行います。早速 Google 先生に聞いてみると、やはり先人がいましたが、残念なが紹介記事が 404 になってました・・・。レポジトリは残っていたようなので、開発には問題なさそうです。
まずは、go のクロスコンパイル用の準備をしましょう。go をインストールしたフォルダのsrcに移動し、下記のコマンドを実行します。
sudo GOARCH=arm ./make.bash
ビルドが走って、arm 用のクロス環境の準備ができます。めっちゃ簡単ですね。
次に、ライブラリをGOPATH以下にインストールしましょう。下記のコマンドでライブラリのインストールができます。
GOARCH=arm go get bitbucket.org/gmcbay/i2c
GOARCH=arm を忘れないようにしてください。これを忘れるとホストマシーンのCPU向けにビルドされます。
あとは、I2C 通信でRaspberry Pi から LPS331AP のセンサーデータを読み込むプログラムを書くだけです。センサーのスペックシートは秋月のページにありますのでそれを見ながら実装します。基本的には コントロールレジスタ(0x20)にこれからデータを読み込むということを書き込み、後はデータを専用のレジスタ(0x28-0x2a, 0x2b-0x2c)から読み込むだけです。lps331apをgolangから使えるようにしたコードを参考までに貼り付けておきます。
package main
import (
"bitbucket.org/gmcbay/i2c"
"errors"
)
type LPS331AP struct {
bus *i2c.I2CBus
addr byte
active bool
}
func newDevice() *LPS331AP {
return &LPS331AP{}
}
func (l *LPS331AP) Init(busNumber byte, addr byte) error {
var err error
l.bus, err = i2c.Bus(busNumber)
l.addr = addr
return err
}
func (l *LPS331AP) Read(reg byte) (byte, error) {
buf, err := l.bus.ReadByteBlock(l.addr, reg, 1)
if err != nil {
return 0, err
}
return buf[0], nil
}
func (l *LPS331AP) ReadPressure() (float32, error) {
buf := make([]byte, 3)
for idx := 0x28; idx <= 0x2a; idx++ {
var err error
buf[idx-0x28], err = l.Read(byte(idx))
if err != nil {
return 0, err
}
}
return float32(int(buf[2])<<16|int(buf[1])<<8|int(buf[0])) / 4096.0, nil
}
func (l *LPS331AP) ReadTemperature() (float32, error) {
buf := make([]byte, 2)
for idx := 0x2b; idx <= 0x2c; idx++ {
var err error
buf[idx-0x2b], err = l.Read(byte(idx))
if err != nil {
return 0, err
}
}
return 42.5 + float32(^(int16(buf[1])<<8|int16(buf[0]))+1)*-1.0/480.0, nil
}
func (l *LPS331AP) Active() error {
id, err := l.Read(0x0f)
if err != nil {
return err
}
if id != 0xbb {
return errors.New("Invalid device.")
}
if err := l.bus.WriteByte(l.addr, 0x20, 0x90); err != nil {
return err
}
l.active = true
return nil
}
func (l *LPS331AP) Deactive() error {
if !l.active {
return nil
}
if err := l.bus.WriteByte(l.addr, 0x20, 0x0); err != nil {
return err
}
l.active = false
return nil
}
メイン関数からは上記の構造体を使うだけです。読み込んだデータをコンソールに書き出しています。
package main
import (
"fmt"
)
func execOrDie(f func() error) {
if err := f(); err != nil {
panic(err)
}
}
func main() {
device := newDevice()
execOrDie(func() error { return device.Init(1, 0x5d) })
execOrDie(device.Active)
defer device.Deactive()
pressure, _ := device.ReadPressure()
temperature, _ := device.ReadTemperature()
fmt.Printf("%.2f\t%.2f\n", pressure, temperature)
}
ビルドは下記のようにします。
GOARCH=arm go build *.go
あとは生成されたバイナリを SCP なりでコピーすれば実行できます。
終わりに
準備不足な感があって申し訳ないですが、 Golang でもセンサーと Raspberry Pi を使えば簡単なセンサーデバイスが作れることを感じとっていただければと思います。さらに Golang では、Webアプリも簡単に作れますので、リアルタイムに取得したデータを公開したり、標準ライブラリの RPC を使ってセンサーネットワークを構築するのもおもしろそうです。 Google webapps にデータを集めてもいいですね。Golangすてき!!!
続いて明日は moriyoshi さんです。よろしくお願いします。