防犯カメラシステムの自作⑥~ウォルボックス内環境に対する温湿度センサとファン制御の実装~
本投稿は,「防犯カメラシステム with Raspberry Pi」(←開発がすべて完了したら「開発物」にページを作成する予定)に関する投稿です.Tipsでの投稿は,内容ごとに分け,下記の通り分割して投稿してゆきます.
①Reolink社製カメラ「RLC-1212A」の挙動確認
②Raspberry PiへのPoE HATのセットアップ
③VPNを用いたリアルタイム映像視聴の環境構築
④クラウドストレージへの動画ファイルの自動アップロード
⑤クラウドストレージへの動画ファイルの自動アップロード+α
⑥ウォルボックス内環境に対する温湿度センサとファン制御の実装(本投稿)
⑦人物など動体検知時におけるスマホへのプッシュ通知機能の実装(予定)
「防犯カメラシステム with Raspberry Pi」は,ウォルボックス内にRaspberry PiやスイッチングPoEハブを格納し,屋外に設置します.そのため,外気温による各機器の熱暴走を避けるべく,ウォルボックス内の温度管理が欠かせません.
本投稿では,ウォルボックスに取り付けるファンを温湿度センサの計測結果に基づき制御することを目的とします.これに対し,電子回路の設計や,制御するプログラムの説明を行います.
使用したRaspberry Piは,Pi 4 Model B 8GBです.OSは,rasRaspberry Pi OS(64bit)において執筆時点で最新バージョン(Debian 13 Trixieがベース.Release Date: 18 Jun 2026)を用いています.
目次
- 本投稿で使用するもの
- ファン単体の回路とテストコード
- 回路
- 余談:GPIOピンからの2.5mAの出力で,ファンに120mAを流せるのか?
- テストコード
- 温湿度センサ単体の回路とテストコード
- 回路
- テストコード
- 温湿度センサに基づくファンの制御
- 完成版コード
- Raspberry Piの起動と共に自動実行
1. 本投稿で使用するもの
筆者が自作しようとしている防犯カメラシステム全体ではなく,本投稿で扱った機材のみを,以下にあげる.
- Raspberry Pi 4 Model B 8GB(Tips②[1]より,下記がセットアップ済み)
- Waveshare社製 PoE HAT(B)
- Raspberry Pi 4 Model B用のヒートシンク
- micro SDカード×1枚
- Raspberry Piに使用(64GB,class10,UHS-1/U3,最大読込100MB/s)
- ファン
- Raspberry PiのCPU向けファンとして売られているもの(5V,0.12A,ブラシレス)
- 上記は4個セットのため,排気と吸気にそれぞれ2個ずつ使用する
- トランジスタ
- 抵抗1kΩ×4個
- もし改めて抵抗を購入するならば,1kΩでなく470Ωの方がより良いかもしれない
- 温湿度センサ×1個
- Googleアカウント
- クラウドストレージとして,Google Driveを使用するために作成が必要
- Rclone(v1.60.1)
2. ファン単体の回路とテストコード
2.1 回路
使用するファンは2線式(5V電源,GND)であるため,Raspberry Piの起動/停止と連動した,ファンの稼働/停止は,Raspberry Piとファンの各ピンを直接繋ぐだけで実現可能である.しかし,Raspberry Piが稼働している最中にファンの稼働/停止の制御を行うには,トランジスタ等の電子部品が別途必要である.
トランジスタの役割は,5V電源の電流に対しそのON/FFのスイッチを行うことである.使用するトランジスタは,Raspberry PiのGPIO(3.3V)のような小さな電圧でも,スイッチの切り替えが可能である.
この第2.1節の結論として,最初にファン単体の回路図を,図1に示す.
図1に示す回路設計にした詳細を説明する前に,Raspberry PiのGPIOの各ピン配置について図2に,トランジスタ・PN2222の端子について図3に,それぞれ示す.


4個のファンに対し,1つの回路にまとめ1つのGPIOピンで制御するのではなく,それぞれ回路を分けた.理由は,主に下記3点である.
- 回路の単純化:
- 4個のファンを1つのGPIOピンで制御をする場合,ファンの電流は合計で0.48Aとなり.電流のスイッチ制御にMOSFETを用いて,さらにそのMOSFETのスイッチ制御にトランジスタとより大きな値の抵抗を用いるという構成が考えられる.これは図1に示した回路と比較すると,複雑さが増す
- 各ファンの独立制御:
- 4個のファンに対し,それぞれGPIOピンを分けることで,例えばウォルボックス内の温度に応じ「段階的に,1個から4個のファンを稼働」させたり,「2個のファンを稼働させ排気だけ行い,さらに高温になったら4個のファンを稼働させ排気と吸気」を行ったりが,可能になる
- ロバスト性:
- 回路を分けておくことで,回路にトラブルが生じた際に,それで全てのファンが停止してしまうリスクを回避する.また,原因箇所の特定と復旧においても,回路を分けておくことで特定しやすくすることを狙っている
使用するトランジスタ・PN2222に対し,ベース抵抗を1kΩに決めた理由を説明する.
まず,トランジスタのデータシートによると,VCE(sat)は,IC = 500mA,IB = 50mAの条件で規定されている[3].つまり,ベース電流はコレクタ電流の1/10である.これに対し,ファンは0.12Aであるため,IC = 0.12A,IB = 0.012Aとなる.
この電流に対し,抵抗値を決めた.
Raspberry PiのGPIOは約3.3Vである.そしてトランジスタ・PN2222のベース―エミッタ間電圧VBEにおいて,筆者は安全側に倒し約0.8Vと仮定した.これは,シリコン製BJT(バイポーラ接合トランジスタ)は,ベース電流を流すと約0.6V~約0.7Vで電流が増えるのが一般的だからである.そしてこれらより,ベース抵抗にかかる電圧は,である.オームの法則 より, となり,抵抗Rは約208Ωとなる.標準の抵抗値では220Ωが候補になる.
しかしここで,ベース抵抗に220Ωを用いた場合,Raspberry PiのGPIOピンから出力する電流は, となる.そしてRaspberry Pi 4 Model Bのデータシートより,GPIOピンは,最大ドライブ強度は16mAとされているものの,通常のデフォルトドライブ強度は8mAである[4].このRaspberry Piの仕様に対し,気温次第では長時間稼働させ続けうるファンの制御に11.4mAを流し続けることは,常時稼働させ続ける防犯カメラシステムとして良い設計とは言い難いだろう.
これに対し,例えばベース抵抗に1kΩを用いた場合,ベース電流は となる.これは,1つのGPIOピンから約2.5mA流れることを意味し,GPIOピンとして余裕のある設計である.
2.2 余談:GPIOピンからの2.5mAの出力で,ファンに120mAを流せるのか?
余談として「GPIOピンからの2.5mAの出力で,ファンに120mAを流せるのか?」について述べる.
第2.1節にて述べた通り,トランジスタ・PN2222のデータシートにて保証されているVCE(sat)は,IC = 500mA,IB = 50mAである[3].コレクタ電流ICを,ベース電流IBで割った値である,強制β = 10の条件である.
これに対し,筆者が採用した2.5mAの出力で120mAを流そうとした場合,強制β = 48 である.これは,「1kΩの場合,データシート上では,確実にスイッチをONにし120mAを流せるとは言えない」ことを意味する.
強制βの観点では,ベース抵抗に約220Ωを用いれば強制βは約10.5となり,強制β = 10に近づけることができる.他方でGPIOピンのドライブ強度の観点では,扱う電流とその負荷が大きくなる.このように,「1kΩを用いた場合,GPIOピンには優しいが,トランジスタ・PN2222を確実にONにできるかデータシート上の保証は無い」というトレードオフのなかで,筆者は1kΩを採用した.
筆者はもともと手元に,100・1k・10k・100kΩの抵抗を所持していたため,その中から1kΩを採用した.もし改めて抵抗を購入するならば,上記のドライブ強度と強制βのトレードオフを考慮し,折衷案として470Ωがより良い候補になると思われる(ただし,筆者は470Ωを所持しておらず,その動作確認は実地に行っていない).
2.3 テストコード
図1に示した回路の動作確認を行うコードを,以下に示す.以下のコードは,Pythonを用いて記述した.実行すると,ファンを5秒間稼働し,その後5秒間停止する,これらを交互に繰り返し続ける.
# //////////////////////////////////////////////////
# ライブラリ
# //////////////////////////////////////////////////
from gpiozero import DigitalOutputDevice # GPIOをPythonで扱うために使用
from time import sleep # ファンのON/OFFの切り替え確認を時間で行うために使用
# //////////////////////////////////////////////////
# 固定値/変数の宣言
# //////////////////////////////////////////////////
# PINの割り当て
FAN1_PIN = 17
FAN2_PIN = 27
FAN3_PIN = 5
FAN4_PIN = 6
# PINの初期化
fan1 = DigitalOutputDevice(FAN1_PIN)
fan2 = DigitalOutputDevice(FAN2_PIN)
fan3 = DigitalOutputDevice(FAN3_PIN)
fan4 = DigitalOutputDevice(FAN4_PIN)
# //////////////////////////////////////////////////
# Main
# //////////////////////////////////////////////////
try:
while True:
fan1.on()
fan2.on()
fan3.on()
fan4.on()
sleep(5)
fan1.off()
fan2.off()
fan3.off()
fan4.off()
sleep(5)
except KeyboardInterrupt:
pass
finally:
fan.off()
input_signal.close()
fan.close()
3. 温湿度センサ単体の回路とテストコード
この第3章では,第2章で前述したファンと組み合わせる前に,まずシンプルな構成として温湿度センサ単体の回路や動作の確認を行う.
3.1 回路
この第3.1節の結論として,最初に温湿度センサ単体の回路図を,図4に示す.また,使用した温湿度センサ・KP-DHT20の端子について,図5にしめす.

温湿度センサ・KP-DHT20のデータシート[5]より,温湿度をセンシングするための穴が空いている面から見て,左から順に,表1のようにRaspberry Piと接続する.
| 温湿度センサの端子と役割 | Raspberry Piとの接続PIN |
|---|---|
| VDD | 3.3V電源(PIN1) |
| SDA | GPIO2(SDA)(PIN3) |
| GND | GND(PIN6など) |
| SCL | GPIO3(SCL)(PIN5) |
温湿度センサ・KP-DHT20は,I2C通信を使用するため,予めRaspberry PiのI2Cを有効化されたし.
3.2 テストコード
図4に示した回路の動作確認を行うコードを,以下に示す.以下のコードは,Pythonを用いて記述した.実行すると,1度だけ温度と湿度を測定し,コンソールに出力したあと終了する.
# //////////////////////////////////////////////////
# ライブラリ
# //////////////////////////////////////////////////
import smbus2 # I2C通信に使用
import time # 温湿度センサが計測完了するまでの待ち時間を設けるために使用
# //////////////////////////////////////////////////
# 固定値
# //////////////////////////////////////////////////
# 温湿度線センサの割り当て
I2C_BUS = 1 # 使用するI2Cバスの番号
I2C_ADDRESS = 0x38 # デバイスのアドレス
MEASURE_COMMAND = [0xAC, 0x33, 0x00] # 測定開始コマンド
# //////////////////////////////////////////////////
# Main
# //////////////////////////////////////////////////
i2c = smbus2.SMBus(I2C_BUS) # I2Cのバスを開く
# センサに測定開始コマンドの送信
write = smbus2.i2c_msg.write(I2C_ADDRESS, MEASURE_COMMAND)
i2c.i2c_rdwr(write)
# 測定完了まで待機
time.sleep(0.08)
# 1回分の測定データ(7バイト)を読み込む
read = smbus2.i2c_msg.read(I2C_ADDRESS, 7)
i2c.i2c_rdwr(read)
data = list(read)
# 7バイトから湿度データの20bitを抽出
humidity_raw = (
(data[1] << 12)
| (data[2] << 4)
| ((data[3] & 0xF0) >> 4)
)
humidity = humidity_raw / 2**20 * 100 # bitから湿度表記へ変換
# 7バイトから温度データの20bitを抽出
temperature_raw = (
((data[3] & 0x0F) << 16)
| (data[4] << 8)
| data[5]
)
temperature = temperature_raw / 2**20 * 200 - 50 # bitから温度表記へ変換
# 結果の表示
print(f"温度: {temperature:.1f} ℃")
print(f"湿度: {humidity:.1f} %")
# I2Cバスを閉じる
i2c.close()
上記のコードにおける測定データとbitの説明を行う.各bitの内容を,温湿度センサ・KP-DHT20のデータシート[5]より引用し図6に示す.測定データは,state~CRC dataまでの7バイトで構成されている.処理の内容を説明する.
湿度の測定データは,2バイト目(コード中のdata[1])から4バイト目(data[3])の前半4bitまでに格納されている.これらを1つにまとめて,変数humidity_rawに格納する.
また,温度の測定データは,4バイト目(data[3])の後半4bitから6バイト目(data[5])の前半4bitまでに格納されている.これらを1つにまとめて,変数temperature_rawに格納する.
これらbit表記から10進数表記に変換について,変換式がデータシート[5]に記載されている.湿度の変換式は,である.そして温度の変換式は,である.特に2^20で割っているのは,合計20bitであるため,2^20で割ることで,およそ0~1の範囲の整数値へといったん正規化している.

4. 温湿度センサに基づくファンの制御
温度センサで取得した温度に基づき,ファンの制御を行うPythonコードを,第4.1節に示す.このコードの特徴を,以下に記述する.ファイル名は,FanController.pyとした.
- 格納ディレクトリ/ファイル名:
- ディレクトリ:/home/<user_name>/SecurityCamera/FanController/FanController.py
- ファイル名:FanController.py
- 5秒間隔で温湿度の計測と,温度に応じたファンのON/OFFの制御を行う
- 温湿度の計測後に,データが破損していないか,CRCチェックを行う
- 計測データが正常で無い場合は,熱暴走などを防ぐべく安全側に倒しファンをONにする
- 計測結果は,CSVファイルとしてRaspberry Pi上のストレージに出力する
- 保存先のディレクトリ:/home/<user_name>/SecurityCamera/logTemperature
4.1 完成版コード
"""FanControler.pyの概要
温湿度センサ・KP-DHT20で計測した温度に基づき,ファンの制御を行うプログラム
2026.8.30 YANAKA Shunsuke
"""
# //////////////////////////////////////////////////
# ライブラリ
# //////////////////////////////////////////////////
from gpiozero import DigitalOutputDevice # GPIOをPythonで扱うために使用
import time # 温湿度の取得とファン制御の周期[秒]の設定に使用
import smbus2 # I2C通信を用いるために使用
import csv # CSV出力に使用
import os # ディレクトリ・パスの操作に使用
from datetime import datetime # タイムスタンプの取得に使用
# //////////////////////////////////////////////////
# 固定値/変数の宣言
# //////////////////////////////////////////////////
# ファン関連
FAN1_PIN = 17
FAN2_PIN = 27
FAN3_PIN = 5
FAN4_PIN = 6
fan1 = DigitalOutputDevice(FAN1_PIN)
fan2 = DigitalOutputDevice(FAN2_PIN)
fan3 = DigitalOutputDevice(FAN3_PIN)
fan4 = DigitalOutputDevice(FAN4_PIN)
FAN_ON_TEMPERATURE = 30
FAN_OFF_TEMPERATURE = 28
# 温湿度線センサ関連
I2C_BUS = 1 # 使用するI2Cバスの番号
I2C_ADDRESS = 0x38 # デバイスのアドレス
i2c = smbus2.SMBus(I2C_BUS) # I2Cのバスを開く
MEASURE_COMMAND = [0xAC, 0x33, 0x00] # 測定開始コマンド
CRC_INITIAL = 0xFF # CRC-8の初期値
CRC_POLYNOMIAL = 0x31 # CRC-8の多項式
# CSV出力関連
LOG_DIR = "/home/<user_name>/SecurityCamera/logTemperature"
CONTROL_INTERVAL = 5 # 温湿度の取得とファン制御を行う時間間隔[秒]
# //////////////////////////////////////////////////
# 関数:温湿度の測定
# //////////////////////////////////////////////////
def read_temperature_and_humidity():
"""関数read_temperature_and_humidity
温湿度センサから温度と湿度を取得する
Returns:
float, float: 温度, 湿度(取得失敗時は,それぞれNone型)
"""
# センサに測定開始コマンドの送信
write = smbus2.i2c_msg.write(I2C_ADDRESS, MEASURE_COMMAND)
i2c.i2c_rdwr(write)
# 測定完了までの待機と完了確認
time.sleep(0.1) # 測定完了までの待機時間
status_read = smbus2.i2c_msg.read(I2C_ADDRESS, 1)
i2c.i2c_rdwr(status_read)
status = list(status_read)[0] # Busy bit(0 = 測定完了,1 = 測定中)
if (status & 0x80) == 1:
print("CRCエラー")
return None
# 1回分の測定データ(7バイト)を読み込む
read = smbus2.i2c_msg.read(I2C_ADDRESS, 7)
i2c.i2c_rdwr(read)
data = list(read)
# CRCチェック
crc = CRC_INITIAL
for byte in data[:6]:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = ((crc << 1) ^ CRC_POLYNOMIAL) & CRC_INITIAL
else:
crc = (crc << 1) & CRC_INITIAL
if crc != data[6]: # 計算したCRCとセンサから受信したCRCを比較
return None
# 7バイトから湿度データの20bitを抽出
humidity_raw = (
(data[1] << 12)
| (data[2] << 4)
| ((data[3] & 0xF0) >> 4)
)
humidity = humidity_raw / 2**20 * 100 # bitから湿度表記へ変換
# 7バイトから温度データの20bitを抽出
temperature_raw = (
((data[3] & 0x0F) << 16)
| (data[4] << 8)
| data[5]
)
temperature = temperature_raw / 2**20 * 200 - 50 # bitから温度表記へ変換
return temperature, humidity
# //////////////////////////////////////////////////
# 関数:ファンの制御
# //////////////////////////////////////////////////
def control_fan(temperature):
"""関数control_fan
温度に応じてファンを制御する
Args:
temperature(float): 温度。センサからの温度取得が失敗した場合はNone型
"""
if temperature is not None:
if temperature >= FAN_ON_TEMPERATURE:
fan1.on()
fan2.on()
fan3.on()
fan4.on()
elif temperature <= FAN_OFF_TEMPERATURE:
fan1.off()
fan2.off()
fan3.off()
fan4.off()
else: # フェイルセーフ(温度を取得できなかった場合は,安全側に倒し,ファンを稼働させる)
fan1.off()
fan2.off()
fan3.off()
fan4.off()
# //////////////////////////////////////////////////
# 関数:CSV出力
# //////////////////////////////////////////////////
def write_csv(timestamp, temperature, humidity):
"""関数write_csv
温湿度の値を日付ごとのCSVファイルへ出力
Args:
timestamp(string):タイムスタンプ
temperature(float):温度
humidity_percent(float):湿度
"""
os.makedirs(LOG_DIR, exist_ok=True) # 保存ディレクトリを作成
filename = timestamp.strftime("%Y-%m-%d.csv")
filepath = os.path.join(LOG_DIR, filename)
file_exists = os.path.exists(filepath) # 新規ファイル作成が必要かを確認
# CSVへ追記
with open(filepath, "a", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
#新規ファイルだった場合,ラベルを作成
if not file_exists:
writer.writerow(["日時", "温度[℃]", "湿度[%]"])
if temperature is not None:
temperature_value = f"{temperature:.2f}"
humidity_value = f"{humidity:.2f}"
# 結果の表示
print(timestamp.strftime("%Y-%m-%d %H:%M:%S"))
print(f"温度: {temperature:.1f} ℃")
print(f"湿度: {humidity:.1f} %")
print("")
else:
temperature_value = ""
humidity_value = ""
writer.writerow([timestamp.strftime("%Y-%m-%d %H:%M:%S"), temperature_value, humidity_value])
file.flush() # 書き込み
# //////////////////////////////////////////////////
# Main
# //////////////////////////////////////////////////
def main():
try:
while True:
timestamp = datetime.now() # タイムスタンプの取得
temperature, humidity = read_temperature_and_humidity() # 温湿度の取得
control_fan(temperature) # ファンの制御
write_csv(timestamp, temperature, humidity) # CSV出力
time.sleep(CONTROL_INTERVAL)
finally: # プログラム終了時
fan1.off()
fan2.off()
fan3.off()
fan4.off()
i2c.close()
if __name__ == "__main__":
main()
4.2 Raspberry Piの起動と共に自動実行
第4.1節にて示した完成版のPythonコードを,Raspberry Piの起動と共に自動実行されるようにする.まずは,下記コマンドにより,.serviceファイルの作成と共にその編集画面を起動する.
sudo nano /etc/systemd/system/FanController.service
作成した.serviceファイルに,下記内容を書き込む.Restart=alwaysおよびRestartSec=5とすることによって,何らかの理由でPythonコードが終了した場合,5秒待ってから再びPythonプログラムを起動するようにしている.
[Unit]
Description=Measures the Temperature and Humidity Inside the Weatherproof box and Controls the Fans
After=multi-user.target
[Service]
Type=simple
User=<user_name>
ExecStart=/usr/bin/python3 /home/<user_name>/SecurityCamera/FanController/FanController.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
.serviceファイルの編集が終わったら,下記コマンドにより,サービスへの認識,自動起動の有効化,今すぐ起動を実行する.
sudo systemctl daemon-reload
sudo systemctl enable --now FanController.service
参考文献
- 防犯カメラシステムの自作②~Raspberry PiへのPoE HATのセットアップ~, word in the world, https://word-in-the-world.com/2026/08/02/securitycamera-02/, (参照2026-8-27).
- App Notes, Guides, & Whitepapers – General Product Information – Product Information Portal – Raspberry Pi, Raspberry Pi, https://pip.raspberrypi.com/categories/685-app-notes-guides-whitepapers, (参照2026-8-27).
- トランジスター PN2222 30V600mA: 半導体 秋月電子通商-電子部品・ネット通販, 秋月電子通商, https://akizukidenshi.com/catalog/g/g109406/, (参照2026-8-27).
- Raspberry Pi 4 Model B – Computers – Product Information Portal – Raspberry Pi, Raspberry Pi, https://pip.raspberrypi.com/categories/545-raspberry-pi-4-model-b, (参照2026-8-27).
- デジタル温湿度センサー/KP-DHT20/4900474027502/共立プロダクツ事業所, 共立プロダクツ事業所, https://prod.kyohritsu.com/KP-DHT20.html, (参照2026-8-27).

