ruby-nxt 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
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
+
19
+ require File.dirname(File.expand_path(__FILE__))+'/sensor'
20
+
21
+ class LightSensor < Sensor
22
+
23
+ def initialize(nxt, port = NXTComm::SENSOR_3)
24
+ super(nxt, port)
25
+ use_illuminated_mode
26
+ end
27
+
28
+ # Get the current light level as a float from 0 to 1.0.
29
+ # 1.0 is maximum, 0 is minimum.
30
+ def get_light_level
31
+ # TODO: probably need to calibrate this... light level never really reaches 1023
32
+ (read_data[:value_scaled]).to_f / 1023.to_f
33
+ end
34
+
35
+
36
+ def use_illuminated_mode
37
+ set_input_mode(NXTComm::LIGHT_ACTIVE, NXTComm::RAWMODE)
38
+ end
39
+
40
+
41
+ def use_ambient_mode
42
+ set_input_mode(NXTComm::LIGHT_INACTIVE, NXTComm::RAWMODE)
43
+ end
44
+
45
+ end
@@ -0,0 +1,93 @@
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
+
19
+ require File.dirname(File.expand_path(__FILE__))+'/../brick'
20
+
21
+ # Controls and reads an NXT sensor.
22
+ class Sensor < Brick
23
+
24
+ POLL_INTERVAL = 0.25
25
+
26
+ # TODO: internal sensor numbering is 0x00 to 0x03, but sensor ports on the brick are marked 1 to 4...
27
+ # need to come up with some way to make sure there isn't confusion (or maybe it's okay, since
28
+ # if the user probably never uses the Sensor classes directly, only interacting via the NXT
29
+ # class?)
30
+
31
+
32
+ def initialize(nxt, port)
33
+ super(nxt, port)
34
+ @port = port
35
+ end
36
+
37
+ def name
38
+ "#{@port + 1}"
39
+ end
40
+
41
+ def set_input_mode(type, mode)
42
+ @nxt.set_input_mode(@port, type, mode)
43
+ end
44
+
45
+ def read_data
46
+ data = @nxt.get_input_values(@port)
47
+
48
+ debug(data.inspect, :read_data)
49
+ return data
50
+ end
51
+
52
+ # Continuously evalutes the given block until it returns at
53
+ # least the given value (that is, until the block returns a
54
+ # value equal or greater than the given argument). If no
55
+ # value is specified, the block will be continuously
56
+ # evaluated until it returns true. Optionally, a comparison
57
+ # operator can be specified as the second parameter,
58
+ # otherwise >= is used (or == if expected value is Boolean).
59
+ #
60
+ # Simple example:
61
+ #
62
+ # ts = TouchSensor.new(@nxt)
63
+ # ts.wait_for_event { ts.is_pressed? }
64
+ #
65
+ # Example with an expected value:
66
+ #
67
+ # ls = LightSensor.new(@nxt)
68
+ # ls.wait_for_event(0.55) do
69
+ # ls.get_light_level
70
+ # end
71
+ #
72
+ def wait_for_event(expected = true, operator = nil)
73
+ if operator
74
+ comp = operator
75
+ elsif expected.respond_to? '>='.intern
76
+ comp = ">="
77
+ else
78
+ comp = "=="
79
+ end
80
+
81
+ while true
82
+ value = yield
83
+ return value if eval("value #{comp} expected")
84
+ sleep(POLL_INTERVAL)
85
+ end
86
+ end
87
+
88
+ def off
89
+ # Turns off the sensor.
90
+ set_input_mode(NXTComm::NO_SENSOR, NXTComm::RAWMODE)
91
+ end
92
+
93
+ end
@@ -0,0 +1,48 @@
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
+
19
+ require File.dirname(File.expand_path(__FILE__))+'/sensor'
20
+
21
+ class SoundSensor < Sensor
22
+
23
+ def initialize(nxt, port = NXTComm::SENSOR_2)
24
+ super(nxt, port)
25
+ use_adjusted_mode
26
+ end
27
+
28
+ # Get the current sound level as a float from 0 to 1.0.
29
+ # 1.0 is maximum, 0 is minimum.
30
+ def get_sound_level
31
+ # TODO: should probably do some basic calibration here...
32
+ read_data[:value_normal] / 1023.to_f
33
+ end
34
+
35
+ # Sound level measurement is NOT adjusted to match the psychoacoustic properties
36
+ # of human hearing. Sounds that may not be loud to the human ear may show up as loud
37
+ # and vice versa.
38
+ def use_unadjusted_mode
39
+ set_input_mode(NXTComm::SOUND_DB, NXTComm::RAWMODE)
40
+ end
41
+
42
+ # Sound level measurement is adjusted to match the psychoacoustic properties of
43
+ # human hearing. This is on by default.
44
+ def use_adjusted_mode
45
+ set_input_mode(NXTComm::SOUND_DBA, NXTComm::RAWMODE)
46
+ end
47
+
48
+ end
@@ -0,0 +1,35 @@
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
+
19
+ require File.dirname(File.expand_path(__FILE__))+'/sensor'
20
+
21
+ class TouchSensor < Sensor
22
+
23
+ def initialize(nxt, port = NXTComm::SENSOR_1)
24
+ super(nxt, port)
25
+ set_input_mode(NXTComm::SWITCH, NXTComm::BOOLEANMODE)
26
+ end
27
+
28
+ # Returns true if the touch sensor is pressed, false otherwise.
29
+ # (The sensor seems to read as "pressed" when it is pushed in about half way)
30
+ def is_pressed?
31
+ read_data[:value_scaled] > 0
32
+ end
33
+ alias_method :pressed?, :is_pressed?
34
+
35
+ end
@@ -0,0 +1,95 @@
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 File.dirname(File.expand_path(__FILE__))+'/sensor'
18
+ require File.dirname(File.expand_path(__FILE__))+'/../ultrasonic_comm'
19
+
20
+ class UltrasonicSensor < Sensor
21
+
22
+ INCHES_PER_CM = 0.3937008
23
+
24
+ def initialize(nxt, port = NXTComm::SENSOR_4)
25
+ super(nxt, port)
26
+
27
+ # The Ultrasonic sensor is digital and unlike the other sensors it
28
+ # uses the lowspeed communication protocol.
29
+ set_input_mode(NXTComm::LOWSPEED_9V, NXTComm::RAWMODE)
30
+
31
+ # Read the sensor in case there was some garbage data in the buffer waiting to be read
32
+ @nxt.ls_read(@port)
33
+
34
+ # Set the sensor to continuously send pings
35
+ @nxt.ls_write(@port, UltrasonicComm.continuous_measurement_command)
36
+ end
37
+
38
+ # Return the measured distance in the default units (the default units being centimeters).
39
+ # A value of 255 is returned when the sensor cannot get an accurate reading (because
40
+ # the object is out of range, or there is too much interference, etc.)
41
+ # Note that the sensor's real-world range is at best 175 cm. At larger distances it
42
+ # will almost certainly just return 255.
43
+ def get_distance
44
+ @nxt.ls_write(@port, UltrasonicComm.read_measurement_byte(0))
45
+
46
+ # Keep checking until we have 1 byte of data to read
47
+ while @nxt.ls_get_status(@port)[0] < 1
48
+ sleep(0.1)
49
+ @nxt.ls_write(@port, UltrasonicComm.read_measurement_byte(0))
50
+ # TODO: implement timeout so we don't get stuck if the expected data never comes
51
+ end
52
+
53
+ resp = @nxt.ls_read(@port)
54
+ # TODO: probably need a better error message here...
55
+ raise "ls_read returned more than one byte!" if resp[:bytes_read] > 1
56
+ raise "ls_read did not return any data!" if resp[:bytes_read] < 1
57
+
58
+ # If the sensor cannot determine the distance, it will return
59
+ # 0xff (255)... this usually means that the object is out of
60
+ # sensor range, but it can also mean that there is too much
61
+ # interference or that the object is too close to the sensor.
62
+ # I considered returning nil or false under such cases, but
63
+ # this makes numeric comparison (i.e. greather than/less than)
64
+ # more difficult
65
+ d = resp[:data][0]
66
+ end
67
+ alias_method :get_distance_in_cm, :get_distance
68
+
69
+ # Return the measured distance in inches.
70
+ def get_distance_in_inches
71
+ get_distance.to_f * INCHES_PER_CM
72
+ end
73
+
74
+ # Same as get_distance, but raises an UnmeasurableDistance
75
+ # exception when the sensor cannot accurately determine
76
+ # the distance.
77
+ def get_distance!
78
+ d = get_distance
79
+ if d == 255
80
+ raise UnmeasurableDistance
81
+ else
82
+ return d
83
+ end
84
+ end
85
+ alias_method :get_distance_in_cm!, :get_distance!
86
+
87
+ def get_distance_in_inches!
88
+ get_distance!.to_f * INCHES_PER_CM
89
+ end
90
+
91
+ # Exception thrown by get_distance! and related methods when
92
+ # the sensor cannot determine the distance.
93
+ class UnmeasurableDistance < Exception; end
94
+
95
+ end
@@ -0,0 +1,102 @@
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
+ # Low-level interface for communicating with the NXT's Ultrasonic
19
+ # sensor. Unlike the other sensors, the Ultrasonic sensor is digital
20
+ # and uses the low-speed I2C protocol for communication. This is
21
+ # defined in Appendix 7 of the Lego Mindstorms NXT SDK.
22
+ class UltrasonicComm
23
+
24
+ @@i2c_dev = 0x02
25
+
26
+ # first value is the i2c address, second value is the expected number of bytes returned
27
+ @@const_codes = {
28
+ 'read_version' => [0x00, 8],
29
+ 'read_product_id' => [0x08, 8],
30
+ 'read_sensor_type' => [0x10, 8],
31
+ 'read_factory_zero' => [0x11, 1],
32
+ 'read_factory_scale_factor' => [0x12, 1],
33
+ 'read_factory_scale_divisor' => [0x13, 1],
34
+ 'read_measurement_units' => [0x14, 7],
35
+ }
36
+
37
+ # value is the i2c address (all of these ops always expect to return 1 byte)
38
+ @@var_codes = {
39
+ 'read_continuous_measurements_interval' => 0x40,
40
+ 'read_command_state' => 0x41,
41
+ 'read_measurement_byte_0' => 0x42,
42
+ 'read_measurement_byte_1' => 0x43,
43
+ 'read_measurement_byte_2' => 0x44,
44
+ 'read_measurement_byte_3' => 0x45,
45
+ 'read_measurement_byte_4' => 0x46,
46
+ 'read_measurement_byte_5' => 0x47,
47
+ 'read_measurement_byte_6' => 0x48,
48
+ 'read_measurement_byte_7' => 0x49,
49
+ 'read_actual_zero' => 0x50,
50
+ 'read_actual_scale_factor' => 0x51,
51
+ 'read_actual_scale_divisor' => 0x52,
52
+ }
53
+
54
+ # first value is the i2c address, second value is the command
55
+ @@cmd_codes = {
56
+ 'off_command' => [0x41, 0x00],
57
+ 'single_shot_command' => [0x41, 0x01],
58
+ 'continuous_measurement_command' => [0x41, 0x02],
59
+ 'event_capture_command' => [0x41, 0x03],
60
+ 'request_warm_reset' => [0x41, 0x04],
61
+ 'set_continuous_measurement_interval' => [0x40],
62
+ 'set_actual_zero' => [0x50],
63
+ 'set_actual_scale_factor' => [0x51],
64
+ 'set_actual_scale_divisor' => [0x52]
65
+ }
66
+
67
+
68
+ def self.read_measurement_byte(num)
69
+ eval "self.read_measurement_byte_#{num}"
70
+ end
71
+
72
+ def self.method_missing(name, *args)
73
+ name = name.to_s
74
+ if @@const_codes.has_key? name
75
+ type = :const
76
+ op = @@const_codes[name]
77
+ addr = op[0]
78
+ rx_len = op[1]
79
+ elsif @@var_codes.has_key? name
80
+ type = :var
81
+ op = @@var_codes[name]
82
+ addr = op
83
+ rx_len = 1
84
+ elsif @@cmd_codes.has_key? name
85
+ type = :cmd
86
+ op = @@cmd_codes[name]
87
+ addr = op[0]
88
+ if op[1] then value = op[1]
89
+ elsif args[0] then value = args[0]
90
+ else raise "Missing argument for command #{name}" end
91
+ rx_len = 0
92
+ else
93
+ raise "Unknown ultrasonic sensor command: #{name}"
94
+ end
95
+
96
+ data = [@@i2c_dev, addr]
97
+ data += [value] if type == :cmd
98
+
99
+ [data.size, rx_len] + data
100
+ end
101
+
102
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require "nxt_comm"
4
+
5
+ $DEBUG = true
6
+
7
+ @nxt = NXTComm.new("/dev/tty.NXT-DevB-1")
8
+
9
+ @nxt.message_write(1,180)
10
+ @nxt.message_write(1,0)
@@ -0,0 +1,89 @@
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
+ begin
18
+ require 'rubygems'
19
+ require_gem "term-ansicolor"
20
+ $COLOUR = true
21
+ rescue LoadError
22
+ $COLOUR = false
23
+ puts "Please install term-ansicolor gem to get colour output."
24
+ puts "Colour output will be disabled..."
25
+ puts
26
+ end
27
+
28
+ module InteractiveTestHelper
29
+ def info(msg)
30
+ msg = Term::ANSIColor.white(msg) if $COLOUR
31
+ puts "#{msg}"
32
+ end
33
+
34
+ def notice(msg)
35
+ msg = Term::ANSIColor.yellow(msg) if $COLOUR
36
+ puts "#{msg}"
37
+ end
38
+
39
+ def prompt(msg)
40
+ msg = Term::ANSIColor.cyan(msg) if $COLOUR
41
+ puts "#{msg}"
42
+ return gets
43
+ end
44
+
45
+ def pass(msg)
46
+ msg = Term::ANSIColor.green(msg) if $COLOUR
47
+ msg = Term::ANSIColor.bold(msg) if $COLOUR
48
+ puts "#{msg}"
49
+ end
50
+
51
+ def fail(msg)
52
+ msg = Term::ANSIColor.red(msg) if $COLOUR
53
+ msg = Term::ANSIColor.red(msg) if $COLOUR
54
+ puts "#{msg}"
55
+ end
56
+
57
+ def puts_sameline(msg)
58
+ puts "#{msg}"
59
+ puts "\e[2A"
60
+ end
61
+
62
+ def meter(value, label = "Level", threshold = nil, max = 100, min = 0)
63
+ width = 40
64
+ if value.nil?
65
+ bars = "?" * width
66
+ spaces = ""
67
+ percent_formatted = " ??? "
68
+ else
69
+ if value >= max
70
+ percent = 1.0
71
+ else
72
+ percent = ((value - min) / max.to_f)
73
+ end
74
+ bars = "|" * (percent * width)
75
+ spaces = " " * ((1 - percent) * width)
76
+
77
+ percent_formatted = "%5.1f" % (percent * 100)
78
+ end
79
+ meterbar = bars + spaces
80
+
81
+ if threshold
82
+ threshold_pos = (((threshold - min) / max.to_f) * 40).to_i
83
+ meterbar[threshold_pos] = "!"
84
+ end
85
+
86
+ puts_sameline " #{label}: (#{percent_formatted}%) [#{min}]#{meterbar}[#{max}]"
87
+ end
88
+
89
+ end