仙石浩明の日記

2007年1月1日

赤外線リモコンを Linux からコントロール

あけましておめでとうございます。 今年もよろしくお願いします。

fj.comp.dev.misc に昨年12月26日に投稿された記事 「USB赤外線リモコン on Mac OS X」で、
河野真治 @ 琉球大学情報工学 さん曰く:

パソコン用学習リモコン PC-OP-RS1
昔は、Crossam 2+ と、そのUSB version があったんだけど、 ちょっと高い。もう売ってないし。 これは、5,000円切っている。 もちろん、ドライバはWindows しかありませんが、 シリアルドライバなんて、なんとでもなる。libusb使ってもいいし。

鎌倉にお参りした帰り、たまたま立ち寄った ラゾーナ川崎 のビックカメラにて、 上記 PC-OP-RS1 を見つけたので衝動買い。

BUFFALOの学習リモコンPC-OP-RS1をLinux(fc3)で使う」を参考にしながら、 perl でテストプログラムを書いてみる。 PC-OP-RS1 は普通のシリアルデバイスとして Linux から見えるので、 CPAN の Device::SerialPort を使えば簡単にコントロールできる。 赤外線リモコンの信号を受信するには、

--> 0x72
<-- 0x59
(リモコン受信)
<-- 0x53
<-- リモコンデータ * 240Byte
<-- 0x45

とするだけ。 そして、受信した 240バイトのデータ(固定長)を、

--> 0x74
<-- 0x59
--> 0x31 ※ 1ch=0x31,2ch=0x32,3ch=0x33,4ch=0x34
<-- 0x59
--> リモコンデータ * 240Byte
<-- 0x45

などと PC-OP-RS1 へ送ってやれば、 PC-OP-RS1 から赤外線が発射される。
ビデオの赤外線受信部近くに、PC-OP-RS1 ↓ の送信部を設置した (両面テープでラックに固定)。

  PC-OP-RS1 & VTR

ラック下段にある PC の上の黒い箱状の ↑ モノが PC-OP-RS1 本体 (拡大写真)。

即席で作った perl スクリプト「irrc」(後述) を、 次のように「-r」オプション付で実行しておいて、 PC-OP-RS1 の受光部へ赤外線リモコンを向けてリモコンのボタンを押す。

% irrc -r
ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff

すると、このように 240 バイトのデータが 16進数で表示される。 このデータを irrc スクリプトの先頭部分で、

$Ir{'aPower'} = [
    pack("H*", "ffffffff ... 中略 .... 00feffff"),
    ];

などと定義する(ちなみに上記データは私の自宅のエアコンの電源ON/OFF)。 複数のボタンのシーケンスを定義する場合は、

$Ir{'AB'} = [
    pack("H*", "ボタンA を押したときの送信データ"),
    pack("H*", "ボタンB を押したときの送信データ"),
    ];

などと定義すればよい。 ここで定義したシーケンス名 (上記「aPower」や「AB」) を、 irrc スクリプトの引数として渡せば、 PC-OP-RS1 から赤外線が発射される。

以下、irrc スクリプト全体:

#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Getopt::Std;

my %Ir;
$Ir{'vPower'} = [
    pack("H*", "ffffffffffffffffffffff0700000000007ef0831ff8c00f7e00003f00800ffc00003f00801f00c00700f00300f8c10f7c00003f00801f00e00700f0831f00c00f7ee0033ff8c10ffc00003ff00100fc00007e00001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    ];
$Ir{'aPower'} = [
    pack("H*", "ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff"),
    ];

our ($opt_v, $opt_r, $opt_d, $opt_c);
getopts("vrd:c:") || &help;
my $Verbose = $opt_v;
my $device;
if ($opt_d) {
    $device = $opt_d;
} else {
    $device = "/dev/ttyUSB0";
}
my $ch = 1;
if ($opt_c && $opt_c =~ /^[1-4]$/) {
    $ch = ord($opt_c) - ord('0');
    print STDERR "Ch: $ch\n" if $Verbose;
}
my $port = &openDev;
&sendData($port, "69");		# LED command
&expect("4f", &readData($port, 1));
if ($opt_r) {
    my $data = &receive($port);
    print unpack("H*", $data), "\n";
}
while ($_ = shift @ARGV) {
    if (defined $Ir{$_}) {
	print STDERR "Tx: $_\n" if $Verbose;
	for my $ir (@{$Ir{$_}}) {
	    &transmit($port, $ch, $ir);
	}
    } else {
	print STDERR "Unknown command: $_\n";
    }
}
exit 0;

sub openDev {
    my $port = new Device::SerialPort($device) || die;
    $port->user_msg(1);
    $port->error_msg(1);
    $port->baudrate(115200);
    $port->databits(8);
    $port->parity("none");
    $port->stopbits(1);
    $port->handshake("none");
    $port->read_const_time(100); # 0.1 sec
    $port->read_char_time(5);
    $port;
}

sub transmit {
    my ($port, $ch, $data) = @_;
    &sendData($port, "74");		# transmit
    &expect("59", &readData($port, 1));
    &sendData($port, sprintf("3%d", $ch % 10));
    &expect("59", &readData($port, 1));
    $port->write($data) || die;
    &expect("45", &readData($port, 1));
}

sub receive {
    my ($port) = @_;
    &sendData($port, "72");		# receive
    &expect("59", &readData($port, 1));
    &expect("53", &readData($port, 1, -1));
    my $data = &readData($port, 240);
    &expect("45", &readData($port, 1));
    $data;
}

sub sendData {
    my ($port, $str) = @_;
    $port->write(pack("H*", $str)) || die;
}

sub readData {
    my ($port, $len, $timeout) = @_;
    my $i = 0;
    my $j = 0;
    my $data;
    if (! defined $timeout) {
	$timeout = 10;
    }
    while ($i < $len) {
	my ($l, $d) = $port->read(1);
	if ($l > 0) {
	    $data .= $d;
	    $i += $l;
	    $j = 0;
	} else {
	    $j++;
	    if ($timeout > 0 && $j > $timeout) {
		print STDERR "TIMEOUT to read $len byte\n";
		exit 1;
	    }
	}
    }
    if ($Verbose) {
	print STDERR "read: ", unpack("H*", $data), "\n";
    }
    $data;
}

sub expect {
    my ($ex, $d) = @_;
    my $str = unpack("H*", $d);
    if ($str ne $ex) {
	print STDERR "expect $ex, but got $str\n";
	exit 1;
    }
}

sub help {
    print STDERR <<EOF;
Usage: psoprs1.pl [opt] <com>...
opt:   -d <dev>   set device
       -c <ch>    set channel
       -r         receive
       -v         verbose
EOF
    print "com: ", join(" ", sort keys %Ir), "\n";
    exit 1;
}

「irrc vPower」を実行すれば、ビデオの電源を ON/OFF し、 「irrc -c 2 aPower」を実行すれば、エアコンの電源を ON/OFF できる。 もちろんエアコンの電源を Linux から ON/OFF できてもあまり嬉しくないが、 CS/BS 放送や CATV のチューナのチャンネルを Linux から切り替えて、 そのチューナの出力を Linux マシンで予約録画できると便利。

Filed under: ハードウェアの認識と制御 — hiroaki_sengoku @ 17:06

2件のコメント »

  1. こんにちは、スクリプト修正して使用させていただきました。
    ありがとうございました。

    コメント by gixxer — 2008年1月17日 @ 15:06

  2. たいへん参考になりました。
    FreeBSD 8.3-RELEASE-p5 でスクリプトを一部修正して使用させていただきます。
    FreeBSD では USBデバイス指定が /dev/ttyU* となります。またパーミッションを変更しないと、一般ユーザでは使用できないので注意が必要です。

    コメント by runrun — 2013年1月5日 @ 00:14

この投稿へのコメントの RSS フィード。

コメントする