I am working with Bluetooth Low Energy devices, and I was wondering whether it is possible to read the Scan Response Data to an advertisement with iOS and Core Bluetooth without connecting.
I understand that after reading an advertisement packet, you can request additional data from the peripheral in the format of a 31 byte scan response. I know that Core Bluetooth suggests that if the ad packet is full, you can put the local name in the scan response packet, but does it allow you to see the whole packet?
Yes, you can use CoreBluetooth to read the full manufacturer data or service data bytes of a BLE advertisement as long as it is NOT an iBeacon advertisement. If it is an iBeacon advertisement, CoreBluetooth will block your ability to see the bytes. The callback you use is as follows:
- (void) centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
The raw service data or manufacturer data bytes will be present inside the NSDictionary *advertisementData. But they key holding those data will be removed by the operating system for iBeacons.
Here's an example of what you get in the advertisementData NSDictionary in the callback. This example is for detecting an AltBeacon advertisement (an open-source beacon standard), with identifiers 2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6 1 2
{
kCBAdvDataIsConnectable = 0;
kCBAdvDataManufacturerData = <1801beac 2f234454 cf6d4a0f adf2f491 1ba9ffa6 00010002 be00>;
}
You can see how to decode the above bytes by looking at the AltBeacon spec here.
For more details about why you can't read iBeacon data along with additional code showing how you set this up, see here:
http://developer.radiusnetworks.com/2013/10/21/corebluetooth-doesnt-let-you-see-ibeacons.html
I am working with a peripheral that has some manufacturer data which I believe is transmitted in the scan response because there's no room for it in the initial advertisement with a 128-bit UUID plus channel, RSSI, and connectable flag. I am receiving two calls to didDiscoverPeripheral:... in quick succession (3ms apart including some handling time in my code). The first does not have the kCBAdvDataManufacturerData key in the dict, but the second does. I am assuming that the scan response is being requested automatically and the reply results in the second call.
Related
I'm working on with BLE of iOS where in i could able to establish a peripheral advertisement and scanning at the same time.
I have 2 devices where in both would scan advertise at the same time, it works fine when the devices are in foreground.
Now the issue is: The device is in background meaning the advertisement is not working(not able to show the Local Name and UUID as it will mask as per Apple) because as per Apple Docs:
advertisement key are placed in a special “overflow” area; they can be discovered only by an iOS device that is explicitly scanning for them.
Now my question how can i scan for this overflow area?
Also note when a device is advertising in background i do not get a call back in below delegate too:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
Any possible way i can do this approach, which would really helpful.
The Solution to the above scenario is as per #Paulw11 said.
We just need to scan for specific Peripheral Id.
Thanks Paulw11
I am working with the Core Bluetooth API in iOS 8.4. My code is interacting with a BLE device that is programmed to be an iBeacon. Here is a logging statement produced by my implementation of the CBCentralManager protocol's didDiscoverPeripheral callback function:
2015-09-04 16:23:08.231 CentralManager - Did discover peripheral [CBPeripheral: 0x1742e0b80, identifier = 8B63C7F8-44D5-F3E2-AD40-2916A2513BA9, name = DfuTarg, state = disconnected] with data [[kCBAdvDataIsConnectable: 0]]
The logging statement prints the value of the function's peripheral and advertisementData arguments. Note that the name of the peripheral is printed (BTW: I happen to know that DfuTarg is the correct name of the device being scanned).
From where does iOS acquire this name? It is not part of the Advertising Packet. It must come from a secondary Scan Request / Scan Response exchange, yes? Is it possible for my code to access the Scan Response?
Yes, the CBPeripheral name comes from the scan response. CoreBluetooth automatically requests the scan response data (at least when the app is in the foreground) when an advertisement is detected.
The didDiscoverPeripheral method is called with information from both the initial advertisement and the scan response when both have been received by the device. Essentially, you are already accessing the scan response info by using that callback method.
What's important to note is that you do not have access to raw manufacturer data in this callback when the advertisement is recognized as an iBeacon. iOS strips it out so you can't access it. This is true for the manufacturer data bytes present in the advertisement and in the scan response.
See here for more info.
I'm creating an app that gets the UUID of all BLE devices within range. I've gotten it working fine in Android, where it gives me the advertisement data as a byte array. Now I'm trying to do the same in iOS.
I'm running a scan and it's detecting the device, and in the callback there's an NSDictionary called advertisementData. But the only information in it is as follows:
kCBAdvDataChannel = 39;
kCBAdvDataIsConnectable = 1;
kCBAdvDataLocalName = jaalee;
kCBAdvDataTxPowerLevel = 0;
Am I right in thinking I should be getting the entirety of the advertising packet? If not, how can I get it?
Unfortunately, iOS does not allow you to access the raw advertisement data. I wrote a blog post demonstrating this. While the post is specifically about iBeacons, it applies to any BLE advertisement.
EDIT: To clarify, you can read the raw manufacturer data bytes or service data bytes of non-iBeacon advertisements. It is only the iBeacon advertisements that have their manufacturer data bytes hidden by CoreLocation. See here: Obtaining Bluetooth LE scan response data with iOS
The equivalent MacOS CoreLocation methods do allow this, so it is probably an intentional security or power saving restriction on iOS.
Based on official documentation from Apple iOS and my personal experience:
YES, iOS does not allow you to access the RAW advertisement data.
BUT
If your intention is to put information in advertising packet and read them from the iOS app without connect with the peripheral, this is possible to do.
Here is described how:
1) in the peripheral firmware you have to insert your manufacturer specifica data in the advertising packet, with the data type GAP_ADTYPE_MANUFACTURER_SPECIFIC (0xFF)
Remember that in the Manufacturer Specific Data, first 2 octets contain the Company Identifier Code followed by the additional manufacturer specific data
2) in the iOS
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
You can see the manufacturer specific data in advertisementData dictionary with key:
CBAdvertisementDataManufacturerDataKey
3) if you want that in your App receive a callback notification every advertising packet sent by peripheral to iOS, remember to change the scan option to YES. Look at this post about that: Core Bluetooth - constant RSSI updates of in-range devices
In my blog post will be soon a tutorial: http://www.megabri.com/
Is it possible to calculate the distance between the device and the iPhone, or at least, have an idea of the strength so I can check if the distance is getting smaller or bigger as I move. I would like to build some sort of system which makes it easier to find "lost" things in a close perimeter.
Is such thing possible? And if so, what is the best way to do this.
I assume that there is no obstruction between the two devices.
Yes, absolutely!
Actually it's basic functionality provided by Core Bluetooth. To reference a link provided by a previous answer:
As soon as a Peripheral is discovered during the scanning, the Central
delegate receives the following callback:
(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber
*)RSSI
This call notifies the Central Manager delegate (the view controller)
that a peripheral with advertisement data and RSSI was discovered.
RSSI stands for Received Signal Strength Indicator. This is a cool
parameter, because knowing the strength of the transmitting signal and
the RSSI, you can estimate the current distance between the Central
and the Peripheral. So, you can use the distance as a filter for a
given service: only if the Central is close enough to the Peripheral,
then your app does something.
The RSSI value allows you to estimate the distance, create virtual fences and can also be used to zero-in on a BLE device by comparing the new RSSI measurement to the previous one.
Can I measure the signal strength of Bluetooth devices within range of my iPhone?
Basically what I want to do is scan for a list of devices within range, and then see which one has the highest signal strength.
Is it possible in iOS and how would I do it if so?
Yes there is a way to measure the signal strength for Bluetooth Low Energy (4.0) it is the RSSI number. When you scan for peripherals you will set the flag CBCentralManagerScanOptionAllowDuplicatesKey to YES as shown below:
NSDictionary * dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#YES, CBCentralManagerScanOptionAllowDuplicatesKey, nil];
// Start scanning for peripherals
[cmanager scanForPeripheralsWithServices:nil options:dictionary];
If you want to see the RSSI number work without writing any code you should check out the app LightBlue in iTunes. When you connect to a peripheral it will show you the updated RSSI number every second when it is connected.
Have a look to the CoreBluetooth documentation:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{ ... }
RSSI is what you are looking for. Disclaimer: Core Bluetooth is made for Bluetooth 4 LE only.
If the exact range does not matter, but you are interested in scanning devices which are available in general, you may have a look to the github project BeeTee, which allows you to scan for all Bluetooth devices around you (not only Bluetooth LE). Again disclaimer: I am the author of BeeTee. ;-)
When you're coding your 'central' code using CBCentralManager, you'll eventually connect to the CBPeripheral you're looking for. Once you're connected to the peripheral, keep a reference to it, set your object as the peripheral's delegate and invoke 'readRSSI' on the peripheral. You'll get a delegate callback peripheral: didReadRSSI: error: If you write a method that invokes 'readRSSI', you can invoke it using performSelector: withObject: afterDelay:.
Another of the suggested answers to this question is to supply the 'allow duplicates' key when scanning. The docs for the dictionary key CBCentralManagerScanOptionAllowDuplicatesKey when passed to scanForPeripheralsWithServices:options: indicates that "Disabling this filtering can have an adverse effect on battery life and should be used only if necessary".
If you choose to write a delayed invocation, you can tune the frequency of the calls to help you manager the impact on your users' batteries.