Raspberry Pi Pico(d_10)BMP280 大気圧センサー
BMP280 (I2C)
BMP280はDIGITAL PRESSURE SENSORです。I2C通信で制御します。Raspberry Pi PicoでBMP280搭載のモジュールを使ってみました。
補償計算(Compensation formula)はデータシートの資料を焼き直しています。
計算の中身はよく分かりませんので、詳細はデータシートを参照ください。
※micro:bitの記事e_43からPiPicoで使えるように追記、修正等をしています。
外観
BMP280モジュールの外観写真です。(以後はBMP280と記載します)
接続例
BMP280は3.3Vで動作するのでRaspberry Pi Picoと直接接続しました。
BMP280は設定によってI2C、SPI通信で動作が可能です。今回はI2C通信で制御します。そのためCSB(chip select)をVCCに接続しました。
SOD(serial data out)をGNDに接続したのでスレーブアドレスは0x76になります。VCCに接続すると0x77になります。
I2C通信にはI2C1のSDA(GP10)SCL(GP11)を使いました。
接続出来たらI2Cデバイスのスレーブアドレスを調べてみます。
ThonnyのShellで確認します。BMP280のスレーブアドレスは以下のように確認できました。
>>> from machine import I2C,Pin
>>> i2c=I2C(1, scl=Pin(11), sda=Pin(10), freq=100_000)
>>> i2c.scan()
[118]
>>> hex(118)
'0x76'
>>>
BMP280の説明
データシートから抜粋してBMP280の説明します。(「###」印は下方のスクリプトに対応する部分です。)
Memory map は下図のようになっています。レジスタ名とアドレスが分かります。
※BOSCH社のBMP280のデータシートから抜粋
■temp、press
温度のデータはtemp_mlsb(0xfa),temp_lsb(0xfb),temp_xlsb(0xfc)の20bitです。
気圧のデータはpress_mlsb(0xf7),press_lsb(0xf8),press_xlsb(0xf9)の20bitです。
スクリプトでは以下のように、レジスタ0xF7から6bytesを読み、各々20bitデータに整理しています。
r_reg(0xF7)
dat=i2c.readfrom(addr,6)
dat_p = (dat[0] << 16 | dat[1] << 8 | dat[2]) >> 4
dat_t = (dat[3] << 16 | dat[4] << 8 | dat[5]) >> 4
■config(0xf5) ### def init_bmp280()
デバイスを設定します。
t_sb[2:0]でstandby time、filter[2:0]でフィルタ、spi3w_en[0]でSPIの可否を設定します。
スクリプトでは、
t_sb 101 1000msec(1sec) standby time
filter 000 IIR filter Filter無
spi(en) 0 spi not use
にしました。
そのため、config(0xF5)は0b1010000(‘0xa0’)としました。
■ctrl_meas(0xf4) ### def init_bmp280()
測定の条件を設定します。
osrs_t[2:0]で温度測定のoversampling、osrs_p[2:0]で気圧測定のoversampling、mode[1:0]でパワーモードを設定します。
スクリプトでは、
osrs_t 001 oversampling ×1 16 bit / 0.0050 °C
osrs_p 001 oversampling ×1 16 bit / 2.62 Pa (Ultra low power)
mode 11 power mode ノーマル
にしました。
そのため、ctr_meas(0xF4)には0b00100111(‘0x27’)としました。
■calib25…calib00 ### def para_bmp280(),def sig(dat)
Compensation parameter(dig_T1,dig_T2…dig_P1,dig_T2)はデータタイプがあります。signed typeは符号計算が必要です。
スクリプトでは各々を読み出し、符号計算の有無を行い、
その結果データを補償用データ配列 digT = []、digP = [] に保管しています。
補償計算をする
温度、気圧の値を得るには、各々20bitの温度データ、気圧データ、及び補償用配列データから補償計算をする必要があります。
データシートに以下の補償計算のサンプルプログラムがあります。しかしながら、何をやってるのかよくわかりません。機械的にこれを焼き直して使います。
※BOSCH社のBMP280のデータシートから抜粋
大まかには、以下の点を変更してサンプルプログラムを焼き直しました。
■温度の補償計算 ### def bmp280_compensate_t(adc_T)
t_fineはglobal値にする
関数の引数はadc_Tにする
(BMP280_S32_t)は削除する
dig_T2などはdigT[1]のように配列データに替える、付数値が1下がる
末尾「;」は削除
計算値を1/100にする
■気圧の補償計算 ### def bmp280_compensate_p(adc_P)
以下の点を変更してサンプルプログラムを焼き直しました。
t_fineはglobal値にする(温度の補償計算値を先にする)
関数の引数はadc_Pにする
(BMP280_S32_t)は削除する
dig_P3などはdigP[2]のように配列データに替える、付数値が1下がる
if文を修正
p=int(p)を追加する
末尾「;」は削除
計算値を1/256/100にする
スクリプト
以上をまとめてBMP280のスクリプトは以下のようにしました。
BMP280のID値、補償用データ配列、温度・気圧をThonnyのshell部に表示します。
※ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。
pico_bmp280_readmes_03b.py
#!/usr/bin/env python
# -*- coding: utf-8 -*
from machine import I2C,Pin
import time
## BMP280 DIGITAL PRESSURE SENSOR / BOSCH
#slave address
addr=0x76 # [118] 0x76
#i2c_init
i2c=I2C(1, scl=Pin(11), sda=Pin(10), freq=100_000)
#Compensation data
digT = [] # 補償用データ配列に保管
digP = []
#t_fine carries fine temperature as global value
t_fine = 0.0
## read out
# read start register set (yomidasu register address set)
def r_reg(dat):
buf=bytearray(1)
buf[0]=dat
i2c.writeto(addr,buf)
# data write resister/data
def w_reg(reg,dat):
buf=bytearray(2)
buf[0]=reg
buf[1]=dat
i2c.writeto(addr,buf)
# signed data fugo keisan 16bit
def sig(dat):
if dat >= (2**15):
dat=dat-(2**16)
return dat
# compensation parameter 補正データ読み込み
def para_bmp280():
global digT
global digP
## dig_T1~T3 0x88~0x8d 6byte
r_reg(0x88)
com_t=i2c.readfrom(addr,6) # dig_T1~T3 6byte
# 16bitデータに変換
for i in range(0,6,2):
digT.append((com_t[i+1] <<8) | (com_t[i])) # digT[0] unsigned # dig_T2,T3(digT1,2) signed data->fugou keisan
for i in range(1,3):
digT[i]=sig(digT[i])
## dig_P1~P9 0x8E~0x9f 18byte
r_reg(0x8E)
com_p=i2c.readfrom(addr,18) # dig_P1~P9 6byte
# 16bitデータに変換
for i in range(0,18,2):
digP.append((com_p[i+1] <<8) | (com_p[i])) # digP[0] unsigned # dig_P2~P9(digP1~P8) signed data->fugou keisan
for i in range(1,9):
digP[i]=sig(digP[i])
## init BME280の測定条件設定
def init_bmp280():
w_reg(0xF4,0x27) #Register 0xF4 “ctrl_meas”
w_reg(0xF5,0x52) #Register 0xF5 “config”
time.sleep_ms(10)
# sleep
def sleep_bmp280():
w_reg(0xF4,0x24) #ctr_meas(0xF4) sleep 0b_00100100'0x24'
time.sleep_ms(2)
# reset
def reset_bmp280():
w_reg(0xe0,0xb6) #Register 0xE0 “reset”
time.sleep_ms(2)
# compensation formula
def bmp280_compensate_t(adc_T):
global t_fine
var1 = ( ((adc_T>>3)-(digT[0]<<1)) * (digT[1]) ) >> 11
var2 = (( ( ((adc_T>>4)-(digT[0])) * ((adc_T>>4)-(digT[0])) ) >> 12) \
* (digT[2]))>> 14
t_fine = var1 + var2
t = (t_fine * 5 + 128) >> 8
t= t/100
#print(var1,var2,t_fine)
return t
def bmp280_compensate_p(adc_P):
global t_fine
var1 = (t_fine)-128000
var2 = var1 * var1 * digP[5]
var2 = var2 + ((var1*digP[4])<<17)
var2 = var2 + ((digP[3])<<35) var1 = ( (var1 * var1 * digP[2]) >>8 ) + ( (var1 * digP[1]) <<12 )
var1 = ( ( ((1)<<47) +var1 ) )*(digP[0])>>33
if var1 == 0:
return 0 # avoid exception caused by division by zero
p = 1048576 - adc_P
p = ( ( (p<<31)-var2 )*3125 )/var1 # p(float)
#print('p',p,type(p)) # p 6.7116e+09 <class 'float'>
p=int(p) # float >> int
#print('p',p,type(p)) # p 6711599104 <class 'int'>
var1 = (digP[8] * (p>>13) * (p>>13)) >> 25
var2 = (digP[7] * p) >>19
p = ( (p + var1 + var2) >>8) + (digP[6] <<4) p = p / 256 / 100 return p ## 温度、気圧データ読込>>補償計算>>温度、気圧の値を返す
def measure_bmp280():
## temp press data register
## 測定データ読み込み press_msb 0xF7~temp_xlsb 0xFC 3+3 6byte
r_reg(0xF7)
dat=i2c.readfrom(addr,6) # dig_P1~P9 6byte
#print('r_mdata=',dat)
#データ変換
dat_p = (dat[0] << 16 | dat[1] << 8 | dat[2]) >> 4 # 0xF7~0xF9 20bit
dat_t = (dat[3] << 16 | dat[4] << 8 | dat[5]) >> 4 # 0xFA~0xFC 20bit
#補正
tmp=bmp280_compensate_t(dat_t)
prs=bmp280_compensate_p(dat_p)
return tmp,prs
# BMP280_ID
def id_bmp280():
r_reg(0xD0) #Register 0xD0 “id”
dat=i2c.readfrom(addr,1) # <class 'bytes'>
#print('ID=',hex(dat[0]))
return dat
## MAIN --------------------
## bmp280_ID register IDを表示
id=id_bmp280()
print('ID=',hex(id[0]))
## calibration data(Trimming parameter) を表示
para_bmp280()
print('digT',digT)
print('digP',digP)
## reset BMP280 リセット
reset_bmp280() # added line
#BME280の初期化
init_bmp280()
## pressure and temperature calculating out
#測定データ読込>>値を表示出力
t,p= measure_bmp280()
print('t=',t)
print('p=',p)
実行結果
結果は以下のようになりました。※Thonnyのshellに表示されます。
pico_bmp280_readmes_03b.py
>>> %Run -c $EDITOR_CONTENT
ID= 0x58
digT [27981, 26705, -1000]
digP [37717, -10811, 3024, 2557, 124, -7, 15500, -14600, 6000]
t= 20.27
p= 1023.004
一度、実行するとThonnyのShellでも関数が使えます。
>>> measure_bmp280()
(20.26, 1022.943)
>>>
日本気象株式会社のサイト「お天気ナビゲータ」で確認してみました。21:00が表示した時刻でそれ以後は予測です。温度は室温と外気温の差で違いますが、気圧(圧力)はスクリプトで表示された結果とほぼ一致しています。
※お天気ナビゲータでの表示を引用
まとめ
Raspberry Pi PicoでBMP280を使って温度、気圧の測定ができました。