Raspbery Pi 5と2.8インチ液晶モジュール(240×320)を使ったCO2濃度、気圧、温度、湿度&WiFi時計(2):CO2濃度センサ_SCD4x

Raspberry Pi 5 とBME280センサ(気圧、温度、湿度)とSCD4xセンサ(CO2濃度、温度、湿度) モジュールをI2C接続、その測定データをSPI接続した2.8インチTFT液晶 (240×320、ILI9341)モジュールに日本語フォントで表示した際の作業メモです。
SCD4xファミリーにはSCD40、SCD41などがあるようです。今回は手持ちのSCD41を使って動作検証しています。

参考:Sensirion SCD4xミニチュアCO2センサー

Raspberry Pi 5とSCD41、BME280センサで測定したCO2濃度、気圧、気温、湿度を2.8インチTFT液晶に表示。
OS起動時にpython3プログラムを自動起動。
目次

使ったパーツと結線図、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)にはピンヘッダが未実装なので手持ちのピンヘッダをはんだ付けしています。

2.8インチTFT液晶モジュールと内蔵SDカードスロット
基板裏面のピン配置とシルク印刷

参考:
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に変更しています。

# 2.8インチ
TFT液晶
J2

シルク印刷
GPIO
ピン番号
GPIO
機能
備考
1VCC13V3 power
2GND30Ground
3CS16GPIO23CSピンを
GPIO8(CE0)から
GPIO23に変更
4RESET18GPIO24
5DC22GPIO25
6SDI (MOSI)19GPIO10 (MOSI)
7SCK23GPIO11 (SCLK)
8LED13V3 power
9SDO (MISO)21GPIO9 (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Ωでプルアップされています。

BME280モジュール(右が6ピン基板、左が4ピン基板)

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センサモジュールにはんだ付けします。

今回使ったSCD41センサ搭載の基板

結線図

2.8インチTFT液晶 (240×320)モジュールを4線式のSPIで、BME280センサモジュール、SCD41センサモジュールをSoC内蔵の I2C(ハードウエア I2C)で接続する結線図です。

GPIOからSCD41、BME280、2.8インチTFT (240×320)モジュールへの結線図

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バージョンなどを確認した結果です。

USBブートしたRaspberry Pi OS(64-bit)の情報

I2CとSPIの設定 はデフォルトでは無効です。
苺メニューの「設定」–>「Raspberry Piの設定」を開いて–>「インターフェース」でI2CとSPIを有効にチェック。

再起動するとSoC内蔵の I2C(ハードウエア I2C )とSPI通信が使えるようになります。

I2CとSPIを有効化

SCD41センサとBME280センサのデフォルトのI2Cスレーブアドレスです。

 #センサモジュール I2Cアドレス
1SCD410x62
2BME2800x76

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 --break-system-packages. This option should not be enabled by default and should carry some connotation that its use is risky.

PEP 668 – Marking Python base environments as “externally managed” | peps.pyth

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

rpi-lgpio 0.6

BME280ライブラリ

BME280のライブラリには「RPi.bme280 0.2.4」をコマンドオプションを付けてインストール。Python3コード上でI2Cアドレスとポート番号を指定できるで使い勝手が良いです。

sudo pip3 install --break-system-packages RPi.bme280

RPi.bme280 0.2.4

https://pypi.org/project/RPi.bme280/

SCD4x ライブラリ

SCD4xのライブラリには「scd4x 0.0.2」をコマンドオプションを付けてインストール。

sudo pip3 install --break-system-packages scd4x

scd4x 0.0.2

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で基本動作を確認しました。

scd4x-python/examples /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センサの[気圧]、[温度]、[湿度]です。

測定データをCSV形式でshell部分に表示。背景がグレイの115、116行の先頭の#を削除

/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

自動起動が不要になた時は、先頭に#をつけて上書き保存するか、行を削除します。

/etc/rc.localを編集


 

 

よかったらシェアしてね!
  • URLをコピーしました!
目次