ラズパイを使って温湿度をセンサから取得しよう！

##　はじめに

　私の所属している研究室では、現在ラズパイを用いてセンサから色々な値を取得してみようという工程に入っています。今回はそのセンサ（SHT-31DIS使用温湿度センサモジュールキット）についての使い方と、ラズパイとの通信方式**I2C**について記述したいと思います！（本記事は少々長めです。目が疲れたら一休みしましょう...！）


##　目標

<details><summary>**センサとラズパイをワイヤーで繋ぎ、ラズパイ側からpythonプログラムを実行して値を取得する！**</summary>

　ラズパイとpythonの実行環境、温湿度センサについては次の通り。

・ラズパイとpython

| 項目 | 詳細 |
|:-----------:|:------------:|
| Model        | Raspberry Pi 3 Model B         |
| OS      | RASPBIAN       |
| OS version        | June 2018(kernel: 4.14)         |
| python version | 3.X.X |

・温湿度センサ
![iOS の画像.jpg](https://qiita-image-store.s3.amazonaws.com/0/298123/348876fe-4759-739e-e421-c044939994c0.jpeg)


[SHT-31DIS使用　高精度温湿度センサモジュールキット](http://akizukidenshi.com/catalog/g/gK-12125/)
＊ リンク先は秋月電子さんですが、リンクが切れている可能性があります。


##　1.　I2C通信とは？
　今回この記事を書く理由になった大きな要因として、I2C通信とは何だ？と思ったことです。一般的に電子工作でセンサを用いる際は、殆どの場合で**シリアル通信**使うのですが（簡単だし）、今回使うセンサの通信方式はI2Cといったように決められています。いつものやり方では値を取得できない...といってI2Cも何なんだ？ということで今回の目標の1つに、このI2Cについて理解を深めることを設定しています。今回お勉強に使ったサイトは、WikipediaのI2Cページ英語版です。

　ぶっちゃけ言えば、I2C（Inter-Integrated Circuit）通信とは「**とある会社が開発したシリアル通信の亜種**」です。ベースとなっているのがシリアル通信だということですね。この通信の最大の特徴は、

- SCL（Serial CLock line）
- SDA（Serial DAta line）

という2本の双方向信号線で全てのデータ通信を行えるということです。
　全体像としては、クロック（SCL）とデータ（SDA）を7ビットアドレッシングとして備えたバス構成となっており、そのバスにはマスタとスレーブがあります。マスタ（主）がスレーブ（奴隷）を操ることができ、マスタが送信するSCLを基準にして、データ信号がSDA上で転送されるのです。

- マスタ（Master）：クロックを生成し、スレーブとの通信を開始する
- スレーブ（Slave）：マスタからのクロックを受信する。マスタによってアドレスが指定される

下の写真はイメージ図です。（Circuit Basicsより拝借）
![i2c仕組み.png](https://qiita-image-store.s3.amazonaws.com/0/298123/ef847bfa-b2f4-4b8e-c98a-d5421e2d1ded.png)

　複雑ですが、アドレス空間についても少しだけ記載しておきます。先程も書いてあった通り、I2Cは7ビットアドレッシングの構造を持っています。このアドレスフレームの7ビットに加えて、ReadかWriteを示す1ビットがデータメッセージのメインを占めています。7ビットは、**MSB(4ビット)＋LSB(3ビット)**で構成されており、MSBはどうやらI2C通信の利用方法ごとに分かれているらしいです。既に予約済のアドレスもあるようなので、詳しく知りたい方は英語版のI2Cのwikiを見てみることをオススメします。（リンクは参考文献に後述）
![i2c通信フレーム.png](https://qiita-image-store.s3.amazonaws.com/0/298123/c4dd3a34-60c2-20f2-9f96-99b440ceab9c.png)

　上の図はメッセージフレームの構成です。（こちらも先程の画像と同じサイト様から拝借しました）ACK/NACKは、確認応答用ですね。TCPのACKと同じ役割です。

　*まとめ*
*I2Cとは、シリアル通信の亜種。SCLとSDAという2本の信号線でデータのやり取りができる！*


##　2.　ラズパイにセンサを接続してみよう！

　さて、長くI2Cについて勉強したことですので、実際にセンサをラズパイに接続してみましょう！付属でついていた取扱説明書を見ながら進めていきます。（日本語版がある、素晴らしい！）
　ところで、私はラズパイを久しぶりに扱うに当たって、多数あるピンの役割をすっかり忘れてしまいました。そんな私と同じ方が殆どだと思いますので、ラズパイを起動してターミナルを立ち上げて下のコマンドを打ち込んで見てください。

```
$ gpio readall
```

　すると、ご丁寧にこんなものが返ってきます。
![readall.png](https://qiita-image-store.s3.amazonaws.com/0/298123/89242ea0-3ca7-a004-c651-e0992be1b31a.png)

　これをみると、どうやらセンサモジュールのSCLとSDAの接続先は決められたようです。ここに繋げば良いのですね。Vddは3.3V、GNDは0Vのピンのところにつなぎましょう。センサを使って値を取得する際は、本来ならプルアップ抵抗を介す必要がありますが、このセンサモジュールの回路図をみると、SCLとSDAには10kΩで抵抗が繋げてあるようです。つまり要らないということで**直接**OKです。
　ちなみに、センサのADRピンとGNDピンをはんだブリッジで繋げた場合、後使うI2Cアドレスが変わるので要注意です。

　*まとめ*

| センサ端子 | センサ側ピン | ラズパイ側ピン | ピン役割 |
|:-----------:|:-----------:|:------------:|:------------:|
| 1 | VDD | Pin#01 | 3.3v   |
| 2 | SDA | Pin#03 | SDA1   |
| 3 | SCL | Pin#05 | SCL1   |
| 4 | ADR | OPEN   | OPEN   |
| 5 | GND | Pin#06 | 0v(GND)|

＊ OPEN：何も接続しない

![iOS の画像 (1).jpg](https://qiita-image-store.s3.amazonaws.com/0/298123/8c6563fa-4004-a6a4-ae36-83e20d19bff8.jpeg)

　ラズパイの向きを間違えないでくださいね！！ラズベリーの絵が左にプリントしてある方です！

##　3.　I2C通信に関するライブラリを導入しよう！
　回路を作ったから、それでは次にプログラムを書こう！と言いたいところなのですが、その前にもう一つやらなければならないことがあります。それは、I2Cを利用するためのライブラリ導入です。おそらくデフォルト状態のラズパイにあるpython標準ライブラリには入っていません。（その他、i2cに関わるパッケージ「i2c-tools」、時間計測用パッケージ「time」を使います。timeはデフォルトで入っていると思います。）

　今回、I2Cを使うために導入するライブラリは**SMbus**です。詳しい説明はこの記事内では省略しますが、「I2Cから派生した規格」です。これを使ってI2C通信を利用した書き込み、読み込みを行うことができます。まだ導入できていない人はターミナルを開いて以下のコマンドを実行しましょう。（pythonバージョン2で試す方は、おそらく「python-smbus」ライブラリかもしれません。）

```
$ sudo apt-get update
$ sudo apt-get install python3-smbus
$ sudo apt-get install i2c-tools
```

　無事にライブラリ導入ができたでしょうか。最後にI2Cポートは使用する前にRaspbianで有効にしなければなりません。デフォルトではオフになっているので、オンにしましょう。

```
$ sudo raspi-config
```

　すると、設定画面が開くはずです。「5.Interfacing Options」 > 「P5 I2C」を選ぶと「Would you like the ARM I2C interface to be enabled?」と出てきます。これに「はい(enabled)」を選びましょう！　これにて準備完了です！！


##　4.　センサから値を取得できるプログラムを書こう！

　ここからは、センサの取扱説明書とにらめっこしながらの作業が始まります。主にSHT-31DISの仕様書を見ながら進めていきましょう。...と思ったのですが、実際にプログラムを提示してから解説した方が手っ取り早い気がしたので、先にセンサを扱うためのpythonプログラムを見せます！

```python:test.py
import time
import smbus

def tempChanger(msb, lsb):
    mlsb = ((msb << 8) | lsb)                                             # P1
    return (-45 + 175 * int(str(mlsb), 10) / (pow(2, 16) - 1))            # P2

def humidChanger(msb, lsb):
    mlsb = ((msb << 8) | lsb)
    return (100 * int(str(mlsb), 10) / (pow(2, 16) - 1))

i2c = smbus.SMBus(1)
i2c_addr = 0x45                                                           # P3

i2c.write_byte_data(i2c_addr, 0x21, 0x30)                                 # P4
time.sleep(0.5)

while 1:
    i2c.write_byte_data(i2c_addr, 0xE0, 0x00)                             # P5
    data = i2c.read_i2c_block_data(i2c_addr, 0x00, 6)                     # P6
    print( str('{:.4g}'.format(tempChanger(data[0], data[1]))) + "C" )
    print( str('{:.4g}'.format(humidChanger(data[3], data[4]))) + "%" )
    print("------")
    time.sleep(1)
```

　コメントアウトでナンバリングをふっているところ（P1~P6）が重要なポイントなので、そこだけピンポイントで解説していきます！その他文字列変換、進数変換等は調べればいくらでも出てくるので、そちらの方でお願いします。**以下の内容は、中身がどうなっているか気になる方だけ読むことをお勧めします。**

　*P1*
　取扱説明書をみると、温度変換式・湿度変換式式には、8ビットのMSBと8ビットのLSBを16ビットのMSB+LSBにしてから計算式に代入する必要があります。その際には、MSBを8ビット論理左シフトし、そのあとLSBと論理和(or)をとりましょう。この手順をわかりやすく手書きしてみました。
![ビットシフト.jpg](https://qiita-image-store.s3.amazonaws.com/0/298123/5d19826f-442b-9167-b3ca-1aa214333a10.jpeg)

　*P2*
　温度変換をしています。この関数の返り値の書式は**数値型**であることに注意してください。同様に、湿度の関数も下で作成しています。pow関数はべき乗計算です。

　*P3*
　接続するI2Cのアドレスです。説明書には、「I2Cのアドレス選択は、基板の4番ピン(ADR)をGNDに接続すると[0x44]となります。開放時には、[0x45]」と書いてあります。先ほどの工程2のときに、ADRのピンをGNDとはんだブリッジすると、ここのアドレス値が変わります。ちなみにきちんと接続されているかどうか、あるいはアドレス番号かいくつかどうかは下のコマンドのどちらかをいれることでわかります。I2Cデバイスの認識状況を確認します。

```
$ sudo i2cdetect -y 0
$ sudo i2cdetect -y 1
```

　*P4*
　説明書をみると、センサにはまずコマンドを与える必要があります。今回は温湿度を定期的に計測したいので、周期的連続測定コマンドをセンサに送りたいと思います。このとき、センサの繰り返し精度のレベルや測定頻度で送るコマンドが異なるようです。詳しくは参考文献に記載されているURLから確認して見てください。今回は、繰り返し精度レベル「高」・測定頻度「1mps」で実験していきます。

| 繰り返し精度レベル | 測定頻度[mps] | コマンドコード(MSB) | コマンドコード(LSB) |
|:-----------:|:-----------:|:------------:|:------------:|
| 高 | 1 | 0x21 | 30 |

　この送信に、SMBusライブラリに導入されているメソッドを使っていきます。write_byte_dataメソッドは引数を3つとり、第1引数にI2Cのアドレス、第2引数に書き込むレジスタ、第3引数に送信する値をとります。今回はI2Cアドレス、コマンドコード(MSB)、コマンドコード(LSB)がこれに該当します。

　*P5*
　ここからは、while内の繰り返しコードとなります。周期的連続測定モードでの測定値の読出しには、説明書によると、次のコマンドをセンサに送る必要があります。
![送信コマンド.png](https://qiita-image-store.s3.amazonaws.com/0/298123/c5d1af27-85d0-6187-fb86-47b4ec45cc27.png)

　まずは、I2Cアドレス、コマンドコード(MSB)、コマンドコード(LSB)を引数としてとるwrite_byte_dataメソッドで送信します。

　*P6*
　そのあとは、上の灰色部分が示すような温度・湿度測定値等の測定データが入った情報が返ってきます。8ビットで構成されたデータが6つです。[温度測定値(MSB)、温度測定値(LSB)、チェックサム、湿度測定値(MSB)、湿度測定値(LSB)、チェックサム]　これを、read_i2c_block_dataメソッドを用いて配列に格納していきます。第1引数にI2Cのアドレス、第2引数に読み込むレジスタ、第3引数に配列の長さをとります。今回は、8ビットで構成されたデータが6つなので、長さは6です。この配列は0,1番目に温度測定値[MSB, LSB]　3,4番目に湿度測定値[MSB, LSB]が入っているので、これを先ほどの変換用関数に突っ込むという手順になっています。


##　5.　試してみよう！
　では、実際にコードを動かして温湿度を測定してみましょう！　ターミナルを開いて、`python3 test.py`を実行します。
![結果.png](https://qiita-image-store.s3.amazonaws.com/0/298123/8bebc56c-fe39-490c-79fb-b298d54760af.png)

　なんとか、温度と湿度の測定ができているようです！数値も、今自分がいる空間ぐらいの温度、湿度であるかを確認してみてください。無事に確認できたらお疲れ様でした。


##　参考文献

- [I2C wikipedia 英語版](http://en.wikipedia.org/wiki/I%C2%B2C)
- [Curcuit Basics - BASICS OF THE I2C COMMUNICATION PROTOCOL](http://www.circuitbasics.com/basics-of-the-i2c-communication-protocol/)
- [SHT-31DIS 仕様書 日本語版](http://akizukidenshi.com/download/ds/sensirion/Sensirion_Humidity_Sensors_SHT3x_DIS_Datasheet_V3_J.pdf)
- [SHT-31DIS使用温湿度センサモジュールキット 取扱説明書](http://akizukidenshi.com/download/ds/akizuki/AE-SHT3x_manu_v1.0.pdf)