Raspberry Pi × Pythonで温湿度ロガーを作る
配線からPythonコード、グラフ化までステップごとに解説します
Raspberry Piに温湿度センサー(DHT11)をつないで、気温と湿度を自動で記録し、Pythonでグラフにするまでをやります。配線からグラフまで、実際に手元で動かした手順です。
Raspberry Piと温湿度センサーを使って、気温・湿度を自動で記録し、Pythonでグラフにするところまでやります。配線からグラフまで、実際に手元のRaspberry Pi 5で動かして確かめた手順です。ログは1時間ぶん(10分おきに8点)取って、そのグラフを載せています。
- 1
- 2
- 3
- 4
- 5
- 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番ピン)
DATAはGPIO4でもかまいません。解説記事ではGPIO4がよく使われますが、決まりではなく、GPIOであればどのピンでも動きます。あとで出てくるコードのboard.D17を、使うピンの番号に書き換えるだけです。ここでGPIO17を使っているのは、9番(GND)と11番(GPIO17)が同じ列で隣り合っていて、挿し間違えにくいからです。
電源電圧(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のコピーと、これから入れるライブラリが入ります。この工作で使うものを、そこに全部まとめておく——やっているのはそれだけです。
Raspberry Pi には、最初からPythonが入っています。OSが自分で使っているPythonです。ここにライブラリを足していくと、OSの動きを壊すことがあります。
だから「この工作だけで使う、別の置き場」を作って、そこにだけ入れます。OSのほうには手を触れません。
ついでに言うと、Raspberry Pi OS がpip3 installを止めているのも、これが理由です。さっき出たexternally-managed-environmentは、「OSのPythonを壊すからやめなさい」という意味でした。
ただのフォルダなので、こういうことができます。
- 要らなくなったら、フォルダごと消せます(
rm -rf ~/dht)。OSには何も残りません - 失敗しても作り直せます。消して、もう一度作るだけです
- 別の工作をするときは、また別の置き場を作れば混ざりません
- 名前は
~/dhtでなくてもかまいません。分かりやすい名前で大丈夫です
Pythonの世界では、この置き場のことを「仮想環境」、作る道具をvenv(ヴイエンブ、ヴェンブ)と呼びます。
「仮想」と言われると身構えますが、中身はいま見たとおりのフォルダです。特別なソフトを入れる必要も、設定ファイルを書く必要もありません。
なお、これはRaspberry Piだけの話ではありません。WindowsでもMacでも、いまのPythonでは同じやり方が当たり前になってきています。ここで一度おぼえておくと、あとがずっと楽です。
この置き場を作ったあとは、python3やpipではなく、その置き場の中のものを使います。
✕ 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
--system-site-packagesを付けているのは、OSが用意しているGPIO関係のパッケージを、仮想環境の中からも見えるようにするためです。これを付けずに作ると、あとでboardの読み込みに失敗することがあります。
~/dhtというフォルダが1つできます。中身を見てみたい人はls ~/dhtで覗けます(binやlibが入っています)。
うまくいくと、最後にこう出ます。
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は手元に無いため、同じ手順で動くかどうかは確かめていません(コードのDHT11をDHT22に変えるだけのはずですが、こちらでは試していません)。
温湿度を取得するプログラムを書く
まずはセンサーの値を取得して表示する、最小限のコードです。DHT11を、GPIO17に繋いだ場合の例です(DHT22ならadafruit_dht.DHT11をadafruit_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
1回目だけ値が飛んでいます。2回目以降は29.1〜29.2度で落ち着いているのに、1回目だけ31.8度・湿度55%でした。電源を入れた直後の1回目は捨てて、2回目から使うほうが安全です。
この記事のログを取るコードでは、その1回目も含めてCSVに書き込んでいます。グラフにしたときに最初の点だけ跳ねていたら、これを疑ってください。
上の結果をよく見ると、温度は小数第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のpandasとmatplotlibを使ってグラフ化します。
先に入れておくもの
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"
手元で使えるフォント名は、これで確かめられます。
~/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 JP/Noto Sans Mono CJK JP/Noto 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として保存されます。この画像は、そのままレポートや記録に使えます。

解説記事では最後にplt.show()を付けることが多いのですが、この記事では外しています。SSHで接続して動かしている場合、画面が無いので何も起きません。画像として保存されていれば、あとでゆっくり見られます。
Raspberry Piの画面を直接使っているなら、plt.show()を足すとその場で表示されます。
グラフから何が読めるか
グラフができたら、次のような視点で見てみると、数字が意味を持ってきます。
- 気温が上がると湿度はどう変化したか(逆の動きをする時間帯はあったか)
- 1日の中で気温・湿度が一番高くなる/低くなる時間帯はいつか
- 晴れの日と雨の日でグラフの形にどんな違いが出たか
「測って終わり」にしないことです。グラフから読み取れることを自分の言葉で書いてみると、次に何を測ればいいかが見えてきます。
うまくいかないときのトラブルシューティング
「配線を確認してください」と言われても、目で見ても分かりません。電気的に確かめられます。
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を書き換えるだけで動きます。
Raspberry Pi OS (Bookworm)以降の仕様です。不具合ではありません。Pythonのセットアップをするのとおり、仮想環境を作って入れてください。
python3 -m venv --system-site-packages ~/dht
~/dht/bin/pip install adafruit-circuitpython-dht
--break-system-packagesを付ければ従来どおり入りますが、OSが管理しているPythonを壊す可能性があるので、おすすめしません。
仮想環境の外のPythonで実行しています。python3ではなく、~/dht/bin/pythonを使ってください。
~/dht/bin/python dht_test.py
そのパッケージは、いまのRaspberry Pi OSにはありません。後継のlibgpiod3が最初から入っているので、この行は実行しなくてかまいません。
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本だけで動きます。ブレッドボードも抵抗も要りません。
