Raspberry Pi3に温度センサーDS18B20をつなげてAzureIoTHubに温度を送ってみた

## はじめに
ハンズオンの準備と単純に暑い部屋に帰りたくないというのがあり、外から温度がわかるものを作ってみました。

この記事はRaspberryPiでセンサー値を収集しAzureIoTHubに集約するところまでを扱います。

また、OSはRaspbian、AzureIoTHubに接続するプログラムはPythonで作成します。

## 準備
* Raspberry Pi3
* micro SDカード(Raspbianをインストール)
* 5V電源
* ブレッドボード
* 抵抗 2k~4kくらい
* DS18B20
* Azureのサブスクリプション

注意：デフォルトでSSHが無効になっているみたいです。SSH接続する場合はブートパーティションにsshというファイルを追加してください。
出典:[raspberry pi 3でSSH接続できない？](http://qiita.com/torch32171/items/c816acd5afa45fddabfe)

## 実体配線図
VCC3V, GND, データ線の3線をつなぎます。
また、データ線にはプルアップ抵抗をはさみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/91a2ed3e-fe8c-8d06-7e55-68520147cdb9.png)


## 温度を読み出す
温度センサーは1-wireという規格なので1-wireを使えるように設定する必要があります。

温度センサーのデータ線をGPIO4につなげた場合は以下のような設定を追加して再起動します。

```text:/boot/config.txt
dtoverlay=w1-gpio,gpiopin=4
```

外部のプルアップ抵抗を有効にするかどうかを切り替えれるようにするにはw1-gpio-pullupというのにするそうですが、実際に確認していないです。
その他のパラメーターなどは/boot/overlays/READMEを参照してください。

再起動後/sys/bus/w1/devices/28-<デバイスのシリアル番号>/w1_slaveを読み出すと温度値が帰ってきます。

```bash
$ cat /sys/bus/w1/devices/28-<デバイスのシリアル番号>/w1_slave

a9 01 55 00 7f ff 0c 10 9b : crc=9b YES
a9 01 55 00 7f ff 0c 10 9b t=26562
```
tを1000で割った値が温度です。

表示されている16進数は温度センサーのメモリをダンプしたもので0バイト目が温度値LSB, 1バイト目が温度値MSBです。

下4bitが小数部なのでこの例では 
0x01a9>>4 = 0x1A = 26という感じです。

## Azure IoT Hubの設定
AzureのポータルにアクセスしIoTHubを追加します。

「新規」から「モノのインターネット」「IoTHub」を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/ff73934c-2110-a4e4-5bac-7cefec261ffb.png)

名前とか料金プランとかを選びます。
ここでは無料プラン（1サブスクリプションにつき1つまで）を選びました。

無料プランの場合8000メッセージ/日 500デバイスまで登録できるみたいです。
参考：https://azure.microsoft.com/ja-jp/pricing/details/iot-hub/

次に「共有アクセスポリシー」を選択し、読み書きできる「iothubowner」を選択し、接続文字列をメモしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/69e977c3-bda5-58d1-20b7-a75dda813227.png)

ここでは全権限をもつiothubownerを使います。

最後にデバイスの追加を行います。
「Device Explorer」を選択し「追加」を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/9e868a17-4928-be1b-fc76-183c90fd631e.png)

デバイスの名前を適当に決めて「AutoGenerateKeys」にチェックを入れて「保存」を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/5ef09265-65ab-32c9-2fcb-35f5210b75d1.png)

デバイス一覧に先程追加したものが追加されているので選択し、接続文字列をメモします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/140084/11cd6af9-0941-8c5a-0c06-60df28936002.png)

## AzureIoTHubのライブラリをビルド
Python用のAzureIotHubのライブラリパッケージはRaspberryPiでは利用できないのでビルドします。
(node.jsなどではライブラリが簡単に利用できるみたいですが...)

ここでは実行したコマンドだけ示します。
詳細は[こちら](https://github.com/Azure/azure-iot-sdk-python/blob/master/doc/python-devbox-setup.md#build-the-azure-iot-hub-sdks-for-python-on-linux)を御覧ください。

```bash
sudo apt-get install -y git cmake build-essential curl libcurl4-openssl-dev libssl-dev uuid-dev
git clone --recursive https://github.com/Azure/azure-iot-sdk-python.git 
cd azure-iot-sdk-python/build_all/linux/
./setup.sh --python-version 3.4
./build.sh --build-python 3.4
cd ../../
```
ビルドが終わるとdevice/とservice/以下のsamplesフォルダーに.soファイルが出来ます。

出来上がったsoファイルと同じところにPythonのスクリプトを置きます。

室温とCPUコアの温度をAzure IoT Hubに送信するサンプル

```python:main.py
#!/usr/bin/env python

import time
import sys
import iothub_client
import json
from iothub_client import IoTHubClient, IoTHubTransportProvider
from iothub_client import IoTHubMessage, IoTHubError
import re
import datetime

PROTOCOL = IoTHubTransportProvider.MQTT
MESSAGE_TIMEOUT = 10000

# Device Explorerで確認した接続文字列
CONNECTION_STRING = "<接続文字列>"
TAG = "<任意の文字列>"

# DS18B20の値を読み出すパス
DS18B20_PATH = "/sys/bus/w1/devices/28-<DS18B20のシリアル番号>/w1_slave"
CPU_TEMP_PATH = "/sys/class/thermal/thermal_zone0/temp"

def cat(path):
    f = open(path)
    ret = f.read()
    f.close()
    return ret

# 初期化関係の関数
def iothub_client_init():
    from iothub_client_cert import CERTIFICATES
    client = IoTHubClient(CONNECTION_STRING, PROTOCOL)
    client.set_option("messageTimeout", MESSAGE_TIMEOUT)
    client.set_option("TrustedCerts", CERTIFICATES)
    client.set_option("logtrace", 0)
    return client

# 送信後に呼び出されるコールバック
def send_confirmation_callback(message, result, user_context):
    print(user_context)
    print("Result" + str(result))

# センサー値の送信処理
def main(client):
    # 室温取得
    text = cat(DS18B20_PATH)
    m = re.search(r"t=(\d*)", text)
    room_temp = int(m.group(1)) / 1000

    # CPU温度取得
    text = cat(CPU_TEMP_PATH)
    cpu_temp = int(text) / 1000

    # Azure IoT Hubへ送信するメッセージを作成
    msg = json.dumps({
        "tag":TAG , "room_temp":room_temp, "cpu_temp":cpu_temp,
        "time":datetime.datetime.now().isoformat()
    })
    message = IoTHubMessage(msg)

    # 送信 第三引数はコールバックに渡される
    client.send_event_async(message, send_confirmation_callback, msg)


if __name__ == '__main__':
    client = iothub_client_init()
    try:
        while True:
            main(client)
            time.sleep(10)
    except KeyboardInterrupt:
        print("EXIT")

```

あとはこのスクリプトを実行するだけでセンサーの値が10秒毎にAzure Iot Hubに送信されます。

## 送信された値を確認する
Azure IoT Hubに送信されたデータはその後他のサービスに通してDBに蓄積したり可視化しますが、ここではちゃんとデータが届いているかだけを確認します。

別の端末などに[iothub-explorer](https://github.com/azure/iothub-explorer)をインストールします。

```bash
npm install -g iothub-explorer
```

インストール後Rasbperry pi上でサンプルプログラムを起動し

```bash:@raspberrypi
python3 main.py
```

iothub-explorerを導入した端末で

```bash:@host
iothub-explorer monitor-events <デバイスID> --login iothubownerの接続文字列

```

を実行すると

```
Monitoring events from device raspberrypi3...
==== From: raspberrypi3 ====
{
  "tag": "tag 1",
  "cpu_temp": 49.925,
  "room_temp": 27.875,
  "time": "2017-06-26T13:52:53.844569"
}
---- properties ----
{}
====================
```

という感じにIoTHubに送信された値を確認できます。

次回はクラウドからデバイスへの制御かPowerBIでの可視化ですかね。