
前回はM5StackのI2Cに接続されているオンボードのデバイスをスキャンしてみました。同じI2Cバスが側面のGroveコネクタへも出力されております。よってここにI2Cデバイスを接続すれば簡単に使える筈。今回はここに定番の圧力センサ、Bosch BMP280を接続してみます。でもドライバはどうする?
※「MicroPython的午睡」投稿順 Indexはこちら
※M5Stack Grayに書き込んだUIFlow2.0 (Alpha-27)対応のMicroPython処理系とWindowsパソコン上で動作しているThonny 4.0.1で動作確認しています。
BMP280をGroveコネクタに接続
念のためBMP280の製品ページが以下に。
Pressure Sensor BMP280 | Bosch Sensortec
BMP280はIO電圧3.3V系のデバイスですが、手元にあったBMP280はArduino用の5V環境用にレギュレータ等搭載して5V環境に直結できるようになっているモジュールです。
M5StackのGroveコネクタには5V電源が出ているものの、以前にも書いたとおり微妙にSCL、SDAの信号端子のレベルは疑問が持たれるものです。しかしま、5V系のモジュールをそのまま接続して動かしているみたいなので、深くは追及いたしますまい。ということでそのまま接続。こんな感じ。
前回使用したI2Cスキャンのプログラムを走らせると以下のようにBMP280が見えました。ADR=0x77の”UNKOWN DEVICE”がソレです。
UIFlow2のMicroPython、BMP280をサポートしてるケド
UIFlow2のMicroPythonは以下のようにBMP280を立派にサポートしてます。以下はdriver.bmp280のhelpの先頭付近です。
これを使えば接続簡単、と思ったのですが、使い方がイマイチ良くわかりませぬ。UIFlow2ではどうしているのかと調べて気づいたのは、
BMP280単独でなく他の温湿度センサと組み合わせたENVモジュール
レベルでサポートしている、ということです。M5Stack社ではENVx(xには番号が入る)という名のモジュールを販売していて、そのモジュールの多くで気圧測定用にBMP280を搭載しているのです。しかし温湿度(BMP280は温度を測れるけど湿度は測れない)測定のため、必ず他のセンサと組み合わせたモジュールになってました。UIFlow2の「ビジュアル」環境ではそのENVモジュールレベルのAPIを使ってました。単独のBMP280レベルで直接制御する方法が不明っす。とりあえずENVモジュールのつもりで単独BMP280に接続してみたらエラーが出ました。
どうしたものか?いやはや忘却力の年寄は既に忘れていましたが、ATOMLite(同じESP32シリーズのマイコン搭載)用に以下の過去回でBMP280に接続していたのです。自前のコードで。
MicroPython的午睡(51) ATOMLite、BMP280の補償計算大変なのね
今回は上記で作成済のプログラムをほぼほぼ流用、M5Stack用にお化粧直し程度の修正であります。ソース、ちょっと長いけど以下です。
# i2cBMP280.py
import M5
import time
from machine import Pin, I2C
M5.begin()
print("M5Stack Gray, I2C0 SCAN")
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
#print(i2c.scan())
bmp280adr = 0x77
bmp280_ID = 0xD0
bmp280_CONF = 0xF5
bmp280_CTRL = 0xF4
bmp280_STAT = 0xF3
bmp280_REST = 0xE0
bmp280_TMPX = 0xFC
bmp280_TMPL = 0xFB
bmp280_TMPM = 0xFA
bmp280_PRSX = 0xF9
bmp280_PRSL = 0xF8
bmp280_PRSM = 0xF7
bmp280_T1 = 0x88
bmp280_T2 = 0x8A
bmp280_T3 = 0x8C
bmp280_P1 = 0x8E
bmp280_P2 = 0x90
bmp280_P3 = 0x92
bmp280_P4 = 0x94
bmp280_P5 = 0x96
bmp280_P6 = 0x98
bmp280_P7 = 0x9A
bmp280_P8 = 0x9C
bmp280_P9 = 0x9E
def readParamsU(rAdr):
dig_b = i2c.readfrom_mem(bmp280adr, rAdr, 2)
tmp = dig_b[1]<<8 | dig_b[0]
return tmp
def readParamsS(rAdr):
tmp = readParamsU(rAdr)
if (tmp & 0x8000) != 0:
tmp = -((~tmp + 1) & 0xFFFF)
return tmp
#This function based on BOSCH reference code.
def bmp280_compensate_T(adc_T, opt=False):
global dig_T1, dig_T2, dig_T3, t_fine
var1 = (adc_T/16384.0 - dig_T1/1024.0) * dig_T2
A1=adc_T/131072.0
T1=dig_T1/8192.0
var2 = ((A1-T1)*(A1-T1))*dig_T3
t_fine = var1 + var2
if opt:
print("var1=",var1)
print("var2=",var2)
print("t_fine=",t_fine)
return (var1+var2)/5120.0
#Following test values from BOSCH reference code.
def test_bmp280_compensate_T():
global dig_T1, dig_T2, dig_T3
dig_T1 = 27504
dig_T2 = 26435
dig_T3 = -1000
adc_T = 519888
print("TEST_T(Expected 25.08)=", bmp280_compensate_T(adc_T, opt=True))
#This function based on BOSCH reference code.
def bmp280_compensate_P(adc_P, opt=False):
global dig_P1, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9, t_fine
var10 = (t_fine/2.0) - 64000.0
var20 = var10*var10*(dig_P6/32768.0)
var21 = var20+var10*(dig_P5)*2.0
var22 = (var21/4.0)+(dig_P4*65536.0)
var11 = (dig_P3*var10*var10/524288.0+dig_P2*var10)/524288.0
var12 = (1.0+var11/32768.0)*dig_P1
p0 = 1048576.0 - adc_P
p = (p0-(var22/4096.0))*6250.0/var12
var1 = dig_P9*p*p/2147483648.0
var2 = p*dig_P8/32768.0
if opt:
print("var10=", var10)
print("var11=", var11)
print("var12=", var12)
print("var1=", var1)
print("var20=", var20)
print("var21=", var21)
print("var22=", var22)
print("var2=", var2)
print("p0=", p0)
print("p=", p)
return p + (var1 + var2 + dig_P7)/16.0
#Following test values from BOSCH reference code.
def test_bmp280_compensate_P():
global dig_P1, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9
dig_P1 = 36477
dig_P2 = -10685
dig_P3 = 3024
dig_P4 = 2855
dig_P5 = 140
dig_P6 = -7
dig_P7 = 15500
dig_P8 = -14600
dig_P9 = 6000
adc_P = 415148
print("TEST_P(Expected 100653)=", bmp280_compensate_P(adc_P, opt=True))
def bmp280_getParamsT(opt=False):
global dig_T1, dig_T2, dig_T3
dig_T1 = readParamsU(bmp280_T1)
dig_T2 = readParamsS(bmp280_T2)
dig_T3 = readParamsS(bmp280_T3)
if opt:
print("DIG_T1:", dig_T1)
print("DIG_T2:", dig_T2)
print("DIG_T3:", dig_T3)
def bmp280_getParamsP(opt=False):
global dig_P1, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9
dig_P1 = readParamsU(bmp280_P1)
dig_P2 = readParamsS(bmp280_P2)
dig_P3 = readParamsS(bmp280_P3)
dig_P4 = readParamsS(bmp280_P4)
dig_P5 = readParamsS(bmp280_P5)
dig_P6 = readParamsS(bmp280_P6)
dig_P7 = readParamsS(bmp280_P7)
dig_P8 = readParamsS(bmp280_P8)
dig_P9 = readParamsS(bmp280_P9)
if opt:
print("DIG_P1:", dig_P1)
print("DIG_P2:", dig_P2)
print("DIG_P3:", dig_P3)
print("DIG_P4:", dig_P4)
print("DIG_P5:", dig_P5)
print("DIG_P6:", dig_P6)
print("DIG_P7:", dig_P7)
print("DIG_P8:", dig_P8)
print("DIG_P9:", dig_P9)
def main():
# test_bmp280_compensate_T()
# test_bmp280_compensate_P()
buf = str(i2c.readfrom_mem(bmp280adr, bmp280_ID, 1), "utf-8")
print("ID = 0x{0:02x}".format(ord(buf[0])))
i2c.writeto_mem(bmp280adr, bmp280_CONF, b'\x40') # 125mS, no filter, no spi
i2c.writeto_mem(bmp280adr, bmp280_CTRL, b'\x27') # x1, x1, normal mode
bmp280_getParamsT(False)
bmp280_getParamsP(False)
loopCounter = 0
while( loopCounter < 60 ):
loopCounter += 1
prslis = i2c.readfrom_mem(bmp280adr, bmp280_PRSM, 3)
tmplis = i2c.readfrom_mem(bmp280adr, bmp280_TMPM, 3)
prs = (prslis[0]<<12) | (prslis[1] << 4)
tmp = (tmplis[0]<<12) | (tmplis[1] << 4)
print("TEMPERATURE: RAW={0} COMPENSATE={1:3.1f} [C]".format(tmp, bmp280_compensate_T(tmp)))
print("PRESSURE : RAW={0} COMPENSATE={1:7.1f} [Pa]".format(prs, bmp280_compensate_P(prs)))
time.sleep(10)
if __name__ == "__main__":
main()
実機動作結果
タイトルが「M5Stack Gray、I2C0 SCAN」になっているのは前回の名残です。一応、温度と気圧(ヘクトパスカルではなく、素のパスカルなのでご注意を)が読み取れているみたい。
折角デバイスサポートされているのだが自前コード。動いているみたいだし、いいか。
