BME280 を M5stack で使って再びハマったこと、BOSCH 純正ドライバの使用について

M5Stack

開発環境 Arduino – ESP32 を設定しておく

プログラムは Arduino IDE を使います。
Arduino IDE はver 1.8.5 で動作確認しておりますので、それを予めインストールしておいてください。

また、Arduino IDE でESP32 のプログラミングが出来るように、Arduino core for the ESP32 をインストールしておいてください。
そのインストール方法は以下の記事を参照してください。

Arduino core for the ESP32 のインストール方法

BOSCH製BME280ドライバをダウンロードしてインストールする

今まで、自作ドライバを使っていましたが、BME280 を正しく測定するために、BOSCH製純正ドライバを使ってみます。
(結果的に自作ドライバとあまり変わりありませんでした)

まず、GitHub の以下のページから ZIP ファイルをダウンロードしていただき、Arduino IDE にインストールしておいてください。

https://github.com/BoschSensortec/BME280_driver

ZIPファイルのまま Arduino IDE にインストールする方法は以下の記事を参照してください。

GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )

BOSCH製BME280ドライバを修正して FLOAT有効にする

インストールした BOSCH製 BME280 ドライバは、デフォルトでは整数値表現なので、使いやすいFLOAT型に変更します。
GitHubページにもその方法は書かれています。

まず、インストールしたフォルダを開きます。
Windows 10 の場合、以下のようなパスになると思います。
User-Name のところがご自分のユーザー名です。

C:\Users\User-Name\Documents\Arduino\libraries\BME280_driver-master

この中の、
bme280_defs.h
というファイルをテキストエディタで編集します。

そして下図の様に、105行目のコメントを解除して、
BME280_FLOAT_ENABLE
を有効にしてください。

こうすることによって、デフォルトでは64Bitモード有効だったものが解除され、32Bitモードになります。

整数表現も使ってみましたが、私にはちょっと使い辛かったので、今回は FLOAT 有効にしてみました。

BOSCH製BME280ドライバのスケッチ例

今までイマイチ使い方が分らなかったのですが、今回は何とか使えるようになりました。
基本的に、GitHub ページにある例に沿って、Arduino – ESP32 用に組んでみました。

以下の場合、1000ms(1秒)毎にセンサー値を取得しています。
フィルターやオーバーサンプリング等の値は、GitHub ページの
stream_sensor_data_normal_mode
をそのまま流用しています。
要するに、室内測定推奨値です。

誤っていたら、コメント投稿等でご連絡いただけると助かります。

最新版Arduino-ESP32で、2018/06/28 頃にダウンロードしたもの
(Commits on Jun 28, 2018)
は、Wireライブラリの仕様が全面変更されました。
その場合、Wire.reset()関数が無くなりましたので、69行目をコメントアウトするなり、消去するなりしてください。
逆に上手く動作しない場合は旧バージョンを使用してください。

(2018/06/28)

 

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <Wire.h>
#include "bme280.h" //BOSCH純正ドライバ

const int sda = 21;
const int scl = 22;

struct bme280_dev dev;
struct bme280_data comp_data;

//*********セットアップ*********
void setup() {
  Serial.begin(115200);
  BoschBME280init();
}
//*********メインループ*********
void loop() {
  BoschBME280dataGet();
}
//*********BME280初期化*********
void BoschBME280init(){
  Wire.begin( sda, scl, 400000 );
  dev.dev_id = BME280_I2C_ADDR_PRIM; //=0x76
  dev.intf = BME280_I2C_INTF; //I2C interface
  dev.read = user_i2c_read; //60行目の関数
  dev.write = user_i2c_write; //82行目の関数
  dev.delay_ms = user_delay_ms; //56行目の関数

  int8_t rslt = bme280_init( &dev );
  Serial.printf( "bme280_init rslt=%d\r\n", rslt );

  dev.settings.osr_h = BME280_OVERSAMPLING_1X;
  dev.settings.osr_p = BME280_OVERSAMPLING_16X;
  dev.settings.osr_t = BME280_OVERSAMPLING_2X;
  dev.settings.filter = BME280_FILTER_COEFF_16;
  dev.settings.standby_time = BME280_STANDBY_TIME_62_5_MS; //62.5ms

  uint8_t settings_sel;
  settings_sel = BME280_OSR_PRESS_SEL;
  settings_sel |= BME280_OSR_TEMP_SEL;
  settings_sel |= BME280_OSR_HUM_SEL;
  settings_sel |= BME280_STANDBY_SEL;
  settings_sel |= BME280_FILTER_SEL;
  rslt = bme280_set_sensor_settings( settings_sel, &dev );
  Serial.printf( "sensor settings rslt=%d\r\n",rslt );
  rslt = bme280_set_sensor_mode(BME280_NORMAL_MODE, &dev );
  Serial.printf( "sensor mode rslt=%d\r\n", rslt );
}
//********BME280センサー値取得**********************
void BoschBME280dataGet(){
  dev.delay_ms( 1000 ); //1秒毎に取得
  int8_t rslt = bme280_get_sensor_data( BME280_ALL, &comp_data, &dev );
  Serial.printf( "bme280_get_sensor_data rslt=%d\r\n", rslt );
  print_sensor_data( &comp_data );
}
//******************************
void user_delay_ms( uint32_t period ){
  delay( period );
}
//******************************
int8_t user_i2c_read( uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len ){
  int8_t rslt_read = 0; /* Return 0 for Success, non-zero for failure */

  Wire.beginTransmission( dev_id );
  Wire.write( reg_addr );
  Wire.endTransmission();
  uint8_t req_from_ret = Wire.requestFrom( dev_id, (uint8_t)len, true );

  if( req_from_ret == 0 ){
    Wire.reset(); //※重要!センサー値読み取りエラーの場合、強制リセット
    Serial.println("@@@@@@@@@@@@@@@@ Wire Reset! @@@@@@@@@@@@@@");
    rslt_read = 1;
  }else{
    for( int i = 0; i < len; i++ ){
      reg_data[i] = Wire.read();
    }
    rslt_read = 0;
  }

  return rslt_read;
}
//******************************
int8_t user_i2c_write( uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len ){
  int8_t rslt_write = 0; /* Return 0 for Success, non-zero for failure */

  Wire.beginTransmission( dev_id );
  Wire.write( reg_addr );
  for( int i = 0; i < len; i++ ){
    Wire.write( reg_data[i] );
  }
  rslt_write = Wire.endTransmission();

  return rslt_write;
}
//******************************
void print_sensor_data(struct bme280_data *comp_data){
  //BME280_FLOAT_ENABLE

  float temperature = (float)comp_data->temperature;
  float pressure = (float)comp_data->pressure/100.0;
  float humidity = (float)comp_data->humidity;
  if( temperature > 100 || temperature < -9 ) temperature = -100;
  if( pressure > 2000 || pressure < 700 ) pressure = 0;
  if( humidity > 100 || humidity < 0 ) humidity = -1;
  char temp_c[10], hum_c[10], pres_c[10];

  String pres_str = "";
  String temp_str = "";
  String hum_str = "";

  if( pressure == 0 ){
    pres_str = "??";
  }else{ //sprintf はArduino-ESP32専用。Arduino純正ハードウェアでは使えないので注意。
    sprintf( pres_c, "%6.1f", pressure ); //小数点含め全6桁、少数1桁
    pres_str = String(pres_c);
  }

  if( temperature == -100 ){
    temp_str = "??";
  }else{
    sprintf( temp_c, "%4.1f", temperature ); //小数点含め全4桁、少数1桁
    temp_str = String(temp_c);
  }

  if( humidity == -1 ){
    hum_str = "??";
  }else{
    sprintf( hum_c, "%5.1f", humidity ); //小数点含め全5桁、少数1桁
    hum_str = String(hum_c);
  }

  Serial.println("-----------------------");
  Serial.print( "pressure = ");
  Serial.print( pres_str );
  Serial.println( " hPa" );
  Serial.print( "temperature = ");
  Serial.print( temp_str );
  Serial.println( " ℃" );
  Serial.print( "humidity = ");
  Serial.print( hum_str );
  Serial.println( " %" );
}

センサー値取得間隔は、50行目で設定します。
ここでは、1000ms(1秒)毎にセンサー値を取得しています。

M5Stack で BME280 測定を行う場合の最も重要なことは、69行目です。
I2Cバスラインのノイズ等で、ACK受信できなかったり、その他のエラーが出たりした場合、I2C出力をリセットします。
このリセットをすることにより、M5Stack で複数の SPIデバイスと同時使用したり、マルチタスク使用したりした場合でも、ほぼ延々と測定可能になります。

因みに、rslt 値は、bme280_defs.h ファイルにエラーコードが定義されています。

BME280_E_NULL_PTR		(-1)
BME280_E_DEV_NOT_FOUND		(-2)
BME280_E_INVALID_LEN		(-3)
BME280_E_COMM_FAIL		(-4)
BME280_E_SLEEP_MODE_FAIL	(-5)

コンパイル書き込み実行

では、下図の様に BME280 モジュールをセットします。
ここで注意したいのは、M5Stack は発熱するので、その上方に BME280 をセットしない方が良いです。
上方にセットしてしまうと、おそらく室内温度より高い値を示し、湿度も低く表示されてしまうので要注意です。

では、コンパイル書き込み実行してみてください。
シリアルモニターを 115200bps で起動すると、下図の様に表示されると思います。

私の場合、市販の温湿度計と比較すると、温度は1℃くらいの誤差ですが、湿度は5~10% 異なります。
いずれにしても、市販の湿度計が正しいとも限らないので、こちらの記事のように簡易的にでも校正した方が良いかも知れません。

因みに、これは BME280 単体で動作させているので、滅多にエラーが出ません。
複数 SPI デバイスやマルチタスクで使用すると、頻繁にエラーが出るようになります。

まとめ

いかがでしたでしょうか。

毎回使うたびにトラブルに合う BME280 ですが、今回もまた未解決のまま使うことになりました。
これは本来すんごい繊細なセンサーで、ガッツリ本格的な回路を組まなければまともな動作してくれないのかも知れません。
例えば、M5Stack と BME280 を思いっきり離して、BME280 専用回路を設け、M5Stack と BME280 をLAN通信するなどでしょうか?
とすると、まだまだ勉強が足りません。

でも、今回分かったことは、I2Cバスラインの問題というよりも、BME280からの出力から中間電位パルスが出ていることが分かっただけでも、今後の対策に繋げられそうです。

そして、I2C出力を ESP32 側でリセットすることにより、ほぼ途切れなく計測することができるようになりました。
ただし、センサー値は中間電位による誤差が大きいことは頭に入れておくということです。

また、改善されたわけではありませんが、BOSCH純正ドライバを使えたことは安心できますね。
これは今後も使っていきたいと思います。

以上、今回はここまでです。
もし対策が解る方がいらっしゃったら教えていただきたいですね。

次回は前回の作品にBME280 のセンサー値を表示させてみたいと思います。

ではまた・・・。

コメント

  1. さや @La_zlo より:

    波形を拝見しました。
    I2Cのリードの直前のライトではSTOPをつけない事を強く推奨します。STOPを介さずにSTARTを発行(RESTART)してください。
    今回のトラブルの原因かどうかはわかりませんがご参考まで。

    補足)
    STOPはバスの解放を意味するので、マルチマスタ環境で誤動作の原因になります。STOPがあるとREADの直前のリードアドレスを他のデバイスに書き換えられる危険があるのです。

    • mgo-tec mgo-tec より:

      さやさん

      ダイレクトメールではいろいろとアドバイスありがとうございました。

      BME280 のデータシートには
      either a stop or a repeated start condition
      を入れると書いてあるので、stopコンディションは入れても問題無いと思います。
      BOSCHドライバの README にも stopコンディションを入れる例が書かれています。

      ただ、「さや」さんがおっしゃっていたとおり、設計仕様書に書いてないと、STOPでリードアドレスまでリセットかけちゃうHW設計者もいるかも知れないということは納得いきました。
      確かに複数デバイスでマルチタスクの場合はそういうリスクありそうですね。
      実際に経験された方からアドバイス頂けることは、独学しかしたことのない私にとってはとても有難いことです。
      これからのプログラミングに参考にしていこうと思います。
      貴重な情報、ありがとうございました。
      m(_ _)m

  2. BotanicFields より:

    いつも参考にさせていただいております。ありがとうございます。I2Cバスの中間電位の件、オシロの設定ミスとありますが、具体的にどの様に設定するとその様な波形が観測できるのでしょうか。実は、SPIで同様の波形を観測したことがあって、原因が不明で困っていました。どうかよろしくお願いいたします。

    • mgo-tec mgo-tec より:

      BotanicFieldsさん

      記事をご覧いただき、ありがとうございます。

      Twitterでも同じ質問をされたことがあります。
      結構みなさん中間電位が現れているようですね。

      私の場合の原因は、デジタルストレージオシロで、2つの波形の平均値を取るモード「Average」を選択してしまっていたことです。
      この波形を取るかなり前に、オシロスコープ設定の裏画面でそういう設定にしていて、すっかり忘れておりました。
      オシロスコープを久々に使うとこういうトラブルがあるもんだと思い知らされました。
      そんな感じで、しょうもないミスでございます。

  3. BotanicFields より:

    ご回答ありがとうございます。原因が分かると気持ちがいいですよね。私自身は全てを理解できたわけではありませんけれど。当方の原因不明の具体的な波形は以下です。ちら見程度にしていただければと思います。
    https://twitter.com/lv107/status/1126854524159684608

    • mgo-tec mgo-tec より:

      BotanicFieldsさん

      波形拝見させていただきました。
      なるほど、怪しい中間電位ですね。
      明らかに頻繁に発生するようでしたら、別の信頼できる測定器で測ってみるとかですかね?
      でも、別のM5Stackで測定して問題無いとすれば、M5Stackに原因があると思います。
      オシロの設定が間違いなければ、それくらいしか思いつきません。

タイトルとURLをコピーしました