iOS 6.0 Core Bluetooth Peripheral reconnect issue - ipod-touch

I have a stable working app in iOS 5.0 running on iPhone 4S. We did the migration to iOS 6.0 to run on iPod (5th gen) having bluetooth low energy. On iPhone 4S we were able to pair/bond, connect, disconnect and reconnect at will. We are having issues doing the same on iPod. The app disconnects successfully and reconnects and the process can be repeated 2-3 times, and after this we start getting Core Bluetooth Error : 14. Here's how the log looks like this
012-12-04 11:30:24.293 Milan[524:907] DISCONNECTED WITH READER: SriRdr11
2012-12-04 11:30:24.295 Milan[524:907] Start Time 376331424.295754 =
2012-12-04 11:30:28.241 Milan[524:907] NSConcreteNotification 0x1dd607d0
2012-12-04 11:30:28.243 Milan[524:907] MAIN PERIPHERAL: 5F953B12-D99F-D770-4F3F-B741BA026858
2012-12-04 11:30:28.617 Milan[524:907] CONNECTED WITH PERIPHERAL: DEVICE1
2012-12-04 11:30:29.173 Milan[524:907] DISCONNECTED WITH PERIPHERAL: DEVICE1
2012-12-04 11:30:29.176 Milan[524:907] Error: The specified device has disconnected from us. -
(null)
The app thinks that the BLE peripheral has disconnected from it, and the peripheral thinks that the app has disconnected from the reader. Quite clearly, it is the app that is doing something weird here. Can someone explain what's going on here?
Here's the code in the didDiscoverPeripheral that gets called for the reconnect.
NSString* uuid = [peripheral uuidString]; NSString* readerName = [advertisementData
objectForKey:#"kCBAdvDataLocalName"];
int meterID= 0;
NSData *deviceIdData = [NSData dataWithBytes:&meterID length:sizeof(meterID)];
NSLog(#"DEVICE DISCOVERED: %# (%#)", deviceName, uuid);
[[[DeviceBluetoothManager sharedInstance]allPeripherals] setObject:deviceIdData forKey:peripheral
uuidString]];
[[DeviceDefaultsManager sharedInstance] adddeviceIdToFavorites:deviceIdData
readerName:readerName withPeripheralId:[peripheral UUID]];
NSDictionary* deviceInfo = [NSDictionary dictionaryWithObject:peripheral forKey:kDeviceKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kBluetoothDeviceDiscovered
object:self userInfo:deviceInfo];
Another Question is if any of the pairing or bonding

Related

BLE Connection time much higher on iPhone 11 running iOS 14.4 as compared to iPhone 6 running iOS 12.4

I am trying to make a BLE connection from iOS devices with a xGM210P dev board acting as a peripheral .I am able to make successful BLE connection with my iOS source code but there is a huge connection time differences in iPhone 6 and iPhone 11 .
iPhone 6 has BLE 4.0 and iPhone 11 has BLE 5.0 inspite of the fact that iphone 11 has BLE 5.0 which is much faster than BLE 4.0 but its connection time is high as compared to iphone 6 .
For making a BLE connection I am using a connectPeripheral method of CoreBluetooth framework.
So the connection timings are as follows:
iPhone 6 running iOS 12.4 takes a total time of ~168ms for making a successful connection.
iPhone 11 running iOS 14.4 takes a total time of ~560ms for making a successful connection.
Above timings are measured from the event of initiating a connectPeripheral request till I get a callback in didConnectPeripheral delegate method and these timings are very high in iPhone 11 as compared to iPhone 6 .
Below is the code snippet which I am using.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.central = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
}
Firstly I have initialised the centralManager than checking for the Bluetooth state, if its poweredOn than started with the scanning.
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
switch (central.state) {
case CBManagerStateUnknown:
printf("Unknown");
break;
case CBManagerStatePoweredOn:{
CBUUID *readerPeripheral = [CBUUID UUIDWithString:#"FEE2457F-5AB5-FD8C-F24A-0E3FF4A72566"];
NSArray *arr = [NSArray arrayWithObject:readerPeripheral];
[central scanForPeripheralsWithServices:arr options:nil];
break;
}}
Then I get a discovered devboard peripheral in didDiscoveredPeripheral delegate method below.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
if (peripheral.name.length > 0 && [peripheral.name isEqualToString:#"DC0998R21"]) {
NSLog(#"advertisementData = %#",advertisementData);
NSLog(#"%#",advertisementData);
NSString * strkCBAdvDataLocalName = [advertisementData valueForKey:#"kCBAdvDataLocalName"];
NSData *datakCBAdvDataManufacturerData = [advertisementData valueForKey:#"kCBAdvDataManufacturerData"];
[central stopScan];
NSLog(#"Connect CALLED");
self.central = central;
self.scannedPeripheral = peripheral;
self.scannedPeripheral.delegate = self;
self.dateConnectionStarted = [NSDate date];
[self.central connectPeripheral:self.scannedPeripheral options:nil];
}}
Then I get a callback in delegate method didConnectPeripheral below.
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSDate *dateWiteSucessfull = [NSDate date];
NSTimeInterval diff = [dateWiteSucessfull timeIntervalSinceDate:self.dateConnectionStarted];
NSLog(#"TimeTakeninDIDCONNECT=%f",diff);
//THIS diff is ~560ms in iPhone 11 and ~168ms in iPhone 6
CBUUID *UUIDService = [CBUUID UUIDWithString:serviceUUID];
[self.scannedPeripheral discoverServices:[NSArray arrayWithObject:UUIDService]];
}
So basically I am calculating the total time taken from initiating the connection request using connectPeripheral method till I get a callback in didConnectPeripheral method of successful connection happened.
I am not able to understand reason for huge connection time difference between 2 iPhones . I have also gone through this stackoverflow link but no success - Bluetooth Low Energy Lag / Latency on OS X 10.11 El Capitan
I am also reading about the effects of connection parameters like Min and Max Connection interval, slave Latency and tried changing the connection parameters at peripheral's end but still didn't receive any success for reducing the connection timings on iPhone 11.
Any help in this regard would be much appreciated.

BLE background reconnect

I want to reconnect to BLE device after device is moved out/terminated by user or system/reboted in background mode.
I know that it's possible : - see this question with description
Question - How can i setup centralManager for automatically reconnect to peripheral in background mode if app was terminated? Can someone describe step-by-step how it can be done?
Few word about current implementation:
I create centralManager with options like:
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:#{
CBCentralManagerOptionRestoreIdentifierKey: #"myCentralManagerIdentifier",
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
After that i start to scan for BLE device
[self.centralManager scanForPeripheralsWithServices:[self discoverableCharacteristics] options:nil];
in - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI i connect to peripheral:
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
[self.centralManager stopScan];
peripheral.delegate = self;
[self.centralManager connectPeripheral:peripheral options: #{
CBConnectPeripheralOptionNotifyOnNotificationKey : #YES
}];
After that i can discover services and characteristics - all looks like ok. When i discover characteristic and read/write data i cancelPeripheralConnection
in didDisconnect i reconnect to device
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
[central connectPeripheral:peripheral options:nil];
}
i also implement centralManager:willRestoreState: like:
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
for (CBPeripheral *peripheral in peripherals) {
[central connectPeripheral:peripheral options:nil];
peripheral.delegate = nil;
}
In plist. added required key App communicates using CoreBluetooth.
Currently if i connected to device and terminate it - it relaunch automatically and connect to device - all it's ok, but if it's terminated again - nothing is happening.
Also if i moved out from peripheral and that come back - nothing happened.
Update
regarding point 5 - my fall - should use this key with connectPeripheral
in WillRestoreState:
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
if (!peripherals.count) {
peripherals = [central retrievePeripheralsWithIdentifiers:[self discoverableCharacteristics]];
}
if (peripherals.count) {
for (CBPeripheral *peripheral in peripherals) {
[central connectPeripheral:peripheral options:#{
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
}
} else {
[self startScanning];
}
Current result - app will relaunched if it not swiped out from tray. I use my mac as a peripheral, so some times when i not launch app that make role of peripheral central can connect to mac itself not to required service.
Another question - are it's good option to reconnect to peripheral while lost connection for keeping connection like:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
[central connectPeripheral:peripheral options:#{
CBCentralManagerRestoredStatePeripheralsKey : #YES,
CBCentralManagerRestoredStateScanServicesKey : #YES,
CBCentralManagerRestoredStateScanOptionsKey : #YES
}];
}
Also try to change notify characteristic on peripheral and read it on device. If all done in foreground - all works perfect, but in case connection was done in background some times didUpdateValueForCharacteristic not called at all, but didUpdateNotificationStateForCharacteristic is called with no error - this mean (i think) that something was done wrong by my side. Maybe u can advice where problem can be
And one more question - is there is some restriction in writing data to characteristics? because in apple sample it's setuped to 20 bytes.
First off I wanna start by saying that I have been working with CoreBluetooth for about two years now and from what I have noticed CoreBluetooth State Preservation and Restoration does not work reliably at all. You can get it working sort of "ok", but you will never get it to reconnect reliably unless Apple fixes it some day.
Having said that, I want to note a few things on your setup.
1) In centralManager:willRestoreState: you can only retrieve peripherals that has done any communication while the app was terminated. This means that you should also implement centralManagerDidUpdateState: and if the state is CBCentralManagerStatePoweredOn then you can use the retrievePeripheralsWithIdentifiers: method to retrieve the other peripheral and reset their delegate as well. This of course means that you have to stor the peripheral identifiers somewhere in your app somewhere. Also remember to reset pending connections here as well.
2) You set the delegate to nil in centralManager:willRestoreState:! So even if it does connect then you will not know about it i:P
3) Your app will only get relaunched if the app was terminated by the system. It will not get relaunched if you manually swipe-kill it from the application list. Neither will it get relaunched if the device is rebooted, unfortunately.
4) The CBConnectPeripheralOptionNotifyOnConnectionKey is not necessary when using the bluetooth-central background mode and is just annoying for a user so I would not use it.
5) CBCentralManagerRestoredStatePeripheralsKey, CBCentralManagerRestoredStateScanServicesKey, CBCentralManagerRestoredStateScanOptionsKey are not valid initialisation options so I don’t get why you are using those..
5) If the bluetooth switches state while the app is terminated then all pending connections will be lost and you will not be relaunched to know about it. This on its own effectively means that State Restoration is rather useless.
Anyhow, I am sad to say it, but if you are developing an app that must rely on the peripheral being reconnected in the background then I wold not recommend doing it. You will be frustrated. I could probably write an essay about all the bugs in Core Bluetooth that Apple does not want to fix. Even more frightening is that you can pretty easily ruin the bluetooth connectivity globally on the device from one single app so that no app can use bluetooth until the device is rebooted. This is pretty bad since it goes against Apples own Sandboxing principle.
If you need any more help, just let me know!
/A

WCSession sendMessage in Xcode 7.0 (7A218) Simulator

I use sendMessage from iWatch (Simulator) to iOS to awake iOS app:
NSDictionary *userInfo = [[NSDictionary alloc]initWithObjectsAndKeys:#"userInfo", #"key", nil];
if ([[WCSession defaultSession] isReachable]){ //iPhone is reachable
NSLog(#"iPhone is reachable");
[[WCSession defaultSession] sendMessage:userInfo replyHandler:^(NSDictionary<NSString *,id> * _Nonnull replyMessage) {
NSLog(#"ReplyHandler run");
} errorHandler:^(NSError * _Nonnull error) {
NSLog(#"iWatch sendMessage Error: %#", error);
}
But I found that sendMessage only works if iOS app counterpart is either in Background or Foreground. If iOS app is not running at all, sendMessage cannot awake the iOS App. Both replyHandler and errorHandler were not called if App is not running in background / foreground.
This does not agree with the documentation: Calling this method from your WatchKit extension while it is active and running wakes up the corresponding iOS app in the background and makes it reachable. Calling this method from your iOS app does not wake up the corresponding WatchKit extension.
I tested on Xcode Beta 5 and the same piece of code worked even if iOS app is not running.
Anyone encounter similar issues in latest Simulator and Xcode?
Any thoughts?
Thanks

unable to connect bluetooth device in ios 8 even existing code not finding bluetooth and main thing it woring very fine on ios 7

problem to connect Bluetooth device like wristband in ios 8 even existing code not finding Bluetooth and main thing it working very fine on ios 7..my code is as
suggest me any idea or changes in ios 8 for Bluetooth..
-(void)connecttodevice {
// Scan for all available CoreBluetooth LE devices
NSArray *services = #[[CBUUID UUIDWithString:info_UUID],[CBUUID UUIDWithString:info_UUID_service],[CBUUID UUIDWithString:BAND_SERVICE_INFO]];
NSLog(#"device connting.....");
CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[centralManager scanForPeripheralsWithServices:services options:nil];
self.centralManager = centralManager;
}
Your problem is that you are invoking scanForPeripheralsWithServices immediately after creating the CBCentralManager rather than waiting until the central manager is in the powered on state as indicated in the centralManagerDidUpdateState delegate.
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
if (central.state == CBCentrallManagerStatePoweredOn {
NSArray *services = #[[CBUUID UUIDWithString:info_UUID],[CBUUID UUIDWithString:info_UUID_service],[CBUUID UUIDWithString:BAND_SERVICE_INFO]];
NSLog(#"device connting.....");
[central scanForPeripheralsWithServices:services options:nil];
}
}
in iOS 7 starting the scan before the power on state was indicated issued a warning on the console. In iOS 8 it doesn't start the scan.

Core Bluetooth - constant RSSI updates of in-range devices

I just started with the core bluetooth framework for iOS and I'm developing an app that needs to constantly scan for BLE devices so that I can retrieve their RSSI number every minute or so.
Currently I have:
manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[manager scanForPeripheralsWithServices:nil options:options];
this starts my app scanning for BLE devices and calls this delegate method when a device is discovered:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"Did discover peripheral. peripheral: %# rssi: %#, UUID: %# advertisementData: %# ", peripheral, RSSI, peripheral.UUID, advertisementData);
//Do something when a peripheral is discovered.
rssiLabel.text = [RSSI stringValue];
[manager retrievePeripherals:[NSArray arrayWithObject:(id)peripheral.UUID]];}
this method gets me the peripheral's RSSI number which i can display. The last line then calls this delegate method:
- (void) centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals {
NSLog(#"Currently known peripherals :");
int i = 0;
for(CBPeripheral *peripheral in peripherals) {
NSLog(#"[%d] - peripheral : %# with UUID : %#",i,peripheral,peripheral.UUID);
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[manager scanForPeripheralsWithServices:nil options:options];
}
This code seems to be working and doing a scan roughly every 1 minute, but I don't exactly know why it working...
The documentation on core bluetooth is pretty sparse so if anyone has any idea on how to do this, or has a better way to do what I'm trying to accomplish I'd appreciate the help!
Have you tried changing the scan option to YES?
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[manager scanForPeripheralsWithServices:nil options:options];
If you do this you will get your "didDiscoverPeripheral" callback with every ad packet that is seen by your iPhone, which would normally be about every 100ms (although I see this callback timing varying a lot for the same device). This includes the RSSI of each device it sees.
This should be a lot faster than your ~1 minute update rate.
Is far as I can see, this should do what you want.
When you started scanning for peripherals with the original call, your delegate should begin to get calls whenever a BLE device is discovered. This will continue until you stop the scan with a call to
[manager stopScan];
I don't think you actually need the second call to scanForPeripheralsWithServices in your centralManager:didRetrievePeripherals method, since, as far as I know, the scanning doesn't stop until you tell it to. I'm still getting started on this, too, though, and there may be a timeout I have not found, yet.
I'm pretty sure the reason you get a call about once a minute is because the BLE device is only advertising that often. If it advertises more often, like a device in discovery mode, I think you will get the calls more often. I would be interesting if you could confirm that. If the device has a discovery mode, you might try triggering it to see if the notices speed up.
You shouldn't do continous scanning as it is very costly for power. Once you discovered devices you have an array of CBPeripheral objects returned to you. On CBPeripheral you can read RSSI and get a callback when RSSI changes. See the following documentation:
http://developer.apple.com/library/mac/#documentation/CoreBluetooth/Reference/CBPeripheralDelegate_Protocol/translated_content/CBPeripheralDelegate.html
Swift Implementation of #Anders solution:
manager.scanForPeripheralsWithServices(nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : NSNumber(value: true)])

Resources