Class: ScanBeacon::BLE112Device

Inherits:
Object
  • Object
show all
Defined in:
lib/scan_beacon/ble112_device.rb

Defined Under Namespace

Classes: BLE112Response

Constant Summary collapse

BG_COMMAND =

define a bunch of constants

0
BG_EVENT =
0x80
BG_MSG_CLASS_SYSTEM =

msg classes

0
BG_MSG_CLASS_CONNECTION =
3
BG_MSG_CLASS_GAP =
6
BG_RESET =

messages

0
BG_DISCONNECT =
0
BG_SET_MODE =
1
BG_GET_ADDRESS =
2
BG_GAP_SET_PRIVACY_FLAGS =
0
BG_GAP_SET_ADV_PARAM =
8
BG_GAP_SET_ADV_DATA =
9
BG_DISCOVER =
2
BG_DISCOVER_STOP =
4
BG_SCAN_PARAMS =
7
BG_GAP_DISCOVER_ALL =

constants/enums

2
BG_GAP_NON_DISCOVERABLE =
0
BG_GAP_NON_CONNECTABLE =
0
BG_GAP_USER_DATA =
4
BG_GAP_CONNECTABLE =
2

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port = nil) ⇒ BLE112Device

Returns a new instance of BLE112Device.



38
39
40
# File 'lib/scan_beacon/ble112_device.rb', line 38

def initialize(port=nil)
  @port = port || BLE112Device.find_all.first
end

Class Method Details

.configure_port(port) ⇒ Object



53
54
55
56
57
# File 'lib/scan_beacon/ble112_device.rb', line 53

def self.configure_port(port)
  if RUBY_PLATFORM =~ /linux/
    system("stty -F #{port} 115200 raw -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke")
  end
end

.find_allObject



30
31
32
33
34
35
36
# File 'lib/scan_beacon/ble112_device.rb', line 30

def self.find_all
  self.reset_all
  possible_devices.select {|device_path|
    device = self.new(device_path)
    device.open{ device.get_addr } != nil
  }
end

.possible_devicesObject



131
132
133
# File 'lib/scan_beacon/ble112_device.rb', line 131

def self.possible_devices
  Dir.glob("/dev/{cu.usbmodem,ttyACM}*")
end

.reset_allObject



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/scan_beacon/ble112_device.rb', line 135

def self.reset_all
  # try to reset all the devices
  device_count = possible_devices.count
  possible_devices.each do |device_path|
    configure_port(device_path)
    File.open(device_path, 'r+b') do |file|
      bg_command(file, BG_MSG_CLASS_GAP, BG_DISCOVER_STOP, nil, nil, 0.5)
    end
    # open and close the file to clear the buffer
    File.open(device_path, 'r+b') {|file| file.sync}
  end
end

Instance Method Details

#get_addrObject



59
60
61
62
63
64
# File 'lib/scan_beacon/ble112_device.rb', line 59

def get_addr
  # use a timeout here because we're using this to detect BLE112s and if it
  # isn't one, it may not respond right away
  response = bg_command(@file, BG_MSG_CLASS_SYSTEM, BG_GET_ADDRESS,nil,nil,0.5)
  response[4..-1].reverse.unpack("H2:H2:H2:H2:H2:H2").join(":") if response && response.length == 10
end

#openObject



42
43
44
45
46
47
48
49
50
51
# File 'lib/scan_beacon/ble112_device.rb', line 42

def open
  response = nil
  self.class.configure_port(@port)
  File.open(@port, 'r+b') do |file|
    @file = file
    response = yield(self)
  end
  @file = nil
  return response
end

#readObject



116
117
118
119
120
# File 'lib/scan_beacon/ble112_device.rb', line 116

def read
  data = bg_read(@file, 0.2)
  return nil if data.nil?
  BLE112Response.new( data )
end

#resetObject



122
123
124
125
126
127
128
129
# File 'lib/scan_beacon/ble112_device.rb', line 122

def reset
  open do
    @file.write([BG_COMMAND, 1, BG_MSG_CLASS_SYSTEM, BG_RESET, 0].pack('C*'))
  end
  # give time for the device to reboot.
  # TODO: figure out a way that doesn't involve sleeping arbitrarily.
  sleep 1
end

#rotate_addrObject



104
105
106
107
108
109
110
# File 'lib/scan_beacon/ble112_device.rb', line 104

def rotate_addr
  # set peripheral into private mode is not needed, as the mac is rotated every time gap_set_mode is called
  bg_command(@file, BG_MSG_CLASS_GAP, BG_GAP_SET_PRIVACY_FLAGS, [1, 0])

  # set gap mode
  bg_command(@file, BG_MSG_CLASS_GAP, BG_SET_MODE, [BG_GAP_USER_DATA, BG_GAP_CONNECTABLE])
end

#start_advertising(ad_data, privacy = false) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/scan_beacon/ble112_device.rb', line 83

def start_advertising(ad_data, privacy = false)
  # disconnect any connections
  bg_command(@file, BG_MSG_CLASS_CONNECTION, BG_DISCONNECT,0)

  # set advertising interval 0x00A0 = 100 ms interval, 7 = all channels
  bg_command(@file, BG_MSG_CLASS_GAP, BG_GAP_SET_ADV_PARAM, [0xA0, 0x00, 0xA0, 0x00, 7])

  # set privacy mode (rotate bluetooth address)
  if privacy
    bg_command(@file, BG_MSG_CLASS_GAP, BG_GAP_SET_PRIVACY_FLAGS, [1, 0])
  end

  # add flags header
  ad_data = "\x02\x01\x06" + ad_data
  ad_data = [0,ad_data.size].pack("C*") + ad_data

  stop_advertising
  bg_command(@file, BG_MSG_CLASS_GAP, BG_GAP_SET_ADV_DATA, ad_data.unpack("C*"))
  bg_command(@file, BG_MSG_CLASS_GAP, BG_SET_MODE, [BG_GAP_USER_DATA, BG_GAP_CONNECTABLE])
end

#start_scanObject



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/scan_beacon/ble112_device.rb', line 66

def start_scan
  # disconnect any connections
  bg_command(@file, BG_MSG_CLASS_CONNECTION, BG_DISCONNECT,0)
  # turn off adverts
  bg_command(@file, BG_MSG_CLASS_GAP, BG_SET_MODE, [BG_GAP_NON_DISCOVERABLE, BG_GAP_NON_CONNECTABLE])
  # stop previous scan
  bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER_STOP)
  # write new scan params
  bg_command(@file, BG_MSG_CLASS_GAP, BG_SCAN_PARAMS, [200,200, 0], "S<S<C")
  # start new scan
  bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER, BG_GAP_DISCOVER_ALL)
end

#stop_advertisingObject



112
113
114
# File 'lib/scan_beacon/ble112_device.rb', line 112

def stop_advertising
  bg_command(@file, BG_MSG_CLASS_GAP, BG_SET_MODE, [BG_GAP_NON_DISCOVERABLE, BG_GAP_NON_CONNECTABLE])
end

#stop_scanObject



79
80
81
# File 'lib/scan_beacon/ble112_device.rb', line 79

def stop_scan
  bg_command(@file, BG_MSG_CLASS_GAP, BG_DISCOVER_STOP)
end