4年前にESP32のモジュールを4種類買ったけどジャンク箱に入ったままでした。
・ESP32S [ESP32 DEVKIT V1] (KKHMF, アマゾン)
・ESP32 DevKitC ESP-WROOM-32 開発ボード(秋月電子通商)
・ESP32-KEY-KIT-R1 (MicroFan, アマゾン)
・ESP32-DEVC-KEY-R1 (MicroFan, アマゾン)
この何れかとボッシュのセンサBME680を接続して温度・湿度・気圧・ガスを測定しようと思い立ちました。
BME680 は以前Arduino NANO と Raspberry Pi Pico で動作した経験があるし、ジャンパ線4本で接続するだけなので楽勝!簡単、カンタン・・・のはずだったのに色々と紆余曲折ありまして、なかなか素直にはいかないのでした。
目次
[1]BME680 とESP32の接続
[2]I2Cの信号線をプルアップ
[3]ブレッドボードが・・
[4]Fritzingのデータ
[5]I2C アドレスに注意!!
[6]プログラム書き込みでエラー
[7]BME680が認識されない!
[1]BME680 とESP32の接続
BME680 はSPIとI2Cのどちらのインタフェースにも対応しています。ボード(モジュール)のメーカーによって、どちらか一方、またはどちらにも対応しているものがあります。スイッチサイエンスにはBME680のモジュールが6種類ほどあり、I2Cのみのものと両対応のものがあります。
私はストロベリーリナックス(SPI/I2C両対応、写真左側)と秋月電子(I2Cのみ)のボードを持っていて、差し替えて比較できるようI2Cで製作しました。
ESP32モジュールはESP32 DEVKIT V1を使うことにしました。ESP32 DEVKIT V1のピンアウト図はネットにどれにしようか迷うほど沢山あります。機能別に集めて並べスッキリと見易い、こちらにしました。
ピンの対応は下記のようになります。
BME680(秋月、ストロベリー) | ESP32 |
---|---|
VIN | 3V3 [注] |
SCL | GPIO22 |
SDA | GPIO21 |
GND | GND |
[注]電源電圧とレベルコンバータ
秋月のAE-BME680は3端子レギュレータ内蔵で電源入力:2.5V~6Vと幅広く、I2CレベルコンバータICも内蔵しているため使い易いです。ストロベリーリナックスはBME680のみ実装し、電源入力:1.71V~3.6Vです。5V電源のデバイスと接続する場合はレベルコンバータが必要となるため注意が必要です。
[2]I2Cの信号線をプルアップ
SDA, SCLの信号線にはプルアップ抵抗が必要です。
10KΩの抵抗を配線する方法と、ESP32に内蔵の抵抗を使用する方法があります。内蔵の抵抗を使用すれば部品も配線の手間も不要でスッキリしますが、敢えて抵抗(10KΩ)を2個使用します。
[3]ブレッドボードが・・
準備OK! よーし始めるか。まずはESP32 DEVKIT V1をブレッドボードに挿して配線だ・・ あれっ、ジャンパ線 abcdefghiまで全滅! 使えるのは1つだけなの?
解決策は2つ。
[その1]5穴のブレッドボードを2つ連結する
[その2]6穴のブレッドボードを買う
サンハヤトのSAD-101と、秋月電子のEIC-3901、どちらも同じに見えます・・
AとKLが使用できます。
[4]Fritzingのデータ
配線図と回路図をFritzingで作成します。 Fritzing のデータが3つ必要です。
①ESP32 DEVKIT V1
②サンハヤトのSAD-101
③BME680
手こずったのはサンハヤトSAD-101のFritzingデータです。「SAD-101 Fritzing」とかで検索してもなかなかヒットしません。誰も作成してなくて世の中に存在しないものを探しているのかもと諦めかけたけど、ついに見つけました!
「Fritzing用パーツ配布サイトまとめ」は、名前の通り様々なサイトのリンク集です。
ここは各種ESP32や、その他様々なFritzingデータのサイトが紹介されてますが、その中に
「DEKOさんの[Frtizing] データ置き場 (サンハヤトのブレッドボードSAD-101もあり)」
というのがありました。
※ヒットしない原因はスペルのミスでしょうか? (誤)Frtizing (正)Fritzing
たぶん「ふりっつぃんぐ」と発音するのだと思いますが、書くときはFrit zing 「ふりっと・じんぐ」なんだと覚えておくとよいかもです。
BME680のFritzingデータは見つけることができませんでした。正確にいうとI2CとSPIの両方に対応したピン数が多いのはあります。これはAdafruit-BME680です。
欲しいのはこれではなく、I2Cにのみ対応した4ピンのものです。どなたか「ここにあるぞ」というのをご存じでしたら是非ともお知らせ下さい。または「自分で作って公開した」というのも大歓迎です。よろしくお願いします。
取り敢えずBME680ではなくBME280のデータを使って配線図と回路図を作成し、そのままだと線の太さが変わったり、曲がり角にこぶができるなど細かいところが気になるので画像処理ソフトで修正しました。
配線図のBME680の画像はPower Pointで作成したjpegを貼りつけました。オリジナルの部品データをFritzingで作成する方法はネットのあちこちで紹介されてるけど面倒くさいなと思って・・・はい、手抜きです。
実物はこんな感じです。
黄色の枠線、電源ラインは14~17間に橋渡しの赤いジャンパ線がありますがGNDライン側はジャンパがありません。必要ないのです。理由はブレッドボードの裏側を見ると分かります。2種類の電源電圧に対応できるようになってるんですね。この赤いジャンパ線、買った時に既に配線済みです。無いと、気が付かずに動かない、動かないと悩むかもですね。
[5]I2C アドレスに注意!!
BME680のI2Cアドレスは、0x76と0x77のどちらかを選択できます。
ハード的には、秋月電子、ストロベリーの何れも半田付けで設定します。
ソフト的にはAdafruitのライブラリを使用したのですが、Adafruit_BME680.h では0x77がデフォルトとなってます。
【参考】この件での失敗談は、下記に書きました。I2CScannerでI2Cアドレスを調べる、といった内容もあります。
[6]プログラムの書き込みでエラー
Arduino IDEの書き込みボタンをクリックすると以下のエラーメッセージが表示されました。
A fatal error occurred: Failed to connect to ESP32.
Wrong boot mode detected(0x13). The chip need to be in download mode.
直訳すると・・・
「重大なエラーが起きた。ESP32の接続に失敗した。
ブートモードが間違っている。ダウンロードモードでなければいけない。」
ふ~む、ダウンロードモードになっていない、ということか。
この対策を検索すると (1)ボタンの操作で対処する方法 (2)ハードウェアの改造
の2つがあります。
(1) Boot ボタンを押しておき、書き込みが始まったら、Bootボタンを放す
IDEの書き込みボタンをクリックすると、メッセージがずらずら表示されます。
大まかな流れは、コンパイル→リンク→書込です。
①様々なライブラリファイルを読み込んで複数のオブジェクトを作る「Compiling」
②オブジェクトを結合する「Linking」
③実行形式のイメージを転送する「Connecting.....」(赤い文字)
完了まで約3分間、ずっと小さなボタンを押し続けると指が痛くなりますが、その必要はありません。
③の「Connecting...」が表示され始めたらすぐにBoot ボタンを押します。
(あわてなくても10秒ぐらいは....................と表示してBoot ボタンが押されるのを待ってます)
Connecting..........の下に何か表示されたらイメージ転送の処理に入ったということなのでBootボタンを放します。
(2)ハードウェアの改造
書込みのたびにメッセージを注視しBoot ボタンを押すのは面倒だという方は、EN ボタンとGND間に0.1μF (104) のコンデンサを半田付けすると解決します。
詳しくは下記をご覧下さい。
[7]BME680が認識されない!
プログラムの書き込みが出来るようになり、これで完成!と思っていたらシリアルモニタに以下のエラーメッセージが表示されました。
Could not find a valid BME680 sensor. check wiring!
え~、BME680を認識してないの? この問題はなかなか難しかったです。
・配線は何度も見直したが問題は無い。
・I2Cアドレス(ハードウェア、ソフトウェア)はOK
・プルアップ抵抗(実部品、またはチップ内蔵の抵抗を使用)はOK
・別のESP32モジュールに交換したけど変わらず。
・Arduino NANOでは正常に動作する。⇒BME680は問題なし。
となると、問題はプログラムにある?
下記はデバッグ中のプログラムの一部です。この短い処理のどこかに問題が潜んでいます。
何がいけないのでしょうか?
試行錯誤した様子が分かるようコメントアウトした文もそのまま残しています。
#include <Wire.h>
#include "Adafruit_BME680.h"
Adafruit_BME680 bme680;
//-----------------------------------------------------
void setup()
{
// Wire.begin(); // I2Cの初期化
Wire.begin(22,21); // SCL:22, SDA:21
// Serial.begin(9600); // for Arduino NANO
Serial.begin(115200); // for ESP32
delay(10);
Serial.println("Start");
// pinMode(21,INPUT_PULLUP); // I2C_SDA 21ピン プルアップ
// pinMode(22,INPUT_PULLUP); // I2C_SCL 22ピン プルアップ
if (!bme680.begin()) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1) {
delay(0);
}
}
(以下省略)
いくら眺めても分からないのでネットで公開されているプログラムと比較してみました。
するとAdafruitのライブラリを使用したものは Wire.begin(); の行が無いことに気が付きました。早速試してみると、これがあるとNG、コメントアウトすると動く!
シリアルモニタを起動し、リセットボタン(EN)を押すと以下のように表示されました。
動作しなかった理由は Adafruit_BME680 bme680; でオブジェクトを作ったのに、その直後 Wire.begin(); でI2C関係を初期化してしまった、ということですね。
このプログラムはBME680で測定した結果をシリアルモニタに表示するだけですが、元はOLEDにも表示し、さらにWifiでAmbientにデータを送ってグラフ表示するというものでした。ネットで公開されたものをいくつか寄せ集めて作成したのですが、その過程でI2CだからWire.begin();を入れないと、と思って深く考えずに入れてしまったようです。BME680を認識しないので調査のためにシリアルモニタ表示のみとしました。
最終的に動作したコードは以下の通りです。
/*
* BME680で10秒毎に温度・湿度・気圧・ガスを測定し、シリアルモニタに表示する
* ボード:ESP32 DEVKIT V1
* 2022-07-18
*
* (注1)Adafruit_BME680.h は、I2Cアドレス=0x77
* (注2)ストロベリーリナックス社のBME680モジュールはSDA,SCLピンのプルアップが必要。
* (注3)Wire.begin(); は不要。あるとBME680を認識しない。
*/
#include <Wire.h>
#include "Adafruit_BME680.h"
Adafruit_BME680 bme680; // I2C
#define PERIOD 10
//-----------------------------------------------------
void setup()
{
Serial.begin(115200); // for ESP32
delay(10);
Serial.println("Start");
if (!bme680.begin()) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1) {
delay(0);
}
}
// Set up oversampling and filter initialization
bme680.setTemperatureOversampling(BME680_OS_8X);
bme680.setHumidityOversampling(BME680_OS_2X);
bme680.setPressureOversampling(BME680_OS_4X);
bme680.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680.setGasHeater(320, 150); // 320*C for 150 ms
}
//-----------------------------------------------------
void loop()
{
int t = millis();
float temp, humid, pressure, gas;
// BME680で温度、湿度、気圧を測定する
if (! bme680.performReading()) {
Serial.println("Failed to perform reading :(");
return;
}
temp = (float)bme680.temperature;
humid = (float)bme680.humidity;
pressure = (float)bme680.pressure / 100.0;
gas = (float)bme680.gas_resistance / 1000.0;
Serial.print("temp: "); Serial.print(temp);
Serial.print(", humid: "); Serial.print(humid);
Serial.print(", pressure: "); Serial.print(pressure);
Serial.print(", gas: "); Serial.println(gas);
t = millis() - t;
t = (t < PERIOD * 1000) ? (PERIOD * 1000 - t) : 1;
delay(t);
}
この記事の続編です。