使用基于广告服务 UUID 的扫描过滤器扫描 BLE 外围设备
Scanning for BLE peripherals with a scan filter based on advertised service UUID
我有一个定制的 BLE 外围设备,可以像这样宣传数据:
换句话说,我的 BLE 外围设备在广告服务数据中广告与唯一标识符关联的服务 UUID,但它不会将该服务 UUID 添加到广告服务列表中,因为如果我这样做,我就没有空间了在 BLE 帧中添加电池电量。
在 iOS,我能够使用基于服务 UUID 的过滤器进行扫描并查看我的外围设备。但是在 Android 上,使用以下扫描过滤器,我没有看到我的外围设备:
val scanSettingsBuilder = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0L)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scanSettingsBuilder
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
}
bluetoothAdapter?.bluetoothLeScanner?.startScan(
arrayListOf(ScanFilter.Builder().setServiceUuid(ParcelUuid(UUID.fromString("00004865-726f-6e54-7261-636b2d475053"))).build()),
scanSettingsBuilder.build(),
leScanCallback
)
有没有人详细说明基于 serviceUUID 的扫描过滤器的工作原理,以及外围设备必须满足哪些条件才能被过滤器接受?
我想出了如何使它工作...有点。问题是我的过滤器在 serviceUuid
上,我假设它查看在 advertisedServices
集合中公布 UUID 的外围设备。我的外围设备仅将 UUID 作为其 serviceData
关联数组中的键进行宣传,因此我按如下方式切换到 serviceData
过滤器,现在我可以找到我的外围设备:
AsyncTask.execute {
val scanFilters = Settings.scannedBleServices.values.map {
ScanFilter.Builder().setServiceData(it, null).build()
}
val scanSettingsBuilder = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0L)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scanSettingsBuilder
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
}
bluetoothAdapter?.bluetoothLeScanner?.startScan(
scanFilters,
scanSettingsBuilder.build(),
leScanCallback
)
}
问题是现在过滤器太宽松了,因为我得到了周围每个外围设备的回调,即使是那些没有任何外围设备的回调 serviceData
,就像我根本没有指定过滤器一样。可能是因为我在过滤器中将 null
作为第二个参数传递给了 setServiceData
,因为我不知道还要在那里添加什么。 documentation 并没有什么帮助。
我的猜测是扫描在后台工作就足够了(我还没有尝试过),但是如果我可以限制调用回调的次数并且我没有这样做会更有意义'不用自己筛选了。
步骤 1)
let kServiceUART = CBUUID(string: "0x1800")
var peripheralHeartRateMonitor: CBPeripheral?`
步骤 2)
cbManger = CBCentralManager(delegate: self, queue: .main)
步骤 3)
extension GuidedStartOnPhoneVC: CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unsupported:
print("BLe Unsupported")
break
case .unauthorized:
print("BLe unauthorized")
break
case .poweredOff:
let alertMessgesInst = AlertMessages.sharedInstance
CommonUtils.showAlert(alertMessgesInst.actofit_Title, message: alertMessgesInst.trun_On_blueTooth)
break
case .poweredOn:
if isNewFirmWareOFImpulse {
let uuidString = StorageServices.readFromDefaults(key: Constants.userDefaultKeys.impulseUUID)
let uuid = UUID(uuidString:uuidString as! String )
let device = cbManger.retrievePeripherals(withIdentifiers: [uuid!])
peripheralHeartRateMonitor = device.first
peripheralHeartRateMonitor!.delegate = self
cbManger?.connect(peripheralHeartRateMonitor!)
}else {
let option:[String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: false)]
cbManger.scanForPeripherals(withServices: nil, options: option)
}
break
case .unknown:
print("BLe unknown")
break
default:
break
} // End Swith
} // End 'centralManagerDidUpdateState' function.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if isNewFirmWareOFImpulse {
peripheralHeartRateMonitor = peripheral
print("UUid of band is :- \(peripheralHeartRateMonitor?.identifier.uuidString)")
if impulseName == peripheral.name {
peripheralHeartRateMonitor!.delegate = self
cbManger.stopScan()
// STEP 6: connect to the discovered peripheral of interest
cbManger?.connect(peripheralHeartRateMonitor!)
} // End impulse condition
}else {
let keysArray = advertisementData.keys
if let tempImpulseName = peripheral.name {
print(impulseName + " and " + tempImpulseName )
if impulseName == tempImpulseName {
for key in keysArray {
if key == "kCBAdvDataManufacturerData"{
let manufactureData = advertisementData[key]
if let stringValue = manufactureData.debugDescription as? String {
var heartValue: String = String()
heartValue = stringValue
heartValue.removeLast()
heartValue.removeLast()
let last = heartValue.removeLast()
let secondLast = heartValue.removeLast()
let hR = String([secondLast, last])
if let value = UInt8(hR, radix: 16){
if Int(value) > 60 {
hrArray.append(Int(value))
}
} // End the value block
} // end of if 'stringValue' condition
} // end 'Key' if condition
} // End for each loop
} // End impulse condition
} // End pheripheral if condition
} // end version condition
} // End function 'didDiscover peripheral'.
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// STEP 8: look for services of interest on peripheral
peripheralHeartRateMonitor?.discoverServices(nil)
} // END func centralManager(... didConnect peripheral
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
print("didDiscoverService Error :- \(error!)")
}
for service in peripheral.services! {
print("Service: \(service)")
if service.uuid.uuidString == "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" {
print("Service: \(service)")
// STEP 9: look for characteristics of interest
// within services of interest
peripheral.discoverCharacteristics(nil, for: service)
}
}
} // END func peripheral(... didDiscoverServices
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print(characteristic)
if characteristic.uuid.uuidString == "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" {
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.uuid.uuidString == "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" {
peripheral.setNotifyValue(true, for: characteristic)
}
//
} // END for
} // END func peripheral(... didDiscoverCharacteristicsFor service
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
// print(characteristic.value!.hexString)
if !isOnRestScreen{
if let batteryLevelValue = characteristic.value {
var buffer = [UInt8](batteryLevelValue)
// print(buffer)
print("Array count is \(buffer.count) :- \(buffer)")
if buffer.count == 20 {
buffer.removeFirst(4)
//MARK:- get Frame Array from the Impulse.
let array1 = Array(buffer.prefix(upTo: 8))
// let array2 = Array(buffer.suffix(from: 8))
makeProceedArray(tempArray: array1)
// makeProceedArray(tempArray: array2)
}else {
print("\(characteristic.service)")
}
}
}
} // END if characteristic.uuid
func decodePeripheralState(peripheralState: CBPeripheralState) {
switch peripheralState {
case .disconnected:
print("Peripheral state: disconnected")
case .connected:
print("Peripheral state: connected")
case .connecting:
print("Peripheral state: connecting")
case .disconnecting:
print("Peripheral state: disconnecting")
}
} // END func decodePeripheralState(peripheralState
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// print("Disconnected!")
if error != nil {
print("didDisconnectPeripheral Error :- \(error!)")
}
// STEP 16: in this use-case, start scanning
// for the same peripheral or another, as long
// as they're HRMs, to come back online
cbManger?.scanForPeripherals(withServices: [kServiceUART])
} // END func centralManager(... didDisconnectPeripheral peripheral
} // 结束扩展
我有一个定制的 BLE 外围设备,可以像这样宣传数据:
换句话说,我的 BLE 外围设备在广告服务数据中广告与唯一标识符关联的服务 UUID,但它不会将该服务 UUID 添加到广告服务列表中,因为如果我这样做,我就没有空间了在 BLE 帧中添加电池电量。
在 iOS,我能够使用基于服务 UUID 的过滤器进行扫描并查看我的外围设备。但是在 Android 上,使用以下扫描过滤器,我没有看到我的外围设备:
val scanSettingsBuilder = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0L)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scanSettingsBuilder
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
}
bluetoothAdapter?.bluetoothLeScanner?.startScan(
arrayListOf(ScanFilter.Builder().setServiceUuid(ParcelUuid(UUID.fromString("00004865-726f-6e54-7261-636b2d475053"))).build()),
scanSettingsBuilder.build(),
leScanCallback
)
有没有人详细说明基于 serviceUUID 的扫描过滤器的工作原理,以及外围设备必须满足哪些条件才能被过滤器接受?
我想出了如何使它工作...有点。问题是我的过滤器在 serviceUuid
上,我假设它查看在 advertisedServices
集合中公布 UUID 的外围设备。我的外围设备仅将 UUID 作为其 serviceData
关联数组中的键进行宣传,因此我按如下方式切换到 serviceData
过滤器,现在我可以找到我的外围设备:
AsyncTask.execute {
val scanFilters = Settings.scannedBleServices.values.map {
ScanFilter.Builder().setServiceData(it, null).build()
}
val scanSettingsBuilder = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0L)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scanSettingsBuilder
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
}
bluetoothAdapter?.bluetoothLeScanner?.startScan(
scanFilters,
scanSettingsBuilder.build(),
leScanCallback
)
}
问题是现在过滤器太宽松了,因为我得到了周围每个外围设备的回调,即使是那些没有任何外围设备的回调 serviceData
,就像我根本没有指定过滤器一样。可能是因为我在过滤器中将 null
作为第二个参数传递给了 setServiceData
,因为我不知道还要在那里添加什么。 documentation 并没有什么帮助。
我的猜测是扫描在后台工作就足够了(我还没有尝试过),但是如果我可以限制调用回调的次数并且我没有这样做会更有意义'不用自己筛选了。
步骤 1)
let kServiceUART = CBUUID(string: "0x1800")
var peripheralHeartRateMonitor: CBPeripheral?`
步骤 2)
cbManger = CBCentralManager(delegate: self, queue: .main)
步骤 3)
extension GuidedStartOnPhoneVC: CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unsupported:
print("BLe Unsupported")
break
case .unauthorized:
print("BLe unauthorized")
break
case .poweredOff:
let alertMessgesInst = AlertMessages.sharedInstance
CommonUtils.showAlert(alertMessgesInst.actofit_Title, message: alertMessgesInst.trun_On_blueTooth)
break
case .poweredOn:
if isNewFirmWareOFImpulse {
let uuidString = StorageServices.readFromDefaults(key: Constants.userDefaultKeys.impulseUUID)
let uuid = UUID(uuidString:uuidString as! String )
let device = cbManger.retrievePeripherals(withIdentifiers: [uuid!])
peripheralHeartRateMonitor = device.first
peripheralHeartRateMonitor!.delegate = self
cbManger?.connect(peripheralHeartRateMonitor!)
}else {
let option:[String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: false)]
cbManger.scanForPeripherals(withServices: nil, options: option)
}
break
case .unknown:
print("BLe unknown")
break
default:
break
} // End Swith
} // End 'centralManagerDidUpdateState' function.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if isNewFirmWareOFImpulse {
peripheralHeartRateMonitor = peripheral
print("UUid of band is :- \(peripheralHeartRateMonitor?.identifier.uuidString)")
if impulseName == peripheral.name {
peripheralHeartRateMonitor!.delegate = self
cbManger.stopScan()
// STEP 6: connect to the discovered peripheral of interest
cbManger?.connect(peripheralHeartRateMonitor!)
} // End impulse condition
}else {
let keysArray = advertisementData.keys
if let tempImpulseName = peripheral.name {
print(impulseName + " and " + tempImpulseName )
if impulseName == tempImpulseName {
for key in keysArray {
if key == "kCBAdvDataManufacturerData"{
let manufactureData = advertisementData[key]
if let stringValue = manufactureData.debugDescription as? String {
var heartValue: String = String()
heartValue = stringValue
heartValue.removeLast()
heartValue.removeLast()
let last = heartValue.removeLast()
let secondLast = heartValue.removeLast()
let hR = String([secondLast, last])
if let value = UInt8(hR, radix: 16){
if Int(value) > 60 {
hrArray.append(Int(value))
}
} // End the value block
} // end of if 'stringValue' condition
} // end 'Key' if condition
} // End for each loop
} // End impulse condition
} // End pheripheral if condition
} // end version condition
} // End function 'didDiscover peripheral'.
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// STEP 8: look for services of interest on peripheral
peripheralHeartRateMonitor?.discoverServices(nil)
} // END func centralManager(... didConnect peripheral
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
print("didDiscoverService Error :- \(error!)")
}
for service in peripheral.services! {
print("Service: \(service)")
if service.uuid.uuidString == "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" {
print("Service: \(service)")
// STEP 9: look for characteristics of interest
// within services of interest
peripheral.discoverCharacteristics(nil, for: service)
}
}
} // END func peripheral(... didDiscoverServices
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print(characteristic)
if characteristic.uuid.uuidString == "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" {
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.uuid.uuidString == "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" {
peripheral.setNotifyValue(true, for: characteristic)
}
//
} // END for
} // END func peripheral(... didDiscoverCharacteristicsFor service
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
// print(characteristic.value!.hexString)
if !isOnRestScreen{
if let batteryLevelValue = characteristic.value {
var buffer = [UInt8](batteryLevelValue)
// print(buffer)
print("Array count is \(buffer.count) :- \(buffer)")
if buffer.count == 20 {
buffer.removeFirst(4)
//MARK:- get Frame Array from the Impulse.
let array1 = Array(buffer.prefix(upTo: 8))
// let array2 = Array(buffer.suffix(from: 8))
makeProceedArray(tempArray: array1)
// makeProceedArray(tempArray: array2)
}else {
print("\(characteristic.service)")
}
}
}
} // END if characteristic.uuid
func decodePeripheralState(peripheralState: CBPeripheralState) {
switch peripheralState {
case .disconnected:
print("Peripheral state: disconnected")
case .connected:
print("Peripheral state: connected")
case .connecting:
print("Peripheral state: connecting")
case .disconnecting:
print("Peripheral state: disconnecting")
}
} // END func decodePeripheralState(peripheralState
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// print("Disconnected!")
if error != nil {
print("didDisconnectPeripheral Error :- \(error!)")
}
// STEP 16: in this use-case, start scanning
// for the same peripheral or another, as long
// as they're HRMs, to come back online
cbManger?.scanForPeripherals(withServices: [kServiceUART])
} // END func centralManager(... didDisconnectPeripheral peripheral
} // 结束扩展