artoo 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed97e1d66baccca21165b0960b2ed18212b2a1d4
4
- data.tar.gz: 77e070f5faa53a75532d5c73f87c2016d9e0f23a
3
+ metadata.gz: c7ec92eb682584d629792c9e30321ec075c44ccc
4
+ data.tar.gz: 469f3321dd5bf852382c66ecf475ad272d0913bd
5
5
  SHA512:
6
- metadata.gz: 6566a8f925b396b38e0641e64548f42787d0611852733077697efad840b972281cc0ac8d218801476593a590c52287802a6bdd8bb25758c19430eaa4c20c4e67
7
- data.tar.gz: 4107fcfa598cc71a9d3a5e11f26b1885336e0b5c64ae264fc13150162db6fe4055d54db3984007c46886a3c74306c92fc3dadb66392d1b41b820888b34662af0
6
+ metadata.gz: 34faae07ab1729b9ef5dc82549ccb6e140d6452db47dc8eb2c451b12a45b645f22a7e1c47f0ca9624b20ffdcb58c2224d9cc2802590adcad76268bb05b3f9739
7
+ data.tar.gz: 0b97958c5708ecd774c3de2aa1b2c0cdc3905573f5296781cb92d29622caeb2dea6baef8c78a2a17382ddf760ecfb2d36aaca412de69d133436a8360b705cf24
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- artoo (0.2.0)
4
+ artoo (0.3.0)
5
5
  active_support (~> 3.0)
6
- celluloid-io (~> 0.12)
6
+ celluloid-io (~> 0.13)
7
7
  multi_json (~> 1.6)
8
+ pry (~> 0.9)
8
9
  rake (~> 10.0)
9
10
  reel (~> 0.3)
10
11
 
@@ -16,11 +17,10 @@ GEM
16
17
  activesupport (3.0.0)
17
18
  bootstrap-sass (2.2.2.0)
18
19
  sass (~> 3.2)
19
- celluloid (0.12.4)
20
- facter (>= 1.6.12)
20
+ celluloid (0.13.0)
21
21
  timers (>= 1.0.0)
22
- celluloid-io (0.12.1)
23
- celluloid (~> 0.12.0)
22
+ celluloid-io (0.13.1)
23
+ celluloid (>= 0.13.0)
24
24
  nio4r (>= 0.4.0)
25
25
  certified (0.1.1)
26
26
  chunky_png (1.2.7)
@@ -41,7 +41,6 @@ GEM
41
41
  eventmachine (1.0.0-java)
42
42
  execjs (1.4.0)
43
43
  multi_json (~> 1.0)
44
- facter (1.6.17)
45
44
  fssm (0.2.10)
46
45
  guard (1.6.2)
47
46
  listen (>= 0.6.0)
@@ -79,6 +78,7 @@ GEM
79
78
  metaclass (~> 0.0.1)
80
79
  multi_json (1.6.1)
81
80
  nio4r (0.4.3)
81
+ nio4r (0.4.3-java)
82
82
  pry (0.9.12)
83
83
  coderay (~> 1.0.5)
84
84
  method_source (~> 0.8)
@@ -89,7 +89,7 @@ GEM
89
89
  slop (~> 3.4)
90
90
  spoon (~> 0.0)
91
91
  rack (1.5.2)
92
- rake (10.0.3)
92
+ rake (10.0.4)
93
93
  reel (0.3.0)
94
94
  celluloid-io (>= 0.8.0)
95
95
  http (>= 0.2.0)
@@ -108,7 +108,7 @@ GEM
108
108
  thor (0.17.0)
109
109
  tilt (1.3.3)
110
110
  timers (1.1.0)
111
- websocket_parser (0.1.1)
111
+ websocket_parser (0.1.2)
112
112
  http
113
113
 
114
114
  PLATFORMS
data/README.md CHANGED
@@ -71,7 +71,7 @@ SPHEROS.each {|p|
71
71
  SpheroRobot.work!(robots)
72
72
  ```
73
73
 
74
- Ruby versions supported: Ruby 2.0, 1.9.3, JRuby 1.7.2, and Rubinius 2.0
74
+ Ruby versions supported: Ruby 2.0, Ruby 1.9.3, JRuby 1.7.2, and Rubinius 2.0-rc1
75
75
 
76
76
 
77
77
  Artoo is conceptualy influenced by Sinatra (https://github.com/sinatra/sinatra) as well as borrowing some code from it.
@@ -100,6 +100,23 @@ end
100
100
 
101
101
  Once the robot or group is working, you can view the main API page at the host and port specified.
102
102
 
103
+ ## CLI:
104
+
105
+ Artoo includes Robi, a Command Line Interface (CLI) based on Pry (http://pryrepl.org/) to allow you to interactively control your robot.
106
+
107
+ ```
108
+ robi ./examples/hello.rb
109
+ I, [2013-03-16T18:14:22.281462 #61513] INFO -- : Registering connection 'loop'...
110
+ I, [2013-03-16T18:14:22.283027 #61513] INFO -- : Preparing work...
111
+ [1] pry(main)> start
112
+ I, [2013-03-16T18:14:23.836523 #61513] INFO -- : Initializing connection loop...
113
+ I, [2013-03-16T18:14:23.842265 #61513] INFO -- : Starting work...
114
+ I, [2013-03-16T18:14:23.842879 #61513] INFO -- : Connecting to 'loop' on port '#<Artoo::Port:0xe3c0>'...
115
+ [2] pry(main)> list
116
+ #<Artoo::MainRobot:0xe5e4>
117
+ [3] pry(main)> exit
118
+ ```
119
+
103
120
  ## Installing:
104
121
 
105
122
  ```ruby
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+ require 'pry'
3
4
 
4
5
  require 'rake/testtask'
5
6
 
@@ -9,3 +10,10 @@ Rake::TestTask.new do |t|
9
10
  end
10
11
 
11
12
  task :default => :test
13
+
14
+ desc "Start an interactive session with robot(s) loaded."
15
+ task :console, :file do |t, args|
16
+ robot_file = args[:file]
17
+ exec "bundle exec robi #{robot_file}"
18
+ end
19
+
@@ -19,11 +19,12 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_runtime_dependency 'celluloid-io', '~> 0.12'
22
+ s.add_runtime_dependency 'celluloid-io', '~> 0.13'
23
23
  s.add_runtime_dependency 'reel', '~> 0.3'
24
24
  s.add_runtime_dependency 'multi_json', '~> 1.6'
25
25
  s.add_runtime_dependency 'active_support', '~> 3.0'
26
26
  s.add_runtime_dependency 'rake', '~> 10.0'
27
+ s.add_runtime_dependency 'pry', '~> 0.9'
27
28
  s.add_development_dependency 'minitest', '~> 4.6'
28
29
  s.add_development_dependency 'mocha', '~> 0.13'
29
30
  end
File without changes
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pry'
3
+
4
+ Pry.config.prompt = proc { "robi> " }
5
+
6
+ command_set = Pry::CommandSet.new do
7
+ block_command "start", "Start all robots working" do
8
+ if defined?(Artoo::MainRobot)
9
+ output.puts "Starting main robot..."
10
+ Artoo::MainRobot.work! unless Artoo::MainRobot.is_running?
11
+ else
12
+ output.puts "Starting robots..."
13
+ end
14
+
15
+ Celluloid::Actor[:master].start_work
16
+ end
17
+
18
+ block_command "pause", "Pause all robots" do
19
+ output.puts "Pausing robots..."
20
+ Celluloid::Actor[:master].pause_work
21
+ end
22
+
23
+ block_command "continue", "Continue all robots" do
24
+ output.puts "Continuing robots..."
25
+ Celluloid::Actor[:master].continue_work
26
+ end
27
+
28
+ block_command "stop", "Stop all robots" do
29
+ output.puts "Not yet implemented..."
30
+ Celluloid::Actor[:master].stop_work
31
+ end
32
+
33
+ block_command "list", "List all robots" do
34
+ output.puts Celluloid::Actor[:master].robots
35
+ end
36
+ end
37
+
38
+ Pry::Commands.import command_set
39
+
40
+ ENV["ARTOO_CLI"] = 'true'
41
+ require ARGV[0]
42
+ Pry.start
@@ -66,8 +66,6 @@ work do
66
66
  on classic, :left_trigger => proc { |*value|
67
67
  if value[1] > 0
68
68
  drone.turn_left(validate_pitch(value[1], @offsets[:lt]))
69
- else
70
- drone.turn_left(0.0)
71
69
  end
72
70
  }
73
71
  end
@@ -1,6 +1,6 @@
1
1
  require 'artoo/robot'
2
2
 
3
- class SpheroRobot < Artoo::Robot
3
+ class ConwaySpheroRobot < Artoo::Robot
4
4
  connection :sphero, :adaptor => :sphero
5
5
  device :sphero, :driver => :sphero
6
6
 
@@ -59,9 +59,9 @@ SPHEROS = {"127.0.0.1:4560" => "/dev/tty.Sphero-BRG-RN-SPP",
59
59
  "127.0.0.1:4566" => "/dev/tty.Sphero-PYG-RN-SPP"}
60
60
  robots = []
61
61
  SPHEROS.each_key {|p|
62
- robots << SpheroRobot.new(:connections =>
62
+ robots << ConwaySpheroRobot.new(:connections =>
63
63
  {:sphero =>
64
64
  {:port => p}})
65
65
  }
66
66
 
67
- SpheroRobot.work!(robots)
67
+ ConwaySpheroRobot.work!(robots)
@@ -5,7 +5,7 @@ class HelloRobot < Artoo::Robot
5
5
 
6
6
  work do
7
7
  every(3.seconds) do
8
- puts "Hello from #{name}"
8
+ puts "Hello from #{name}"
9
9
  end
10
10
  after(10.seconds) do
11
11
  puts "#{name} is alive!" if name == 'Number 5'
@@ -19,4 +19,3 @@ robots = []
19
19
  end
20
20
 
21
21
  HelloRobot.work!(robots)
22
- sleep
@@ -1,6 +1,6 @@
1
1
  require 'artoo/robot'
2
2
 
3
- class SpheroRobot < Artoo::Robot
3
+ class DoubleSpheroRobot < Artoo::Robot
4
4
  connection :sphero, :adaptor => :sphero
5
5
  device :sphero, :driver => :sphero
6
6
 
@@ -18,9 +18,9 @@ SPHEROS = {"127.0.0.1:4560" => "/dev/tty.Sphero-BRG-RN-SPP",
18
18
  "127.0.0.1:4561" => "/dev/tty.Sphero-YBW-RN-SPP"}
19
19
  robots = []
20
20
  SPHEROS.each_key {|p|
21
- robots << SpheroRobot.new(:connections =>
21
+ robots << DoubleSpheroRobot.new(:connections =>
22
22
  {:sphero =>
23
23
  {:port => p}})
24
24
  }
25
25
 
26
- SpheroRobot.work!(robots)
26
+ DoubleSpheroRobot.work!(robots)
@@ -0,0 +1,26 @@
1
+ require 'artoo'
2
+
3
+ connection :arduino, :adaptor => :firmata, :port => "8023"
4
+ device :classic, :driver => :wiiclassic, :connection => :arduino, :interval => 0.1
5
+
6
+ work do
7
+ on classic, :a_button => proc { puts "a button pressed!" }
8
+ on classic, :b_button => proc { puts "b button pressed!" }
9
+ on classic, :x_button => proc { puts "x button pressed!" }
10
+ on classic, :y_button => proc { puts "y button pressed!" }
11
+ on classic, :home_button => proc { puts "home button pressed!" }
12
+ on classic, :start_button => proc { puts "start button pressed!" }
13
+ on classic, :select_button => proc { puts "select button pressed!" }
14
+ on classic, :left_joystick => proc { |*value|
15
+ puts "left joystick x: #{value[1][:x]}, y: #{value[1][:y]}" unless (value[1][:x] == 0 && value[1][:y] == 0)
16
+ }
17
+ on classic, :right_joystick => proc { |*value|
18
+ puts "right joystick x: #{value[1][:x]}, y: #{value[1][:y]}" unless (value[1][:x] == 0 && value[1][:y] == 0)
19
+ }
20
+ on classic, :right_trigger => proc { |*value|
21
+ puts "right trigger: #{value[1]}" unless (value[1]== 0)
22
+ }
23
+ on classic, :left_trigger => proc { |*value|
24
+ puts "left trigger: #{value[1]}" unless (value[1]== 0)
25
+ }
26
+ end
@@ -4,6 +4,7 @@ module Artoo
4
4
  module Adaptors
5
5
  # Connect to a ARDrone 2.0 (http://ardrone2.parrot.com/)
6
6
  class Ardrone < Adaptor
7
+ finalizer :finalize
7
8
  attr_reader :ardrone
8
9
 
9
10
  def finalize
@@ -4,7 +4,7 @@ module Artoo
4
4
  module Adaptors
5
5
  # Connect to a Roomba (http://www.irobot.com/en/us/robots/Educators/Create.aspx)
6
6
  class Roomba < Adaptor
7
-
7
+ finalizer :finalize
8
8
  attr_reader :sp
9
9
 
10
10
  def finalize
@@ -4,6 +4,7 @@ module Artoo
4
4
  module Adaptors
5
5
  # Connect to a Sphero (http://gosphero.com)
6
6
  class Sphero < Adaptor
7
+ finalizer :finalize
7
8
  RETRY_COUNT = 5
8
9
  attr_reader :sphero
9
10
 
@@ -17,32 +17,42 @@ module Artoo
17
17
  end
18
18
 
19
19
  get '/robots' do
20
- MultiJson.dump(Actor[:master].robots.collect {|r|r.to_hash})
20
+ MultiJson.dump(master.robots.collect {|r|r.to_hash})
21
21
  end
22
22
 
23
23
  get '/robots/:robotid' do
24
- Actor[:master].get_robot_by_name(@params['robotid']).as_json
24
+ master.get_robot(@params['robotid']).as_json
25
25
  end
26
26
 
27
27
  get '/robots/:robotid/devices' do
28
- MultiJson.dump(Actor[:master].get_robot_by_name(@params['robotid']).devices.each_value.collect {|d| d.to_hash})
28
+ MultiJson.dump(master.get_robot_devices(@params['robotid']).each_value.collect {|d| d.to_hash})
29
29
  end
30
30
 
31
31
  get '/robots/:robotid/devices/:deviceid' do
32
- Actor[:master].get_robot_by_name(@params['robotid']).devices[@params['deviceid'].intern].as_json
32
+ device(@params['robotid'], @params['deviceid']).as_json
33
33
  end
34
34
 
35
35
  get_ws '/robots/:robotid/devices/:deviceid/events' do
36
- DeviceEventClient.new(@req, Actor[:master].get_robot_by_name(@params['robotid']).devices[@params['deviceid'].intern].event_topic_name('update'))
36
+ DeviceEventClient.new(@req, device(@params['robotid'], @params['deviceid']).event_topic_name('update'))
37
37
  return nil
38
38
  end
39
39
 
40
40
  get '/robots/:robotid/connections' do
41
- MultiJson.dump(Actor[:master].get_robot_by_name(@params['robotid']).connections.each_value.collect {|c| c.to_hash})
41
+ MultiJson.dump(master.get_robot_connections(@params['robotid']).each_value.collect {|c| c.to_hash})
42
42
  end
43
43
 
44
44
  get '/robots/:robotid/connections/:connectionid' do
45
- Actor[:master].get_robot_by_name(@params['robotid']).connections[@params['connectionid'].intern].as_json
45
+ master.get_robot_connection(@params['robotid'], @params['connectionid']).as_json
46
+ end
47
+
48
+ protected
49
+
50
+ def master
51
+ Actor[:master]
52
+ end
53
+
54
+ def device(robot_id, device_id)
55
+ master.get_robot_device(robot_id, device_id)
46
56
  end
47
57
  end
48
58
  end
@@ -47,6 +47,14 @@ module Artoo
47
47
  MultiJson.dump(to_hash)
48
48
  end
49
49
 
50
+ def to_s
51
+ "#{self.class}:0x#{self.object_id}"
52
+ end
53
+
54
+ def inspect
55
+ "#<#{to_s}>"
56
+ end
57
+
50
58
  def method_missing(method_name, *arguments, &block)
51
59
  unless adaptor.connected?
52
60
  Logger.warn "Cannot call unconnected adaptor '#{name}', attempting to reconnect..."
@@ -51,6 +51,10 @@ module Artoo
51
51
  driver.send(method_name, *arguments, &block)
52
52
  end
53
53
 
54
+ def inspect
55
+ "#<Device @id=#{object_id}, @name='name', @driver='driver'>"
56
+ end
57
+
54
58
  private
55
59
 
56
60
  def require_driver(d)
@@ -1,60 +1,16 @@
1
- require 'artoo/drivers/driver'
1
+ require 'artoo/drivers/wiidriver'
2
2
 
3
3
  module Artoo
4
4
  module Drivers
5
5
  # Wiichuck driver behaviors for Firmata
6
- class Wiichuck < Driver
7
- attr_reader :joystick
8
-
9
- def address; 0x52; end
10
-
11
- INITIAL_DEFAULTS = {
12
- :sy_origin => nil,
13
- :sx_origin => nil
14
- }
15
-
16
- def initialize(params={})
17
- @joystick = INITIAL_DEFAULTS
18
- super
19
- end
20
-
21
- def start_driver
22
- begin
23
- listener = ->(value) { update(value) }
24
- connection.on("i2c_reply", listener)
25
-
26
- connection.i2c_config(0)
27
- every(interval) do
28
- connection.i2c_write_request(address, 0x40, 0x00)
29
- p
30
- connection.i2c_write_request(address, 0x00, 0x00)
31
- p
32
- connection.i2c_read_request(address, 6)
33
- p
34
- connection.read_and_process
35
- end
36
-
37
- super
38
- rescue Exception => e
39
- p "start driver"
40
- p e.message
41
- p e.backtrace.inspect
42
- end
43
-
44
- end
45
-
6
+ class Wiichuck < Wiidriver
46
7
  def update(value)
47
8
  begin
48
- if encrypted?(value)
49
- Logger.error "Encrypted bytes from wiichuck!"
50
- return
51
- end
9
+ super
52
10
 
53
- data = parse_wiichuck(value)
54
-
55
- adjust_origins(data)
56
- update_buttons(data)
57
- update_joystick(data)
11
+ adjust_origins
12
+ update_buttons
13
+ update_joystick
58
14
 
59
15
  rescue Exception => e
60
16
  Logger.error "wiichuck update exception!"
@@ -63,44 +19,35 @@ module Artoo
63
19
  end
64
20
  end
65
21
 
66
- def adjust_origins(data)
22
+ def adjust_origins
67
23
  set_joystick_default_value(:sy_origin, data[:sy])
68
24
  set_joystick_default_value(:sx_origin, data[:sx])
69
25
  end
70
26
 
71
- def set_joystick_default_value(joystick_axis, default_value)
72
- joystick[joystick_axis] = default_value if joystick[joystick_axis].nil?
73
- end
74
-
75
- def update_buttons(data)
27
+ def update_buttons
76
28
  publish(event_topic_name("c_button")) if data[:c] == true
77
29
  publish(event_topic_name("z_button")) if data[:z] == true
78
30
  end
79
31
 
80
- def update_joystick(data)
81
- publish(event_topic_name("joystick"), {:x => data[:sx] - @joystick[:sx_origin], :y => data[:sy] - @joystick[:sy_origin]})
32
+ def update_joystick
33
+ publish(event_topic_name("joystick"), {:x => calculate_joystick_value(:sx, :sx_origin), :y => calculate_joystick_value(:sy, :sy_origin)})
82
34
  end
83
35
 
84
36
  private
85
37
 
86
- def encrypted?(value)
87
- value[:data][0] == value[:data][1] && value[:data][2] == value[:data][3] && value[:data][4] == value[:data][5]
88
- end
89
-
90
- def decode( x )
91
- return ( x ^ 0x17 ) + 0x17
92
- end
93
-
94
- def get_value(value, index)
95
- decode(value[:data][index])
38
+ def get_defaults
39
+ {
40
+ :sy_origin => nil,
41
+ :sx_origin => nil
42
+ }
96
43
  end
97
44
 
98
- def parse_wiichuck(value)
45
+ def parse(value)
99
46
  return {
100
- :sx => get_value(value, 0),
101
- :sy => get_value(value, 1),
102
- :z => (get_value(value, 5) & 0x01 == 0 ? true : false ),
103
- :c => (get_value(value, 5) & 0x02 == 0 ? true : false )
47
+ :sx => decode_value(value, 0),
48
+ :sy => decode_value(value, 1),
49
+ :z => generate_bool(decode_value(value, 5) & 0x01),
50
+ :c => generate_bool(decode_value(value, 5) & 0x02)
104
51
  }
105
52
  end
106
53
  end
@@ -1,66 +1,18 @@
1
- require 'artoo/drivers/driver'
1
+ require 'artoo/drivers/wiidriver'
2
2
 
3
3
  module Artoo
4
4
  module Drivers
5
5
  # Wiiclassic driver behaviors for Firmata
6
- class Wiiclassic < Driver
7
- attr_reader :joystick
8
-
9
- def address; 0x52; end
10
-
11
- INITIAL_DEFAULTS = {
12
- :ry_origin => nil,
13
- :rx_origin => nil,
14
- :ly_origin => nil,
15
- :lx_origin => nil,
16
- :rt_origin => nil,
17
- :lt_origin => nil
18
- }
19
-
20
- def initialize(params={})
21
- @joystick = INITIAL_DEFAULTS
22
- super
23
- end
24
-
25
- def start_driver
26
- begin
27
- listener = ->(value) { update(value) }
28
- connection.on("i2c_reply", listener)
29
-
30
- connection.i2c_config(0)
31
- every(interval) do
32
- connection.i2c_write_request(address, 0x40, 0x00)
33
- p
34
- connection.i2c_write_request(address, 0x00, 0x00)
35
- p
36
- connection.i2c_read_request(address, 6)
37
- p
38
- connection.read_and_process
39
- end
40
-
41
- super
42
- rescue Exception => e
43
- p "start driver"
44
- p e.message
45
- p e.backtrace.inspect
46
- end
47
-
48
- end
49
-
6
+ class Wiiclassic < Wiidriver
50
7
  def update(value)
51
8
  begin
52
- if encrypted?(value)
53
- Logger.error "Encrypted bytes from wiiclassic!"
54
- return
55
- end
56
-
57
- data = parse_wiiclassic(value)
9
+ super
58
10
 
59
- adjust_origins(data)
60
- update_buttons(data)
61
- update_left_joystick(data)
62
- update_right_joystick(data)
63
- update_triggers(data)
11
+ adjust_origins
12
+ update_buttons
13
+ update_left_joystick
14
+ update_right_joystick
15
+ update_triggers
64
16
 
65
17
  rescue Exception => e
66
18
  Logger.error "wiiclassic update exception!"
@@ -69,7 +21,7 @@ module Artoo
69
21
  end
70
22
  end
71
23
 
72
- def adjust_origins(data)
24
+ def adjust_origins
73
25
  set_joystick_default_value(:ly_origin, data[:ly])
74
26
  set_joystick_default_value(:lx_origin, data[:lx])
75
27
  set_joystick_default_value(:ry_origin, data[:ry])
@@ -78,73 +30,100 @@ module Artoo
78
30
  set_joystick_default_value(:lt_origin, data[:lt])
79
31
  end
80
32
 
81
- def set_joystick_default_value(joystick_axis, default_value)
82
- joystick[joystick_axis] = default_value if joystick[joystick_axis].nil?
33
+ def update_buttons
34
+ update_button("a_button", :a)
35
+ update_button("b_button", :b)
36
+ update_button("x_button", :x)
37
+ update_button("y_button", :y)
38
+ update_button("home_button", :h)
39
+ update_button("start_button", :+)
40
+ update_button("select_button", :-)
41
+ end
42
+
43
+ def update_button(name, key)
44
+ publish(event_topic_name(name)) if data[key] == true
83
45
  end
84
46
 
85
- def update_buttons(data)
86
- publish(event_topic_name("a_button")) if data[:a] == true
87
- publish(event_topic_name("b_button")) if data[:b] == true
88
- publish(event_topic_name("x_button")) if data[:x] == true
89
- publish(event_topic_name("y_button")) if data[:y] == true
90
- publish(event_topic_name("home_button")) if data[:h] == true
91
- publish(event_topic_name("start_button")) if data[:+] == true
92
- publish(event_topic_name("select_button")) if data[:-] == true
47
+ def update_left_joystick
48
+ publish(event_topic_name("left_joystick"), {:x => calculate_joystick_value(:lx, :lx_origin), :y => calculate_joystick_value(:ly, :ly_origin)})
93
49
  end
94
50
 
95
- def update_left_joystick(data)
96
- publish(event_topic_name("left_joystick"), {:x => data[:lx] - @joystick[:lx_origin], :y => data[:ly] - @joystick[:ly_origin]})
51
+ def update_right_joystick
52
+ publish(event_topic_name("right_joystick"), {:x => calculate_joystick_value(:rx, :rx_origin), :y => calculate_joystick_value(:ry, :ry_origin)})
97
53
  end
98
54
 
99
- def update_right_joystick(data)
100
- publish(event_topic_name("right_joystick"), {:x => data[:rx] - @joystick[:rx_origin], :y => data[:ry] - @joystick[:ry_origin]})
55
+ def update_triggers
56
+ publish(event_topic_name("right_trigger"), calculate_joystick_value(:rt, :rt_origin))
57
+ publish(event_topic_name("left_trigger"), calculate_joystick_value(:lt, :lt_origin))
101
58
  end
102
59
 
103
- def update_triggers(data)
104
- publish(event_topic_name("right_trigger"), data[:rt] - @joystick[:rt_origin])
105
- publish(event_topic_name("left_trigger"), data[:lt] - @joystick[:lt_origin])
60
+ private
61
+
62
+ def get_defaults
63
+ {
64
+ :ry_origin => nil,
65
+ :rx_origin => nil,
66
+ :ly_origin => nil,
67
+ :lx_origin => nil,
68
+ :rt_origin => nil,
69
+ :lt_origin => nil
70
+ }
71
+ end
72
+
73
+ def parse(value)
74
+ return parse_joysticks(value).
75
+ merge(parse_buttons(value)).
76
+ merge(parse_triggers(value)).
77
+ merge(parse_dpad(value)).
78
+ merge(parse_zbuttons(value))
106
79
  end
107
80
 
108
- private
81
+ def parse_joysticks(value)
82
+ {
83
+ :lx => decode_value(value, 0) & 0x3f,
84
+ :ly => decode_value(value, 1) & 0x3f,
85
+ :rx => ((decode_value(value, 0) & 0xC0) >> 2) | ((decode_value(value, 1) & 0xC0) >> 4) | (decode_value(value, 2)[7]),
86
+ :ry => decode_value(value, 2) & 0x1f
87
+ }
88
+ end
109
89
 
110
- def encrypted?(value)
111
- value[:data][0] == value[:data][1] && value[:data][2] == value[:data][3] && value[:data][4] == value[:data][5]
90
+ def parse_buttons(value)
91
+ {
92
+ :a => get_bool_decoded_value(value, 5, 4),
93
+ :b => get_bool_decoded_value(value, 5, 6),
94
+ :x => get_bool_decoded_value(value, 5, 3),
95
+ :y => get_bool_decoded_value(value, 5, 5),
96
+ :+ => get_bool_decoded_value(value, 4, 2),
97
+ :- => get_bool_decoded_value(value, 4, 4),
98
+ :h => get_bool_decoded_value(value, 4, 3)
99
+ }
112
100
  end
113
101
 
114
- def decode( x )
115
- return ( x ^ 0x17 ) + 0x17
102
+ def parse_triggers(value)
103
+ {
104
+ :lt => ((decode_value(value, 2) & 0x60) >> 3) | ((decode_value(value, 3) & 0xC0) >> 6),
105
+ :rt => decode_value(value, 3) & 0x1f
106
+ }
116
107
  end
117
108
 
118
- def get_value(value, index)
119
- decode(value[:data][index])
109
+ def parse_dpad(value)
110
+ {
111
+ :d_up => get_bool_decoded_value(value, 5, 0),
112
+ :d_down => get_bool_decoded_value(value, 4, 6),
113
+ :d_left => get_bool_decoded_value(value, 5, 1),
114
+ :d_right => get_bool_decoded_value(value, 4, 7)
115
+ }
120
116
  end
121
117
 
122
- def parse_wiiclassic(value)
123
- return {
124
- :lx => get_value(value, 0) & 0x3f,
125
- :ly => get_value(value, 1) & 0x3f,
126
- :rx => ((get_value(value, 0) & 0xC0) >> 2) | ((get_value(value, 1) & 0xC0) >> 4) | (get_value(value, 2)[7]),
127
- :ry => get_value(value, 2) & 0x1f,
128
- :lt => ((get_value(value, 2) & 0x60) >> 3) | ((get_value(value, 3) & 0xC0) >> 6),
129
- :rt => get_value(value, 3) & 0x1f,
130
- :d_up => generate_bool(get_value(value, 5)[0]),
131
- :d_down => generate_bool(get_value(value, 4)[6]),
132
- :d_left => generate_bool(get_value(value, 5)[1]),
133
- :d_right => generate_bool(get_value(value, 4)[7]),
134
- :zr => generate_bool(get_value(value, 5)[2]),
135
- :zl => generate_bool(get_value(value, 5)[7]),
136
- :a => generate_bool(get_value(value, 5)[4]),
137
- :b => generate_bool(get_value(value, 5)[6]),
138
- :x => generate_bool(get_value(value, 5)[3]),
139
- :y => generate_bool(get_value(value, 5)[5]),
140
- :+ => generate_bool(get_value(value, 4)[2]),
141
- :- => generate_bool(get_value(value, 4)[4]),
142
- :h => generate_bool(get_value(value, 4)[3]),
118
+ def parse_zbuttons(value)
119
+ {
120
+ :zr => get_bool_decoded_value(value, 5, 2),
121
+ :zl => get_bool_decoded_value(value, 5, 7)
143
122
  }
144
123
  end
145
124
 
146
- def generate_bool(value)
147
- value == 0 ? true : false
125
+ def get_bool_decoded_value(value, offset1, offset2)
126
+ generate_bool(decode_value(value, offset1)[offset2])
148
127
  end
149
128
  end
150
129
  end
@@ -0,0 +1,89 @@
1
+ require 'artoo/drivers/driver'
2
+
3
+ module Artoo
4
+ module Drivers
5
+ # Wii-based controller shared driver behaviors for Firmata
6
+ class Wiidriver < Driver
7
+ attr_reader :joystick, :data
8
+
9
+ def address; 0x52; end
10
+
11
+ def initialize(params={})
12
+ @joystick = get_defaults
13
+ @data = {}
14
+ super
15
+ end
16
+
17
+ def start_driver
18
+ begin
19
+ listener = ->(value) { update(value) }
20
+ connection.on("i2c_reply", listener)
21
+
22
+ connection.i2c_config(0)
23
+ every(interval) do
24
+ connection.i2c_write_request(address, 0x40, 0x00)
25
+ p
26
+ connection.i2c_write_request(address, 0x00, 0x00)
27
+ p
28
+ connection.i2c_read_request(address, 6)
29
+ p
30
+ connection.read_and_process
31
+ end
32
+
33
+ super
34
+ rescue Exception => e
35
+ p "start driver"
36
+ p e.message
37
+ p e.backtrace.inspect
38
+ end
39
+ end
40
+
41
+ def update(value)
42
+ if encrypted?(value)
43
+ Logger.error "Encrypted bytes from wii device!"
44
+ raise "Encrypted bytes from wii device!"
45
+ end
46
+
47
+ @data = parse(value)
48
+ end
49
+
50
+ protected
51
+
52
+ def get_defaults
53
+ {}
54
+ end
55
+
56
+ def parse
57
+ {}
58
+ end
59
+
60
+ def set_joystick_default_value(joystick_axis, default_value)
61
+ joystick[joystick_axis] = default_value if joystick[joystick_axis].nil?
62
+ end
63
+
64
+ def calculate_joystick_value(axis, origin)
65
+ data[axis] - joystick[origin]
66
+ end
67
+
68
+ def encrypted?(value)
69
+ [[0, 1], [2, 3], [4, 5]].all? {|a| get_value(value, a[0]) == get_value(value, a[1]) }
70
+ end
71
+
72
+ def decode(x)
73
+ return ( x ^ 0x17 ) + 0x17
74
+ end
75
+
76
+ def decode_value(value, index)
77
+ decode(get_value(value, index))
78
+ end
79
+
80
+ def get_value(value, index)
81
+ value[:data][index]
82
+ end
83
+
84
+ def generate_bool(value)
85
+ value == 0 ? true : false
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,18 @@
1
+ # monkeypatches for Celluloid Actor class
2
+ module Celluloid
3
+ def timers
4
+ Actor.timers
5
+ end
6
+
7
+ class Actor
8
+ attr_accessor :timers
9
+
10
+ class << self
11
+ def timers
12
+ actor = Thread.current[:celluloid_actor]
13
+ raise NotActorError, "not in actor scope" unless actor
14
+ actor.timers
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ # monkeypatches for Timers & Timer classes used by Celluloid
2
+ class Timers
3
+ def initialize
4
+ @timers = SortedSet.new
5
+ @paused_timers = SortedSet.new
6
+ end
7
+
8
+ def pause(timer = nil)
9
+ return pause_all if timer.nil?
10
+ raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer
11
+ @timers.delete timer
12
+ @paused_timers.add timer
13
+ end
14
+
15
+ def pause_all
16
+ @timers.each {|timer| timer.pause}
17
+ end
18
+
19
+ def continue(timer = nil)
20
+ return continue_all if timer.nil?
21
+ raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer
22
+ @paused_timers.delete timer
23
+ @timers.add timer
24
+ end
25
+
26
+ def continue_all
27
+ @paused_timers.each {|timer| timer.continue}
28
+ end
29
+
30
+ class Timer
31
+ # Pause this timer
32
+ def pause
33
+ @timers.pause self
34
+ end
35
+
36
+ # Continue this timer
37
+ def continue
38
+ @timers.continue self
39
+ end
40
+ end
41
+ end
@@ -9,8 +9,43 @@ module Artoo
9
9
  @robots = bots
10
10
  end
11
11
 
12
- def get_robot_by_name(name)
13
- robots.find_all {|r| r.name == name}.first
12
+ def robot(name)
13
+ robots.find {|r| r.name == name}
14
+ end
15
+
16
+ def robot_devices(name)
17
+ robot(name).devices
18
+ end
19
+
20
+ def robot_device(name, device_id)
21
+ robot_devices(name)[device_id.intern]
22
+ end
23
+
24
+ def robot_connections(name)
25
+ robot(name).connections
26
+ end
27
+
28
+ def robot_connection(robot_id, connection_id)
29
+ robot_connections(robot_id)[connection_id.intern]
30
+ end
31
+
32
+ def start_work
33
+ robots.each {|r| r.async.work} unless Artoo::Robot.is_running?
34
+ end
35
+
36
+ def pause_work
37
+ robots.each {|r|
38
+ Logger.info "pausing #{r.name}"
39
+ r.async.pause_work
40
+ }
41
+ end
42
+
43
+ def continue_work
44
+ robots.each {|r| r.async.continue_work}
45
+ end
46
+
47
+ def stop_work
48
+ #robots.each {|r| r.async.stop_work} unless !Artoo::Robot.is_running?
14
49
  end
15
50
  end
16
51
  end
@@ -1,5 +1,8 @@
1
+ require 'celluloid/autostart'
1
2
  require 'celluloid/io'
2
3
  require 'multi_json'
4
+ require 'artoo/ext/timers'
5
+ require 'artoo/ext/actor'
3
6
 
4
7
  require 'artoo/basic'
5
8
  require 'artoo/connection'
@@ -10,6 +13,7 @@ require 'artoo/master'
10
13
  require 'artoo/port'
11
14
  require 'artoo/utility'
12
15
 
16
+
13
17
  module Artoo
14
18
  # The most important class used by Artoo is Robot. This represents the primary
15
19
  # interface for interacting with a collection of physical computing capabilities.
@@ -29,7 +33,7 @@ module Artoo
29
33
  end
30
34
 
31
35
  class << self
32
- attr_accessor :device_types, :working_code,
36
+ attr_accessor :device_types, :working_code, :running,
33
37
  :use_api, :api_host, :api_port
34
38
 
35
39
  def connection_types
@@ -80,6 +84,18 @@ module Artoo
80
84
  # an array of existing instances
81
85
  # or, a new instance can be created
82
86
  def work!(robot=nil)
87
+ return if !test? && is_running?
88
+ prepare_robots(robot)
89
+
90
+ unless cli?
91
+ Celluloid::Actor[:api] = Api.new(self.api_host, self.api_port) if self.use_api
92
+ Celluloid::Actor[:master].start_work
93
+ self.running = true
94
+ sleep # sleep main thread, and let the work commence!
95
+ end
96
+ end
97
+
98
+ def prepare_robots(robot=nil)
83
99
  if robot.respond_to?(:work)
84
100
  robots = [robot]
85
101
  elsif robot.kind_of?(Array)
@@ -88,17 +104,21 @@ module Artoo
88
104
  robots = [self.new]
89
105
  end
90
106
 
91
- robots.each {|r| r.async.work}
92
-
93
107
  Celluloid::Actor[:master] = Master.new(robots)
94
- Celluloid::Actor[:api] = Api.new(self.api_host, self.api_port) if self.use_api
95
-
96
- sleep # sleep main thread, and let the work commence!
97
108
  end
98
109
 
99
110
  def test?
100
111
  ENV["ARTOO_TEST"] == 'true'
101
112
  end
113
+
114
+ def cli?
115
+ ENV["ARTOO_CLI"] == 'true'
116
+ end
117
+
118
+ def is_running?
119
+ self.running ||= false
120
+ self.running == true
121
+ end
102
122
  end
103
123
 
104
124
  def safe_name
@@ -121,12 +141,22 @@ module Artoo
121
141
  execute_working_code
122
142
  end
123
143
 
144
+ def pause_work
145
+ Logger.info "Pausing work..."
146
+ current_instance.timers.pause
147
+ end
148
+
149
+ def continue_work
150
+ Logger.info "Continuing work..."
151
+ current_instance.timers.continue
152
+ end
153
+
124
154
  def disconnect
125
155
  connections.each {|k, c| c.async.disconnect}
126
156
  end
127
157
 
128
158
  def default_connection
129
- connections[connections.keys.first]
159
+ connections.values.first
130
160
  end
131
161
 
132
162
  def connection_types
@@ -153,6 +183,10 @@ module Artoo
153
183
  MultiJson.dump(to_hash)
154
184
  end
155
185
 
186
+ def inspect
187
+ "#<Robot #{object_id}>"
188
+ end
189
+
156
190
  private
157
191
 
158
192
  def initialize_connections(params={})
@@ -1,5 +1,5 @@
1
1
  module Artoo
2
2
  unless const_defined?('VERSION')
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -7,10 +7,5 @@ describe Artoo::Drivers::Wiiclassic do
7
7
  @driver = Artoo::Drivers::Wiiclassic.new(:parent => @device)
8
8
  end
9
9
 
10
- it 'must Artoo::Drivers::Wiiclassic#set_joystick_default_value' do
11
- val = "101"
12
- @driver.joystick[:test_axis] = nil
13
- @driver.set_joystick_default_value(:test_axis, val)
14
- @driver.joystick[:test_axis].must_equal val
15
- end
10
+ it 'must do things'
16
11
  end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+ require 'artoo/drivers/wiidriver'
3
+
4
+ class Artoo::Drivers::Wiidriver
5
+ public :set_joystick_default_value, :calculate_joystick_value, :encrypted?,
6
+ :decode, :get_value, :generate_bool
7
+ end
8
+
9
+ describe Artoo::Drivers::Wiidriver do
10
+ before do
11
+ @device = mock('device')
12
+ @driver = Artoo::Drivers::Wiidriver.new(:parent => @device)
13
+ end
14
+
15
+ it 'Artoo::Drivers::Wiidriver#set_joystick_default_value' do
16
+ val = "101"
17
+ @driver.joystick[:test_axis] = nil
18
+ @driver.set_joystick_default_value(:test_axis, val)
19
+ @driver.joystick[:test_axis].must_equal val
20
+ end
21
+
22
+ it 'Artoo::Drivers::Wiidriver#calculate_joystick_value' do
23
+ @driver.data[:test_axis] = 8
24
+ @driver.joystick[:test_origin] = 5
25
+ @driver.calculate_joystick_value(:test_axis, :test_origin).must_equal 3
26
+ end
27
+
28
+ it 'Artoo::Drivers::Wiidriver#encrypted?' do
29
+ value = {:data => [0, 0, 0, 0, 0, 0]}
30
+ @driver.encrypted?(value).must_equal true
31
+ end
32
+
33
+ it 'Artoo::Drivers::Wiidriver#encrypted?' do
34
+ value = {:data => [1, 2, 3, 4, 5, 6]}
35
+ @driver.encrypted?(value).must_equal false
36
+ end
37
+
38
+ it 'Artoo::Drivers::Wiidriver#decode' do
39
+ @driver.decode(22).must_equal 24
40
+ @driver.decode(0).must_equal 46
41
+ @driver.decode(16).must_equal 30
42
+ end
43
+
44
+ it 'Artoo::Drivers::Wiidriver#get_value' do
45
+ value = {:data => [1, 2, 3, 4, 5, 6], :other_data => [10, 20, 30, 40, 50, 60]}
46
+ @driver.get_value(value, 1).must_equal 2
47
+ @driver.get_value(value, 5).must_equal 6
48
+ end
49
+
50
+ it 'Artoo::Drivers::Wiidriver#generate_bool' do
51
+ @driver.generate_bool(0).must_equal true
52
+ @driver.generate_bool(1).must_equal false
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class MockRobot
4
+ attr_reader :name
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def devices
11
+ ["#{name}-device1", "#{name}-device2", "#{name}-device3"]
12
+ end
13
+
14
+ def connections
15
+ ["#{name}-connection1", "#{name}-connection2", "#{name}-connection3"]
16
+ end
17
+ end
18
+
19
+ describe Artoo::Master do
20
+ before do
21
+ @robots = []
22
+
23
+ @robot1 = MockRobot.new("robot1")
24
+ @robot2 = MockRobot.new("robot2")
25
+ @robot3 = MockRobot.new("robot3")
26
+
27
+ @robots << @robot1
28
+ @robots << @robot2
29
+ @robots << @robot3
30
+
31
+ @master = Artoo::Master.new(@robots)
32
+ end
33
+
34
+ it 'Artoo::Master#robot' do
35
+ @master.robot("robot2").must_equal @robot2
36
+ end
37
+
38
+ it 'Artoo::Master#robot_devices' do
39
+ @master.robot_devices("robot2").first.must_equal "robot2-device1"
40
+ end
41
+
42
+ it 'Artoo::Master#robot_connections' do
43
+ @master.robot_connections("robot2").last.must_equal "robot2-connection3"
44
+ end
45
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: artoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ron Evans
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-03-08 00:00:00.000000000 Z
15
+ date: 2013-04-09 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: celluloid-io
@@ -20,14 +20,14 @@ dependencies:
20
20
  requirements:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: '0.12'
23
+ version: '0.13'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
28
  - - ~>
29
29
  - !ruby/object:Gem::Version
30
- version: '0.12'
30
+ version: '0.13'
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: reel
33
33
  requirement: !ruby/object:Gem::Requirement
@@ -84,6 +84,20 @@ dependencies:
84
84
  - - ~>
85
85
  - !ruby/object:Gem::Version
86
86
  version: '10.0'
87
+ - !ruby/object:Gem::Dependency
88
+ name: pry
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '0.9'
94
+ type: :runtime
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: '0.9'
87
101
  - !ruby/object:Gem::Dependency
88
102
  name: minitest
89
103
  requirement: !ruby/object:Gem::Requirement
@@ -117,6 +131,7 @@ email:
117
131
  - artoo@hybridgroup.com
118
132
  executables:
119
133
  - retry.sh
134
+ - robi
120
135
  - sphero.sh
121
136
  extensions: []
122
137
  extra_rdoc_files: []
@@ -160,6 +175,7 @@ files:
160
175
  - api/public/partials/robot-index.html
161
176
  - artoo.gemspec
162
177
  - bin/retry.sh
178
+ - bin/robi
163
179
  - bin/sphero.sh
164
180
  - examples/ardrone.rb
165
181
  - examples/ardrone_nav.rb
@@ -186,6 +202,7 @@ files:
186
202
  - examples/sphero_multiple.rb
187
203
  - examples/sphero_wiichuck.rb
188
204
  - examples/wiichuck.rb
205
+ - examples/wiiclassic.rb
189
206
  - lib/artoo.rb
190
207
  - lib/artoo/adaptors/adaptor.rb
191
208
  - lib/artoo/adaptors/ardrone.rb
@@ -217,7 +234,10 @@ files:
217
234
  - lib/artoo/drivers/sphero.rb
218
235
  - lib/artoo/drivers/wiichuck.rb
219
236
  - lib/artoo/drivers/wiiclassic.rb
237
+ - lib/artoo/drivers/wiidriver.rb
220
238
  - lib/artoo/events.rb
239
+ - lib/artoo/ext/actor.rb
240
+ - lib/artoo/ext/timers.rb
221
241
  - lib/artoo/main.rb
222
242
  - lib/artoo/master.rb
223
243
  - lib/artoo/port.rb
@@ -244,6 +264,8 @@ files:
244
264
  - test/drivers/sphero_test.rb
245
265
  - test/drivers/wiichuck_test.rb
246
266
  - test/drivers/wiiclassic_test.rb
267
+ - test/drivers/wiidriver_test.rb
268
+ - test/master_test.rb
247
269
  - test/port_test.rb
248
270
  - test/robot_test.rb
249
271
  - test/test_helper.rb
@@ -267,32 +289,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
289
  version: '0'
268
290
  requirements: []
269
291
  rubyforge_project: artoo
270
- rubygems_version: 2.0.0
292
+ rubygems_version: 2.0.3
271
293
  signing_key:
272
294
  specification_version: 4
273
295
  summary: Ruby-based microframework for robotics
274
- test_files:
275
- - test/adaptors/adaptor_test.rb
276
- - test/adaptors/ardrone_test.rb
277
- - test/adaptors/firmata_test.rb
278
- - test/adaptors/loopback_test.rb
279
- - test/adaptors/sphero_test.rb
280
- - test/api_test.rb
281
- - test/artoo_test.rb
282
- - test/connection_test.rb
283
- - test/delegator_test.rb
284
- - test/device_test.rb
285
- - test/drivers/ardrone_navigation_test.rb
286
- - test/drivers/ardrone_test.rb
287
- - test/drivers/ardrone_video_test.rb
288
- - test/drivers/driver_test.rb
289
- - test/drivers/led_test.rb
290
- - test/drivers/motor_test.rb
291
- - test/drivers/servo_test.rb
292
- - test/drivers/sphero_test.rb
293
- - test/drivers/wiichuck_test.rb
294
- - test/drivers/wiiclassic_test.rb
295
- - test/port_test.rb
296
- - test/robot_test.rb
297
- - test/test_helper.rb
298
- - test/utility_test.rb
296
+ test_files: []