rubysphero 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []