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

M5Stack

こんばんは。

今回は、以前から何度も悩まされていた BOSCH(ボッシュ)製、気圧・温度・湿度センサー BME280 をM5Stack で使ってみたけれど、やっぱり難解だったという報告と、BOSCH純正ドライバが使えるようになったという報告です。

でも、いろいろ試行錯誤した結果、測定が中断せずに誤差を加味しながら延々と測定することが可能になりました。

スポンサーリンク

実は、前回の記事の M5Stack ( ESP32搭載 ) Yahooニュース天気予報時計に BME280 の測定値を表示させようと思ったのですが、以前のBME280用スケッチを流用しても、なぜか3時間経過後にACKエラーが重なり、測定不能になってしまったんです。
I2C クロック周波数を上げると、もっと顕著に30分で測定不能になったりしました。

BME280 は当ブログで何度も使ってきたのですが、そういえば、以前もこんなことがありました。
その時は、I2C クロック周波数を下げればある程度長い時間測定できたし、以前、以下の記事では、BME280 の 室内測定の推奨値を知って、ある程度の精度も出たので放置していました。

ESP32 の Wi-Fi のみ OFF および温度・湿度・気圧センサー BME280 の再調整など

今回も、I2Cクロック周波数を 10kHz まで下げると13時間以上エラーも無く問題無く測定できました。
しかし・・・

何かおかしい!

I2C のスタンダードモードでは 100kHz ~ 400kHz の範囲だったと思います。
10kHz はあまりに遅すぎます。

また、ESP32 開発ボードおよび M5Stack で他のデバイスは一切使用せず、BME280単体でシリアルモニターに表示させるだけならば、精度良く、長時間安定した測定をすることができます。
でも、他のSPIデバイスと同時でWi-Fi使用したり、あるいはマルチタスクを使用したりすると、測定誤差が大きくなり、しかも数時間後に測定不能になるのです。

明らかに何かがおかしい!

BME280 ドライバライブラリは SPI用、I2C用とも、データシートやスイッチサイエンスさんのサイトを参考にして自分で組み上げたものを使っていたので、それが原因かと思いました。

そこで、BOSCH社が公式でGitHubに公開している BME280ドライバライブラリを使ってみようと思いました。
以前も使おうと思ったのですが使い方が分からなかったやつです。
今回はそれがようやく使えるようになりました!

ただ、結論から言うと、それを使ったからと言って測定不能になることは変わりありませんでした。
測定精度も自作ライブラリとあまり変わりありませんでした。

では、いったい何が原因だったのか・・・。

そんなこんなで、いろいろハマった経緯を報告したいと思います。
暇つぶしに読んでやってください。
何か根本的に間違えているかもしれませんので、分かる方がいらっしゃったらコメント投稿等で教えていただけると助かります。

2018/06/28以降、大幅更新された Arduino – ESP32 で、I2Cインターフェースの Wireライブラリが一新され、ほぼ I2Cトラブルが解消されました。
マルチタスクを使う場合、Wire関数を同じタスク内に収めることなどの注意点があります。
以下の記事を参照してください。
Arduino – ESP32 大幅更新( 2018/06/28以降 )と I2C 不具合解決、その他気付いたこと
(2018/07/07)

 

2018/06/28 以降更新されたArduino-ESP32は、Wireライブラリの仕様が全面変更されました。
その場合、Wire.reset()関数が無くなり、Wire.requestFrom関数の引数の型が変更になりました。
(2018/07/03)
以下、2018/06/24バージョンのArduino-ESP32を使った場合の対策方法です。

 

ハマった経緯、そして解決?

今回、ハマった経緯は、私の備忘録でもあるので、ダラ~っと長ったらしく書きます。
覚悟して読んでください。
勘違いしていることも有るかも知れません。

先にも紹介しましたが、ESP-WROOM-02 ( ESP8266 ) や、ESP-WROOM-32 ( ESP32 ) 開発ボードの時にも何度も謎の計測不能に陥ったり誤差が出たりしましたが、ある程度計測できていたのでそのまま放置していました。
それで、今回、梅雨や台風時季に突入するにあたり、久々に BME280を動かしてみようと思いました。
ESP32搭載 M5Stack で複数の SPI デバイスと Wi-Fi、そしてマルチタスクで動かしながら BME280 を使ってみましたが、3時間後に ACKエラーを検知し、計測不能に陥りました。

まず、以前、Twitter のタイムラインに、ESP32 の I2C がバグっているのではないかというものが流れてきたので、今回の BME280 もそれかと思いました。

そこで、「こばさんの wakwak 山歩き」というブログで、以下の記事が真っ先に検索に挙がりました。

ESP32 の I2C は壮絶にバグってる気がする

そちらに書いてあるように、Arduino-ESP32 の Wire ライブラリが原因かもと疑いました。
でも、このブログに書いてあるように、Wire.reset() では根本的な解決にならないだろうということに同感したので、別の原因を探りました。

すると、t-yosh さんのブログで、以下の記事が見つかりました。

I2C通信 デバッグ – M5Stack – 20180411解決

この記事をザッと読み、当方でオシロスコープ確認をする前に、記事の最後の方で紹介されている最終解決を試してみました。
それは、Stickbreaker さんがGitHub で公開している修正方法です。

でも、私の場合、最新版 Arduino-ESP32 ではコンパイルエラーになってしまったので、その方法は諦め、別の方法を調べました。

すると、Stickbreaker さんが、GitHub の issue で、I2C のプルアップ抵抗値によって、クロック周波数を変えなければならないという文面がありました。
そこで、以前、あまりにも難解で読むのを諦めたことのある、Philips社の I2C バス仕様書を再び調べてみました。
これはググるとNXP社から配布されている日本語訳が出ているので、すぐに見つかると思います。

この I2C バス仕様書、ホントに難しいんです。
再び読んでも、やっぱり難解で、案の定、心が折れました・・・。

I2C 通信っていうのは電子工作家にとっては手軽に思えて、数珠つなぎにできて、使いやすいものだと思っていましたが、実はとっても調整が難しい通信みたいです。
(後で述べますが、実はそんなに難しく考えなくても良いことが分かりました。)
M5Stackには Groveコネクタで I2C が出ているし、見た目で手軽って思ってしまいますね。

ところで、私は Twitterで今回のトラブルをツイートしていましたが、ある方から、I2C はアナログの要素があり、リターンノイズとかもあるので SPIの方が使いやすい、という情報を頂きました。
その方によれば、SPIデバイスでまず構成して、CSが足りなくなった場合に I2C を使う方が良いというアドバイスをいただきました。
ほんとにそうだなと思いました。
これから電子工作する場合には、その方のおっしゃる通り、SPIデバイスから構成していこうと思います。
いいアドバイスありがとうございました。
m(_ _)m

さて、それからオシロスコープを引っ張り出してきて、I2C 信号を測定してみることにしました。
まず、M5Stack のプルアップ抵抗値から調べなければいけません。

ただ、公式の M5Stack 回路図を見ても、GPIO #21, #22 のプルアップ抵抗値は記載されていないようなので、テスターで 3V3 端子と GPIO #21, #22 端子の抵抗値を調べました。
すると、
約 3.2kΩ
となっていました。
他の抵抗を並列接続させても抵抗値は増えないので、概ねその近辺の抵抗値と思われます。

ここで、ストロベリーリナックスさんの以下の製品のコメントで、プルアップ抵抗値について書かれているところを見つけました。

https://strawberry-linux.com/support/27001/619011

なるほどなー・・・。
そういうこともあるのかと思いました。
そうすると、M5Stack の I2C プルアップ抵抗値は低すぎることになりそうです。
ますますオシロスコープで波形を見なければならなくなりました。

まず、M5Stack の標準 I2C 出力 GPIO #21, #22 を使って、BME280 と I2C 接続します。

この時、個人的に留意しなければいけない点があります。
私の目的として、前回の記事のプログラムに BME280 を表示させる機能を持たせることです。

そうすると、M5Stack の発熱が問題になるので、できるだけ I2C バスラインを延長して、M5Stack から離さねばなりません。
もちろん、I2Cバスは短くしないとエラーを起こし易いことは十分承知しています
ここは、あくまでホビーおよび趣味用途として割り切って、I2Cバスラインを伸ばすことにします。

下図の様に接続して、オシロで波形を見てみます。
(BME280モジュール基板の作り方は後で説明します)

BME280モジュールと M5Stack の接続方法は下図のようになります。

 

100MHz クラスのオシロスコープで波形を拡大してみます。
I2C クロック周波数は 400kHz にしています。

これを見ると、M5Stack の I2Cプルアップ抵抗 3.2kΩで、バスラインが長くても、それほど波形はなまっておらず、GND付近までしっかり振れています。
ということは、I2Cバスラインのプルアップ抵抗値が低すぎるということでも無いようです。

ところで、先ほど、I2Cバス仕様書が難解だと言いましたが、ここでふとひらめきました。
考えてみれば、ESP32 の I2C 信号は、所詮 GPIO の HIGH-LOW 切り替えだけで生成されているだけなので、バスラインをオシロで波形を見ながら、信号が鈍らないようなプルアップ抵抗値やコンデンサ容量を決めればよいだけのことだよなー、と・・・。

SCL信号の立ち上がりでちょっと鈍っていますが、これはコンデンサに充電する時間と考えれば納得いきます。
バスラインが長いことによる、コンデンサ容量が大きいと言えると思います。
そして、プルアップ抵抗値が大きいと、コンデンサに充電する時間が長くなるので、波形が鈍ります。

そう考えると、プルアップ抵抗値をこれ以上上げても、波形が鈍るだけかなと予想できます。

試しに、プルアップ抵抗値を大きくしてみます。
M5Stack の GPIO #21, #22 は予め抵抗がハンダ付けされているので大きく出来ませが、GPIO #16, #17 に切り替えて、プルアップ抵抗20kΩを接続して波形を測定してみました。
すると、こんな感じです。

予想した通り、プルアップ抵抗値を上げたことにより、クロック波形の立ち上がりが鈍りました。
これはコンデンサの充電時間が長くなった感じと一緒です。
100kΩでも試してみましたが、その場合、3.3V まで振れませんでした。
先に述べた Stickbreaker さんがおっしゃっていた通り、プルアップ抵抗が大きくなると I2C クロック周波数を遅くしないとパルスとして認識しないですね。
このことから、バスラインのプルアップ抵抗は、M5Stack そのままの3.2kΩで良いと言えそうです。

では、通常の M5Stack GPIO #21, #22 の 3.2kΩプルアップに戻して、再度、センサー読み込み時の波形を見てみます。

データシート通り、最初にBME280デバイスアドレス0x76 を左へ1bitシフトした値を書き込み、BME280 からのACK受信(LOWレベルにする)し、その後、気圧のMSB読み込み用コントロールバイト 0xF7 を書き込んで、ACK受信しています。
その後、STOPコンディションになっていることが分かると思います。
ここで STOPコンディションを入れているのは、BOSCH製純正ドライバの例に沿ってプログラムを組んだからです。
(BOSCH製ドライバの使い方は後で紹介しています)

その後、BME280 からのセンサー値読み込みには、Arduino – ESP32 の Wire ライブラリ関数、Wire.requestFrom を使います。
すると、アドレス 0x76 を書き込んだ後のビットが、上図の様に1になっていることが分かると思います。
その後の ACK受信で BME280 からのセンサー値が出力されるということです。

これを見る限り、2つ目の ACK 受信前のスパイクノイズ以外は特に問題になるところは見当たりません。
このスパイクノイズは、先に紹介した t-yosh さんのブログでも同じ症状が見てとれますので、気にしなくても良いと思われます。

以下のBME280からの中間電位は私のオシロスコープ設定が誤っておりました。
BME280からは正常なパルスが出ておりました。
お騒がせしてゴメンナサイ。
m(_ _)m
(2018/07/06)

 

問題はその先です。
以下の波形を見てください。

なんと、謎の中間電位パルスが出ています。
これ、M5Stack ( ESP32 ) からの Write 時には一切出ないのですが、決まって BME280 からの読み込み時に現れます
先に紹介した、ストロベリーリナックスさんのサイトに書いてあるように、プルアップ抵抗を20kΩ以上にしても消えませんでした。

私のオシロスコープが壊れているのかと思い、試しに Arduino UNO で同じ症状が出るか確認してみましたが、同じ症状でした。
クロック周波数を遅くしても消えませんでした。
ひどい時には頻繁に出ます。
別の BME280 に替えても同じ症状でした。

これ、いったい何でしょうね???
サッパリ分かりません。
I2C バスラインがノイズを拾っていることも考えられますが、それとは違うパルスのような気がします。
I2C バスラインが長すぎることによるノイズなのか、M5Stack からのノイズを拾ったのか?
BME280 を電磁シールドしたら消えるんでしょうか?
でも、電磁シールドしてしまったら、正しい温度湿度は測定できませんよね。

この中間電位は皆さん出ているんでしょうか?
私の方法が何か間違えているんでしょうか?

中間電位が続くと還流電流が生じるという情報をネットで見たことがあります。
それによって誤作動が起こるらしいです。

同時に電源やGNDラインも測定しましたが、変なスパイクノイズは出ていません。

もしかしたら、I2C バスラインを極力短くすると消えるかもしれませんが、発熱するM5Stack に近づけると意味ない測定になるので、それはしたくありません。

因みに、このバスラインのブレッドボードを外して、距離を半分に縮めても中間電位は消えませんでした。

前回の記事のものにBME280をセットして、マルチタスクで毎秒計測していくと、400kHz では30分以内でシリアルモニターに下図の様なエラー表示が出てきます。
BOSH純正ドライバを使っています。
ツールメニューの「Core Debug Level」は「Verbose」にしています。

ACKエラーが初めて出現したあと、連続でエラーになります。

rslt = -4
というエラーコードは、
BME280_E_COMM_FAIL
ですので、コマンドエラーですね。

この時、Wire.endTransmission return = 5 となっていますが、これは、
I2C_ERROR_BUSY
ということです。

では、こうなった時のI2Cバスラインのオシロ波形はこんな感じになっています。

これ、まさしく、先に紹介した t-yosh さんのブログの結果と全く同じ波形が出ました。
延々と SCL クロックパルスが出続け、SDA ラインが HIGH レベルに張り付いています。

こうなってしまうと、当然エラーが出るわけですよね。
中間電位が悪さをして、還流電流が生じたのでしょうか?
ACK やSTOPコンディションを取りこぼした結果でしょうか?

そう言えば、以前、こちらの記事で電波時計を合わせるためのパルスを ESP32 から出した時、マルチタスクで動かすと、波形が乱れるということを経験しました。
安定したパルスが GPIO から出力できなかったのです。
それとも関係しているんでしょうか?
全くの謎です。

ということで、今の私の電子工作レベルではこれは解決できません。
もし、分かる方がいらっしゃったら、コメント投稿等で教えていただけると助かります。

これが溜まりに溜まって BME280 の測定誤差につながる可能性は高いですし、しばらく経ってからACK 読み取り不良が起こるのも納得できます。
BME280 って、正確な測定をするには難しいセンサーのようです。
素人の私では正しく扱うことができない繊細なものだと改めて実感しました。

もしかしたら、間に別途通信用バッファを設けないといけないかも・・・。

結局、今回のこの問題は私の実力では解決できなく、先へ進めないので、未解決のまま進行していこうと思います。
今後分かり次第、このブログでご報告したいと思います。

では、この エラーは放っておいても元に戻らないので、Arduino スケッチ上の Wire ライブラリでReset をかけることにしました。
つまり、結局は、最初に紹介した「こばさん」のWire.reset() をかける方法に落ち着いたわけです。
これは、t-yosh さんも試していました。

これは、M5Stack の ESP32 の I2C 通信をリセットするだけで、蓄積された BME280 のデータをリセットするわけではありません。
このリセットをかけないと、正常な I2C パルスを出力してくれませんので、それが一番良いという私の個人的結論です。

ということで、ACKエラーをひねりつぶして、測定誤差を加味しながら長時間 M5Stack で測定する方法に転向することにします。
これでようやく先へ進めます。

次の項では、M5Stack で BME280 を使う個人的方法と、BOSCH 製純正ドライバを使う方法を紹介し、エラーが出たら I2Cリセットをかけて測定し続ける方法を紹介してみたいと思います。

コメント

  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をコピーしました