工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

BLEモジュール BL652ブレイクアウトボードで遊ぶ(3) --- I2Cセンサとの通信 ---

概要

BL652ブレイクアウトボードで、I2C接続のセンサからデータを取得してみた。



背景と目的

前回、BL652ブレイクアウトボードを使うための開発環境を整え動作を確認した。今回は、実用を想定してI2C接続のセンサからデータを取得してみる。



詳細

0. 実施環境と参考情報


1. smartBASICでFLOAT型を使おうとしたら使えないのでファームウェアをアップデート

FLOAT型を使おうとしたらいきなりつまずいたので、メモ。 BL652は、smartBASICという言語でプログラムを書く。参考資料1にはFLOAT型の説明が載っているので、FLOAT型を使おうとしたのだが、なぜかCompileするときにUNKNOWN TYPEになってしまう。 どうもおかしいと思って色々調べたところ、ファームウェアが古いせい?に見えたので、こちらを参考に、ファームウェアの更新を行った。以下のように、もともとv28.6.1.2だったのを、v28.11.8.0にバージョンアップできた。

f:id:dekuo-03:20211004005957p:plain

f:id:dekuo-03:20211004010010p:plain

アップデート後、FLOAT型を使ってみたところ、エラーが出なくなった。ということで、問題解決。


2. I2C接続

I2C接続のセンサとして、手持ち品のBME280を使ってみた。 しかし、これが悪手だった。 今まで、ESP32などではArduino向けライブラリを使っていたせいで大して苦労はなかったが、smartBASICでは用意されていないので、BME280の仕様書を見ながら自前でコーディングしなければならないということに、やり始めて気づいてしまった。途中までやってしまったのでどうにかやり切ったのだが、まあこの手の作業はあまりやりたいものではない。具体的には、参考資料1の6.14に従った。なお、サンプルに書いてあるI2C読み出し関数のリターンコードに応じたエラー処理はすべて省略している。

一応、温度、湿度が取れるようになった。気圧は、なぜかうまくいかない。苦戦中。。。ひとまずここまでの出来高を載せておく。動作としては、

  • 1秒周期で、温度、湿度、気圧(うまくいってないが)をシリアル出力

するだけ。

2.0 BME280関連の定数、グローバル変数

// BME280定義値 ----------------------------------------------
// デバイスアドレス
DIM nSlaveAddr: nSlaveAddr = 0x76

// キャリブレーション値
DIM dig_T[3] AS INTEGER // 温度
DIM dig_H[6] AS INTEGER // 湿度
DIM dig_P[9] AS INTEGER // 気圧
DIM t_fine AS FLOAT

2.1 雑多な計算用

// singed int32に変換
FUNCTION to_SInt32(A AS INTEGER, b AS INTEGER)
    //PRINT "A = "; A; ", b = "; b;
    IF A >= b THEN
        A = 0xFFFFFFFF - (b * 2 - A) + 0x01
        //PRINT " -> A = "; A; "\n"
    ELSE
        //PRINT "\n"
    ENDIF
ENDFUNC A

2.2 初期設定

// 初期設定
FUNCTION INIT_BME280()

    // configレジスタ(0xf5)
    // 0xa0 = スタンバイ時間1000ms, IIRフィルタなし, SPI4線(無関係)
    DIM nRegAddrConf: nRegAddrConf = 0xf5
    DIM nRegValConf: nRegValConf = 0xa0
    rc = I2cWriteReg8(nSlaveAddr, nRegAddrConf, nRegValConf)

    // ctrl_measレジスタ(0xf4)
    // 0x27 = 温度オーバーサンプリングx1, 気圧オーバーサンプリングx1, ノーマルモード
    DIM nRegAddrCtrl: nRegAddrCtrl = 0xf4
    DIM nRegValCtrl: nRegValCtrl = 0x27
    rc = I2cWriteReg8(nSlaveAddr, nRegAddrCtrl, nRegValCtrl)

    // ctrl_humレジスタ(0xf2)
    // 0x27 = 湿度オーバーサンプリングx1
    DIM nRegAddrCtrh: nRegAddrCtrh = 0xf2
    DIM nRegValCtrh: nRegValCtrh = 0x01
    rc = I2cWriteReg8(nSlaveAddr, nRegAddrCtrh, nRegValCtrh)

ENDFUNC 0

2.3 キャリブレーションデータの読み出し

// キャリブレーションデータの読み出し
FUNCTION READ_TRIM()

    DIM i AS INTEGER
    DIM work[7] AS INTEGER

    // 温度 ----------------------------------------------------------
    DIM nRegAddrT: nRegAddrT = 0x88 // 先頭アドレス
    FOR i = 0 to 2 STEP 1
        rc = I2cReadReg16(nSlaveAddr, nRegAddrT + 2 * i, dig_T[i])
        IF i != 0 THEN
            dig_T[i] = to_SInt32(dig_T[i], 0x8000)
        ENDIF
        //PRINT "dig_T"; i + 1; "="; dig_T[i]; "\n"
    NEXT
    
    // 湿度 ----------------------------------------------------------
    DIM nRegAddrH1 : nRegAddrH1 = 0xa1
    DIM nRegAddrH2 : nRegAddrH2 = 0xe1
    
    rc = I2cReadReg8(nSlaveAddr, nRegAddrH1, dig_H[0]) // unsinged char
    //PRINT "dig_H1="; dig_H[0]; "\n"
    
    FOR i = 0 to 6 STEP 1
        rc = I2cReadReg8(nSlaveAddr, nRegAddrH2 + i, work[i])
        //PRINT "work["; i; "]="; work[i]; "\n"
    NEXT
    
    dig_H[1] = work[1] * 256 + work[0] // signed short
    //PRINT "dig_H2="; dig_H[1]; "\n"
    
    dig_H[2] = work[2] // unsigned char
    //PRINT "dig_H3="; dig_H[2]; "\n"
    
    dig_H[3] = to_SInt32(work[3] * 16 + (work[4] & 0x0F), 0x800) // ビット演算を()で囲む必要あり
    //PRINT "dig_H4="; dig_H[3]; "\n"
    
    dig_H[4] = to_SInt32((work[4] >> 4) + work[5] * 16, 0x800) // ビット演算を()で囲む必要あり
    //PRINT "dig_H5="; dig_H[4]; "\n"
    
    dig_H[5] = to_SInt32(work[6], 0x80)
    //PRINT "dig_H6="; dig_H[5]; "\n"
    
    // 気圧 ----------------------------------------------------------------
    DIM nRegAddrP : nRegAddrP = 0x8e // 先頭アドレス
    FOR i = 0 to 16 STEP 2
        rc = I2cReadReg8(nSlaveAddr, nRegAddrP + i, work[0])
        PRINT "work[0]="; work[0]; ", "
        rc = I2cReadReg8(nSlaveAddr, nRegAddrP + i + 1, work[1])
        PRINT "work[1]="; work[1]; "\n"
        
        dig_P[i / 2] = ((work[1] << 8) | work[0])
        
        IF i != 0 THEN
            dig_P[i / 2] = to_SInt32(dig_P[i / 2], 0x8000)
        ENDIF
        
        PRINT "dig_P"; i / 2 + 1; "="; dig_P[i / 2]; "\n"
    NEXT

ENDFUNC 0

2.4 温度を取得する関数

// 温度
FUNCTION get_temperature() AS FLOAT

    DIM nRegAddr: nRegAddr = 0xfa // 温度
    DIM adc1 AS INTEGER
    DIM adc2 AS INTEGER
    DIM adc3 AS INTEGER
    DIM var1 AS FLOAT
    DIM var2 AS FLOAT
    DIM var21 AS FLOAT
    DIM T AS FLOAT
    DIM adc_T AS FLOAT
    
    // 読み出し
    // 0xfa - 0xfcの3バイトに格納されている
    // ビッグエンディアンなので8ビットずつ読みだして結合
    rc = I2cReadReg8(nSlaveAddr, nRegAddr, adc1)
    //PRINT "adc1="; adc1; "\n"
    rc = I2cReadReg8(nSlaveAddr, nRegAddr + 1, adc2)
    //PRINT "adc2="; adc2; "\n"
    rc = I2cReadReg8(nSlaveAddr, nRegAddr + 2, adc3)
    //PRINT "adc3="; adc3; "\n"
    
    // FLOAT化
    adc_T = adc1 * 256 * 16.0 + adc2 * 16.0 + adc3 / 16.0 // 20ビット化
    //PRINT "adc_T="; adc_T; "\n"

    var1 = (adc_T / 16384.0 - dig_T[0] / 1024.0) * dig_T[1]
    //PRINT "var1="; var1; "\n"
    
    var21 = (adc_T / 131072.0 - dig_T[0] / 8192.0) * (adc_T / 131072.0 - dig_T[0] / 8192.0)
    //PRINT "var21="; var21; "\n"
    
    var2 = var21 * dig_T[2]
    //PRINT "var2="; var2; "\n"
    
    t_fine = var1 + var2
    PRINT "t_fine="; t_fine; "\n"
    
    T = t_fine / 5120.0
    //PRINT "T="; T; "\n"
    
ENDFUNC T

2.5 湿度を取得する関数

FUNCTION get_humidity() AS FLOAT

    DIM nRegAddr: nRegAddr = 0xfd
    DIM H AS FLOAT
    DIM var_H AS FLOAT
    DIM var_H1 AS FLOAT
    DIM var_H2 AS FLOAT
    DIM var_H3 AS FLOAT
    DIM adc_H AS FLOAT
    DIM adc1 AS INTEGER
    DIM adc2 AS INTEGER
    
    // 読み出し
    // 0xfa - 0xfcの3バイトに格納されている
    // ビッグエンディアンなので8ビットずつ読みだして結合
    rc = I2cReadReg8(nSlaveAddr, nRegAddr, adc1)  
    //PRINT "adc1="; adc1; "\n"
    rc = I2cReadReg8(nSlaveAddr, nRegAddr + 1, adc2)
    //PRINT "adc2="; adc2; "\n"
    
    adc_H = adc1 * 256.0 + adc2 // ビッグエンディアン
    //PRINT "adc_H="; adc_H; "\n"
    
    var_H = t_fine - 76800.0
    var_H1 = dig_H[3] * 64.0 + dig_H[4] / 16384.0 * var_H
    var_H2 = 1.0 + dig_H[2] / 67108864.0 * var_H
    var_H3 = 1.0 + dig_H[5] / 67108864.0 * var_H * var_H2
    var_H = (adc_H - var_H1) * dig_H[1] / 65536.0 * var_H3
    H = var_H * (1.0 - dig_H[0] * var_H / 524288.0)
    
    IF H > 100.0 THEN
        H = 100.0
    ELSEIF H < 0.0 THEN
        H = 0.0
    ENDIF
    
    //PRINT "H="; H; "\n"

ENDFUNC H

2.6 気圧を取得する関数

苦戦中。。。以下、正しく算出できない。

// 気圧
FUNCTION get_pressure() AS FLOAT

    DIM nRegAddr : nRegAddr = 0xf7
    DIM var1 AS FLOAT
    DIM var2 AS FLOAT
    DIM P AS FLOAT
    DIM adc1 AS INTEGER
    DIM adc2 AS INTEGER
    DIM adc3 AS INTEGER
    DIM adc_P AS FLOAT
    //DIM adc_P AS INTEGER
    
    // 読み出し
    // 0xf7 - 0xf9の3バイトに格納されている
    // ビッグエンディアンなので8ビットずつ読みだして結合
    rc = I2cReadReg8(nSlaveAddr, nRegAddr, adc1)  
    PRINT "adc1="; adc1; "\n"
    
    rc = I2cReadReg8(nSlaveAddr, nRegAddr + 1, adc2)   
    PRINT "adc2="; adc2; "\n"
    
    rc = I2cReadReg8(nSlaveAddr, nRegAddr + 2, adc3)
    //PRINT "adc3="; adc3; "\n"
    
    adc_P = adc1 * 256 * 16.0 + adc2 * 16.0 + adc3 / 16.0 // 20ビット化
    PRINT "adc_P="; adc_P; "\n"
    
    var1 = t_fine / 2.0 - 64000.0
    
    var2 = var1 * var1 / 32768.0 * dig_P[5]
    
    var2 = var2 + var1 * dig_P[4] * 2.0
    
    var2 = var2 / 4.0 + dig_P[3] * 65536.0
    
    var1 = dig_P[2] / 524288.0 * var1 * var1 + dig_P[1] / 524288.0 * var1
    
    var1 = (1.0 + var1 / 32768.0) * dig_P[0]
    
    IF var1 == 0.0 THEN
        P = 0
    ELSE
        P = 1048576.0 - adc_P
        
        P = (P - var2 / 4096.0) * 6250 / var1
        
        var1 = dig_P[8] * P * P / 2147483648.0
        
        var2 = P * dig_P[7] / 32768.0
        
        P = P + (var1 + var2 + dig_P[6]) / 16.0
    ENDIF
    
    P = P / 100.0 // 単位は[Pa]なので[hPa]に直す
    
ENDFUNC P

2.7 タイマーイベントのハンドラ関数

// 一定周期で実行
FUNCTION HandlerTimer0()

    DIM T AS FLOAT
    DIM H AS FLOAT
    DIM P AS FLOAT
    
    // 温度, 湿度, 気圧を計測
    T = get_temperature()
    H = get_humidity()
    P = get_pressure()

    PRINT "Temperature="; T; "\n"
    PRINT "Humidity="; H; "\n"
    PRINT "Pressure="; P; "\n"

ENDFUNC 1 //remain blocked in WAITEVENT

2.8 メイン処理

DIM handle
DIM rc

// オープン
rc = I2cOpen(400 * 1000, 0, handle)

// 初期設定
DIM x: x = INIT_BME280()

// キャリブレーションデータの読み出し
x = READ_TRIM()

// 計測タイマー
ONEVENT EVTMR0 CALL HandlerTimer0
TimerStart(0, 1000, 1) //start a 500 millisecond recurring timer

// クローズ
//I2cClose(handle)

// 計測待ち
WAITEVENT


3. 動作確認

UwTerminalXにて、結果を表示し、ひとまず温度、湿度は正しく表示されることを確認できた。



まとめと今後の課題

BL652でBME280とI2Cで通信し、計測値を取得することができた。(ただし、気圧はうまくいっていない)次回は、BLEのアドバタイズに載せて飛ばしてみたいと思う。