add USB IR toy
This commit is contained in:
commit
1b50259948
|
@ -0,0 +1,20 @@
|
||||||
|
LaserTag infra-red greande.
|
||||||
|
based on the MilesTag v2 protocol
|
||||||
|
|
||||||
|
usb ir toy
|
||||||
|
==========
|
||||||
|
|
||||||
|
script to record and decode infra-red signal using the Dangerous Prototypes USB IR toy board.
|
||||||
|
I had to replace the 38 kHz demodulator with a 56 kHz demodulator.
|
||||||
|
|
||||||
|
ir.rb
|
||||||
|
-----
|
||||||
|
|
||||||
|
script to use the USB IR toy.
|
||||||
|
can detect frequency, record transmission, play recordings, ...
|
||||||
|
|
||||||
|
decode.rb
|
||||||
|
---------
|
||||||
|
|
||||||
|
decode the MilesTag v2 recorded transmission
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: UTF-8
|
||||||
|
# tested with ruby 1.9.1 and 1.9.3
|
||||||
|
# decode milestag II message recorded by ir.rb
|
||||||
|
|
||||||
|
UNIT = 21.3333 # ir burst unit, in µs
|
||||||
|
|
||||||
|
# verify command parameters
|
||||||
|
raise "provide a IR record file to parse" if ARGV.size!=1
|
||||||
|
# read file
|
||||||
|
file = File.open(ARGV[0],"r")
|
||||||
|
|
||||||
|
# parse ticks in IR packets
|
||||||
|
ir_packets = []
|
||||||
|
packet = []
|
||||||
|
while data = file.read(2) do
|
||||||
|
# one IR packet
|
||||||
|
burst = data.unpack('n')[0]
|
||||||
|
if burst==0xffff then
|
||||||
|
# create next packet
|
||||||
|
packet << 1 # trailing space
|
||||||
|
ir_packets << packet
|
||||||
|
packet = []
|
||||||
|
else
|
||||||
|
# decode tick
|
||||||
|
burst *= UNIT
|
||||||
|
# the number of ticks (a tick = 600µs)
|
||||||
|
tick = (burst/600.0).round
|
||||||
|
packet << tick
|
||||||
|
end
|
||||||
|
end
|
||||||
|
puts "got #{ir_packets.size} IR packets"
|
||||||
|
puts ir_packets*" "
|
||||||
|
|
||||||
|
# decode ticks in bits
|
||||||
|
bit_packets = []
|
||||||
|
ir_packets.each do |ir_packet|
|
||||||
|
bit_packet = []
|
||||||
|
header = false
|
||||||
|
space = false
|
||||||
|
ir_packet.each do |ir|
|
||||||
|
# wait for header
|
||||||
|
if ir==4 then
|
||||||
|
if bit_packet.size>0 then
|
||||||
|
bit_packets << bit_packet
|
||||||
|
bit_packet = []
|
||||||
|
end
|
||||||
|
header = true
|
||||||
|
space = true
|
||||||
|
else
|
||||||
|
next unless header
|
||||||
|
if space then
|
||||||
|
space = false
|
||||||
|
next
|
||||||
|
else
|
||||||
|
if ir==1 or ir==2 then
|
||||||
|
bit_packet<<(ir-1)
|
||||||
|
space = true
|
||||||
|
else
|
||||||
|
puts "broken burst"
|
||||||
|
header = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
bit_packets << bit_packet
|
||||||
|
end
|
||||||
|
puts "got #{bit_packets.size} bit packets"
|
||||||
|
puts bit_packets*" "
|
||||||
|
|
||||||
|
# parse bit packet
|
||||||
|
bit_packets.each do |bit_packet|
|
||||||
|
if bit_packet[0]==0 then
|
||||||
|
# shot packet
|
||||||
|
if bit_packet.size!=14 then
|
||||||
|
puts "broken shot packet"
|
||||||
|
else
|
||||||
|
player = (bit_packet[1,7]*"").to_i(2)
|
||||||
|
team = (bit_packet[8,2]*"").to_i(2)
|
||||||
|
damage = (bit_packet[10,4]*"").to_i(2)
|
||||||
|
puts "shot: player=#{player}, team=#{team}, damage=#{damage}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# control packet
|
||||||
|
if bit_packet.size%8!=0 then
|
||||||
|
puts "broken control packet"
|
||||||
|
else
|
||||||
|
data = (bit_packet*"").to_i(2).to_s(16).rjust(bit_packet.size/4,"0")
|
||||||
|
puts "control packet: 0x#{data}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,209 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: UTF-8
|
||||||
|
# tested with ruby 1.9.1 and 1.9.3
|
||||||
|
# USB IR Toy tool
|
||||||
|
# based on http://dangerousprototypes.com/docs/USB_IR_Toy:_Sampling_mode
|
||||||
|
require 'serialport'
|
||||||
|
|
||||||
|
# display debug messages
|
||||||
|
@debug = false
|
||||||
|
|
||||||
|
def help
|
||||||
|
puts "USB IR Toy tool"
|
||||||
|
puts ""
|
||||||
|
puts "option:"
|
||||||
|
puts "\t--help,-h\t\tdisplay this help"
|
||||||
|
puts "\t--device,-d <device>\tuse this serial port"
|
||||||
|
puts "\t--record,-r <file>\trecord IR stream into this file"
|
||||||
|
puts "\t--play,-p <file>\tplay IR activity from this file"
|
||||||
|
puts "\t--freq,-f\t\tdetect frequency of IR signal"
|
||||||
|
puts "\t--laser,-l\t\tuse lasertag frequency (56kHz) when transmitting"
|
||||||
|
puts "\t--traffic,-t\t\tshow communication traffic with USB IR toy"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
device = "/dev/ttyACM0"
|
||||||
|
mode = :record
|
||||||
|
file = nil
|
||||||
|
fast = false # false = default 38.4kHz, true = 56kHz
|
||||||
|
while argument = ARGV.shift do
|
||||||
|
case argument
|
||||||
|
when "--help","-h"
|
||||||
|
help
|
||||||
|
when "--device","-d"
|
||||||
|
raise "no device specified" unless device = ARGV.shift
|
||||||
|
when "--record","-r"
|
||||||
|
raise "no file specified" unless file = ARGV.shift
|
||||||
|
file = File.open(file,"w")
|
||||||
|
mode = :record
|
||||||
|
when "--play","-p"
|
||||||
|
raise "no file specified" unless file = ARGV.shift
|
||||||
|
file = File.open(file,"r")
|
||||||
|
mode = :play
|
||||||
|
when "--freq","-f"
|
||||||
|
mode = :freq
|
||||||
|
when "--laser","-l"
|
||||||
|
fast = true
|
||||||
|
when "--traffic","-t"
|
||||||
|
@debug = true
|
||||||
|
else
|
||||||
|
help
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# read n bytes from serial
|
||||||
|
def read(bytes)
|
||||||
|
data = @serial.read bytes
|
||||||
|
str = "> "
|
||||||
|
data.bytes {|b| str += "%02x " % b}
|
||||||
|
puts str if @debug
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
# write data to serial
|
||||||
|
def write(data)
|
||||||
|
to_send = nil
|
||||||
|
to_send = case data
|
||||||
|
when Fixnum
|
||||||
|
[data].pack "C"
|
||||||
|
when Array
|
||||||
|
data.pack "C*"
|
||||||
|
when String
|
||||||
|
data
|
||||||
|
else
|
||||||
|
raise "unknown data format to send"
|
||||||
|
end
|
||||||
|
|
||||||
|
str = "< "
|
||||||
|
to_send.bytes {|b| str += "%02x " % b}
|
||||||
|
puts str if @debug
|
||||||
|
|
||||||
|
@serial.write to_send
|
||||||
|
end
|
||||||
|
|
||||||
|
# open serial to USB IR Toy (adapt device path. any baudrate is accepted)
|
||||||
|
print "opening serial port #{device} … "
|
||||||
|
$stdout.flush
|
||||||
|
@serial = SerialPort.new(device,115200)
|
||||||
|
@serial.read_timeout = 0
|
||||||
|
puts "✓"
|
||||||
|
|
||||||
|
print "reseting … "
|
||||||
|
$stdout.flush
|
||||||
|
# empty buffer
|
||||||
|
@serial.read_timeout = 500
|
||||||
|
loop while @serial.read(1)
|
||||||
|
@serial.read_timeout = 0
|
||||||
|
5.times do
|
||||||
|
write [0x00]
|
||||||
|
end
|
||||||
|
puts "✓"
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
print "getting version … "
|
||||||
|
$stdout.flush
|
||||||
|
write 'v'
|
||||||
|
@serial.read_timeout = 500
|
||||||
|
version = read 4
|
||||||
|
@serial.read_timeout = 0
|
||||||
|
if version and version.length==4 then
|
||||||
|
puts "HW v#{version[1,1]}, FW v#{version[2,2]}"
|
||||||
|
else
|
||||||
|
puts "USB IR Toy is stuck. un- and re-plug it"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
print "enter sampling mode … "
|
||||||
|
$stdout.flush
|
||||||
|
write 's'
|
||||||
|
protocol = nil
|
||||||
|
begin
|
||||||
|
protocol = read 3
|
||||||
|
end until protocol and protocol.length==3
|
||||||
|
puts "protocol version: #{protocol}"
|
||||||
|
|
||||||
|
case mode
|
||||||
|
when :record
|
||||||
|
STATES = ["–","_"]
|
||||||
|
state = 0
|
||||||
|
unit = 21.3333 #us
|
||||||
|
loop do
|
||||||
|
data = read 2
|
||||||
|
time = data.unpack('n')[0]
|
||||||
|
if time==0xffff then
|
||||||
|
puts "|"
|
||||||
|
else
|
||||||
|
print "#{STATES[state%2]}#{(time*unit/100).round}#{STATES[state%2]}"
|
||||||
|
end
|
||||||
|
$stdout.flush
|
||||||
|
state += 1
|
||||||
|
file.write data if file
|
||||||
|
end
|
||||||
|
when :play
|
||||||
|
if fast then
|
||||||
|
puts "setting transmit modulation to 56kHz used by lasertag"
|
||||||
|
# use command http://dangerousprototypes.com/docs/USB_IR_Toy:_Sampling_mode#Setup_transmit_modulation_.280x06.29
|
||||||
|
# use value from http://www.micro-examples.com/public/microex-navig/doc/097-pwm-calculator (55555.56kHz)
|
||||||
|
write [0x06,0x35,0x00]
|
||||||
|
end
|
||||||
|
print "enter transmit mode (with handshake) … "
|
||||||
|
$stdout.flush
|
||||||
|
write 0x24 # enable by count report
|
||||||
|
write 0x25 # notify on complete
|
||||||
|
write 0x26 # handshake
|
||||||
|
puts "✓"
|
||||||
|
buffer = 0 # free buffer size
|
||||||
|
burst = true # start new burst transmittion
|
||||||
|
while data = file.read(2) do
|
||||||
|
if burst then
|
||||||
|
burst = false
|
||||||
|
buffer = 0
|
||||||
|
write 0x03 # transmit
|
||||||
|
puts "transmitting burst"
|
||||||
|
end
|
||||||
|
# wait for free buffer
|
||||||
|
if buffer<=0 then
|
||||||
|
buffer=read(1).unpack("C")[0]
|
||||||
|
puts "new buffer size: #{buffer}"
|
||||||
|
end
|
||||||
|
write data
|
||||||
|
buffer -= data.size
|
||||||
|
if data.unpack('n')[0]==0xffff then
|
||||||
|
# restart transmit
|
||||||
|
burst = true
|
||||||
|
# last handshake(s) until count byte
|
||||||
|
begin
|
||||||
|
report = read(1)
|
||||||
|
sleep 1
|
||||||
|
end until report and report[0]=='t'
|
||||||
|
# byte count
|
||||||
|
count = read(2)
|
||||||
|
puts "#{count[0,2].unpack('n')[0]} bytes processed"
|
||||||
|
# notification
|
||||||
|
case tmp=read(1)
|
||||||
|
when "C"
|
||||||
|
puts "transmission complete"
|
||||||
|
when "F"
|
||||||
|
puts "buffer underrun"
|
||||||
|
else
|
||||||
|
puts "unknown notification: #{tmp}"
|
||||||
|
end
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
when :freq
|
||||||
|
print "waiting for a burst … "
|
||||||
|
loop until read(2).unpack('n')[0]==0xffff
|
||||||
|
$stdout.flush
|
||||||
|
puts "✓"
|
||||||
|
write 0x04
|
||||||
|
data = read(8).unpack('n*')
|
||||||
|
# PIC clock is 12MHz
|
||||||
|
clock = 1.0/12E6
|
||||||
|
freq1 = (1/(clock*(data[0])))/(1E3)
|
||||||
|
freq2 = (1/(clock*(data[1]-data[0])))/(1E3)
|
||||||
|
freq3 = (1/(clock*(data[2]-data[1])))/(1E3)
|
||||||
|
puts "detected frequency: #{freq1.round(2)}kHz, #{freq2.round(2)}kHz, #{freq3.round(2)}kHz"
|
||||||
|
else
|
||||||
|
puts "unknown mode #{mode}"
|
||||||
|
end
|
Loading…
Reference in New Issue