Raspbery Pi 4B(以下4B)とSCD30センサの測定データをSPI接続した2.8インチTFT液晶(240×320、ILI9341)モジュールに日本語フォントで表示した際の作業メモです。
Raspberry Pi OS(bullseye)ではソフトウエアupdate通知アイコンがタスクバーに表示されるので作業前に更新しておきます。
Sensirion(センシリオン)社のSCD30センサはI2C接続
CO2センサを選ぶにあたってTVOC(Total Volatile Organic Compounds 総揮発性有機化合物の総称)測定がメインのセンサ(VOCガス濃度からCO2濃度を概算)もあるので迷いましたが、CO2 濃度測定の精度が高いSensirion社の 非分散型赤外線(NDIR)ベースの SCD30センサモジュールを使っています。
このモジュールには基板内に同じSensirion社のSHT31センサを補正用として内蔵しているので湿度や温度も測定できますが、SCD30センサモジュールとしての動作温度は0℃~+50℃の仕様です。
パーツの結線とRaspberry Pi OSのI2C、SPIの有効化
SCD30センサでCO2濃度、補正用として内蔵するSHT31センサの気温、湿度を2.8インチTFT液晶 (240×320)モジュールに日本語フォントで表示します。
結線図
SCD30センサモジュールは、GPIO2(SDA)とGPIO3(SCL)の2線を使うSoC内蔵の I2C(ハードウエア I2C)接続 。I2Cアドレスは0x61です。2.8インチTFT液晶(240×320)液晶モジュールは4線式のSPIです。
2.8インチTFT液晶モジュールとGPIOとのピン接続の詳細は下記ページに纏めました。本記事ではライブラリのインストールなど必要事項のみの記載です。
Raspberry Pi OSのI2CとSPIを有効化
I2CとSPIの設定 はデフォルトでは無効です。
苺メニューの「設定」–>「Raspberry Piの設定」を開いて–>「インターフェース」でI2CとSPIを有効にチェック。再起動するとSoC内蔵の I2C(ハードウエア I2C とSPI通信が使えるようになります。
Python3:ライブラリのインストール
ライブラリのインストールにはpipを使います。pipは、Pythonパッケージのインストールなどを行うユーティリティで、Raspberry Pi OS with desktop(bullseye)にはPython3とともにインストールされています。
バージョンはPythonが3.9.2、pipが20.3.4。
構築したRaspberry Pi OSにはPython3のみなので、コマンドではpipとpip3を区別していません。
smbus2
未インストールであればi2C通信を行うためにsmbus2をインストールします。smbus2パッケージは「smbus2-0.3.0」でした。
sudo pip install smbus2
2.8インチTFT液晶ライブラリ
2.8インチTFT液晶 (240×320)モジュールの液晶コントローラLSIはILI9341。Adafruitのライブラリ「Adafruit_CircuitPython_RGB_Display」を利用させていただきました。
sudo pip install adafruit-circuitpython-rgb-display
https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display
SCD30ライブラリ
SCD30のライブラリには「scd30_i2c」を利用させていただきました。ライブラリ「scd30_i2c」のバージョンは「scd30-i2c-0.0.6」でした。利用には、Pythonバージョン >=3.7.3が必要です。
sudo pip install scd30-i2c
https://pypi.org/project/scd30-i2c/
Python3:SCD30測定データを2.8インチTFT液晶に表示
ライブラリのサンプルプログラムで動作確認
pypiサイトの「SCD30 CO2 sensor I²C driver in Python 3」掲載のサンプルコードで動作確認しました。
Thonny Python IDEでサンプルコード「scd30_sample.py」を実行するとShell部分にCO2濃度、気温、湿度が2秒間隔で表示されます。
当方の環境では「import time」が必要だったので追加しています。
scd30_sample.py ※ここをクリックするとコード表示
#scd30_sample.py
import time
from scd30_i2c import SCD30
scd30 = SCD30()
scd30.set_measurement_interval(2)
scd30.start_periodic_measurement()
time.sleep(2)
while True:
if scd30.get_data_ready():
m = scd30.read_measurement()
if m is not None:
print(f"CO2: {m[0]:.2f}ppm, temp: {m[1]:.2f}'C, rh: {m[2]:.2f}%")
time.sleep(2)
else:
time.sleep(0.2)
2.8インチTFT液晶(240×320)モジュールに表示
Adafruitライブラリ付属のサンプルプログラム「rgb_display_pillow_stats.py」を参考にして、「SCD30 CO2 sensor I²C driver in Python 3」掲載のサンプルコードの出力先として2.8インチTFT液晶モジュールを加えた「scd30_sample_TFT_csv.py」を作りました。
日本語を表示するために、独立行政法人情報処理推進機構 (IPA) が提供している日本語フォント「IPAex フォント」をインストール。「ipaexfont-gothic」を使っています。
sudo apt install fonts-ipaexfont
Raspberry Pi OS with desktop(bullseye)にプリインストール されているThonny Python IDEを使って動作確認しました。
Thonny Python IDEのshell部分にCSV形式で連続表示するとともに、2.8インチTFT液晶モジュールにも日時(曜日)、CO2濃度、気温、湿度を2秒間隔で表示します。
Thonny Python IDEのshell部分へのCSV表示が不要な場合は97行目を#でコメントアウトします。
scd30_sample_TFT_csv.py
※ここをクリックするとコード表示を開閉できます。
#scd30_sample_TFT_csv.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.CE0)
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", 65)
font2 = ImageFont.truetype("/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf", 25)
font3 = ImageFont.truetype("/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf", 20)
#---SCD30 init-----------------scd30_sample.py---------
from scd30_i2c import SCD30
scd30 = SCD30()
scd30.set_measurement_interval(2)
scd30.start_periodic_measurement()
time.sleep(2)
while True:
#---csv out ------------------------------------------
if scd30.get_data_ready():
m = scd30.read_measurement()
if m is not None:
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="ー"
pscd30 = f"{m[0]:.1f},{m[1]:.1f},{m[2]:.1f}"
P_csv = dtd + jdta + " " + dtt + "," + pscd30
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, 188), dtd, font=font2, fill="#00ff00")
draw.text((50, 212), dtt, font=font2, fill="#00ff00")
draw.text((225, 212), jdta, font=font2, fill="#00ff00")
draw.text((20, 6), "温度:", font=font2, fill="#ffffff")
draw.text((24, 32), "( ℃ )", font=font3, fill="#ffffff")
temp =format(float(m[1]), '.1f')
draw.text((90, 0), temp, font=font1, fill="#FFFF00")
draw.text((20, 68), "CO :", font=font2, fill="#ffffff")
draw.text((58, 76), "2", font=font3, fill="#ffffff")
draw.text((18, 93), "( ppm )", font=font3, fill="#ffffff")
co2 =format(float(m[0]), '.1f')
draw.text((90, 60), co2, font=font1, fill="#FFFF00")
draw.text((20, 130), "湿度:", font=font2, fill="#ffffff")
draw.text((24, 155), "( % )", font=font3, fill="#ffffff")
humi =format(float(m[2]), '.1f')
draw.text((90, 120), humi, font=font1, fill="#FFFF00")
disp.image(image)
time.sleep(2)
else:
time.sleep(0.2)
SCD30センサのセルフキャリブレーション
SCD30センサは換気の良い場所で電源を入れたままで一定時間連続測定してセルフキャリブレーションします。400ppm台の数値に落ち着くと思います。
SparkFun SCD30 CO₂ センサー ライブラリ
注: SCD30 には、自動セルフキャリブレーション ルーチンがあります。Sensirion は、セルフキャリブレーションを完了するために、少なくとも 1 日 1 時間の「新鮮な空気」で 7 日間の連続測定を推奨しています。Note: The SCD30 has an automatic self-calibration routine. Sensirion recommends 7 days of continuous readings with at least 1 hour a day of ‘fresh air’ for self-calibration to complete.
https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
Interface Description Sensirion SCD30 Sensor Module p.13/21
1.4.6 自動セルフキャリブレーション (ASC) の (非) アクティブ化
継続的な自動セルフキャリブレーションは、次のコマンドで (非) アクティブにすることができます。 初めてアクティブ化する場合、アルゴリズムが ASC の初期パラメーター セットを見つけることができるように、最低 7 日間必要です。 センサーは、毎日少なくとも 1 時間は新鮮な空気にさらす必要があります。 また、その間、センサーを電源から切り離すことはできません。そうしないと、キャリブレーション パラメータを見つける手順が中止され、最初からやり直す必要があります。 正常に計算されたパラメータは SCD30 の不揮発性メモリに保存され、再起動後も以前に見つかった ASC のパラメータが引き続き存在するという効果があります。1.4.6 (De-)Activate Automatic Self-Calibration (ASC)
https://sensirion.com/media/documents/D7CEEF4A/6165372F/Sensirion_CO2_Sensors_SCD30_Interface_Description.pdf
Continuous automatic self-calibration can be (de-)activated with the following command. When activated for the first time a period of minimum 7 days is needed so that the algorithm can find its initial parameter set for ASC. The sensor has to be exposed to fresh air for at least 1 hour every day. Also during that period, the sensor may not be disconnected from the power supply, otherwise the procedure to find calibration parameters is aborted and has to be restarted from the beginning. The successfully calculated parameters are stored in non-volatile memory of the SCD30 having the effect that after a restart the previously found parameters for ASC are still present.