rubysphero 0.0.2 → 0.1.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.
- 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: []
|