Raspberry PIで温度湿度センサーをRubyで動かす

## はじめに
この記事では次のようなことをしています。

- Raspberry Piを使います
- OSはRaspbianを使います
- 温度湿度センサーはAM2321を使います
- AM2321はI2Cで接続します
- センサーの読み出しにはRubyのコードを使用します
- 値の読み出しのみでWebに連携するなどは行なっていません

![image](https://qiita-image-store.s3.amazonaws.com/0/55138/5c592ed4-36bb-3692-2065-a1972004c37f.png)

## AM2321センサー
温度と湿度を同時に測定できるセンサーモジュールです。I2Cの他に1-wireに似た方式で通信できます。
ピンは1.27mmピッチなので汎用のブレッドボードに繋ぐ場合は2.54mmピッチに変換してあげる必要があります。

![image](https://qiita-image-store.s3.amazonaws.com/0/55138/b1564191-1f10-5693-554d-4dc1748aa992.png)

### 参考サイト
- [秋月電子通商：温湿度センサ　モジュール　ＡＭ２３２１](http://akizukidenshi.com/catalog/g/gM-07001/)
- [デジタル 温度・湿度センサー AM2321 を制御する](http://www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/AM2321/AM2321test.html)

## 配線
配線は次の図の通りです。
なお、通常I2Cの接続にはプルアップ抵抗が必要ですが、Raspberry Piは標準で入っているため不要です。
![image](https://qiita-image-store.s3.amazonaws.com/0/55138/1c0bf835-cfc2-f18d-0498-200cdef11963.png)


### 参考サイト
- [Raspberry PiでIO制御] (http://www.pwv.co.jp/~take/TakeWiki/index.php?raspberrypi%2FRaspberry%20Pi%E3%81%A7IO%E5%88%B6%E5%BE%A1#f82dc47c)
- [i2c pull up resistors](http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=7664)


## Raspbianの設定
Raspbianの標準ではi2cが有効になっていないので有効にします。

### i2c-toolsのインストール
次のコマンドでインストールされます

```bash
$ sudo apt-get install i2c-tools
```

### 使用するユーザー(UserId=pi)をi2cのグループに追加する
piユーザーでi2cにアクセスできるようにするため、i2cグループに追加します。

```bash
$ sudo adduser pi i2c
```

### /etc/modules
次のコマンドで/etc/modulesを編集します。

```bash
$ sudo vi /etc/modules
```

次の２行を追加します。

```
i2c-bcm2708
i2c-dev
```

### /etc/modprobe.d/raspi-blacklist.conf
次のコマンドで/etc/modprobe.d/raspi-blacklist.confを編集します。

```bash
$ sudo vi /etc/modprobe.d/raspi-blacklist.conf
```

次の１行をコメントアウトします。

```
#blacklist i2c-bcm2708
```

### 参考サイト
- [Raspberry Piで「I2C LCD Breakout」を使ってみた！](http://mag.switch-science.com/2013/07/30/use-i2c-lcd-brakeout-board-on-raspberry-pi/)

## 疎通確認
前述でインストールしたi2c-toolsのi2cdetectコマンドを使って疎通確認します。
「i2cdetect -y 1」コマンドで確認できます。

```bash
$ i2cdetect -y 1 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- 5c -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --        
```

AM2321のアドレスである0x5cが検出されました。
ちなみにAM2321はセンサーが自己発熱による湿度誤差を低減するため、休止状態になるという機構があるそうです。
このため、休止状態のときにi2cdetectコマンドを実行すると検出されないことがります。
この場合、複数回連続で実行すると検出されるようになります。
この休止状態の機構のためか、私の環境ではi2c-toolsのコマンドは全般的に正しく動きませんでした。ちょっと辛いです。

- [Raspberry Piに温度センサーを繋いでRubyで取得してみた](http://blog.makotokw.com/2013/10/17/raspberry-pi%E3%81%AB%E6%B8%A9%E5%BA%A6%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%82%92%E7%B9%8B%E3%81%84%E3%81%A7ruby%E3%81%A7%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)


## Rubyのコード

Rubyからi2cを操作するため、i2cのGemが必要になるのでインストールします。

``` 
sudo gem install i2c
```

次のコードはi2c経由でAM2321経由でRubyで値を取得するコードです。
- Wake up sensorの箇所は休止状態のセンサーを起こす処理です
- Read sensor valuesの"\x03\x00\x04"は、AM2321固有のコマンドです [参考](http://www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/AM2321/AM2321test.html)
- CRC16は自前で計算しています

```rb
#!/usr/bin/env ruby

require 'rubygems'
require 'i2c'

class AM2321

  def initialize(path, address = 0x5c)
    @device = I2C.create(path)
    @address = address
  end

  def crc16(data)
    crc = 0xFFFF
    data.each do |b|
      crc ^= b;
      8.times do
        if (crc & 0x01) != 0 then
          crc = crc >> 1
          crc ^= 0xA001
        else
	  crc = crc >> 1
        end
      end
    end
    return crc
  end

  def read
    # Wake up sensor
    begin
      @device.write(@address, "")
    rescue
      # ignore
    end

    # Read sensor values
    begin
      s = @device.read(@address, 8, "\x03\x00\x04")
    rescue
      return nil
    end
    func_code, ret_len, hum_h, hum_l, temp_h, temp_l, crc_l, crc_h = s.bytes.to_a
    orig_crc = (crc_h << 8) | crc_l
    hum = (hum_h << 8) | hum_l
    temp = (temp_h << 8) | temp_l

    # Calc CRC
    crc = crc16(s[0,6].bytes)

    return nil if crc != orig_crc
    return hum/10.0, temp/10.0
  end

end

sensor = AM2321.new('/dev/i2c-1')

100.times do
  p sensor.read
  sleep(0.5)
end
```

### 参考サイト
- [Raspberry Piに温度センサーを繋いでRubyで取得してみた](http://blog.makotokw.com/2013/10/17/raspberry-pi%E3%81%AB%E6%B8%A9%E5%BA%A6%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%82%92%E7%B9%8B%E3%81%84%E3%81%A7ruby%E3%81%A7%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)
- [Raspberry Pi で湿度センサ AM2321 を使う](http://technology-memo.seesaa.net/article/404719464.html)
- [AM2321 Userspace Driver](https://github.com/takagi/am2321)

## おわりに
AM2321をRaspberryPiで動作させたくて始めたのですが意外にやることが多くて結構嵌りました。
Rubyにしたのは勉強がてらというので特に意味はありません。
とりあえず値が読み出せたので、ここからCrondで定期的に値を読み取り、Webにアップロードしたり、Google Spread Sheetに保存したりするなどの応用に繋げられるようになりました。
繋げられるものを繋いでいって面白いものが作れればと思います。