Raspberry Pi 5 とBME280センサ(気圧、温度、湿度)とSCD4xセンサ(CO2濃度、温度、湿度) モジュールをI2C接続、その測定データをSPI接続した2.8インチTFT液晶 (240×320、ILI9341)モジュールに日本語フォントで表示した際の作業メモです。
SCD4xファミリーにはSCD40、SCD41などがあるようです。今回は手持ちのSCD41を使って動作検証しています。
参考:Sensirion SCD4xミニチュアCO2センサー
使ったパーツと結線図、OSセットアップ
2.8インチTFT液晶 (240×320)モジュールを4線式のSPIで、SCD41とBME280センサモジュールをSoC内蔵の I2C(ハードウエア I2C)で接続する結線図です。
2.8インチTFT液晶モジュール(ILI9341)をRaspberry Pi 5とSPI接続
2.8インチ液晶モジュールは、ドライバーICとしてILI9341を使った240×320の液晶モジュール。
今回、XPT2046を使ったタッチパネル、SDカードスロット部分は使っていません。SDカード側コネクタ(J4)にはピンヘッダが未実装なので手持ちのピンヘッダをはんだ付けしています。
参考:
・ILI9341搭載2.8インチSPI制御タッチパネル付TFT液晶(akizukidenshi.com)
・2.8inch SPI Module ILI9341 SKU-MSP2807 – LCD wiki(回路図)
SPI はマスタ(ここではPi 5)とスレーブ(ここでは2.8インチTFT液晶)の間をつなぐ4線(CS、CLK、MOSI、MISO)を使った双方向のクロック同期方式のシリアルインターフェース。これらのSPIピンに加えてDCピン、RESETピンを使います。
Raspberry Pi 4BやRaspberry Pi Zero 2 W(いずれもbullseye)でCSピンとして使っていたGPIO8(CE0)では動作させることができなかったので、GPIO23に変更しています。
# | .8インチ TFT液晶 J2 シルク印刷 | 2GPIO ピン番号 | GPIO 機能 | 備考 |
1 | VCC | 1 | 3V3 power | |
2 | GND | 30 | Ground | |
3 | CS | 16 | GPIO23 | CSピンを GPIO8(CE0)から GPIO23に変更 |
4 | RESET | 18 | GPIO24 | |
5 | DC | 22 | GPIO25 | |
6 | SDI (MOSI) | 19 | GPIO10 (MOSI) | |
7 | SCK | 23 | GPIO11 (SCLK) | |
8 | LED | 1 | 3V3 power | |
9 | SDO (MISO) | 21 | GPIO9 (MISO) |
BME280センサモジュールをRaspberry Pi 5とI2C接続
今回購入したBME280モジュール(6ピン基板)のインターフェースはI2CとSPIの両方が使えます。今回はI2C接続(VCC、GND、SCL、SDAの4ピンのみ利用)でつなぎます。このモジュールにはI2Cバスの信号レベル変換回路や電源レギュレータ回路は入っていないので、電源電圧、信号レベルともに3.3V。Raspberry PiのGPIO信号ピンのロジックレベルは3.3Vなので利用できます。信号線は10kΩでプルアップされています。
以前購入したBME280モジュール(4ピン基板)のインターフェースはI2Cのみですが、裏面にI2Cバスの5Vと3.3Vのレベル変換回路、VIN(5V)から3.3Vへのレギュレータ回路(LDO:Low Drop Out)を搭載しています。そのため、5V系のAndroidや3.3V系のESP32やRaspbery Piの両方で使うには便利です。信号線は10kΩでプルアップされています。
Sensirion(センシリオン)社のSCD41センサ
SCD4xファミリーにはSCD40、SCD41などがあるようです。今回は手持ちのSCD41を使って動作検証しています。SCD4xは光音響NDIRセンシング原理を使用することで光学キャビティの寸法を小型化(10.1mm x 10.1mm x 6.5mm)したCO2 およびRH/T(相対湿度 (RH) と温度 (T) )センサです。
参考:Sensirion SCD4xミニチュアCO2センサー(特徴、仕様)
ピンヘッダが付属しないときは、ピンヘッダ 1列タイプ 40ピンから4ピンをニッパ(1×4)でカットしてSCD4xセンサモジュールにはんだ付けします。
結線図
2.8インチTFT液晶 (240×320)モジュールを4線式のSPIで、BME280センサモジュール、SCD41センサモジュールをSoC内蔵の I2C(ハードウエア I2C)で接続する結線図です。
5.1V/5AのACアダプター
Raspberry Pi 5では、電源として5.1V/5AのACアダプターが求められます。対応していないACアダプターでUSBストレージから起動しようとすると警告メッセージが表示されます。
基板上のSTATUS LED(ACT LED)の横にある電源ボタンを押すとUSBポートの電流制限が解除され、USBストレージからRaspberry Pi OSを起動できます。
Raspberry Pi OSのセットアップ、I2CとSPIを有効化
Raspberry Pi 5にはRaspberry Pi Imager v1.8.5を使って、USBメモリにRaspberry Pi OS(64-bit)を書き込んでUSBブートしています。LXTerminalでOSバージョンなどを確認した結果です。
I2CとSPIの設定 はデフォルトでは無効です。
苺メニューの「設定」–>「Raspberry Piの設定」を開いて–>「インターフェース」でI2CとSPIを有効にチェック。
再起動するとSoC内蔵の I2C(ハードウエア I2C )とSPI通信が使えるようになります。
SCD41センサとBME280センサのデフォルトのI2Cスレーブアドレスです。
# | モジュール | センサアドレス | I2C
1 | SCD41 | 0x62 |
2 | BME280 | 0x76 |
Python3:pipでコマンドオプションを付けてライブラリをインストール
新しいラズパイ(Raspberry Pi 5、bookworm)になって、python環境が変わりました。pipからセンサや液晶モジュールなどライブラリをlocal環境にインストールしようとするとエラーとなる場合があります。
Python仮想環境(venv)を使えばよいのですが、まっさらな状態の仮想環境に必要なライブラリやソースコードを全てそろえるのは骨が折れます。
回避策の一つであるpipでインストールする際にコマンドオプションbreak-system-packages
を付けることで、ライブラリをlocal環境にインストールしています。
The installer should have a way for the user to override these rules, such as a command-line flag
PEP 668 – Marking Python base environments as “externally managed” | peps.pyth--break-system-packages
. This option should not be enabled by default and should carry some connotation that its use is risky.
Pythonとpipのバージョンを、python -V、pip -Vコマンドで確認すると、
Pythonは Python 3.11.2 、
pipは、pip 23.0.1 from /usr/lib/python3/dist-packages/pip (python 3.11)。
組み込まれたライブラリのリストとバージョンは pip list コマンドで確認できます。
smbus2
smbus2パッケージはデフォルトでRaspberry Pi OSに組み込み済みでした。Version 0.4.2 。
2.8インチTFT液晶ライブラリ
2.8インチTFT液晶 (240×320)モジュールの液晶コントローラLSIはILI9341。Adafruitのライブラリ「Adafruit_CircuitPython_RGB_Display」をコマンドオプションを付けてインストール。
sudo pip3 install --break-system-packages adafruit-circuitpython-rgb-display
adafruit/Adafruit_CircuitPython_RGB_Display
https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display
rpi-lgpioライブラリ
Raspberry Pi 5ではRPi.GPIOやpigpioライブラリが使えなくなり、今後は gpiozeroが推奨とのこと、今回はrpi-lgpioライブラリをコマンドオプションを付けてインストール。
sudo pip3 install --break-system-packages rpi-lgpio
rpi-lgpio 0.6
BME280ライブラリ
BME280のライブラリには「RPi.bme280 0.2.4」をコマンドオプションを付けてインストール。Python3コード上でI2Cアドレスとポート番号を指定できるで使い勝手が良いです。
sudo pip3 install --break-system-packages RPi.bme280
https://pypi.org/project/RPi.bme280/
SCD4x ライブラリ
SCD4xのライブラリには「scd4x 0.0.2」をコマンドオプションを付けてインストール。
sudo pip3 install --break-system-packages scd4x
https://pypi.org/project/scd4x/
IPAex フォント
日本語を表示するために、独立行政法人情報処理推進機構 (IPA) が提供している日本語フォント「IPAex フォント」をインストール。ipaexfont-gothic を使っています。
sudo apt install fonts-ipaexfont
Python3:BME280測定データを2.8インチTFT液晶に表示
Raspberry Pi OSにプリインストール されているThonny Python IDEを使って動作確認しました。
SCD41のサンプルプログラム – basic.pyで動作確認
SCD4X C02 Sensorプロジェクトサイトのサンプルプログラムbasic.pyで基本動作を確認しました。
https://github.com/pimoroni/scd4x-python/blob/main/examples/basic.py
basic.py
※ここをクリックするとコード表示を開閉できます。
#!/usr/bin/env python3
from datetime import datetime, timezone
from scd4x import SCD4X
device = SCD4X(quiet=False)
device.start_periodic_measurement()
try:
while True:
co2, temperature, relative_humidity, timestamp = device.measure()
date = datetime.fromtimestamp(timestamp, timezone.utc)
print(f"""
Time: {date.strftime("%Y/%m/%d %H:%M:%S:%f %Z %z")}
CO2: {co2:.2f}PPM
Temperature: {temperature:.4f}c
Humidity: {relative_humidity:.2f}%RH""")
except KeyboardInterrupt:
pass
Thonny Python IDEで basic.py を実行すると、shell部分にSCD41センサの[CO2濃度]、[温度]、[湿度]を表示します。
bme280_scd4x_sample_TFT_csv_pi5.pyのテスト
ドキュメントフォルダに保存した下記 bme280_scd4x_sample_TFT_csv_pi5.py をLXTerminalから起動すると、2.8インチTFT液晶モジュールにSCD41センサの[CO2濃度]、BME280センサの[気圧]、[温度]、[湿度]と日時(曜日)を 5秒間隔で表示します。
python3 /home/pi/ドキュメント/bme280_scd4x_sample_TFT_csv_pi5.py
このpython3プログラム実行時のタスクバーのCPU稼働率モニタの値は1%以下でした。
bme280_scd4x_sample_TFT_csv_pi5.py
※ここをクリックするとコード表示を開閉できます。
#bme280_scd4x_sample_TFT_csv_pi5.py
#coding: utf-8
#---TFT-ili9341 init-----rgb_display_pillow_stats.py---
import time
import subprocess
import digitalio
import board
from PIL import Image, ImageDraw, ImageFont
from adafruit_rgb_display import ili9341
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.D23)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = digitalio.DigitalInOut(board.D24)
# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000
# Setup SPI bus using hardware SPI:
spi = board.SPI()
# pylint: disable=line-too-long
# Create the display:
disp = ili9341.ILI9341(
spi,
rotation=90, # 2.2", 2.4", 2.8", 3.2" ILI9341
cs=cs_pin,
dc=dc_pin,
rst=reset_pin,
baudrate=BAUDRATE,
)
# pylint: enable=line-too-long
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
if disp.rotation % 180 == 90:
height = disp.width # we swap height/width to rotate it to landscape!
width = disp.height
else:
width = disp.width # we swap height/width to rotate it to landscape!
height = disp.height
image = Image.new("RGB", (width, height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
disp.image(image)
# First define some constants to allow easy positioning of text.
padding = -2
x = 0
# Load a ipaexfont-gothic font.
font1 = ImageFont.truetype("/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf", 50)
font2 = ImageFont.truetype("/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf", 20)
font3 = ImageFont.truetype("/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf", 15)
#---BME280 init----------bme280_sample.py----------
import smbus2
import bme280
port = 1
address = 0x76
bus = smbus2.SMBus(port)
calibration_params = bme280.load_calibration_params(bus, address)
# the sample method will take a single reading and return a
# compensated_reading object
data = bme280.sample(bus, address, calibration_params)
#---SCD4x init-----------------basic.py---------------
from scd4x import SCD4X
device = SCD4X(quiet=False)
device.start_periodic_measurement()
while True:
#---csv out ------------------------------------------
dtd = time.strftime('%Y年%m月%d日', time.localtime())
dtt = time.strftime('%H時%M分%S秒', time.localtime())
dta = time.strftime('%a', time.localtime())
if dta == "Mon":
jdta = dta.replace("Mon", "(月)")
elif dta == "Tue":
jdta = dta.replace("Tue", "(火)")
elif dta == "Wed":
jdta = dta.replace("Wed", "(水)")
elif dta == "Thu":
jdta = dta.replace("Thu", "(木)")
elif dta == "Fri":
jdta = dta.replace("Fri", "(金)")
elif dta == "Sat":
jdta = dta.replace("Sat", "(土)")
elif dta == "Sun":
jdta = dta.replace("Sun", "(日)")
else:
jdta="ー"
data = bme280.sample(bus, address, calibration_params)
co2, temperature, relative_humidity, timestamp = device.measure()
pbme280 = f"{data.pressure:.1f},{data.temperature:.1f},{data.humidity:.1f}"
pscd4x = f"{co2:.1f},{temperature:.1f},{relative_humidity:.1f}"
# P_csv = dtd + jdta + " " + dtt + "," + pscd4x + "," + pbme280
# print(P_csv)
#---TFT-ili9341- Draw ---------------------------------
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
draw.text((50, 192), dtd, font=font2, fill="#00ff00")
draw.text((50, 212), dtt, font=font2, fill="#00ff00")
draw.text((220, 192), jdta, font=font2, fill="#00ff00")
draw.text((20, 3), "温度:", font=font2, fill="#ffffff")
draw.text((24, 25), "( ℃ )", font=font3, fill="#ffffff")
temp =format(float(data.temperature), '.1f')
draw.text((90, 0), temp, font=font1, fill="#ffffff")
draw.text((14, 50), "CO :", font=font2, fill="#FFFF00")
draw.text((47, 58), "2", font=font3, fill="#FFFF00")
draw.text((14, 70), "( ppm )", font=font3, fill="#FFFF00")
co2 =format(float(co2), '.1f')
draw.text((90, 47), co2, font=font1, fill="#FFFF00")
draw.text((20, 97), "気圧:", font=font2, fill="#ffffff")
draw.text((18, 117), "( hpa )", font=font3, fill="#ffffff")
pres =format(float(data.pressure), '.1f')
draw.text((90, 93), pres, font=font1, fill="#ffffff")
draw.text((20, 143), "湿度:", font=font2, fill="#ffffff")
draw.text((24, 165), "( % )", font=font3, fill="#ffffff")
humi =format(float(data.humidity), '.1f')
draw.text((90, 140), humi, font=font1, fill="#ffffff")
disp.image(image)
Thonny Python IDEのshell部分へのCSV表示が必要な場合は、115、116行目先頭の#を消します。
データ形式は、[日時]、SCD41センサの[CO2濃度]、[温度]、[湿度]、BME280センサの[気圧]、[温度]、[湿度]です。
/etc/rc.localを編集、OS起動時にpython3プログラムを自動起動
自動起動方法は色々ありますが、「/etc/rc.local」にpython3プログラムの実行スクリプトを追加する方法が簡便です。Raspberry Pi 5に液晶モニタ、キーボード、マウスなどをつなぐことなく、ACアダプターをつないでOS起動・python3プログラムを自動実行できます。
LXTerminalからnano(エディタ)を起動して/etc/rc.localを編集。
sudo nano /etc/rc.local
今回、自動起動するpython3プログラムは「bme280_sample_TFT_csv_pi5.py」、保管先は「 /home/pi/ドキュメント」です。このpython3スクリプトを/etc/rc.localに付け加えて、上書き保存。
python3 /home/pi/ドキュメント/bme280_scd4x_sample_TFT_csv_pi5.py
自動起動が不要になた時は、先頭に#をつけて上書き保存するか、行を削除します。