温度・湿度・気圧・ガスの4つを測定できるセンサ ”BME680” の測定値をOLEDに表示する
Arduino NANO で動作したスケッチが、ラズパイPico で動かず! ⇒ 対策して動きました。
概要
Raspberry Pi Pico の開発環境・言語は
・Python(MicroPython, CircuitPython)
・C/C++
・Arduino IDE(Mbed OS RP2040版[公式版]、earlephilhower版)
の3通りあります。
ここでは Arduino IDE の Mbed OS RP2040版を使って
・BME680(温度・湿度・気圧・ガスのセンサ)の測定値を、
・0.96インチ, 128x64ドットのOLED(有機ELディスプレイ)[制御チップSSD1306]に表示
するシステムを紹介します。何れもAdafruitのライブラリを使用します。
これと似たような内容はネットを探せば他にもありますが、この記事の目玉は Arduino NANO では問題なく動作したのに、同じスケッチがラズパイPico では何故か動作しない! Why?
しかし、簡単な修正(1行)で何とか動くようになった、という内容です。
Arduino NANO では問題なく動作しました。
シリアルモニタにも表示されます。
ところが、同じスケッチをラズパイPico で実行するとOLEDは真っ暗、シリアルモニタにはエラーが表示されました。
この現象はスケッチを1行修正するだけで解決しました。それは最後に書きます。
目次
【1】準備編:デバイスを個別に動かします
【1-1】I2CScanner で I2Cアドレスを調べる
【1-2】OLED に文字を表示する
【1-3】BME680 の測定値をシリアルモニタに表示する
【2】BME680の測定値をOLEDに表示する
【予告編】Arduino IDE で Raspberry Pi Pico の2系統のI2Cを制御する
【ご参考】RaspberryPi Pico に内蔵のRTCを Arduino IDE で使用する
【1】準備編:デバイスを個別に動かします
【1-1】I2CScanner で I2Cアドレスを調べる
I2Cのデバイスを動作させるには正確なI2Cアドレスを知る必要があります。アドレスがジャンパで切替え可能になっていて違うアドレスを指定したり(やってしまいました!後述)、説明書のアドレス表記方法を勘違い(読み違い)すると動きません。こういう失敗を防ぐには、I2Cアドレス・スキャナでアドレスを確認すると良いです。まずはOLEDのI2Cアドレスを調べましょう。
■■■■ SDAはGP6 (Picoの9ピン)、SCLはGP7 (Picoの10ピン)に接続します。■■■
ラズパイPicoのI2Cは、I2C0とI2C1の2系統あります。使用するピンのペアはそれぞれ6組、計12組あります。
I2C0: [GP0/GP1]、[GP4/GP5]、[GP8/GP9]、 [GP12/GP13]、[GP16/GP17]、[GP20/GP21]
I2C1: [GP2/GP3]、[GP6/GP7]、[GP10/GP11]、[GP14/GP15]、[GP18/GP19]、[GP26/GP27]
このピンアウト図は https://datasheets.raspberrypi.org/pico/Pico-R3-A4-Pinout.pdf
からダウンロードしたものを、ロゴの位置をずらすなど少し加工しました。
ピンアウト図ではデフォルトとしてGP4/GP5のI2C0_SDA/I2C0_SCAが濃い青色で表示されてますが、最初これに騙されてかなり悩みました。
I2C0系統のピンペアではなくI2C1系統のGP6/GP7であることも謎です。なんでこんな中途半端な位置のピンなの?普通、番号若い方の系統、且つ先頭にあるGP0/GP1をデフォルトにするのが常識だろうが(怒)。 しかし、これはピンアウト図が間違っているのではなく、Arduino IDEで開発する場合はPython等とは違う何らかの理由があってこのようにしたのかなと思います。な~んか納得いかないモヤモヤするけど、このまま進みます。
下図のようにOLEDを接続します。
■■■■ I2Cscanner のライブラリをインストール ■■■■
Arduino IDEの[スケッチ]⇒[ライブラリをインクルード]⇒[ライブラリを管理]⇒[ライブラリマネージャ]の検索窓で I2Cscanner と入力し、ライブラリが表示されたら「インストール」をクリックします。
[ファイル]⇒[スケッチ例]⇒[I2CScanner]でCheck/Excute/Scannerの3つあります。
Check はシリアルモニタに何も表示されませんでした。ソース見るとこれじゃあなぁ・・と納得。Excute はコンパイルエラーで、この2つは放置。
Scannerは、他の方が報告されているものに比べるとソースの行数が極端に少ない。が、ちゃんと動作しました。
[ツール]⇒[ボード]⇒[ボードマネージャ]⇒[Arduino Mbed OS RP2040 Bords]で[Raspberry Pi Pico]を選択します。
続いて、シリアルポートも設定します。
「書き込み」ボタンをクリックし、ダウンロードが終了したら画面右上の虫眼鏡アイコンのシリアルモニタをクリック。
OLEDのI2Cアドレスは 0x3C であることが確認できました。
【1-2】OLEDに文字を表示する
使用するのは 0.96インチ, 128x64ドット有機ELディスプレイ(OLED)[制御チップSSD1306]です。まずは、動きました!の証拠写真。
Adafruitのライブラリを使用します。I2CScannerのライブラリのインストールと同様に[ライブラリマネージャ]の検索窓で SSD1306 と入力してインストールします。Adafruit_GFX.hの方は自動的にインストールされたように思います。
スケッチは下記のようにしました。
・setup() の中の OLED.begin() で先ほど調べたI2Cアドレス 0x3C を指定します。
・「電源入ってる」「動作してる」ということを示すために内蔵LEDを点滅させています。
OLEDの使い方は、[ファイル]⇒[スケッチ例]⇒[Adafruit_SSD1306]に、スクロールなど色々な表示方法がサンプルとして入っているので参考になります。
# include <Wire.h> // I2C
# include <Adafruit_GFX.h> // グラフィックのコア・ライブラリ
# include <Adafruit_SSD1306.h> // OLED ライブラリ
Adafruit_SSD1306 OLED(128, 64, &Wire, -1);
// 4つ目の引数はRESETピン: -1はArduinoのRESETピンと共有
//-----------------------------------------------------------------
void setup() {
digitalWrite(LED_BUILTIN, HIGH); // 内蔵LEDを点灯
Wire.begin(); // I2Cを初期化
OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C); // 内部チャージ回路をON
OLED.clearDisplay();
OLED.display();
OLED.setTextSize(1); // フォントサイズを1~で指定。
OLED.setTextColor(WHITE); // フォント色
OLED.setCursor(0, 0); // テキスト表示する座標
OLED.print("Raspberry Pi Pico"); // テキスト
OLED.setCursor(0, 8);
OLED.print("OLED Display");
OLED.setCursor(10, 16);
OLED.print("128 x 64");
OLED.display(); // テキストを表示
delay(2000);
}
//-----------------------------------------------------
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // 内蔵LEDを点灯
delay(300);
digitalWrite(LED_BUILTIN, LOW); // 内蔵LEDを消灯
delay(700);
}
【1-3】BME680(温度・湿度・気圧・ガスセンサ)
ガスセンサは、有機溶剤、アルコール、一酸化炭素などの室内空気を汚染する揮発性有機化合物を検出しますが、二酸化炭素Co2は検出できません。検出すると抵抗値が低くなります。通常400KΩ以上のところ、アルコール消毒液(エタノール)を付けた指を近づけると瞬間的に数KΩに下がりました。おならも検出できたという報告をネットで見ました。電源投入後、値が安定するまでかなり時間がかかります。10分以上経過してから測定すべきです。
写真左側は秋月電子通商、右側はストロベリーリナックスのBME680モジュールです。
[注1]I2CとSPIの選択
BME680 は I2C と SPI の何れにも対応しています。ストロベリーリナックスはジャンパーで切替え可能ですが、秋月は I2C 固定です。I2Cのピンの並び順はどちらも同じです。
[注2]電源電圧とレベルコンバータ
秋月は3端子レギュレータ内蔵で電源入力:2.5V~6Vと幅広く、I2CレベルコンバータICも内蔵しているため使い易いです。ストロベリーリナックスはBME680のみ実装し、電源入力:1.71V~3.6Vです。5V電源のデバイスと接続する場合はレベルコンバータが必要となるため注意が必要です。
[注3]I2Cアドレス
秋月/ストロベリーの何れもI2Cアドレスを、0x76と0x77をジャンパで選択できますが、Adafruitのライブラリはデフォルトで0x77になっているようです。
【失敗談】
ストロベリーのモジュールで、ソースプログラムを確認せずに「フツー、若い方をデフォルトにするでしょ」と勝手な思い込みで0x76でジャンパ接続したら動作しませんでした。レベルコンバータを入れてなかったので壊してしまったかも!?と思い慌てて秋月のモジュールを買ったのですが、やっぱり動かない。悩んだ末0x77に変更したらすんなり動きました。ストロベリーのモジュールも動いた。トレラントのお陰で壊れてなかったのかな。やれやれ・・
■ ハードウェアの接続はOLEDの代わりにBME680モジュールを入れ替えるだけなので配線図と回路図は省略します。
[BME680のライブラリとスケッチ]
Adafruitのライブラリを使用します。I2CScannerのライブラリのインストールと同様に[ライブラリマネージャ]の検索窓で BME680 と入力するといくつか表示されますが「Adafruit BME680 Libraly」をインストールします。
スケッチは、[ファイル]⇒[スケッチ例]⇒[Adafruit BME680 Library]⇒[bme680test]です。
OLEDのスケッチと同様、念のため「電源が入ってる」ことを示すために setup() で内蔵LEDを点灯し、「動作してる」ことを示すためにloop()で点滅させるようコードを追加すると不具合発生時のデバッグに役立ちます。
Arduino IDE の右上にある虫眼鏡のシリアルモニタのボタンをクリックすると以下のように表示されました。
【2】BME680の測定値をOLEDに表示する
下図のように接続します。
スケッチは、[ファイル]⇒[スケッチ例]⇒[Adafruit BME680 Library]⇒[bme680oled]です。
「電源入ってる」「動作してる」ということを示すために内蔵LEDを点灯させるには、以下のように追加すると良いでしょう。
void setup() {
digitalWrite(LED_BUILTIN, HIGH); // 内蔵LEDを点灯
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // 内蔵LEDを点灯
delay(100);
digitalWrite(LED_BUILTIN, LOW); // 内蔵LEDを消灯
このスケッチをラズパイPicoで実行すると、この記事の最初の方に書いたようにOLEDは真っ暗、シリアルモニタにはエラーが表示されました。配線をよ~く確認しましたが間違いは無く、何が悪いのかわかりません。
Arduino のボードではどうなんだろう?と思って同じスケッチをArduino NANO にダウンロードして実行すると正常に動作しました。
Arduino NANO + OLED + BME680 配線図
ここまでの内容を整理すると、
(1) ラズパイPicoでOLEDもBME680も、単体では正常に動作した。
(2)[bme680oled]はArduino NANOでは正常に動作する。
(3)[bme680oled]はラズパイPicoではエラーになる。
(1) (2) と(3) の違いは何か?
・OLEDとBME680を同じI2Cラインで配線した。
・動作周波数がArduino NANO:16MHzに対して、ラズパイPicoは125MHzと、かなり速い。
I2Cの動作がタイミング的に何か問題があるのかも?と思い、スケッチのあちこちに delay(100); を挿入したところ、シリアルモニタにFailed to perform reading のエラーメッセージの連続だったのが、BME680の4つの測定値を1度だけ表示してからエラーメッセージの連続に変化しました(OLEDは真っ黒で変化なし)。しかし、それ以上の進展は無く問題は解決しません。
(1)でOLEDもBME680も、それぞれ単体では動作確認しているので、それらのスケッチと比較したところ、OLEDのオブジェクト生成の記述で違いがありました。
(先頭のコメントは省略)
# include <Wire.h>
# include <SPI.h>
# include <Adafruit_Sensor.h>
# include "Adafruit_BME680.h"
# include <Adafruit_GFX.h>
# include <Adafruit_SSD1306.h>
# define BME_SCK 13
# define BME_MISO 12
# define BME_MOSI 11
# define BME_CS 10
# define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME680 bme; // I2C
//Adafruit_BME680 bme(BME_CS); // hardware SPI
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
Adafruit_SSD1306 display = Adafruit_SSD1306();
void setup() {
Serial.begin(9600);
Serial.println(F("BME680 test"));
(以下、省略)
Adafruit_SSD1306 display = Adafruit_SSD1306();
Adafruit_SSD1306 OLED(128, 64, &Wire, -1);
そこで試しに以下のように変更してみました。
//Adafruit_SSD1306 display = Adafruit_SSD1306();
Adafruit_SSD1306 display(128, 64, &Wire, -1);
するとOLEDに正常に表示されるようになりました。
シリアルモニタにも正常に計測値が表示されます。
(当初、I2Cラインにはレベルコンバータを使用し、プルアップ抵抗を接続してましたが、どちらも無くても動作してます。)
しかし、疑問が・・・
・Arduino NANOは、元のオリジナルのままでも正常に動作したのにラズパイPicoで動作しないのは何故?
・上記のようにスケッチを変更すると正常に動作するのは何故?
ご意見やヒントなどお寄せ頂ければ幸いです。
【関連記事】Arduino IDE で Raspberry Pi Pico の2系統のI2Cを制御する
任意のピン番号を指定して、2系統のI2Cを同時に使用する方法。
https://qiita.com/totuto/items/81944426dc81ab4b7e57