Raspberry Pi 5で外出先からエアコンを操作できるようにしてみた
「帰宅前にエアコンを付けておきたい」「消し忘れた照明を外出先からオフにしたい」——そんな悩みを、Raspberry Pi 5 と赤外線 LED で解決できます。
この記事では、既存の家電のリモコン信号を学習して、スマホからいつでもどこでも家電を操作できるリモコンサーバーの作り方を解説します。
完成イメージ
- スマホのブラウザから家電を ON/OFF
- エアコン、照明、テレビなど赤外線リモコン対応の家電すべてに対応
- 外出先からも操作可能(VPN またはトンネリング経由)
- タイマー設定で自動操作も可能
必要なもの
| パーツ | 目安価格 | |--------|----------| | Raspberry Pi 5(4GB 以上) | 約10,000円 | | microSD カード(32GB 以上) | 約1,000円 | | USB-C 電源アダプター | 約2,000円 | | 赤外線 LED(940nm) | 約100円 | | 赤外線受信モジュール(VS1838B 等) | 約100円 | | NPN トランジスタ(2SC1815 等) | 約50円 | | 抵抗 100Ω × 1、10kΩ × 1 | 約20円 | | ブレッドボード+ジャンパーワイヤー | 約500円 |
赤外線 LED とトランジスタは電子部品店やネットで購入できます。秋月電子の「赤外線リモコンキット」がまとめて手に入るので便利です。
STEP 1: LIRC をインストールする
LIRC(Linux Infrared Remote Control)は、赤外線信号の送受信を制御するソフトウェアです。
sudo apt update
sudo apt install -y lirc
GPIO の設定
/boot/firmware/config.txt を編集します。
sudo nano /boot/firmware/config.txt
ファイル末尾に以下を追加します。
dtoverlay=gpio-ir,gpio_pin=22
dtoverlay=gpio-ir-tx,gpio_pin=23
| 設定 | GPIO ピン | 用途 | |------|----------|------| | gpio-ir | GPIO 22 (Pin 15) | 赤外線受信 | | gpio-ir-tx | GPIO 23 (Pin 16) | 赤外線送信 |
再起動します。
sudo reboot
STEP 2: 赤外線回路を配線する
受信回路(リモコン信号の学習用)
| VS1838B ピン | 接続先 | |-------------|--------| | OUT(左) | GPIO 22 (Pin 15) | | GND(中) | GND (Pin 6) | | VCC(右) | 3.3V (Pin 1) |
送信回路(家電の操作用)
赤外線 LED は GPIO から直接駆動すると光量不足になるため、トランジスタで増幅します。
GPIO 23 (Pin 16) --- [100Ω 抵抗] --- トランジスタ(ベース)
トランジスタ(エミッタ) --- GND
5V (Pin 2) --- 赤外線LED(アノード) --- 赤外線LED(カソード) --- トランジスタ(コレクタ)
赤外線 LED は肉眼では見えませんが、スマホのカメラを通すと光っているか確認できます。送信テスト時にはカメラで確認しましょう。
STEP 3: LIRC の設定を行う
LIRC 設定ファイルの編集
sudo nano /etc/lirc/lirc_options.conf
以下の2行を変更します。
driver = default
device = /dev/lirc0
LIRC の再起動
sudo systemctl restart lircd
STEP 4: リモコン信号を学習する
4-1. LIRC サービスを一時停止
sudo systemctl stop lircd
4-2. 信号を記録する
mode2 -d /dev/lirc0
この状態でリモコンを受信モジュールに向けてボタンを押すと、パルスデータが表示されます。データが表示されれば受信回路は正常です。Ctrl+C で終了します。
4-3. irrecord でリモコンを登録する
sudo irrecord -d /dev/lirc0 ~/lircd.conf
対話式のウィザードが起動します。手順に従ってください。
- デバイス名を入力(例:
aircon,light,tv) - 指示に従ってリモコンのボタンをランダムに押す(信号パターンの学習)
- 各ボタンの名前を入力してボタンを押す
# ボタン名の例
KEY_POWER (電源)
KEY_VOLUMEUP (音量UP)
KEY_VOLUMEDOWN (音量DOWN)
KEY_CHANNELUP (チャンネルUP)
エアコンのリモコンは信号が複雑なため、
irrecordではうまく記録できないことがあります。その場合はmode2の生データを使う方法(後述)を試してください。
4-4. 設定ファイルを配置する
sudo cp ~/lircd.conf /etc/lirc/lircd.conf.d/myremote.lircd.conf
sudo systemctl restart lircd
4-5. 送信テスト
irsend SEND_ONCE myremote KEY_POWER
家電の電源が入れば成功です。
STEP 5: Web API サーバーを作る
スマホから操作するための Web サーバーを構築します。
python3 -m venv ~/ir-remote
source ~/ir-remote/bin/activate
pip install flask
# ir_server.py
from flask import Flask, jsonify, render_template_string
import subprocess
app = Flask(__name__)
# === リモコンコマンド定義 ===
DEVICES = {
"aircon": {
"name": "エアコン",
"remote": "aircon",
"commands": {
"power": {"name": "電源", "key": "KEY_POWER"},
},
},
"light": {
"name": "照明",
"remote": "light",
"commands": {
"on": {"name": "ON", "key": "KEY_POWER"},
"off": {"name": "OFF", "key": "KEY_POWER2"},
"bright": {"name": "明るく", "key": "KEY_BRIGHTNESSUP"},
"dim": {"name": "暗く", "key": "KEY_BRIGHTNESSDOWN"},
},
},
"tv": {
"name": "テレビ",
"remote": "tv",
"commands": {
"power": {"name": "電源", "key": "KEY_POWER"},
"vol_up": {"name": "音量+", "key": "KEY_VOLUMEUP"},
"vol_down": {"name": "音量-", "key": "KEY_VOLUMEDOWN"},
"ch_up": {"name": "CH+", "key": "KEY_CHANNELUP"},
"ch_down": {"name": "CH-", "key": "KEY_CHANNELDOWN"},
},
},
}
HTML = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>リモコン</title>
<style>
body { font-family: sans-serif; max-width: 480px; margin: 0 auto; padding: 16px; background: #f5f5f5; }
h1 { color: #1b4332; font-size: 1.4em; }
.device { background: #fff; border-radius: 12px; padding: 16px; margin: 12px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.device h2 { margin: 0 0 12px 0; font-size: 1.1em; color: #2d6a4f; }
.buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.btn { padding: 12px 20px; border: none; border-radius: 8px; background: #2d6a4f; color: #fff; font-size: 1em;
cursor: pointer; transition: background 0.2s; }
.btn:active { background: #1b4332; }
.status { text-align: center; color: #999; font-size: 0.85em; margin-top: 8px; }
</style>
</head>
<body>
<h1>IR リモコン</h1>
{% for dev_id, dev in devices.items() %}
<div class="device">
<h2>{{ dev.name }}</h2>
<div class="buttons">
{% for cmd_id, cmd in dev.commands.items() %}
<button class="btn" onclick="send('{{ dev_id }}', '{{ cmd_id }}')">{{ cmd.name }}</button>
{% endfor %}
</div>
</div>
{% endfor %}
<div class="status" id="status"></div>
<script>
async function send(device, command) {
const el = document.getElementById('status');
el.textContent = '送信中...';
const res = await fetch(`/api/send/${device}/${command}`);
const data = await res.json();
el.textContent = data.status === 'ok' ? '送信完了' : 'エラー: ' + data.message;
setTimeout(() => el.textContent = '', 3000);
}
</script>
</body>
</html>
"""
@app.route("/")
def index():
return render_template_string(HTML, devices=DEVICES)
@app.route("/api/send/<device>/<command>")
def send_command(device, command):
if device not in DEVICES:
return jsonify({"status": "error", "message": "Unknown device"}), 404
dev = DEVICES[device]
if command not in dev["commands"]:
return jsonify({"status": "error", "message": "Unknown command"}), 404
remote = dev["remote"]
key = dev["commands"][command]["key"]
result = subprocess.run(
["irsend", "SEND_ONCE", remote, key],
capture_output=True, text=True,
)
if result.returncode == 0:
return jsonify({"status": "ok", "device": device, "command": command})
return jsonify({"status": "error", "message": result.stderr}), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
実行
python ir_server.py
スマホのブラウザで http://raspberrypi.local:5000 にアクセスすると、リモコン画面が表示されます。
STEP 6: 自動起動を設定する
sudo nano /etc/systemd/system/ir-remote.service
[Unit]
Description=IR Remote Server
After=network.target
[Service]
ExecStart=/home/pi/ir-remote/bin/python /home/pi/ir_server.py
WorkingDirectory=/home/pi
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
sudo systemctl enable ir-remote
sudo systemctl start ir-remote
STEP 7: 外出先からアクセスする(任意)
家庭内 LAN の外からアクセスするには、以下の方法があります。
方法A: Tailscale(おすすめ)
Tailscale は無料で使える VPN サービスです。Raspberry Pi とスマホの両方にインストールするだけで、外出先から安全にアクセスできます。
# Raspberry Pi にインストール
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
スマホにも Tailscale アプリをインストールして同じアカウントでログインすれば、Tailscale が割り当てた IP アドレス経由でアクセスできます。
方法B: cron でタイマー操作
外出先アクセスが不要な場合は、cron で時間指定の自動操作が便利です。
crontab -e
# 平日19:00にエアコンON(帰宅前)
0 19 * * 1-5 curl -s http://localhost:5000/api/send/aircon/power
# 毎日23:00に照明OFF(消し忘れ防止)
0 23 * * * curl -s http://localhost:5000/api/send/light/off
エアコン信号の記録(補足)
エアコンのリモコンは温度・モードなどの情報を含む長い信号を送るため、irrecord では記録が難しいことがあります。その場合は生データを使います。
# 生データで記録
mode2 -d /dev/lirc0 > aircon_on.txt
# → リモコンの「冷房26度」ボタンを押す → Ctrl+C
mode2 -d /dev/lirc0 > aircon_off.txt
# → リモコンの「停止」ボタンを押す → Ctrl+C
記録した生データは ir-ctl コマンドで送信できます。
ir-ctl -d /dev/lirc0 --send=aircon_on.txt
トラブルシューティング
| 症状 | 原因と対処 |
|------|-----------|
| 受信で何も表示されない | VS1838B の配線(OUT/GND/VCC)を確認。GPIO 番号が config.txt と一致しているか確認 |
| 送信しても家電が反応しない | 赤外線 LED の向きを確認。スマホカメラで LED が光っているか確認。距離を近づけてテスト |
| 「hardware does not support sending」 | config.txt の gpio-ir-tx が正しく設定されているか確認。再起動を試す |
| エアコンの信号が記録できない | irrecord の代わりに mode2 で生データを記録する方法を使う |
市販のスマートリモコンは数千円しますが、Raspberry Pi なら数百円のパーツで同じことが実現でき、しかも自由にカスタマイズできます。帰宅前のエアコン起動だけでも、毎日の快適さが変わりますよ。