iOS Bluetooth UUID changes after a few app launches on 8.2 - ios

I have an app that connects to a CC2541 over Bluetooth Low Energy. I simply want to cache something unique to each device so a user only connects to "their" device. I've tried using
- (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers
and even scanning and comparing the UUID's of the scanned peripherals, but I have to select a newly generated UUID every few app launches. The same code worked well on 8.1 and I rarely (if ever) had to select a new UUID.
I've debated checking the System ID (2A23 uuid) in the Device Information service, but that requires a connection to check the address so I'd have to connect to each nearby peripheral, get and compare the System ID, then either stay connected or disconnect and check the next one.
The CC2541 code I'm running isn't using pairing and I'd like to keep it that way. I'm curious if anyone else has found a solution to this situation.

Having been down this road, spending weeks to find a work-around to the limitations, the only way I have found is if you have control over the hardware and can put your own unique identifier in the advertising packet. While one can make all kinds of assertions about the (in)sanity of this, it is what it is.

I just want to post some code here that may help others in the future.
Put this somewhere outside of a function:
__xdata __no_init uint8 BT_MAC_ADDR[6] # 0x780E;
Put this in your advertData array:
7, //length of data
GAP_ADTYPE_MANUFACTURER_SPECIFIC, //Custom data
0,0,0,0,0,0, //Placeholder bytes
And finally update the advertData before you call GAPRole_SetParameter.
uint8 advertSize = sizeof(advertData);
memcpy(&advertData[advertSize-6], BT_MAC_ADDR, 6);

Related

New ibeacons not detected in my app

I am developing a beacon detection application and at the moment works very well for kontakt brand beacons.
The client has sent me some new beacons that i have never seen and I have searched with google but i can't find what brand they can be.
The following information related to this beacon they gave me:
uuid: fda50693-a4e2-4fb1-afcf-c6eb07647825
pass: 000000
name: ion_beacon00021
major: 16789
minor: 24532
I have transformed my code to monitor both uuid without making it work
for (index, beaconID) in beaconsIds.enumerated() {
let beaconUUID = UUID(uuidString: beaconID)!
let identifier = "BLERegionBeacon \(index)"
let beaconRegion = CLBeaconRegion(proximityUUID: beaconUUID, identifier: identifier)
self.locationManager?.startMonitoring(for: beaconRegion)
self.locationManager?.startRangingBeacons(in: beaconRegion)
}
func configureLocationManager() {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
self.locationManager!.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager!.distanceFilter = kCLDistanceFilterNone;
self.locationManager!.activityType = CLActivityType.automotiveNavigation;
for region in self.locationManager!.monitoredRegions {
self.locationManager?.stopMonitoring(for: region)
}
if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways {
self.locationManager?.requestAlwaysAuthorization()
}
self.locationManager!.startUpdatingLocation()
}
When not finding how to make this new ibeacon work I have resorted to third party applications (like MyBeacon, Detector, Beacon Demonstrator, Locate) to get validate if the information I have of this beacon is correct, but in no application I have been able to make it detect this ibeacon.
I bought a new battery to make sure it was not as simple as that, but neither. :'(
I had the idea to use any application that detects bluetooth devices and I found BLE Scanner. With this application i was lucky and could realize that at least the name that i have of the beacon is correct, but not found any related to major or minor or pass. I also thought that the long id seen there could be correct and that the I had was not... But I discarded it because when I connected the brand kontakt also gave me a different uuid.
ISCONNECTABLE Jumps between YES and NO.
When i use BLE Scanner to detect beacons Kontakt show me addional info called SERVICEDATA and ISCONNECTABLE still in YES.
- Is there anything else that needs to be done to detect these beacons?
- Anyone have any idea what may be going on?
Thank you very much
EDIT
Following the help of davidgyoung, I could realize that the Locate application if it works but only in its version for android. And in fact correctly returns the information I had been given from that beacon at first.
I found that these beacons with configurable using an application called BeaconFlyer and there I realized the reason for being that password that had sent me. I was able to change the uuid although strangely I just put numbers and no letters. I changed it by 32 zeros following the same pattern of 8-4-4-4-12. I still get no results in IOS but works on android, even after changing the uuid.
It is possible that some brands need to be certified to be detected by iOS devices and that the android do not have these restrictions.
EDIT 2
Considering all these details, it seems that the problem is IOS and not android, so it occurred to me that maybe if I created a simple android project just to test the functionality of the beacons, I should be able to detect it.
The test was done by registering this UUID f7826da6-4fa2-4e98-8024-bc5b71e0893e which is the one that has always works for me and with which I detect without any problem the beacons markkontakt.
To my surprise I detect it without problem even without registering the UUID that I have for this brand. It is very strange considering that only register 1 UUID that was the mark kontakt
If you are detecting another beacon with the same code, then the simplest explanation is that the ProxmityUUID you were given is wrong.
You won't be able to determine the correct ProximityUUID from BLE Scanner on iOS, as iOS blocks access to iBeacon advertisements unless you know the ProximityUUID up front. If you can get your hands on an Android device you can use the Locate app to see any beacon regardless of its identifiers. https://play.google.com/store/apps/details?id=com.radiusnetworks.locate&hl=en You can use this to find out what it is actually sending. There are similar apps for MacOS as well if you have access to a Mac.
It is quite clear that your client sent you "beacons" and not necessarily "iBeacons". As davidyoung pointed out, what constitutes an iBeacon is specified (quite strictly) by Apple. Download the specs from there and have a look, if you are so inclined.
I have used several BLE scanners to check what uuid, major, and minor numbers various brand iBeacons I worked with have, among them the mentioned BLE scanner. I think the specification does not prevent somehow putting additional services into an iBeacon, which results in different brand iBeacons potentially showing slightly different info in different scanners.
However, what you show in the screenshot and the fact that the isconnectable field switches values make me think that these beacons are not correctly working.
There will be no way to make them work on iOS that way then. You can write your own Bluetooth protocol code to do stuff with them, but that then won't rely on Apple's iBeacon mechanics. This will mean the nice stuff like region monitoring even while the app is suspended and so on won't work. It's also probably more battery draining and in general more work.
I am as said not a Bluetooth expert, but learned this much: UUIDs are all over the specs. The UUID you (and Apple) refer to is a part of the data packet the iBeacon is supposed to send, but I have also seen BLE services in general being identified by UUIDs in (non-Apple) reference documents. This can get confusing when you scan such a beacon and see UUIDs (or at least strings that look like ones) in several places. So even if the beacons you have follow the spec (or can be configured to do so somehow) my guess is that someone screwed up. For example, they could have edited (maybe even with a hardwired connection and tool) the wrong UUID, not setting the one you need to use, but accidentally overwriting the part that specifies the device as an iBeacon (byte 5 and 6 of the data packet come to mind), making it impossible for you to use them.
As an advice I would suggest the following: If your client is interested in actual iBeacon functionality, tell them they should buy correct beacons. the kontakt ones are actually quite good. I've tested cheap china knockoffs and would advise against those, as they quickly become unreliable. It might be tempting to save a few bucks at first, but if you need to constantly replace beacons in the long run that becomes more expensive in the end.
One other theory about what might be happening:
A beacon advertisement packet implementing iBeacon must have Apple's manufacturer code 004c in order to be detected by iOS. Android has no such restriction. If this beacon is sending a different manufacturer code it would explain why only Android can detect it.
How do you find out if this is true? You need to see the raw bytes of the advertisement somehow.
The beacon brand is Iotton and it's looks like the one we sell:
https://www.beaconzone.co.uk/ibeacon/ton9108
It's unlikely the Kontakt app is showing the Iotton beacon because the local name is incorrect. It's more likely your Kontakt beacon - turn it off for now. With this or any beacon, the best app for getting the UUID and seeing if the beacon is working is Nordic nRF Connect.
Are you sure you have set the delegate on CLLocationManager? The code doesn't show it, so unless this is somewhere above or below, you'll need to add:
self.locationManager?.delegate = self
Then make sure your class implements CLLocationManagerDelegate the didRange(beacons: region:) method to get callbacks.
If you think you already have this set up, showing that code might reveal something else that is missing preventing it from working.

Pairing two BLE Devices with the same UUID (with CoreBluetooth)

I have two BLE devices with the same UUID. Is there any way to connect to both of them so that they are both connected at the same time?
Right now I'm not able to even see both devices simultaneously using a bluetooth explorer, so I assume for the system they appear as just one device. Is it still somehow possible?
I assume that with UUID you mean the MAC-Address (Bluetooth address) of the device. UUIDs are typically used for services and characteristics.
All connection procedures work with the Bluetooth address as the main parameter. So two devices with the same address will be treated as the same device. (In fact, it is a common requirement that MAC-Addresses of Bluetooth devices should be unique, exactly for this reason.)
So it is not possible to connect to two devices with the same address at the same time. (*)
You can find more information about how connection is done in the Bluetooth Core Spec, Vol. 3, Part C, chapter 9.3.5 - 9.3.8.
(*) This is meant in the common way of having a connection to two different devices. Since incoming messages are not distinguishable on Link Layer level between the two devices with identical address (your control will always think there is only one device with the given address out there), you could mess around with this fact, especially in non-encrypted connections (since encryption is negotiated on a device-to-device basis, another device could not join in an encrypted connection, even if it has the same Bluetooth address). But it is definitely not recommended to do so.
If two devices are having the same UUID , then I think they would be consider as a single device. What I would suggest , send a trigger notification to the UUID and check which device will respond first . Secondly , try to send notification to devices put them far apart . The nearer to your smartphone/bluetooth explorer , will get notify first . Bluetooth works on the distance basis, the device which is near to the source will get the notification than the other one.I tried the same with Beacons but end up like you.

Trying to retrieve previously paired bluetooth device in IOS app will not respond with failure if device is off

Sorry for the long title, but we are having a pretty interesting issue with using corebluetooth for ios. We are issuing a call to retrievePeripherals in CBCentralManager and are able to find the previously paired device.
This happens though regardless if the device is on or off though. I can't find anything in apple's documentation as to why it's able to find the device when it is off though and it isn't showing up in Settings -> Bluetooth -> Devices. I'm suspecting that Apple is caching this information but can't find any documentation to confirm this. Also, when the device is off and we issue the connect call, the program continues to execute as normal but the delegate for didFailToConnect never gets called. When the device is turned on, it will connect immediately.
Is there a way to pass a timeout parameter when trying to connect to a device? If not, what would the best solution be to handling reconnecting to a previously used device for an application (we're storing the last connected device within the app).
Two points you need to know about retrievePeripherals: and connectPeripheral:
1.) retrievePeripherals: attempts to retrieve the CBPeripheral object associated with the uuid you supply. Even if the ble device is off (or on the other side of the country) retrievePeripherals: will still return an instance of CBPeripheral that you can call connectPeripheral: on. This is done intentionally so that you can issue a call to a peripheral that is not even around and still automatically connect to it when it comes back into range. It basically creates a marker inside the system bluetooth so that when the device is actually seen, it will know it should connect to it.
2.)connectPeripheral: will not time out unless the communication channel is broken with the actual device. If the iOS device has not seen the device, it will not fail and should not time out (unless some error occurs inside the system bluetooth).
And as for the timeout parameter, there is no documented way inside the CoreBluetooth framework. You can create your own implementation for it, however I believe you'd be better off keeping a list of which peripheral uuid's you've actually called connectPeripheral: on and then just pop them from the list when they connect. If you no longer want to connect to a peripheral in the list call cancelPeripheral: on that UUID, call connectPeripheral: on the other, and swap entries. Good to go.

CoreBluetooth: What is the lifetime of unique UUIDs

Following off of this question:
Corebluetooth, How to get a unique UUID?
After connecting to a peripheral, I can retrieve its CFUUIDRef, which is unique, and can use it to reconnect via retrievePeripherals. However, I have found that I can not use it to reconnect after closing the application.
Is the unique UUID generated after a connection only valid for the lifetime of the application or are we supposed to be able to save it for later use? I am trying to have my application remember one particular peripheral, but besides peripheral.UUID, I don't know of any other unique identifiers.
Long story short, YES you can use the UUID to reconnect to the same device even after you close the application (in exactly the way you say).
I assume however, that you are not actually pairing with peripheral. That's a big problem right there. You need to actually establish the pairing request and get the peripheral to show up in the bluetooth table. The UUID will then be solidified with the iOS device and will remain until you flush the Network Settings of the iOS device.
The other possibility is that your BLE device has a firmware problem, wherein after you disconnect, it forces itself into advertising mode or something. This will also mess with your ability to reconnect. Let me know if you have any questions!

bluetooth low energy advertising packet and topology

I want to advertise a single id lets say "stackoverflow1" on a ble device. So people close to the sensor can get this message (welcome to wwdc2012) as popups on the iPhone. That's it! meaning that there is no update on the value or anything else in other words I just want to know which room I came in. In another room there's another sensor adversing "stackoverflow2".
Now the question is, should I put the rooms' sensor (advertiser) as Peripherals and visitors' iPhones as Centrals?
If your answer is yes, can I send this Id in advertising packet? i.e. can I skip connection to the room's peripheral? Please guide me a little but on this
Thanks
Yes, the iPhones should be centrals and the in-room device should be a peripheral. It's perfectly fine to put enough data in the advertisement that the iOS app can do something useful after simply seeing the peripheral advertised without actually connecting to it. That way, multiple centrals/phones can detect proximity at once without a single phone tying up an exclusive connection to the device. Instead of specifying the room in the service name, you should be able to put it in the advertisement data for the device, giving them all the same service name. That way the iOS app doesn't need to know the completely list of rooms (i.e. services) in advance and they can be added to without changing the app.
In short, the gist of what you described should work fine, and seems like a sound approach to me.
You can include information in the advertisement from the peripheral as "Manufacturer Specific" data. Then the iOS application can get it from the advertisement data dictionary using the CBAdvertisementDataManufacturerDataKey.

Resources