Class: NXT
- Inherits:
-
Object
- Object
- NXT
- Defined in:
- lib/nxt.rb
Overview
High-level interface for controlling motors and sensors connected to the NXT. Currently only motors and some other misc functionality is implemented.
Examples:
nxt = NXT.new('/dev/tty.NXT-DevB-1')
nxt.motor_a do |m|
m.forward(:degrees => 180, :power => 15)
end
nxt.motors_bc do |m|
m.backward(:time => 5, :power => 20)
end
nxt.motors_abc do |m|
m.reset_tacho
m.forward(:time => 3, :power => 10)
puts "Motor #{m.name} moved #{m.read_state[:degree_count]} degrees."
end
nxt.disconnect
Be sure to call NXT#disconnect when done sending commands, otherwise there may be trouble if you try to connect or send commands again afterwards.
Instance Method Summary collapse
-
#disconnect ⇒ Object
Waits for all running jobs to finish and cleanly closes all connections to the NXT device.
-
#initialize(dev = $DEV) ⇒ NXT
constructor
Initialize the NXT.
- #method_missing(method, *args, &block) ⇒ Object
-
#motor(id, proc) ⇒ Object
Runs the given proc for the given motor.
-
#motors(which, proc) ⇒ Object
Runs the given proc for multiple motors.
- #sensor(id, proc) ⇒ Object
Constructor Details
#initialize(dev = $DEV) ⇒ NXT
Initialize the NXT. This creates three Motor instances and one kind of each sensor. It is assumed that the sensors are connected to the standard ports as follows:
-
Port 1: Touch
-
Port 2: Sound
-
Port 3: Light
-
Port 4: Ultrasonic
You can specify the path to the serialport device (e.g. ‘/dev/tty.NXT-DevB-1’) or omit the argument to use the serialport device specified in the global $DEV variable.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/nxt.rb', line 64 def initialize(dev = $DEV) @nxt = NXTComm.new(dev) @motors = {} @motors[:a] = Motor.new(@nxt, :a) @motors[:b] = Motor.new(@nxt, :b) @motors[:c] = Motor.new(@nxt, :c) @sensors = {} @sensors[1] = TouchSensor.new(@nxt, NXTComm::SENSOR_1) @sensors[2] = SoundSensor.new(@nxt, NXTComm::SENSOR_2) @sensors[3] = LightSensor.new(@nxt, NXTComm::SENSOR_3) @sensors[4] = UltrasonicSensor.new(@nxt, NXTComm::SENSOR_4) @motor_threads = {} @sensor_threads = {} end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/nxt.rb', line 82 def method_missing(method, *args, &block) name = method.id2name if /^motor_([abc])$/ =~ name motor($1, block) elsif /^motors_([abc]+?)$/ =~ name motors($1, block) elsif /^sensor_([1234])$/ =~ name sensor($1, block) elsif /^sensor_(touch|sound|light|ultrasonic)$/ =~ name or /^(touch|sound|light|ultrasonic)_sensor$/ =~ name case $1 when 'touch' sensor(1, block) when 'sound' sensor(2, block) when 'light' sensor(3, block) when 'ultrasonic' sensor(4, block) else raise "'#{$1}' is not a valid sensor." end else # if the method is not recognized, we assume it is a low-level NXTComm command m = @nxt.method(method) m.call(*args) end end |
Instance Method Details
#disconnect ⇒ Object
Waits for all running jobs to finish and cleanly closes all connections to the NXT device. You should always call this when done sending commands to the NXT.
183 184 185 186 187 |
# File 'lib/nxt.rb', line 183 def disconnect @sensor_threads.each {|i,t| t.join} @motor_threads.each {|i,t| t.join} @nxt.close end |
#motor(id, proc) ⇒ Object
Runs the given proc for the given motor. You should use the motor_x dynamic method instead of calling this directly. For example…
nxt.motor_a {|m| m.forward(:degrees => 180)}
…would rotate motor A by 180 degrees, while…
nxt.motor_b {|m| m.forward(:degrees => 180)}
…would do the same for motor B.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/nxt.rb', line 145 def motor(id, proc) id = id.intern if id.kind_of? String # If a thread for this motor is already running, wait until it's finished. # In other words, don't try to send another command to the motor if it is already # doing something else; wait until it's done and then send. # FIXME: I think this blocks the entire program... is that what we really want? # I think it is, but need to think about it more... @motor_threads[id].join if (@motor_threads[id] and @motor_threads[id].alive?) t = Thread.new(@motors[id]) do |m| proc.call(m) end @motor_threads[id] = t end |
#motors(which, proc) ⇒ Object
Runs the given proc for multiple motors. You should use the motors_xxx dynamic method instead of calling this directly. For example…
nxt.motors_abc {|m| m.forward(:degrees => 180)}
…would run the given block simultanously on all three motors, while…
nxt.motors_bc {|m| m.forward(:degrees => 180)}
…would only run it on motors B and C.
123 124 125 126 127 128 129 130 131 |
# File 'lib/nxt.rb', line 123 def motors(which, proc) which = which.scan(/\w/) if which.kind_of? String which = which.uniq which = @motors.keys if which.nil? or which.empty? which.each do |id| motor(id, proc) end end |
#sensor(id, proc) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/nxt.rb', line 162 def sensor(id, proc) id = id.to_i @sensor_threads[id].join if (@sensor_threads[id] and @sensor_threads[id].alive?) t = Thread.new(@sensors[id]) do |m| proc.call(m) end # FIXME: this blocks until we get something back from the sensor... probably # not the smartest way to do this t.join # FIXME: do we need to store the thread? it will always be dead by this point.. @sensor_threads[id] = t end |