rubysphero 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rubysphero.rb +257 -35
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb0c823e70621ca1e5f9db16cd92117a224a9b74
|
4
|
+
data.tar.gz: f495e0bb123f9d8a9c38f304b72833ab748b1e5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02e5cb6513c19cca8535b2321556350250d72c04dd265cb5910f4d9a2a42adc067afcefa719dea1e487f0e4bf118241f2f3e4fd74456fc63ea418add6ec06545
|
7
|
+
data.tar.gz: 725362545376ee7b56c99eadc82bd16a8c41699f3f54b502d1f1505d49a3a8184b464c880841973130cf2c4c7d4836b4ab4fc5c8978bc9ca7dffe068800166a5
|
data/lib/rubysphero.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'rubyserial'
|
2
2
|
require 'date'
|
3
|
+
require 'thread'
|
3
4
|
|
4
|
-
|
5
|
+
MIN_LENGTH_OF_PACKET=6
|
6
|
+
RESPONSE_TIMEOUT_SECONDS=2
|
7
|
+
BAUD_RATE=115200
|
5
8
|
|
9
|
+
module SpheroUtilities
|
6
10
|
|
7
11
|
def print_format_bytes(bytes)
|
8
12
|
return_str=""
|
@@ -33,16 +37,30 @@ module SpheroUtilities
|
|
33
37
|
return full_bytes
|
34
38
|
end # def
|
35
39
|
|
40
|
+
|
41
|
+
|
42
|
+
end # mod
|
43
|
+
|
44
|
+
class SpheroBase
|
45
|
+
|
46
|
+
attr_accessor :debug
|
47
|
+
|
36
48
|
def logd(log_str="")
|
37
|
-
|
49
|
+
if @debug==nil then
|
50
|
+
# chill.
|
51
|
+
elsif @debug==true then
|
52
|
+
puts DateTime.now.strftime("%Y-%m-%d %H:%M:%S.%L") + " " + log_str
|
53
|
+
end # elsif
|
38
54
|
end # def
|
39
55
|
|
40
|
-
end #
|
56
|
+
end # class
|
41
57
|
|
42
|
-
|
58
|
+
|
59
|
+
class SpheroClient < SpheroBase
|
43
60
|
|
44
61
|
include SpheroUtilities
|
45
62
|
COMMS_RETRY=5
|
63
|
+
@read_thread
|
46
64
|
|
47
65
|
COLOURS = { :blue => [0x00,0x00,0xFF],
|
48
66
|
:green => [0x00,0xFF,0x00],
|
@@ -54,44 +72,185 @@ class SpheroClient
|
|
54
72
|
:pink => [0xFF,0x00,0xFF],
|
55
73
|
:grey => [0x80,0x80,0x80]}
|
56
74
|
|
75
|
+
def collision_detection(status=true)
|
76
|
+
logd()
|
77
|
+
logd "Building request: Collision detection"
|
78
|
+
if status == true
|
79
|
+
meth=0x01
|
80
|
+
else
|
81
|
+
meth=0x00
|
82
|
+
end # else
|
83
|
+
|
84
|
+
request=SpheroRequest.new(:synchronous, self.debug)
|
85
|
+
|
86
|
+
request.did=0x02
|
87
|
+
request.cid=0x12 # Collision Detection
|
88
|
+
request.seq=get_sequence
|
89
|
+
request.dlen=0x07
|
90
|
+
|
91
|
+
request.push_data meth
|
92
|
+
request.push_data 0x7F # xt
|
93
|
+
request.push_data 0x40 # xspd
|
94
|
+
request.push_data 0x7F # yt
|
95
|
+
request.push_data 0x40 # yspd
|
96
|
+
request.push_data 0x40 # dead
|
97
|
+
|
98
|
+
if send_and_check(request) then
|
99
|
+
return true
|
100
|
+
else
|
101
|
+
return false
|
102
|
+
end # else
|
103
|
+
end # def
|
104
|
+
|
105
|
+
|
106
|
+
|
57
107
|
def ping
|
58
108
|
logd()
|
59
109
|
logd "Building request: ping"
|
60
|
-
request=SpheroRequest.new(:synchronous)
|
110
|
+
request=SpheroRequest.new(:synchronous, self.debug)
|
61
111
|
|
62
112
|
request.did=0x00
|
63
113
|
request.cid=0x01 # Ping
|
64
114
|
request.seq=get_sequence
|
65
115
|
request.dlen=0x01
|
66
116
|
|
67
|
-
|
117
|
+
if send_and_check(request) then
|
118
|
+
logd("PING RESPONSE RECEIVED!!!")
|
119
|
+
return true
|
120
|
+
else
|
121
|
+
return false
|
122
|
+
end # else
|
68
123
|
end # def
|
69
124
|
|
125
|
+
def define_event_handling_for(event_type, &actions)
|
126
|
+
if event_type==:collision then
|
127
|
+
@collision_actions.push actions
|
128
|
+
end # if
|
129
|
+
end # def
|
130
|
+
|
131
|
+
def handle_collision_event(the_event_response )
|
132
|
+
logd("Handling collision event!")
|
133
|
+
Thread.new do
|
134
|
+
if @collision_actions != nil then
|
135
|
+
logd "Collision action was NOT nil"
|
136
|
+
|
137
|
+
@collision_actions.each do |collision_action_item|
|
138
|
+
collision_action_item.call the_event_response
|
139
|
+
end # each
|
140
|
+
|
141
|
+
else
|
142
|
+
logd "Collision action was nil"
|
143
|
+
end # if
|
144
|
+
end # thread
|
145
|
+
end #
|
146
|
+
|
70
147
|
def send_and_check(request)
|
148
|
+
queue_a_request(request)
|
149
|
+
|
71
150
|
send_data(request.build_packet)
|
72
151
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
152
|
+
responded =nil
|
153
|
+
start_time=Time.now.getutc.to_i
|
154
|
+
logd("Start time: #{start_time}")
|
155
|
+
begin
|
156
|
+
|
157
|
+
if @responses.has_key? request.seq
|
158
|
+
logd("Response Queue has matching sequence")
|
159
|
+
forget_a_request request
|
160
|
+
return @responses[request.seq]
|
161
|
+
end # if
|
162
|
+
|
163
|
+
sleep 0
|
164
|
+
|
165
|
+
if Time.now.getutc.to_i > (start_time+RESPONSE_TIMEOUT_SECONDS)
|
166
|
+
logd("Timing out waiting for a response.")
|
167
|
+
forget_a_request request
|
168
|
+
return false
|
78
169
|
end # if
|
79
|
-
|
80
|
-
end
|
81
|
-
|
170
|
+
|
171
|
+
end while !responded
|
172
|
+
|
173
|
+
return response.valid
|
174
|
+
|
82
175
|
end # data
|
83
176
|
|
84
177
|
def send_data(data)
|
85
|
-
logd("Wire send next.")
|
86
178
|
@connection.write data
|
87
179
|
end # def
|
88
180
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
response = SpheroResponse.new( bytes)
|
181
|
+
def listen_to_sphero
|
182
|
+
while true do
|
183
|
+
logd("Listening...")
|
93
184
|
|
94
|
-
|
185
|
+
response=read_data
|
186
|
+
@responses[response.echoed_seq] =response
|
187
|
+
sleep 0
|
188
|
+
end # while
|
189
|
+
|
190
|
+
end # def
|
191
|
+
|
192
|
+
def read_data()
|
193
|
+
bytes=[]
|
194
|
+
|
195
|
+
logd("Reading...")
|
196
|
+
|
197
|
+
while @connection !=nil do
|
198
|
+
byte_maybe=@connection.getbyte
|
199
|
+
|
200
|
+
if byte_maybe
|
201
|
+
|
202
|
+
bytes.push byte_maybe
|
203
|
+
|
204
|
+
logd("Received a byte: #{byte_maybe}")
|
205
|
+
|
206
|
+
# Check response is long enough to bother with.
|
207
|
+
if bytes.length >= MIN_LENGTH_OF_PACKET then
|
208
|
+
|
209
|
+
if (bytes[0] == 0xFF) && (bytes[1] == 0xFE)
|
210
|
+
|
211
|
+
logd("First 2 bytes indicate it is an Asyschronous Packet")
|
212
|
+
|
213
|
+
elsif (bytes[0] == 0xFF) && (bytes[1] == 0xFF)
|
214
|
+
|
215
|
+
logd("First 2 bytes indicate it is a Syschronous Packet")
|
216
|
+
|
217
|
+
else
|
218
|
+
|
219
|
+
logd("Odd response starts with: #{bytes}, will try removing first byte.")
|
220
|
+
bytes.shift
|
221
|
+
|
222
|
+
end # else
|
223
|
+
|
224
|
+
response = SpheroResponse.new(bytes.dup, self.debug)
|
225
|
+
|
226
|
+
if response.valid
|
227
|
+
|
228
|
+
# Handle Asynchronous Collision detection responses.
|
229
|
+
if (response.synchronicity? == :asynchronous) && (bytes[2] == 0x07) then
|
230
|
+
handle_collision_event(response )
|
231
|
+
end # else
|
232
|
+
|
233
|
+
return response
|
234
|
+
|
235
|
+
else
|
236
|
+
|
237
|
+
logd("Response not valid yet, keep reading.")
|
238
|
+
|
239
|
+
end # else
|
240
|
+
|
241
|
+
|
242
|
+
end # if bytes > length
|
243
|
+
|
244
|
+
else
|
245
|
+
# No Bytes to read
|
246
|
+
sleep 0
|
247
|
+
end # else
|
248
|
+
|
249
|
+
end # while conn not nil
|
250
|
+
|
251
|
+
logd("Connection must have nbeen lost")
|
252
|
+
|
253
|
+
return false
|
95
254
|
end # def
|
96
255
|
|
97
256
|
def orientation
|
@@ -122,7 +281,7 @@ class SpheroClient
|
|
122
281
|
logd()
|
123
282
|
logd "Building request: set back led output b"
|
124
283
|
|
125
|
-
request=SpheroRequest.new(:synchronous)
|
284
|
+
request=SpheroRequest.new(:synchronous, self.debug)
|
126
285
|
|
127
286
|
request.did=0x02
|
128
287
|
request.cid=0x21
|
@@ -164,7 +323,7 @@ class SpheroClient
|
|
164
323
|
logd
|
165
324
|
logd "Building request: colour"
|
166
325
|
|
167
|
-
request=SpheroRequest.new(:synchronous)
|
326
|
+
request=SpheroRequest.new(:synchronous, self.debug)
|
168
327
|
|
169
328
|
|
170
329
|
request.did=0x02
|
@@ -185,7 +344,7 @@ class SpheroClient
|
|
185
344
|
logd()
|
186
345
|
logd( "Building request: roll")
|
187
346
|
|
188
|
-
request=SpheroRequest.new(:synchronous)
|
347
|
+
request=SpheroRequest.new(:synchronous, self.debug)
|
189
348
|
heading = heading_raw%359
|
190
349
|
logd( "Heading: #{heading}")
|
191
350
|
|
@@ -204,24 +363,58 @@ class SpheroClient
|
|
204
363
|
|
205
364
|
end #def
|
206
365
|
|
366
|
+
def queued_requests
|
367
|
+
return @queued_requests
|
368
|
+
end # def
|
369
|
+
|
370
|
+
def queue_a_request(request)
|
371
|
+
@queued_requests.push request
|
372
|
+
end # def
|
373
|
+
|
374
|
+
def forget_a_request(request)
|
375
|
+
@queued_requests.delete request
|
376
|
+
end # def
|
377
|
+
|
207
378
|
def get_sequence
|
208
379
|
@sequence_val+=1
|
209
380
|
if @sequence_val > 255
|
210
381
|
@sequence_val=0
|
211
382
|
end # if
|
212
383
|
logd("Getting seq: #{@sequence_val}")
|
384
|
+
|
213
385
|
return @sequence_val
|
214
386
|
end # def
|
215
387
|
|
216
|
-
def initialize(bluetooth_address)
|
388
|
+
def initialize(bluetooth_address, debugval=false)
|
217
389
|
@sequence_val=0
|
218
|
-
|
390
|
+
@responses=Hash.new
|
391
|
+
@collision_actions=[]
|
392
|
+
|
393
|
+
@debug=debugval
|
394
|
+
|
395
|
+
@queued_requests=[]
|
396
|
+
logd("Calling open connnection next. Using: #{bluetooth_address}")
|
397
|
+
|
398
|
+
conn = open(bluetooth_address)
|
399
|
+
|
400
|
+
@read_thread = Thread.new {
|
401
|
+
logd("Listen thread started...")
|
402
|
+
listen_to_sphero
|
403
|
+
logd("Listen thread ending...")
|
404
|
+
} # thread
|
405
|
+
|
406
|
+
return conn
|
407
|
+
|
219
408
|
end # class
|
220
409
|
|
221
410
|
def open(bluetooth_address)
|
222
411
|
begin
|
223
|
-
|
412
|
+
logd("About to open Connection")
|
413
|
+
@connection = Serial.new bluetooth_address, BAUD_RATE ,8
|
414
|
+
logd("Connection:#{@connection.to_s}")
|
224
415
|
rescue RubySerial::Exception
|
416
|
+
logd("Connection failed, about to retry...")
|
417
|
+
sleep 1
|
225
418
|
open bluetooth_address
|
226
419
|
end
|
227
420
|
return @connection
|
@@ -233,7 +426,7 @@ class SpheroClient
|
|
233
426
|
|
234
427
|
end # class
|
235
428
|
|
236
|
-
class SpheroResponse
|
429
|
+
class SpheroResponse < SpheroBase
|
237
430
|
|
238
431
|
include SpheroUtilities
|
239
432
|
|
@@ -243,13 +436,40 @@ class SpheroResponse
|
|
243
436
|
attr_accessor :raw_data
|
244
437
|
attr_accessor :data
|
245
438
|
attr_accessor :valid
|
439
|
+
attr_accessor :sop1
|
440
|
+
attr_accessor :sop2
|
246
441
|
|
247
442
|
|
248
|
-
def initialize(raw_data)
|
443
|
+
def initialize(raw_data, debugval)
|
444
|
+
@debug=debugval
|
249
445
|
@valid=false
|
446
|
+
@raw_data=raw_data.dup
|
250
447
|
@data=process_data(raw_data)
|
251
448
|
end # def
|
252
449
|
|
450
|
+
def synchronicity?
|
451
|
+
logd("Synchronicity?")
|
452
|
+
logd("#{@sop1}")
|
453
|
+
logd("#{@sop2}")
|
454
|
+
|
455
|
+
if (@sop1==0xFF) && (@sop2==0xFE) then
|
456
|
+
return :asynchronous
|
457
|
+
elsif (@sop1==0xFF) && (@sop2==0xFF)
|
458
|
+
return :synchronous
|
459
|
+
else
|
460
|
+
return nil
|
461
|
+
end # else
|
462
|
+
|
463
|
+
end # def
|
464
|
+
|
465
|
+
def raw_length
|
466
|
+
if @data==nil
|
467
|
+
return 0
|
468
|
+
else
|
469
|
+
@raw_data.length
|
470
|
+
end # else
|
471
|
+
end # def
|
472
|
+
|
253
473
|
def process_data(bytes)
|
254
474
|
if bytes.length == 0
|
255
475
|
logd "Response: None received"
|
@@ -257,26 +477,28 @@ class SpheroResponse
|
|
257
477
|
logd "Response: Was nil"
|
258
478
|
else
|
259
479
|
logd "Response data raw: #{print_format_bytes(bytes)}"
|
260
|
-
bytes.shift
|
261
|
-
bytes.shift
|
480
|
+
@sop1 = bytes.shift
|
481
|
+
@sop2 = bytes.shift
|
262
482
|
@raw_checksum=bytes.pop
|
263
483
|
|
264
484
|
@calculated_checksum=do_checksum( bytes )
|
265
485
|
logd "Response checksum: #{@calculated_checksum.to_s(16)}"
|
266
486
|
if @raw_checksum == @calculated_checksum then
|
267
|
-
logd("Response Checksum is
|
487
|
+
logd("Response Checksum is Valid")
|
268
488
|
@valid=true
|
269
489
|
logd("Response data:#{bytes}")
|
270
490
|
@echoed_seq=bytes[1]
|
271
491
|
@data=bytes
|
272
|
-
|
492
|
+
else
|
493
|
+
logd("Response Checksum is BAD")
|
494
|
+
end # else
|
273
495
|
|
274
496
|
end # else
|
275
497
|
|
276
498
|
end # def
|
277
499
|
end # class
|
278
500
|
|
279
|
-
class SpheroRequest
|
501
|
+
class SpheroRequest < SpheroBase
|
280
502
|
include SpheroUtilities
|
281
503
|
|
282
504
|
attr_accessor :sop1
|
@@ -289,12 +511,12 @@ class SpheroRequest
|
|
289
511
|
attr_accessor :sequence
|
290
512
|
|
291
513
|
|
292
|
-
def initialize(type=:synchronous)
|
514
|
+
def initialize(type=:synchronous, debugval)
|
293
515
|
if type==:synchronous
|
294
516
|
@sop1=0xFF
|
295
517
|
@sop2=0xFF
|
296
518
|
end # if
|
297
|
-
|
519
|
+
@debug=debugval
|
298
520
|
@packet_data=Array.new
|
299
521
|
@payload_data=Array.new
|
300
522
|
end # def
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubysphero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Houghton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyserial
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.2.4
|
33
|
-
description: A
|
33
|
+
description: A Sphero client library to control your Orbotic Sphero 2.0
|
34
34
|
email: pete@investigatingsoftware.co.uk
|
35
35
|
executables: []
|
36
36
|
extensions: []
|
@@ -60,5 +60,5 @@ rubyforge_project:
|
|
60
60
|
rubygems_version: 2.4.5.1
|
61
61
|
signing_key:
|
62
62
|
specification_version: 4
|
63
|
-
summary:
|
63
|
+
summary: An easy to use Sphero client library.
|
64
64
|
test_files: []
|