08/14 秋月電子のAK-BME280とRaspberry Piで温度・湿度・気圧を測る

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ピン分をニッパーで折って半田付けする。

010e64e5db9e4fd38b4ee5f05d78d938583eac23b7

いちおうブッレドボードに組むけれど周辺部品はゼロ。単にピンヘッダを刺し、ジャンパー線でRaspberry PiのGPIOピンとつ
なぐ。メスーメスのジャンパー線があればブレッドボード無しで済む。

つなぎ方はI2Cインターフェイスを使うので

AK-BME280 データ Raspberry Pi GPIO
1ピン VDD 1ピン
2ピン GND 6ピン
3ピン 未使用  
4ピン SDA  
5ピン 未使用  
6ピン SCL 5ピン

となる。今回、SDAは使わないので接続するのは3本だけ。

ae-bme280_ブレッドボード
本来ならばジャンパー線は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で公開するとかやってみようかな。もっともやるとしたら、少なくとも気温は直射日光が当たらず、風通しの良、雨の降り込まない屋外にセンサーを置くべきだろう。

「08/14 秋月電子のAK-BME280とRaspberry Piで温度・湿度・気圧を測る」への3件のフィードバック

  1. はじめまして。
    冷蔵庫に入れて計測したところ、
    温度と湿度が入れ替わってるように思えます。
    いかがでしょうか?

  2. AK-BME280ですが、I2Cで使うためにはJ3をジャンパ接続する必要があります。
    また5番ピン(SDOはGNDかVDDにしてI2Cのアドレスを指定すべきだと思います)

    当初このページの情報だけで試して、以下のコマンドで見つかりませんでした。

    i2cdetect -y 1

    その後、以下のページの情報を見て動いたのですが。その辺りどうでしょうか?このページを参照して試して失敗される方が他にいないように問い合わさせてください。

    http://deviceplus.jp/hobby/raspberrypi_entry_039/

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください