sphero 1.0.0 → 1.4.1
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.
- data/CHANGELOG.rdoc +33 -0
- data/Manifest.txt +1 -0
- data/README.markdown +66 -2
- data/Rakefile +3 -3
- data/lib/sphero.rb +307 -33
- data/lib/sphero/request.rb +96 -86
- data/lib/sphero/response.rb +118 -3
- data/test/test_sphero.rb +97 -6
- data/test/test_sphero_request.rb +15 -0
- metadata +13 -43
data/CHANGELOG.rdoc
CHANGED
@@ -4,3 +4,36 @@
|
|
4
4
|
|
5
5
|
* Birthday!
|
6
6
|
|
7
|
+
=== 1.0.1 / 2012-10-28
|
8
|
+
|
9
|
+
* 3 minor fixes/enhancements
|
10
|
+
|
11
|
+
* Works with Ruby 1.9.2 for us regular folks
|
12
|
+
* Sphero.start method to endlessly retry when connecting
|
13
|
+
* I think heading might work again, not too sure
|
14
|
+
|
15
|
+
=== 1.1.0 / 2013-01-04
|
16
|
+
|
17
|
+
* 3 enhancements
|
18
|
+
|
19
|
+
* Pass a block to Sphero.start for DSL-awesomeness
|
20
|
+
* Use Sphero#keep_going as a nicer alias for Ruby's sleep command
|
21
|
+
* Add some additional test coverage
|
22
|
+
|
23
|
+
=== 1.1.2 / 2013-01-07
|
24
|
+
|
25
|
+
* 1 bugfix
|
26
|
+
|
27
|
+
* Correct Sphero#keep_going to call Kernel sleep method correctly
|
28
|
+
|
29
|
+
=== 1.1.3 / 2013-01-07
|
30
|
+
|
31
|
+
* 1 enhancement/bugfix depending on how you look at it
|
32
|
+
|
33
|
+
* Change Sphero#sleep to Sphero#sphero_sleep to distingish naive call to Kernel sleep
|
34
|
+
|
35
|
+
=== 1.1.4 / 2013-01-10
|
36
|
+
|
37
|
+
* 1 enhancement
|
38
|
+
|
39
|
+
* Add constants FORWARD, BACKWARD, RIGHT, & LEFT for quick directionality
|
data/Manifest.txt
CHANGED
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# sphero
|
2
2
|
|
3
|
-
* http://github.com/
|
3
|
+
* http://github.com/hybridgroup/sphero
|
4
4
|
|
5
5
|
## DESCRIPTION:
|
6
6
|
|
@@ -13,6 +13,51 @@ provided by the bluetooth connection.
|
|
13
13
|
|
14
14
|
## SYNOPSIS:
|
15
15
|
|
16
|
+
You can easily start your Sphero and send it commands like this:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
Sphero.start '/dev/tty.Sphero-YBW-RN-SPP' do
|
20
|
+
roll 60, FORWARD
|
21
|
+
keep_going 3
|
22
|
+
|
23
|
+
roll 60, RIGHT
|
24
|
+
keep_going 3
|
25
|
+
|
26
|
+
roll 60, BACKWARD
|
27
|
+
keep_going 3
|
28
|
+
|
29
|
+
roll 60, LEFT
|
30
|
+
keep_going 3
|
31
|
+
|
32
|
+
stop
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
Here is another example:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Sphero.start "/dev/tty.Sphero-PRG-RN-SPP" do
|
40
|
+
ping
|
41
|
+
|
42
|
+
# Roll 0 degrees, speed 125
|
43
|
+
roll(125, 0)
|
44
|
+
|
45
|
+
# Turn 360 degrees, 30 degrees at a time
|
46
|
+
0.step(360, 30) { |h|
|
47
|
+
h = 0 if h == 360
|
48
|
+
|
49
|
+
# Set the heading to h degrees
|
50
|
+
heading = h
|
51
|
+
keep_going 1
|
52
|
+
}
|
53
|
+
|
54
|
+
keep_going 1
|
55
|
+
stop
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Here is a another way to do the same thing as the previos example, via just normal method calls instead of the DSL:
|
60
|
+
|
16
61
|
```ruby
|
17
62
|
s = Sphero.new "/dev/tty.Sphero-PRG-RN-SPP"
|
18
63
|
s.ping
|
@@ -32,19 +77,38 @@ sleep 1
|
|
32
77
|
s.stop
|
33
78
|
```
|
34
79
|
|
80
|
+
## Pairing sphero with ubuntu
|
81
|
+
Add your user to the `dialout` group
|
82
|
+
```
|
83
|
+
$ sudo usermod -a -G dialout <user>
|
84
|
+
```
|
85
|
+
Then logout or restart your computer. Once your user is logged back in, pair the sphero with the ubuntu bluetooth manager.
|
86
|
+
|
87
|
+
Once paired, you may now bind your sphero to a rfcomm port
|
88
|
+
```
|
89
|
+
$ sudo hcitool scan
|
90
|
+
Scanning ...
|
91
|
+
<address> Sphero
|
92
|
+
$ sudo rfcomm bind /dev/rfcomm0 <address> 1
|
93
|
+
```
|
94
|
+
|
95
|
+
You may now access the sphero from `/dev/rfcomm0`
|
96
|
+
|
35
97
|
## REQUIREMENTS:
|
36
98
|
|
37
99
|
* A Sphero ball connected to your computer
|
100
|
+
* Supports MRI 1.9.2/1.9.3 and Rubinius 2.0rc1 for sure...
|
38
101
|
|
39
102
|
## INSTALL:
|
40
103
|
|
41
|
-
* gem install sphero
|
104
|
+
* gem install hybridgroup-sphero
|
42
105
|
|
43
106
|
## LICENSE:
|
44
107
|
|
45
108
|
(The MIT License)
|
46
109
|
|
47
110
|
Copyright (c) 2012 Aaron Patterson
|
111
|
+
Copyright (c) 2012-2013 The Hybrid Group
|
48
112
|
|
49
113
|
Permission is hereby granted, free of charge, to any person obtaining
|
50
114
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -4,19 +4,19 @@ require 'rubygems'
|
|
4
4
|
require 'hoe'
|
5
5
|
|
6
6
|
Hoe.plugins.delete :rubyforge
|
7
|
+
Hoe.plugins.delete :flog
|
7
8
|
Hoe.plugin :minitest
|
8
9
|
Hoe.plugin :gemspec # `gem install hoe-gemspec`
|
9
10
|
Hoe.plugin :git # `gem install hoe-git`
|
10
11
|
|
11
12
|
Hoe.spec 'sphero' do
|
12
|
-
developer('
|
13
|
+
developer('Hybrid Group', 'sphero@hybridgroup.com')
|
13
14
|
self.readme_file = 'README.markdown'
|
14
15
|
self.history_file = 'CHANGELOG.rdoc'
|
15
16
|
self.extra_rdoc_files = FileList['*.{rdoc,markdown}']
|
16
|
-
self.extra_deps << ['serialport']
|
17
17
|
|
18
18
|
self.spec_extras = {
|
19
|
-
:required_ruby_version => '>= 1.9.
|
19
|
+
:required_ruby_version => '>= 1.9.2'
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
data/lib/sphero.rb
CHANGED
@@ -1,16 +1,62 @@
|
|
1
|
-
require 'serialport'
|
2
1
|
require 'sphero/request'
|
3
2
|
require 'sphero/response'
|
4
3
|
require 'thread'
|
5
4
|
|
6
5
|
class Sphero
|
7
|
-
VERSION = '1.
|
6
|
+
VERSION = '1.4.1'
|
7
|
+
|
8
|
+
FORWARD = 0
|
9
|
+
RIGHT = 90
|
10
|
+
BACKWARD = 180
|
11
|
+
LEFT = 270
|
12
|
+
|
13
|
+
DEFAULT_RETRIES = 3
|
14
|
+
|
15
|
+
attr_accessor :connection_types, :messages
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def start(dev, &block)
|
19
|
+
retries_left = DEFAULT_RETRIES
|
20
|
+
begin
|
21
|
+
sphero = self.new dev
|
22
|
+
if (block_given?)
|
23
|
+
begin
|
24
|
+
sphero.instance_eval(&block)
|
25
|
+
ensure
|
26
|
+
sphero.close
|
27
|
+
end
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
return sphero
|
31
|
+
rescue Errno::EBUSY
|
32
|
+
puts retries_left
|
33
|
+
retries_left = retries_left - 1
|
34
|
+
retry unless retries_left < 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
8
38
|
|
9
39
|
def initialize dev
|
10
|
-
|
40
|
+
if dev.is_a?(String)
|
41
|
+
initialize_serialport dev
|
42
|
+
else
|
43
|
+
@sp = dev
|
44
|
+
end
|
45
|
+
|
11
46
|
@dev = 0x00
|
12
47
|
@seq = 0x00
|
13
48
|
@lock = Mutex.new
|
49
|
+
@messages = Queue.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
begin
|
54
|
+
stop
|
55
|
+
rescue Exception => e
|
56
|
+
puts e.message
|
57
|
+
ensure
|
58
|
+
@sp.close
|
59
|
+
end
|
14
60
|
end
|
15
61
|
|
16
62
|
def ping
|
@@ -41,7 +87,7 @@ class Sphero
|
|
41
87
|
write Request::GetPowerState.new(@seq)
|
42
88
|
end
|
43
89
|
|
44
|
-
def
|
90
|
+
def sphero_sleep wakeup = 0, macro = 0
|
45
91
|
write Request::Sleep.new(@seq, wakeup, macro)
|
46
92
|
end
|
47
93
|
|
@@ -57,6 +103,11 @@ class Sphero
|
|
57
103
|
write Request::Heading.new(@seq, h)
|
58
104
|
end
|
59
105
|
|
106
|
+
def color colorname, persistant = false
|
107
|
+
color = COLORS[colorname]
|
108
|
+
rgb color[:r], color[:g], color[:b], persistant
|
109
|
+
end
|
110
|
+
|
60
111
|
def rgb r, g, b, persistant = false
|
61
112
|
write Request::SetRGB.new(@seq, r, g, b, persistant ? 0x01 : 0x00)
|
62
113
|
end
|
@@ -77,18 +128,76 @@ class Sphero
|
|
77
128
|
write Request::SetRotationRate.new(@seq, h)
|
78
129
|
end
|
79
130
|
|
131
|
+
# just a nicer alias for Ruby's own sleep
|
132
|
+
def keep_going(duration)
|
133
|
+
Kernel::sleep duration
|
134
|
+
end
|
135
|
+
|
136
|
+
## async messages
|
137
|
+
|
138
|
+
# configure power notification messages
|
139
|
+
def set_power_notification enable=true
|
140
|
+
write Request::SetPowerNotification.new(@seq, enable ? 0x01 : 0x00)
|
141
|
+
end
|
142
|
+
|
143
|
+
# configure data streaming notification messages
|
144
|
+
def set_data_streaming n, m, mask, pcnt, mask2
|
145
|
+
write Request::SetDataStreaming.new(@seq, n, m, mask, pcnt, mask2)
|
146
|
+
end
|
147
|
+
|
148
|
+
# configure collision detection messages
|
149
|
+
def configure_collision_detection meth, x_t, y_t, x_spd, y_spd, dead
|
150
|
+
write Request::ConfigureCollisionDetection.new(@seq, meth, x_t, y_t, x_spd, y_spd, dead)
|
151
|
+
end
|
152
|
+
|
80
153
|
private
|
154
|
+
|
155
|
+
def is_windows?
|
156
|
+
os = RUBY_PLATFORM.split("-")[1]
|
157
|
+
if (os == 'mswin' or os == 'bccwin' or os == 'mingw' or os == 'mingw32')
|
158
|
+
true
|
159
|
+
else
|
160
|
+
false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def initialize_serialport dev
|
165
|
+
require 'serialport'
|
166
|
+
@sp = SerialPort.new dev, 115200, 8, 1, SerialPort::NONE
|
167
|
+
if is_windows?
|
168
|
+
@sp.read_timeout=1000
|
169
|
+
@sp.write_timeout=0
|
170
|
+
@sp.initial_byte_offset=5
|
171
|
+
end
|
172
|
+
rescue LoadError
|
173
|
+
puts "Please 'gem install hybridgroup-serialport' for serial port support."
|
174
|
+
end
|
81
175
|
|
82
176
|
def write packet
|
83
|
-
header = nil
|
84
|
-
|
177
|
+
header, body = nil
|
178
|
+
|
179
|
+
IO.select([], [@sp], [], 20)
|
85
180
|
|
86
181
|
@lock.synchronize do
|
87
182
|
@sp.write packet.to_str
|
88
183
|
@seq += 1
|
184
|
+
end
|
185
|
+
|
186
|
+
IO.select([@sp], [], [], 20)
|
187
|
+
header = read_header(true)
|
188
|
+
body = read_body(header.last, true) if header
|
89
189
|
|
90
|
-
|
91
|
-
|
190
|
+
# pick off asynch packets and store, till we get to the message response
|
191
|
+
while header && Response.async?(header)
|
192
|
+
messages << Response::AsyncResponse.response(header, body)
|
193
|
+
|
194
|
+
IO.select([@sp], [], [], 20)
|
195
|
+
header = read_header(true)
|
196
|
+
if header
|
197
|
+
body = read_body(header.last, true)
|
198
|
+
else
|
199
|
+
body = nil
|
200
|
+
end
|
92
201
|
end
|
93
202
|
|
94
203
|
response = packet.response header, body
|
@@ -96,38 +205,203 @@ class Sphero
|
|
96
205
|
if response.success?
|
97
206
|
response
|
98
207
|
else
|
99
|
-
raise
|
208
|
+
raise "Unable to write to Sphero!"
|
100
209
|
end
|
101
210
|
end
|
102
|
-
end
|
103
211
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
212
|
+
def read_header(blocking=false)
|
213
|
+
header = nil
|
214
|
+
begin
|
215
|
+
data = read_next_chunk(5, blocking)
|
216
|
+
return nil unless data
|
217
|
+
header = data.unpack 'C5'
|
218
|
+
rescue Errno::EBUSY
|
219
|
+
retry
|
220
|
+
rescue Exception => e
|
221
|
+
puts e.message
|
222
|
+
puts e.backtrace.inspect
|
223
|
+
retry
|
224
|
+
end
|
110
225
|
|
111
|
-
|
112
|
-
|
113
|
-
}
|
226
|
+
header
|
227
|
+
end
|
114
228
|
|
115
|
-
|
116
|
-
|
117
|
-
|
229
|
+
def read_body(len, blocking=false)
|
230
|
+
data = nil
|
231
|
+
begin
|
232
|
+
data = read_next_chunk(len, blocking)
|
233
|
+
return nil unless data && data.length == len
|
234
|
+
rescue Errno::EBUSY
|
235
|
+
retry
|
236
|
+
rescue Exception => e
|
237
|
+
puts e.message
|
238
|
+
puts e.backtrace.inspect
|
239
|
+
retry
|
240
|
+
end
|
118
241
|
|
119
|
-
|
242
|
+
data
|
243
|
+
end
|
120
244
|
|
121
|
-
|
122
|
-
|
123
|
-
|
245
|
+
def read_next_chunk(len, blocking=false)
|
246
|
+
data = nil
|
247
|
+
begin
|
248
|
+
@lock.synchronize do
|
249
|
+
if blocking || is_windows?
|
250
|
+
data = @sp.read(len)
|
251
|
+
else
|
252
|
+
data = @sp.read_nonblock(len)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
rescue Errno::EBUSY
|
256
|
+
retry
|
257
|
+
rescue Exception => e
|
258
|
+
puts e.message
|
259
|
+
puts e.backtrace.inspect
|
260
|
+
return nil
|
124
261
|
end
|
125
|
-
|
262
|
+
data
|
263
|
+
end
|
126
264
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
265
|
+
COLORS = {
|
266
|
+
'aliceblue' => {:r => 240, :g => 248, :b => 255},
|
267
|
+
'antiquewhite' => {:r => 250, :g => 235, :b => 215},
|
268
|
+
'aqua' => {:r => 0, :g => 255, :b => 255},
|
269
|
+
'aquamarine' => {:r => 127, :g => 255, :b => 212},
|
270
|
+
'azure' => {:r => 240, :g => 255, :b => 255},
|
271
|
+
'beige' => {:r => 245, :g => 245, :b => 220},
|
272
|
+
'bisque' => {:r => 255, :g => 228, :b => 196},
|
273
|
+
'black' => {:r => 0, :g => 0, :b => 0},
|
274
|
+
'blanchedalmond' => {:r => 255, :g => 235, :b => 205},
|
275
|
+
'blue' => {:r => 0, :g => 0, :b => 255},
|
276
|
+
'blueviolet' => {:r => 138, :g => 43, :b => 226},
|
277
|
+
'brown' => {:r => 165, :g => 42, :b => 42},
|
278
|
+
'burlywood' => {:r => 222, :g => 184, :b => 135},
|
279
|
+
'cadetblue' => {:r => 95, :g => 158, :b => 160},
|
280
|
+
'chartreuse' => {:r => 127, :g => 255, :b => 0},
|
281
|
+
'chocolate' => {:r => 210, :g => 105, :b => 30},
|
282
|
+
'coral' => {:r => 255, :g => 127, :b => 80},
|
283
|
+
'cornflowerblue' => {:r => 100, :g => 149, :b => 237},
|
284
|
+
'cornsilk' => {:r => 255, :g => 248, :b => 220},
|
285
|
+
'crimson' => {:r => 220, :g => 20, :b => 60},
|
286
|
+
'cyan' => {:r => 0, :g => 255, :b => 255},
|
287
|
+
'darkblue' => {:r => 0, :g => 0, :b => 139},
|
288
|
+
'darkcyan' => {:r => 0, :g => 139, :b => 139},
|
289
|
+
'darkgoldenrod' => {:r => 184, :g => 134, :b => 11},
|
290
|
+
'darkgray' => {:r => 169, :g => 169, :b => 169},
|
291
|
+
'darkgreen' => {:r => 0, :g => 100, :b => 0},
|
292
|
+
'darkkhaki' => {:r => 189, :g => 183, :b => 107},
|
293
|
+
'darkmagenta' => {:r => 139, :g => 0, :b => 139},
|
294
|
+
'darkolivegreen' => {:r => 85, :g => 107, :b => 47},
|
295
|
+
'darkorange' => {:r => 255, :g => 140, :b => 0},
|
296
|
+
'darkorchid' => {:r => 153, :g => 50, :b => 204},
|
297
|
+
'darkred' => {:r => 139, :g => 0, :b => 0},
|
298
|
+
'darksalmon' => {:r => 233, :g => 150, :b => 122},
|
299
|
+
'darkseagreen' => {:r => 143, :g => 188, :b => 143},
|
300
|
+
'darkslateblue' => {:r => 72, :g => 61, :b => 139},
|
301
|
+
'darkslategray' => {:r => 47, :g => 79, :b => 79},
|
302
|
+
'darkturquoise' => {:r => 0, :g => 206, :b => 209},
|
303
|
+
'darkviolet' => {:r => 148, :g => 0, :b => 211},
|
304
|
+
'deeppink' => {:r => 255, :g => 20, :b => 147},
|
305
|
+
'deepskyblue' => {:r => 0, :g => 191, :b => 255},
|
306
|
+
'dimgray' => {:r => 105, :g => 105, :b => 105},
|
307
|
+
'dodgerblue' => {:r => 30, :g => 144, :b => 255},
|
308
|
+
'firebrick' => {:r => 178, :g => 34, :b => 34},
|
309
|
+
'floralwhite' => {:r => 255, :g => 250, :b => 240},
|
310
|
+
'forestgreen' => {:r => 34, :g => 139, :b => 34},
|
311
|
+
'fuchsia' => {:r => 255, :g => 0, :b => 255},
|
312
|
+
'gainsboro' => {:r => 220, :g => 220, :b => 220},
|
313
|
+
'ghostwhite' => {:r => 248, :g => 248, :b => 255},
|
314
|
+
'gold' => {:r => 255, :g => 215, :b => 0},
|
315
|
+
'goldenrod' => {:r => 218, :g => 165, :b => 32},
|
316
|
+
'gray' => {:r => 128, :g => 128, :b => 128},
|
317
|
+
'green' => {:r => 0, :g => 128, :b => 0},
|
318
|
+
'greenyellow' => {:r => 173, :g => 255, :b => 47},
|
319
|
+
'honeydew' => {:r => 240, :g => 255, :b => 240},
|
320
|
+
'hotpink' => {:r => 255, :g => 105, :b => 180},
|
321
|
+
'indianred' => {:r => 205, :g => 92, :b => 92},
|
322
|
+
'indigo' => {:r => 75, :g => 0, :b => 130},
|
323
|
+
'ivory' => {:r => 255, :g => 255, :b => 240},
|
324
|
+
'khaki' => {:r => 240, :g => 230, :b => 140},
|
325
|
+
'lavender' => {:r => 230, :g => 230, :b => 250},
|
326
|
+
'lavenderblush' => {:r => 255, :g => 240, :b => 245},
|
327
|
+
'lawngreen' => {:r => 124, :g => 252, :b => 0},
|
328
|
+
'lemonchiffon' => {:r => 255, :g => 250, :b => 205},
|
329
|
+
'lightblue' => {:r => 173, :g => 216, :b => 230},
|
330
|
+
'lightcoral' => {:r => 240, :g => 128, :b => 128},
|
331
|
+
'lightcyan' => {:r => 224, :g => 255, :b => 255},
|
332
|
+
'lightgoldenrodyellow' => {:r => 250, :g => 250, :b => 210},
|
333
|
+
'lightgreen' => {:r => 144, :g => 238, :b => 144},
|
334
|
+
'lightgrey' => {:r => 211, :g => 211, :b => 211},
|
335
|
+
'lightpink' => {:r => 255, :g => 182, :b => 193},
|
336
|
+
'lightsalmon' => {:r => 255, :g => 160, :b => 122},
|
337
|
+
'lightseagreen' => {:r => 32, :g => 178, :b => 170},
|
338
|
+
'lightskyblue' => {:r => 135, :g => 206, :b => 250},
|
339
|
+
'lightslategray' => {:r => 119, :g => 136, :b => 153},
|
340
|
+
'lightsteelblue' => {:r => 176, :g => 196, :b => 222},
|
341
|
+
'lightyellow' => {:r => 255, :g => 255, :b => 224},
|
342
|
+
'lime' => {:r => 0, :g => 255, :b => 0},
|
343
|
+
'limegreen' => {:r => 50, :g => 205, :b => 50},
|
344
|
+
'linen' => {:r => 250, :g => 240, :b => 230},
|
345
|
+
'magenta' => {:r => 255, :g => 0, :b => 255},
|
346
|
+
'maroon' => {:r => 128, :g => 0, :b => 0},
|
347
|
+
'mediumaquamarine' => {:r => 102, :g => 205, :b => 170},
|
348
|
+
'mediumblue' => {:r => 0, :g => 0, :b => 205},
|
349
|
+
'mediumorchid' => {:r => 186, :g => 85, :b => 211},
|
350
|
+
'mediumpurple' => {:r => 147, :g => 112, :b => 219},
|
351
|
+
'mediumseagreen' => {:r => 60, :g => 179, :b => 113},
|
352
|
+
'mediumslateblue' => {:r => 123, :g => 104, :b => 238},
|
353
|
+
'mediumspringgreen' => {:r => 0, :g => 250, :b => 154},
|
354
|
+
'mediumturquoise' => {:r => 72, :g => 209, :b => 204},
|
355
|
+
'mediumvioletred' => {:r => 199, :g => 21, :b => 133},
|
356
|
+
'midnightblue' => {:r => 25, :g => 25, :b => 112},
|
357
|
+
'mintcream' => {:r => 245, :g => 255, :b => 250},
|
358
|
+
'mistyrose' => {:r => 255, :g => 228, :b => 225},
|
359
|
+
'moccasin' => {:r => 255, :g => 228, :b => 181},
|
360
|
+
'navajowhite' => {:r => 255, :g => 222, :b => 173},
|
361
|
+
'navy' => {:r => 0, :g => 0, :b => 128},
|
362
|
+
'oldlace' => {:r => 253, :g => 245, :b => 230},
|
363
|
+
'olive' => {:r => 128, :g => 128, :b => 0},
|
364
|
+
'olivedrab' => {:r => 107, :g => 142, :b => 35},
|
365
|
+
'orange' => {:r => 255, :g => 165, :b => 0},
|
366
|
+
'orangered' => {:r => 255, :g => 69, :b => 0},
|
367
|
+
'orchid' => {:r => 218, :g => 112, :b => 214},
|
368
|
+
'palegoldenrod' => {:r => 238, :g => 232, :b => 170},
|
369
|
+
'palegreen' => {:r => 152, :g => 251, :b => 152},
|
370
|
+
'paleturquoise' => {:r => 175, :g => 238, :b => 238},
|
371
|
+
'palevioletred' => {:r => 219, :g => 112, :b => 147},
|
372
|
+
'papayawhip' => {:r => 255, :g => 239, :b => 213},
|
373
|
+
'peachpuff' => {:r => 255, :g => 218, :b => 185},
|
374
|
+
'peru' => {:r => 205, :g => 133, :b => 63},
|
375
|
+
'pink' => {:r => 255, :g => 192, :b => 203},
|
376
|
+
'plum' => {:r => 221, :g => 160, :b => 221},
|
377
|
+
'powderblue' => {:r => 176, :g => 224, :b => 230},
|
378
|
+
'purple' => {:r => 128, :g => 0, :b => 128},
|
379
|
+
'red' => {:r => 255, :g => 0, :b => 0},
|
380
|
+
'rosybrown' => {:r => 188, :g => 143, :b => 143},
|
381
|
+
'royalblue' => {:r => 65, :g => 105, :b => 225},
|
382
|
+
'saddlebrown' => {:r => 139, :g => 69, :b => 19},
|
383
|
+
'salmon' => {:r => 250, :g => 128, :b => 114},
|
384
|
+
'sandybrown' => {:r => 244, :g => 164, :b => 96},
|
385
|
+
'seagreen' => {:r => 46, :g => 139, :b => 87},
|
386
|
+
'seashell' => {:r => 255, :g => 245, :b => 238},
|
387
|
+
'sienna' => {:r => 160, :g => 82, :b => 45},
|
388
|
+
'silver' => {:r => 192, :g => 192, :b => 192},
|
389
|
+
'skyblue' => {:r => 135, :g => 206, :b => 235},
|
390
|
+
'slateblue' => {:r => 106, :g => 90, :b => 205},
|
391
|
+
'slategray' => {:r => 112, :g => 128, :b => 144},
|
392
|
+
'snow' => {:r => 255, :g => 250, :b => 250},
|
393
|
+
'springgreen' => {:r => 0, :g => 255, :b => 127},
|
394
|
+
'steelblue' => {:r => 70, :g => 130, :b => 180},
|
395
|
+
'tan' => {:r => 210, :g => 180, :b => 140},
|
396
|
+
'teal' => {:r => 0, :g => 128, :b => 128},
|
397
|
+
'thistle' => {:r => 216, :g => 191, :b => 216},
|
398
|
+
'tomato' => {:r => 255, :g => 99, :b => 71},
|
399
|
+
'turquoise' => {:r => 64, :g => 224, :b => 208},
|
400
|
+
'violet' => {:r => 238, :g => 130, :b => 238},
|
401
|
+
'wheat' => {:r => 245, :g => 222, :b => 179},
|
402
|
+
'white' => {:r => 255, :g => 255, :b => 255},
|
403
|
+
'whitesmoke' => {:r => 245, :g => 245, :b => 245},
|
404
|
+
'yellow' => {:r => 255, :g => 255, :b => 0},
|
405
|
+
'yellowgreen' => {:r => 154, :g => 205, :b => 50}
|
406
|
+
}
|
133
407
|
end
|
data/lib/sphero/request.rb
CHANGED
@@ -3,6 +3,8 @@ class Sphero
|
|
3
3
|
SOP1 = 0xFF
|
4
4
|
SOP2 = 0xFF
|
5
5
|
|
6
|
+
attr_reader :data
|
7
|
+
|
6
8
|
def initialize seq, data = []
|
7
9
|
@seq = seq
|
8
10
|
@data = data
|
@@ -19,7 +21,12 @@ class Sphero
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def response header, body
|
22
|
-
|
24
|
+
name = self.class.name.split('::').last
|
25
|
+
if Response.const_defined?(name)
|
26
|
+
Response.const_get(name).new header, body
|
27
|
+
else
|
28
|
+
Response.new header, body
|
29
|
+
end
|
23
30
|
end
|
24
31
|
|
25
32
|
def packet_header
|
@@ -49,49 +56,26 @@ class Sphero
|
|
49
56
|
end
|
50
57
|
end
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def packet_body
|
60
|
-
@data.pack 'n'
|
61
|
-
end
|
59
|
+
def self.make_command klass, cid, &block
|
60
|
+
Class.new(klass) {
|
61
|
+
define_method(:initialize) do |seq, *args|
|
62
|
+
super(seq, args)
|
63
|
+
@cid = cid
|
64
|
+
end
|
65
|
+
}
|
62
66
|
end
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
68
|
+
SetBackLEDOutput = make_command Sphero, 0x21
|
69
|
+
SetRotationRate = make_command Sphero, 0x03
|
70
|
+
SetRGB = make_command Sphero, 0x20
|
71
|
+
GetRGB = make_command Sphero, 0x22
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
class SetRGB < Sphero
|
79
|
-
def initialize seq, r, g, b, persistant
|
80
|
-
super(seq, [r, g, b, persistant])
|
81
|
-
@cid = 0x20
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
class GetRGB < Sphero
|
86
|
-
def initialize seq
|
87
|
-
super(seq, [])
|
88
|
-
@cid = 0x22
|
89
|
-
end
|
90
|
-
|
91
|
-
def response header, body
|
92
|
-
Response::GetRGB.new header, body
|
93
|
-
end
|
94
|
-
end
|
73
|
+
Ping = make_command Request, 0x01
|
74
|
+
GetVersioning = make_command Request, 0x02
|
75
|
+
GetBluetoothInfo = make_command Request, 0x11
|
76
|
+
SetAutoReconnect = make_command Request, 0x12
|
77
|
+
GetAutoReconnect = make_command Request, 0x13
|
78
|
+
GetPowerState = make_command Request, 0x20
|
95
79
|
|
96
80
|
class Roll < Sphero
|
97
81
|
def initialize seq, speed, heading, delay
|
@@ -105,70 +89,96 @@ class Sphero
|
|
105
89
|
end
|
106
90
|
end
|
107
91
|
|
108
|
-
class
|
109
|
-
def initialize seq
|
110
|
-
super(seq, [])
|
111
|
-
@cid
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
class GetVersioning < Request
|
116
|
-
def initialize seq
|
117
|
-
super(seq, [])
|
118
|
-
@cid = 0x02
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
class GetBluetoothInfo < Request
|
123
|
-
def initialize seq
|
124
|
-
super(seq, [])
|
125
|
-
@cid = 0x11
|
92
|
+
class Heading < Request
|
93
|
+
def initialize seq, heading
|
94
|
+
super(seq, [heading])
|
95
|
+
@cid = 0x01
|
126
96
|
end
|
127
97
|
|
128
|
-
|
129
|
-
|
98
|
+
private
|
99
|
+
def packet_body
|
100
|
+
@data.pack 'n'
|
130
101
|
end
|
131
102
|
end
|
132
103
|
|
133
|
-
class
|
134
|
-
def initialize seq,
|
135
|
-
super(seq, [
|
136
|
-
@cid
|
104
|
+
class Sleep < Request
|
105
|
+
def initialize seq, wakeup, macro
|
106
|
+
super(seq, [wakeup, macro])
|
107
|
+
@cid = 0x22
|
137
108
|
end
|
138
|
-
end
|
139
109
|
|
140
|
-
|
141
|
-
def initialize seq
|
142
|
-
super(seq, [])
|
143
|
-
@cid = 0x13
|
144
|
-
end
|
110
|
+
private
|
145
111
|
|
146
|
-
def
|
147
|
-
|
112
|
+
def packet_body
|
113
|
+
@data.pack 'nC'
|
148
114
|
end
|
149
115
|
end
|
150
116
|
|
151
|
-
class
|
152
|
-
def initialize seq
|
153
|
-
super(seq, [])
|
154
|
-
@cid =
|
155
|
-
end
|
156
|
-
|
157
|
-
def response header, body
|
158
|
-
Response::GetPowerState.new header, body
|
117
|
+
class SetPowerNotification < Sphero
|
118
|
+
def initialize seq, enable
|
119
|
+
super(seq, [enable])
|
120
|
+
@cid = 0x21
|
159
121
|
end
|
160
122
|
end
|
161
123
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
124
|
+
GYRO_AXIS_H_FILTERED = 0x0000_0001
|
125
|
+
GYRO_AXIS_M_FILTERED = 0x0000_0002
|
126
|
+
GYRO_AXIS_L_FILTERED = 0x0000_0004
|
127
|
+
LEFT_MOTOR_BACK_EMF_FILTERED = 0x0000_0020
|
128
|
+
RIGHT_MOTOR_BACK_EMF_FILTERED = 0x0000_0040
|
129
|
+
MAGNETOMETER_AXIS_Z_FILTERED = 0x0000_0080
|
130
|
+
MAGNETOMETER_AXIS_Y_FILTERED = 0x0000_0100
|
131
|
+
MAGNETOMETER_AXIS_X_FILTERED = 0x0000_0200
|
132
|
+
GYRO_AXIS_Z_FILTERED = 0x0000_0400
|
133
|
+
GYRO_AXIS_Y_FILTERED = 0x0000_0800
|
134
|
+
GYRO_AXIS_X_FILTERED = 0x0000_1000
|
135
|
+
ACCELEROMETER_AXIS_Z_FILTERED = 0x0000_2000
|
136
|
+
ACCELEROMETER_AXIS_Y_FILTERED = 0x0000_4000
|
137
|
+
ACCELEROMETER_AXIS_X_FILTERED = 0x0000_8000
|
138
|
+
IMU_YAW_ANGLE_FILTERED = 0x0001_0000
|
139
|
+
IMU_ROLL_ANGLE_FILTERED = 0x0002_0000
|
140
|
+
IMU_PITCH_ANGLE_FILTERED = 0x0004_0000
|
141
|
+
LEFT_MOTOR_BACK_EMF_RAW = 0x0020_0000
|
142
|
+
RIGHT_MOTOR_BACK_EMF_RAW = 0x0040_0000
|
143
|
+
MAGNETOMETER_AXIS_Z_RAW = 0x0080_0000
|
144
|
+
MAGNETOMETER_AXIS_Y_RAW = 0x0100_0000
|
145
|
+
MAGNETOMETER_AXIS_X_RAW = 0x0200_0000
|
146
|
+
GYRO_AXIS_Z_RAW = 0x0400_0000
|
147
|
+
GYRO_AXIS_Y_RAW = 0x0800_0000
|
148
|
+
GYRO_AXIS_X_RAW = 0x1000_0000
|
149
|
+
ACCELEROMETER_AXIS_Z_RAW = 0x2000_0000
|
150
|
+
ACCELEROMETER_AXIS_Y_RAW = 0x4000_0000
|
151
|
+
ACCELEROMETER_AXIS_X_RAW = 0x8000_0000
|
152
|
+
|
153
|
+
QUATERNION_Q0 = 0x0000_0001
|
154
|
+
QUATERNION_Q1 = 0x0000_0002
|
155
|
+
QUATERNION_Q2 = 0x0000_0004
|
156
|
+
QUATERNION_Q3 = 0x0000_0008
|
157
|
+
ODOMETER_X = 0x0000_0010
|
158
|
+
ODOMETER_Y = 0x0000_0020
|
159
|
+
ACCELONE = 0x0000_0040
|
160
|
+
VELOCITY_X = 0x0000_0080
|
161
|
+
VELOCITY_Y = 0x0000_0100
|
162
|
+
|
163
|
+
class SetDataStreaming < Sphero
|
164
|
+
def initialize seq, n, m, mask, pcnt, mask2
|
165
|
+
super(seq, [n, m, mask, pcnt, mask2])
|
166
|
+
@cid = 0x12
|
167
|
+
@mask = mask
|
168
|
+
@mask2 = mask2
|
166
169
|
end
|
167
170
|
|
168
171
|
private
|
169
172
|
|
170
173
|
def packet_body
|
171
|
-
@data.pack '
|
174
|
+
@data.pack 'nnNCN'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class ConfigureCollisionDetection < Sphero
|
179
|
+
def initialize seq, meth, x_t, y_t, x_spd, y_spd, dead
|
180
|
+
super(seq, [meth, x_t, y_t, x_spd, y_spd, dead])
|
181
|
+
@cid = 0x12
|
172
182
|
end
|
173
183
|
end
|
174
184
|
end
|
data/lib/sphero/response.rb
CHANGED
@@ -7,22 +7,36 @@ class Sphero
|
|
7
7
|
DLEN = 4
|
8
8
|
|
9
9
|
CODE_OK = 0
|
10
|
+
SIMPLE_RESPONSE = 0xFF
|
11
|
+
ASYNC_RESPONSE = 0xFE
|
12
|
+
|
13
|
+
def self.simple?(header)
|
14
|
+
header[SOP2] == SIMPLE_RESPONSE
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.async?(header)
|
18
|
+
header[SOP2] == ASYNC_RESPONSE
|
19
|
+
end
|
10
20
|
|
11
21
|
def initialize header, body
|
12
22
|
@header = header
|
13
23
|
@body = body
|
14
24
|
end
|
15
25
|
|
26
|
+
def valid?
|
27
|
+
@header && @body
|
28
|
+
end
|
29
|
+
|
16
30
|
def empty?
|
17
|
-
@header[DLEN] == 1
|
31
|
+
valid? && @header[DLEN] == 1
|
18
32
|
end
|
19
33
|
|
20
34
|
def success?
|
21
|
-
@header[MRSP] == CODE_OK
|
35
|
+
valid? && @header[MRSP] == CODE_OK
|
22
36
|
end
|
23
37
|
|
24
38
|
def seq
|
25
|
-
@header[SEQ]
|
39
|
+
valid? && @header[SEQ]
|
26
40
|
end
|
27
41
|
|
28
42
|
def body
|
@@ -84,5 +98,106 @@ class Sphero
|
|
84
98
|
def g; body[1]; end
|
85
99
|
def b; body[2]; end
|
86
100
|
end
|
101
|
+
|
102
|
+
class AsyncResponse < Response
|
103
|
+
ID_CODE = 2
|
104
|
+
DLEN_MSB = 3
|
105
|
+
DLEN_LSB = 4
|
106
|
+
|
107
|
+
POWER_NOTIFICATION = 0x01
|
108
|
+
LEVEL_1_DIAGNOSTIC = 0x02
|
109
|
+
SENSOR_DATA = 0x03
|
110
|
+
CONFIG_BLOCK = 0x04
|
111
|
+
PRESLEEP_WARNING = 0x05
|
112
|
+
MACRO_MARKERS = 0x06
|
113
|
+
COLLISION_DETECTED = 0x07
|
114
|
+
|
115
|
+
VALID_REPONSES = {POWER_NOTIFICATION => 'Sphero::Response::PowerNotification',
|
116
|
+
#LEVEL_1_DIAGNOSTIC => 'AsyncResponse',
|
117
|
+
SENSOR_DATA => 'Sphero::Response::SensorData',
|
118
|
+
#CONFIG_BLOCK => 'AsyncResponse',
|
119
|
+
#PRESLEEP_WARNING => 'AsyncResponse',
|
120
|
+
#MACRO_MARKERS => 'AsyncResponse',
|
121
|
+
COLLISION_DETECTED => 'Sphero::Response::CollisionDetected'}
|
122
|
+
|
123
|
+
def self.valid?(header)
|
124
|
+
VALID_REPONSES.keys.include?(header[ID_CODE])
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.response header, body
|
128
|
+
raise "no good" unless self.valid?(header)
|
129
|
+
constantize(VALID_REPONSES[header[ID_CODE]]).new(header, body)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.constantize(camel_cased_word)
|
133
|
+
names = camel_cased_word.split('::')
|
134
|
+
names.shift if names.empty? || names.first.empty?
|
135
|
+
|
136
|
+
constant = Object
|
137
|
+
names.each do |name|
|
138
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
139
|
+
end
|
140
|
+
constant
|
141
|
+
end
|
142
|
+
|
143
|
+
def empty?
|
144
|
+
@header[DLEN_LSB] == 1
|
145
|
+
end
|
146
|
+
|
147
|
+
def success?
|
148
|
+
AsyncResponse.valid?(@header)
|
149
|
+
end
|
150
|
+
|
151
|
+
def seq
|
152
|
+
1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class PowerNotification < GetPowerState
|
157
|
+
end
|
158
|
+
|
159
|
+
class SensorData < AsyncResponse
|
160
|
+
def body
|
161
|
+
@body.unpack 's*'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class CollisionDetected < AsyncResponse
|
166
|
+
def body
|
167
|
+
@body.unpack 'nnnCnnCN'
|
168
|
+
end
|
169
|
+
|
170
|
+
def x
|
171
|
+
body[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
def y
|
175
|
+
body[1]
|
176
|
+
end
|
177
|
+
|
178
|
+
def z
|
179
|
+
body[2]
|
180
|
+
end
|
181
|
+
|
182
|
+
def axis
|
183
|
+
body[3]
|
184
|
+
end
|
185
|
+
|
186
|
+
def x_magnitude
|
187
|
+
body[4]
|
188
|
+
end
|
189
|
+
|
190
|
+
def y_magnitude
|
191
|
+
body[5]
|
192
|
+
end
|
193
|
+
|
194
|
+
def speed
|
195
|
+
body[6]
|
196
|
+
end
|
197
|
+
|
198
|
+
def timestamp
|
199
|
+
body[7]
|
200
|
+
end
|
201
|
+
end
|
87
202
|
end
|
88
203
|
end
|
data/test/test_sphero.rb
CHANGED
@@ -1,14 +1,105 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'sphero'
|
3
|
+
require 'mocha/setup'
|
3
4
|
|
4
5
|
class TestSphero < MiniTest::Unit::TestCase
|
5
|
-
def
|
6
|
-
|
7
|
-
|
6
|
+
def setup
|
7
|
+
Sphero.any_instance.stubs(:initialize_serialport)
|
8
|
+
Sphero.any_instance.stubs(:initialize_socket)
|
9
|
+
@sphero = Sphero.new 'port123'
|
10
|
+
@seq = 0x00
|
8
11
|
end
|
9
12
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
+
def test_start_returns_new_sphero
|
14
|
+
assert_kind_of Sphero, Sphero.start('someport')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_start_sphero_executes_block
|
18
|
+
Sphero.any_instance.expects(:ping)
|
19
|
+
Sphero.any_instance.expects(:close)
|
20
|
+
Sphero.start('someport') do
|
21
|
+
ping
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_ping
|
26
|
+
Sphero::Request::Ping.expects(:new).with(@seq)
|
27
|
+
@sphero.expects(:write)
|
28
|
+
@sphero.ping
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_version
|
32
|
+
Sphero::Request::GetVersioning.expects(:new).with(@seq)
|
33
|
+
@sphero.expects(:write)
|
34
|
+
@sphero.version
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_bluetooth_info
|
38
|
+
Sphero::Request::GetBluetoothInfo.expects(:new).with(@seq)
|
39
|
+
@sphero.expects(:write)
|
40
|
+
@sphero.bluetooth_info
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_auto_reconnect=
|
44
|
+
@time_s = "10"
|
45
|
+
Sphero::Request::SetAutoReconnect.expects(:new).with(@seq, @time_s)
|
46
|
+
@sphero.expects(:write)
|
47
|
+
@sphero.auto_reconnect = @time_s
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_auto_reconnect
|
51
|
+
@time_s = "10"
|
52
|
+
packet = mock 'packet'
|
53
|
+
packet.stubs(:time).returns(@time_s)
|
54
|
+
|
55
|
+
Sphero::Request::GetAutoReconnect.expects(:new).with(@seq)
|
56
|
+
@sphero.expects(:write).returns(packet)
|
57
|
+
assert_equal @sphero.auto_reconnect, @time_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_disable_auto_reconnect
|
61
|
+
Sphero::Request::SetAutoReconnect.expects(:new).with(@seq, 0, 0x00)
|
62
|
+
@sphero.expects(:write)
|
63
|
+
@sphero.disable_auto_reconnect
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_power_state
|
67
|
+
Sphero::Request::GetPowerState.expects(:new).with(@seq)
|
68
|
+
@sphero.expects(:write)
|
69
|
+
@sphero.power_state
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_sphero_sleep
|
73
|
+
wakeup = 1
|
74
|
+
macro = 2
|
75
|
+
Sphero::Request::Sleep.expects(:new).with(@seq, wakeup, macro)
|
76
|
+
@sphero.expects(:write)
|
77
|
+
@sphero.sphero_sleep wakeup, macro
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_roll
|
81
|
+
speed = 1
|
82
|
+
heading = 2
|
83
|
+
state = 1
|
84
|
+
Sphero::Request::Roll.expects(:new).with(@seq, speed, heading, state)
|
85
|
+
@sphero.expects(:write)
|
86
|
+
@sphero.roll speed, heading, true
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_stop
|
90
|
+
@sphero.expects(:roll).with(0, 0)
|
91
|
+
@sphero.stop
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_keepgoing
|
95
|
+
Kernel.expects(:sleep).with(3)
|
96
|
+
@sphero.keep_going 3
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_directions
|
100
|
+
assert_equal 0, Sphero::FORWARD
|
101
|
+
assert_equal 90, Sphero::RIGHT
|
102
|
+
assert_equal 180, Sphero::BACKWARD
|
103
|
+
assert_equal 270, Sphero::LEFT
|
13
104
|
end
|
14
105
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sphero'
|
3
|
+
|
4
|
+
class TestSpheroRequest < MiniTest::Unit::TestCase
|
5
|
+
def test_ping_checksum
|
6
|
+
ping = Sphero::Request::Ping.new 0
|
7
|
+
expected_bytes = "\xFF\xFF\x00\x01\x00\x01\xFD".force_encoding(Encoding::ASCII_8BIT)
|
8
|
+
assert_equal expected_bytes, ping.to_str
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_sleep_dlen
|
12
|
+
sleep = Sphero::Request::Sleep.new 0, 0, 0
|
13
|
+
assert_equal 0x04, sleep.dlen
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,48 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sphero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Hybrid Group
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: serialport
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: minitest
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: '3.2'
|
38
|
-
type: :development
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '3.2'
|
46
14
|
- !ruby/object:Gem::Dependency
|
47
15
|
name: rdoc
|
48
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,7 +18,7 @@ dependencies:
|
|
50
18
|
requirements:
|
51
19
|
- - ~>
|
52
20
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
21
|
+
version: '4.0'
|
54
22
|
type: :development
|
55
23
|
prerelease: false
|
56
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,7 +26,7 @@ dependencies:
|
|
58
26
|
requirements:
|
59
27
|
- - ~>
|
60
28
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
29
|
+
version: '4.0'
|
62
30
|
- !ruby/object:Gem::Dependency
|
63
31
|
name: hoe
|
64
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +34,7 @@ dependencies:
|
|
66
34
|
requirements:
|
67
35
|
- - ~>
|
68
36
|
- !ruby/object:Gem::Version
|
69
|
-
version: '3.
|
37
|
+
version: '3.6'
|
70
38
|
type: :development
|
71
39
|
prerelease: false
|
72
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -74,13 +42,13 @@ dependencies:
|
|
74
42
|
requirements:
|
75
43
|
- - ~>
|
76
44
|
- !ruby/object:Gem::Version
|
77
|
-
version: '3.
|
45
|
+
version: '3.6'
|
78
46
|
description: ! 'A ruby gem for controlling your Sphero ball. Sends commands over
|
79
47
|
the TTY
|
80
48
|
|
81
49
|
provided by the bluetooth connection.'
|
82
50
|
email:
|
83
|
-
-
|
51
|
+
- sphero@hybridgroup.com
|
84
52
|
executables:
|
85
53
|
- sphero
|
86
54
|
extensions: []
|
@@ -99,8 +67,9 @@ files:
|
|
99
67
|
- lib/sphero/request.rb
|
100
68
|
- lib/sphero/response.rb
|
101
69
|
- test/test_sphero.rb
|
70
|
+
- test/test_sphero_request.rb
|
102
71
|
- .gemtest
|
103
|
-
homepage: http://github.com/
|
72
|
+
homepage: http://github.com/hybridgroup/sphero
|
104
73
|
licenses: []
|
105
74
|
post_install_message:
|
106
75
|
rdoc_options:
|
@@ -113,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
82
|
requirements:
|
114
83
|
- - ! '>='
|
115
84
|
- !ruby/object:Gem::Version
|
116
|
-
version: 1.9.
|
85
|
+
version: 1.9.2
|
117
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
87
|
none: false
|
119
88
|
requirements:
|
@@ -122,9 +91,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
91
|
version: '0'
|
123
92
|
requirements: []
|
124
93
|
rubyforge_project: sphero
|
125
|
-
rubygems_version: 1.8.
|
94
|
+
rubygems_version: 1.8.24
|
126
95
|
signing_key:
|
127
96
|
specification_version: 3
|
128
97
|
summary: A ruby gem for controlling your Sphero ball
|
129
98
|
test_files:
|
130
99
|
- test/test_sphero.rb
|
100
|
+
- test/test_sphero_request.rb
|