使用基于公布的服务UUID的扫描过滤器扫描BLE外设

时间:2019-11-06 13:50:04

标签: android bluetooth-lowenergy android-ble

我有一个自定义的BLE外设,可发布这样的数据:

enter image description here

换句话说,我的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的扫描过滤器如何工作的详细信息,并且外围设备必须满足哪些条件才能被过滤器接受?

2 个答案:

答案 0 :(得分:3)

我想出了使它起作用的方法。问题是我的过滤器位于serviceUuid上,我假设它查看的是在advertisedServices集合中通告UUID的外围设备。我的外围设备仅在其serviceData关联数组中将UUID作为密钥发布,因此我按如下所示切换到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 :(得分:0)

步骤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

} //结束扩展名