iOS — Scan and Connect to a BLE peripheral in the background

Image for post
Image for post

Written by Charlie Bartel and Jeremy Gustine

Use Case

More information about ANGi is available here: https://www.specialized.com/us/en/stories/angi

Connecting to a device while the app is in the background

Declare the correct background mode

“While your app is in the background you can still discover and connect to peripherals, and explore and interact with peripheral data. In addition, the system wakes up your app when any of the CBCentralManagerDelegate or CBPeripheralDelegate delegate methods are invoked, allowing your app to handle important central role events, such as when a connection is established or torn down, when a peripheral sends updated characteristic values, and when a central manager’s state changes.”

Scan for device by Service UUID

public struct ImpactService {
public static let service = CBUUID(string: "AFD0FFA0-2A9E-41A9- B9DB-115A0E511DE4")
}

centralManager.scanForPeripherals(withServices: [ImpactService.service], options: scanOptions)

It is not working. Why not?

Inspecting the advertisement data with nRF Connect

Image for post
Image for post

The first item in the list is the ANGi device that we are looking for. We see some interesting information that is given to us during the scan, including the device name (SHS-2071), and our Service UUID (afd0ffa0–2a9e-41a9-b9db-115a0e511de4). We can tap on ‘RAW’ to check out the raw advertising data being returned to us.

Image for post
Image for post

Ok, this looks interesting, but what does it all mean? Let’s follow the link that nRF Connect provides in that screen.

Interpreting the advertisement data

  • 0x19 — Appearance
  • 0x01 — Flags
  • 0x07–128-bit Service Class UUIDs
  • 0x09 — Complete Local Name

The value for the Appearance type is 0x000 - it is completely empty. The value for the 128-bit Service Class UUIDs type is a hexadecimal representation of our service UUID. The value for the Complete Local Name type is 0x5348532D32303731 which translates to SHS-2071 if passed through a hexadecimal to ASCII converter. What does 0x06 mean for the Flags type? Luckily, nRF Connect provides us with that data:

Image for post
Image for post

Everything appears to look good. We are advertising our service UUID. So why can we not connect to our device while the app is in the background? Are the flags problematic? We ended up finding another BLE device that advertised the same flags and we were able to successfully discover the device while performing a background scan, so that ruled out that theory.

nRF Connect does not tell the whole story — digging into the packets

It is also possible to sniff the packets with a phone that has Android 4.4+ installed. For example, this link describes how to collect the data packets with a Google Pixel. In short, you must enable bluetooth logging and then kick off the bluetooth process that you would like to debug — in our case, using nRF Connect to scan for our ANGi device. This generates a btsnoop_hci.log file that we can inspect with Wireshark. Let's take a look at what the ANGi's advertisement packets look like with Wireshark.

The phone collects quite a bit of data, so we will have to sift through it to find what you are looking for. We are looking for the advertisement data for our ANGi device. We find it at this packet here:

Image for post
Image for post

We can see that the Event Type is listed as Connectable Undirected Advertising. This is the advertising packet that our ANGi device is broadcasting. This advertising packet includes the Appearance, Flags, and Device Name. Where is the Service UUID?

It turns out that it is in the next record in Wireshark:

Image for post
Image for post

This packet’s Event Type is Scan Response. This includes our Service UUID.

So it turns out that the raw data that we received in nRF Connect is actually a combination of the data from the Advertising and Scan Response packets.

Active vs Passive scanning

When not connected, Bluetooth devices can either advertise their presence by transmitting advertisement packets or scan for nearby devices that are advertising. This process of scanning for devices is called device discovery. There are two types of scanning; active and passive. The difference is that an active scanner can send a scan request to request additional information from the advertiser, while a passive scanner can only receive data from advertising device. Note that the terms discovery and scanning may be used interchangeably. The figure below shows the sequence where the scanner sends a scan request to an advertiser during an advertising event.

In other words, while the iOS device is in the foreground and Actively scanning, it will receive Advertising packets, send out Scan Requests, and receive Scan Response packets. In the background, the iOS device will only receive Advertising packets. Since our ANGI’s Service UUID is in the Scan Response, we finally have an explanation as to why we can discover other BLE devices while backgrounded but not our ANGi.

Fixing the problem!

An advertising packet can be up to 31 bytes of data. Each field (i.e. appearance, name, service UUID or similar) have a header of 2 bytes (length and type), meaning that the maximum user payload is 29 bytes.

So rather than just moving our Service UUID into the Advertising packet which already includes the Flags, Apperance, and Local Name, let’s move the Service UUID into the Advertising packet and move the Local Name to the Scan Response.

Once that change is made…it works! We are now able to successfully connect to our device while our app is in the background.

After that change, here is how the raw advertisement data looks in nRF Connect:

Image for post
Image for post

It looks the same! This is because nRF Connect is performing an Active scan and is combining the data from both the Advertisement data packet and the Scan Response data packet.

Here is how the Advertisement data packet now looks with the Service UUID included:

Image for post
Image for post

And here is how the Scan Response data packet looks with the Device Name:

Image for post
Image for post

In Summary

Kids, code, bikes, trails

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store