mr_ms_206.rb-0.1-alpha.rb

The only ruby gem required for mr_ms_206-01-alpha.rb is the "serialport" gem.  This can be installed in Linux or MacOSX by executing the following command as root:

~# gem install serialport

Once you install the serialport gem and specify the block device of the MSR206 within the code in the "tty_dev" variable (defaults to "/dev/ttyUSB0") executing the application is simple from a shell:

~# ./mr_ms_206-01-alpha.rb

Lights should start flashing on your MSR206 and you should be presented with a menu ;)


# --- COPY BELOW THIS LINE!!!

#!/usr/bin/env ruby
require 'rubygems'
require 'serialport'

# Be Sure to Choose the Appropriate Block Device
# (Typically /dev/ttyUSB0 when Using a Serial to USB Cable
# Specify Serial Device
tty_dev = "/dev/ttyUSB0"
tty_baud_choices = [ 19200, 9600, 4800, 2400, 1200 ]
# 9600 Baud Seems to Work the Best as of 2010-07-06
tty_baud = tty_baud_choices[1]
tty_data_bits = 8
tty_stop_bits = 1
# Can be SerialPort::NONE, HARD, SOFT, SPACE, MARK, EVEN, or ODD
tty_parity = SerialPort::NONE
@sp = SerialPort.new(tty_dev, tty_baud, tty_data_bits, tty_stop_bits, tty_parity)
@sp.read_timeout = 100

# General Reader Commands
@gencmd = {
  :g_39=>[:version_report,"39"],
  :g_7F=>[:simulate_power_cycle_warm_reset,"7F"],
  :g_23=>[:configuration_request,"23"],
  :g_25=>[:reproduce_last_command,"25"],
  :g_11=>[:resume_transmission_to_host,"11"],
  :g_13=>[:pause_transmission_to_host,"13"],
  :g_1B=>[:abort_command,"1B"]
}

# LED Commands
@led = {
  :l_4D=>[:red_on,"4D"],
  :l_6D=>[:red_off,"6D"],
  :l_29=>[:red_flash,"29"],
  :l_4C=>[:green_on,"4C"],
  :l_6C=>[:green_off,"6C"],
  :l_28=>[:green_flash,"28"],
  :l_4B=>[:yellow_on,"4B"],
  :l_6B=>[:yellow_off,"6B"],
  :l_7C=>[:yellow_flash,"7C"]
}

# Magnetic Reading Commands
@readcmd = {
  :r_50=>[:arm_to_read,"50"],            # P
  :r_70=>[:arm_to_read_w_speed_prompts,"70"],  
  :r_51=>[:tx_iso_std_data_track1,"51"],    # Q
  :r_52=>[:tx_iso_std_data_track2,"52"],    # R
  :r_53=>[:tx_iso_std_data_track3,"53"],    # S
  :r_71=>[:alt_tx_iso_std_data_track1,"71"],    # q
  :r_72=>[:alt_tx_iso_std_data_track2,"72"],    # r
  :r_73=>[:alt_tx_iso_std_data_track3,"73"],    # s
  :r_49=>[:tx_error_data,"49"],
  :r_45=>[:tx_custom_data_forward_track1,"45"],
  :r_46=>[:tx_custom_data_forward_track2,"46"],
  :r_47=>[:tx_custom_data_forward_track3,"47"],
  :r_58=>[:tx_passbook_data,"58"],
  :r_78=>[:alt_tx_passbook_data,"78"],
  :r_3F=>[:write_verify,"3F"],
  :r_26=>[:card_edge_detect,"26"]
}

# Magnetic Writing Commands
@writecmd = {
  :w_41=>[:load_iso_std_data_for_writing_track1,"41"],
  :w_42=>[:load_iso_std_data_for_writing_track2,"42"],
  :w_43=>[:load_iso_std_data_for_writing_track3,"43"],
  :w_61=>[:alt_load_iso_std_data_for_writing_track1,"61"],
  :w_62=>[:alt_load_iso_std_data_for_writing_track2,"62"],
  :w_63=>[:alt_load_iso_std_data_for_writing_track3,"63"],
  :w_6A=>[:load_passbook_data_for_writing,"6A"],
  :w_45=>[:load_custom_data_for_writing_track1,"45"],
  :w_46=>[:load_custom_data_for_writing_track2,"46"],
  :w_47=>[:load_custom_data_for_writing_track3,"47"],
  :w_3B=>[:set_write_density,"3B"],
  :w_4F=>[:set_write_density_210_bpi_tracks_1_3,"4F"],
  :w_6F=>[:set_write_density_75_bpi_tracks_1_3,"6F"],
  :w_4E=>[:set_write_density_210_bpi_tracks_2,"4E"],
  :w_6E=>[:set_write_density_75_bpi_tracks_2,"6E"],
  :w_5B=>[:set_default_write_current,"5B"],
  :w_5D=>[:view_default_write_current,"5D"],
  :w_3C=>[:set_temp_write_current,"3C"],
  :w_3E=>[:view_temp_write_current,"3E"],
  :w_40=>[:arm_to_write_with_raw,"40"],
  :w_5A=>[:arm_to_write_no_raw,"5A"],
  :w_7A=>[:arm_to_write_with_raw_speed_prompts,"7A"]
}

@solicited_unsolicited_response = {
  :s_5E=>"Command Completed (ACK)",
  :s_21=>"Invalid Command",
  :s_2A=>"Error",
  :s_2B=>"No Data Found",
  :s_2D=>"Insufficient Leading Zeros for Custom Writing",
  :s_2F=>"lsb of First Character Not a '1' for Custom Writing",
  :s_3F=>"Communications Error",
  :s_7E=>"Can't Execute - Hardware Does Not Support Command",
  :u_3A=>"Power on Report",
  :u_5E=>"Card Swipe Occurred when Armed to Read or Write",
  :u_31=>"Unsuccessful Read After Write - Track1",
  :u_32=>"Unsuccessful Read After Write - Track2",
  :u_33=>"Unsuccessful Read After Write - Track3",
  :u_28=>"Card Speed Measurement Start",
  :u_29=>"Card Speed Measurement End",
  :u_3E=>"Card Edge Detected"
}

@charset = {
  :c_10110000=>"0",    # Confirmed
  :c_00110001=>"1",    # Confirmed
  :c_00110010=>"2",    # Confirmed
  :c_10110011=>"3",    # Confirmed
  :c_00110100=>"4",    # Confirmed
  :c_10110101=>"5",    # Confirmed
  :c_10110110=>"6",    # Confirmed
  :c_00110111=>"7",    # Confirmed
  :c_00111000=>"8",    # Confirmed
  :c_10111001=>"9",    # Confirmed
  :c_11000001=>"A",    # Confirmed
  :c_11000010=>"B",    # Confirmed
  :c_01000011=>"C",    # Confirmed
  :c_11000100=>"D",    # Confirmed
  :c_01000101=>"E",    # Confirmed
  :c_01000110=>"F",    # Confirmed
  :c_11000111=>"G",    # Confirmed
  :c_11001000=>"H",    # Confirmed
  :c_01001001=>"I",    # Confirmed
  :c_01001010=>"J",    # Confirmed
  :c_11001011=>"K",    # Confirmed
  :c_01001100=>"L",    # Confirmed
  :c_11001101=>"M",    # Confirmed
  :c_11001110=>"N",    # Confirmed
  :c_01001111=>"O",    # Confirmed
  :c_11010000=>"P",    # Confirmed
  :c_01010001=>"Q",    # Confirmed
  :c_01010010=>"R",    # Confirmed
  :c_11010011=>"S",    # Confirmed
  :c_01010100=>"T",    # Confirmed
  :c_11010101=>"U",    # Confirmed
  :c_11010110=>"V",    # Confirmed
  :c_01010111=>"W",    # Confirmed
  :c_01011000=>"X",    # Confirmed
  :c_11011001=>"Y",    # Confirmed
  :c_11011010=>"Z",    # Confirmed
  :c_00100000=>" ",    # Confirmed
  :c_01000000=>"@",    # Confirmed
  :c_10100001=>"!",    # Confirmed
  :c_10100010=>'"',    # Confirmed
  :c_00100011=>"#",    # Confirmed
  :c_10100100=>"$",    # Confirmed
  :c_00100101=>"%",    # Confirmed
  :c_00100110=>"&",    # Confirmed
  :c_000111=>"/",    # Confirmed
  :c_00101111=>"/",    # Confirmed
  :c_11011100=>"\\",    # Confirmed
  :c_10101000=>"(",    # Confirmed
  :c_00101001=>")",    # Confirmed
  :c_01011011=>"[",    # Confirmed
  :c_01011101=>"]",    # Confirmed
  :c_10111100=>"<",    # Confirmed
  :c_00111110=>">",    # Confirmed
  :c_10111010=>":",    # Confirmed
  :c_00111011=>";",    # Confirmed
  :c_00101010=>"*",    # Confirmed
  :c_10101011=>"+",    # Confirmed
  :c_00101100=>",",    # Confirmed
  :c_10101101=>"-",    # Confirmed
  :c_00111101=>"=",    # Confirmed
  :c_10101110=>".",    # Confirmed
  :c_01011110=>"^",    # Confirmed
  :c_011111=>"?",    # Unconfirmed
  :c_111111=>"DEL"    # Unconfirmed
}

# Initialize or Invoke a Command
def init_cmd(current_cmd,opt_param=nil,track=nil,verbose=false,trackdebug=false)
  @sp.write "#{current_cmd.hex.chr}#{opt_param}"
  #response = @sp.read.encoding.inspect
  response = @sp.read.force_encoding("US-ASCII")
  #response = @sp.read.force_encoding("ISO-8859-1")
  #response = @sp.read
  if (verbose == true)
    if (trackdebug == true)
      puts "Raw Request: #{current_cmd.hex.chr}#{opt_param}"
      puts "Hex Request: #{current_cmd}#{opt_param}"
    end
    @formatted_hex_response = ""
    @hex_response = ""
    @formatted_bin_response = ""
    @bin_response = ""
    response.each_char do |hex_char|
      @formatted_hex_response << "#{hex_char.unpack('H*')[0].upcase} "
      @hex_response << "#{hex_char.unpack('H*')[0].upcase}"
      @formatted_bin_response << "#{hex_char.unpack('B*')[0].upcase} "
      @bin_response << "#{hex_char.unpack('B*')[0].upcase}"
    end

    chunk_binary = @bin_response.to_s.scan(/.{8}/)
    puts "Encoding: #{response.encoding.name}"
    if (trackdebug == true)
      puts "ASCII Hex Response (Raw): #{@hex_response}"
      puts "ASCII Hex Response (Formatted): #{@formatted_hex_response}"
      puts "Binary Response (Raw): #{@bin_response}"
      puts "Binary Response (Formatted): #{chunk_binary}"
      puts "Binary Response Formatted Array Count: #{chunk_binary.size}"
    end
    puts "Raw Response: #{response}"
    puts "Raw Character Length: #{response.size}"

    case track
      when 1
        decoded_response = ""
        puts "Decoded Track 1: "
        chunk_binary.each do |bin_arr_value|
          t1_key = "c_#{bin_arr_value}".to_sym
          if (@charset[t1_key])
            decoded_response << @charset[t1_key]
            print @charset[t1_key]
          end
        end
        print "\n"
        puts "Decoded Track 1 Character Length: #{decoded_response.size}"
      when 2
        decoded_response = ""
        puts "Decoded Track 2: "
        chunk_binary.each do |bin_arr_value|
          t2_key = "c_#{bin_arr_value}".to_sym
          if (@charset[t2_key])
            decoded_response << @charset[t2_key]
            print @charset[t2_key]
          end
        end
        print "\n"
        puts "Decoded Track 2 Character Length: #{decoded_response.size}"
      when 3
        decoded_response = ""
        puts "Decoded Track 3: "
        chunk_binary.each do |bin_arr_value|
          t3_key = "c_#{bin_arr_value}".to_sym
          if (@charset[t3_key])
            decoded_response << @charset[t3_key]
            print @charset[t3_key]
          end
        end
        print "\n"
        puts "Decoded Track 3 Character Length: #{decoded_response.size}"
      else
        decoded_response = ""
        puts "Decoded #{track} Response: "
        chunk_binary.each do |bin_arr_value|
          key = "c_#{bin_arr_value}".to_sym
          if (@charset[key])
            decoded_response << @charset[key]
            print @charset[key]
          end
        end
        print "\n"
        puts "Decoded #{track} Response Character Length: #{decoded_response.size}"
    end
    s_key = "s_#{@hex_response}".to_sym
    u_key = "u_#{@hex_response}".to_sym
    if (@solicited_unsolicited_response[s_key])
      puts "Solicited Response: #{@solicited_unsolicited_response[s_key]}"
    else
      if (trackdebug == true)
        puts "Solicited Response: nil"
      end
    end
    if (@solicited_unsolicited_response[u_key])
      puts "Unsolicited Response: #{@solicited_unsolicited_response[u_key]}"
    else
      if (trackdebug == true)
        puts "Unsolicited Response: nil"
      end
    end
    print "\n"
  end
  return 0
end

# Startup Light Intro - Used to Ensure All LEDs are Turning On/Off/Blinking i.e. Working Properly
def led_test
  sleep 1
  init_cmd(@led[:l_4D][1])
  init_cmd(@led[:l_4B][1])
  init_cmd(@led[:l_4C][1])
  sleep 1.5
  init_cmd(@led[:l_29][1])
  init_cmd(@led[:l_7C][1])
  init_cmd(@led[:l_28][1])
  sleep 1.5
  init_cmd(@led[:l_6D][1])
  init_cmd(@led[:l_6B][1])
  init_cmd(@led[:l_6C][1])
  return 0
end

def load_file(file)
  puts File.read(file)
  main_menu_options
end

def invalid_menu_option
  puts "Invalid Menu Option!"
  init_cmd(@led[:l_6C][1])
  init_cmd(@led[:l_6B][1])
  init_cmd(@led[:l_29][1])
  sleep 2
  init_cmd(@led[:l_6D][1])
end

def advanced_menu_options
  while true
    puts "\n>> ADVANCED MENU OPTIONS:"
    puts "[(G)eneral Commands]"
    puts "[(R)ead Commands]"
    puts "[(W)rite Commands]"
    puts "[(L)ED Commands]"
    puts "[(E)xecute a Command]"
    puts "[(B)ack]"
    print ">> SELECT A MENU OPTION OR ENTER A COMMAND: "
    option = gets.chomp
    case option
      when "G","g" then
        puts "\tGENERAL COMMANDS:"
        puts "\t\tcmd\tdesc"
        @gencmd.each do |key,val|
          puts "\t\t#{key}\t#{val.inspect}"
        end
      when "R","r" then
        puts "\tREAD COMMANDS:"
        puts "\t\tcmd\tdesc"
        @readcmd.each do |key,val|
          puts "\t\t#{key}\t#{val.inspect}"
        end
      when "W","w" then
        puts "\tWRITE COMMANDS:"
        puts "\t\tcmd\tdesc"
        @writecmd.each do |key,val|
          puts "\t\t#{key}\t#{val.inspect}"
        end
      when "L","l" then
        puts "\tLED COMMANDS:"
        puts "\t\tcmd\tdesc"
        @led.each do |key,val|
          puts "\t\t#{key}\t#{val.inspect}"
        end
      when "B","b" then
        main_menu_options
      else
        if (@gencmd[option.to_sym])
          init_cmd(@gencmd[option.to_sym][1],nil,nil,true,false)
        elsif (@writecmd[option.to_sym])
          init_cmd(@writecmd[option.to_sym][1],nil,nil,true,false)
        elsif (@readcmd[option.to_sym])
          init_cmd(@readcmd[option.to_sym][1],nil,nil,true,false)
        elsif (@led[option.to_sym])
          init_cmd(@led[option.to_sym][1])
        else
          invalid_menu_option
        end
    end
  end
end

def main_menu_options
  while true
    puts "\n>> MAIN MENU OPTIONS:"
    puts "[(A)dvanced Menu Options]"
    puts "[(R)ead Card]"
    puts "[(O)pen File]"
    puts "[(E)dit Card Data]"
    puts "[(W)rite Card]"
    puts "[(S)ave to File]"
    puts "[(Q)uit]"
    print ">> SELECT A MENU OPTION: "
    option = gets.chomp
    case option
      when "A","a" then
        advanced_menu_options
      when "R","r" then
        init_cmd(@led[:l_4C][1])
        wait_for_swipe(:read)
      when "O", "o" then
        print "File Location: "
        file = gets.chomp
        load_file(file)
      when "W","w" then
        puts "Coming Soon..."
        wait_for_swipe(:write)
      when "Q","q" then
        init_cmd(@led[:l_6C][1])
        init_cmd(@led[:l_6B][1])
        exit
      else
        invalid_menu_option
    end
  end
end

def dump_read_response(extra_formats=false)
  puts "*** ISO Track Format: Standard #{'*'*17}"
  print ">> Track 1\n"
  # Toggle last two bool params to increase verbosity for debugging
  #init_cmd(@readcmd[:r_51][1],nil,1,true,true)
  init_cmd(@readcmd[:r_51][1],nil,1,true,false)
  print "\n>> Track 2\n"
  #init_cmd(@readcmd[:r_52][1],nil,2,true,true)
  init_cmd(@readcmd[:r_52][1],nil,2,true,false)
  print "\n>> Track 3\n"
  #init_cmd(@readcmd[:r_53][1],nil,3,true,true)
  init_cmd(@readcmd[:r_53][1],nil,3,true,false)
  if (extra_formats == true)
    3.times do |i|
      i+=1
      puts "*** ISO Track Format: #{i} #{'*'*24}"
      print ">> Track 1\n"
      #init_cmd(@readcmd[:r_71][1],i,1,true,true)
      init_cmd(@readcmd[:r_71][1],i,1,true,false)
      print "\n>> Track 2\n"
      #init_cmd(@readcmd[:r_72][1],i,2,true,true)
      init_cmd(@readcmd[:r_71][1],i,1,true,false)
      print "\n>> Track 3\n"
      #init_cmd(@readcmd[:r_73][1],i,3,true,true)
      init_cmd(@readcmd[:r_71][1],i,1,true,false)
      print "\n"
    end
  end
  main_menu_options
end

def wait_for_swipe(type)
  case type
    when :read then
      init_cmd(@readcmd[:r_50][1])
    when :write then
      init_cmd(@writecmd[:w_40][1])
    else
      puts "Unknown 'wait_for_swipe' method: 'type' parameter.  Goodbye."
      return 1
  end
  print "Ready.  Please Swipe Card Now:"
  while true do
    response = @sp.read.unpack('H*')[0].upcase
    if (response == "5E")
      break
    end

    s_key = "s_#{response.unpack('H*')[0].upcase}".to_sym
    u_key = "u_#{response.unpack('H*')[0].upcase}".to_sym
    if (@solicited_unsolicited_response[s_key])
      puts "Solicited Response: (#{response}) #{@solicited_unsolicited_response[s_key]}"
    end

    if (@solicited_unsolicited_response[u_key])
      puts "Unsolicited Response: (#{response}) #{@solicited_unsolicited_response[u_key]}"
    end

    if (response != "")
      puts "Attention Potential SUResponse Received: #{response}"
    end  
  end
  print "\n"
  init_cmd(@led[:l_4B][1])
  dump_read_response
end

puts "Welcome to #{$0} - Enjoy the Lightshow!"
puts "Connected to Device: #{tty_dev} #{tty_baud}bps #{tty_data_bits} #{tty_stop_bits} #{tty_parity}"
# Reset Everything in Case the MSR206-7HLR is Powered in a Weird State
init_cmd(@gencmd[:g_7F][1])
#init_cmd(@gencmd[:g_13][1])
#init_cmd(@gencmd[:g_23][1])
sleep 1

# Test LEDs
#led_test

# Starting Application
init_cmd(@gencmd[:g_39][1],nil,"Version",true,true)
# Turn on Green LED
#init_cmd(@led[:l_4C][1])
main_menu_options
 
@sp.close

# --- END OF CODE

If you enjoyed this post, send us = kudos = 
(Bitcoin Addr: 19n6q3GZfoM64oqv5HsDnhzqvcEvJUvmdx)