ruby-nxt 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +340 -0
- data/README +49 -0
- data/examples/commands.rb +180 -0
- data/examples/drb_client.rb +11 -0
- data/examples/drb_server.rb +40 -0
- data/examples/mary.rb +78 -0
- data/examples/move_by_degrees.rb +42 -0
- data/examples/nxt_comm_demo.rb +65 -0
- data/examples/nxt_remote_control.rb +70 -0
- data/examples/socket_server.rb +37 -0
- data/lib/autodetect_nxt.rb +36 -0
- data/lib/brick.rb +53 -0
- data/lib/commands.rb +40 -0
- data/lib/commands/light_sensor.rb +82 -0
- data/lib/commands/mixins/motor.rb +84 -0
- data/lib/commands/mixins/sensor.rb +38 -0
- data/lib/commands/motor.rb +136 -0
- data/lib/commands/move.rb +210 -0
- data/lib/commands/rotation_sensor.rb +72 -0
- data/lib/commands/sound.rb +102 -0
- data/lib/commands/sound_sensor.rb +67 -0
- data/lib/commands/touch_sensor.rb +84 -0
- data/lib/commands/ultrasonic_sensor.rb +84 -0
- data/lib/motor.rb +235 -0
- data/lib/nxt.rb +189 -0
- data/lib/nxt_comm.rb +596 -0
- data/lib/sensors/light_sensor.rb +45 -0
- data/lib/sensors/sensor.rb +93 -0
- data/lib/sensors/sound_sensor.rb +48 -0
- data/lib/sensors/touch_sensor.rb +35 -0
- data/lib/sensors/ultrasonic_sensor.rb +95 -0
- data/lib/ultrasonic_comm.rb +102 -0
- data/test/bt_test.rb +10 -0
- data/test/interactive/interactive_test_helper.rb +89 -0
- data/test/interactive/test_sensors.rb +236 -0
- data/test/test.rb +22 -0
- data/test/test_helper.rb +1 -0
- data/test/unit/motor_test.rb +177 -0
- data/test/unit/nxt_comm_test.rb +155 -0
- data/test/unit/nxt_test.rb +60 -0
- metadata +95 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# example code showing how to make a drb server to communicate with nxt
|
3
|
+
# it also spawns a thread that periodically calls keep_alive to prevent
|
4
|
+
# the nxt from going to sleep
|
5
|
+
|
6
|
+
require "drb"
|
7
|
+
require "thread"
|
8
|
+
require "nxt_comm"
|
9
|
+
|
10
|
+
$DEBUG = false
|
11
|
+
$DEV = '/dev/tty.NXT-DevB-1'
|
12
|
+
$SAFE = 1 # disable eval() and friends
|
13
|
+
|
14
|
+
class NXTServer
|
15
|
+
def initialize(dev=$DEV,port=9000)
|
16
|
+
@nxt = NXTComm.new(dev)
|
17
|
+
@last_keep_alive = Time.now
|
18
|
+
@keep_alive_time = 300
|
19
|
+
|
20
|
+
Thread.new do
|
21
|
+
DRb.start_service "druby://localhost:#{port}", @nxt
|
22
|
+
puts "NXTServer ready at: #{DRb.uri}"
|
23
|
+
end
|
24
|
+
|
25
|
+
Thread.new do
|
26
|
+
loop do
|
27
|
+
if Time.now - @last_keep_alive > @keep_alive_time / 2
|
28
|
+
@keep_alive_time = @nxt.keep_alive / 1000
|
29
|
+
puts "Sending next keep alive in #{@keep_alive_time / 2} seconds..."
|
30
|
+
@last_keep_alive = Time.now
|
31
|
+
end
|
32
|
+
# need small sleep time or DRb thread won't have time to fire
|
33
|
+
# I can't get it to work if I just sleep the keep_alive interval
|
34
|
+
sleep 1
|
35
|
+
end
|
36
|
+
end.join # make thread block
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
server = NXTServer.new
|
data/examples/mary.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
# Plays "Mary Had A Little Lamb"
|
4
|
+
# Author: Christopher Continanza <christopher.continanza@villanova.edu>
|
5
|
+
|
6
|
+
require "nxt_comm"
|
7
|
+
|
8
|
+
$DEBUG = true
|
9
|
+
|
10
|
+
def sleeper
|
11
|
+
sleep(0.5)
|
12
|
+
end
|
13
|
+
|
14
|
+
@nxt = NXTComm.new("/dev/tty.NXT-DevB-1")
|
15
|
+
|
16
|
+
#E D C D E E E
|
17
|
+
@nxt.play_tone(659,500)
|
18
|
+
sleeper
|
19
|
+
@nxt.play_tone(587,500)
|
20
|
+
sleeper
|
21
|
+
@nxt.play_tone(523,500)
|
22
|
+
sleeper
|
23
|
+
@nxt.play_tone(587,500)
|
24
|
+
sleeper
|
25
|
+
@nxt.play_tone(659,500)
|
26
|
+
sleeper
|
27
|
+
@nxt.play_tone(659,500)
|
28
|
+
sleeper
|
29
|
+
@nxt.play_tone(659,500)
|
30
|
+
sleeper
|
31
|
+
sleeper
|
32
|
+
#D D D E G G
|
33
|
+
@nxt.play_tone(587,500)
|
34
|
+
sleeper
|
35
|
+
@nxt.play_tone(587,500)
|
36
|
+
sleeper
|
37
|
+
@nxt.play_tone(587,500)
|
38
|
+
sleeper
|
39
|
+
sleeper
|
40
|
+
@nxt.play_tone(659,500)
|
41
|
+
sleeper
|
42
|
+
@nxt.play_tone(784,500)
|
43
|
+
sleeper
|
44
|
+
@nxt.play_tone(784,500)
|
45
|
+
sleeper
|
46
|
+
sleeper
|
47
|
+
#E D C D E E E
|
48
|
+
@nxt.play_tone(659,500)
|
49
|
+
sleeper
|
50
|
+
@nxt.play_tone(587,500)
|
51
|
+
sleeper
|
52
|
+
@nxt.play_tone(523,500)
|
53
|
+
sleeper
|
54
|
+
@nxt.play_tone(587,500)
|
55
|
+
sleeper
|
56
|
+
@nxt.play_tone(659,500)
|
57
|
+
sleeper
|
58
|
+
@nxt.play_tone(659,500)
|
59
|
+
sleeper
|
60
|
+
@nxt.play_tone(659,500)
|
61
|
+
sleeper
|
62
|
+
#E D D E D C
|
63
|
+
@nxt.play_tone(659,500)
|
64
|
+
sleeper
|
65
|
+
@nxt.play_tone(587,500)
|
66
|
+
sleeper
|
67
|
+
@nxt.play_tone(587,500)
|
68
|
+
sleeper
|
69
|
+
@nxt.play_tone(659,500)
|
70
|
+
sleeper
|
71
|
+
@nxt.play_tone(587,500)
|
72
|
+
sleeper
|
73
|
+
@nxt.play_tone(523,750)
|
74
|
+
sleeper
|
75
|
+
sleeper
|
76
|
+
sleeper
|
77
|
+
|
78
|
+
@nxt.close
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "nxt_comm"
|
4
|
+
|
5
|
+
$DEBUG = false
|
6
|
+
|
7
|
+
@nxt = NXTComm.new('/dev/tty.NXT-DevB-1')
|
8
|
+
@degrees = 360
|
9
|
+
@motor = NXTComm::MOTOR_B
|
10
|
+
|
11
|
+
# set the tacho_count = 0
|
12
|
+
@nxt.reset_motor_position(@motor)
|
13
|
+
|
14
|
+
puts @nxt.get_output_state(@motor).inspect
|
15
|
+
|
16
|
+
# ramps up to (close to) requested degrees and then if no other command is sent, it will
|
17
|
+
# try to return to the requested degrees if force moves motor
|
18
|
+
|
19
|
+
# have to start at least power = 1 to ramp up
|
20
|
+
@nxt.set_output_state(@motor,1,NXTComm::MOTORON | NXTComm::BRAKE | NXTComm::REGULATED,
|
21
|
+
NXTComm::REGULATION_MODE_MOTOR_SPEED,0,NXTComm::MOTOR_RUN_STATE_RUNNING,0)
|
22
|
+
|
23
|
+
# ramp up to the requested degrees
|
24
|
+
@nxt.set_output_state(@motor,75,NXTComm::MOTORON | NXTComm::BRAKE | NXTComm::REGULATED,
|
25
|
+
NXTComm::REGULATION_MODE_MOTOR_SPEED,0,NXTComm::MOTOR_RUN_STATE_RAMPUP,@degrees)
|
26
|
+
|
27
|
+
until @nxt.get_output_state(@motor)[:run_state] == NXTComm::MOTOR_RUN_STATE_IDLE
|
28
|
+
puts @nxt.get_output_state(@motor).inspect
|
29
|
+
sleep(0.5)
|
30
|
+
end
|
31
|
+
|
32
|
+
# abruptly break and use power to prevent movement
|
33
|
+
# @nxt.set_output_state(@motor,0,NXTComm::MOTORON | NXTComm::BRAKE | NXTComm::REGULATED,
|
34
|
+
# NXTComm::REGULATION_MODE_MOTOR_SPEED,0,NXTComm::MOTOR_RUN_STATE_RUNNING,0)
|
35
|
+
|
36
|
+
# coast to a stop and allow force to move motor
|
37
|
+
# @nxt.set_output_state(@motor,0,NXTComm::COAST,
|
38
|
+
# NXTComm::REGULATION_MODE_IDLE,0,NXTComm::MOTOR_RUN_STATE_IDLE,0)
|
39
|
+
|
40
|
+
puts @nxt.get_output_state(@motor).inspect
|
41
|
+
|
42
|
+
@nxt.close
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
require "nxt_comm"
|
4
|
+
|
5
|
+
$DEBUG = false
|
6
|
+
|
7
|
+
@nxt = NXTComm.new("/dev/tty.NXT-DevB-1")
|
8
|
+
# puts "Connected!"
|
9
|
+
#
|
10
|
+
# puts "Starting program"
|
11
|
+
# @nxt.start_program("Try-Touch.rtm")
|
12
|
+
# sleep(3)
|
13
|
+
# puts "Currently running: " + @nxt.get_current_program_name
|
14
|
+
# puts "Stopping program"
|
15
|
+
# @nxt.stop_program
|
16
|
+
#
|
17
|
+
# @nxt.play_sound_file("Good Job.rso")
|
18
|
+
|
19
|
+
@nxt.play_tone(500,500)
|
20
|
+
|
21
|
+
# @nxt.set_output_state(
|
22
|
+
# NXTComm::MOTOR_A,
|
23
|
+
# 100,
|
24
|
+
# NXTComm::REGULATED,
|
25
|
+
# NXTComm::REGULATION_MODE_MOTOR_SPEED,
|
26
|
+
# 100,
|
27
|
+
# NXTComm::MOTOR_RUN_STATE_RUNNING,
|
28
|
+
# 0
|
29
|
+
# )
|
30
|
+
# sleep(3)
|
31
|
+
# @nxt.set_output_state(
|
32
|
+
# NXTComm::MOTOR_A,
|
33
|
+
# 100,
|
34
|
+
# NXTComm::BRAKE,
|
35
|
+
# NXTComm::REGULATION_MODE_MOTOR_SPEED,
|
36
|
+
# 0,
|
37
|
+
# NXTComm::MOTOR_RUN_STATE_RAMPDOWN,
|
38
|
+
# 0
|
39
|
+
# )
|
40
|
+
|
41
|
+
# set_input_mode(port,type,mode)
|
42
|
+
# @nxt.set_input_mode(NXTComm::SENSOR_3,NXTComm::LIGHT_INACTIVE,NXTComm::RAWMODE)
|
43
|
+
# while true
|
44
|
+
# s = @nxt.get_input_values(NXTComm::SENSOR_3)
|
45
|
+
# puts s.inspect
|
46
|
+
# sleep(1)
|
47
|
+
# end
|
48
|
+
|
49
|
+
# puts @nxt.get_output_state(NXTComm::MOTOR_B).inspect
|
50
|
+
|
51
|
+
# puts @nxt.get_input_values(NXTComm::SENSOR_1).inspect
|
52
|
+
|
53
|
+
# @nxt.reset_input_scaled_value(NXTComm::SENSOR_1)
|
54
|
+
|
55
|
+
# @nxt.message_write(1,"Chunky Robotic Bacon!")
|
56
|
+
|
57
|
+
# @nxt.reset_motor_position(NXTComm::MOTOR_B)
|
58
|
+
|
59
|
+
# puts "Battery Level: #{@nxt.get_battery_level/1000.0} V"
|
60
|
+
|
61
|
+
#@nxt.play_sound_file("Woops.rso",true)
|
62
|
+
#sleep(2)
|
63
|
+
#@nxt.stop_sound_playback
|
64
|
+
|
65
|
+
@nxt.close
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "tk"
|
4
|
+
require "nxt"
|
5
|
+
|
6
|
+
class NXTRemoteControl
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@nxt = NXTComm.new($DEV)
|
10
|
+
ph = { 'padx' => 10, 'pady' => 10 }
|
11
|
+
root = TkRoot.new { title "NXT Remote Control" }
|
12
|
+
top = TkFrame.new(root)
|
13
|
+
|
14
|
+
_forward = proc{forward}
|
15
|
+
_backward = proc{backward}
|
16
|
+
_left = proc{left}
|
17
|
+
_right = proc{right}
|
18
|
+
_stop = proc{stop}
|
19
|
+
|
20
|
+
@text = TkVariable.new
|
21
|
+
@entry = TkEntry.new(top, 'textvariable' => @text)
|
22
|
+
@entry.pack(ph)
|
23
|
+
|
24
|
+
TkButton.new(top) {text 'Forward'; command _forward; pack ph}
|
25
|
+
TkButton.new(top) {text 'Backward'; command _backward; pack ph}
|
26
|
+
TkButton.new(top) {text 'Left'; command _left; pack ph}
|
27
|
+
TkButton.new(top) {text 'Right'; command _right; pack ph}
|
28
|
+
TkButton.new(top) {text 'Stop'; command _stop; pack ph}
|
29
|
+
|
30
|
+
TkButton.new(top) {text 'Exit'; command {proc exit}; pack ph}
|
31
|
+
|
32
|
+
top.pack('fill'=>'both', 'side' =>'top')
|
33
|
+
end
|
34
|
+
|
35
|
+
def forward
|
36
|
+
@text.value = "Moving forward..."
|
37
|
+
@m = Commands::Move.new(@nxt)
|
38
|
+
@m.start
|
39
|
+
end
|
40
|
+
|
41
|
+
def backward
|
42
|
+
@text.value = "Moving backward..."
|
43
|
+
@m = Commands::Move.new(@nxt)
|
44
|
+
@m.direction = :backward
|
45
|
+
@m.start
|
46
|
+
end
|
47
|
+
|
48
|
+
def left
|
49
|
+
@text.value = "Turning left"
|
50
|
+
@m = Commands::Move.new(@nxt)
|
51
|
+
@m.steering = :left
|
52
|
+
@m.start
|
53
|
+
end
|
54
|
+
|
55
|
+
def right
|
56
|
+
@text.value = "Turning right"
|
57
|
+
@m = Commands::Move.new(@nxt)
|
58
|
+
@m.steering = :right
|
59
|
+
@m.start
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop
|
63
|
+
@text.value = "Stopping"
|
64
|
+
@m = Commands::Move.new(@nxt)
|
65
|
+
@m.stop
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
NXTRemoteControl.new
|
70
|
+
Tk.mainloop
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# example code showing how to make a socket server to communicate with nxt
|
3
|
+
# To use it via telnet:
|
4
|
+
# telnet localhost 3001
|
5
|
+
# type forward then enter
|
6
|
+
|
7
|
+
require "socket"
|
8
|
+
require "nxt_comm"
|
9
|
+
|
10
|
+
$DEBUG = true
|
11
|
+
|
12
|
+
@port = (ARGV[0] || 3001).to_i
|
13
|
+
@server = TCPServer.new('localhost', @port)
|
14
|
+
@nxt = NXTComm.new('/dev/tty.NXT-DevB-1')
|
15
|
+
|
16
|
+
@move = Commands::Move.new(@nxt)
|
17
|
+
|
18
|
+
puts "Ready."
|
19
|
+
|
20
|
+
while (@session = @server.accept)
|
21
|
+
|
22
|
+
@request = @session.gets
|
23
|
+
|
24
|
+
puts "Request: #{@request}"
|
25
|
+
|
26
|
+
case @request.chomp
|
27
|
+
when "forward"
|
28
|
+
@move.start
|
29
|
+
when "stop"
|
30
|
+
@move.stop
|
31
|
+
else
|
32
|
+
@session.puts "Unknown request: #{@request}"
|
33
|
+
end
|
34
|
+
|
35
|
+
@session.close
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Matt Zukowski <matt@roughest.net>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program; if not, write to the Free Software Foundation,
|
15
|
+
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
16
|
+
|
17
|
+
|
18
|
+
# This script attempts to automatically set the global $DEV variable
|
19
|
+
# by searching for a NXT tty device node.
|
20
|
+
|
21
|
+
# TODO: This curently only works on *nix based systems (MacOS X too!)
|
22
|
+
# I don't know how to implement this for Win32 :(
|
23
|
+
|
24
|
+
# If there is an NXT or DEV environment variable, or a $DEV global,
|
25
|
+
# we'll use that and not try to auto-set it ourselves.
|
26
|
+
$DEV = $DEV || ENV['NXT'] || ENV['DEV']
|
27
|
+
|
28
|
+
unless $DEV or ENV['NXT'] or ENV['DEV']
|
29
|
+
begin
|
30
|
+
devices = Dir["/dev/*NXT*"]
|
31
|
+
$DEV = devices[0] if devices.size > 0
|
32
|
+
puts "Auto-detected NXT at #{$DEV}"
|
33
|
+
rescue
|
34
|
+
# the /dev directory probably doesn't exist... maybe we're on Win32?
|
35
|
+
end
|
36
|
+
end
|
data/lib/brick.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Matt Zukowski <matt@roughest.net>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program; if not, write to the Free Software Foundation,
|
15
|
+
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
16
|
+
|
17
|
+
require "yaml"
|
18
|
+
require "logger"
|
19
|
+
require "nxt_comm"
|
20
|
+
|
21
|
+
#Logger::Formatter::Format = "%s, [%s#%d] %5s -- %s:\n%s\n"
|
22
|
+
|
23
|
+
# Abstract parent class of motor and sensor bricks.
|
24
|
+
# Currently provides only very basic common functionality but may
|
25
|
+
# be expanded in the future.
|
26
|
+
class Brick
|
27
|
+
|
28
|
+
attr_reader :port
|
29
|
+
attr_reader :log
|
30
|
+
|
31
|
+
def initialize(nxt, port)
|
32
|
+
logfile = File.expand_path(File.dirname(File.expand_path(__FILE__)))+"/../log/#{self.class}_#{port}.log"
|
33
|
+
@log = Logger.new logfile
|
34
|
+
@log.level = Logger::DEBUG
|
35
|
+
#puts "Logging to #{logfile}"
|
36
|
+
|
37
|
+
debug("#{self.class}::#{nxt}(#{port})", :initialize)
|
38
|
+
|
39
|
+
@nxt = nxt
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def debug(msg, method = false)
|
44
|
+
@log.info(method) do
|
45
|
+
if msg.kind_of? String
|
46
|
+
msg
|
47
|
+
else
|
48
|
+
msg.to_yaml
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/commands.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Command object based interface that implements the "blocks" in NXT-G. This should
|
2
|
+
# be easy to understand if you are familiar with the NXT-G graphical programming
|
3
|
+
# system. This is automatically included in NXTComm.
|
4
|
+
#
|
5
|
+
# === Example
|
6
|
+
#
|
7
|
+
# require 'nxt_comm'
|
8
|
+
#
|
9
|
+
# @nxt = NXTComm.new('/dev/tty.NXT-DevB-1')
|
10
|
+
#
|
11
|
+
# # more examples can be found in examples/commands.rb
|
12
|
+
#
|
13
|
+
# us = Commands::UltrasonicSensor.new(@nxt)
|
14
|
+
# us.mode = :centimeters
|
15
|
+
# puts "Distance: #{us.distance}cm"
|
16
|
+
# us.mode = :inches
|
17
|
+
# puts "Distance: #{us.distance}in"
|
18
|
+
#
|
19
|
+
# us.comparison = "<"
|
20
|
+
# us.trigger_point = 5
|
21
|
+
#
|
22
|
+
# while us.logic == false
|
23
|
+
# sleep(0.5)
|
24
|
+
# puts "Move #{us.comparison} #{us.trigger_point} #{us.mode} from the sensor..."
|
25
|
+
# puts "Distance: #{us.distance}in"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# puts "Got it!"
|
29
|
+
#
|
30
|
+
module Commands
|
31
|
+
require 'commands/move'
|
32
|
+
require 'commands/sound'
|
33
|
+
require 'commands/motor'
|
34
|
+
|
35
|
+
require 'commands/touch_sensor'
|
36
|
+
require 'commands/sound_sensor'
|
37
|
+
require 'commands/light_sensor'
|
38
|
+
require 'commands/ultrasonic_sensor'
|
39
|
+
require 'commands/rotation_sensor'
|
40
|
+
end
|