ラズパイで温湿度気圧センサ(bme280)を使う方法

# 目次
- [開発環境](#開発環境)
- [ラズパイへの接続方法](#ラズパイへの接続方法)
- [コード](#コード)
    - [main関数の解説](#main関数の解説)
- [応用](#応用)

# 開発環境
RaspberryPi Zero W
OS : 2021-05-07-raspios-buster-armhf
python 3.7
# ラズパイへの接続方法
Raspberry PiはI2C通信用のピンを備えています
I2Cとは、マイコン間で主に使用される通信方式の一つです
BME280はI2C通信で情報をやり取りできるので、Raspberry PiとBME280をI2C通信できるように接続します

<img width="400" alt="2021-11-21 (2).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550181/b451c1f5-63d7-1390-b434-16950daffbc4.png">

# I2Cの有効化
Raspberry PiのI2Cはデフォルトでは無効になっているので、設定を変更して有効にします
以下のコマンドを使用してラズパイの設定を変更します

`raspi-config`

<img width="400" alt="Screenshot from 2018-04-29 11-56-31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550181/9dd83330-0a6f-42c8-9d9e-57f65dbd44b7.png">

Interfacing Options -> I2C -> Yes を選択してI2Cを有効化します
設定できたら、左右矢印でFinishを選択して、エンターキーを押して、raspi-configを終わります
Raspberry PiはI2C通信用のピンを備えています
I2Cとは、マイコン間で主に使用される通信方式の一つです
BME280はI2C通信で情報をやり取りできるので、Raspberry PiとBME280をI2C通信できるように接続します

# コードを動かすための前準備
PythonからI2C をコントロールするためのライブラリ「wiringpi2」のインストール
`$ sudo pip install wiringpi2`
データをファイルに書出すので「bme280_logs.csv」の空ファイルを準備する
※この例ではユーザーネームがpiであることが前提ですが適宜自分のユーザー名に読み替えてください
```
$ mkdir /home/pi/data
$ sudo touch /home/pi/data/bme280_logs.csv
$ sudo chown  pi /home/pi/data/bme280_logs.csv
```
 

# コード
関数ごとの役割はコード上にコメントアウトで書いてあるので適宜見てください

```python:bme280.py
#!/usr/bin/python3 -u
# -*- coding: utf-8 -*-

############################################################
# Written by pond-e
# 
# Environment:  Python 3.4.2
#               OS: Raspi OS
#               on Raspberry Pi Zero W
# Used sensor: BME-280(Akiduki denshi)
#
# PythonからI2C をコントロールするためのライブラリ「wiringpi2」のインストール必要。
#    $ sudo pip install wiringpi2 
#
# データをファイルに書出すので「bme280_logs.csv」の空ファイルを準備する
# pi@raspberrypi ~ $ sudo touch bme280_logs.csv
# pi@raspberrypi ~ $ sudo chown  pi bme280_logs.csv
#データ計測時間は　SAMPLING_TIME x TIMES
############################################################

import sys              #sysモジュールの呼び出し
import wiringpi as wi   #wiringPiモジュールの呼び出し
import time             #timeライブラリの呼び出し
import datetime         #datetimeモジュールの呼び出し
import os

#データ計測時間は　SAMPLING_TIME x TIMES
SAMPLING_TIME = 0.1     #データ取得の時間間隔[sec]
TIMES = 100                   #データの計測回数

wi.wiringPiSetup()      #wiringPiの初期化
i2c = wi.I2C()          #i2cの初期化

i2c_address = 0x76 # #I2Cアドレス SDO=GND
#i2c_address = 0x77 # #I2Cアドレス SDO=VCC
bme280 = i2c.setup(i2c_address)            #i2cアドレス0x76番地をbme280として設定(アドレスは$sudo i2cdetect 1で見られる)

digT = []	#配列を準備
digP = []	#配列を準備
digH = []	#配列を準備

temp = 0
humi = 0
press = 0
t_fine = 0.0

# レジスタへの書き込み
def writeReg(reg_address, data):
	i2c.writeReg8(bme280,reg_address,data)

# キャリブレーションデータの取得
def get_calib_param():
	calib = []
	
	for i in range (0x88,0x88+24):
		calib.append(i2c.readReg8(bme280,i))
	calib.append(i2c.readReg8(bme280,0xA1))
	for i in range (0xE1,0xE1+7):
		calib.append(i2c.readReg8(bme280,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 = []
	global temp
	global humi
	global press
	for i in range (0xF7, 0xF7+8):
		data.append(i2c.readReg8(bme280,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]
	
	temp = compensate_T(temp_raw)
	press = compensate_P(pres_raw)
	humi = 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)  
	
	print("pressure : %7.2f hPa" % (pressure/100))
	return 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("temp : %-6.2f ℃" % (temperature)) 
	return 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))
	return var_h

# 設定
def setup():
	#osrs_t[2:0] 温度オーバーサンプリング設定
	#スキップ(output set to 0x80000)=0 オーバーサンプリング×1=1 オーバーサンプリング×2= 2 オーバーサンプリング×4= 3 オーバーサンプリング ×8=4 オーバーサンプリング ×16=5
	osrs_t = 1			#温度オーバーサンプリング x 1
	#osrs_p[2:0] 気圧オーバーサンプリング 設定
	#スキップ(output set to 0x80000)=0 オーバーサンプリング ×1=1 オーバーサンプリング ×2=2	オーバーサンプリング ×4=3 オーバーサンプリング ×8=4 オーバーサンプリング ×16=5
	osrs_p = 3			#気圧オーバーサンプリング x 4
	#osrs_h[2:0] 湿度オーバーサンプリング settings
	#スキップ(output set to 0x80000)=0 オーバーサンプリング ×1=1 オーバーサンプリング ×2=2	オーバーサンプリング ×4=3 オーバーサンプリング ×8=4 オーバーサンプリング ×16=5
	osrs_h = 0			#湿度オーバーサンプリング Skipped
	mode   = 3			#ノーマルモード (Sleep mode:0 Forced mode:1 Normal mode:3)
	#t_sb[2:0] tstandby [ms]
	# 0= 0.5[ms] 1= 62.5[ms] 2= 125[ms] 3= 250[ms] 4= 500[ms] 5= 1000[ms] 6= 10[ms] 7= 20[ms]
	t_sb   = 0			#Tstandby 0.5ms
	#filter[2:0] Filter coefficient
	#0= Filter off 1=coefficient2 2=coefficient4 3=coefficient8 4=coefficient 16
	filter = 4			#Filter coefficient 16
	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()

#ファイルへ書出し準備
now = datetime.datetime.now()
#現在時刻を織り込んだファイル名を生成
fmt_name = "/home/pi/data/bme280_logs_{0:%Y%m%d-%H%M%S}.csv".format(now)
f_bme280= open(fmt_name, 'w')    #書き込みファイル
#f_bme280= open('bme280_logs.csv', 'w')    #書き込みファイル
value="yyyy-mm-dd hh:mm:ss.mmmmmm, T[℃],H[%],P[hPa]"   #header行への書き込み内容
f_bme280.write(value+"\n")   #header行をファイル出力

if __name__ == '__main__':
	#while True:
	for _i in range(TIMES):
		try:
			date = datetime.datetime.now()  #now()メソッドで現在日付・時刻のdatetime型データの変数を取得 世界時：UTCnow
			now     = time.time()     #現在時刻の取得
			readData()
			#ファイルへ書出し
			value= "%s,%6.2f,%6.2f,%7.2f" % (date, temp,humi,press)      #時間、温度、湿度、気圧
			f_bme280 .write(value + "\n")       #ファイルを出力
			#指定秒数の一時停止
			sleepTime       = SAMPLING_TIME - (time.time() - now)
			if sleepTime < 0.0:
				continue
			time.sleep(sleepTime)
		except KeyboardInterrupt:
			pass

	f_bme280 .close()                       #書き込みファイルを閉じる
```
## main関数の解説
- まず初期設定をする
- /home/pi/data/ にbme280_logs_{日時}.csvというファイルを生成してそこにデータを書き込んでいきます
- データは時間、温度、湿度、気圧の順に書き込まれます
- それぞれ、date、temp、humi、pressという名前の変数に取得した値が入っているのでこのセンサの値を使って何かをする場合はこの変数を用いましょう

# 応用
気圧から物体の高さを知ることが出来ます