あけましておめでとうございます。 今年もよろしくお願いします。
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 の上の黒い箱状の ↑ モノが 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 マシンで予約録画できると便利。
こんにちは、スクリプト修正して使用させていただきました。
ありがとうございました。
Comment by gixxer — 2008年1月17日 @ 15:06
たいへん参考になりました。
FreeBSD 8.3-RELEASE-p5 でスクリプトを一部修正して使用させていただきます。
FreeBSD では USBデバイス指定が /dev/ttyU* となります。またパーミッションを変更しないと、一般ユーザでは使用できないので注意が必要です。
Comment by runrun — 2013年1月5日 @ 00:14