「土日で完成! 趣味のラズパイ」
土日で完成! 趣味のラズパイ BME680の測定データを小型ディスプレイに表示させる
前回までで、「BME680」を利用して気温と湿度、気圧、空気の汚染度を測ってきました。今回はこの測定値をCSVファイルに記録したり、小型ディスプレイに表示させたりしてみましょう。
土日で完成! 趣味のラズパイ
第16回 ラズパイとChatGPTで遊ぼう——圧電ブザーで音を鳴らす
第15回 ラズパイとChatGPTで遊ぼう——気温/湿度/気圧を測定する
第14回 ChatGPTで遊ぼう——ラズパイで作ったデジタル時計に天気情報も表示させる
第13回 ラズパイとChatGPTで遊ぼう——液晶時計を作る
第12回 ラズパイとChatGPTで遊ぶ——その1:Lチカさせてみる
第11回 ラズパイをブリッジモードで使って、Wi-Fi接続する
第10回 ラズパイをWi-Fiルーターにする
特別編 【実機レビュー】よりパワフルになったRaspberry Pi 5を検証!
第9回 VolumioでラズパイからSpotifyやradikoを楽しむ
第8回 ラズパイで音楽プレーヤー「Volumio」を楽しむ
第7回 GPSを導入して正確な時刻を刻む
第6回 ラズパイをNTPサーバーにする
第5回 ラズパイを使ってデジタル時計を作る
第4回 Raspberry Pi Camera Module 3を使って見守りカメラを作る
第3回 BME680の測定データを小型ディスプレイに表示させる
第2回 空気質センサー「BME680」で空気汚染度を測る
第1回 ラズパイ工作の基本! 気温と湿度、気圧を計測する
一覧ページへ
まずはデータをCSVファイルに出力できるようにしましょう。このあたりは「今さらきけない『ラズパイってなんですか?』の『キャラクターディスプレイモジュールって何ですか』」で紹介した手順とほぼ同じです。「気温と湿度、気圧を計測する」で紹介したプログラム「sokutei.py」を下地に、プログラムを実行したら1度だけ値を返すようにプログラムを改造します。
改造のポイントは、まずwhile文の無限ループをやめて気温と湿度、気圧の変数をそれぞれtemp、hum、pressとして定義し、時刻と共に表示させたところにあります。
#!usr/bin/env python # -*- coding: utf-8 -*- import bme680 import datetime command = 0x00 d = datetime.datetime.now() # datetime変数で日付と時刻を取得 sensor = bme680.BME680() sensor.set_temperature_oversample(bme680.OS_8X) sensor.set_humidity_oversample(bme680.OS_2X) sensor.set_pressure_oversample(bme680.OS_4X) temp = sensor.data.temperature # 気温の変数 hum = sensor.data.humidity # 湿度の変数 press = sensor.data.pressure # 気圧の変数 def main(): # データ取得 output = '{0:.2f},{1:.2f},{2:.2f}'.format(temp, hum, press) # 変数をoutputに代入 # 温度、湿度、気圧の表示 print(d.strftime("%Y/%m/%d,%H:%M,"),output) return if __name__ == "__main__": main()
これを実行すると以下のように表示されます。
----------- $ python pth_measure.py 2023/03/15,11:07, 25.46,51.81,1026.95 -----------
左から日付、時間、気温、湿度、気圧の順です。例えばこれを定期的にCSVに出力し、グラフなどを作ってもよいですね。この場合は「cron」という仕組みを使います。cronは定期的にプログラムを動かすように設定できるコマンドです。今回は以下のように設定します。
まず「crontab -e」と入力して、cronをエディターモードで起動します。初期設定の場合、利用するエディターが聞かれますが、「1」のnanoを選んでください。
cronが開いたら「#」が続いている行の最後に以下の内容を記述します。
*/10 * * * * python /home/pi/pth_measure.py >> /home/pi/data.csv
記述したら「Ctrl+x」でファイルを閉じ、記述するか聞かれるので「y」と入力します。
この記述の中で「*/10 * * * *」は「10分おきにこのコマンドを実行します」という意味です。それぞれの「*」は分(0~29)、時(0~23)、日(1~31)、月(1~12)の順番で最後が曜日です。曜日は0、1、2、3、4、5、6にそれぞれ日、月、火、水、木、金、土が割り当てられています。
このほかに「ステップ値(/)」と「値リストセパレーター(,)」「値の範囲(-)」を設定できます。例えば以下の通りです。
0 0,6 * * * 0時0分と6時0分
0 9-17 * * * 9時から17時までの毎0分
0 */6 * * * 0時から6時間おきの毎0分
0 9-17/2 * * * 9時から17時の間に2時間おきの毎0分
なおファイル名は“絶対パス”で書く必要があります。絶対パスとはディレクトリが作成されているパスを省略せずにフルに記述するという意味です。ユーザーフォルダは「pi」にありますので、「/home/pi」から始まるように書いていきます。ファイル名をつないでいる「>>」は、これは記述したファイルの最後に内容を追記していくという意味になります。
測定データを小型ディスプレイに表示させる
続いて小型ディスプレイへの表示に取りかかります。先ほど挙げた「キャラクターディスプレイモジュールって何ですか」でたどった道とほぼ同じです。秋月電子通商で販売されている「有機ELキャラクタディスプレイモジュール 16×2行 黄色」を使って表示させます。
なお今回は、測定したデータを文字列として変数の中に保管し、それを利用して小型ディスプレイに表示させるので、「round」文で得たデータを小数点1桁で四捨五入し、それを「str」文で文字列に変換し、表示させます。これを踏まえたプログラムが以下になります。測定データを10秒表示して消えるようになっています。
#!usr/bin/env python # -*- coding: utf-8 -*- import bme680 import time import so1602 # ディスプレイ初期化 so1602.setaddr(0x3c) so1602.command(0x0c) so1602.command(0x01) command = 0x00 sensor = bme680.BME680() sensor.set_temperature_oversample(bme680.OS_8X) sensor.set_humidity_oversample(bme680.OS_2X) sensor.set_pressure_oversample(bme680.OS_4X) temp = str(round(sensor.data.temperature,1)) # 四捨五入して文字列に hum = str(round(sensor.data.humidity,1)) # 四捨五入して文字列に press = str(round(sensor.data.pressure,1)) # 四捨五入して文字列に def main(): #ディスプレイに表示 so1602.command(0x80) so1602.write("T=" + temp + "°C") so1602.command(0x89) so1602.write("H=" + hum + "%") so1602.command(0xa0) so1602.write("P=" + press + "hPa") time.sleep(10) # ディスプレイ初期化 so1602.command(0x0c) so1602.command(0x01) return if __name__ == "__main__": main()
このプログラムを「pth_disp.py」などのように名付けて保存したら、以下のようにプログラムを実行します。
$ python pth_disp.py
すると下の写真のように小型ディスプレイに表示されます。
空気の汚染度を小型ディスプレイに表示させる
では次に、BME680による、空気の汚染度をディスプレイに表示する仕組みを作っていきましょう。BME680で得られるのはガス抵抗値です。このためしばらく動作させて平衡になったところの値を求め、そこからどのように変化するのかによって空気の汚染度を測ります。そのため、先ほど述べたような1発測定では正確な値を得ることができません。代わりに5秒置きに表示が切り替わるように設定します。一定時間ごとに測定するためには、前回のようなwhile文でプログラムを構成する必要があります。
このあたりをプログラムにまとめると以下のようになります。
#!usr/bin/env python # -*- coding: utf-8 -*- import bme680 import so1602 import time # ディスプレイ初期化 so1602.setaddr(0x3c) so1602.command(0x0c) so1602.command(0x01) command = 0x00 sensor = bme680.BME680() sensor.set_filter(bme680.FILTER_SIZE_3) sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) sensor.set_gas_heater_temperature(320) sensor.set_gas_heater_duration(150) sensor.select_gas_heater_profile(0) try: while True: if sensor.get_sensor_data(): if sensor.data.heat_stable: gas_resist = str(round(sensor.data.gas_resistance,2)) # ディスプレイ初期化 so1602.command(0x0c) so1602.command(0x01) # ディスプレイ表示 so1602.command(0x80) so1602.write("Air Registance") so1602.command(0xa0) so1602.write(gas_resist + "Ω") time.sleep(1) else: # ディスプレイ初期化 so1602.command(0x0c) so1602.command(0x01) # ディスプレイ表示 so1602.command(0x80) so1602.write("Measurement") so1602.command(0xa0) so1602.write("preparation") time.sleep(1) except:KeyboardInterrupt # ディスプレイを初期化して終了 so1602.command(0x0c) so1602.command(0x01)
しつこいくらいに小型ディスプレイの初期化をしていますが、これをやらないと表示にゴミ(不要な表示)が残ってしまうためです。
このプログラムをair_register.pyなどのように名前を付けて保存します。以下のようにすると実行できます。
$ python air_register.py
1秒置きにガス抵抗値が小型ディスプレイに表示されます。Ctrl+Cで終了できます。
難しそうに感じますが、やってみると意外と簡単にできます。プログラムがうまくいっており、コンソールにも表示されるのにOLEDに表示されない場合は、接触不良を疑ってください。ブレッドボードで組んでいきますので、残念ながらこういう事象はままあります。ただそれを含めても楽しい工作です。ラズパイ以外のパーツはそれほど値段がかかりませんので、チャレンジして頂けるとうれしいです。