【问题标题】:Scanning for BLE peripherals with a scan filter based on advertised service UUID使用基于广告服务 UUID 的扫描过滤器扫描 BLE 外围设备
【发布时间】:2019-11-06 13:50:04
【问题描述】:

我有一个自定义的 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 的扫描过滤器如何工作的详细信息,以及外围设备必须满足哪些条件才能被过滤器接受?

【问题讨论】:

  • 如果您没有对扫描应用过滤器并读取所有设备,您是否能够通过检查他们的服务事后检测到您感兴趣的设备? (并不是说这是唯一的解决方案;只是好奇。)这会导致您的 BLE 使用被标记为未优化,但如果这对您来说不是问题,这可能是一个可行的解决方案。
  • 是的,如果我删除扫描过滤器,我可以看到我的设备并读取广告数据,但正如您指出的那样,此扫描被标记为未优化,我需要能够连续扫描也在后台,所以我想我需要扫描过滤器才能消除 Android 对后台扫描的一些限制。
  • 好的!我正在跟踪方法调用,看看是否能找到更多关于过滤是如何实现的......
  • 嗯,我被困在androidxref.com/9.0.0_r3/xref/system/bt/binder/android/…;不知道如何找到生成的存根的实现。
  • 您的 scanSettings 是什么样的?你也可以提供吗?

标签: android bluetooth-lowenergy android-ble


【解决方案1】:

我想出了如何让它工作......有点。问题是我的过滤器在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 也不是很有帮助。

我的猜测是扫描在后台工作就足够了(我还没有尝试过),但如果我可以限制回调被调用的次数并且我没有自己过滤。

【讨论】:

    【解决方案2】:

    步骤 1)

    let kServiceUART = CBUUID(string: "0x1800")
    
    var peripheralHeartRateMonitor: CBPeripheral?`
    

    第二步)

    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
    

    } // 结束扩展

    【讨论】:

    • 这是一个 iOS 解决方案的例子。问题是问题是关于 Android 的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 2021-05-14
    相关资源
    最近更新 更多