リモコンは紛失しやすいので、学習リモコン (BUFFALO PC-OP-RS1) を買ってみた。
http://buffalo.jp/products/catalog/item/p/pc-op-rs1/index.html
これを Debian/etch で動作させたときのメモを残す。
参考にさせていただいたサイト:
http://k-home.no-ip.info/wiki/index.php?PC-OP-RS1
http://blog.gcd.org/archives/50846722.html
以下、見よう見まねと試行錯誤でやっているので、万が一問題がおきても僕は責任とりません。また、よりよい方法を教えていただけるとうれしいです。
1. 上記サイトを参考に、カーネルモジュールに以下のパッチをあてる (参考サイトの情報は少し古く、パッチがあてられなかったので、若干推測気味) 。
diff -ru linux-source-2.6.18.org/drivers/usb/serial/ftdi_sio.c linux-source-2.6.18/drivers/usb/serial/ftdi_sio.c @@ -306,6 +306,7 @@ static struct usb_device_id id_table_combined [] = { + { USB_DEVICE(BUFFALO_VID, BUFFALO_PCOPRS1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, diff -ru linux-source-2.6.18.org/drivers/usb/serial/ftdi_sio.h linux-source-2.6.18/drivers/usb/serial/ftdi_sio.h --- linux-source-2.6.18.org/drivers/usb/serial/ftdi_sio.h 2006-09-20 12:42:06.000000000 +0900 +++ linux-source-2.6.18/drivers/usb/serial/ftdi_sio.h 2007-05-21 22:20:33.000000000 +0900 @@ -996,3 +996,8 @@ * */ +/* + * BUFFALO RemoteStation PC-OP-RS1 + */ +#define BUFFALO_VID 0x0411 /* BUFFALO Vendor ID */ +#define BUFFALO_PCOPRS1_PID 0x00b3 /* RemoteStation PC-OP-RS1 */
2. コンパイルする。
3. できあがった linux-source-2.6.18/drivers/usb/serial/ftdi_sio.ko を /lib/modules/2.6.18-4-686/kernel/drivers/usb/serial/ へ上書きコピーする。
4. /lib/modules/2.6.18-4-686/modules.usbmap に以下の行を追加する (もっといい方法ありそう) 。
ftdi_sio 0x0003 0x0411 0x00b3 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
5. /lib/modules/2.6.18-4-686/modules.alias に以下の行を追加する (同上) 。
alias usb:v0411p00B3d*dc*dsc*dp*ic*isc*ip* ftdi_sio
6. /etc/udev/udev.rules に以下の行を追加する。
SUBSYSTEMS=="usb", KERNEL=="ttyUSB*", \ ATTRS{product}=="BUFFALO RemoteStation PC-OP-RS1", \ SYMLINK+="pc-op-rs1"
7. 再起動。
8. PC-OP-RS1 を接続すると、/dev/pc-op-rs1 ができているはず。これを使って、上記サイトを参考に通信すればいい。libtermios-ruby を使って以下のようなプログラムを書いた。
#!/usr/bin/env ruby require "fcntl" require "termios" require "optparse" class RemoteStation def self.transaction(dev_name, verbose = false) i = new(dev_name, verbose) yield(i) i.close end def initialize(dev_name, verbose = false) @verbose = verbose @dev = open(dev_name, File::RDWR | File::NONBLOCK) mode = @dev.fcntl(Fcntl::F_GETFL, 0) @dev.fcntl(Fcntl::F_SETFL, mode & ~File::NONBLOCK) @old_tio = Termios.getattr(@dev) new_tio = Termios.new_termios new_tio.iflag = Termios::IGNPAR new_tio.oflag = 0 new_tio.cflag = (Termios::CS8 | Termios::CLOCAL | Termios::CREAD) new_tio.lflag = 0 new_tio.cc[Termios::VTIME] = 0 new_tio.cc[Termios::VMIN] = 1 new_tio.ispeed = Termios::B115200 new_tio.ospeed = Termios::B115200 Termios.flush(@dev, Termios::TCIOFLUSH) Termios.setattr(@dev, Termios::TCSANOW, new_tio) end def close Termios.setattr(@dev, Termios::TCSANOW, @old_tio) @dev.close @dev = nil end def send_p(cmd) @dev.write cmd info("-->", cmd) end def recv_p(len = 1) res = @dev.read(len) info("<--", res) res end def info(prefix, data) return unless @verbose str = data.size == 1 ? "0x%02x" % data[0] : "(#{ data.size } bytes)" puts(prefix + " " + str) end # LED を点灯させる def led # --> 0x69 send_p("i") # <-- 0x4f ※ 0x59 が帰ってくるときは LED は点かない [ "O", "Y" ].include? recv_p end # 受信する def recv # --> 0x72 send_p("r") # <-- 0x59 return false unless recv_p == "Y" yield if block_given? # <-- 0x53 return false unless recv_p == "S" # <-- リモコンデータ 240 Bytes packet = recv_p(240) # <-- 0x45 return false unless recv_p == "E" packet end # 送信する def send(packet, channel = 1) raise "invalid packet" unless packet.size == 240 raise "invalid channel" unless 1 <= channel && channel <= 4 # --> 0x74 send_p("t") # <-- 0x59 return false unless recv_p == "Y" # --> 0x31 ※ 1ch=0x31,2ch=0x32,3ch=0x33,4ch=0x34 send_p((?0 + channel).chr) # --> 0x59 return false unless recv_p == "Y" yield if block_given? # --> リモコンデータ 240 Bytes send_p(packet) # <-- 0x45 return false unless recv_p == "E" true end end dev_name = "/dev/pc-op-rs1" channel = 1 led = recv = send = quite = verbose = nil opts = OptionParser.new opts.on("-d devfile", "set device file") {|dev_name|} opts.on("-c channel", "set channel (1 - 4)", Integer) {|channel|} opts.on("-l", "flash led") {|led|} opts.on("-r packetfile", "receive packet") {|recv|} opts.on("-t packetfile", "transmit packet") {|send|} opts.on("-q", "quite mode") {|quite|} opts.on("-v", "verbose mode") {|verbose|} opts.parse!(ARGV) RemoteStation.transaction(dev_name, verbose) do |rs| rs.led if led if recv cmd = rs.recv do puts "receiving..." if !quite && $stdout.tty? end fh = recv == "-" ? $stdout : open(recv, "wb") fh.write(cmd) fh.close end if send fh = send == "-" ? $stdin : open(send, "rb") rs.send(fh.read, channel) do puts "sending..." if !quite && $stdout.tty? end end end
パケットを受信するときは、./pc-op-rs1.rb -r (ファイル名) を実行した状態で、リモコンを PC-OP-RS1 に向けて発信する。ファイル名にデータが保存 (上書き) される。パケットを送信するときは、PC-OP-RS1 の発信機を所望の機器に向けて ./pc-op-rs1.rb -t (ファイル名) を実行すればよい。
使った感触としては、出力が弱いのか指向性が強すぎるのか、発信機をかなり近づけないと動かない。しょうがないので 2.5 mm ステレオのオーディオ延長ケーブルを買ってきて延長して使っている。