テレビに接続しているゲーム機には、本体を起動するとテレビも連動して起動させて、HDMIの入力も自動で切り替えてくれる機能を持っています。
我が家ではPCもテレビに接続しているのですが、PCからはそういったテレビ連動はできません。
そこを何とかできないか、ということで家に転がっていたRaspberry Piで何とかする、というのがこの記事の内容です。
外部機器がテレビを操作する「HDMI-CEC」について
Nintendo Switchのようなゲーム機はテレビとの電源連動機能を持っており、本体を起動すると「テレビの電源を入れて」「HDMIの入力切替を行って」自動的にゲーム画面がテレビに表示されます。
この機能は HDMI CEC(Consumer Electronics Control) という規格に基づく信号を送信することで実現しています。
で、ゲーム機がCECを送信出来るのならPCでもCECを送信できるだろうと思ったのですが、ハードウェアレベルで実装してないためできないらしいです。
そしてPCはCECの信号を送信できないのですが、実はRaspberry PiはCECの信号を出力することができます。
やりたいこと
テレビに接続したPCを起動したら
- テレビをつける
- PCを接続しているHDMI入力に切り替える
PCがシャットダウンしたら
- テレビの電源を落とす
Raspberry Piの準備
OSはRaspbianを使用。
CECの使用するためにcec-utils
をインストールしてください。
apt update
apt install cec-utils
CECの使用方法
cec-utilsでCECフレームを送信する方法はいくつかあるのですが、ここではechoで必要なメッセージを出力して、single-commandモード(-s
)で実行するcec-client
に渡す方法を使います。
echo "tx 10:04" | cec-client -s
tx
を先頭に入力し、続けて引数としてCECフレームを入力します。
CECフレームの先頭2バイト部分10
は、1バイト目が送信元論理アドレス、2バイト目が送信先論理アドレスとなります。
テレビの論理アドレスは0
で固定、送信元となるRaspberry Piの論理アドレスはcec-client -l
コマンドで出力されるdevice情報から確認できます。
この記事では、Raspberry Piの論理アドレスは1
であるものとしてCECフレームを作成します。
# cec-client -l
libCEC version: 6.0.2, compiled on Linux-5.10.63-v8+ ... , features: P8_USB, DRM, P8_detect, randr, RPi, Exynos, Linux, AOCEC
Found devices: 1
device: 1
com port: RPI
vendor id: 2708
product id: 1001
firmware version: 1
type: Raspberry Pi
HDMIの入力切替を行う際には、「物理アドレス」も指定しなくてはいけません。
調査方法は割愛しますが、基本的に「HDMI 1:1.0.0.0
」~「HDMI 4:4.0.0.0
」というようなアドレッシングになっています。
この記事で使用するCECフレームの内容は以下の通り。
CECフレーム | 機能 | 備考 |
---|---|---|
10:04 | テレビの電源をONにする |
echo "on 0" と同値 |
10:36 | テレビの電源をOFFにする |
echo "standby 0" と同値 |
1f:82:20:00 | いわゆる"HDMI 2"をアクティブにする | 宛先論理アドレスはTVの0 ではなく、ブロードキャストのf を指定する必要がある3-4バイト目はアクティブソースを指定する宣言 5-8バイト目は対象となるHDMI入力の物理アドレスを指定する |
PC(Windows)との連動
本当はWindowsのタスクスケジューラーを使い、起動時/終了時にRaspberry PiにHTTPアクセスをしてCECの操作をしたかったのですが、どうしてもタスクスケジューラーが機能してくれませんでした。
(たぶん私の知識不足)
そのため、「Raspberry PiからPCに定期的にpingを打ち、その結果でCECの操作を行う」という方法としました。
具体的に、以下のようなシェルをRaspberry Pi上に配置し、systemdにてサービス化させました。
- 1秒周期でWindowsPCにpingを打つ
- ping応答があればカウント変数をデクリメント。ただしカウント変数がすでに"0"なら何もしない
- デクリメントした結果が"0"になった場合、TVの電源をON+"HDMI 2"をアクティブにする
- ping応答がなければカウント変数をインクリメント。ただしカウント変数がすでに"3"なら何もしない
- インクリメントした結果が"3"になった場合、TVの電源をOFFにする
#!/bin/bash
CNT=0
while true
do
ping -c 1 -w 1 <WindowsPCのIPアドレス> > /dev/null
BUF=$?
if [ $BUF -eq 0 ]; then
if [ $CNT -gt 0 ]; then
CNT=`echo $(($CNT-1))`
if [ $CNT -eq 0 ]; then
echo "tx 10:04"|/usr/bin/cec-client -s -d 1 > /dev/null
echo "tx 1f:82:20:00"|/usr/bin/cec-client -s -d 1 > /dev/null
fi
else
CNT=0
fi
else
if [ $CNT -lt 3 ]; then
CNT=`echo $(($CNT+1))`
if [ $CNT -eq 3 ]; then
echo "tx 10:36"|/usr/bin/cec-client -s -d 1 > /dev/null
fi
else
CNT=3
fi
fi
sleep 1
done