sphero_pwn 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/sphero_diagnostics.rb +71 -0
- data/bin/sphero_map_flash.rb +82 -0
- data/bin/sphero_save_flash_block.rb +47 -0
- data/bin/sphero_set_flag.rb +38 -0
- data/bin/sphero_soft_reboot.rb +33 -0
- data/bin/sphero_toggle_user_hack.rb +36 -0
- data/lib/sphero_pwn.rb +24 -14
- data/lib/sphero_pwn/asyncs/flash_block.rb +26 -0
- data/lib/sphero_pwn/channel.rb +72 -29
- data/lib/sphero_pwn/commands/boot_main_app.rb +17 -0
- data/lib/sphero_pwn/commands/enter_bootloader.rb +15 -0
- data/lib/sphero_pwn/commands/get_device_mode.rb +35 -0
- data/lib/sphero_pwn/commands/get_flash_block.rb +34 -0
- data/lib/sphero_pwn/commands/get_permanent_flags.rb +44 -0
- data/lib/sphero_pwn/commands/get_versions.rb +12 -10
- data/lib/sphero_pwn/commands/is_page_blank.rb +36 -0
- data/lib/sphero_pwn/commands/l1_diagnostics.rb +0 -1
- data/lib/sphero_pwn/commands/l2_diagnostics.rb +50 -0
- data/lib/sphero_pwn/commands/set_device_mode.rb +25 -0
- data/lib/sphero_pwn/commands/set_permanent_flags.rb +29 -0
- data/lib/sphero_pwn/session.rb +12 -4
- data/sphero_pwn.gemspec +130 -0
- data/test/command_test.rb +10 -0
- data/test/commands/boot_main_app_test.rb +11 -0
- data/test/commands/enter_bootloader_test.rb +33 -0
- data/test/commands/get_device_mode_test.rb +40 -0
- data/test/commands/get_flash_block_test.rb +101 -0
- data/test/commands/get_permanent_flags_test.rb +48 -0
- data/test/commands/get_versions_test.rb +10 -1
- data/test/commands/is_page_blank_test.rb +65 -0
- data/test/commands/l2_diagnostics_test.rb +58 -0
- data/test/commands/set_device_mode_test.rb +56 -0
- data/test/commands/set_permanent_flags_test.rb +55 -0
- data/test/data/enter_bootloader.txt +4 -0
- data/test/data/get_device_mode.txt +2 -0
- data/test/data/get_factory_config.txt +2 -0
- data/test/data/get_permanent_flags.txt +2 -0
- data/test/data/get_soul.txt +2 -0
- data/test/data/get_version.txt +2 -2
- data/test/data/is_page_blank.txt +6 -0
- data/test/data/l2_diagnostics.txt +8 -0
- data/test/data/ping.txt +2 -2
- data/test/data/set_device_mode.txt +12 -0
- data/test/data/set_permanent_flags.txt +8 -0
- metadata +44 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9eac1b907fc6e5963161d6572b72c2d8f2ff241d
|
4
|
+
data.tar.gz: ae48c0c7bb5ca8a3c851bbaf512a7612e5caee5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33bf8e533323091af250cccdeefbec7abb09ad2d71901ce9c7c61cc2aa4d35eb64b66cde688c28c8a088c7d2624f219e6eac4f89728ecf4da41dcd73c0faa421
|
7
|
+
data.tar.gz: f6357fc7cda85e4c77d25787827e99251f4304f3f9e34cc5b6f7dcfa45746545b037c2b07d01284311123d255b3b89982b42242f26c1bad5d5f5bd254c81cf74
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
unless ARGV.length == 1
|
8
|
+
puts <<END_USAGE
|
9
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device
|
10
|
+
|
11
|
+
The block name can be soul|factory_config|user_config.
|
12
|
+
END_USAGE
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
rfconn_path = ARGV[0]
|
17
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
18
|
+
session = SpheroPwn::Session.new channel
|
19
|
+
|
20
|
+
session.send_command SpheroPwn::Commands::L1Diagnostics.new
|
21
|
+
response = nil
|
22
|
+
async = nil
|
23
|
+
while response.nil? || async.nil?
|
24
|
+
message = session.recv_message
|
25
|
+
if message.nil?
|
26
|
+
sleep 0.05
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
if message.kind_of? SpheroPwn::Response
|
31
|
+
response = message
|
32
|
+
if response.code == :ok
|
33
|
+
puts "Queued command to L1 diagnostics.\n"
|
34
|
+
else
|
35
|
+
puts "Failed to get L1 diagnostics. Code: #{response.code}\n"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
else
|
39
|
+
async = message
|
40
|
+
puts "L1 diagnostics:\n"
|
41
|
+
puts message.text
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
session.send_command SpheroPwn::Commands::L2Diagnostics.new
|
46
|
+
response = nil
|
47
|
+
async = nil
|
48
|
+
while response.nil? || async.nil?
|
49
|
+
message = session.recv_message
|
50
|
+
if message.nil?
|
51
|
+
sleep 0.05
|
52
|
+
next
|
53
|
+
end
|
54
|
+
|
55
|
+
if message.kind_of? SpheroPwn::Response
|
56
|
+
response = message
|
57
|
+
if response.code == :ok
|
58
|
+
puts "Queued command to L2 diagnostics.\n"
|
59
|
+
else
|
60
|
+
puts "Failed to get L2 diagnostics. Code: #{response.code}\n"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
else
|
64
|
+
async = message
|
65
|
+
puts "L2 diagnostics:\n"
|
66
|
+
pp message.counters
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
session.close
|
71
|
+
exit 0
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
unless ARGV.length == 1
|
6
|
+
puts <<END_USAGE
|
7
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device
|
8
|
+
END_USAGE
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
rfconn_path = ARGV[0]
|
13
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
14
|
+
session = SpheroPwn::Session.new channel
|
15
|
+
|
16
|
+
session.send_command SpheroPwn::Commands::EnterBootloader.new
|
17
|
+
response = session.recv_until_response
|
18
|
+
unless response.code == :ok
|
19
|
+
puts "Failed to enter bootloader. Code: #{response.code}\n"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
page_map = []
|
24
|
+
first_good = 0
|
25
|
+
first_bad = 0
|
26
|
+
|
27
|
+
begin
|
28
|
+
loop do
|
29
|
+
print "Probing page #{first_good}... "
|
30
|
+
session.send_command SpheroPwn::Commands::IsPageBlank.new(first_good)
|
31
|
+
response = session.recv_until_response
|
32
|
+
case response.code
|
33
|
+
when :ok
|
34
|
+
page_map[first_good] = response.is_blank? ? '.' : '*'
|
35
|
+
puts(response.is_blank? ? 'blank' : 'used')
|
36
|
+
break
|
37
|
+
when :bad_page
|
38
|
+
page_map[first_good] = 'X'
|
39
|
+
puts 'bad'
|
40
|
+
first_good += 1
|
41
|
+
next
|
42
|
+
else
|
43
|
+
puts "Failed to get page status. Code: #{response.code}\n"
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
first_bad = first_good + 1
|
49
|
+
loop do
|
50
|
+
print "Probing page #{first_bad}... "
|
51
|
+
session.send_command SpheroPwn::Commands::IsPageBlank.new(first_bad)
|
52
|
+
response = session.recv_until_response
|
53
|
+
case response.code
|
54
|
+
when :ok
|
55
|
+
page_map[first_bad] = response.is_blank? ? '.' : '*'
|
56
|
+
puts(response.is_blank? ? 'blank' : 'used')
|
57
|
+
first_bad += 1
|
58
|
+
next
|
59
|
+
when :bad_page
|
60
|
+
page_map[first_bad] = 'X'
|
61
|
+
puts 'bad'
|
62
|
+
break
|
63
|
+
else
|
64
|
+
puts "Failed to get page status. Code: #{response.code}\n"
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
ensure
|
69
|
+
puts "First valid page: #{first_good}\n"
|
70
|
+
puts "Last valid page: #{first_bad - 1}\n"
|
71
|
+
puts "Page map:\n#{page_map.join('')}\n"
|
72
|
+
|
73
|
+
session.send_command SpheroPwn::Commands::BootMainApp.new
|
74
|
+
response = session.recv_until_response
|
75
|
+
unless response.code == :ok
|
76
|
+
puts "Failed to boot main application. Code: #{response.code}\n"
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
session.close
|
82
|
+
exit 0
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
unless ARGV.length == 3
|
6
|
+
puts <<END_USAGE
|
7
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device block_name flash_file.bin
|
8
|
+
|
9
|
+
The block name can be soul|factory_config|user_config.
|
10
|
+
END_USAGE
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
rfconn_path = ARGV[0]
|
15
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
16
|
+
session = SpheroPwn::Session.new channel
|
17
|
+
|
18
|
+
session.send_command SpheroPwn::Commands::GetFlashBlock.new ARGV[1].to_sym
|
19
|
+
|
20
|
+
response = nil
|
21
|
+
async = nil
|
22
|
+
while response.nil? || async.nil?
|
23
|
+
message = session.recv_message
|
24
|
+
if message.nil?
|
25
|
+
sleep 0.05
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
if message.kind_of? SpheroPwn::Response
|
30
|
+
response = message
|
31
|
+
if response.code == :ok
|
32
|
+
puts "Queued command to get #{ARGV[1]} block.\n"
|
33
|
+
else
|
34
|
+
puts "Failed to get #{ARGV[1]} block. Code: #{response.code}\n"
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
else
|
38
|
+
async = message
|
39
|
+
File.open ARGV[2], 'wb' do |f|
|
40
|
+
f.write async.data_bytes.pack('C*')
|
41
|
+
end
|
42
|
+
puts "Wrote #{ARGV[1]} block to #{ARGV[2]}\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
session.close
|
47
|
+
exit 0
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
unless ARGV.length == 3
|
6
|
+
puts <<END_USAGE
|
7
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device flag_name {true|false}
|
8
|
+
|
9
|
+
Known flags: #{SpheroPwn::Commands::SetPermanentFlags::FLAGS.keys.join(' ')}
|
10
|
+
END_USAGE
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
rfconn_path = ARGV[0]
|
15
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
16
|
+
session = SpheroPwn::Session.new channel
|
17
|
+
|
18
|
+
session.send_command SpheroPwn::Commands::GetPermanentFlags.new
|
19
|
+
response = session.recv_until_response
|
20
|
+
unless response.code == :ok
|
21
|
+
puts "Failed to retrieve current flags. Code: #{response.code}\n"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
puts "Current flags: #{response.flags.inspect}\n"
|
25
|
+
|
26
|
+
new_flags = response.flags.merge({ ARGV[1].to_sym => (ARGV[2] == 'true') })
|
27
|
+
puts "New flags: #{new_flags}\n"
|
28
|
+
|
29
|
+
session.send_command SpheroPwn::Commands::SetPermanentFlags.new(new_flags)
|
30
|
+
response = session.recv_until_response
|
31
|
+
unless response.code == :ok
|
32
|
+
puts "Failed to set new flags. Code: #{response.code}\n"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "New flags set successfully\n"
|
37
|
+
session.close
|
38
|
+
exit 0
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
unless ARGV.length == 1
|
6
|
+
puts <<END_USAGE
|
7
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device
|
8
|
+
END_USAGE
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
rfconn_path = ARGV[0]
|
13
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
14
|
+
session = SpheroPwn::Session.new channel
|
15
|
+
|
16
|
+
session.send_command SpheroPwn::Commands::EnterBootloader.new
|
17
|
+
response = session.recv_until_response
|
18
|
+
unless response.code == :ok
|
19
|
+
puts "Failed to enter bootloader. Code: #{response.code}\n"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
puts "Entered bootloader\n"
|
23
|
+
|
24
|
+
session.send_command SpheroPwn::Commands::BootMainApp.new
|
25
|
+
response = session.recv_until_response
|
26
|
+
unless response.code == :ok
|
27
|
+
puts "Failed to boot main application. Code: #{response.code}\n"
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
puts "Entered main app\n"
|
31
|
+
|
32
|
+
session.close
|
33
|
+
exit 0
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/sphero_pwn.rb'
|
4
|
+
|
5
|
+
unless ARGV.length == 1
|
6
|
+
puts <<END_USAGE
|
7
|
+
Usage: #{$PROGRAM_NAME} /dev/bluetooth-device
|
8
|
+
END_USAGE
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
rfconn_path = ARGV[0]
|
13
|
+
channel = SpheroPwn::Channel.new rfconn_path
|
14
|
+
session = SpheroPwn::Session.new channel
|
15
|
+
|
16
|
+
session.send_command SpheroPwn::Commands::GetDeviceMode.new
|
17
|
+
response = session.recv_until_response
|
18
|
+
unless response.code == :ok
|
19
|
+
puts "Failed to retrieve device mode. Code: #{response.code}\n"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
puts "Current device mode: #{response.mode}\n"
|
23
|
+
|
24
|
+
new_mode = (response.mode == :normal) ? :user_hack : :normal
|
25
|
+
puts "Switching device to mode: #{new_mode}\n"
|
26
|
+
|
27
|
+
session.send_command SpheroPwn::Commands::SetDeviceMode.new(new_mode)
|
28
|
+
response = session.recv_until_response
|
29
|
+
unless response.code == :ok
|
30
|
+
puts "Failed to set device mode. Code: #{response.code}\n"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
puts "New mode set successfully\n"
|
35
|
+
session.close
|
36
|
+
exit 0
|
data/lib/sphero_pwn.rb
CHANGED
@@ -2,19 +2,29 @@
|
|
2
2
|
module SpheroPwn
|
3
3
|
end
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
require_relative './sphero_pwn/async.rb'
|
6
|
+
require_relative './sphero_pwn/asyncs.rb'
|
7
|
+
require_relative './sphero_pwn/channel.rb'
|
8
|
+
require_relative './sphero_pwn/channel_recorder.rb'
|
9
|
+
require_relative './sphero_pwn/command.rb'
|
10
|
+
require_relative './sphero_pwn/commands.rb'
|
11
|
+
require_relative './sphero_pwn/replay_channel.rb'
|
12
|
+
require_relative './sphero_pwn/response.rb'
|
13
|
+
require_relative './sphero_pwn/session.rb'
|
14
|
+
require_relative './sphero_pwn/test_channel.rb'
|
15
15
|
|
16
|
-
|
16
|
+
require_relative './sphero_pwn/asyncs/flash_block.rb'
|
17
|
+
require_relative './sphero_pwn/asyncs/l1_diagnostics.rb'
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
require_relative './sphero_pwn/commands/boot_main_app.rb'
|
20
|
+
require_relative './sphero_pwn/commands/enter_bootloader.rb'
|
21
|
+
require_relative './sphero_pwn/commands/get_device_mode.rb'
|
22
|
+
require_relative './sphero_pwn/commands/get_flash_block.rb'
|
23
|
+
require_relative './sphero_pwn/commands/get_permanent_flags.rb'
|
24
|
+
require_relative './sphero_pwn/commands/get_versions.rb'
|
25
|
+
require_relative './sphero_pwn/commands/is_page_blank.rb'
|
26
|
+
require_relative './sphero_pwn/commands/l1_diagnostics.rb'
|
27
|
+
require_relative './sphero_pwn/commands/l2_diagnostics.rb'
|
28
|
+
require_relative './sphero_pwn/commands/ping.rb'
|
29
|
+
require_relative './sphero_pwn/commands/set_device_mode.rb'
|
30
|
+
require_relative './sphero_pwn/commands/set_permanent_flags.rb'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# The result of a get flash block request.
|
2
|
+
#
|
3
|
+
# This is an asynchronous message because it's too long to fit into the command
|
4
|
+
# response structure.
|
5
|
+
class SpheroPwn::Asyncs::FlashBlock < SpheroPwn::Async
|
6
|
+
# @return {String} the text form of the diagnostics
|
7
|
+
attr_reader :text
|
8
|
+
|
9
|
+
def initialize(data_bytes)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class SpheroPwn::Asyncs::FlashBlock::Config < SpheroPwn::Asyncs::FlashBlock
|
15
|
+
def self.id_code
|
16
|
+
0x04
|
17
|
+
end
|
18
|
+
end
|
19
|
+
SpheroPwn::Asyncs.register SpheroPwn::Asyncs::FlashBlock::Config
|
20
|
+
|
21
|
+
class SpheroPwn::Asyncs::FlashBlock::Soul < SpheroPwn::Asyncs::FlashBlock
|
22
|
+
def self.id_code
|
23
|
+
0x0D
|
24
|
+
end
|
25
|
+
end
|
26
|
+
SpheroPwn::Asyncs.register SpheroPwn::Asyncs::FlashBlock::Soul
|
data/lib/sphero_pwn/channel.rb
CHANGED
@@ -10,29 +10,17 @@ class SpheroPwn::Channel
|
|
10
10
|
#
|
11
11
|
# @param {String} rfconn_path the path to the device file connecting to the
|
12
12
|
# robot's Bluetooth RFCONN service
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
raise e unless e.message == 'EBUSY'
|
21
|
-
raise e if Time.now >= give_up_at
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
13
|
+
# @param {Hash} options the options below
|
14
|
+
# @option {Number} connect_timeout retry for this number of seconds when the
|
15
|
+
# connection gets refused with EBUSY
|
16
|
+
def initialize(rfconn_path, options = {})
|
17
|
+
@connect_timeout = options[:connect_timeout] || 15
|
18
|
+
@read_timeout = options[:read_timeout] || 30
|
19
|
+
@read_backoff = options[:read_backoff] || 0.2
|
25
20
|
@send_queue = Queue.new
|
26
|
-
@send_thread = Thread.new @send_queue do
|
27
|
-
send_queue = @send_queue
|
28
21
|
|
29
|
-
|
30
|
-
|
31
|
-
break if bytes == :close
|
32
|
-
|
33
|
-
@port.write bytes
|
34
|
-
end
|
35
|
-
end
|
22
|
+
@port = connect rfconn_path, @connect_timeout
|
23
|
+
@send_thread = spawn_sending_thread @send_queue, @port
|
36
24
|
end
|
37
25
|
|
38
26
|
# @param {String} bytes a binary-encoded string of bytes to be sent to the
|
@@ -43,15 +31,27 @@ class SpheroPwn::Channel
|
|
43
31
|
self
|
44
32
|
end
|
45
33
|
|
46
|
-
# @param {Integer}
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
34
|
+
# @param {Integer} byte_count the number of bytes to be read from the RFCONN
|
35
|
+
# port
|
36
|
+
# @return {String} a binary-encoded string of bytes retrieved from the RFCONN
|
37
|
+
# port; the string may have fewer bytes than requested
|
38
|
+
def recv_bytes(byte_count)
|
39
|
+
buffer = ''.encode Encoding::BINARY
|
40
|
+
|
41
|
+
last_byte_at = Time.now
|
42
|
+
loop do
|
43
|
+
new_bytes = @port.read byte_count - buffer.length
|
44
|
+
buffer.concat new_bytes
|
45
|
+
break if buffer.length == byte_count
|
46
|
+
|
47
|
+
if new_bytes.empty?
|
48
|
+
break if Time.now - last_byte_at >= @read_timeout
|
49
|
+
sleep @read_backoff
|
50
|
+
else
|
51
|
+
last_byte_at = Time.now
|
52
|
+
end
|
53
53
|
end
|
54
|
-
|
54
|
+
buffer
|
55
55
|
end
|
56
56
|
|
57
57
|
# Gracefully shuts down the communication channel with the robot.
|
@@ -62,4 +62,47 @@ class SpheroPwn::Channel
|
|
62
62
|
@port.close
|
63
63
|
self
|
64
64
|
end
|
65
|
+
|
66
|
+
# Connects to an RFCONN serial port.
|
67
|
+
#
|
68
|
+
# @param {String} rfconn_path the path to the device file connecting to the
|
69
|
+
# robot's Bluetooth RFCONN service
|
70
|
+
# @param {Number} timeout the number of seconds to retry connecting when
|
71
|
+
# getting EBUSY
|
72
|
+
# @return {Serial} the connected port
|
73
|
+
def connect(rfconn_path, timeout)
|
74
|
+
give_up_at = Time.now + timeout
|
75
|
+
port = nil
|
76
|
+
while port.nil?
|
77
|
+
begin
|
78
|
+
port = Serial.new rfconn_path, 115200, 8
|
79
|
+
rescue RubySerial::Exception => e
|
80
|
+
raise e unless e.message == 'EBUSY'
|
81
|
+
raise e if Time.now >= give_up_at
|
82
|
+
end
|
83
|
+
end
|
84
|
+
port
|
85
|
+
end
|
86
|
+
private :connect
|
87
|
+
|
88
|
+
# Creates a thread that reads data from a queue and writes it to an IO.
|
89
|
+
#
|
90
|
+
# The thread expects to pop String instances from the queue, and will
|
91
|
+
# write them to the IO. When the thread pops the :close symbol, it stops
|
92
|
+
# executing.
|
93
|
+
#
|
94
|
+
# @param {Queue} send_queue the queue that the tread will read data from
|
95
|
+
# @param {IO} io the IO that the data bytes will be written to
|
96
|
+
# @return {Thread} the newly created thread
|
97
|
+
def spawn_sending_thread(send_queue, io)
|
98
|
+
Thread.new do
|
99
|
+
loop do
|
100
|
+
bytes = send_queue.pop
|
101
|
+
break if bytes == :close
|
102
|
+
|
103
|
+
io.write bytes
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
private :spawn_sending_thread
|
65
108
|
end
|