Daily Hack

Raspberry Pi 5で家の電気代を見える化してみた

ラズベリーパイ
#Raspberry Pi#自動化#IoT#節約#電力

「電気代が先月より高いけど、何が原因かわからない」——そんな経験はありませんか? Raspberry Pi 5 と CT センサーを使えば、家庭の消費電力をリアルタイムで「見える化」できます。

この記事では、消費電力の計測からグラフでの可視化まで、電力モニタリングシステムの作り方を解説します。

電力モニタリングシステム 構成図

完成イメージ

  • 家庭の消費電力をリアルタイムで計測
  • 1分ごとにデータを記録
  • ブラウザでグラフを確認
  • 時間帯別・日別の消費電力を比較

必要なもの

パーツ 目安価格
Raspberry Pi 5(4GB 以上) 約10,000円
microSD カード(32GB 以上) 約1,000円
USB-C 電源アダプター 約2,000円
CT センサー(SCT-013-030 / 30A対応) 約1,000円
MCP3008(AD コンバーター) 約500円
分圧抵抗 10kΩ × 2 約20円
バーデン抵抗 33Ω 約10円
電解コンデンサ 10μF 約20円
ブレッドボード+ジャンパーワイヤー 約500円

安全に関する重要な注意: CT センサーはケーブルの外側にクランプするだけで電流を計測できるため、配線に直接触れる必要はありません。ただし分電盤の作業を行う場合は、感電に十分注意してください。不安な場合は電気工事士に相談することをおすすめします。

STEP 1: CT センサーの仕組みを理解する

CT(Current Transformer)センサーは、ケーブルを流れる電流が作る磁場を検知して、電流値に比例した小さな電流を出力するセンサーです。

[分電盤のケーブル] → (磁場) → [CTセンサー] → 微小な交流電流 → [計測回路] → [Raspberry Pi]

SCT-013-030 の場合、30A の電流が流れると 1V の出力が得られます。

STEP 2: SPI を有効にする

MCP3008 を使用するため、SPI を有効にします。

sudo raspi-config

Interface OptionsSPIYes → Finish → 再起動

sudo reboot

STEP 3: 計測回路を組む

回路の概要

Raspberry Pi の GPIO はマイナス電圧を受け付けないため、CT センサーの交流出力にバイアス電圧を加えて、0V 〜 3.3V の範囲に収める必要があります。

回路図

3.3V ─── [10kΩ] ──┬── MCP3008 CH0
                    │
GND ──── [10kΩ] ──┘
                    │
CTセンサー出力 ─ [33Ω] ─┘
                    │
              [10μF コンデンサ]
                    │
                   GND

各部品の役割

部品 役割
10kΩ × 2(分圧) 1.65V のバイアス電圧を作る
33Ω(バーデン抵抗) CT センサーの電流出力を電圧に変換
10μF コンデンサ ノイズの除去

MCP3008 の配線

MCP3008 ピン 接続先
VDD (Pin 16) 3.3V
VREF (Pin 15) 3.3V
AGND (Pin 14) GND
CLK (Pin 13) GPIO 11 (SCLK)
DOUT (Pin 12) GPIO 9 (MISO)
DIN (Pin 11) GPIO 10 (MOSI)
CS (Pin 10) GPIO 8 (CE0)
DGND (Pin 9) GND
CH0 (Pin 1) 計測回路の出力
PRスポンサーリンク
スポンサーリンク

STEP 4: Python 環境を構築する

python3 -m venv ~/power-monitor
source ~/power-monitor/bin/activate

pip install spidev flask matplotlib

STEP 5: 電力計測スクリプトを作る

キャリブレーション用スクリプト

まず、センサーの出力値を確認して補正係数を調整します。

# calibrate.py
import spidev
import time
import math

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1350000

def read_adc(channel=0):
    """MCP3008 から値を読み取る"""
    adc = spi.xfer2([1, (8 + channel) << 4, 0])
    return ((adc[1] & 3) << 8) + adc[2]

def measure_current(samples=1000):
    """RMS 電流を計算する"""
    sum_squared = 0
    for _ in range(samples):
        raw = read_adc(0)
        # 1.65V バイアスを引く(ADC中央値 = 512)
        voltage = (raw - 512) * 3.3 / 1024
        # バーデン抵抗 33Ω → 電流換算
        # SCT-013-030: 30A = 1V → 係数 = 30
        current = voltage * 30
        sum_squared += current * current
        time.sleep(0.001)  # 1ms 間隔

    rms_current = math.sqrt(sum_squared / samples)
    return rms_current

print("電流のキャリブレーション")
print("家電を ON/OFF して値の変化を確認してください")
print("-" * 40)

while True:
    current = measure_current()
    power = current * 100  # 日本の家庭用: 100V
    print(f"電流: {current:.2f}A / 消費電力: {power:.0f}W")
    time.sleep(2)
python calibrate.py

家電を ON/OFF して値が変化することを確認します。

本番用 計測・記録スクリプト

# power_monitor.py
import spidev
import time
import math
import csv
import os
from datetime import datetime

# === 設定 ===
INTERVAL = 60              # 記録間隔(秒)
CSV_FILE = "power_log.csv"
VOLTAGE = 100              # 家庭用電圧(日本: 100V)
CT_RATIO = 30              # CTセンサーの変換比
BURDEN_RESISTANCE = 33     # バーデン抵抗(Ω)

# === SPI 初期化 ===
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1350000

def read_adc(channel=0):
    adc = spi.xfer2([1, (8 + channel) << 4, 0])
    return ((adc[1] & 3) << 8) + adc[2]

def measure_power(samples=2000):
    """RMS電流を計測し、消費電力を算出する"""
    sum_squared = 0
    for _ in range(samples):
        raw = read_adc(0)
        voltage = (raw - 512) * 3.3 / 1024
        current = voltage * CT_RATIO
        sum_squared += current * current
        time.sleep(0.0005)

    rms_current = math.sqrt(sum_squared / samples)
    power_watts = rms_current * VOLTAGE
    return round(rms_current, 3), round(power_watts, 1)

def log_data(timestamp, current, power):
    """CSV にデータを記録する"""
    file_exists = os.path.exists(CSV_FILE)
    with open(CSV_FILE, "a", newline="") as f:
        writer = csv.writer(f)
        if not file_exists:
            writer.writerow(["timestamp", "current_A", "power_W"])
        writer.writerow([timestamp, current, power])

# === メインループ ===
print("電力モニタリングを開始します")
print(f"記録間隔: {INTERVAL}秒 / 保存先: {CSV_FILE}")
print("-" * 50)

while True:
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    current, power = measure_power()
    log_data(timestamp, current, power)
    print(f"[{timestamp}] {current}A / {power}W")
    time.sleep(INTERVAL)

STEP 6: Web ダッシュボードを作る

# power_dashboard.py
from flask import Flask, render_template_string
import csv
import os
from datetime import datetime, timedelta

app = Flask(__name__)
CSV_FILE = "power_log.csv"
ELECTRICITY_RATE = 31  # 電気料金単価(円/kWh)目安

HTML = """
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>電力ダッシュボード</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    body { font-family: sans-serif; max-width: 960px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
    h1 { color: #1b4332; }
    .cards { display: flex; gap: 16px; flex-wrap: wrap; }
    .card { background: #fff; border-radius: 12px; padding: 20px; flex: 1; min-width: 200px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08); text-align: center; }
    .card .value { font-size: 2.2em; font-weight: bold; color: #2d6a4f; }
    .card .label { color: #666; font-size: 0.9em; margin-top: 4px; }
    .chart-card { background: #fff; border-radius: 12px; padding: 20px; margin: 16px 0;
                  box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
  </style>
</head>
<body>
  <h1>電力モニタリング</h1>
  <div class="cards">
    <div class="card">
      <div class="value">{{ current_power }} W</div>
      <div class="label">現在の消費電力</div>
    </div>
    <div class="card">
      <div class="value">{{ today_kwh }} kWh</div>
      <div class="label">今日の使用量</div>
    </div>
    <div class="card">
      <div class="value">{{ today_cost }} 円</div>
      <div class="label">今日の電気代(目安)</div>
    </div>
    <div class="card">
      <div class="value">{{ month_cost }} 円</div>
      <div class="label">今月の電気代(目安)</div>
    </div>
  </div>

  <div class="chart-card">
    <h2>24時間の消費電力推移</h2>
    <canvas id="powerChart"></canvas>
  </div>

  <script>
    new Chart(document.getElementById("powerChart"), {
      type: "line",
      data: {
        labels: {{ timestamps | tojson }},
        datasets: [{
          label: "消費電力 (W)",
          data: {{ powers | tojson }},
          borderColor: "#c1121f",
          backgroundColor: "rgba(193, 18, 31, 0.1)",
          fill: true,
          tension: 0.3,
        }]
      },
      options: { responsive: true, scales: { y: { beginAtZero: true } } }
    });
  </script>
</body>
</html>
"""

def load_data():
    rows = []
    if os.path.exists(CSV_FILE):
        with open(CSV_FILE) as f:
            rows = list(csv.DictReader(f))
    return rows

@app.route("/")
def dashboard():
    rows = load_data()

    # 現在の消費電力
    current_power = rows[-1]["power_W"] if rows else "0"

    # 今日のデータ
    today = datetime.now().strftime("%Y-%m-%d")
    today_rows = [r for r in rows if r["timestamp"].startswith(today)]
    today_kwh = round(sum(float(r["power_W"]) for r in today_rows) / 60 / 1000, 2) if today_rows else 0
    today_cost = round(today_kwh * ELECTRICITY_RATE)

    # 今月のデータ
    month = datetime.now().strftime("%Y-%m")
    month_rows = [r for r in rows if r["timestamp"].startswith(month)]
    month_kwh = round(sum(float(r["power_W"]) for r in month_rows) / 60 / 1000, 2)
    month_cost = round(month_kwh * ELECTRICITY_RATE)

    # 24時間グラフ用データ
    recent = rows[-1440:]  # 直近24時間(1分間隔)
    timestamps = [r["timestamp"][-8:-3] for r in recent]  # HH:MM
    powers = [float(r["power_W"]) for r in recent]

    return render_template_string(
        HTML,
        current_power=current_power,
        today_kwh=today_kwh,
        today_cost=today_cost,
        month_cost=month_cost,
        timestamps=timestamps,
        powers=powers,
    )

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

実行

# 2つのターミナルで実行
python power_monitor.py   # データ記録
python power_dashboard.py  # ダッシュボード

ブラウザで http://raspberrypi.local:8080 にアクセスするとダッシュボードが表示されます。

STEP 7: 自動起動を設定する

sudo nano /etc/systemd/system/power-monitor.service
[Unit]
Description=Power Monitor
After=network.target

[Service]
ExecStart=/home/pi/power-monitor/bin/python /home/pi/power_monitor.py
WorkingDirectory=/home/pi
Restart=always
User=pi

[Install]
WantedBy=multi-user.target
sudo systemctl enable power-monitor
sudo systemctl start power-monitor

ダッシュボード用のサービスも同様に作成します。

sudo nano /etc/systemd/system/power-dashboard.service
[Unit]
Description=Power Dashboard
After=network.target

[Service]
ExecStart=/home/pi/power-monitor/bin/python /home/pi/power_dashboard.py
WorkingDirectory=/home/pi
Restart=always
User=pi

[Install]
WantedBy=multi-user.target
sudo systemctl enable power-dashboard
sudo systemctl start power-dashboard

電気代の節約ポイント

データが溜まってくると、以下のような発見があるはずです。

  • 待機電力の大きい家電: 常に 5〜10W 消費している家電は、使わないときコンセントを抜く
  • 電力のピーク時間帯: エアコンと電子レンジを同時に使う時間帯を避ける
  • エアコンの設定温度: 1度変えると消費電力がどれくらい変わるか実測できる
  • 不在時の無駄: 外出中に想定外の電力消費がないか確認する

電力モニタリングを始めた家庭では、意識が変わるだけで月10〜15%の電気代削減に成功しているデータもあります。

トラブルシューティング

症状 原因と対処
値が常に 0 CT センサーがケーブルにしっかりクランプされているか確認。SPI が有効か確認
値が異常に大きい キャリブレーション係数を調整。バーデン抵抗の値を確認
値がマイナスになる バイアス電圧(中央値 512)のずれ。raw - 512 の 512 を実測値に合わせる
ノイズが多い コンデンサの接続を確認。サンプル数を増やす

「見える化」は節約の第一歩です。実際の数字を見ると、「あの家電、こんなに電気を使っていたのか」という発見が必ずあります。データに基づいた省エネで、家計を守りましょう。

PRスポンサーリンク
ゲーム向けスポンサー