温度、湿度、気圧を測定する 【Raspberry Pi & BME280】

Raspberry Pi

Raspberry Pi を使って、温度、湿度、気圧を測定する方法についてまとめておきます。
以下の環境で動作確認をしています。
環境:
・ Raspberry Pi 5 (bookworm)
・ BME280 温度・湿度・気圧センサー(下記の外部リンク参照)
・ ブレッドボード他(下記参照)
・ Windows パソコン/LANネットワーク(Raspberry Pi の設定のため)

背景 ~ Raspberry Pi 5 で温湿度&大気圧測定!

しばらく前に BME280 という温度、湿度、気圧を測定できるデバイスを購入し、Raspberry Pi で室内の温湿度等の自動測定をしていました。

最近の Raspberry Pi 5 でも同様の温湿度測定をしようとしたところ、ライブラリが動かなくなっていました。オープンソースのフレームワークはとても便利なのですが、すぐに古くなって使えなくなります。可能であれば、レガシー化する可能性のある要素は最小化したいところです。

ライブラリを極力入れず、いくらか試行錯誤したところ、Raspberry Pi 5 (bookworm) で BME280 のデバイスを動かすことができましたので、手順やスクリプトをまとめ、公開しておくことにします。

なお、BME280 を搭載したデバイスは複数のメーカーから市販されているようですので、製品・外観等が異なる場合は、仕様書を確認し、適宜、記載を読み替えてください。設定する項目・内容としては、どの製品も同様だと思います。

手順

ステップ1: Raspberry Pi 側で I2C 通信を有効にする

① Raspberry Pi 5 を起動し、以下の要領で I2C (Inter-Integrated Circuit) 通信が有効となるよう設定します。I2C は、シリアル方式でデータを送受信させることで配線をシンプルにできる通信方式です。
以下を実行します。

$ sudo raspi-config

GUI が起動するので、矢印キーと [enter] キーを使って、”Interface Options” を選択します。
“I2C” を選択して [enter] キーを押します。
I2C を enable にするか否かを聞いてくるので、”<はい>” を選択します。
Enable にしたという表示が出たら、”<了解>” を選択します。
[tab] キーで “<Finish>” を選択し、[enter] キーを押します。
→ これで、I2C 通信の有効化は完了です。

※ I2C 通信を有効にすると、Raspberry Pi の GPIO2、GPIO3 が、それぞれ、I2C 通信の SDA、SCL 端子として機能します。これらの端子を BME280 デバイスの対応する端子と接続します。

ステップ2: BME280 デバイスの設定・接続をする

② 仕様書、下記のブレッドボード図等を参照し、Raspberry Pi に BME280 デバイスを接続します。
※ 製品、外観が異なる場合は、仕様書を確認してください。
※ ブレッドボード図は、I2C 通信モードに設定し、かつ、I2C 通信のアドレスを 0x76 とする接続例としています。アドレスの数値自体は、メーカー指定の数値となっており、ジャンパの接続の有無で数値を選択できるようになっています。
※ 通信モードとアドレスの設定に2本のジャンパを使っています。加えて、Raspberry Pi と I2C デバイス間に4本のジャンパを使いますので、合計6つの接続をしています。

ステップ3: Raspberry Pi に BME280 を設定する

続いて、Raspberry Pi 側で、BME280 のデバイスとアドレスを設定します。
③ 以下を実行し、設定ファイル config.txt を編集します。

$ sudo vi /boot/firmware/config.txt

※ 以前の Raspberry Pi では、”sudo vi /boot/config.txt” としていました。設定ファイルの場所が変更になっています。
④ config.txt の末尾に以下を追記します。

$ dtoverlay=i2c-sensor,bme280,param=0x76

→ 記載したら、[esc] : wq [enter] と入力して、書き込んで終了します。
もし、書き込まずに編集を終える場合は、wq の代わりに q! とします。

⑤ 上記のすべての設定を反映させるため、Raspberry Pi を再起動します。

$ sudo reboot

⑥ 以下を実行し、設定が反映されていることを確認します。

$ i2cdetect -y 1

※ 上記のコマンドを実行すると、I2Cバス上で認識されているアドレスマップが表示されます。
※ I2Cデバイスが適切に接続されていると、アドレスマップ上で対応する箇所の “- -” が、”数値”、または、”UU” として表示されます。I2Cデバイスを複数接続している場合は、デバイスの個数分の表示がされます。これにより、I2Cデバイスが電気的に正しく接続できているかどうかを確認できます。
※ ”数値”が表示されている場合は、I2Cデバイスの通信まで機能しています。”数値” は②で設定したアドレスになっているはずです。
※ ”UU” が表示されている場合は、I2Cデバイスの通信が機能しており、かつ、Linux のカーネル側からのアクセスができている状態となっています。”UU” となっていれば、下記の Python のスクリプトなどから、デバイスのデータを読み出すなどが可能です。
※ もしも、アドレスマップ上の表示がすべて “- -” となっている場合は、Raspberry Pi 側で I2C デバイスの検出ができていません。配線、電気的な接続に間違いがないか確認してください。
とくに、GND、電源(3.3V)の接続が正しくできているか、また、”SCL” と “SDA” を逆につないでいないか、などを確認してください。
※ ”数値”の表示となったままで、”UU” が表示されていない場合は、Linux 側の設定(③、④)に間違いがないか、確認してください。
スペルは正しいか、I2Cデバイス(②)と Raspberry Pi(④)のアドレスの設定が対応しているかなどを確認してください。また、再起動を行ったか確認してください。
過去の経験では、何度か再起動を行ったところ、”数値” が “UU” の表示に切り替わったことがありました。

ステップ4: 温湿度・気圧測定用のスクリプトの設定・実行

⑦ 下記を参考に BME280 用のフォルダを作り、テキストファイル(THP1.py)を作成し、末尾のスクリプトをコピー&ペーストして保存します。
例: /home/pi/BME280/THP1.py
※ フォルダの場所やファイル名は任意ですので、必要により適宜、読み替えて作成してください。

⑧ 上記の Python のスクリプトを実行します。

$ python /home/pi/BME280/THP1.py

→ ターミナルに、日時、温湿度、気圧が表示されたら成功です!
実行後、⑦のフォルダに測定結果が保存されるようにしてあります。

うまく動いたら

うまく動いたら、スクリプトをご自由にアレンジしてみてください。

プログラムを実行すると、結果を画面に表示し、ログ用のファイル(tmp1.txt)に日時と測定結果を追記していくようにしてあります。画面表示やファイルの保存等が不要であれば、随時、コメントアウトするなど修正してください。

また、ご自宅などに温度計・湿度計があるようでしたら、結果が同一となるよう、キャリブレーションの機能を入れることも可能です。
この場合、簡単に行うのであれば、温度、湿度、大気圧の測定結果にそれぞれ定数を足して置き換え、基準にしたい温湿度計と結果が同一となるよう微調整すればよいです。

上記のスクリプトの実行コマンドを Linux の crontab で登録しておくことで、指定時刻に、温度、湿度、気圧の自動測定をすることが可能です。
一般に市販されている SD カードの書き換え回数は 1000 回程度からとなっています。
非常に多くの/長期間の/頻繁な測定をする場合は、書き込み回数に比例するように、SDカードが破損する可能性が高まっていきます。そこで、ログの保存先は外部のSDカードとするか、保存先をいったん RAMフォルダとして、データがある程度まとまった段階で日付をつけて外部の SDカードに一括保存する(書き込み回数、書き換え回数を減らす)など、SDカードの劣化対策をしておくことをお勧めします。書き込むファイル名に年月日を入れても、その日付が過ぎると同一領域への書き込みはなくなるので一定の対策にはなると思います。

自分が過去に行った応用例としては、まず、上記のログを一定時間おきに取り続けます。湿度が上がってくると、雨が降る前兆となることがわかっています。そこで、湿度が急激に上がると湿度の上昇を音声通知するプログラムを作ったことがあります。
(出かけることの多い時間帯に音声通知をすることで、傘などを忘れないようにする。経験的に、乾燥した状態で雨が降ってくることはほぼないです。自然換気があるので室内の湿度も外気に応じて上下します。ただし、室内で料理などをしても湿度が上昇します。)

接続図

ブレッドボード図


※ CSB 端子を GND に直結することで、I2C通信となるよう設定しています。
※ SD0 端子を抵抗を介して GND に接続することで、I2C通信のアドレスが 0x76 になるよう設定しています。

ピン配列

Raspberry Pi のターミナルで “pinout” を実行すると、上記のピン配列に関する情報が得られます。参考として、Raspberry Pi 5 での例を以下に挙げておきます。

3V3 (1) (2) 5V
GPIO2: SDA (3) (4) 5V
GPIO3: SCL (5) (6) GND
GPIO4 (7) (8) GPIO14
GND (9) (10) GPIO15
GPIO17 (11) (12) GPIO18
GPIO27 (13) (14) GND
GPIO22 (15) (16) GPIO23
3V3 (17) (18) GPIO24
GPIO10 (19) (20) GND
GPIO9 (21) (22) GPIO25
GPIO11 (23) (24) GPIO8
GND (25) (26) GPIO7
GPIO0 (27) (28) GPIO1
GPIO5 (29) (30) GND
GPIO6 (31) (32) GPIO12
GPIO13 (33) (34) GND
GPIO19 (35) (36) GPIO16
GPIO26 (37) (38) GPIO20
GND (39) (40) GPIO21

詳細

BME280 デバイスの設定について

・ 上記の BME280 デバイスの場合、CSB 端子を GND に直結することで、I2C 通信のモードになります。
同デバイスの場合、BME280 の基板内部にプルアップ抵抗が入っているようですが、CSB 端子を GND に接続しても、特に問題なく機能しています。
・ SD0 端子は、I2C 通信でのアドレス設定用のピンです。
抵抗等を介して GND につなぐことでアドレスが 0x76 になります。
また、GND ではなく VCC につなぐと、アドレスが 0x77 となります。
・ 上記の写真、ブレッドボード図では、SD0 端子は、10kΩの抵抗(茶黒赤金)を介して GND に接続しています。
抵抗をつけずに SD0 端子を GND に直結しても機能すると思います。
・ 上記の BME280 デバイス上での通信方式(I2C 通信)、アドレス(0x76)の設定と、Raspberry Pi 側での通信方式(I2C)、アドレス(0x76)の設定とが、それぞれ一致するようにします。

温度、湿度、気圧データの読み出し方

Raspberry Pi で以下のコマンドを実行すると、BME280 から測定値を読み出すことができます。

cat /sys/bus/i2c/devices/i-0076/iio:device0/in_temp_input
cat /sys/bus/i2c/devices/i-0076/iio:device0/in_humidityrelative_input
cat /sys/bus/i2c/devices/i-0076/iio:device0/in_pressure_input

※ 上から順に、温度、相対湿度、大気圧を読み出すコマンドです。
※ 上記のコマンドを機能させるには、BME280 デバイスと Raspberry Pi の両者で、I2C 通信の接続・設定を終えている必要があります。
※ 上記の例は、I2C 通信のアドレスを 0x76 に設定した場合のコマンドです。
I2C デバイスのアドレスを 0x77 に設定した場合は、”i-0076″ の部分を “i-0077” に差し替えてください。
※ 末尾のスクリプトは、Python でこれらを読み出すようにしたものです。

スクリプトの説明

・ スクリプトの前半でファイルの読み出し、書き込み等をする関数を定義しています。
・ read1_thp() 関数は、BMP280 の内容を読み込む関数です。
通常のファイルの読み込みと同等の要領でデータを読み出すことができ、浮動小数に型変換をしています。
・ yymmdd_hhmmss1() 関数は、プログラムを実行した時点での年月日と時刻を生成する関数です。
測定日時に対応し、日時つきのログとするために使用します。
・ path1 は、温度、湿度、気圧のデータを書き出すパスを定義しています。
・ path2 は、BMP280 の内容を読み出すアクセス先です。
アドレスは 0x76 としていますので、0x77 に設定した場合は、0076 の部分を 0077 に修正してください。
・ 以後、str1 = …, str2 = …, str3 = … としたところで、温度、相対湿度、気圧を呼び出しています。
・ BME280 デバイスの仕様として、温度、相対湿度の出力は、1000倍された ℃、% の単位で定義されています。また、気圧の出力は、kPa の単位で定義されています。
・ そこで、Python のスクリプトでは、温度、湿度について、読み出した値を 1/1000 倍して、℃、% の単位にしています。気圧については、kPa で読み出した値をよく使われる hPa にするために 10倍しています。(1 kPa = 1000 Pa, 1 hPa = 100 Pa)
・ プログラムを実行した日時、取得した温度、相対湿度、気圧から文字列を作っています。この文字列を、ターミナルに表示し、また、ログファイルに追加します。
・ ファイルへの書き込みはアペンドモードとしています。これにより、スクリプトを実行するたび、過去のログデータが蓄積していくようにしています。

まとめ

Raspberry Pi 5 (bookworm) で BME280 を使った温湿度、気圧測定の手順についてまとめました。

I2C 通信とアドレスの設定さえ終えてしまえば、コマンド操作で、温度、湿度、気圧の読み出しが可能です。
また、Python がいくらかでも理解できるのであれば、ライブラリなどは不要で、どのようにでもカスタマイズできるということがわかります。

他にも、Raspberry Pi の GPIO を使ってモーターを動かしたり、ネットから天気予報を取得したりしています。関心のある方は、下記の関連リンクなども参照してみてください。

関連リンク
・ 天気予報を取得してテキストで出力する 【Python】
・ LED を ON/OFF する 【Raspberry Pi & Python】
・ サーボモーターを動かす 【Raspberry Pi】
・ DCモーターを動かす 【Raspberry Pi】 
・ 
Raspberry Pi で PWM制御 【Python】
・ リアルタイムクロックを設定する 【Raspberry Pi & RTC】 
・ 乾燥時間の機械学習を行ってみる 【Python & Tensorflow】 
・ 指定したフォルダを RAM 上に設定する 【Raspberry Pi】 

外部リンク
・ The Linux Kernel documentation — The Linux Kernel documentation
・ BME280 データシート Bosch Sensortec 
・ HiLetgo BME280 温度センサー、湿度センサー、気圧センサー [PR]
  (Amazon 等のサイトでは、温度・湿度・気圧の3つが測定できるタイプと、温湿度の2つのみで気圧が測定できないタイプとがあり、価格も異なります。用途に応じて確認をしてください。)

HiLetgo BME280 温度 センサー 湿度 センサー 気圧 センサー 

サンプルスクリプト

THP1.py ~ BME280 で温度、湿度、気圧を測定するスクリプト

import os 
from datetime import datetime

def read1( file1 ): 
    str1 = ""
    with open( file1, 'r', encoding='utf-8' ) as f1: 
        str1 = f1.read() 
    return str1 

def write1( file1, str1 ): 
    with open( file1, 'a', encoding='utf-8' ) as f1: 
        f1.write( str1 ) 
    return 0 

def read1_thp(file1): 
    v1 = float(read1(file1).strip())
    return v1 
    
def yymmdd_hhmmss1():
    return datetime.now().strftime('%y/%m/%d %H:%M:%S')

path1 = os.path.dirname(__file__) + "/" 
path2 = "/sys/bus/i2c/devices/1-0076/iio:device0/" 

file1 = path2 + "in_temp_input"
str1 = f'{read1_thp(file1)/1000:.2f}'    # mdegC -> degC 

file1 = path2 + "in_humidityrelative_input"
str2 = f'{read1_thp(file1)/1000:.2f}'    # m% -> % 

file1 = path2 + "in_pressure_input"
str3 = f'{read1_thp(file1)*10:.2f}'      # kPa -> hPa 

str1 = yymmdd_hhmmss1() + ", " + str1 + ", " + str2 + ", " + str3 + "\n"   # yymmdd HH:MM:SS, xx.xx, xx.xx xxxx.xx 
print(str1.strip()) 

file1 = path1 + "thp1.txt" 
write1(file1, str1) 


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