rumba 0.1.0 → 0.2.0

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.
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