Raspberry PiからSORACOM Beamに温度データを送る

はじめに

先日の記事でSORACOMとAWS間のプライベートネットワーク接続をまとめました。

今回はRaspberry Piでセンサからデータを取得し、ソラコムへ送信する部分をまとめます。
バイス側の実装を極力シンプルに保つため、SORACOMのメタデータサービスに必要な情報を格納するようにします。
また、デバイスごとに固有の識別子を振るのは管理が煩雑になるのでSORACOM Beamにデータを送信し、そこでIMSIやIMEIといったユニークになるキーをヘッダに付与してもらいます。

使うもの

植物観察ハンズオンで利用したものを流用します。
バイスはSORACOMのWebコンソールの「購入」からセットで買うことができます。

f:id:nananao_dev:20181222103904p:plain

また、ハンズオン資料ではシェルスクリプトでHarvestへのデータ登録を行なっていますが、今回はGo言語を利用します。

実装

それでは実装に入っていきます。

配線

ハンズオンと同等の配線を行います。

f:id:nananao_dev:20181222105346p:plain

モジュールの読み込み

GPIO4の設定を行います。/boot/config.txt の末尾に以下を追記します。

dtoverlay=w1-gpio-pullup,gpiopin=4

1-Wireバイスのモジュールを読み込ませます。/etc/modules の末尾に以下を追記します。

w1-gpio
w1-therm

設定を反映させるために再起動します。

# reboot

すると /sys/bus/w1/devices/ に '28-' から始まるディレクトリが見えるようになります。
配下の w1_slave というファイルに現在の温度が書かれています。(値の1000分の1 が温度となります。)

$ cat /sys/bus/w1/devices/28-*/w1_slave
5a 01 4b 46 7f ff 06 10 a3 : crc=a3 YES
5a 01 4b 46 7f ff 06 10 a3 t=21625

Goから値を読み込む

今回は以下のパッケージを利用しました。
github.com

実態はioutil.ReadFile("/sys/bus/w1/devices/w1_bus_master1/w1_master_slaves") で接続されているセンサーを走査しioutil.ReadFile("/sys/bus/w1/devices/" + sensor + "/w1_slave") でファイルの中身を読み取っている実直な作りになっているようです。

エラーハンドリング等省略しますが以下のように温度が取得できそうです。

package main

import (
    "fmt"
    "github.com/yryz/ds18b20"
)

func main() {
    // 接続されているセンサーを取得
    sensors, _ := ds18b20.Sensors()
    // 温度を取得(今回はセンサーを1つしか接続していないので `sensors[0]` として利用)
    t, _ := ds18b20.Temperature(sensors[0])
    // 温度を表示
    fmt.Printf(temperature: %.2f°C\n", t)
}

SORACOM メタデータから可変なデータを取得する

接続先のURL情報など変わる可能性のあるものをデバイスに埋め込んでしまうと、URLが変わるたびに全デバイスへのファイル更新が必要となってしまいます。

今回はその練習としてSORACOM BeamのURLをSORACOM メタデータから取得してみます。

まずはソラコムのWebコンソールのSIMグループ設定からメタデータをONにし、ユーザーデータを設定し保存します。

{"beamURL": "http://beam.soracom.io:8888/"}

f:id:nananao_dev:20181222121027p:plain

SIM経由で以下のURLへアクセスすると設定したデータが取得できます。

$ curl http://metadata.soracom.io/v1/userdata

これを以下のようにGoで書き直してみます。

type URL struct {
    BeamURL string `json:"beamURL"`
}

func getMetadata() (url *URL, err error) {
    res, _ := http.Get("http://metadata.soracom.io/v1/userdata")
    defer res.Body.Close()

    body, _ := ioutil.ReadAll(res.Body)

    url = &URL{}
    err = json.Unmarshal(body, &url)
    if err != nil {
        return nil, err
    }

    return url, nil
}

SORACOM Beamへデータを送信する

送りたいデータと送信先が取得できたので、SORACOM Beamへデータを送信します。
ソラコムのWebコンソールのSIMグループ設定からBeamを設定します。

前回設定したプライベート接続したAWS EC2への転送設定とIMSI/IMEIのヘッダを追加しています。

こうすることでデバイス側にIMSI/IMEIを取得する実装を省けますし、転送先を変更したくなった場合でもデバイスに手を入ることなく瞬時に変更することができそうです。

f:id:nananao_dev:20181222123325p:plain

Goで簡単なHTTP Clientを書いてみます。
取得時間と温度を5秒間隔で送信します。

type Postdata struct {
    Time time.Time `json:"time"`
    Temp float64 `json:"temp"`
}

func main() {
    // 接続されたセンサーを走査
    sensors, _ := ds18b20.Sensors()
    // メタデータを取得
    metadata, _ := getMetadata()
    // 5秒間隔を設定
    t := time.NewTicker(5 * time.Second)
    for {
        // 温度を取得
        temp, _ := ds18b20.Temperature(sensors[0])
        // POSTするデータを作成
        postdata, _ := json.Marshal(Postdata{Time: time.Now(), Temp: temp})
        // データを送信
        resp, _ := http.Post(metadata.BeamURL, "application/json", bytes.NewBuffer(postdata))
        // 5秒ブロック
        <-t.C
    }
}

まとめ

コードはこちらにのせておきます。

メタデータ、Beam等を活用することでデバイス実装をシンプルかつ変更に強いものにできそうです。
次回はBeamで付与したIMSI等の固有識別子を活用してサーバー側でデータをハンドリングしてみようと思います。

以上