TOPに戻る

ラズパイ5 pythonの仮想環境 ⑪ CO2センサSCD41

 Adafruitから入手したCO2センサ SCD41(Sensirion)を利用します。温度と湿度、そしてCO2が測定できます。

AdafruitのStemma QT/Qwiicボード

 SCD41ボード解説のページ

 Stemma QT/Qwiic(JST SH 4ピン)コネクタは2か所に装着されていて、どちらにつないでもかまいません。このコネクタを使ってI2Cで制御する場合、特に、ジャンパ線をつなぐなどは不要です。

 コネクタは、表と裏のどちらも差し込めそうですが、ピンが内部の上部に並んでいるので、差し込める方向は一意です。ロック機構はないですが、すぐに抜けるということはありません。

CO2センサSCD41のおもなスペック

 SCD41のデータシート

  • 電源電圧 2.4~5.5V
  • 消費電流 17mA
  • 温度測定範囲 -10~+60℃、確度;±0.8℃
  • CO2測定範囲 400~5000ppm、確度;±(40ppm + 読み取り値の5%)
  • 湿度測定範囲 0~100%、確度;±6%
  • インターフェース I2C(最大400kHz)
  • スレーブ・アドレス 0x62

環境

  • ハードウェア Raspberry Pi 5(4GBモデル)
  • OS Raspberry Pi OS (64ビット)、リリース日December 5th 2023
  • Windows10 22H2にて、ssh(OpenSSH_9.2p1 Debian-2+deb12u2, OpenSSL 3.0.11 19 Sep 2023)および、VNC Viewerを動作させている

接続

テスト環境envtestで、ライブラリのインストールの実験

 仮想環境enctestを作って入ります。smbus2が残っていました。

$ source envtest/bin/activate

(envtest) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
pip        23.0.1
setuptools 66.1.1
smbus2     0.4.3

(envtest) yoshi@ras05:~ $ pip install scd4x
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting scd4x
  Using cached https://www.piwheels.org/simple/scd4x/scd4x-0.0.2-py3-none-any.whl (6.7 kB)
Requirement already satisfied: smbus2 in ./envtest/lib/python3.11/site-packages (from scd4x) (0.4.3)
Installing collected packages: scd4x
Successfully installed scd4x-0.0.2


(envtest) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
pip        23.0.1
scd4x      0.0.2
setuptools 66.1.1
smbus2     0.4.3

  サンプル・プログラムをダウンロードしてきます。

   python-i2c-scd/examples/example_usage_linux_scd4x.py

 scd41.pyで保存しました。

# (c) Copyright 2023 Sensirion AG, Switzerland

import time
import argparse
from sensirion_i2c_driver import LinuxI2cTransceiver, I2cConnection
from sensirion_i2c_scd import Scd4xI2cDevice

parser = argparse.ArgumentParser()
parser.add_argument('--i2c-port', '-p', default='/dev/i2c-1')
args = parser.parse_args()

# Connect to the I2C 1 port
with LinuxI2cTransceiver(args.i2c_port) as i2c_transceiver:
    # Create SCD4x device
    scd4x = Scd4xI2cDevice(I2cConnection(i2c_transceiver))

    # Make sure measurement is stopped, else we can't read serial number or
    # start a new measurement
    scd4x.stop_periodic_measurement()

    print("scd4x Serial Number: {}".format(scd4x.read_serial_number()))

    scd4x.start_periodic_measurement()

    # Measure every 5 seconds for 5 minute
    for _ in range(60):
        time.sleep(5)
        co2, temperature, humidity = scd4x.read_measurement()
        # use default formatting for printing output:
        print("{}, {}, {}".format(co2, temperature, humidity))

 動作させると足りないライブラリが2点あったのでインストールします。

(envtest) yoshi@ras05:~ $ pip install sensirion_i2c_driver

(envtest) yoshi@ras05:~ $ pip install sensirion_i2c_scd

(envtest) yoshi@ras05:~ $ pip list
Package              Version
-------------------- -------
pip                  23.0.1
scd4x                0.0.2
sensirion-i2c-driver 1.0.0
sensirion-i2c-scd    0.1.2
setuptools           66.1.1
smbus2               0.4.3

 実行した様子です。

測定結果をグラフィック・ディスプレイに表示

 上記のテスト環境でインストールしたライブラリを、前回まで使っていた仮想環境encadcにインストールします。

yoshi@ras05:~ $ source envadc/bin/activate
(envadc) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
i2cdevice  1.0.0
numpy      1.26.4
pillow     10.2.0
pip        23.0.1
setuptools 66.1.1
smbus2     0.4.3
spidev     3.6
ST7789     0.0.4

(envadc) yoshi@ras05:~ $ pip install scd4x
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting scd4x
  Using cached https://www.piwheels.org/simple/scd4x/scd4x-0.0.2-py3-none-any.whl (6.7 kB)
Requirement already satisfied: smbus2 in ./envadc/lib/python3.11/site-packages (from scd4x) (0.4.3)
Installing collected packages: scd4x
Successfully installed scd4x-0.0.2


(envadc) yoshi@ras05:~ $ pip install sensirion_i2c_driver
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting sensirion_i2c_driver
  Using cached https://www.piwheels.org/simple/sensirion-i2c-driver/sensirion_i2c_driver-1.0.0-py3-none-any.whl (16 kB)
Installing collected packages: sensirion_i2c_driver
Successfully installed sensirion_i2c_driver-1.0.0


(envadc) yoshi@ras05:~ $ pip install sensirion_i2c_scd
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting sensirion_i2c_scd
  Using cached https://www.piwheels.org/simple/sensirion-i2c-scd/sensirion_i2c_scd-0.1.2-py3-none-any.whl (13 kB)
Requirement already satisfied: sensirion-i2c-driver~=1.0.0 in ./envadc/lib/python3.11/site-packages (from sensirion_i2c_scd) (1.0.0)
Installing collected packages: sensirion_i2c_scd
Successfully installed sensirion_i2c_scd-0.1.2


(envadc) yoshi@ras05:~ $ python scd41.py
scd4x Serial Number: 33787350563647
968 ppm, 24.7 ツーC, 42.6 %RH
942 ppm, 24.2 ツーC, 43.6 %RH

関数化

 いくつかのエラーがI2Cバスのアクセス時に時々発生したので、try文を追加しました。

# (c) Copyright 2023 Sensirion AG, Switzerland
import time
import re
import argparse
from sensirion_i2c_driver import LinuxI2cTransceiver, I2cConnection
from sensirion_i2c_scd import Scd4xI2cDevice

parser = argparse.ArgumentParser()
parser.add_argument('--i2c-port', '-p', default='/dev/i2c-1')
args = parser.parse_args()

co2 = CO2 = ''
temperature = Temparature =''
humidity = Humidity = ''

def readData():
    global co2
    global temperature
    global humidity    
    with LinuxI2cTransceiver(args.i2c_port) as i2c_transceiver:
        # Create SCD4x device
        scd4x = Scd4xI2cDevice(I2cConnection(i2c_transceiver))
        # Measure
        try:
            (co2, temperature, humidity) = scd4x.read_measurement()
        except BaseException:
            pass
    return  co2, temperature, humidity

#main
while 1:
    co2, temperature, humidity = readData()
    print(co2, temperature, humidity)
    try:
         CO2 =float(re.sub(r'[^\d.-]', '', str(co2)))
    except ValueError:
         pass
    try:
         Temparature =float(re.sub(r'[^\d.-]', '', str(temperature)))
    except ValueError:
         pass
    try:
         Humidity =float(re.sub(r'[^\d.-]', '', str(humidity)))
    except ValueError:
         pass
    print(CO2, Temparature, Humidity)
    print('')
    time.sleep(3)

 得られる測定値は、123.4ppmなどというオブジェクトなので、文字列にし、小数点を含む数字だけを取り出し、floatで数値化しました。

 readData()中のtry文は、I2Cバスのアクセスでエラーが出たのをスキップするため、CO2 =float(Co2)などの数値化でのtry文は、re.sub()がまれに '' を返すこときにスキップするためです。

グラフィック・ディスプレイに表示

 前回のst7789のライブラリを利用します。

# (c) Copyright 2023 Sensirion AG, Switzerland
import time
import re
import argparse
from sensirion_i2c_driver import LinuxI2cTransceiver, I2cConnection
from sensirion_i2c_scd import Scd4xI2cDevice
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import ST7789

parser = argparse.ArgumentParser()
parser.add_argument('--i2c-port', '-p', default='/dev/i2c-1')
args = parser.parse_args()

co2 = CO2 = ''
temperature = Temparature =''
humidity = Humidity = ''

def readData():
    global co2
    global temperature
    global humidity    
    with LinuxI2cTransceiver(args.i2c_port) as i2c_transceiver:
        # Create SCD4x device
        scd4x = Scd4xI2cDevice(I2cConnection(i2c_transceiver))
        # Measure
        try:
            (co2, temperature, humidity) = scd4x.read_measurement()
        except BaseException:
            pass
    return  co2, temperature, humidity

MESSAGE01 = "SCD41 "
MESSAGE02 = "CO2 Temparature Humidity"
print(MESSAGE01+MESSAGE02)

disp = ST7789.ST7789( height=240,width=320,rotation=0,
    port=0,cs=0,dc=22,rst=25,backlight=None,spi_speed_hz=80 * 1000 * 1000,
    )

disp.begin()
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
COLOR_ORANGE = (255, 167, 24)
COLOR_RED    = (255, 0, 0)
COLOR_WHITE  = (255, 255, 255)
COLOR_BLACK  = (5, 5, 5)
img = Image.new('RGB', (320, 240), color=COLOR_WHITE)
draw = ImageDraw.Draw(img)
draw.text((10,20),MESSAGE01, font=font, fill=COLOR_RED)
draw.text((10,40),MESSAGE02, font=font, fill=COLOR_RED)
disp.display(img)

while 1:
    co2, temperature, humidity = readData()
    print(co2, temperature, humidity)
    try:
         CO2 =float(re.sub(r'[^\d.-]', '', str(co2)))
    except ValueError:
         pass
    try:
         Temparature =float(re.sub(r'[^\d.-]', '', str(temperature)))
    except ValueError:
         pass
    try:
         Humidity =float(re.sub(r'[^\d.-]', '', str(humidity)))
    except ValueError:
         pass
    #print(CO2, Temparature, Humidity)
    print('')
    MESSAGE0 = 'CO2= '+str(round(CO2))+'ppm'
    MESSAGE1 = 'Temparature= '+str(round(Temparature,3))+'`C'
    MESSAGE2 = 'Humidity= '+str(round(Humidity,3))+'%RH'
    draw.rectangle((10, 74, 280,190), COLOR_BLACK)
    draw.text((20,90), MESSAGE0, font=font, fill=COLOR_ORANGE)
    draw.text((20,120),MESSAGE1, font=font, fill=COLOR_ORANGE)
    draw.text((20,150),MESSAGE2, font=font, fill=COLOR_ORANGE)
    disp.display(img)
    time.sleep(3)

 実行中の様子です。

-