温湿度・気圧センサー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通信のどちらの通信方式にも対応しています。
モジュールとRaspberry Piの接続
接続図
接続写真
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.初期設定
#補正用
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 | |
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> | 0 | 0 | 0 | 0 | |||
temp_lsb | 0xFB | temp_lsb<7:0> | |||||||
temp_msb | 0xFA | temp_msb<7:0> | |||||||
press_xlsb | 0xF9 | press_xlsb<7:4> | 0 | 0 | 0 | 0 | |||
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シフトします。
湿度データ
8bit × 2を 16bit データにします。左へ 8bit シフトと「or」です。
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ボタンを押します。
②温度、気圧、湿度データを画面に表示します。
以上、BME280からI2C通信で温湿度データと気圧データを読み込んで表示するサンプルでした。
While文などを使用すれば、連続してデータを読み込むことが可能となります。チャレンジしてみてください。
まとめ
BME280モジュールからRaspberry PiにI2C通信で温度、気圧、湿度データを読み込んで表示するサンプルでした。如何でしたでしょうか。
今回のサンプルプログラムでは、消費電流やフィルタの帯域幅、応答時間に出力レートなど、設定変更は省略しましたが、これらを変えて測定することが可能です。
※プログラムの補正計算が間違い易いので注意してください(以下の2つ)。
- 補正を行う順番(温度補正が最初です)
- 湿度補正データの配列への格納(ちょっと、ややこしい並びとなっています。dat_h[4]の0~3バイトをdigH[3]に格納、dat_h[4]の4~7バイトをdigH[4]に格納です。心配な方はデータシートで再確認をお願いします。)
プログラミングやソフトウェアの記事です。
コメント