-
-
Notifications
You must be signed in to change notification settings - Fork 157
Peripheral discovery API comparison
This page compares the APIs for discovering BLE peripherals on different platforms. This includes starting and stopping a scan, getting events for advertisements, and getting information about peripherals the system knows about in some way.
Calling StartDiscovery()
on an org.bluez.Adapter1
starts discovery. This appears to be an active scan; there is no way to explicitly request a passive scan.
Various options may be set with SetDiscoveryFilter(dict filter)
, e.g. to filter by service UUID, MAC address or name, or to choose whether duplicate service data and manufacturer-specific data are included. This is set per application but applied system-wide, so if two different applications set different discovery filters then BlueZ will discover peripherals which match either filter and return them to both applications. Thus an application must be prepared to see peripherals it didn't ask for.
BlueZ maintains a set of peripherals which it knows about as D-Bus objects implementing org.bluez.Device1
. These may be queried cheaply at any time (without talking to hardware) to get the latest information that BlueZ knows about them. The set of peripherals is system-wide and persistent, so will change in response to other applications using Bluetooth (e.g. starting scans, connecting to devices). It includes not just peripherals which are currently in range, but also those that were discovered at some point in the past, or that have been paired.
There are no events directly corresponding to advertisements, but it is possible to use standard D-Bus interfaces to subscribe to signals for new objects being added (via org.freedesktop.DBus.ObjectManager.InterfacesAdded
) or some properties being updated (via org.freedesktop.DBus.Properties.PropertiesChanged
).
CBCentralManager -scanForPeripheralsWithServices:options:
will start a scan. If serviceUUIDs
is nil
then it will scan for all peripherals, otherwise it will filter to ones with the given service UUIDs advertised. The options
control duplicate filtering. There is no way to explicitly choose between active or passive scanning.
A CBCentralManagerDelegate may be registered with the CBCentralManager
. This has several different methods for various events, but in particular centralManager:didDiscoverPeripheral:advertisementData:RSSI:
is called in response to advertisements. This includes the advertisement data (e.g. name, manufacturer data, service data), the current RSSI, and the CBPeripheral object used to connect to the peripheral and perform other operations on it.
CBCentralManager
also has two methods to retrieve peripherals directly: retrieveConnectedPeripheralsWithServices:
to get all connected peripherals with a particular set of service UUIDs, and retrievePeripheralsWithIdentifiers:
to look them up by ID. The latter could be used by an application to connect to a peripheral it has previously connected to without having to scan again.
Note that CoreBluetooth doesn't expose the MAC addresses of peripherals to applications, only the unique IDs which it assigns internally.
A scan can be started by creating a BluetoothLEAdvertisementWatcher
and calling Start
on it. A filter on advertisement properties or RSSI may be set. ScanningMode
can be set to active, passive or none. None appears to be an opportunistic mode, where advertisements will be received if other applications are scanning, but otherwise no scanning will happen.
The BluetoothLEAdvertisementWatcher.Received
event will be sent when an advertisement is received. The BluetoothLEAdvertisementReceivedEventArgs
include the MAC address, RSSI, timestamp, and the BluetoothLEAdvertisement
which in turn contains the name, manufacturer-specific data, and raw data sections from which we can decode the service data.
Alternatively, DeviceInformation.FindAllAsync
may be used to find devices matching a particular filter (e.g. all BLE devices). There are also interfaces to show a prompt to the user to let them select a device. The device enumeration sample lists some different approaches.
A BluetoothLEDevice
may also be constructed directly from a MAC address with BluetoothLEDevice.FromBluetoothAddressAsync
.
A BLE scan in the foreground may be started with BluetoothLeScanner#startScan(List<ScanFilter>,ScanSettings,ScanCallback)
. The ScanSettings
control duplicates and the power mode (duty cycle), among other things. There doesn't appear to be a way to explicitly choose between active and passive scanning.
Scanning without a filter will only work while the screen is on; to continue with the screen off a filter must be set.
To continue scanning while the application is not running, BluetoothLeScanner#startScan(List<ScanFilter>,ScanSettings,PendingIntent)
can be used instead, passing a PendingIntent
to start the application in response to a matching scan result.
Scan results are sent to the ScanCallback
or PendingIntent
, either individually or in batches. Each ScanResult
includes among other things the RSSI, the ScanRecord
with the name, manufacturer-specific data and service data, and the BluetoothDevice
which can be used to connect to the peripheral and perform other operations on it.
Individual peripherals can be retrieved by MAC address without a scan with BluetoothAdapter#getRemoteDevice(byte[])
, and all paired devices can be retrieved with BluetoothAdapter#getBondedDevices()
.