はじめに
Raspberry Pi 4 で湿度、温度、気圧、二酸化炭素を取得してディスプレイに表示したいと思います。
湿度、温度、気圧の取得にはBMS280 を利用します。
二酸化炭素の取得にはMH-Z19Bを利用します。
ディスプレイは2.8" TFTインチ液晶ディスプレイ タッチパネル 240x320 SPI ILI9341 です。
下記のサイトを参考にさせてもらっています。ありがとうございます。
前提
前提条件として使用するOSがあります。
busterベースのOSが必要です。レガシーです。
bullseyeベースでは動作しないと思われます。
Python3で動作します。
配線
配線による機器の破損などは責任は持ちませんが下記のように接続します。
Rasberry Pi 4とBMS280、MH-Z19B、ディスプレイを接続していきます。



Rasberry Piの設定
Rasberry Piの設定を行います。raspi-config でSPI、I2C、Serial Port(accessible over serial? NO、port hardware :Yes)を有効にします。
pip3 install mh-z19
pip3 install adafruit-circuitpython-bme280
apt install fonts-ipaexfont
ディスプレイに表示させるためののプログラム(参考サイトのbme280_scd30_sample_TFT_csv.pyを利用しています。)
#bme280_scd30_sample_TFT_csv.py
#coding: utf-8
#---TFT-ili9341 init-----rgb_display_pillow_stats.py---
import time
import subprocess
import digitalio
import board
import mh_z19
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", 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)
#---SCD30 init-----------------scd30_sample.py------------
#outco2 = mh_z19.read_co2valueonly()
time.sleep(2)
while True:
outco2 = mh_z19.read_co2valueonly()
#---csv out ------------------------------------------
dtd = time.strftime('%Y年%m月%d日', time.localtime())
dtt = time.strftime('%H時%M分%S秒', time.localtime())
dta = time.strftime('%a', 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)
pbme280 = f"{data.pressure:.1f},{data.temperature:.1f},{data.humidity:.1f}"
#pscd30 = f"{m[0]:.1f},{m[1]:.1f},{m[2]:.1f}"
pcsv = dtd + jdta + " " + dtt + "," + str(outco2) + "," + pbme280
#print(pcsv)
#print(pbme280)
# 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(outco2), '.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)
time.sleep(2)
実行します。
python3 bme280_mh-z19b.py
バックエンドで動作させる
nohup python3 bme280_mh-z19b.py &

表示されることを確認しましょう。
まとめ
ここまでできればキレイに纏めて部屋のディスプレイにも耐えられそうです。換気が必要な時期にはあると便利だと思います。
