rumba 0.2.0 → 0.2.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b1d712a0e9b5806c625ea88f90a7ac66d326cde2
4
- data.tar.gz: 8119e32a631feffedfdced0645e5cfbf3aefe958
3
+ metadata.gz: 61b352b09eef601f9b36509b7283772d366fe906
4
+ data.tar.gz: 5fd8ed4266bbe4b72ccac8a2ee5ead83217857ac
5
5
  SHA512:
6
- metadata.gz: e3cb42c3be2c4d84dbfa611840eb8351b628607d486d46ccd216ee53863e1b128d8c0cf4991cfc37dbeed6b72f7fa9808fd307571fd78c3c35ae669c3de4dc44
7
- data.tar.gz: 1db707dc89538cc544c587e2efbd00a9959d5df7a3e01405defc576a36afe96b5bdbaf66f30a6dd007953de68cf3ff4239fe2142f40c077681343003611cfd92
6
+ metadata.gz: fe9715009338618cd7357377b666b95243eb4261abc356ed2d208b6f9ce18c96bdafac2c6e3bee85df21c9e81775b85dd464ffd9d4d38a284353e8125b1d08b8
7
+ data.tar.gz: e85b669ef50bf3faf18fbe0fe41650e7a156fe4c83ccf8a09b1debe0daa00681905b1717be630b7ecfc99b5a624a5d476528b657787a65382afd6dbc502d7e32
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Rumba
2
+ A Ruby wrapper for the Roomba Serial Command Interface
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/rumba.svg)](http://badge.fury.io/rb/rumba)
5
+
6
+ - - -
7
+
8
+ This is a no-frills, lightweight, and cross-platform implementation of the iRobot Roomba Serial Command Interface. You can use it to control your Roomba from your computer :D
9
+
10
+ Practically all of the interesting parts of the SCI have been implemented. There's also an optional DSL that only supports movements currently (but is under active development!).
11
+
12
+ Originally used as part of my senior design project in school, but now gem-ified for the handful of other people who think robots and Ruby are a good combination :)
13
+
14
+ For more serious robot hacking in Ruby, check out [Artoo](http://artoo.io/)! It's really neat, but possibly a little overkill for small hacks.
15
+
16
+ I welcome pull requests and feedback!
17
+
18
+ Happy hacking!
19
+
20
+ ### Dependencies
21
+ * [serialport](http://ruby-serialport.rubyforge.org/)
22
+
23
+ ### Usage
24
+
25
+ Here's an example program:
26
+
27
+ (using the experimental DSL)
28
+
29
+ ```ruby
30
+ require 'rumba'
31
+
32
+ Roomba.new('/dev/tty.usbserial') do
33
+ safe_mode
34
+ forward meter(1)
35
+ rotate :left
36
+ rotate -90 # degrees
37
+
38
+ rotate :right
39
+ rotate 90
40
+ backward meter(1)
41
+
42
+ # access to any methods in the Roomba class here!
43
+ end
44
+ ```
45
+
46
+ (the old-fashioned way!)
47
+
48
+ ```ruby
49
+ require 'rumba'
50
+ r = Roomba.new('/dev/tty.SerialIO1-SPP')
51
+ r.full_mode # Change to full mode (unrestricted access)
52
+ r.straight(300) # Move forwards at 300 mm/s
53
+ sleep(2)
54
+ r.straight(-300) # Move backwards at 300 mm/s
55
+ sleep(2)
56
+ r.spin_left(500) # Spin to the left at 500 mm/s
57
+ sleep(2)
58
+ r.spin_right(500) # Spin to the right at 500 mm/s
59
+ r.halt # Stop moving
60
+ ```
61
+
62
+ ### Roadmap
63
+ * Create an optional DSL (IN PROGRESS!)
64
+ * REAL documentation!
65
+ * ???
66
+ * idk
67
+
68
+ ### More Information
69
+
70
+ The complete Roomba SCI specification can be found [here](http://www.irobot.com/images/consumer/hacker/roomba_sci_spec_manual.pdf)
71
+
72
+ ### License
73
+
74
+ ```
75
+ Copyright (c) 2014, Eric Wood
76
+ All rights reserved.
77
+
78
+ Redistribution and use in source and binary forms, with or without
79
+ modification, are permitted provided that the following conditions are met:
80
+ * Redistributions of source code must retain the above copyright
81
+ notice, this list of conditions and the following disclaimer.
82
+ * Redistributions in binary form must reproduce the above copyright
83
+ notice, this list of conditions and the following disclaimer in the
84
+ documentation and/or other materials provided with the distribution.
85
+ * Neither the name of the author nor the
86
+ names of its contributors may be used to endorse or promote products
87
+ derived from this software without specific prior written permission.
88
+
89
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
90
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
91
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
92
+ DISCLAIMED. IN NO EVENT SHALL ERIC WOOD BE LIABLE FOR ANY
93
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
94
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
95
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
96
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
97
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
98
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
99
+ ```
@@ -0,0 +1,54 @@
1
+ # This library has a TON of annoying constants...
2
+ # we have quarantined them here to save your eyes
3
+
4
+ class Rumba
5
+ module Constants
6
+ # These opcodes require no arguments
7
+ OPCODES = {
8
+ start: 128,
9
+ control: 130,
10
+ power: 133,
11
+ spot: 134,
12
+ clean: 135,
13
+ max: 136,
14
+ dock: 143,
15
+ play_script: 153,
16
+ show_script: 154
17
+ }
18
+
19
+ # Create a method for each opcode that writes its data.
20
+ # This allows us to simply call roomba.code,
21
+ # and it's a cool excuse to do some metaprogramming :)
22
+ OPCODES.each do |name,val|
23
+ send :define_method, name do
24
+ write_chars([val])
25
+ end
26
+ end
27
+
28
+ SAFE_MODE = 131
29
+ FULL_MODE = 132
30
+
31
+ # These opcodes require arguments
32
+ DRIVE = 137
33
+ MOTORS = 138
34
+ LEDS = 139
35
+ SONG = 140
36
+ PLAY_SONG = 141
37
+ SENSORS = 142
38
+ QUERY_LIST = 149
39
+ DRIVE_DIRECT = 145
40
+
41
+ # Used for making the Roomba sing!
42
+ # Note that nil is simply a rest
43
+ NOTES = {
44
+ 'A' => 69, 'A#' => 70, 'B' => 71, 'C' => 72, 'C#' => 73, 'D' => 74,
45
+ 'D#' => 75, 'E' => 76, 'F' => 77, 'F#' => 78, 'G' => 79, 'G#' => 80,
46
+ nil => 0
47
+ }
48
+
49
+ MOTORS_MASK_SIDE_BRUSH = 0x1
50
+ MOTORS_MASK_VACUUM = 0x2
51
+ MOTORS_MASK_MAIN_BRUSH = 0x4
52
+
53
+ end
54
+ end
data/lib/rumba/dsl.rb ADDED
@@ -0,0 +1,72 @@
1
+ # Define the Rumba "DSL"
2
+ # Lots of easy to use methods for basic tasks
3
+
4
+ class Rumba
5
+ module Dsl
6
+ # Remember, Roomba speeds are defined in mm/s (max is 200)
7
+ DEFAULT_SPEED = 200
8
+
9
+ # Radius of an average Roomba, used for calculating rotation
10
+ RADIUS = 165.1 # 6.5 inches
11
+
12
+ # distance is in mm!
13
+ def forward(distance, speed: DEFAULT_SPEED)
14
+ duration = distance / speed
15
+ straight(speed)
16
+ sleep(duration)
17
+ halt
18
+ end
19
+
20
+ # distance is in mm!
21
+ def backward(distance, speed: DEFAULT_SPEED)
22
+ duration = distance / speed
23
+ straight(-speed)
24
+ sleep(duration)
25
+ halt
26
+ end
27
+
28
+ # Direction can either be a Fixnum for number of degrees,
29
+ # or a symbol for the direction (:left, :right)
30
+ def rotate(direction, speed: DEFAULT_SPEED)
31
+ # handle symbols...
32
+ case direction
33
+ when :left
34
+ direction = -90
35
+ when :right
36
+ direction = 90
37
+ end
38
+
39
+ circumfrence = 2 * Math::PI * RADIUS
40
+
41
+ # based on the angle, this is how far we need to turn
42
+ distance = ((circumfrence / 360) * direction).abs
43
+
44
+ direction < 0 ? spin_left(speed) : spin_right(speed)
45
+ duration = (distance / speed).abs
46
+ sleep(duration)
47
+ halt
48
+ end
49
+
50
+ # MEASUREMENT HELPERS
51
+ # TODO: break these out into separate helpers file?
52
+ def inches(num)
53
+ 25.4 * num
54
+ end
55
+ alias_method :inch, :inches
56
+
57
+ def feet(num)
58
+ inches(num) * 12
59
+ end
60
+ alias_method :foot, :feet
61
+
62
+ def meters(num)
63
+ num * 1000
64
+ end
65
+ alias_method :meter, :meters
66
+
67
+ # eh, why not?
68
+ alias_method :forwards, :forward
69
+ alias_method :backwards, :backward
70
+ alias_method :turn, :rotate
71
+ end
72
+ end
@@ -0,0 +1,296 @@
1
+ # Sensor-related code is all here!
2
+
3
+ class Rumba
4
+ module Sensor
5
+ class Boolean
6
+ def self.convert(v)
7
+ v == 1 ? true : false
8
+ end
9
+ end
10
+
11
+ class ChargingState
12
+ def self.convert(v)
13
+ case v
14
+ when 0
15
+ :not_charging
16
+ when 1
17
+ :reconditioning_charging
18
+ when 2
19
+ :full_charging
20
+ when 3
21
+ :trickle_charging
22
+ when 4
23
+ :waiting
24
+ when 5
25
+ :charging_fault_condition
26
+ end
27
+ end
28
+ end
29
+
30
+ class OIMode
31
+ def self.convert(v)
32
+ case v
33
+ when 0
34
+ :off
35
+ when 1
36
+ :passive
37
+ when 2
38
+ :safe
39
+ when 3
40
+ :full
41
+ end
42
+ end
43
+ end
44
+
45
+ class ChargingSourceAvailable
46
+ def self.convert(v)
47
+ h = {}
48
+ h[:internal_charger] = v & 0b1 > 0 ? true : false
49
+ h[:home_base] = v & 0b10 > 0 ? true : false
50
+ h
51
+ end
52
+ end
53
+
54
+ class LightBumper
55
+ def self.convert(v)
56
+ h = {}
57
+ h[:light_bumper_left] = v & 0b1 > 0 ? true : false
58
+ h[:light_bumper_front_left] = v & 0b10 > 0 ? true : false
59
+ h[:light_bumper_center_left] = v & 0b100 > 0 ? true : false
60
+ h[:light_bumper_center_right] = v & 0b1000 > 0 ? true : false
61
+ h[:light_bumper_front_right] = v & 0b10000 > 0 ? true : false
62
+ h[:light_bumper_right] = v & 0b100000 > 0 ? true : false
63
+ h
64
+ end
65
+ end
66
+
67
+ class WheelOvercurrents
68
+ def self.convert(v)
69
+ h = {}
70
+ h[:side_brush] = v & 0b1 > 0 ? true : false
71
+ h[:main_brush] = v & 0b100 > 0 ? true : false
72
+ h[:right_wheel] = v & 0b1000 > 0 ? true : false
73
+ h[:left_wheel] = v & 0b10000 > 0 ? true : false
74
+ h
75
+ end
76
+ end
77
+
78
+ class BumpsAndWheelDrops
79
+ def self.convert(v)
80
+ h = {}
81
+ h[:bump_right] = v & 0b1 > 0 ? true : false
82
+ h[:bump_left] = v & 0b10 > 0 ? true : false
83
+ h[:wheel_drop_right] = v & 0b100 > 0 ? true : false
84
+ h[:wheel_drop_left] = v & 0b1000 > 0 ? true : false
85
+ h
86
+ end
87
+
88
+ end
89
+
90
+ INFRARED_CHARACTER = {
91
+ 129 => :left,
92
+ 130 => :forward,
93
+ 131 => :right,
94
+ 132 => :spot,
95
+ 133 => :max,
96
+ 134 => :small,
97
+ 135 => :medium,
98
+ 136 => :large,
99
+ 137 => :stop,
100
+ 138 => :power,
101
+ 139 => :arc_left,
102
+ 140 => :arc_right,
103
+ 141 => :stop,
104
+ 142 => :download,
105
+ 143 => :seek_dock,
106
+ 160 => :reserved,
107
+ 161 => :force_field,
108
+ 164 => :green_buoy,
109
+ 165 => :green_buoy_and_force_field,
110
+ 168 => :red_buoy,
111
+ 169 => :red_buoy_and_force_field,
112
+ 172 => :red_and_green_buoy,
113
+ 173 => :red_and_green_buoy_and_force_field,
114
+ 240 => :reserved,
115
+ 248 => :red_buoy,
116
+ 244 => :green_buoy,
117
+ 242 => :force_field,
118
+ 252 => :red_and_green_buoy,
119
+ 250 => :red_buoy_and_force_field,
120
+ 246 => :green_buoy_and_force_field,
121
+ 254 => :red_and_green_buoy_and_force_field,
122
+ 162 => :virtual_wall
123
+ }
124
+
125
+ class InfraredCharacter
126
+ def self.convert(v)
127
+ INFRARED_CHARACTER[v]
128
+ end
129
+ end
130
+
131
+ SENSORS_PACKETS_SIZE = [
132
+ 0, # 0
133
+ 0,0,0,0,0,0, # 1-6
134
+ 1,1,1,1,1,1,1,1,1,1,1,1, # 7-18
135
+ 2,2, # 19-20
136
+ 1, # 21
137
+ 2,2, # 22-23
138
+ 1, # 24
139
+ 2,2,2,2,2,2,2, # 25-31
140
+ 1, # 32
141
+ 2, # 33
142
+ 1,1,1,1,1, # 34-38
143
+ 2,2,2,2,2,2, # 39-44
144
+ 1, # 45
145
+ 2,2,2,2,2,2, # 46-51
146
+ 1,1, # 52-53
147
+ 2,2,2,2, # 54-57
148
+ 1 # 58
149
+ ]
150
+
151
+ SENSORS_PACKETS_SIGNEDNESS = [
152
+ :na, # 0
153
+ :na,:na,:na,:na,:na,:na, # 1-6
154
+ :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 7-14
155
+ :signed,:signed,:unsigned,:unsigned, # 15-18
156
+ :signed,:signed, # 19-20
157
+ :unsigned, # 21
158
+ :unsigned,:signed, # 22-23
159
+ :signed, # 24
160
+ :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 25-31
161
+ :unsigned, # 32
162
+ :unsigned, # 33
163
+ :unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 34-38
164
+ :signed,:signed,:signed,:signed,:unsigned,:unsigned, # 39-44
165
+ :unsigned, # 45
166
+ :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 46-51
167
+ :unsigned,:unsigned, # 52-53
168
+ :signed,:signed,:signed,:signed, # 54-57
169
+ :unsigned # 58
170
+ ]
171
+
172
+ # Human readable packets name
173
+ SENSORS_PACKETS_SYMBOL = [
174
+ :ignore, # 0
175
+ :ignore,:ignore,:ignore,:ignore,:ignore,:ignore, # 1-6
176
+ :bumps_and_wheel_drops,:wall,:cliff_left,:cliff_front_left,:cliff_front_right,:cliff_right,:virtual_wall,:wheel_overcurrents, # 7-14
177
+ :dirt_detect,:ignore,:infrared_character_omni,:buttons,# 15-18
178
+ :distance,:angle, # 19-20
179
+ :charging_state, # 21
180
+ :voltage,:current, # 22-23
181
+ :temperature, # 24
182
+ :battery_charge,:battery_capacity,:wall_signal,:cliff_left_signal,:cliff_front_left_signal,:cliff_front_right_signal,:cliff_right_signal, # 25-31
183
+ :ignore, # 32
184
+ :ignore, # 33
185
+ :charging_sources_available,:oi_mode,:song_number,:song_playing,:number_of_stream_packets, # 34-38
186
+ :requested_velocity,:requested_radius,:requested_right_velocity,:requested_left_velocity,:right_encoder_count,:left_encoder_count, # 39-44
187
+ :light_bumper, # 45
188
+ :light_bump_left_signal,:light_bump_front_left_signal,:light_bump_center_left_signal,:light_bump_center_right_signal,:light_bump_front_right_signal,:light_bump_right_signal, # 46-51
189
+ :infrared_character_left,:infrared_character_right, # 52-53
190
+ :left_motor_current,:right_motor_current,:main_brush_motor_current,:side_brush_motor_current, # 54-57
191
+ :stasis # 58
192
+ ]
193
+
194
+ # Sensors mapper
195
+ SENSORS_PACKETS_VALUE = {
196
+ wall: Boolean,
197
+ cliff_left: Boolean,
198
+ cliff_front_left: Boolean,
199
+ cliff_front_right: Boolean,
200
+ cliff_right: Boolean,
201
+ virtual_wall: Boolean,
202
+ song_playing: Boolean,
203
+ stasis: Boolean,
204
+
205
+ charging_state: ChargingState,
206
+ oi_mode: OIMode,
207
+ charging_sources_available: ChargingSourceAvailable,
208
+ light_bumper: LightBumper,
209
+ wheel_overcurrents: WheelOvercurrents,
210
+ bumps_and_wheel_drops: BumpsAndWheelDrops,
211
+ infrared_character_omni: InfraredCharacter,
212
+ infrared_character_left: InfraredCharacter,
213
+ infrared_character_right: InfraredCharacter
214
+ }
215
+
216
+ # Sensors groups
217
+ SENSORS_GROUP_PACKETS = {
218
+ 0 => 7..26,
219
+ 1 => 7..16,
220
+ 2 => 17..20,
221
+ 3 => 21..26,
222
+ 4 => 27..34,
223
+ 5 => 35..42,
224
+ 6 => 7..42,
225
+ 100 => 7..58,
226
+ 101 => 43..58,
227
+ 106 => 40..51,
228
+ 107 => 54..58
229
+ }
230
+
231
+ # Convert sensors bytes to packets hash
232
+ def sensors_bytes_to_packets(bytes,packets)
233
+ packets_h = {}
234
+ pack = ""
235
+ packets.each do |packet|
236
+ size = SENSORS_PACKETS_SIZE[packet]
237
+ signedness = SENSORS_PACKETS_SIGNEDNESS[packet]
238
+ case size
239
+ when 1
240
+ case signedness
241
+ when :signed
242
+ pack += "c"
243
+ when :unsigned
244
+ pack += "C"
245
+ end
246
+ when 2
247
+ case signedness
248
+ when :signed
249
+ pack += "s>"
250
+ when :unsigned
251
+ pack += "S>"
252
+ end
253
+ end
254
+ end
255
+
256
+ nums = bytes.unpack(pack)
257
+
258
+ cur_packet = 0
259
+ packets.each do |packet|
260
+ pname = SENSORS_PACKETS_SYMBOL[packet]
261
+ unless pname == :ignore
262
+ value = nums[cur_packet]
263
+ conv = SENSORS_PACKETS_VALUE[pname]
264
+ if conv
265
+ value = conv.convert(value)
266
+ end
267
+ packets_h[pname] = value
268
+ end
269
+
270
+ cur_packet += 1
271
+ end
272
+
273
+ packets_h
274
+ end
275
+
276
+ # Get sensors by group
277
+ # Default group 100 = all packets
278
+ def get_sensors(group=100)
279
+ sensors_bytes_to_packets(write_chars_with_read([SENSORS,group]),SENSORS_GROUP_PACKETS[group])
280
+ end
281
+
282
+ # Get sensors by list
283
+ # Array entry can be packet ID or symbol
284
+ def get_sensors_list(list)
285
+ ids_list=(list.map do |l|
286
+ if l.class == Symbol
287
+ SENSORS_PACKETS_SYMBOL.find_index(l)
288
+ else
289
+ l
290
+ end
291
+ end)
292
+
293
+ sensors_bytes_to_packets(write_chars_with_read([QUERY_LIST,ids_list.length]+ids_list),ids_list)
294
+ end
295
+ end
296
+ end
data/lib/rumba.rb CHANGED
@@ -1,157 +1,16 @@
1
1
  require 'rubygems'
2
2
  require 'serialport'
3
3
  require 'timeout'
4
- require_relative 'sensors.rb'
4
+ require 'rumba/constants'
5
+ require 'rumba/sensors'
6
+ require 'rumba/dsl'
5
7
 
6
- class Roomba
7
- attr_accessor :serial
8
-
9
- # These opcodes require no arguments
10
- OPCODES = {
11
- start: 128,
12
- control: 130,
13
- power: 133,
14
- spot: 134,
15
- clean: 135,
16
- max: 136,
17
- dock: 143,
18
- play_script: 153,
19
- show_script: 154
20
- }
21
-
22
- # Create a method for each opcode that writes its data.
23
- # This allows us to simply call roomba.code,
24
- # and it's a cool excuse to do some metaprogramming :)
25
- OPCODES.each do |name,val|
26
- send :define_method, name do
27
- write_chars([val])
28
- end
29
- end
30
-
31
- SAFE_MODE = 131
32
- FULL_MODE = 132
33
-
34
- # These opcodes require arguments
35
- DRIVE = 137
36
- MOTORS = 138
37
- LEDS = 139
38
- SONG = 140
39
- PLAY_SONG = 141
40
- SENSORS = 142
41
- QUERY_LIST = 149
42
- DRIVE_DIRECT = 145
43
-
44
- # Used for making the Roomba sing!
45
- # Note that nil is simply a rest
46
- NOTES = {
47
- 'A' => 69, 'A#' => 70, 'B' => 71, 'C' => 72, 'C#' => 73, 'D' => 74,
48
- 'D#' => 75, 'E' => 76, 'F' => 77, 'F#' => 78, 'G' => 79, 'G#' => 80,
49
- nil => 0
50
- }
8
+ class Rumba
9
+ include Constants
10
+ include Sensor
11
+ include Dsl
51
12
 
52
- MOTORS_MASK_SIDE_BRUSH = 0x1
53
- MOTORS_MASK_VACUUM = 0x2
54
- MOTORS_MASK_MAIN_BRUSH = 0x4
55
-
56
- SENSORS_PACKETS_SIZE = [
57
- 0, # 0
58
- 0,0,0,0,0,0, # 1-6
59
- 1,1,1,1,1,1,1,1,1,1,1,1, # 7-18
60
- 2,2, # 19-20
61
- 1, # 21
62
- 2,2, # 22-23
63
- 1, # 24
64
- 2,2,2,2,2,2,2, # 25-31
65
- 1, # 32
66
- 2, # 33
67
- 1,1,1,1,1, # 34-38
68
- 2,2,2,2,2,2, # 39-44
69
- 1, # 45
70
- 2,2,2,2,2,2, # 46-51
71
- 1,1, # 52-53
72
- 2,2,2,2, # 54-57
73
- 1 # 58
74
- ]
75
-
76
- SENSORS_PACKETS_SIGNEDNESS = [
77
- :na, # 0
78
- :na,:na,:na,:na,:na,:na, # 1-6
79
- :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 7-14
80
- :signed,:signed,:unsigned,:unsigned, # 15-18
81
- :signed,:signed, # 19-20
82
- :unsigned, # 21
83
- :unsigned,:signed, # 22-23
84
- :signed, # 24
85
- :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 25-31
86
- :unsigned, # 32
87
- :unsigned, # 33
88
- :unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 34-38
89
- :signed,:signed,:signed,:signed,:unsigned,:unsigned, # 39-44
90
- :unsigned, # 45
91
- :unsigned,:unsigned,:unsigned,:unsigned,:unsigned,:unsigned, # 46-51
92
- :unsigned,:unsigned, # 52-53
93
- :signed,:signed,:signed,:signed, # 54-57
94
- :unsigned # 58
95
- ]
96
-
97
- # Human readable packets name
98
- SENSORS_PACKETS_SYMBOL = [
99
- :ignore, # 0
100
- :ignore,:ignore,:ignore,:ignore,:ignore,:ignore, # 1-6
101
- :bumps_and_wheel_drops,:wall,:cliff_left,:cliff_front_left,:cliff_front_right,:cliff_right,:virtual_wall,:wheel_overcurrents, # 7-14
102
- :dirt_detect,:ignore,:infrared_character_omni,:buttons,# 15-18
103
- :distance,:angle, # 19-20
104
- :charging_state, # 21
105
- :voltage,:current, # 22-23
106
- :temperature, # 24
107
- :battery_charge,:battery_capacity,:wall_signal,:cliff_left_signal,:cliff_front_left_signal,:cliff_front_right_signal,:cliff_right_signal, # 25-31
108
- :ignore, # 32
109
- :ignore, # 33
110
- :charging_sources_available,:oi_mode,:song_number,:song_playing,:number_of_stream_packets, # 34-38
111
- :requested_velocity,:requested_radius,:requested_right_velocity,:requested_left_velocity,:right_encoder_count,:left_encoder_count, # 39-44
112
- :light_bumper, # 45
113
- :light_bump_left_signal,:light_bump_front_left_signal,:light_bump_center_left_signal,:light_bump_center_right_signal,:light_bump_front_right_signal,:light_bump_right_signal, # 46-51
114
- :infrared_character_left,:infrared_character_right, # 52-53
115
- :left_motor_current,:right_motor_current,:main_brush_motor_current,:side_brush_motor_current, # 54-57
116
- :stasis # 58
117
- ]
118
-
119
- # Sensors mapper
120
- SENSORS_PACKETS_VALUE = {
121
- :wall=>RoombaSensor::Boolean,
122
- :cliff_left=>RoombaSensor::Boolean,
123
- :cliff_front_left=>RoombaSensor::Boolean,
124
- :cliff_front_right=>RoombaSensor::Boolean,
125
- :cliff_right=>RoombaSensor::Boolean,
126
- :virtual_wall=>RoombaSensor::Boolean,
127
- :song_playing=>RoombaSensor::Boolean,
128
- :stasis=>RoombaSensor::Boolean,
129
-
130
- :charging_state=>RoombaSensor::ChargingState,
131
- :oi_mode=>RoombaSensor::OIMode,
132
- :charging_sources_available=>RoombaSensor::ChargingSourceAvailable,
133
- :light_bumper=>RoombaSensor::LightBumper,
134
- :wheel_overcurrents=>RoombaSensor::WheelOvercurrents,
135
- :bumps_and_wheel_drops=>RoombaSensor::BumpsAndWheelDrops,
136
- :infrared_character_omni=>RoombaSensor::InfraredCharacter,
137
- :infrared_character_left=>RoombaSensor::InfraredCharacter,
138
- :infrared_character_right=>RoombaSensor::InfraredCharacter
139
- }
140
-
141
- # Sensors groups
142
- SENSORS_GROUP_PACKETS = {
143
- 0 => 7..26,
144
- 1 => 7..16,
145
- 2 => 17..20,
146
- 3 => 21..26,
147
- 4 => 27..34,
148
- 5 => 35..42,
149
- 6 => 7..42,
150
- 100 => 7..58,
151
- 101 => 43..58,
152
- 106 => 40..51,
153
- 107 => 54..58
154
- }
13
+ attr_accessor :serial
155
14
 
156
15
  #############################################################################
157
16
  # HELPERS #
@@ -194,56 +53,11 @@ class Roomba
194
53
  sleep(0.1)
195
54
  data=""
196
55
  while(data.length==0)
197
- data+=@serial.read
56
+ data += @serial.read
198
57
  end
199
58
  data
200
59
  end
201
60
 
202
- # Convert sensors bytes to packets hash
203
- def sensors_bytes_to_packets(bytes,packets)
204
- packets_h = {}
205
- pack = ""
206
- packets.each do |packet|
207
- size = SENSORS_PACKETS_SIZE[packet]
208
- signedness = SENSORS_PACKETS_SIGNEDNESS[packet]
209
- case size
210
- when 1
211
- case signedness
212
- when :signed
213
- pack += "c"
214
- when :unsigned
215
- pack += "C"
216
- end
217
- when 2
218
- case signedness
219
- when :signed
220
- pack += "s>"
221
- when :unsigned
222
- pack += "S>"
223
- end
224
- end
225
- end
226
-
227
- nums = bytes.unpack(pack)
228
-
229
- cur_packet = 0
230
- packets.each do |packet|
231
- pname = SENSORS_PACKETS_SYMBOL[packet]
232
- unless pname == :ignore
233
- value = nums[cur_packet]
234
- conv = SENSORS_PACKETS_VALUE[pname]
235
- if conv
236
- value = conv.convert(value)
237
- end
238
- packets_h[pname] = value
239
- end
240
-
241
- cur_packet+=1
242
- end
243
-
244
- packets_h
245
- end
246
-
247
61
  # Convert integer to two's complement signed 16 bit integer.
248
62
  # Note that the Roomba is big-endian...I need to fix this
249
63
  # code to make it portable across different architectures.
@@ -324,26 +138,6 @@ class Roomba
324
138
  write_chars([PLAY_SONG,song_number])
325
139
  end
326
140
 
327
- # Get sensors by group
328
- # Default group 100 = all packets
329
- def get_sensors(group=100)
330
- sensors_bytes_to_packets(write_chars_with_read([SENSORS,group]),SENSORS_GROUP_PACKETS[group])
331
- end
332
-
333
- # Get sensors by list
334
- # Array entry can be packet ID or symbol
335
- def get_sensors_list(list)
336
- ids_list=(list.map do |l|
337
- if l.class == Symbol
338
- Roomba::SENSORS_PACKETS_SYMBOL.find_index(l)
339
- else
340
- l
341
- end
342
- end)
343
-
344
- sensors_bytes_to_packets(write_chars_with_read([QUERY_LIST,ids_list.length]+ids_list),ids_list)
345
- end
346
-
347
141
  #############################################################################
348
142
  # Convenience methods #
349
143
  #############################################################################
@@ -376,7 +170,7 @@ class Roomba
376
170
  end
377
171
 
378
172
  def battery_percentage
379
- sensors=get_sensors(3)
173
+ sensors = get_sensors(3)
380
174
  ((sensors[:battery_charge].to_f / sensors[:battery_capacity].to_f) * 100).to_i
381
175
  end
382
176
 
@@ -400,7 +194,7 @@ class Roomba
400
194
  write_chars([MOTORS,MOTORS_MASK_MAIN_BRUSH])
401
195
  end
402
196
 
403
- def initialize(port, timeout=10)
197
+ def initialize(port, baud=57600, &block)
404
198
  @leds = {
405
199
  advance: false,
406
200
  play: false,
@@ -408,15 +202,22 @@ class Roomba
408
202
  intensity: 0
409
203
  }
410
204
 
411
- @timeout = timeout
412
- Timeout::timeout(@timeout) do
413
- # Initialize the serialport
414
- # 115200 for Roomba 5xx
415
- # 57600 for older models ?
416
- @serial = SerialPort.new(port, 115200)
417
- @serial.read_timeout = 15
418
- self.start
205
+ # Initialize the serialport
206
+ # 115200 for Roomba 5xx
207
+ # 57600 for older models (and iRobot Create)
208
+ @serial = SerialPort.new(port, baud)
209
+ @serial.read_timeout = 15
210
+ self.start
211
+
212
+ # initialize the "DSL" here!
213
+ if block_given?
214
+ instance_eval(&block)
215
+
216
+ # clean up after ourselves (this is a Roomba, after all!)
217
+ self.power_off
419
218
  end
420
219
  end
421
220
  end
422
221
 
222
+ # Alias for backwards-compatibility
223
+ Roomba = Rumba
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Wood
@@ -30,8 +30,11 @@ executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - README.md
33
34
  - lib/rumba.rb
34
- - lib/sensors.rb
35
+ - lib/rumba/constants.rb
36
+ - lib/rumba/dsl.rb
37
+ - lib/rumba/sensors.rb
35
38
  homepage: http://github.com/eric-wood/roomba
36
39
  licenses:
37
40
  - BSD
data/lib/sensors.rb DELETED
@@ -1,128 +0,0 @@
1
- module RoombaSensor
2
- class Boolean
3
- def self.convert(v)
4
- v == 1 ? true : false
5
- end
6
- end
7
-
8
- class ChargingState
9
- def self.convert(v)
10
- case v
11
- when 0
12
- :not_charging
13
- when 1
14
- :reconditioning_charging
15
- when 2
16
- :full_charging
17
- when 3
18
- :trickle_charging
19
- when 4
20
- :waiting
21
- when 5
22
- :charging_fault_condition
23
- end
24
- end
25
- end
26
-
27
- class OIMode
28
- def self.convert(v)
29
- case v
30
- when 0
31
- :off
32
- when 1
33
- :passive
34
- when 2
35
- :safe
36
- when 3
37
- :full
38
- end
39
- end
40
- end
41
-
42
- class ChargingSourceAvailable
43
- def self.convert(v)
44
- h = {}
45
- h[:internal_charger] = v & 0b1 > 0 ? true : false
46
- h[:home_base] = v & 0b10 > 0 ? true : false
47
- h
48
- end
49
- end
50
-
51
- class LightBumper
52
- def self.convert(v)
53
- h = {}
54
- h[:light_bumper_left] = v & 0b1 > 0 ? true : false
55
- h[:light_bumper_front_left] = v & 0b10 > 0 ? true : false
56
- h[:light_bumper_center_left] = v & 0b100 > 0 ? true : false
57
- h[:light_bumper_center_right] = v & 0b1000 > 0 ? true : false
58
- h[:light_bumper_front_right] = v & 0b10000 > 0 ? true : false
59
- h[:light_bumper_right] = v & 0b100000 > 0 ? true : false
60
- h
61
- end
62
- end
63
-
64
- class WheelOvercurrents
65
- def self.convert(v)
66
- h = {}
67
- h[:side_brush] = v & 0b1 > 0 ? true : false
68
- h[:main_brush] = v & 0b100 > 0 ? true : false
69
- h[:right_wheel] = v & 0b1000 > 0 ? true : false
70
- h[:left_wheel] = v & 0b10000 > 0 ? true : false
71
- h
72
- end
73
- end
74
-
75
- class BumpsAndWheelDrops
76
- def self.convert(v)
77
- h = {}
78
- h[:bump_right] = v & 0b1 > 0 ? true : false
79
- h[:bump_left] = v & 0b10 > 0 ? true : false
80
- h[:wheel_drop_right] = v & 0b100 > 0 ? true : false
81
- h[:wheel_drop_left] = v & 0b1000 > 0 ? true : false
82
- h
83
- end
84
-
85
- end
86
-
87
- INFRARED_CHARACTER = {
88
- 129 => :left,
89
- 130 => :forward,
90
- 131 => :right,
91
- 132 => :spot,
92
- 133 => :max,
93
- 134 => :small,
94
- 135 => :medium,
95
- 136 => :large,
96
- 137 => :stop,
97
- 138 => :power,
98
- 139 => :arc_left,
99
- 140 => :arc_right,
100
- 141 => :stop,
101
- 142 => :download,
102
- 143 => :seek_dock,
103
- 160 => :reserved,
104
- 161 => :force_field,
105
- 164 => :green_buoy,
106
- 165 => :green_buoy_and_force_field,
107
- 168 => :red_buoy,
108
- 169 => :red_buoy_and_force_field,
109
- 172 => :red_and_green_buoy,
110
- 173 => :red_and_green_buoy_and_force_field,
111
- 240 => :reserved,
112
- 248 => :red_buoy,
113
- 244 => :green_buoy,
114
- 242 => :force_field,
115
- 252 => :red_and_green_buoy,
116
- 250 => :red_buoy_and_force_field,
117
- 246 => :green_buoy_and_force_field,
118
- 254 => :red_and_green_buoy_and_force_field,
119
- 162 => :virtual_wall
120
- }
121
-
122
- class InfraredCharacter
123
- def self.convert(v)
124
- INFRARED_CHARACTER[v]
125
- end
126
- end
127
- end
128
-