Skip to content

Peripheral discovery API comparison

Andrew Walbran edited this page Mar 14, 2021 · 6 revisions

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.

BlueZ (Linux)

Start a scan

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.

Get peripherals

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).

CoreBluetooth (MacOS and iOS)

Start a scan

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.

Get peripherals

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.

UWP (Windows)

Start scan

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.

Get peripherals

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.

Android

Start scan

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.

Get peripherals

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().