scan_beacon 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d78009f7808d20ac2554c8c370a00ecd36b75e2e
4
- data.tar.gz: 1f1f238adbe3cfcc939f349e9e2c57636505b292
3
+ metadata.gz: 5ae0be9271595f436430f8494d6f0cb9710e8083
4
+ data.tar.gz: 7afac292c5b16a158134c0461ca483a07e506b3a
5
5
  SHA512:
6
- metadata.gz: 146ee276d74af008154a018ce72b0f410df1a294a1cf1f6383466cc06bbc2a37fa90e84272ceb9db70d9085f0491af86493091c7fb80d857dc44c31ca25e3912
7
- data.tar.gz: 9947d6aced47920d648f5f5390a1a693997bc24d2a3a35d04b9c1eb9d409b0b6b0797a2c1d948b21c2cac44e5f0b6975ca405e6e977e9ef6d80518c99a5f88ba
6
+ metadata.gz: bf44f28c567d9c4c221802aa4fa98b3fbcd00f0459faf60b4a8cb662107de7a84f1e2f9ef59ade343efed26edc1328976e046bf11ad0ba00b3a602c9b8562199
7
+ data.tar.gz: 791bd526ce8efaddf2b5085317e5d88024cadf2d676f4bfd61c30965bc01caa2d31d1417cb851200ddfea31cb460844bf3dcef7f74f966d37e55f50d3505eb84
data/README.md CHANGED
@@ -31,5 +31,15 @@ scanner.scan do |beacons|
31
31
  end
32
32
  ```
33
33
 
34
+ ## Add a custom beacon layout
35
+ By default, this gem supports AltBeacon advertisements. But you can add a beacon parser to support other major beacon formats as well.
36
+
37
+ Example:
38
+ ``` ruby
39
+ scanner = ScanBeacon::BLE112Scanner.new
40
+ scanner.add_parser( ScanBeacon::BeaconParser.new(:mybeacon, "m:2-3=0000,i:4-19,i:20-21,i:22-23,p:24-24") )
41
+ ...
42
+ ```
43
+
34
44
  # Dependencies
35
45
  You must have a BLE112 device plugged in to a USB port.
@@ -0,0 +1,117 @@
1
+ module ScanBeacon
2
+ class BLE112Device
3
+
4
+ # define a bunch of constants
5
+ BG_COMMAND = 0
6
+ BG_EVENT = 0x80
7
+ # msg classes
8
+ BG_MSG_CLASS_CONNECTION = 3
9
+ BG_MSG_CLASS_GAP = 6
10
+ # messages
11
+ BG_DISCONNECT = 0
12
+ BG_SET_MODE = 1
13
+ BG_DISCOVER = 2
14
+ BG_DISCOVER_STOP = 4
15
+ BG_SCAN_PARAMS = 7
16
+ # constants/enums
17
+ BG_GAP_DISCOVER_ALL = 2
18
+ BG_GAP_NON_DISCOVERABLE = 0
19
+ BG_GAP_NON_CONNECTABLE = 0
20
+
21
+ def initialize(port=nil)
22
+ @port = port || Dir.glob("/dev/{cu.usbmodem,ttyACM}*")[0]
23
+ end
24
+
25
+ def open
26
+ File.open(@port, 'r+b') do |file|
27
+ @file = file
28
+ yield self
29
+ end
30
+ @file = nil
31
+ end
32
+
33
+ def start_scan
34
+ # disconnect any connections
35
+ bg_command(@file, BG_MSG_CLASS_CONNECTION, BG_DISCONNECT,0)
36
+ # turn off adverts
37
+ bg_command(@file, BG_MSG_CLASS_GAP, BG_SET_MODE, [BG_GAP_NON_DISCOVERABLE, BG_GAP_NON_CONNECTABLE])
38
+ # stop previous scan
39
+ bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER_STOP)
40
+ # write new scan params
41
+ bg_command(@file, BG_MSG_CLASS_GAP, BG_SCAN_PARAMS, [200,200, 0], "S<S<C")
42
+ # start new scan
43
+ bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER, BG_GAP_DISCOVER_ALL)
44
+ end
45
+
46
+ def stop_scan
47
+ bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER_STOP)
48
+ end
49
+
50
+ def read
51
+ BLE112Response.new( bg_read(@file) )
52
+ end
53
+
54
+ class BLE112Response
55
+ def initialize(data)
56
+ @data = data
57
+ end
58
+
59
+ def size
60
+ @data.size
61
+ end
62
+
63
+ def event?
64
+ @data[0].unpack('C')[0] == BG_EVENT
65
+ end
66
+
67
+ def gap_scan?
68
+ @data[2..3].unpack('CC') == [BG_MSG_CLASS_GAP, 0]
69
+ end
70
+
71
+ def manufacturer_ad?
72
+ size > 20 && advertisement_type == 0xFF
73
+ end
74
+
75
+ def advertisement?
76
+ event? && gap_scan? && manufacturer_ad?
77
+ end
78
+
79
+ def advertisement_type
80
+ @data[19].unpack('C')[0]
81
+ end
82
+
83
+ def advertisement_data
84
+ @advertisement_data ||= @data[20..-1]
85
+ end
86
+
87
+ def mac
88
+ @data[6..11].unpack('H2 H2 H2 H2 H2 H2').join(":")
89
+ end
90
+
91
+ def rssi
92
+ @data[4].unpack('c')[0]
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def bg_command(port, msg_class, msg, data=nil, data_format=nil)
99
+ data = [data].compact unless data.is_a? Array
100
+ if data_format.nil?
101
+ data = data.pack('C*')
102
+ else
103
+ data = data.pack(data_format)
104
+ end
105
+ cmd = [0, data.size, msg_class, msg].flatten.pack('C*') + data
106
+ port.write(cmd)
107
+ bg_read(port)
108
+ end
109
+
110
+ def bg_read(port)
111
+ response = port.read(1) while response.nil? || ![0x00, 0x80].include?(response.unpack('C')[0])
112
+ response << port.read(3)
113
+ payload_length = response[1].unpack('C')[0]
114
+ response << port.read(payload_length)
115
+ end
116
+ end
117
+ end
@@ -2,17 +2,13 @@ require 'timeout'
2
2
 
3
3
  module ScanBeacon
4
4
  class BLE112Scanner
5
- SCAN_CMD = [0,1,6,2,2].pack('CCCCC')
6
- SCAN_PARAMS = [0, 5, 6, 7, 200,200, 0].pack('CCCCS<S<C')
7
- RESET_CMD = [0,1,9,0,0].pack('ccccc')
8
- MANUFACTURER_AD = 0xFF
9
5
 
10
6
  DEFAULT_LAYOUTS = {altbeacon: "m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"}
11
7
 
12
8
  attr_reader :beacons
13
9
 
14
10
  def initialize(opts = {})
15
- @port = opts[:port] || Dir.glob("/dev/{cu.usbmodem,ttyACM}*")[0]
11
+ @device = BLE112Device.new opts[:port]
16
12
  @cycle_seconds = opts[:cycle_seconds] || 1
17
13
  @parsers = DEFAULT_LAYOUTS.map {|name, layout| BeaconParser.new name, layout }
18
14
  @beacons = []
@@ -23,58 +19,33 @@ module ScanBeacon
23
19
  end
24
20
 
25
21
  def scan
26
- clear_the_buffer
27
- open_port do |port|
28
- send_scan_command(port)
22
+ @device.open do |device|
23
+ device.start_scan
29
24
  cycle_end = Time.now + @cycle_seconds
30
25
 
31
- while true do
32
- byte = port.each_byte.next
33
- append_to_buffer(byte)
34
- if Time.now > cycle_end
35
- yield @beacons
36
- @beacons = []
37
- cycle_end = Time.now + @cycle_seconds
26
+ begin
27
+ while true do
28
+ check_for_beacon( device.read )
29
+ if Time.now > cycle_end
30
+ yield @beacons
31
+ @beacons = []
32
+ cycle_end = Time.now + @cycle_seconds
33
+ end
38
34
  end
35
+ ensure
36
+ device.stop_scan
39
37
  end
40
- end
41
- end
42
38
 
43
- def open_port
44
- File.open(@port, 'r+b') do |port|
45
- yield port
46
39
  end
47
40
  end
48
41
 
49
- def append_to_buffer(byte)
50
- if @buffer.size == 0 && (byte == 0x00 || byte == 0x80)
51
- @buffer << byte
52
- @packet_type = byte
53
- elsif @buffer.size == 1
54
- @buffer << byte
55
- @payload_length = byte
56
- @expected_size = 4 + (@packet_type & 0x07) + @payload_length
57
- elsif @buffer.size > 1
58
- @buffer << byte
59
- check_for_beacon
60
- end
61
- end
62
-
63
- def check_for_beacon
64
- if @expected_size && @buffer.size >= @expected_size
65
- packet_class = @buffer[2]
66
- packet_command = @buffer[3]
67
- payload = @buffer[4..-1].pack('C*')
68
- if (@packet_type & 0x80 != 0x00) && (packet_class == 0x06) &&
69
- (packet_command == 0x00) && @buffer[19] == MANUFACTURER_AD
70
- data = payload[16..-1]
71
- beacon = nil
72
- if @parsers.detect {|parser| beacon = parser.parse(data) }
73
- beacon.mac = parse_mac(payload)
74
- add_beacon(beacon, parse_rssi(payload))
75
- end
42
+ def check_for_beacon(response)
43
+ if response.advertisement?
44
+ beacon = nil
45
+ if @parsers.detect {|parser| beacon = parser.parse(response.advertisement_data) }
46
+ beacon.mac = response.mac
47
+ add_beacon(beacon, response.rssi)
76
48
  end
77
- clear_the_buffer
78
49
  end
79
50
  end
80
51
 
@@ -88,35 +59,5 @@ module ScanBeacon
88
59
  beacon.add_rssi(rssi)
89
60
  end
90
61
 
91
- def parse_mac(payload)
92
- payload[2..7].unpack('H2 H2 H2 H2 H2 H2').join(":")
93
- end
94
-
95
- def parse_rssi(payload)
96
- payload[0].unpack('c')[0]
97
- end
98
-
99
- def clear_the_buffer
100
- @buffer = []
101
- @expected_size = nil
102
- end
103
-
104
- def send_scan_command(port)
105
- # disconnect any connections
106
- port.write([0,1,3,0,0].pack('CCCCC'))
107
- port.read(7)
108
- # turn off adverts
109
- port.write([0,2,6,1,0,0].pack('CCCCCC'))
110
- port.read(6)
111
- # stop previous scan
112
- port.write([0,0,6,4].pack('CCCC'))
113
- port.read(6)
114
- # write new scan params
115
- port.write(SCAN_PARAMS)
116
- port.read(6)
117
- # start new scan
118
- port.write(SCAN_CMD)
119
- port.read(6)
120
- end
121
62
  end
122
63
  end
@@ -1,3 +1,3 @@
1
1
  module ScanBeacon
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/scan_beacon.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "scan_beacon/version"
2
2
  require "scan_beacon/beacon"
3
3
  require "scan_beacon/beacon_parser"
4
+ require "scan_beacon/ble112_device"
4
5
  require "scan_beacon/ble112_scanner"
5
6
 
6
7
  module ScanBeacon
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scan_beacon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radius Networks
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-06-22 00:00:00.000000000 Z
11
+ date: 2015-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,7 @@ files:
66
66
  - lib/scan_beacon.rb
67
67
  - lib/scan_beacon/beacon.rb
68
68
  - lib/scan_beacon/beacon_parser.rb
69
+ - lib/scan_beacon/ble112_device.rb
69
70
  - lib/scan_beacon/ble112_scanner.rb
70
71
  - lib/scan_beacon/version.rb
71
72
  - scan_beacon.gemspec