Raspberry Piが動くようになったので、何かセンサーをつないでIoTの実験をやることにした。一番簡単で実用性があるものといったら気温など気象データの測定・収集だろう。1チップで温度・湿度・気圧を測定できるセンサーを接続し、部屋の状態を観測してみた。秋月電子のボッシュBME280を使った温湿度・気圧センサモジュールキットAE-BME280。1個1080円とちょっと高いが、1チップで3種類の測定ができるのだから、バラで買うよりは割安だ。
センサの仕様は
電源電圧 | DC1.71V~3.6V |
通信方式 | I2C、SPI |
測定レンジ | 温度:-40~+85度、±1度 湿度:0~100%±3% 気圧:300~1100hPa、±1hPa |
分解能 | 温度:0.01度、湿度:0.008%、気圧:0.18pa |
消費電力 | 3.6μA(測定時)0.1μA(スリープモード) |
I2Cアドレス | 0x76、0x77 |
基板サイズ | 16×10mm、1.2mm厚 |
キットはプリント基板にBME280、0.1㎌のコンデンサ、4.7kΩのプルアップ抵抗が実装されている。BME280をはじめ、いずれも表面実装タイプなので、とてもじゃないが自分で作ることはできない。ただ、6本のピンヘッダは購入者が半田付けしないといけない。同梱されているピンヘッダは10ピンだったので、4ピン分をニッパーで折って半田付けする。
いちおうブッレドボードに組むけれど周辺部品はゼロ。単にピンヘッダを刺し、ジャンパー線でRaspberry PiのGPIOピンとつ
なぐ。メスーメスのジャンパー線があればブレッドボード無しで済む。
つなぎ方はI2Cインターフェイスを使うので
AK-BME280 | データ | Raspberry Pi GPIO |
1ピン | VDD | 1ピン |
2ピン | GND | 6ピン |
3ピン | 未使用 | |
4ピン | SDA | |
5ピン | 未使用 | |
6ピン | SCL | 5ピン |
となる。今回、SDAは使わないので接続するのは3本だけ。
本来ならばジャンパー線はVDDが赤、GNDが黒、SDAとSCLに白とか黄色とかを使えばいいのだが、手持ちの関係で黒と赤だけになってしまった。
RasbianにI2Cモジュールなどをインストールする
標準状態のRasbianはI2Cを扱えないので、必要なモジュールをインストールしなければならない。エディタで/etc/modulesを開く。
$ sudo vi /etc/modules
次の2行を書き込み、保存終了。
i2c-bcm2708
i2c-dev
rasbianをアップデートし、pythonのライブラリをインストールする。
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install python-smbus
再起動する。
$ sudo reboot now
再起動したらエディタで/etc/modprobe.d/raspi-blacklist.confを開き、spi-bcm2708とi2c-bcm2708があればコメントアウトする。
$ sudo vi /etc/modprobe.d/raspi-blacklist.conf
行頭に「#」を付けてコメントアウトする。
#blacklist spi-bcm2708
#blacklist i2c-bcm2708
BME280が認識されているか確認する。
$ sudo 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: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --
BME280のアドレスx76が表示され、センサーが認識されていることが確認できた。
Pythonのコードに手を加える
センサーからデータを読み取るプログラムはスイッチサイエンスがPythonのコードを公開していたので、それに手を入れて使う。
$ mkdir ~/python_apps/ss
$ cd python_apps/ss
$ wget https://raw.githubusercontent.com/SWITCHSCIENCE/BME280/master/Python27/bme280_sample.py
まずはデータが取れるかどうかテスト
$ sudo python bme280_sample.py
$ temp : 36.00 ℃
$ pressure : 997.62 hPa
$ hum : 47.03 %
ちゃんと動いている。
このサンプルプログラムにタイムスタンプ取得を加え、日付、時刻、温度、湿度、気圧の順番にCSVで出力するように修正する。pythonの文法とかよく知らないでツギハギしているので、美しくない。
#!/usr/bin/python #coding: utf-8 import smbus import time import datetime # datetimeモジュールのインポート import locale # import文はどこに書いてもOK(可読性などの為、慣例でコードの始め の方) # # today()メソッドで現在日付・時刻のdatetime型データの変数を取得 d = datetime.datetime.today() # ↑モジュール名.クラス名.メソッド名 bus_number = 1 i2c_address = 0x76 bus = smbus.SMBus(bus_number) digT = [] digP = [] digH = [] t_fine = 0.0 timestamp = 0 # 時刻 jikoku = 0 # 時刻 kion = 0 # 気温 shitsudo = 0 # 湿度 kiatsu = 0 # 気圧 def writeReg(reg_address, data): bus.write_byte_data(i2c_address,reg_address,data) def get_calib_param(): calib = [] for i in range (0x88,0x88+24): calib.append(bus.read_byte_data(i2c_address,i)) calib.append(bus.read_byte_data(i2c_address,0xA1)) for i in range (0xE1,0xE1+7): calib.append(bus.read_byte_data(i2c_address,i)) digT.append((calib[1] << 8) | calib[0]) digT.append((calib[3] << 8) | calib[2]) digT.append((calib[5] << 8) | calib[4]) digP.append((calib[7] << 8) | calib[6]) digP.append((calib[9] << 8) | calib[8]) digP.append((calib[11]<< 8) | calib[10]) digP.append((calib[13]<< 8) | calib[12]) digP.append((calib[15]<< 8) | calib[14]) digP.append((calib[17]<< 8) | calib[16]) digP.append((calib[19]<< 8) | calib[18]) digP.append((calib[21]<< 8) | calib[20]) digP.append((calib[23]<< 8) | calib[22]) digH.append( calib[24] ) digH.append((calib[26]<< 8) | calib[25]) digH.append( calib[27] ) digH.append((calib[28]<< 4) | (0x0F & calib[29])) digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F)) digH.append( calib[31] ) for i in range(1,2): if digT[i] & 0x8000: digT[i] = (-digT[i] ^ 0xFFFF) + 1 for i in range(1,8): if digP[i] & 0x8000: digP[i] = (-digP[i] ^ 0xFFFF) + 1 for i in range(0,6): if digH[i] & 0x8000: digH[i] = (-digH[i] ^ 0xFFFF) + 1 def readData(): data = [] for i in range (0xF7, 0xF7+8): data.append(bus.read_byte_data(i2c_address,i)) pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) hum_raw = (data[6] << 8) | data[7] compensate_T(temp_raw) compensate_P(pres_raw) compensate_H(hum_raw) def compensate_P(adc_P): global t_fine pressure = 0.0 v1 = (t_fine / 2.0) - 64000.0 v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5] v2 = v2 + ((v1 * digP[4]) * 2.0) v2 = (v2 / 4.0) + (digP[3] * 65536.0) v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8) + ((digP[1] * v1) / 2.0)) / 262144 v1 = ((32768 + v1) * digP[0]) / 32768 if v1 == 0: return 0 pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125 if pressure < 0x80000000: pressure = (pressure * 2.0) / v1 else: pressure = (pressure / v1) * 2 v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096 v2 = ((pressure / 4.0) * digP[7]) / 8192.0 pressure = pressure + ((v1 + v2 + digP[6]) / 16.0) global kiatsu kiatsu = pressure/100 # print "pressure : %7.2f hPa" % (pressure/100) def compensate_T(adc_T): global t_fine v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1] v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2] t_fine = v1 + v2 temperature = t_fine / 5120.0 # print "\n", # print d.strftime("%Y%m%d %H:%M:%S") # print "temp : %-6.2f" % (temperature) global kion kion = temperature def compensate_H(adc_H): global t_fine var_h = t_fine - 76800.0 if var_h != 0: var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h))) else: return 0 var_h = var_h * (1.0 - digH[0] * var_h / 524288.0) if var_h > 100.0: var_h = 100.0 elif var_h < 0.0: var_h = 0.0 # print "hum : %6.2f" % (var_h) global shitsudo shitsudo = var_h def setup(): osrs_t = 1 #Temperature oversampling x 1 osrs_p = 1 #Pressure oversampling x 1 osrs_h = 1 #Humidity oversampling x 1 mode = 3 #Normal mode t_sb = 5 #Tstandby 1000ms filter = 0 #Filter off spi3w_en = 0 #3-wire SPI Disable ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode config_reg = (t_sb << 5) | (filter << 2) | spi3w_en ctrl_hum_reg = osrs_h writeReg(0xF2,ctrl_hum_reg) writeReg(0xF4,ctrl_meas_reg) writeReg(0xF5,config_reg) setup() get_calib_param() if __name__ == '__main__': try: readData() except KeyboardInterrupt: pass #csv(): print d.strftime("%Y%m%d,%H:%M"),",%2.2f,%4.2f,%6.2f" % (kion, shitsudo,kiatsu)
要するに各モジュールでのprint命令をコメントアウトして測定値をグローバル変数に代入し、日時と並べてカンマ区切りで出力している。
この改訂プログラムがちゃんと動くことを確認したら、cronで定時実行するように設定した。毎時0分から20分ごとにプログラムを実行し、room_condition.txtというファイルに追加していく。
$ sudo crontab -e
以下の行を追加する。
0,20,40 * * * * /home/pi/python_apps/ss/bme280_sample.py >> /home/pi/room_condition.txt
catコマンドでroom_condition.txtを見れば、これまでの観測結果累積が表示される。ちなみにこれまで一番暑かった8月7日の結果は以下のとおり。
20150807,13:00, 39.08,38.30,1007.23
20150807,13:20, 39.47,37.45,1006.99
20150807,13:40, 39.51,36.82,1006.80
20150807,14:00, 39.93,36.72,1006.77
20150807,14:20, 40.24,36.70,1006.61
20150807,14:40, 40.15,35.20,1006.24
20150807,15:00, 40.49,35.65,1006.15
20150807,15:20, 40.12,39.88,1006.40
20150807,15:40, 39.80,41.64,1006.51
20150807,16:00, 39.54,42.06,1006.72
締め切った2階とはいえ、室温40度は暑すぎる。
データをCSVで出力しているのだから、統計を取るとかWebで公開するとかやってみようかな。もっともやるとしたら、少なくとも気温は直射日光が当たらず、風通しの良、雨の降り込まない屋外にセンサーを置くべきだろう。
はじめまして。
冷蔵庫に入れて計測したところ、
温度と湿度が入れ替わってるように思えます。
いかがでしょうか?
AK-BME280ですが、I2Cで使うためにはJ3をジャンパ接続する必要があります。
また5番ピン(SDOはGNDかVDDにしてI2Cのアドレスを指定すべきだと思います)
当初このページの情報だけで試して、以下のコマンドで見つかりませんでした。
i2cdetect -y 1
その後、以下のページの情報を見て動いたのですが。その辺りどうでしょうか?このページを参照して試して失敗される方が他にいないように問い合わさせてください。
http://deviceplus.jp/hobby/raspberrypi_entry_039/