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) | 計測回路の出力 |

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スポンサーリンク
スポンサーリンク