Raspberry Piで温度、湿度、CO2濃度を測定する方法を紹介します。
各種センサの値をモニタに表示して、感覚的な暑い/寒いを数値化します。また、不快指数を算出することで、冷房/暖房をつける目安にもなります。
Contents
Raspberry Piのモニタリング完成イメージ

各種センサを表示した様子です。ラズパイやセンサーを固定するブレッドボードの作り方は以下の記事を参考にしてください。

こちらがLCDモニタに表示しているデータです。室内の温度、湿度、不快指数(DI)、CO2濃度を確認することができます。
温度と湿度はDHT22から値を取得しています。
不快指数(Discomfort Index=DI)は蒸し暑さを数量的に表した指数で、温度と湿度から計算することができます。不快指数と体感の関係配下のとおりです。
| 不快指数 | 体感 |
|---|---|
| 〜50 | 寒くてたまらない |
| 50〜55 | 寒い |
| 55〜60 | 肌寒い |
| 60〜65 | 何も感じない |
| 65〜70 | 快適 |
| 70〜75 | 不快感を持つ人が出始める |
| 75〜80 | 半数以上が不快に感じる |
| 80〜85 | 全員が不快に感じる |
| 85〜 | 暑くてたまらない |
LCDモニタには英数値のみを表示できるため、体感を英語で置き換えて表示しています。
CO2濃度はMH-Z19Cから値を取得しています。外気の標準が410ppm程度です。室内の濃度は1000ppm以下が望ましいとされていますが、狭いワンルームに設置しているため室内で軽い運動をすると1100ppmになることがあります。1000ppmを超えるとLINEに通知するような仕組みも有用そうです。
準備
接続するセンサとディスプレイは以下です。
- DHT22(温湿度計)
- MH-Z19C(CO2濃度計)
- I2C 1602 LCD(ディスプレイ)
I2C 1602 LCDはOSOYOO社から発売されているスターターキットに同封されていたものを使用しました。
今回の構成について、ラズパイ3B+とRaspberry Pi Zero WHで動作を確認済みです。
※ワンポイントアドバイス(電源の注意点)
DHT22やMH-Z19Cといった複数のセンサーやLCDディスプレイを同時に接続する場合、Raspberry Piの給電不足によって動作が不安定になったり、センサーの値が取得できなくなったりするトラブルが発生します。ラズパイ本体の電源アダプタには、必ず2.5A〜3A以上の安定した出力ができる高品質なものを使用してください。
回路図

各センサーとGPIOを接続した回路図をfritzingを用いて作成しました。初めてブレッドボードビューを作成したため、かなり見づらいとは思いますがご了承ください。
今回の回路について、本来であれば抵抗および5V, 3V電源を考慮する必要がありますが、今回は動けばいいや精神で組んでいます。(よくない)
ラズパイのGPIOについては公式サイトを参照してください。


シリアル通信
それでは手順を解説します。まずは設定画面を開き、シリアル通信を有効にします。
sudo raspi-config
Interface Options > P6 Serial Portを選択します。
Would you like a login shell to be accessible over serial? と訊かれるので、「いいえ」を選択します。
Would you like the serial port hardware to be enabled? と訊かれるので、「はい」を選択して「了解」を選択します。
以下のコマンドで再起動して設定を適用します。
sudo shutdown -r now
ライブラリの準備
Pythonがインストールされていない場合は以下のコマンドからインストールしてください。
sudo apt update && sudo apt install -y python3-pip python3-dev python3-venv libgpiod2
DHTライブラリのインストール
以下のコマンドを実行します。
pip3 install adafruit-circuitpython-dht
ライブラリのインストールが完了したら、DHT22が動作していることを確認します。
DHTライブラリのテストプログラム
作業用ディレクトリを作成して、以下のテストプログラムを作成および実行してください。
mkdir sensorScript
cd sensorScript
vim testDHT22.py
以下のPythonプログラムを作成してください。
#!/usr/bin/env python3
import board
import adafruit_dht
dhtDevice = adafruit_dht.DHT22(board.D4)
temperature = dhtDevice.temperature
humidity = dhtDevice.humidity
# 標準出力
if humidity is not None and temperature is not None:
print('Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(temperature, humidity))
else:
print('Failed to get reading. Try again!')
python3 testDHT22.py
実行して以下のように測定された気温と湿度が表示されればOKです。

※ワンポイントアドバイス(エラーが出た場合)
DHT22は通信タイミングがシビアなため、プログラム実行時に RuntimeError などの一時的な読み込みエラーが発生することがあります。これはセンサーの故障ではなく仕様です。連続で値を取得する際は、1〜2秒以上のインターバル(time.sleep)を空けるか、エラー時に自動リトライする例外処理を入れておくと動作が安定します。
MH-Z19ライブラリのインストール
以下のコマンドでライブラリをインストールします。
sudo pip3 install mh-z19
動作確認をします。
sudo python3 -m mh_z19

CO2濃度の値が表示されればOKです。
温湿度とCO2濃度をLCDに表示するプログラム
スクリプトの準備が完了したら、メインのLCDに表示するプログラムを作成します。
作成した sensorScript ディレクトリに移動します。以下のパスは環境に応じて変更してください。
cd ~/sensorScript
vim displaySensor.py
以下のPythonプログラムを作成してください。
#!/usr/bin/env python3
import smbus
import time
import datetime
import board
import adafruit_dht
import sys
import mh_z19
I2C_ADDR = 0x27 # I2C device address, if any error, change this address to 0x27
LCD_WIDTH = 16 # Maximum characters per line
# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command
LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line
ENABLE = 0b00000100 # Enable bit
# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005
#Open I2C interface
#bus = smbus.SMBus(0) # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1
def lcd_init():
# Initialise display
lcd_byte(0x33,LCD_CMD) # 110011 Initialise
lcd_byte(0x32,LCD_CMD) # 110010 Initialise
lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
lcd_byte(0x01,LCD_CMD) # 000001 Clear display
time.sleep(E_DELAY)
def lcd_byte(bits, mode):
# Send byte to data pins
# bits = the data
# mode = 1 for data
# 0 for command
LCD_BACKLIGHT = 0x08 # On
now_dt = datetime.datetime.now()
# Turn off backlight between 0:00 ~ 6:00
if 0 <= now_dt.hour <= 5:
LCD_BACKLIGHT = 0x00 # Off
bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT
# High bits
bus.write_byte(I2C_ADDR, bits_high)
lcd_toggle_enable(bits_high)
# Low bits
bus.write_byte(I2C_ADDR, bits_low)
lcd_toggle_enable(bits_low)
def lcd_toggle_enable(bits):
# Toggle enable
time.sleep(E_DELAY)
bus.write_byte(I2C_ADDR, (bits | ENABLE))
time.sleep(E_PULSE)
bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
time.sleep(E_DELAY)
def lcd_string(message,line):
# Send string to display
message = message.ljust(LCD_WIDTH," ")
lcd_byte(line, LCD_CMD)
for i in range(LCD_WIDTH):
lcd_byte(ord(message[i]),LCD_CHR)
def getDI(h, t):
DI_value = 0.81 * t + 0.01 * h *(0.99 * t - 14.3) + 46.3
if DI_value < 55:
taikan = "COLD"
elif 55 <= DI_value < 60:
taikan = "CHILLY"
elif 60 <= DI_value < 65:
taikan = "NORMAL"
elif 65 <= DI_value < 70:
taikan = "COMFORT"
elif 70 <= DI_value < 75:
taikan = "NOT HOT"
elif 75 <= DI_value < 80:
taikan = "BIT HOT"
elif 80 <= DI_value < 85:
taikan = "HOT"
elif 85 <= DI_value:
taikan = "VERY HOT"
return taikan
def main():
dhtDevice = adafruit_dht.DHT22(board.D4)
try:
t = dhtDevice.temperature
h = dhtDevice.humidity
except RuntimeError as error:
print(error.args[0])
sys.exit(1)
if h is not None and t is not None:
lcd_init()
lcd_string("{0:0.1f}\'C {1:0.1f}%".format(t, h), LCD_LINE_1)
lcd_string(getDI(h, t) + " " + str(mh_z19.read()) + 'ppm', LCD_LINE_2)
print("{0:0.1f}".format(t))
else:
print('Failed to get reading. Try again!')
sys.exit(1)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
finally:
pass
#lcd_byte(0x01, LCD_CMD)
ポイント
- 午前0時~午前5時まではLCDのライトが眩しいため、次の部分で消灯しています。
# Turn off backlight between 0:00 ~ 6:00 if 0 <= now_dt.hour <= 5: LCD_BACKLIGHT = 0x00 # Offこの部分の0と5を変更することで、消灯時刻を変更できます。 - 不快指数(DI)は
getDI(h, t)で算出しています。 - LCDの1行目は、
lcd_string("{0:0.1f}\'C {1:0.1f}%".format(t, h), LCD_LINE_1)で表示しています。tに温度、hに湿度が代入されています。 - LCDの2行目は、
lcd_string(getDI(h, t) + " " + str(mh_z19.read()) + 'ppm', LCD_LINE_2)で表示しています。不快指数とCO2濃度ライブラリから取得した値を表示しています。
以下のコマンドで動作確認してください。(CO2濃度のライブラリを呼び出すため、管理者権限で実行します)
sudo python3 ~/sensorScript/displaySensor.py
ディスプレイに各種センサが表示されれば成功です。
cronで定期実行する
作成したスクリプトをcronに登録して定期実行しましょう。
crontab -e
vimの編集画面が開いたら、以下のコマンドを文末に追加します。[ユーザー名]は置き換えてください。以下の例は2分おきにスクリプトが実行されます。
*/2 * * * * sudo python3 ~/sensorScript/displaySensor.py
保存したら、以下のコマンドで確認します。
crontab -l
追加したコマンドが表示されればOKです。
おわりに
ラズパイに温湿度計とCO2濃度計を接続してLCDに表示する方法を解説しました。センサーで取得した値をmuninを用いてグラフ化したり、グラフ化した画像をPythonのライブラリを用いてDiscordやLineに送るなど様々な活用方法があります。
CO2濃度計は少し高いですが、温湿度計は安価で手に入るため、まずは温湿度計を接続して表示するのもいいかもしれません。また、ディスプレイに日経平均やSP500、暗号通貨のレートや明日の天気情報などを常時表示させるのも楽しいですね。
ちなみに、今回はこれまでの記事で最も時間がかかったかもしれません…配線が抜けたりMicroSDカードが故障したりしたときに復旧できるように、記録として残しました。少しでも参考になれば幸いです。










