ESP32 用 BME280 ( 温度 湿度 気圧センサー ) ライブラリを作ってみた

ESP32 ( ESP-WROOM-32 )
2018/06/28以降、大幅更新された Arduino – ESP32 で、I2Cインターフェースの Wireライブラリが一新され、ほぼ I2Cトラブルが解消されました。
マルチタスクを使う場合、Wire関数を同じタスク内に収めること、などの注意点があります。
以下の記事を参照してください。
Arduino – ESP32 大幅更新( 2018/06/28以降 )と I2C 不具合解決、その他気付いたこと
(2018/07/07)
その他、以下の記事も合わせてご覧ください。
BME280 を M5Stack で使って再びハマったこと、BOSCH 純正ドライバの使用について
(2018/06/22)

 

こんばんは。

台風の季節がやってきたので、去年と同じように温度・湿度・気圧センサーの電子工作に取り組むことになりました。

去年はBOSCH 製 BME280 を ESP8266 用 SPI通信で動かしました。

BME280 搭載、温度・湿度・気圧センサーを SPI で動かしてみた( ESP-WROOM-02 ( ESP8266 )使用)

今年はそれを ESP32 ( ESP-WROOM-32 )で動かしてみます。

そして、Arduino core for ESP32 用ライブラリも作ってみました。
I2C 通信用と、 SPI 通信用の2種類作ってみました。

それぞれ、長所短所がいろいろとあったので、ご報告したいと思います。

また、スイッチサイエンス製 ESPr Developer 環境センサシールドも使って見ましたが、ESP-WROOM-32 ( ESP32 )の発熱によって正しく計測できないので、その注意点なども報告したいと思います。

スポンサーリンク

使用するもの

BME280 モジュールまたは ESPr Developer用環境センサシールド

BME280 モジュールはスイッチサイエンス製の以下のものがお勧めです。
ピンヘッダ実装済みと別途ハンダ付け用のものとあります。
これは、I2Cと SPI 通信と両対応です。

スイッチサイエンス BME280温度/湿度/気圧センサモジュール SSCI-022361
スイッチサイエンス
¥2,649(2024/12/27 03:57時点)

現在Amazonで販売されていないようなので、以下のスイッチサイエンスさんの公式サイトをご参照ください。
https://www.switch-science.com/products/2236?_pos=3&_sid=373c89d96&_ss=r

ESPr Developer 用環境センサシールドは、BME280 とフォトトランジスタを搭載していて、明るさを測ることができます。
ただし、これを使う場合の詳細は後で述べますが、注意しなければならないことがあります。

まず、I2C通信専用ということと、ESP-WROOM-32 の発熱で正しく測定できないので、DeepSleep で使用するなどの条件があります。

ESPr Developer用環境センサシールド
スイッチサイエンス(Switch Science)

ESPr Developer 32 または ESP32-DevKitC

ESP-WROOM-32開発ボードは、USB機器に優しいソフトスタート機能が付いていて、Windows機器との相性が良いFTDI製 USBシリアル変換を搭載した、スイッチサイエンス製の ESPr Developer 32 がお勧めです。

ESPr Developer 32
スイッチサイエンス(Switch Science)

スイッチサイエンスさんのページ→ ESPr® Developer 32

私のレビューの記事は以下を参照してください。

ESPr Developer 32 ( スイッチサイエンス製 ) を使ってみました

本家 Espressif社製の開発ボードは、秋月電子通商さんや、Amazonさんで購入できます。

秋月電子通商さんのページ→ ESP32-DevKitC ESP-WROOM-32開発ボード

固定抵抗 2kΩ×2つ

I2C通信で使用する場合は、SCL, SDAラインに2kΩ程度のプルアップ抵抗がそれぞれ必要です。

サンハヤト SAD-101 ニューブレッドボード

ブレッドボードはサンハヤト製の SAD-101 は列が多くて、抜き差しし易くお勧めです。

サンハヤト SAD-101 ニューブレッドボード
サンハヤト
¥568(2024/12/26 07:02時点)

温度湿度計(参照用)

BME280 が正しく動作しているかどうかの目安に温度湿度計が必要です。
私の場合は、コンパクトで薄く、置き場所に困らない以下の物を使用しています。

これ、意外と正確なんですよ!
とても使い勝手が良いのでお勧めです。

EMPEX  TD-8173

EMPEX (エンペックス) デジカード(デジタル温・湿度計) TD-8173
エンペックス(Empex)
¥2,980(2024/12/26 14:59時点)

Arduino core for ESP32 の設定を事前に済ませておく

Arduino IDE は最新版の 1.8.3 以降を使用してください。

Arduino core for ESP32 のインストール方法は以下のページを参照してください。

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

自作ライブラリのインストール

スイッチサイエンスさんのページ
BME280搭載 温湿度・気圧センサモジュールの使い方
や、Adafruit さんのコードを参照して、私なりに Arduino core for ESP32 用ライブラリを作ってみました。

I2Cインターフェース用と、SPIインターフェース用の2種類作りました。
GitHub の以下のページに置いておきます。
因みに無保証ですのでご了承ください。

【I2C用】
https://github.com/mgo-tec/ESP32_BME280_I2C

【SPI ( HSPI )用】
https://github.com/mgo-tec/ESP32_BME280_SPI

ZIPファイルをダウンロードして、Arduino IDE にインストールしてください。
インストール方法は以下のページを参照してください。

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

I2Cインターフェースで使用する場合

ESP32 および M5Stack で BME280 を I2C で制御する場合のエラー原因について、新たな事実が分かりました。
以下の記事も合わせてご覧ください。
BME280 を M5Stack で使って再びハマったこと、BOSCH 純正ドライバの使用について
(2018/06/22)

 

では、まず、I2Cインターフェースの場合の場合、接続はそれぞれこのようになります。

【ESPr Developer 32 の場合】

実際の写真はこうなります。

【ESP32-DevKitC の場合】

実際の写真はこうなります。

 

I2C の場合、SCL, SDAラインは適当なプルアップ抵抗が必要です。
ここでは 2kΩとしました。
スイッチサイエンスさんの BME280 モジュールの場合、電源の Vio と Vcore は基板内部で接続されているので、どちらかに接続すればOKです。

SDO端子は GND に接続すれば、デバイスアドレスが 0x76
Vio に接続すれば 0x77 になります。
必ずどちらかに接続してください。

CSB端子は誤作動を避けるため、それぞれ GND や 3V3に接続しておきます。

では、サンプルスケッチはこんな感じになります。

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

#include "ESP32_BME280_I2C.h"

ESP32_BME280_I2C bme280i2c(0x76, 21, 25, 400000); //address, SCK, SDA, frequency

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  uint8_t t_sb = 0; //stanby 0.5ms
  uint8_t filter = 4; //IIR filter = 16
  uint8_t osrs_t = 2; //OverSampling Temperature x2
  uint8_t osrs_p = 5; //OverSampling Pressure x16
  uint8_t osrs_h = 1; //OverSampling Humidity x1
  uint8_t Mode = 3; //Normal mode
 
  bme280i2c.ESP32_BME280_I2C_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);
  delay(1000);
}

void loop(){
  bme_get();
  delay(5000);
}

//************** BME280 測定 *************************
void bme_get(){ 
  byte temperature = (byte)round(bme280i2c.Read_Temperature());
  byte humidity = (byte)round(bme280i2c.Read_Humidity());
  uint16_t pressure = (uint16_t)round(bme280i2c.Read_Pressure());

  char temp_c[10], hum_c[10], pres_c[10];
  sprintf(temp_c, "%2d ℃", temperature);
  sprintf(hum_c, "%2d %", humidity);
  sprintf(pres_c, "%4d hPa", pressure);

  Serial.println("-----------------------");
  Serial.print("Temperature = "); Serial.println(temp_c);
  Serial.print("Humidity = "); Serial.println(hum_c);
  Serial.print("Pressure = "); Serial.println(pres_c);
  Serial.println("-----------------------");
  Serial.flush();
}

【解説】

●3行目:
自作ライブラリのクラス名を定義と初期化です。
0x76 はデフォルトの I2C アドレスです。
SCK ラインは GPIO 21番。
SDA ラインは GPIO 25番。
I2C クロック周波数はとりあえず 400kHz としています。

●9-14行目:
ここでは、BME280 のデータシートの屋内測定推奨値を使っています。
その他、スイッチサイエンスさんの以下のページも参照してください。

BME280搭載 温湿度・気圧センサモジュールの使い方

filter はゼロでも値に特に変化はありませんでした。
オーバーサンプリングは値を大きくするとサンプリング時間が多くかかりますが、4くらいにすると手持ちの温度湿度計とほぼ同じ値になりました。

Mode は sleep モードにできますが、今回はノーマルモードで使用します。

●16行目:
ここで、bme280 の初期化を行います。

●26-42行:
ここで、温度、湿度、気圧を測定しています。
27-29行では、round関数で小数点以下を四捨五入しています。

その他、過去に ESP8266 でも使用した例がありますので、過去記事を参照してください。

BME280 搭載、温度・湿度・気圧センサーを SPI で動かしてみた( ESP-WROOM-02 ( ESP8266 )使用)

コンパイル書き込み実行

コンパイル書き込み実行させて、シリアルモニターを 115200bps で起動してください。

すると、このように表示されればOKです。
値が安定するまでに5~10分以上、辛抱強く待ってください。

実際の温度湿度計と比較した写真は以下のようになります。

湿度が3%ほど誤差がありますが、ほぼ許容範囲内かと思います。

この温度湿度計は先ほど紹介した EMPEX  TD-8173 です。

WiFi を使わずに、ただ I2C だけを使うのであれば、ESP-WROOM-32 もそれほど発熱せず、ほぼ正しい値で表示してくれると思います。

ただ、後で述べますが、WiFi を使ったり、デュアルコアを使ったりすると、ESP-WROOM-32 自体がかなり発熱しますので、その周辺は正常な計測ができませんのでご注意ください。

SPI ( HSPI )接続で使用する

Arduino core for ESP32 の SPI インターフェースは VSPI と HSPI 接続がありますが、BME280測定の場合、HSPI 接続をお勧めします。
( VSPI, HSPI については次のページを参照してください)
ESP32 の SPI_MODE が修正。HSPI , VSPI , 複数SPIデバイス制御 , SPI高速化などについて

なぜ、HSPI かというと、VSPI は SDカードで使用することが多く、SDライブラリと併用するとうまく動きません。
HSPI ならば、他のデバイスと併用しても特に問題無く動作するからです。

接続方法は以下の通りです。

【ESPr Developer 32 の場合】

実際の写真はこうなります。

 

【ESP32-DevKitC の場合】

実際の写真はこうなります。

 

では、以下のスケッチを入力してみてください。

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

#include "ESP32_BME280_SPI.h"

const uint8_t SCLK_bme280 = 14;
const uint8_t MOSI_bme280 =13; //Master Output Slave Input ESP32=Master,BME280=slave 
const uint8_t MISO_bme280 =12; //Master Input Slave Output
const uint8_t CS_bme280 = 26; //CS pin

ESP32_BME280_SPI bme280spi(SCLK_bme280, MOSI_bme280, MISO_bme280, CS_bme280, 10000000);

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  uint8_t t_sb = 0; //stanby 0.5ms
  uint8_t filter = 4; //IIR filter = 16
  uint8_t osrs_t = 2; //OverSampling Temperature x2
  uint8_t osrs_p = 5; //OverSampling Pressure x16
  uint8_t osrs_h = 1; //OverSampling Humidity x1
  uint8_t Mode = 3; //Normal mode
 
  bme280spi.ESP32_BME280_SPI_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);
  delay(1000);
}

void loop(){
  bme_get();
  delay(5000);
}

//************** BME280 測定 *************************
void bme_get(){ 
  byte temperature = (byte)round(bme280spi.Read_Temperature());
  byte humidity = (byte)round(bme280spi.Read_Humidity());
  uint16_t pressure = (uint16_t)round(bme280spi.Read_Pressure());

  char temp_c[10], hum_c[10], pres_c[10];
  sprintf(temp_c, "%2d ℃", temperature);
  sprintf(hum_c, "%2d %", humidity);
  sprintf(pres_c, "%4d hPa", pressure);

  Serial.println("-----------------------");
  Serial.print("Temperature = "); Serial.println(temp_c);
  Serial.print("Humidity = "); Serial.println(hum_c);
  Serial.print("Pressure = "); Serial.println(pres_c);
  Serial.println("-----------------------");
  Serial.flush();
}

【解説】

●3-6行:
SPI ( HSPI )通信用の GPIO ピンアサインです。
HSPI ハードウェア CS ピンは、他のデバイスと共有する場合は使い辛いので、GPIO #26 としました。
ライブラリ内で、digitalWrite 関数を使って HIGH , LOW を切り替えています。

●8行:
自作ライブラリのクラス名定義と、初期化です。
SPI クロック周波数は、BME280 のデータシートから、最大値 10MHz としています。

以上、それ以降は I2C と同様です。

コンパイル書き込み実行

では、コンパイル書き込み実行してみてください。
I2C と同様、5分以上辛抱強く待つと値が安定します。

シリアルモニターではI2Cと同様ですが、こんな感じに表示されればOKです。

ところで、私がいろいろ実験したところ、ディスプレイや SD カードで SPI インターフェースを使うことが多く、最近ではデュアルコアも使ったりしていると、SPI 信号が競合してしまって、うまく動作しないことがありました。

温湿度、気圧測定はそれほどスピードを要求されることは無いので、私個人的には SPI よりも I2C で十分かと思います。

ESPr Developer 環境センサシールドを使う場合の注意点

スイッチサイエンスさんのESPr Developer 32 を使う場合、衝動的に環境センサシールドを使いたくなりますね。

私も見た目に惹きつけられ、買ってしまいました。

しかし、ESPr Developer 32 にマウントする場合、いろいろと注意しなければいけない事があります。
スイッチサイエンスさんの次のサイトと合わせてご参照ください。https://www.switch-science.com/catalog/2703/

外観

外観はこんな感じのもので、ピンヘッダは予めハンダ付けされています。
ESPr Developer や ESPr Developer 32 に簡単にマウントできるようになっています。

ESPr Developer用環境センサシールド
スイッチサイエンス(Switch Science)

I2Cピンアサインの変更

これは I2C 専用ですが、デフォルトで使用すると、SPI インターフェースと GPIOが競合するので、ピンアサインを変更します。

私がやったことは下図の様な方法ですが、別のアサインでも構いません。

 

まず、下図の様に SDA ピンと IO4 の基板パターンをカッターで切れ目を入れてカットします。
あまりカッターの刃を深く入れ過ぎないように注意してください。

パターンカットしたら、導通していないかテスターでチェックします。

次に、すずメッキ線や、余った抵抗のリード線などで、SDAを IO14 ピンと接続してハンダ付けします。
するとこんな感じになります。

これで、先に紹介したESPr Developer 32 の I2C 接続のピンアサインと同じになります。

GPIO #21  —- BME280 SCK
GPIO #25  —- BME280 SDI

プルアップ抵抗はこの環境センサシールド内で接続されているので不要です。

では、このシールドを下図の様に ESPr Developer 32 にマウントしてください。
方向を間違えないようにしてください。
3V3ピンを合わせれば良いと思います。
(ESPr Developer 32 については以下のページを参照してください)
ESPr Developer 32 ( スイッチサイエンス製 ) を使ってみました

では、これで、先に紹介した I2C 通信用のスケッチをコンパイル実行してみてください。
5分以上経過した結果はこんな感じになりました。

気温が 27 ℃、湿度 64% なのに、後ろに見えているシリアルモニターでは、
30℃、58% を表示していますね。

要するに、温度が3℃ 高くなっています。
温度が高くなると、その周辺の湿度は下がります。

要するに、ESP-WROOM-32 の発熱による影響のようです。
触ると、僅かですが暖かくなっているような感じがします。

では、例えば、デュアルコアで、ディスプレイ表示させて、CPU をフル回転させた場合はどうなるでしょうか?

実は、下図のように温度が10℃ も高く表示されてしまいます。

試しに、ロングピンソケットをもう一段追加して、間にプレートとコピー用紙を挟んでみました。

それでも、7℃ も高く表示されてしまいました。
熱が周囲の温度を上げてしまっているようです。

これでは使い物になりません。

でも、折角買ってしまったので、何とか有効活用したい。

ということで、ESP32 を Deep Sleep モードで動かしてみました。
Arduino core for ESP32 のスケッチの例を参考にしています。
こんな感じです。

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

#include "ESP32_BME280_I2C.h"

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

ESP32_BME280_I2C bme280i2c(0x76, 21, 25, 400000);

void print_wakeup_reason(){
  esp_deep_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_deep_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case 1  : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2  : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3  : Serial.println("Wakeup caused by timer");
      bme_get();
      break;
    case 4  : Serial.println("Wakeup caused by touchpad"); break;
    case 5  : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  uint8_t t_sb = 0; //stanby 0.5ms
  uint8_t filter = 4; //IIR filter = 16
  uint8_t osrs_t = 2; //OverSampling Temperature x2
  uint8_t osrs_p = 5; //OverSampling Pressure x16
  uint8_t osrs_h = 1; //OverSampling Humidity x1
  uint8_t Mode = 3; //Normal mode
  
  bme280i2c.ESP32_BME280_I2C_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);
  delay(1000);

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  esp_deep_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

//************** BME280 測定 *************************
void bme_get(){ 
  byte temperature = (byte)round(bme280i2c.Read_Temperature());
  byte humidity = (byte)round(bme280i2c.Read_Humidity());
  uint16_t pressure = (uint16_t)round(bme280i2c.Read_Pressure());

  char temp_c[10], hum_c[10], pres_c[10];
  sprintf(temp_c, "%2d ℃", temperature);
  sprintf(hum_c, "%2d %", humidity);
  sprintf(pres_c, "%4d hPa", pressure);

  Serial.println("-----------------------");
  Serial.print("Temperature = "); Serial.println(temp_c);
  Serial.print("Humidity = "); Serial.println(hum_c);
  Serial.print("Pressure = "); Serial.println(pres_c);
  Serial.println("-----------------------");
  Serial.flush();
}

5秒毎にWake Up させて測定しています。

コンパイル実行した結果、こういう感じになりました。

後ろに見えているシリアルモニターと比べてみても、湿度が1%の誤差です。
これは殆ど誤差が無いと言ってよいのではないでしょうか。
Deep Sleep にするとかなり正確に表示されますね。
ESP-WROOM-32 に触ってみても冷たいままで、バッチリOKです。

以上から、ESPr Developer 環境センサシールドを正しく使うには、以下の方法かと思います。

1.Deep Sleep モードで使用する。
2.ESP-WROOM-32 にファンを当てて冷ます。
3.距離を離した別途ボードにマウントする

残念ながら、かっこよくシンプルにシールドとして使用するには難しいデバイスですね。
結局のところ、CPU フルパワーで使用するには、BME280 単体ボードを、距離を離して設置することが良いようです。

まとめ

BME280 は結構繊細で、測定場所をちょっと変えるだけで敏感に反応してくれる優れものです。

ですが、SPI で通信する必要はなく、低速の I2C で十分です。
SPI で通信すると、他の高速デバイスのディスプレイや SD カードと競合してしまうし、デュアルコア(マルチタスク)で使用する場合に不都合です。

それと、ESP-WROOM-32 モジュールをCPUフルパワーで使用する場合は、十分距離を離すか、Deep Sleep にするなどの対策をしないと正しく測定できないことを十分考慮する必要があります。

以上、Arduino core for ESP32 で BME280センサーを使う方法でした。
これを使えば、台風の季節で外出できなくても家の中で気圧の変化を楽しめますね。

今回はここまでです。

ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/12/26 11:02時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥28,006(2024/12/26 17:56時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/12/26 09:03時点)

コメント

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