Reading RSSI of Bluetooth peripheral in background at short interval

An iPhone app that we are currently developing needs to read the RSSI of a peripheral with short intervals as soon as it has established a local connection using

CBCentralManager
, both in foreground and background. This means that setting
CBCentralManagerScanOptionAllowDuplicatesKey
to
true
when calling
scanForPeripherals(withServices:options:)
is out of the question since it is both ignored when an app is running in the background and it only enables RSSI updates via
CBCentralManagerDelegate
's
centralManager(_:didDiscover:advertisementData:rssi:)
method, at this point the peripheral is not connected yet.


The purpose of reading the RSSI value is the measurement of a physical approach to a smart lock that is currently being developed. The approach is divided into multiple phases, each phase being initiated by a certain distance to the lock, and we would like to determine the distance by using an RSSI value.


I implemented a characteristic in our peripheral that has a

notify
property and updates itself every 0.25 seconds. Within my app I use
CBPeripheral
's
setNotifyValue(_:for:)
method to get notified of the characteristic updates via
peripheral(_:didUpdateValueFor:error:)
. Within this method I call
peripheral.readRSSI()
and get an RSSI update via
peripheral(_:didReadRSSI:error:)
.


A simplified version of my code looks like this:


...
peripheral.setNotifyValue(true, for: characteristicUpdatingEveryQuarterOfASecond)
...

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {


     peripheral.readRSSI()
}

func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {

     print("Peripheral \(peripheral) read RSSI: \(RSSI)")
}


The problem is that the RSSI is only reported back roughly once every second through peripheral(_:didReadRSSI:error:), while peripheral.readRSSI() is being called every 0.25 seconds. The interval of roughly once every second is not sufficient for our app, the physical approach cannot be measured precisely enough in this way, ideally we would like to sample the RSSI at shorter intervals.


The RSSI value that comes back every second could have been useful for our app if it was relatively stable, but unfortunately this is not the case. We did a measurement with an iPhone 5s, the result can be found in this PDF:


https://drive.google.com/file/d/1Lqzs_37k4IvST3yAK9rpJIMExvXPQDrn/view?usp=sharing


This is a measurement over about three minutes with the iPhone 5s at 1 meter from the peripheral (a custom Mac app using Core Bluetooth running on a MacBook Pro). The difference between the lowest and highest dB value is 14, this is quite substantial (this is even after filtering out a few big spikes).


As an alternative we have looked into using iBeacons, but this is not an ideal solution since they can be easily spoofed. We did, however, do a measurement to see the stability of the RSSI value when using iBeacons (retrieved using the CLBeacon.rssi property). The result can be found in this PDF:


https://drive.google.com/file/d/1YuQamfICuBeYGLrQZAdPHtM0mNFWUtnD/view?usp=sharing


We noticed that the RSSI value is much more stable here, the difference between the lowest and highest dB value is only 3. The value is also coming back roughly once every second (this is the interval at which locationManager(_:didRangeBeacons:in:) is being called), but the low value fluctuation makes it much more useful.


In the end I have two questions:

  • Is there any way to decrease the interval at which peripheral(_:didReadRSSI:error:) is being called, or is roughly once every second a built-in limitation in the Core Bluetooth framework?
  • Is there a reason why the Core Location framework is able to do much more stable RSSI measurements? Is it somehow possible to achieve this stability when using the Core Bluetooth framework?


Thanks in advance!

Reading RSSI of Bluetooth peripheral in background at short interval
 
 
Q