温湿度・気圧センサーBME280の使い方

BME280 温湿度・気圧センサー センサー

温湿度・気圧センサーBME280から温度・湿度データ気圧データを読み取る方法を紹介します。

読み取りに使用するマイコンはRaspberry Piで、プログラム言語はPythonです。

サンプルプログラムでは、センサーからデータの読み取りだけでなく、測定条件設定や補正計算なども行っています。ここでは、I2C通信で読み取ります。

データレート、ノイズ、応答時間、および消費電流などの調整可能です。

センサーの主な仕様
温度測定/湿度測定/気圧測定
測定範囲:-40~85℃/0~100%/300~1100hPa
確度:±1℃/±3%/±1hPa
分解能:0.001℃/0.008%/ 0.18hPa

通信方式:I2C通信/SPI通信

準備

BM280の購入

金属ケースで8端子のLGAと呼ばれる形状です。
センサーを手ではんだ付けするのは不可能なので、モジュールの購入になると思います。

下の写真は秋月電子通商で購入したBME280モジュールです。今回は、このモジュールを使用します。I2C通信とSPI通信のどちらの通信方式にも対応しています。

BME280 モジュール写真

モジュールとRaspberry Piの接続

接続図

ブレッドボードを使用したBME280モジュールとRaspberryPiの接続

接続写真

ブレッドボードを使用したBM280の接続写真 1
ブレッドボードを使用したBM280の接続写真 2

Raspberry Piの通信設定とPythonの操作方法

Raspberry PiのI2C通信設定を有効にします。
下の記事でRaspberry Piの通信設定と、Pythonの基本操作を説明しています。

通信プログラミング

プログラムコード

Pythonプログラムです。

import smbus

#補正用
t_fine = 0.0

digT = []
digP = []
digH = []

#I2C設定
i2c = smbus.SMBus(1)
address = 0x76

#BME280の設定
def init_bme280():
    ret = i2c.write_byte_data(address, 0xF2, 0x01)    #ctrl_hum
    ret = i2c.write_byte_data(address, 0xF4, 0x27)    #ctrl_meas
    ret = i2c.write_byte_data(address, 0xF5, 0xA0)    #config

#補正データ読み込み
def read_compensate():
    #温度補正データ読み込み
    dat_t = i2c.read_i2c_block_data(address, 0x88, 0x6)
    
    digT.append((dat_t[1] << 8) | dat_t[0])
    digT.append((dat_t[3] << 8) | dat_t[2])
    digT.append((dat_t[5] << 8) | dat_t[4])
    
    #極性判断
    for i in range(1, 2):
        if digT[i] >= 32768:
            digT[i] -= 65536
    
    #気圧補正データ読み込み
    dat_p = i2c.read_i2c_block_data(address, 0x8E, 0x12)
    
    digP.append((dat_p[1] << 8) | dat_p[0])
    digP.append((dat_p[3] << 8) | dat_p[2])
    digP.append((dat_p[5] << 8) | dat_p[4])
    digP.append((dat_p[7] << 8) | dat_p[6])
    digP.append((dat_p[9] << 8) | dat_p[8])
    digP.append((dat_p[11] << 8) | dat_p[10])
    digP.append((dat_p[13] << 8) | dat_p[12])
    digP.append((dat_p[15] << 8) | dat_p[14])
    digP.append((dat_p[17] << 8) | dat_p[16])
    
    #極性判断
    for i in range(1, 8):
        if digP[i] >= 32768:
            digP[i] -= 65536
    
    #湿度補正データ読み込み
    dh = i2c.read_byte_data(address, 0xA1)
    digH.append(dh)
    dat_h = i2c.read_i2c_block_data(address, 0xE1, 0x08)
    digH.append((dat_h[1] << 8) | dat_h[0])
    digH.append(dat_h[2])
    digH.append((dat_h[3] << 4) | (0x0F & dat_h[4]))
    digH.append((dat_h[5] << 4) | ((dat_h[4] >> 4) & 0x0F))
    digH.append(dat_h[6])
    
    #極性判断
    if digH[1] >= 32768:
        digH[1] -= 65536
    
    for i in range(3, 4):
        if digH[i] >= 32768:
            digH[i] -= 65536
    
    if digH[5] >= 128:
            digH[5] -= 256

#測定データ読み込み
def read_data():
    
    #データ読み込み
    dat = i2c.read_i2c_block_data(address, 0xF7, 0x08)
    
    #データ変換
    dat_p = (dat[0] << 16 | dat[1] << 8 | dat[2]) >> 4
    dat_t = (dat[3] << 16 | dat[4] << 8 | dat[5]) >> 4
    dat_h = dat[6] << 8 | dat[7]
    
    #補正
    tmp = bme280_compensate_t(dat_t)
    prs = bme280_compensate_p(dat_p)
    hum = bme280_compensate_h(dat_h)
    
    #表示
    print('t = ' + str(tmp))
    print('p = ' + str(prs))
    print('h = ' + str(hum))

#温度補正
def bme280_compensate_t(adc_T):
    
    global t_fine
    
    var1 = (adc_T / 8.0 - digT[0] * 2.0) * (digT[1]) / 2048.0
    var2 = (adc_T / 16.0 - digT[0]) * (adc_T / 16.0 - digT[0]) / 4096.0 \
            * digT[2] / 16384.0 
    
    t_fine = var1 + var2
    
    t = (t_fine * 5 + 128) / 256
    t= t / 100
    
    return t

#湿度補正
def bme280_compensate_p(adc_P):
    
    global  t_fine
    p = 0.0
    
    var1 = t_fine - 128000.0
    var2 = var1 * var1 * digP[5]
    var2 = var2 + ((var1 * digP[4]) * 131072.0)
    var2 = var2 + (digP[3] * 3.435973837e10)    
    var1 = ((var1 * var1 * digP[2])/ 256.0) + (var1 * digP[1]) * 4096
    var1 = (1.407374884e14 + var1) * (digP[0] / 8589934592.0)
    
    if var1 == 0:
        return 0
    
    p = 1048576.0 - adc_P
    p = ((p * 2147483648.0 - var2) * 3125) / var1
    
    var1 = (digP[8] * (p / 8192.0) * (p / 8192.0)) / 33554432.0
    var2 = (digP[7] * p) / 524288.0
    
    p = (p + var1 + var2) / 256 + digP[6] * 16.0
    p = p / 256 / 100
    
    return p

#湿度補正
def bme280_compensate_h(adc_H):
    
    global t_fine

    var_H = float(t_fine) - 76800.0
    var_H = (adc_H - (float(digH[3]) * 64.0 + float(digH[4]) / 16384.0 * \
             var_H)) * (float(digH[1]) / 65536.0 * (1.0 + float(digH[5]) / \
             67108864.0 * var_H * (1.0 + float(digH[2]) / 67108864.0 * var_H)))
    var_H = var_H * (1.0 - float(digH[0]) * var_H / 524288.0)
    
    return var_H

#BME280の初期化
init_bme280()

#補正データ読み込み
read_compensate()

#測定データ読み込み  
read_data()

プログラムの説明

  1. 初期設定
  2. センサーの設定
  3. センサーから補正データを取得
  4. センサーから温湿度データ、気圧データを取得
  5. データを補正
  6. 補正したデータを画面に表示

1.初期設定

#補正用
t_fine = 0.0

digT = []
digP = []
digH = []

補正用の変数(配列)宣言です。

#I2C設定
i2c = smbus.SMBus(1)
address = 0x76

i2c = smbus.SMBus(1)
インスタンス生成です。使用するBus1を指定します。

address = 0x76
アドレス指定です。モジュールのSDO(5番)ピンをGNDへ接続して0x76となります。
通信時にこのアドレスを指定します。

LXTerminalから「i2cdetect -y 1」コマンドで、アドレスが0x76であることを確認出来ます。

2.センサーの設定

#センサーの設定
def init_bme280():
    ret = i2c.write_byte_data(address, 0xF2, 0x01)    #ctrl_hum
    ret = i2c.write_byte_data(address, 0xF4, 0x27)    #ctrl_meas
    ret = i2c.write_byte_data(address, 0xF5, 0xA0)    #config

設定は write_byte_data(adr, reg, dat)関数を使用します。

第1引数 adr:I2C通信のアドレス
第2引数 reg:レジスタ(設定したい項目の記憶場所)
第3引数 dat:データ(レジスタに設定する内容)

サンプルプログラムでは、3つのレジスタ設定をしています。レジスタの設定内容は以下の通りです。

ctrl_humレジスタ(0xF2)

i2c.write_byte_data(address, 0xF2, 0x01)

0xF2
osrs_h[2:0](湿度オーバーサンプリング)

0x01
osrs_h[2:0](001:×1)

詳細は、データシートの『Table 19: Register 0xF2 “ctrl_hum”』で確認できます。

osrs_h2 osrs_h1 osrs_h0
0 0 1
0 1

ctrl_measレジスタ(0xF4)

i2c.write_byte_data(address, 0xF4, 0x27)

0xF4
mode[1:0](センサーモード)
osrs_p[2:0](気圧オーバーサンプリング)
osrs_t[2:0](温度オーバーサンプリング)

0x27
mode[1:0](11:ノーマルモード)
osrs_p[2:0](001:×1)
osrs_t[2:0](001:×1)

詳細は、データシート『Table 22: Register 0xF4 “ctrl_meas”』で確認できます。

osrs_t2 osrs_t1 osrs_t0 osrs_p2 osrs_p1 osrs_p0 mode1 mode0
0 0 1 0 0 1 1 1
2 7

configレジスタ(0xF5)

i2c.write_byte_data(address, 0xF5, 0xA0)

0xF5(configレジスタ)
spi3w_en[0](3wire SPI 有効/無効)
filter[2:0](IIRフィルタ時定数)
t_sb[2:0](非アクティブ期間)

0xA0
spi3w_en[0](0:無効(デフォルト))
filter[2:0](000:Filter off(デフォルト))
t_sb[2:0](101:1000ms)

詳細は、データシートTable 26: Register 0xF5 “config”』で確認できます。

t_sb2 t_sb1 t_sb0 filter2 filter1 filter0 spi3w
1 0 1 0 0 0 0 0
A 0

3.センサーから補正データを取得

#補正データ読み込み
def read_compensate():
    #温度補正データ読み込み
    dat_t = i2c.read_i2c_block_data(address, 0x88, 0x6)

    #以下省略

    #気圧補正データ読み込み
    dat_p = i2c.read_i2c_block_data(address, 0x8E, 0x12)

    #湿度補正データ読み込み
    dh = i2c.read_byte_data(address, 0xA1)
    digH.append(dh)
    dat_h = i2c.read_i2c_block_data(address, 0xE1, 0x08)

取得は read_byte_block(adr, reg, size)関数を使用します。

第1引数 adr:I2C通信のアドレス
第2引数 reg:先頭のレジスタ(測定データの記憶場所)
第3引数 size:データのサイズ

関数 def read_compensate():を作成し、レジスタから補正データを読み込みます。

温度補正

dat_t = i2c.read_i2c_block_data(address, 0x88, 0x6)
6バイトのデータを読み込みます。下表のData Typeの型に変換してdigT[0]~digT[2]に格納します。
(digT[0]は unsigned short、digT[1]とdigT[2]は signed short)

気圧補正

dat_p = i2c.read_i2c_block_data(address, 0x8E, 0x12)
18バイトのデータを読み込みます。下表のDataTypeの型に変換してdigP[0]~digP[8]に格納します。(digP[0]は unsigned short、digP[1]~digP[8]は signed short)

湿度補正

dh = i2c.read_byte_data(address, 0xA1)
dat_h = i2c.read_i2c_block_data(address, 0xE1, 0x08)

1バイト + 8バイトのデータを読み込みます。下表のDataTypeの型に変換してdigH[0]~digP[5]に格納します。
(unsigned char、signed char、unsigned short、signed shortの4種類の型に変換します。)

注意
dat_h[4]の0~3バイトをdigH[3]格納します。また、dat_h[4]の4~7バイトをdigH[4]格納します。ここが混乱しやすいです。

詳細は、データシートTable 16: Compensation parameter storage, naming and data type』で確認できます。

Register Address Register content Data type サイズ
0x88/0x89 dig_T1[7:0]/[15:8] unsigned short 0x06
0x8A/0x8B dig_T2[7:0]/[15:8] signed short
0x8C/0x8D dig_T3[7:0]/[15:8] signed short
0x8E/0x8F dig_P1[7:0]/[15:8] unsigned short 0x12
0x90/0x91 dig_P2[7:0]/[15:8] signed short
0x92/0x93 dig_P3[7:0]/[15:8] signed short
0x94/0x95 dig_P4[7:0]/[15:8] signed short
0x96/0x97 dig_P5[7:0]/[15:8] signed short
0x98/0x99 dig_P6[7:0]/[15:8] signed short
0x9A/0x9B dig_P7[7:0]/[15:8] signed short
0x9C/0x9D dig_P8[7:0]/[15:8] signed short
0x9E/0x9F dig_P9[7:0]/[15:8] signed short
0xA1 dig_H1[7:0] unsigned char 0x01
0xE1/0xE2 dig_H2[7:0]/[15:8] signed short 0x08
0xE3 dig_H3[7:0] unsigned char
0xE4/0xE5[3:0] dig_H4[11:4]/[3:0] signed short
0xE5[7:4]/0xE6 dig_H5[3:0]/[11:4] signed short
0xE7 dig_H6 signed char

4.センサーから温湿度データ、気圧データを取得

#測定データ読み込み
def read_data():
    
    #データ読み込み
    dat = i2c.read_i2c_block_data(address, 0xF7, 0x08)

取得は read_byte_block(adr, reg, size)関数を使用します。

dat = i2c.read_i2c_block_data(address, 0xF7, 0x08)
温度データ 24bit、気圧データ 24bit、湿度データ 16bit
合計64bit(0x08 byte)を読み込みます。

詳細は、データシートの『Table 18: Memory map』で確認できます。

Register Name Address bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
hum_lsb 0xFE hum_lsb<7:0>
hum_msb 0xFD hum_msb<7:0>
temp_xlsb 0xFC temp_xlsb<7:4>
temp_lsb 0xFB temp_lsb<7:0>
temp_msb 0xFA temp_msb<7:0>
press_xlsb 0xF9 press_xlsb<7:4>
press_lsb 0xF8 press_lsb<7:0>
press_msb 0xF7 press_msb<7:0>
#データ変換
dat_p = (dat[0] << 16 | dat[1] << 8 | dat[2]) >> 4
dat_t = (dat[3] << 16 | dat[4] << 8 | dat[5]) >> 4
dat_h = dat[6] << 8 | dat[7]

温度データと気圧データ
8bit × 3 を 24bit データにします。
上位 20bit が有効で、下位 4bit は0で埋まっているので、右へ4bitシフトします。

24bitデータ右に4bitシフト

湿度データ
8bit × 2を 16bit データにします。左へ 8bit シフトと「or」です。

16bitデータ

5.データの補正をします

#補正
tmp = bme280_compensate_t(dat_t)
prs = bme280_compensate_p(dat_p)
hum = bme280_compensate_h(dat_h)

bme280_compensate_t(dat_t):温度補正
bme280_compensate_p(dat_p):気圧補正
bme280_compensate_h(dat_h):湿度補正

データの補正計算の詳細は、BME280のデータシートを参照してください。

注意
温度補正関数で求めた t_fineを気圧補正と湿度補正で使用するので、温度補正を最初に実行してください。

6. 補正したデータを画面に表示します

#表示
print('t = ' + str(tmp))
print('p = ' + str(prs))
print('h = ' + str(hum))

print(‘t = ‘ + str(tmp))
温度、気圧、湿度を画面に表示します。

プログラムの実行結果

プログラムを実行します。

①Runボタンを押します。
②温度、気圧、湿度データを画面に表示します。

Pythonプログラムの実行

以上、BME280からI2C通信で温湿度データと気圧データを読み込んで表示するサンプルでした。

While文などを使用すれば、連続してデータを読み込むことが可能となります。チャレンジしてみてください。

まとめ

BME280モジュールからRaspberry PiにI2C通信で温度、気圧、湿度データを読み込んで表示するサンプルでした。如何でしたでしょうか。

今回のサンプルプログラムでは、消費電流やフィルタの帯域幅、応答時間に出力レートなど、設定変更は省略しましたが、これらを変えて測定することが可能です。

※プログラムの補正計算が間違い易いので注意してください(以下の2つ)。

  1. 補正を行う順番(温度補正が最初です)
  2. 湿度補正データの配列への格納(ちょっと、ややこしい並びとなっています。dat_h[4]の0~3バイトをdigH[3]に格納、dat_h[4]の4~7バイトをdigH[4]に格納です。心配な方はデータシートで再確認をお願いします。)

プログラミングやソフトウェアの記事です。

コメント