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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rubysphero.rb +257 -35
  3. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 634d5c52ce52dece3c540e662dbfef7f7aa50fb9
4
- data.tar.gz: 721f4e07381929fd20fc64c2a381d6a5e17a7a46
3
+ metadata.gz: fb0c823e70621ca1e5f9db16cd92117a224a9b74
4
+ data.tar.gz: f495e0bb123f9d8a9c38f304b72833ab748b1e5b
5
5
  SHA512:
6
- metadata.gz: 914745898bccac50217daab9c34d1768d9eb98fefb724f5a5e4e84d3271af9243818a3e5a4a3efdba27b9db9752451e6ba5c3e871a2729f6ee4f226244285173
7
- data.tar.gz: d16fca27c0aaed518fb6657f4c00212f57f009fc022e814b4bbb1bc0b0b1d5e3a21e576294b37396bf64006ee59329b9185d571376fff328446722d0f12e380f
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
- module SpheroUtilities
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
- puts DateTime.now.strftime("%Y-%m-%d %H:%M:%S.%L") + " " + log_str
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 # mod
56
+ end # class
41
57
 
42
- class SpheroClient
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
- return send_and_check(request)
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
- COMMS_RETRY.times do
74
- response = read_data(request.length)
75
- if request.seq == response.echoed_seq then
76
- logd("Sent and received Sequences MATCH.")
77
- return true
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
- logd("Sequences do not match. Sent:#{request.seq} Rec:#{response.echoed_seq} ")
80
- end # times
81
- return false
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 read_data(length)
90
- bytes=@connection.read(length).unpack("C*")
91
- logd("Wire read finished.")
92
- response = SpheroResponse.new( bytes)
181
+ def listen_to_sphero
182
+ while true do
183
+ logd("Listening...")
93
184
 
94
- return response
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
- return open(bluetooth_address)
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
- @connection = Serial.new bluetooth_address, 115200 ,8
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 good")
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
- end # if
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.2
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-04-04 00:00:00.000000000 Z
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 simple Sphero client library to control your Orbotic Sphero 2.0
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: A simple Sphero client library.
63
+ summary: An easy to use Sphero client library.
64
64
  test_files: []