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,72 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Tony Buser <tbuser@gmail.com> - http://juju.org
|
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
|
+
# Implements the "Rotation Sensor" block in NXT-G (using an nxt motor as rotation sensor)
|
18
|
+
class Commands::RotationSensor
|
19
|
+
|
20
|
+
attr_accessor :port, :trigger_direction, :trigger_point, :comparison
|
21
|
+
|
22
|
+
def initialize(nxt)
|
23
|
+
@nxt = nxt
|
24
|
+
|
25
|
+
# defaults the same as NXT-G
|
26
|
+
@port = :a
|
27
|
+
@trigger_direction = :forward
|
28
|
+
@trigger_point = 360
|
29
|
+
@comparison = ">"
|
30
|
+
reset
|
31
|
+
@internal_counter = degrees
|
32
|
+
end
|
33
|
+
|
34
|
+
# returns true or false based on comparison type and difference between last time reset point
|
35
|
+
def logic
|
36
|
+
@trigger_direction == :forward ? trigger = @trigger_point : trigger = -@trigger_point
|
37
|
+
case @comparison
|
38
|
+
when ">"
|
39
|
+
degrees >= trigger ? true : false
|
40
|
+
when "<"
|
41
|
+
degrees <= trigger ? true : false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# resets the value_scaled property, use this to reset the degrees counter
|
46
|
+
def reset
|
47
|
+
@nxt.reset_motor_position(NXTComm.const_get("MOTOR_#{@port.to_s.upcase}"))
|
48
|
+
end
|
49
|
+
|
50
|
+
# returns the number of degrees moved since last reset point
|
51
|
+
def degrees
|
52
|
+
@internal_counter = rotation_count
|
53
|
+
@internal_counter
|
54
|
+
end
|
55
|
+
|
56
|
+
# attempts to determine direction based on last time position was requested (may not be accurate)
|
57
|
+
def direction
|
58
|
+
case true
|
59
|
+
when rotation_count > @internal_counter
|
60
|
+
"forwards"
|
61
|
+
when rotation_count < @internal_counter
|
62
|
+
"backwards"
|
63
|
+
when rotation_count == @internal_counter
|
64
|
+
"stopped"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# attempt to return the output_state requested
|
69
|
+
def method_missing(cmd)
|
70
|
+
@nxt.get_output_state(NXTComm.const_get("MOTOR_#{@port.to_s.upcase}"))[cmd]
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Tony Buser <tbuser@gmail.com> - http://juju.org
|
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
|
+
# Implements the "Sound" block in NXT-G
|
18
|
+
class Commands::Sound
|
19
|
+
|
20
|
+
attr_accessor :action
|
21
|
+
attr_accessor :control
|
22
|
+
attr_accessor :volume # TODO not sure how to set this with direct commands?
|
23
|
+
attr_accessor :repeat
|
24
|
+
attr_accessor :file
|
25
|
+
attr_accessor :note
|
26
|
+
attr_accessor :duration
|
27
|
+
attr_accessor :wait
|
28
|
+
|
29
|
+
def initialize(nxt)
|
30
|
+
@nxt = nxt
|
31
|
+
|
32
|
+
# defaults the same as NXT-G
|
33
|
+
@action = :file
|
34
|
+
@control = :play
|
35
|
+
@volume = 75
|
36
|
+
@repeat = false
|
37
|
+
@file = "Good Job.rso"
|
38
|
+
@note = "C"
|
39
|
+
@duration = 0.5
|
40
|
+
@wait = true
|
41
|
+
end
|
42
|
+
|
43
|
+
# execute the Sound command based on the properties specified
|
44
|
+
def start
|
45
|
+
if @action == :file
|
46
|
+
@nxt.play_sound_file(@file,@repeat)
|
47
|
+
else
|
48
|
+
@nxt.play_tone(@note.to_freq,(@duration * 1000).to_i)
|
49
|
+
end
|
50
|
+
|
51
|
+
# TODO figure out a logical way to repeat a tone without blocking execution
|
52
|
+
|
53
|
+
if @wait
|
54
|
+
if @action == :tone
|
55
|
+
sleep(@duration)
|
56
|
+
else
|
57
|
+
# TODO don't know how to sleep until a sound file finishes
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# stop the Sound command
|
63
|
+
def stop
|
64
|
+
@nxt.stop_sound_playback
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class String
|
70
|
+
# converts a note string to equiv frequency in Hz
|
71
|
+
# TODO need to get a better range...
|
72
|
+
def to_freq
|
73
|
+
case self.downcase
|
74
|
+
when "c"
|
75
|
+
523
|
76
|
+
when "c#"
|
77
|
+
554
|
78
|
+
when "d"
|
79
|
+
587
|
80
|
+
when "d#"
|
81
|
+
622
|
82
|
+
when "e"
|
83
|
+
659
|
84
|
+
when "f"
|
85
|
+
698
|
86
|
+
when "f#"
|
87
|
+
740
|
88
|
+
when "g"
|
89
|
+
784
|
90
|
+
when "g#"
|
91
|
+
830
|
92
|
+
when "a"
|
93
|
+
880
|
94
|
+
when "a#"
|
95
|
+
923
|
96
|
+
when "b"
|
97
|
+
988
|
98
|
+
else
|
99
|
+
raise "Unknown Note: #{self}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Tony Buser <tbuser@gmail.com> - http://juju.org
|
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 "nxt_comm"
|
18
|
+
require "commands/mixins/sensor"
|
19
|
+
|
20
|
+
# Implements the "Sound Sensor" block in NXT-G
|
21
|
+
class Commands::SoundSensor
|
22
|
+
|
23
|
+
include Commands::Mixins::Sensor
|
24
|
+
|
25
|
+
attr_reader :port, :mode
|
26
|
+
attr_accessor :trigger_point, :comparison
|
27
|
+
|
28
|
+
def initialize(nxt)
|
29
|
+
@nxt = nxt
|
30
|
+
|
31
|
+
# defaults the same as NXT-G
|
32
|
+
@port = 2
|
33
|
+
@trigger_point = 50
|
34
|
+
@comparison = ">"
|
35
|
+
@mode = "dba"
|
36
|
+
set_mode
|
37
|
+
end
|
38
|
+
|
39
|
+
def mode=(mode)
|
40
|
+
@mode = mode
|
41
|
+
set_mode
|
42
|
+
end
|
43
|
+
|
44
|
+
# scaled value read from sensor
|
45
|
+
def sound_level
|
46
|
+
value_scaled
|
47
|
+
end
|
48
|
+
|
49
|
+
# returns the raw value of the sensor
|
50
|
+
def raw_value
|
51
|
+
value_raw
|
52
|
+
end
|
53
|
+
|
54
|
+
# sets up the sensor port
|
55
|
+
def set_mode
|
56
|
+
@nxt.set_input_mode(
|
57
|
+
NXTComm.const_get("SENSOR_#{@port}"),
|
58
|
+
NXTComm.const_get("SOUND_#{@mode.upcase}"),
|
59
|
+
NXTComm::PCTFULLSCALEMODE
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
# attempt to return the input_value requested
|
64
|
+
def method_missing(cmd)
|
65
|
+
@nxt.get_input_values(NXTComm.const_get("SENSOR_#{@port}"))[cmd]
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Tony Buser <tbuser@gmail.com> - http://juju.org
|
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 File.dirname(File.expand_path(__FILE__))+'/../nxt_comm'
|
18
|
+
# require File.dirname(File.expand_path(__FILE__))+'/mixins/sensor'
|
19
|
+
require "nxt_comm"
|
20
|
+
require "commands/mixins/sensor"
|
21
|
+
|
22
|
+
# Implements the "Touch Sensor" block in NXT-G
|
23
|
+
class Commands::TouchSensor
|
24
|
+
|
25
|
+
include Commands::Mixins::Sensor
|
26
|
+
|
27
|
+
attr_reader :port, :action
|
28
|
+
|
29
|
+
def initialize(nxt)
|
30
|
+
@nxt = nxt
|
31
|
+
|
32
|
+
# defaults the same as NXT-G
|
33
|
+
@port = 1
|
34
|
+
@action = :pressed
|
35
|
+
set_mode
|
36
|
+
end
|
37
|
+
|
38
|
+
def action=(action)
|
39
|
+
@action = action
|
40
|
+
set_mode
|
41
|
+
end
|
42
|
+
alias trigger_point= action=
|
43
|
+
|
44
|
+
def comparison=(op)
|
45
|
+
raise NotImplementedError, "Cannot assign a comparison operator for this sensor type."
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns true or false based on action type
|
49
|
+
def logic
|
50
|
+
case @action
|
51
|
+
when :pressed
|
52
|
+
value_scaled > 0 ? true : false
|
53
|
+
when :released
|
54
|
+
value_scaled > 0 ? false : true
|
55
|
+
when :bumped
|
56
|
+
value_scaled > 0 ? true : false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# returns the raw value of the sensor
|
61
|
+
def raw_value
|
62
|
+
value_raw
|
63
|
+
end
|
64
|
+
|
65
|
+
# resets the value_scaled property, use this to reset the sensor when in :bumped mode
|
66
|
+
def reset
|
67
|
+
@nxt.reset_input_scaled_value(NXTComm.const_get("SENSOR_#{@port}"))
|
68
|
+
end
|
69
|
+
|
70
|
+
# sets up the sensor port
|
71
|
+
def set_mode
|
72
|
+
@action == :bumped ? mode = NXTComm::PERIODCOUNTERMODE : mode = NXTComm::BOOLEANMODE
|
73
|
+
@nxt.set_input_mode(
|
74
|
+
NXTComm.const_get("SENSOR_#{@port}"),
|
75
|
+
NXTComm::SWITCH,
|
76
|
+
mode
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
# attempt to return the input_value requested
|
81
|
+
def method_missing(cmd)
|
82
|
+
@nxt.get_input_values(NXTComm.const_get("SENSOR_#{@port}"))[cmd]
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# ruby-nxt Control Mindstorms NXT via Bluetooth Serial Port Connection
|
2
|
+
# Copyright (C) 2006 Tony Buser <tbuser@gmail.com> - http://juju.org
|
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 "nxt_comm"
|
18
|
+
require "ultrasonic_comm"
|
19
|
+
require "commands/mixins/sensor"
|
20
|
+
|
21
|
+
# Implements the "Ultrasonic Sensor" block in NXT-G
|
22
|
+
class Commands::UltrasonicSensor
|
23
|
+
|
24
|
+
include Commands::Mixins::Sensor
|
25
|
+
|
26
|
+
# Exception thrown by distance! when the sensor cannot determine the distance.
|
27
|
+
class UnmeasurableDistance < Exception; end
|
28
|
+
|
29
|
+
attr_reader :port
|
30
|
+
attr_accessor :mode, :trigger_point, :comparison
|
31
|
+
|
32
|
+
def initialize(nxt)
|
33
|
+
@nxt = nxt
|
34
|
+
|
35
|
+
# defaults the same as NXT-G
|
36
|
+
@port = 4
|
37
|
+
@trigger_point = 50
|
38
|
+
@comparison = "<"
|
39
|
+
@mode = :inches
|
40
|
+
set_mode
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns distance in requested mode (:inches or :centimeters)
|
44
|
+
def distance
|
45
|
+
@nxt.ls_write(NXTComm.const_get("SENSOR_#{@port}"), UltrasonicComm.read_measurement_byte(0))
|
46
|
+
|
47
|
+
# Keep checking until we have data to read
|
48
|
+
while @nxt.ls_get_status(NXTComm.const_get("SENSOR_#{@port}")) < 1
|
49
|
+
sleep(0.1)
|
50
|
+
# TODO: implement timeout so we don't get stuck if the expected data never comes
|
51
|
+
end
|
52
|
+
|
53
|
+
distance = @nxt.ls_read(NXTComm.const_get("SENSOR_#{@port}"))[:data][0]
|
54
|
+
|
55
|
+
if @mode == :centimeters
|
56
|
+
distance.to_i
|
57
|
+
else
|
58
|
+
(distance * 0.3937008).to_i
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias value_scaled distance
|
62
|
+
|
63
|
+
# returns the distance in requested mode; raises an UnmeasurableDistance exception
|
64
|
+
# when the distance cannot be measured (i.e. when the distance == 255, which is
|
65
|
+
# the code the sensor returns when it cannot get a distance reading)
|
66
|
+
def distance!
|
67
|
+
d = distance
|
68
|
+
raise UnmeasurableDistance if d == 255
|
69
|
+
return d
|
70
|
+
end
|
71
|
+
|
72
|
+
# sets up the sensor port
|
73
|
+
def set_mode
|
74
|
+
@nxt.set_input_mode(
|
75
|
+
NXTComm.const_get("SENSOR_#{@port}"),
|
76
|
+
NXTComm::LOWSPEED_9V,
|
77
|
+
NXTComm::RAWMODE
|
78
|
+
)
|
79
|
+
# clear buffer
|
80
|
+
@nxt.ls_read(NXTComm.const_get("SENSOR_#{@port}"))
|
81
|
+
# set sensor to continuously send pings
|
82
|
+
@nxt.ls_write(NXTComm.const_get("SENSOR_#{@port}"), UltrasonicComm.continuous_measurement_command)
|
83
|
+
end
|
84
|
+
end
|
data/lib/motor.rb
ADDED
@@ -0,0 +1,235 @@
|
|
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 "brick"
|
19
|
+
|
20
|
+
# Controls an NXT motor.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# m = Motor.new('a')
|
25
|
+
# m.forward(:degrees => 180, :power => 50)
|
26
|
+
#
|
27
|
+
class Motor < Brick
|
28
|
+
|
29
|
+
POLL_INTERVAL = 0.5
|
30
|
+
|
31
|
+
attr_accessor :ratio
|
32
|
+
|
33
|
+
def initialize(nxt, port)
|
34
|
+
super(nxt, port)
|
35
|
+
|
36
|
+
@port = formalize_motor_port_name(port)
|
37
|
+
|
38
|
+
@ratio = 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def name
|
42
|
+
case self.port
|
43
|
+
when NXTComm::MOTOR_A then 'a'
|
44
|
+
when NXTComm::MOTOR_B then 'b'
|
45
|
+
when NXTComm::MOTOR_C then 'c'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
### high level commands #####################################################
|
50
|
+
|
51
|
+
# Rotate the motor forwards.
|
52
|
+
# See Motor#run.
|
53
|
+
# Examples:
|
54
|
+
#
|
55
|
+
# m.forward(:degrees => 360, :power => 20)
|
56
|
+
#
|
57
|
+
# The above rotates the motor 360 degrees forward at 20% power.
|
58
|
+
#
|
59
|
+
# m.forward(:time => 5)
|
60
|
+
#
|
61
|
+
# The above rotates the motor forwards for 5 seconds at 25% power
|
62
|
+
# (because 25% is the default).
|
63
|
+
def forward(options)
|
64
|
+
debug(options.to_yaml, :forward)
|
65
|
+
options[:direction] = 1
|
66
|
+
return run(options)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Rotate the motor backwards.
|
70
|
+
# See Motor#forward and Motor#run.
|
71
|
+
def backward(options)
|
72
|
+
debug(options, :backward)
|
73
|
+
options[:direction] = -1
|
74
|
+
return run(options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the current state of the motor as a hash.
|
78
|
+
# See NXTComm#get_output_state for info on what data is available here.
|
79
|
+
def read_state
|
80
|
+
@log.debug(:read_state) { "getting state"}
|
81
|
+
state = @nxt.get_output_state(@port)
|
82
|
+
@log.debug(:read_state) { "got state" }
|
83
|
+
|
84
|
+
debug(state, :state)
|
85
|
+
|
86
|
+
return state
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
### low level commands ######################################################
|
91
|
+
|
92
|
+
# Low-level command for initiating motor rotation.
|
93
|
+
# Options is a hash with the following keys:
|
94
|
+
# [+:power+] Power from 0 to 100. Default is 25.
|
95
|
+
# [+:time+] Maximum time to run the motor in seconds.
|
96
|
+
# By default there is no time limit.
|
97
|
+
# If :degrees is also specified then the motor
|
98
|
+
# will only turn as far as :degrees and will stop and
|
99
|
+
# wait out the remaining :time has expired.
|
100
|
+
# [+:regulate+] False to disable power regulation. It is true (i.e. on)
|
101
|
+
# by default.
|
102
|
+
# [+:degrees+] The maximum tachometer degrees to turn before automatically
|
103
|
+
# stopping. Use negative values for backward movement (need to
|
104
|
+
# double check that this is in fact true)
|
105
|
+
# [+:direction+] Direction in which the motor should move. 1 for forward,
|
106
|
+
# -1 for backward. The default is 1 (i.e. forward).
|
107
|
+
# [+:wait_until_complete+] If true, the motor will block further commands
|
108
|
+
# until this command is complete. This is true by default when
|
109
|
+
# :degrees or :time is specified, false by default otherwise.
|
110
|
+
# NOTE: currently this setting is always on and cannot be turned off
|
111
|
+
# when :time is specified... this will probably be fixed in the future
|
112
|
+
# [+:brake_on_stop+] If true, the motor will try to hard brake when the
|
113
|
+
# command completes (otherwise when the command finishes the
|
114
|
+
# motor may continue to coast for a while -- especially at higher
|
115
|
+
# power levels). This is true by default when :degrees or :time
|
116
|
+
# is specified, false by default otherwise.
|
117
|
+
#
|
118
|
+
#
|
119
|
+
# ====Examples:
|
120
|
+
#
|
121
|
+
# Rotate backward up to 90 degrees:
|
122
|
+
#
|
123
|
+
# m.run(:degrees => 90, :direction => -1)
|
124
|
+
#
|
125
|
+
# Rotate forward for 8 seconds at 100% power:
|
126
|
+
#
|
127
|
+
# m.run(:time => 8, :power => 100)
|
128
|
+
#
|
129
|
+
# Forward for 3 seconds up to 180 degrees at 50% power. If the 180 degree
|
130
|
+
# rotation takes only 1 second to complete, the motor will
|
131
|
+
# sit there and wait out the full 3 seconds anyway:
|
132
|
+
#
|
133
|
+
# m.run(:power => 50, :degrees => 180, :time => 3)
|
134
|
+
#
|
135
|
+
# Rotate forward indefinitely (until Motor#stop is called).
|
136
|
+
#
|
137
|
+
# m.run
|
138
|
+
#
|
139
|
+
def run(options)
|
140
|
+
debug(options, :run)
|
141
|
+
|
142
|
+
if options[:power]
|
143
|
+
power = options[:power].to_i.abs
|
144
|
+
else
|
145
|
+
power = 25
|
146
|
+
end
|
147
|
+
|
148
|
+
time = options[:time] || nil
|
149
|
+
regulate = options[:regulate] || true
|
150
|
+
regulation_mode = options[:regulation_mode] || "speed"
|
151
|
+
degrees = options[:degrees] || 0
|
152
|
+
ratio = options[:ratio] || self.ratio
|
153
|
+
direction = options[:direction] || 1 # 1 is forward, -1 is backward
|
154
|
+
|
155
|
+
brake_on_stop = options.has_key?(:time) || options.has_key?(:degrees)
|
156
|
+
wait_until_complete = options.has_key?(:time) || options.has_key?(:degrees)
|
157
|
+
|
158
|
+
brake_on_stop = options[:brake_on_stop] if options.has_key?(:brake_on_stop)
|
159
|
+
|
160
|
+
wait_until_complete = options[:wait_until_complete] if options.has_key?(:wait_until_complete)
|
161
|
+
# FIXME: wait_until_complete MUST be true if a time period is specified, otherwise we have no way of
|
162
|
+
# enforcing the time limit (this is a problem with the way threading is implemented...)
|
163
|
+
wait_until_complete = true if options.has_key?(:time)
|
164
|
+
|
165
|
+
power = direction * power
|
166
|
+
|
167
|
+
mode = NXTComm::MOTORON
|
168
|
+
mode |= NXTComm::BRAKE if brake_on_stop
|
169
|
+
mode |= NXTComm::REGULATED if regulate
|
170
|
+
|
171
|
+
if regulate
|
172
|
+
case regulation_mode
|
173
|
+
when "idle"
|
174
|
+
regulation_mode = NXTComm::REGULATION_MODE_IDLE
|
175
|
+
when "speed"
|
176
|
+
regulation_mode = NXTComm::REGULATION_MODE_MOTOR_SPEED
|
177
|
+
when "sync"
|
178
|
+
regulation_mode = NXTComm::REGULATION_MODE_MOTOR_SYNC
|
179
|
+
end
|
180
|
+
else
|
181
|
+
regulation_mode = NXTComm::REGULATION_MODE_IDLE
|
182
|
+
end
|
183
|
+
|
184
|
+
@log.debug(:run) {"sending run command"}
|
185
|
+
@nxt.set_output_state(@port, power, mode, regulation_mode, ratio, NXTComm::MOTOR_RUN_STATE_RUNNING, degrees)
|
186
|
+
|
187
|
+
if time.nil?
|
188
|
+
if wait_until_complete
|
189
|
+
@log.debug(:run) {"sleeping until run_state is idle"}
|
190
|
+
until read_state[:run_state] == NXTComm::MOTOR_RUN_STATE_IDLE
|
191
|
+
sleep(POLL_INTERVAL)
|
192
|
+
@log.debug(:run) {"checking run_state again"}
|
193
|
+
end
|
194
|
+
@log.debug(:run) {"run_state is idle"}
|
195
|
+
end
|
196
|
+
else
|
197
|
+
@log.debug(:run) {"waiting #{time} seconds until stop"}
|
198
|
+
sleep(time)
|
199
|
+
@log.debug(:run) {"stopping"}
|
200
|
+
self.stop
|
201
|
+
@log.debug(:run) {"stopped"}
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Stop movement.
|
206
|
+
def stop
|
207
|
+
debug(nil, :stop)
|
208
|
+
@nxt.set_output_state(@port, 100, NXTComm::BRAKE, NXTComm::REGULATION_MODE_MOTOR_SPEED, 100, NXTComm::MOTOR_RUN_STATE_IDLE, 0)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Resets the motor's tachometer movement count (i.e. +:degree_count+ in Motor#state).
|
212
|
+
def reset_tacho
|
213
|
+
@log.debug(:reset_tacho) { "resetting tacho" }
|
214
|
+
@nxt.reset_motor_position(@port, false)
|
215
|
+
@log.debug(:reset_tacho) { "reset tacho" }
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
def formalize_motor_port_name(port)
|
220
|
+
port = port.downcase.intern if port.kind_of? String
|
221
|
+
|
222
|
+
if port.kind_of? Symbol
|
223
|
+
case port
|
224
|
+
when :a then return NXTComm::MOTOR_A
|
225
|
+
when :b then return NXTComm::MOTOR_B
|
226
|
+
when :c then return NXTComm::MOTOR_C
|
227
|
+
end
|
228
|
+
elsif [NXTComm::MOTOR_A, NXTComm::MOTOR_B, NXTComm::MOTOR_C].include? port
|
229
|
+
return port
|
230
|
+
else
|
231
|
+
raise "'#{port}' is not a valid motor port"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|