Search code examples
swiftibeaconcore-bluetooth

How to scan only for beacons with CoreBluetooth


I used this function for scanning BLE around me, but I receive too much signals. I'd like to work with signals only from beacons.

func centralManagerDidUpdateState(_ central: CBCentralManager) {
    centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : NSNumber(value: true)])
}

I had used LightBlue and found out that my beacon has 5 services: 180A, FFA0, FFB0, FFF0 and 180F.

Then I tried this code, but no peripherals found with this service.

let serviceUUIDs = [CBUUID(string: "FFA0")]

func centralManagerDidUpdateState(_ central: CBCentralManager) {
    centralManager.scanForPeripherals(withServices: serviceUUIDs, options: [CBCentralManagerScanOptionAllowDuplicatesKey : NSNumber(value: true)])
}

And didDiscover method never called

func centralManager(_ central: CBCentralManager,
                    didDiscover peripheral: CBPeripheral,
                    advertisementData: [String : Any],
                    rssi: NSNumber)
{
    if peripheral.name == "MY_BEACON_NAME" {
        print("peripheral: \(peripheral)")
        print("rssi = \(rssi)")
    }
}

What I'm doing wrong?

P. S. I can't use CoreLocation for this project, only CoreBluetooth.


Solution

  • Depending on what kind of beacon format your hardware beacon is advertising, it may not be possible to detect with CoreBluetooth. CoreBluetooth can detect Eddystone formats and the AltBeacon format, but it cannot detect the iBeacon format.

    A few points to understand:

    1. Beacon formats don't necessarily include a GATT Service UUID, which is what you are filtering for when you use the line of code centralManager.scanForPeripherals(withServices: serviceUUIDs .... While Eddystone advertisements do include a Service UUID, iBeacon and AltBeacon do not because they are a different kind of BLE advertisement called a Manufacturer advertisement. Only GATT advertisements have Service UUIDs.

    2. Eddystone formats use the FEAA service UUID, and you can use the technique you describe to filter for this beacon type.

    3. You can only detect AltBeacon advertisements without a serviceUUID filter, because it does not have one.

    4. You can never detect iBeacon with CoreBluetooth, because Apple has special code filters that block his API from receiving these advertisements. You have no choice but to use CoreLocation for iBeacon detection on iOS.

    5. Many beacon hardware manufacturers also include some supplemental GATT Services that have their own Service UUIDS. These services perform functions like configuration or add on actions. But these have nothing to do with the beaconing functionality, and there is no way to know when (if ever) these Service UUIDs are advertised, and what it means if they are. These UUIDs will absolutely be different from one beacon manufacturer to the next. The Service UUIDs being detected by LightBlue are likely supplemental services, and for this reason they are simply not helpful to solving this problem.

    Bottom line: There is no easy way to use CoreBluetooth to filter for only the beacons you want. Your best bet is to write your own filtering software on top of CoreBluetooth and simply ignore the packets you don't care about.