老け込むには早すぎる。
最近、防災グッズを買って安心している、自分では作れない、難しそう、そんな大人が増えている。
原因は年齢じゃない。
“挑戦する神経が眠っているんだ”
表示付き防災システムは、ただの電子工作じゃない。

水位を監視する、温度を監視する、異常を表示する、危険を知らせる、家族の安心を24時間見守る。つまり、「眠った発明家魂を呼び覚ます家庭用防災基地」だ。
一つずつ機能を追加するたびに、止まっていた神経が目を覚ます。
未来の家は、買うものではない。
自分で創るものだ。
老けるな。神経を止めるな。
人生はアフターファイブから始まる。
※当サイトは、アフィリエイトプログラムを利用して商品を紹介しています。
情報の見える化
前回は、家に目を作れ イケオジ流!小さなディスプレイが命を守るRaspberry Piで防災情報をリアルタイム を紹介しました。
今回は、以前紹介した 家一軒、AIが守る時代へ イケオジ流!Raspberry Piで作る究極のオールインワン防災システム に、前回紹介した小型ディスプレイを追加して“情報の見える化”を実現します。
センサーが取得したデータやカメラの状態を画面にリアルタイム表示できるようになり、災害時の状況確認や防犯監視がより直感的に。家庭でも設置しやすく、実用性がさらに高まった進化型の防災IoT電子工作を紹介します。
必要なもの
- Raspberry Pi
- SSD1306 OLEDディスプレイ
- 超音波センサー HC-SR04
- 温度センサー ADT7410
- Raspberry Piカメラ Raspberry Pi Camera Module 3
- 専用カメラケーブル(Raspberry Pi 5対応)
- 抵抗(2.2kΩ、3.3kΩ:ECHOピンの分圧用)
- ジャンパーワイヤー(オス-メス)
- ブレッドボード
最初の一歩が未来を変える イケオジ流!ラズベリーパイ:本体・モニター・センサー類の部品リスト
システム構成の概要
この電子工作は、2つのPythonプログラムで構成されています。
| ファイル名 | 実行環境 | 役割 |
|---|---|---|
shikoshikosys.py | 仮想環境外 | センサー測定・カメラ撮影・データ保存 |
shikoshikosys2.py | 仮想環境内 | 測定結果を画像表示 |
システムの動作イメージ
- 超音波センサーで水位を計測
- 温度センサー(ADT7410)で気温を取得
- カメラで撮影し、
~/picturesフォルダに保存 - 測定データを
~/sensor_data.txtに保存 - 仮想環境内プログラムがファイルを監視して画面更新
事前準備(ハードウェア・配線の確認)
1) HC-SR04(超音波)配線 — 重要:ECHOは5V → GPIOは3.3V
- HC-SR04 VCC → 5V(Pin 2)
- HC-SR04 GND → GND(Pin 6)
- TRIG → GPIO24(Pin 18)
- ECHO → GPIO23(Pin 16) に 分圧して接続、GND(Pin 9)
分圧(ECHO → 約3.0V):上側 2.2kΩ、下側 3.3kΩ(ECHO → 2.2k → 分岐 → 3.3k → GND、分岐 → GPIO23)

2) ADT7410(I2C温度)配線
- VCC → 3.3V(Pin 1)
- GND → GND(Pin 6)
- SCL → GPIO3 (Pin 5)
- SDA → GPIO2 (Pin 3)
ADT7410は3.3V動作。I2Cバスに複数デバイスをつなげられます。アドレスは 0x48(デフォルト)など — i2cdetectで確認できます。
3) SSD1306(OLEDディスプレイ)配線(I2C)
- GND → GND ADT7410と共存(Pin 6)
- VCC → 3.3V ADT7410と共存(Pin 1)
- SCL → GPIO3 ADT7410と共存(Pin 5)
- SDA → GPIO2 / Pin3 ADT7410と共存(Pin 3)
ADT7410 と SSD1306 は同じ I2C バス(SDA/SCL)に共存できます。アドレスは一般に 0x3C または 0x3D。
4) カメラ
- カメラを CSIコネクタ に確実に差し込む(Pi4/5はケーブル形状に注意)
- ケーブルの向き・ロックを確認

Raspberry pi本体 GPIOピンの配置
3.3V (1) (2) 5V
GPIO2 (3) (4) 5V
GPIO3 (5) (6) GND
GPIO4 (7) (8) GPIO14
GND (9) (10) GPIO15
GPIO17(11)(12) GPIO18
GPIO27(13)(14) GND
GPIO22(15)(16) GPIO23
3.3V (17)(18) GPIO24
GPIO10(19)(20) GND
GPIO9 (21)(22) GPIO25
GPIO11(23)(24) GPIO8
GND (25)(26) GPIO7
ID_SD (27)(28) ID_SC
GPIO5 (29)(30) GND
GPIO6 (31)(32) GPIO12
GPIO13(33)(34) GND
GPIO19(35)(36) GPIO16
GPIO26(37)(38) GPIO20
GND (39)(40) GPIO21
ソフトウェア準備
① システムのアップデート
まずはRaspberry Pi OSを最新の状態にします。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。
以下を実行してください。
sudo apt update
sudo apt full-upgrade -y
sudo reboot
再起動後、OSが最新状態になります。
これを怠ると、PythonライブラリやI2C通信がうまく動作しない場合があります。
② I2C機能を有効化する
温度センサー(ADT7410)やOLEDディスプレイ(SSD1306)はI2C通信で動作します。
そのため、I2C機能をONにします。
設定手順
ターミナルを開きます。Ctrl + Alt + T を同時に押します。
以下を実行してください。
sudo raspi-config
以下の順で選択します。
Interface OptionsI2C- 「Yes」を選択して有効化
- 設定を終了し、再起動


③ I2C接続デバイスの確認
再起動後、I2Cデバイスが正しく認識されているかを確認します。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。以下を実行してください。
sudo apt install -y i2c-tools
sudo i2cdetect -y 1
出力例
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- 3c -- -- -- --
40: 48 -- -- -- -- -- -- -- -- -- -- --
ここで
0x3C→ SSD1306 OLEDディスプレイ0x48→ ADT7410 温度センサー
が見えていれば接続成功です!

ご不明な点がございましたら、 こちら をご参照ください。
④ 日本語フォントのインストール
OLEDディスプレイに日本語文字(例:「観測中」「温度」「水位」など)を表示するには、日本語フォント(Takaoフォント)が必要です。(仮想環境外でもOK)
ターミナルを開きます。Ctrl + Alt + T を同時に押す。
sudo apt install fonts-takao
フォントをインストールしておかないと、文字化けや空白になることがあります。
⑤ OLEDディスプレイ用ライブラリ「luma.oled」の導入
仮想環境を使うことで、Pythonのモジュール管理が簡単&安全になります。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。
以下を入力します。
# 仮想環境を作成(ホームディレクトリ内)
python3 -m venv ~/myenv
# 仮想環境を有効化
source ~/myenv/bin/activate
→ プロンプトの先頭に (myenv) と表示されればOK!

次に、必要なモジュールをインストールします。
pip install --upgrade pip setuptools wheel
pip install luma.oled
仮想環境が有効になっている状態で、以下を実行します。
pip show luma.oled
これで Name: luma.oled と表示されれば、インストール成功です。
ご不明な点がございましたら、災害時も一目で状況把握:小さなディスプレイに情報を表示しよう! をご参照ください。
Pythonプログラム
① 仮想環境外で動作(センサー+カメラ)
新しいPythonファイルを作ります。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。以下を入力します。
ファイル名:shikoshikosys.py
nano shikoshikosys.py
nanoエディタで以下のプログラムを入力します。
このスクリプトでは、超音波センサーと温度センサーでデータを取得し、カメラで定期的に撮影します。
撮影した画像とデータは、~/picturesフォルダとsensor_data.txtに保存されます。
# shikoshikosys.py(仮想環境外で実行)
from gpiozero import DistanceSensor
import smbus2
import threading
from time import sleep
from datetime import datetime
from picamera2 import Picamera2
import os
import tkinter as tk
from PIL import Image, ImageTk
# ---------------- 設定 ----------------
MAX_DISTANCE = 5.0
sensor = DistanceSensor(echo=23, trigger=24, max_distance=MAX_DISTANCE)
I2C_ADDR = 0x48
I2C_CH = 1
bus = smbus2.SMBus(I2C_CH)
camera = Picamera2()
camera.configure(camera.create_still_configuration())
SAVE_DIR = os.path.expanduser("~/pictures")
os.makedirs(SAVE_DIR, exist_ok=True)
# データ共有ファイル(仮想環境内と共有する場合)
SHARED_PATH = os.path.expanduser("~/sensor_data.txt")
TMP_PATH = SHARED_PATH + ".tmp"
def write_shared_data(distance_cm, temp_c):
"""原子操作で書き込み"""
try:
with open(TMP_PATH, "w") as f:
f.write(f"{distance_cm:.1f},{temp_c:.2f}\n")
os.replace(TMP_PATH, SHARED_PATH)
except Exception as e:
print("データ書き込みエラー:", e)
def capture_image():
"""撮影してファイルに保存し、パスを返す"""
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = os.path.join(SAVE_DIR, f"capture_{timestamp}.jpg")
try:
camera.start()
sleep(1)
camera.capture_file(filename)
camera.stop()
print(f" 撮影: {filename}")
return filename
except Exception as e:
print("カメラ撮影エラー:", e)
return None
def read_temperature():
try:
data = bus.read_i2c_block_data(I2C_ADDR, 0x00, 2)
raw = (data[0] << 8) | data[1]
raw = raw >> 3
if raw & 0x1000:
raw -= 1 << 13
return raw * 0.0625
except Exception as e:
print("通信エラー:", e)
return None
# ---------------- GUI設定 ----------------
root = tk.Tk()
root.title("センサー&カメラ")
root.geometry("600x500")
label = tk.Label(root, text="測定中...", font=("Helvetica", 16))
label.pack(pady=10)
image_label = tk.Label(root)
image_label.pack(pady=10)
# ---------------- 測定スレッド ----------------
def measure_loop():
while True:
distances, temps = [], []
for _ in range(10):
actual_distance = sensor.distance * MAX_DISTANCE
reversed_distance = max(0.0, MAX_DISTANCE - actual_distance)
distances.append(reversed_distance)
temp = read_temperature()
if temp is not None:
temps.append(temp)
sleep(1)
avg_distance = sum(distances) / len(distances)
avg_temp = sum(temps) / len(temps) if temps else 0.0
distance_cm = avg_distance * 100.0
# データ保存
write_shared_data(distance_cm, avg_temp)
# 撮影してファイル名取得
filename = capture_image()
# テキスト更新
text = f"️水位: {distance_cm:.1f} cm\n️温度: {avg_temp:.2f} ℃"
label.after(0, lambda t=text: label.config(text=t))
# 撮影画像をGUIに反映
if filename and os.path.exists(filename):
def update_image():
img = Image.open(filename)
img = img.resize((400, 300))
tk_img = ImageTk.PhotoImage(img)
image_label.config(image=tk_img)
image_label.image = tk_img # 参照保持
image_label.after(0, update_image)
# ---------------- 実行開始 ----------------
threading.Thread(target=measure_loop, daemon=True).start()
root.mainloop()

保存は Ctrl + O → Enter、終了は Ctrl + X です。
② 仮想環境内で動作(GUI表示)
ファイル名:shikoshikosys2.py
こちらは、仮想環境内で動作するGUIプログラムです。shikoshikosys.pyが生成したsensor_data.txtを読み込み、最新の水位と温度を表示します。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。以下を入力します。
nano shikoshikosys.py
nanoエディタで以下のプログラムを入力します。
# shikoshikosys2.py (仮想環境内で実行)
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from PIL import Image, ImageDraw, ImageFont
import time
import os
SHARED_PATH = os.path.expanduser("~/sensor_data.txt")
I2C_ADDR = 0x3C #環境によって0x3Cか0x3Dを確認
FONT_PATH = "/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf"
# OLED 初期化
serial = i2c(port=1, address=I2C_ADDR)
device = ssd1306(serial)
# フォントの読み込み(無ければデフォルト)
try:
font_small = ImageFont.truetype(FONT_PATH, 12)
font_large = ImageFont.truetype(FONT_PATH, 16)
except Exception:
font_small = ImageFont.load_default()
font_large = ImageFont.load_default()
def read_shared_data():
""" sensor_data.txt の読み取り。フォーマット: distance_cm,temp_c """
try:
with open(SHARED_PATH, "r") as f:
line = f.readline().strip()
if not line:
return None, None
parts = line.split(",")
if len(parts) >= 2:
distance = float(parts[0])
temp = float(parts[1])
return distance, temp
except FileNotFoundError:
return None, None
except Exception as e:
print("読み取りエラー:", e)
return None, None
def draw_display(distance_cm, temp_c):
img = Image.new("1", device.size, "black")
draw = ImageDraw.Draw(img)
# ヘッダ
draw.text((2, 0), "観測中", font=font_large, fill=255)
# データ部分
if distance_cm is not None:
draw.text((2, 20), f"水位: {distance_cm:.1f} cm", font=font_small, fill=255)
else:
draw.text((2, 20), "水位: ---", font=font_small, fill=255)
if temp_c is not None:
draw.text((2, 36), f"温度: {temp_c:.2f} ℃", font=font_small, fill=255)
else:
draw.text((2, 36), "温度: ---", font=font_small, fill=255)
# 時刻
draw.text((80, 50), time.strftime("%H:%M:%S"), font=font_small, fill=255)
device.display(img)
def main_loop():
while True:
distance, temp = read_shared_data()
draw_display(distance, temp)
time.sleep(2) # 表示更新間隔(任意)
if __name__ == "__main__":
print("OLED 表示開始")
main_loop()

保存は Ctrl + O → Enter、終了は Ctrl + X です。
プログラムの実行
センサー側の実行(仮想環境外)
センサーやGPIO、カメラなどを扱うPythonスクリプトは、仮想環境外で実行する方が安定します。
センサー制御スクリプトを実行します。ターミナルを開きます。Ctrl + Alt + T を同時に押す。
以下を入力します。
python shikoshikosys.py
動作内容(例)
- HC-SR04:距離を測定して表示(cm単位)
- ADT7410:温度を読み取って表示(℃)
- Raspberry Piカメラ:撮影テスト・プレビュー
- 測定データは一時ファイル(例:
/tmp/sensor_data.txt)に保存されます
動作中のターミナル例
水位: 463.3 cm
温度: 24.59 °C
カメラ: Captured image_001.jpg

表示側の実行(仮想環境内)
OLEDディスプレイ制御では luma.oled ライブラリを使います。
これは仮想環境にインストールされているため、仮想環境を有効にして実行します。
実行コマンド
source ~/myenv/bin/activate
python shikoshikosys2.py
動作内容(例)
OLEDには仮想環境外と同じ情報が表示されます。
水位: 463.3cm
温度: 24.59℃
データは数秒ごとに更新され、センサー側で取得した内容がリアルタイムで表示されます。

動作イメージ
- センサー側:定期的に
/tmp/sensor_data.txtに測定データを書き込み - 表示側:そのファイルを読み取り、OLEDに表示
- 双方が連携して動作することで、「測定 → 表示 → 更新」のサイクルが実現します。
トラブルシューティング
| 現象 | 原因 | 対処方法 |
|---|---|---|
| OLEDが真っ黒のまま | 仮想環境が有効でない | source ~/myenv/bin/activate で有効化 |
| I2Cエラー | I2Cが無効 / 配線ミス | sudo raspi-config → Interface Options → I2C を「Yes」 |
| Cameraエラー | Camera設定未有効 / 接続不良 | libcamera-hello でカメラ単体動作を確認 |
| PermissionError (GPIO) | sudoなしでGPIO操作 | 一時的に sudo python3 sensor_and_camera.py を試す |
Raspberry Piにディスプレイを追加することで、災害監視の精度と使いやすさが大幅に向上しました。豪雨時の水位変化を確認したり、気温上昇による熱中症リスクを把握したり、カメラで防犯を行ったりと、1台で多機能な防災・防犯システムが完成します。
メール送信する
shikoshikosys.py (仮想環境外で実行)コードに水位が30cm以上(危険水位)になったときのみメールを送信するようにしたコードです。その他の処理(GUI表示・撮影・温度計測)はそのままです。shikoshikosys2.py (仮想環境内で実行)もそのまま使用します。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。
ターミナルで nano shikoshikosysmail.py と入力し、新しいファイルを作成。
nano shikoshikosysmail.py
次に、以下のプログラムを入力します。LolipopレンタルサーバーのSMTPを使ったメール送信例です。
# shikoshikosysmail.py (仮想環境外で実行)
from gpiozero import DistanceSensor
import smbus2
import threading
from time import sleep
from datetime import datetime
from picamera2 import Picamera2
import os
import tkinter as tk
from PIL import Image, ImageTk
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.utils import formatdate
from email.mime.base import MIMEBase
from email import encoders
# ---------------- 設定 ----------------
MAX_DISTANCE = 5.0
sensor = DistanceSensor(echo=23, trigger=24, max_distance=MAX_DISTANCE)
I2C_ADDR = 0x48
I2C_CH = 1
bus = smbus2.SMBus(I2C_CH)
camera = Picamera2()
camera.configure(camera.create_still_configuration())
SAVE_DIR = os.path.expanduser("~/pictures")
os.makedirs(SAVE_DIR, exist_ok=True)
# データ共有ファイル
SHARED_PATH = os.path.expanduser("~/sensor_data.txt")
TMP_PATH = SHARED_PATH + ".tmp"
# ---------------- メール設定 ----------------
SMTP_SERVER = "smtp.lolipop.jp"
SMTP_PORT = 465
EMAIL_ADDRESS = "メールアドレス" # 送信元(あなたのメールアドレス)
EMAIL_PASSWORD = "ここにメールパスワードを入力" # 注意:絶対に公開しない!
TO_ADDRESS = "受信者のメールアドレス" # 送信先(自分や家族など)
# 水位メール送信のしきい値(cm)
SNOW_THRESHOLD = 30.0
def send_email(distance_cm, temp_c, image_path=None):
"""水位データと温度をメール送信"""
try:
subject = f"水位観測アラート! {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
body = f"現在の水位: {distance_cm:.1f} cm\n温度: {temp_c:.2f} ℃\n\n30cmを超えました。ご確認ください。"
msg = MIMEMultipart()
msg["From"] = EMAIL_ADDRESS
msg["To"] = TO_ADDRESS
msg["Date"] = formatdate(localtime=True)
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain", "utf-8"))
# 添付画像
if image_path and os.path.exists(image_path):
with open(image_path, "rb") as f:
part = MIMEBase("image", "jpeg")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f'attachment; filename="{os.path.basename(image_path)}"',
)
msg.attach(part)
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as smtp:
smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
smtp.send_message(msg)
print("メール送信成功:", subject)
except Exception as e:
print("メール送信エラー:", e)
def write_shared_data(distance_cm, temp_c):
"""原子操作で書き込み"""
try:
with open(TMP_PATH, "w") as f:
f.write(f"{distance_cm:.1f},{temp_c:.2f}\n")
os.replace(TMP_PATH, SHARED_PATH)
except Exception as e:
print("データ書き込みエラー:", e)
def capture_image():
"""撮影してファイルに保存し、パスを返す"""
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = os.path.join(SAVE_DIR, f"capture_{timestamp}.jpg")
try:
camera.start()
sleep(1)
camera.capture_file(filename)
camera.stop()
print(f" 撮影: {filename}")
return filename
except Exception as e:
print("カメラ撮影エラー:", e)
return None
def read_temperature():
try:
data = bus.read_i2c_block_data(I2C_ADDR, 0x00, 2)
raw = (data[0] << 8) | data[1]
raw = raw >> 3
if raw & 0x1000:
raw -= 1 << 13
return raw * 0.0625
except Exception as e:
print("通信エラー:", e)
return None
# ---------------- GUI設定 ----------------
root = tk.Tk()
root.title("センサー&カメラ")
root.geometry("600x500")
label = tk.Label(root, text="測定中...", font=("Helvetica", 16))
label.pack(pady=10)
image_label = tk.Label(root)
image_label.pack(pady=10)
# ---------------- 測定スレッド ----------------
def measure_loop():
while True:
distances, temps = [], []
for _ in range(10):
actual_distance = sensor.distance * MAX_DISTANCE
reversed_distance = max(0.0, MAX_DISTANCE - actual_distance)
distances.append(reversed_distance)
temp = read_temperature()
if temp is not None:
temps.append(temp)
sleep(1)
avg_distance = sum(distances) / len(distances)
avg_temp = sum(temps) / len(temps) if temps else 0.0
distance_cm = avg_distance * 100.0
# データ保存
write_shared_data(distance_cm, avg_temp)
# 撮影
filename = capture_image()
# GUI更新
text = f"️水位: {distance_cm:.1f} cm\n️温度: {avg_temp:.2f} ℃"
label.after(0, lambda t=text: label.config(text=t))
if filename and os.path.exists(filename):
def update_image():
img = Image.open(filename)
img = img.resize((400, 300))
tk_img = ImageTk.PhotoImage(img)
image_label.config(image=tk_img)
image_label.image = tk_img
image_label.after(0, update_image)
# ----------------
# 水位30cm以上でのみメール送信
# ----------------
if distance_cm >= SNOW_THRESHOLD:
print(f"水位 {distance_cm:.1f}cm (しきい値 {SNOW_THRESHOLD}cm 以上)メール送信!")
send_email(distance_cm, avg_temp, filename)
else:
print(f"水位 {distance_cm:.1f}cm(しきい値未満)メール送信なし")
# 測定間隔(例: 1分ごと)
sleep(60)
# ---------------- 実行開始 ----------------
threading.Thread(target=measure_loop, daemon=True).start()
root.mainloop()
保存は Ctrl + O → Enter、終了は Ctrl + X です。
センサー側の実行(仮想環境外)
センサー制御スクリプトを実行します。ターミナルを開きます。Ctrl + Alt + T を同時に押す。
以下を入力します。
python shikoshikosysmail.py
表示側の実行(仮想環境内)
表示側は仮想環境を有効にして実行します。
ターミナルを開きます。Ctrl + Alt + T を同時に押す。
以下を入力します。
source ~/myenv/bin/activate
python shikoshikosys2.py
水位が30cm以上になったとき、メールアドレスに現在の水位と温度、画像が添付されてきます。

「見える防災」は、家庭の安全意識を高める第一歩。
今回のアップグレードで、あなたのRaspberry Piが、まさに“家庭の防災センター”へと進化します。
次回は、イケオジ流!Raspberry Piで作るIoT防災システム リアルタイム監視で家族を守る です。
人生再起動装備
ここにあるのは、イケオジが実際に興味を持ち、試したいと思った”武器”だ。
ただの買い物ではない。「人生を再起動する装備」だ。
人生逃げ切りサロン:[PR]Webプログラミング、ノーコード・動画編集・WEBデザイン・Lステップ・広告運用・AIライティング・WEBディレクション・コーチング・YouTube運営における実践的スキルを習得をする講座です。

「人生再起動装置一覧」の中にも、そそられるアイテムがあるはずだ。よかったら見てくれ。
イケオジ流!ラズベリーパイ(Raspberry Pi)一覧はこちら
最初の一歩が未来を変える イケオジ流!ラズベリーパイ:本体・モニター・センサー類の部品リスト
X(旧twitter) 今日のイケオジ
イケオジのキング・オブ・アスリートへの道はこちら
コメントを残す