Raspberry Piで温湿度ロガーを作る|pip installが止まる・日本語が□になる所まで

raspberry pi

Raspberry Pi × Python

Raspberry Pi × Pythonで温湿度ロガーを作る

配線からPythonコード、グラフ化までステップごとに解説します

実機で検証済み
Raspberry Pi 5 / Trixie
DHT11で確認

Raspberry Piに温湿度センサー(DHT11)をつないで、気温と湿度を自動で記録し、Pythonでグラフにするまでをやります。配線からグラフまで、実際に手元で動かした手順です。

Raspberry Piと温湿度センサーを使って、気温・湿度を自動で記録し、Pythonでグラフにするところまでやります。配線からグラフまで、実際に手元のRaspberry Pi 5で動かして確かめた手順です。ログは1時間ぶん(10分おきに8点)取って、そのグラフを載せています。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6

用意するもの

  • Raspberry Pi本体(Pi 4/Pi 5/Pi Zero 2Wなど)
  • DHT11またはDHT22(温湿度センサー)
  • ブレッドボード
  • ジャンパー線
  • プルアップ抵抗(4.7kΩ〜10kΩ) … 3ピンのモジュール型なら要りません(基板に載っています)


DHT11とDHT22、どちらを選ぶ?

どちらも温湿度センサーですが、精度と価格に違いがあります。

  • DHT11:価格が安く、初めて電子工作に挑戦する場合におすすめ。精度はやや粗め
  • DHT22:DHT11よりやや高価だが、温度・湿度ともに測定精度が高い。長く記録して細かい変化を見たいとき向き

「まずは安く試してみたい」ならDHT11、「グラフの精度にもこだわりたい」ならDHT22を選ぶとよいでしょう。

センサーの配線をする

DHT11・DHT22には2つの形が出回っています。形によって配線が変わるので、まず手元のものがどちらかを確かめてください。

  • 3ピン(モジュール型) … 小さな基板に載っていて、ピンが3本。基板に「VCC」「DATA」「GND」と印字されている。赤いLEDが付いていることが多い。プルアップ抵抗は基板に載っているので、別に用意する必要はありません
  • 4ピン(素子単体) … 水色または白の樹脂部品から、足が4本出ている。DATAとVCCの間に、プルアップ抵抗(4.7kΩ〜10kΩ)を自分で入れます

この記事で実際に動かしたのは3ピンのモジュール型です。以下はその配線です。

  • VCCピン → Raspberry Piの3.3V(物理1番ピン)
  • GNDピン → Raspberry PiのGND(物理9番ピン)
  • DATAピン → Raspberry PiのGPIO17(物理11番ピン)
MEMO

DATAはGPIO4でもかまいません。解説記事ではGPIO4がよく使われますが、決まりではなく、GPIOであればどのピンでも動きます。あとで出てくるコードのboard.D17を、使うピンの番号に書き換えるだけです。ここでGPIO17を使っているのは、9番(GND)と11番(GPIO17)が同じ列で隣り合っていて、挿し間違えにくいからです。

DHT11の3ピンモジュールとRaspberry Piを結ぶ配線図。VCCを1番(3.3V)、GNDを9番、DATAを11番(GPIO17)に繋ぐ
3ピンのモジュール型の配線。プルアップ抵抗は基板に載っているので、別に用意しません
注意 — VCCとGNDを逆に挿すと壊れます

電源電圧(3.3Vか5Vか)はセンサーの製品仕様によって異なります。購入した商品の説明書を必ず確認してください。

そして、VCCとGNDの向きを必ず確かめてください。この記事を書くにあたって実際に逆に挿してしまい、Raspberry PiのGPIO4が壊れました。そのときに出た症状を、そのまま書いておきます。

  • モジュールの赤いLEDが光らない … 正しく挿さっていれば、電源を入れた時点で光ります
  • データのピンがLOWに張り付く … あとで出てくるpinctrlで確かめられます
  • センサーを外してもLOWのまま … ここまで来ると、GPIO側が壊れています

壊れたのはGPIO4の1本だけで、残り27本は無事でした。別のGPIOに移せば、そのまま作業を続けられます(この記事がGPIO17を使っているのは、そのためです)。Raspberry Pi本体を買い直す必要はありませんが、1本失うのは痛いので、挿す前に指差しで確認してください。

なお、センサー側は無事でした。かかっていたのが3.3Vだったからだと思われますが、5Vで同じことをした場合にどうなるかは確かめていません。

Pythonのセットアップをする

DHT11/DHT22をPythonで扱うには、Adafruitが提供するadafruit-circuitpython-dhtライブラリを使います。以前よく使われていたAdafruit_DHTライブラリは非推奨(deprecated)になっているので、新しく始めるならこちらです。

多くの記事に書いてあるコマンドは、いまは通りません

ネットの解説記事には、たいていこう書いてあります。

pip3 install adafruit-circuitpython-dht
sudo apt-get install libgpiod2

この2行は、いまのRaspberry Pi OSでは両方とも通りません。実際に叩くと、1行目でこう止まります。

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.

Raspberry Pi OS (Bookworm)以降、システムのPythonに直接pipで入れることが禁止されました(PEP 668)。OSが管理しているPythonを壊さないための変更で、不具合ではありません。

2行目のlibgpiod2も、いまはそういう名前のパッケージがありません。

$ apt-cache policy libgpiod2 libgpiod3
libgpiod3:
  インストールされているバージョン: 2.2.1-2+deb13u1
  ...
N: パッケージ libgpiod2 が見つかりません

後継のlibgpiod3が、最初から入っています。つまりこの行は、名前を直す必要すらなく、実行しなくてよいということです。

その前に — これから作るのは、ただのフォルダです

次の手順で、~/dhtというフォルダを1つ作ります。それだけです。

中にはPythonのコピーと、これから入れるライブラリが入ります。この工作で使うものを、そこに全部まとめておく——やっているのはそれだけです。

POINT — なぜフォルダを分けるのか

Raspberry Pi には、最初からPythonが入っています。OSが自分で使っているPythonです。ここにライブラリを足していくと、OSの動きを壊すことがあります。

だから「この工作だけで使う、別の置き場」を作って、そこにだけ入れます。OSのほうには手を触れません。

ついでに言うと、Raspberry Pi OS がpip3 installを止めているのも、これが理由です。さっき出たexternally-managed-environmentは、「OSのPythonを壊すからやめなさい」という意味でした。

ただのフォルダなので、こういうことができます。

  • 要らなくなったら、フォルダごと消せます(rm -rf ~/dht)。OSには何も残りません
  • 失敗しても作り直せます。消して、もう一度作るだけです
  • 別の工作をするときは、また別の置き場を作れば混ざりません
  • 名前は~/dhtでなくてもかまいません。分かりやすい名前で大丈夫です
MEMO — これを「仮想環境」と呼びます

Pythonの世界では、この置き場のことを「仮想環境」、作る道具をvenv(ヴイエンブ、ヴェンブ)と呼びます。

「仮想」と言われると身構えますが、中身はいま見たとおりのフォルダです。特別なソフトを入れる必要も、設定ファイルを書く必要もありません。

なお、これはRaspberry Piだけの話ではありません。WindowsでもMacでも、いまのPythonでは同じやり方が当たり前になってきています。ここで一度おぼえておくと、あとがずっと楽です。

注意 — ここだけ覚えてください

この置き場を作ったあとは、python3pipではなく、その置き場の中のものを使います。

✕ pip install ○○        ← OSのPython。入れても使えません
○ ~/dht/bin/pip install ○○   ← 作った置き場のpip

この記事のコマンドが全部~/dht/bin/で始まっているのは、そのためです。ここを間違えると、入れたはずなのに「そんなライブラリは無い」と言われます。いちばん多い引っかかりどころです。

すでに環境が混ざってしまって困っている場合や、Windowsでの整理のしかたは、python環境がぐちゃぐちゃになったら読む記事のほうが詳しいです。

実際に通る手順

では、その置き場を作って、ライブラリを入れます。

python3 -m venv --system-site-packages ~/dht
~/dht/bin/pip install adafruit-circuitpython-dht
MEMO

--system-site-packagesを付けているのは、OSが用意しているGPIO関係のパッケージを、仮想環境の中からも見えるようにするためです。これを付けずに作ると、あとでboardの読み込みに失敗することがあります。

~/dhtというフォルダが1つできます。中身を見てみたい人はls ~/dhtで覗けます(binlibが入っています)。

うまくいくと、最後にこう出ます。

Successfully installed Adafruit-Blinka-9.2.0 ... adafruit-circuitpython-dht-4.0.12 ...

確かめた環境

この記事の手順は、次の組み合わせで実際に動かして確かめています。

Raspberry Pi 5
Raspberry Pi OS (Debian 13 / Trixie)
Python 3.13.5
adafruit-circuitpython-dht 4.0.12
Adafruit-Blinka 9.2.0
libgpiod3 2.2.1-2+deb13u1

センサーはDHT11(3ピンのモジュール型)です。DHT22は手元に無いため、同じ手順で動くかどうかは確かめていません(コードのDHT11DHT22に変えるだけのはずですが、こちらでは試していません)。

温湿度を取得するプログラムを書く

まずはセンサーの値を取得して表示する、最小限のコードです。DHT11を、GPIO17に繋いだ場合の例です(DHT22ならadafruit_dht.DHT11adafruit_dht.DHT22に、別のピンを使うならboard.D17を書き換えてください)。

import time
import board
import adafruit_dht

dhtDevice = adafruit_dht.DHT11(board.D17)

while True:
    try:
        temperature = dhtDevice.temperature
        humidity = dhtDevice.humidity
        print(f"温度: {temperature}度  湿度: {humidity}%")
    except RuntimeError as error:
        # 読み取りに失敗することがあるため、エラーは無視して次のループへ
        print(error.args[0])
    time.sleep(2.5)

実行は、仮想環境のPythonを指定します。

~/dht/bin/python dht_test.py

手元で10回読んだ結果が、これです。

 1  温度 31.8   湿度 55
 2  温度 29.1   湿度 60
 3  温度 29.2   湿度 60
 4  温度 29.2   湿度 60
 5  温度 29.1   湿度 60
 6  温度 29.1   湿度 60
 7  温度 29.2   湿度 60
 8  温度 29.1   湿度 60
 9  温度 29.1   湿度 60
10  温度 29.1   湿度 60

成功 10 / 失敗 0 / 試行 10
POINT

1回目だけ値が飛んでいます。2回目以降は29.1〜29.2度で落ち着いているのに、1回目だけ31.8度・湿度55%でした。電源を入れた直後の1回目は捨てて、2回目から使うほうが安全です。

この記事のログを取るコードでは、その1回目も含めてCSVに書き込んでいます。グラフにしたときに最初の点だけ跳ねていたら、これを疑ってください。

MEMO — 出てくる値の細かさで、DHT11かDHT22かが分かります

上の結果をよく見ると、温度は小数第1位まで出ているのに、湿度は整数しか出ていません。これがDHT11の特徴です。DHT22なら湿度も小数第1位まで出ます。

DHT11とDHT22は見た目がよく似ています(水色がDHT11、白がDHT22と言われますが、例外もあります)。手元のものがどちらか分からなくなったら、値の細かさで見分けられます。

なお、DHTセンサーは読み取りに失敗すること(RuntimeErrorが出ること)がある仕様です。数秒待ってもう一度読めば問題ありません。今回は10回とも成功しましたが、失敗率は環境によって変わります。

一定間隔でデータをCSVに記録する

次に、一定間隔(例:10分おき)で温湿度を測定し、CSVファイルに追記していくプログラムに発展させます。

import time
import csv
import datetime
import board
import adafruit_dht

dhtDevice = adafruit_dht.DHT11(board.D17)
INTERVAL_MINUTES = 10
CSV_FILE = "ondoshitsudo_log.csv"

# ファイルが無ければヘッダー行を書き込む
with open(CSV_FILE, "a", newline="") as f:
    writer = csv.writer(f)
    if f.tell() == 0:
        writer.writerow(["日時", "気温(度)", "湿度(%)"])

while True:
    try:
        temperature = dhtDevice.temperature
        humidity = dhtDevice.humidity
        if temperature is not None and humidity is not None:
            now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            with open(CSV_FILE, "a", newline="") as f:
                writer = csv.writer(f)
                writer.writerow([now, temperature, humidity])
            print(f"{now} 記録しました: {temperature:.1f}度 / {humidity:.1f}%")
    except RuntimeError as error:
        print("読み取りエラー:", error.args[0])
    time.sleep(INTERVAL_MINUTES * 60)

このプログラムを実行したままにしておくと、10分おきの気温・湿度データがCSVファイルにたまっていきます。この記事では1時間だけ回して、8点ぶんのデータで動きを確かめました。長く回したい場合は、次の「うまくいかないときのトラブルシューティング」に、ターミナルを閉じても止まらない方法を書いています。

記録したデータをグラフにする(matplotlib)

データが集まったら、Pythonのpandasmatplotlibを使ってグラフ化します。

先に入れておくもの

Pythonのセットアップで作った仮想環境に、2つ追加します。pip3 installではなく、仮想環境のpipを使ってください。

~/dht/bin/pip install pandas matplotlib
注意

pip3 install pandas matplotlibと書いてある記事が多いですが、これもセットアップのときと同じexternally-managed-environmentで止まります。

日本語のラベルは、そのままでは表示されません

ここが、Raspberry Piでいちばん引っかかるところです。matplotlibが標準で使うフォント(DejaVu Sans)は、日本語の文字を持っていません。

フォントを指定せずに実行すると、こういう警告が大量に出ます。

UserWarning: Glyph 26085 (日) missing from font(s) DejaVu Sans.
UserWarning: Glyph 26178 (時) missing from font(s) DejaVu Sans.
  ...(「気」「温」「度」「湿」「の」「推」「移」と続く)

そして、できあがる画像がこれです。

日本語のラベルがすべて四角(豆腐)になってしまったグラフ
フォントを指定しないとこうなります。線と数字は正しいのに、タイトルも軸の名前も読めません

直し方は1行です。Raspberry Pi OSには日本語フォントが最初から入っているので、それを使うように指定します。

matplotlib.rcParams["font.family"] = "Noto Sans CJK JP"
MEMO

手元で使えるフォント名は、これで確かめられます。

~/dht/bin/python -c "from matplotlib import font_manager as fm; print(sorted({f.name for f in fm.fontManager.ttflist if 'CJK JP' in f.name}))"

この記事の環境ではNoto Sans CJK JPNoto Sans Mono CJK JPNoto Serif CJK JPの3つが出ました。1つも出ない場合はsudo apt install fonts-noto-cjkで入ります。

グラフを作るコード

import pandas as pd
import matplotlib
import matplotlib.pyplot as plt

# この1行が無いと、日本語のラベルが全部 □ になります
matplotlib.rcParams["font.family"] = "Noto Sans CJK JP"

df = pd.read_csv("ondoshitsudo_log.csv", parse_dates=["日時"])

fig, ax1 = plt.subplots(figsize=(10, 5))

ax1.plot(df["日時"], df["気温(度)"], color="tab:red", label="気温(度)")
ax1.set_xlabel("日時")
ax1.set_ylabel("気温(度)", color="tab:red")

ax2 = ax1.twinx()
ax2.plot(df["日時"], df["湿度(%)"], color="tab:blue", label="湿度(%)")
ax2.set_ylabel("湿度(%)", color="tab:blue")

plt.title("気温・湿度の推移")
fig.autofmt_xdate()
plt.tight_layout()
plt.savefig("ondoshitsudo_graph.png")

実行は、仮想環境のPythonを指定します。

~/dht/bin/python graph.py

気温(赤)と湿度(青)が1つのグラフに重なり、ondoshitsudo_graph.pngとして保存されます。この画像は、そのままレポートや記録に使えます。

日本語のラベルが正しく表示された気温・湿度の折れ線グラフ
フォントを指定したあと。実際に1時間ぶん(10分おきに8点)記録したデータです
MEMO — plt.show() を入れていない理由

解説記事では最後にplt.show()を付けることが多いのですが、この記事では外しています。SSHで接続して動かしている場合、画面が無いので何も起きません。画像として保存されていれば、あとでゆっくり見られます。

Raspberry Piの画面を直接使っているなら、plt.show()を足すとその場で表示されます。

グラフから何が読めるか

グラフができたら、次のような視点で見てみると、数字が意味を持ってきます。

  • 気温が上がると湿度はどう変化したか(逆の動きをする時間帯はあったか)
  • 1日の中で気温・湿度が一番高くなる/低くなる時間帯はいつか
  • 晴れの日と雨の日でグラフの形にどんな違いが出たか
POINT

「測って終わり」にしないことです。グラフから読み取れることを自分の言葉で書いてみると、次に何を測ればいいかが見えてきます。

うまくいかないときのトラブルシューティング

DHT sensor not found, check wiring と出続けます

「配線を確認してください」と言われても、目で見ても分かりません。電気的に確かめられます。

pinctrl get 17

DATAを繋いだピンの番号を指定します。出てくるのはこの形です。

17: ip    pu | hi // GPIO17 = input
              ↑    ↑
        プルアップ  値
  • hi(HIGH)なら、配線は生きています。待機中のDATAはHIGHです
  • lo(LOW)なら、どこかがGND側に引いています。VCCとGNDが逆VCCが挿さっていないDATAのつもりでGNDのピンを挿しているあたりを疑ってください

もう一段 絞り込むには、全部のGPIOをまとめて見ます。

pinctrl set 2-27 ip pu
pinctrl get 2-27 | grep " lo "

LOWになっているピンだけが出ます。そこが、いま何かに引かれているピンです。狙ったピン以外が出てきたら、挿す場所を間違えています。

注意

センサーを全部外してもLOWのままなら、そのGPIOが壊れています。実際にこの記事を書く途中でGPIO4を壊しました(VCCとGNDを逆に挿したためです)。その場合は別のGPIOに移してください。コードのboard.D17を書き換えるだけで動きます。

pip install が externally-managed-environment で止まります

Raspberry Pi OS (Bookworm)以降の仕様です。不具合ではありません。Pythonのセットアップをするのとおり、仮想環境を作って入れてください。

python3 -m venv --system-site-packages ~/dht
~/dht/bin/pip install adafruit-circuitpython-dht

--break-system-packagesを付ければ従来どおり入りますが、OSが管理しているPythonを壊す可能性があるので、おすすめしません。

ModuleNotFoundError: No module named ‘board’ と出ます

仮想環境の外のPythonで実行しています。python3ではなく、~/dht/bin/pythonを使ってください。

~/dht/bin/python dht_test.py
sudo apt-get install libgpiod2 が「見つかりません」になります

そのパッケージは、いまのRaspberry Pi OSにはありません。後継のlibgpiod3が最初から入っているので、この行は実行しなくてかまいません。

時々しか値が取れません
DHTセンサーは仕様上、読み取り失敗が起きやすいセンサーです。数秒おきにリトライする作りになっていれば問題ありません。手元では10回試して10回とも成功しましたが、失敗率は環境によって変わります。ただし1回目だけ値が飛ぶことは繰り返し起きたので、最初の1回は捨てるほうが安全です。
CSVに何も記録されません
プログラムを実行しているターミナルを閉じていないか確認してください。SSH接続の場合はnohupやscreenコマンドを使うと、接続を切っても記録を続けられます。

まとめ:実際に動かして分かったこと

Raspberry PiとPythonで、温湿度センサーのデータ収集からグラフ化までを実際にやりました。引っかかったのは、センサーそのものではなく、その手前と後ろでした。

  • pip3 installが止まる(PEP 668)。仮想環境を作れば通る
  • libgpiod2はもう無い。後継が最初から入っている
  • VCCとGNDを逆に挿すとGPIOが壊れる。配線はpinctrlで確かめられる
  • 日本語のグラフはそのままでは全部 □ になる。フォント指定の1行で直る

どれも、解説記事にはあまり書かれていないところです。同じところで止まった人の役に立てば幸いです。

この記事で使ったもの

Raspberry Pi 5 / Raspberry Pi OS (Trixie) / DHT11(3ピンのモジュール型) / Python 3.13.5 / adafruit-circuitpython-dht 4.0.12

ジャンパー線3本だけで動きます。ブレッドボードも抵抗も要りません。

タイトルとURLをコピーしました