rumba 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rumba.rb +240 -11
  3. data/lib/sensors.rb +128 -0
  4. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 115b8d8b186d7f86152bbff988ac8b0fae8fcc39
4
- data.tar.gz: ba6345f5aaaf814e2c4a10aa55b2493c26cd7e4d
3
+ metadata.gz: b1d712a0e9b5806c625ea88f90a7ac66d326cde2
4
+ data.tar.gz: 8119e32a631feffedfdced0645e5cfbf3aefe958
5
5
  SHA512:
6
- metadata.gz: f71cc909ed9079bb10054cdd86b972e9fa1daa5d7561fdb4d97796829bdd3aefbed720eedd63140be9388c2bed6957ceb0cdce642a4aa5146dae77724d3965ce
7
- data.tar.gz: 9eb5465a3e7dbaca2e72b782a07955420edf819ceaab878090867cbc754924e9dcf7238a84517c4730b94f8be4aaf57ae9741da2dfdc743c948c2a15f10361b7
6
+ metadata.gz: e3cb42c3be2c4d84dbfa611840eb8351b628607d486d46ccd216ee53863e1b128d8c0cf4991cfc37dbeed6b72f7fa9808fd307571fd78c3c35ae669c3de4dc44
7
+ data.tar.gz: 1db707dc89538cc544c587e2efbd00a9959d5df7a3e01405defc576a36afe96b5bdbaf66f30a6dd007953de68cf3ff4239fe2142f40c077681343003611cfd92
data/lib/rumba.rb CHANGED
@@ -1,15 +1,22 @@
1
1
  require 'rubygems'
2
2
  require 'serialport'
3
3
  require 'timeout'
4
+ require_relative 'sensors.rb'
4
5
 
5
6
  class Roomba
7
+ attr_accessor :serial
8
+
6
9
  # These opcodes require no arguments
7
10
  OPCODES = {
8
- start: 128,
9
- control: 130,
10
- dock: 143,
11
- play_script: 153,
12
- show_script: 154
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
13
20
  }
14
21
 
15
22
  # Create a method for each opcode that writes its data.
@@ -26,9 +33,12 @@ class Roomba
26
33
 
27
34
  # These opcodes require arguments
28
35
  DRIVE = 137
36
+ MOTORS = 138
29
37
  LEDS = 139
30
38
  SONG = 140
31
39
  PLAY_SONG = 141
40
+ SENSORS = 142
41
+ QUERY_LIST = 149
32
42
  DRIVE_DIRECT = 145
33
43
 
34
44
  # Used for making the Roomba sing!
@@ -38,14 +48,118 @@ class Roomba
38
48
  'D#' => 75, 'E' => 76, 'F' => 77, 'F#' => 78, 'G' => 79, 'G#' => 80,
39
49
  nil => 0
40
50
  }
41
-
51
+
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
+ }
155
+
42
156
  #############################################################################
43
157
  # HELPERS #
44
158
  #############################################################################
45
159
 
46
160
  # Converts input data (an array) into bytes before
47
161
  # sending it over the serial connection.
48
- def write_chars(data);
162
+ def write_chars(data)
49
163
  data.map! do |c|
50
164
  if c.class == String
51
165
  result = c.bytes.to_a.map { |b| [b].pack("C") }
@@ -61,7 +175,75 @@ class Roomba
61
175
  @serial.write(data)
62
176
  @serial.flush
63
177
  end
64
-
178
+
179
+ # Write data then read response
180
+ def write_chars_with_read(data)
181
+ data.map! do |c|
182
+ if c.class == String
183
+ result = c.bytes.to_a.map { |b| [b].pack("C") }
184
+ else
185
+ result = [c].pack("C")
186
+ end
187
+
188
+ result
189
+ end
190
+
191
+ data = data.flatten.join
192
+
193
+ @serial.write(data)
194
+ sleep(0.1)
195
+ data=""
196
+ while(data.length==0)
197
+ data+=@serial.read
198
+ end
199
+ data
200
+ end
201
+
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
+
65
247
  # Convert integer to two's complement signed 16 bit integer.
66
248
  # Note that the Roomba is big-endian...I need to fix this
67
249
  # code to make it portable across different architectures.
@@ -141,7 +323,27 @@ class Roomba
141
323
  raise RangeError if song_number < 0 || song_number > 15
142
324
  write_chars([PLAY_SONG,song_number])
143
325
  end
144
-
326
+
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
+
145
347
  #############################################################################
146
348
  # Convenience methods #
147
349
  #############################################################################
@@ -173,6 +375,31 @@ class Roomba
173
375
  @serial.close
174
376
  end
175
377
 
378
+ def battery_percentage
379
+ sensors=get_sensors(3)
380
+ ((sensors[:battery_charge].to_f / sensors[:battery_capacity].to_f) * 100).to_i
381
+ end
382
+
383
+ def stop_all_motors
384
+ write_chars([MOTORS,0])
385
+ end
386
+
387
+ def start_all_motors
388
+ write_chars([MOTORS,MOTORS_MASK_SIDE_BRUSH|MOTORS_MASK_VACUUM|MOTORS_MASK_MAIN_BRUSH])
389
+ end
390
+
391
+ def start_side_brush_motor
392
+ write_chars([MOTORS,MOTORS_MASK_SIDE_BRUSH])
393
+ end
394
+
395
+ def start_vacumm_motor
396
+ write_chars([MOTORS,MOTORS_MASK_VACUUM])
397
+ end
398
+
399
+ def start_main_brush_motor
400
+ write_chars([MOTORS,MOTORS_MASK_MAIN_BRUSH])
401
+ end
402
+
176
403
  def initialize(port, timeout=10)
177
404
  @leds = {
178
405
  advance: false,
@@ -184,8 +411,10 @@ class Roomba
184
411
  @timeout = timeout
185
412
  Timeout::timeout(@timeout) do
186
413
  # Initialize the serialport
187
- @serial = SerialPort.new(port, 57600)
188
- @serial.read_timeout = 1000
414
+ # 115200 for Roomba 5xx
415
+ # 57600 for older models ?
416
+ @serial = SerialPort.new(port, 115200)
417
+ @serial.read_timeout = 15
189
418
  self.start
190
419
  end
191
420
  end
data/lib/sensors.rb ADDED
@@ -0,0 +1,128 @@
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
+
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.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Wood
@@ -31,6 +31,7 @@ extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
33
  - lib/rumba.rb
34
+ - lib/sensors.rb
34
35
  homepage: http://github.com/eric-wood/roomba
35
36
  licenses:
36
37
  - BSD