Reputation: 453
I'm am working on using BLE on a project previously using MFi devices. The goal is to achieve a sort of serial connection.
With EAaccessory I had a popover asking for the device to connect to and was working fine.
Using CB, I added a view that I call to scan and select the device to connect to. I can connect the device fine, but when I go back to main view, I loose the connection to the peripheral.
CoreBluetooth[WARNING] <CBConcretePeripheral: ... IsConnected = YES> is being dealloc'ed while connected
So, as I am not a genius nor a good iOs programmer... Could someone point me in the right direction to retain the connected peripheral from one view to the main ?
I tried to understand a sample from ConnectBlue, the manufacturer of the BT devices on my project, but they use Storyboard, and I don't. Using their demo, I can connect to the peripheral, on the main view and chat with it on another view.
I tried to google, but found nothing relevant.
Edit: The connection code part
#import "ScanTableViewController.h"
#import <CoreBluetooth/CBCentralManager.h>
#import <CoreBluetooth/CBPeripheral.h>
#import "DiscoveredPeripheral.h"
#import "ScanCell.h"
typedef enum
{
SCAN_S_NOT_LOADED,
SCAN_S_DISAPPEARED,
SCAN_S_WILL_DISAPPEAR,
SCAN_S_APPEARED_IDLE,
SCAN_S_APPEARED_SCANNING
} SCAN_State;
@interface ScanTableViewController ()
@end
@implementation ScanTableViewController
{
SCAN_State state;
CBCentralManager *cbCentralManager;
NSMutableArray *discoveredPeripherals;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"Scan viewDidLoad");
cbCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
// Do any additional setup after loading the view from its nib.
CGRect tableViewFrame = self.view.bounds;
UITableView *tableview = [[UITableView alloc] initWithFrame:tableViewFrame style:UITableViewStylePlain];
self.myTableView = tableview;
self.myTableView.rowHeight = 60;
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
//Make sure our table view resizes correctly
self.myTableView.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:tableview];
discoveredPeripherals = [[NSMutableArray alloc] init];
//[cbCentralManager retrieveConnectedPeripherals];
state = SCAN_S_DISAPPEARED;
}
- (void)viewDidUnload
{
//[self setScanButton:nil];
[super viewDidUnload];
NSLog(@"Scan viewDidUnload");
cbCentralManager = nil;
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
state = SCAN_S_NOT_LOADED;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(@"Scan viewWillAppear");
[self.myTableView reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSLog(@"Scan viewDidAppear");
[self clearPeriph];
state = SCAN_S_APPEARED_IDLE;
[self.myTableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated
{
NSLog(@"Scan viewWillDisappear");
//[self scan: FALSE];
state = SCAN_S_WILL_DISAPPEAR;
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
NSLog(@"Scan viewDidDisappear");
state = SCAN_S_DISAPPEARED;
}
-(void) enterForeground
{
NSLog(@"Scan enterForeground");
[self clearPeriph];
state = SCAN_S_APPEARED_IDLE;
}
-(void) enterBackground
{
NSLog(@"Scan enterBackground");
[self scan: FALSE];
state = SCAN_S_DISAPPEARED;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) initWithPeripherals: (NSMutableArray*) dp
{
NSLog(@"Scan initWithPeripherals");
discoveredPeripherals = dp;
state = SCAN_S_NOT_LOADED;
}
#pragma mark - Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//NSLog(@"Nombre de sections");
return 2;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSInteger nRows;
switch(section)
{
case 0:
nRows = 1;
break;
case 1:
NSLog(@"Scan Nbre ligne section 1 : %i",discoveredPeripherals.count);
nRows = discoveredPeripherals.count;
break;
default:
nRows = 0;
break;
}
return nRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//NSLog(@"Remplissage");
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton;
}
NSLog(@"Scan Section table:%u",indexPath.section);
switch(indexPath.section)
{
case 0:
{
cell.textLabel.text = @"";
if(state == SCAN_S_APPEARED_SCANNING)
{
cell.textLabel.text = @"Stop Scan";
//cell.labelInfo.text = @"Active";
//[cell.activityView startAnimating];
}
else
{
cell.textLabel.text = @"Start Scan";
//cell.labelInfo.text = @"Inactive";
//
//[cell.activityView stopAnimating];
}
break;
}
case 1:
{
if ( [discoveredPeripherals count] > 0)
{
DiscoveredPeripheral* discoveredPeripheral;
discoveredPeripheral = [discoveredPeripherals objectAtIndex:indexPath.row];
cell.textLabel.text =discoveredPeripheral.peripheral.name;
}
cell.detailTextLabel.textColor = [UIColor blackColor];
}
}
return cell;
}
- (NSString*) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *str;
switch(section)
{
case 0:
str = @"Bluetooth Low Energy Scanning";
break;
case 1:
str = @"Found Devices";
break;
default:
break;
}
return str;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(cbCentralManager.state == CBCentralManagerStatePoweredOn)
{
//ScanCell* cell = (ScanCell*)[tableView cellForRowAtIndexPath:indexPath];
//static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if(indexPath.section == 0)
{
if(state == SCAN_S_APPEARED_SCANNING)
{
[self scan: FALSE];
cell.textLabel.text = @"Start Scan";
//cell.labelInfo.text = @"Inactive";
//[cell.activityView stopAnimating];
state = SCAN_S_APPEARED_IDLE;
}
else if((state == SCAN_S_APPEARED_IDLE) &&
(cbCentralManager.state == CBCentralManagerStatePoweredOn))
{
[self scan: TRUE];
cell.textLabel.text = @"Stop Scan";
//cell.labelInfo.text = @"Active";
//[cell.activityView startAnimating];
state = SCAN_S_APPEARED_SCANNING;
}
}
else
{
DiscoveredPeripheral* dp = [discoveredPeripherals objectAtIndex:indexPath.row];
NSDictionary *dictionary;
switch (dp.state)
{
case DP_STATE_IDLE:
cell.textLabel.text = @"Connecting";
//[cell.activityView startAnimating];
dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
[cbCentralManager connectPeripheral:dp.peripheral options:dictionary];
dp.state = DP_STATE_CONNECTING;
break;
case DP_STATE_CONNECTED:
case DP_STATE_CONNECTING:
[cbCentralManager cancelPeripheralConnection:dp.peripheral];
cell.textLabel.text = @"";
//[cell.activityView stopAnimating];
cell.accessoryType = UITableViewCellAccessoryNone;
dp.state = DP_STATE_IDLE;
break;
default:
break;
}
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
- (void) scan: (bool) enable
{
if(enable == TRUE)
{
NSLog(@"Scan Scan ON");
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
[cbCentralManager scanForPeripheralsWithServices:nil options:dictionary];
}
else
{
NSLog(@"Scan Scan Off");
[cbCentralManager stopScan];
}
}
- (IBAction)startScan:(id)sender {
if(state == SCAN_S_APPEARED_IDLE)
{
[self scan: TRUE];
state = SCAN_S_APPEARED_SCANNING;
}
else if(state == SCAN_S_APPEARED_SCANNING)
{
[self scan: FALSE];
state = SCAN_S_APPEARED_IDLE;
}
}
- (void) clearPeriphForRow: (NSInteger)row
{
DiscoveredPeripheral* dp = [discoveredPeripherals objectAtIndex:row];
//if( (dp.peripheral.isConnected == FALSE) &&
// ( (dp.state == DP_STATE_CONNECTED) || (dp.state == DP_STATE_DISCONNECTING)))
if(dp.peripheral.isConnected == FALSE)
{
dp.state = DP_STATE_IDLE;
}
else if( (dp.peripheral.isConnected == TRUE) &&
(dp.state != DP_STATE_CONNECTED))
{
dp.state = DP_STATE_CONNECTED;
}
if(dp.state == DP_STATE_IDLE)
{
[discoveredPeripherals removeObjectAtIndex:row];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:1];
//ScanCell* cell = (ScanCell*)[self.tableView cellForRowAtIndexPath:indexPath];
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
//[cell.activityView stopAnimating];
cell.accessoryType = UITableViewCellAccessoryNone;
[self.myTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void) clearPeriph
{
if(self->discoveredPeripherals.count > 0)
{
for(int i = discoveredPeripherals.count - 1; i >= 0 ; i--)
{
[self clearPeriphForRow:i];
}
}
[self.myTableView reloadData];
}
- (IBAction)clearPeripherals:(id)sender {
[self clearPeriph];
[self scan: FALSE];
state = SCAN_S_APPEARED_IDLE;
}
- (NSInteger)getRowForPeripheral: (CBPeripheral*)peripheral
{
NSInteger row = -1;
DiscoveredPeripheral* p;
for(int i = 0; (i < discoveredPeripherals.count) && (row == -1); i++)
{
p = [discoveredPeripherals objectAtIndex:i];
if([peripheral isEqual:p.peripheral] == TRUE)
{
row = i;
}
}
return row;
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSInteger row = [self getRowForPeripheral:peripheral];
if(row != -1)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:1];
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
//ScanCell* cell = (ScanCell*)[self.tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = [[NSString alloc] initWithFormat:@"Connected"];
//[cell.activityView stopAnimating];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
DiscoveredPeripheral* dp = [discoveredPeripherals objectAtIndex:row];
dp.state = DP_STATE_CONNECTED;
//[peripheral discoverServices:nil];
//[self scan:FALSE];
//[[self navigationController] popViewControllerAnimated:NO];
//[self.delegate didConnectedPeriph:dp];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSInteger row = [self getRowForPeripheral:peripheral];
if(row != -1)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:1];
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
//ScanCell* cell = (ScanCell*)[self.tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = [[NSString alloc] initWithFormat:@""];
cell.accessoryType = UITableViewCellAccessoryNone;
//[cell.activityView stopAnimating];
DiscoveredPeripheral* dp = [discoveredPeripherals objectAtIndex:row];
dp.state = DP_STATE_IDLE;
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
bool new = TRUE;
DiscoveredPeripheral* discPeripheral;
int row = -1;
if((state == SCAN_S_APPEARED_SCANNING) &&
(peripheral != nil))
{
for(int i = 0; (i < discoveredPeripherals.count) && (new == TRUE); i++)
{
NSLog(@"Scan Ajout periph");
discPeripheral = [discoveredPeripherals objectAtIndex:i];
if(discPeripheral.peripheral == peripheral)
{
new = false;
row = i;
discPeripheral.peripheral = peripheral;
}
}
if(new == TRUE)
{
discPeripheral = [[DiscoveredPeripheral alloc] initWithPeripheral:peripheral andAdvertisment:advertisementData andRssi:RSSI];
discPeripheral.rssi = RSSI;
if(peripheral.isConnected == TRUE)
{
discPeripheral.state = DP_STATE_CONNECTED;
}
[discoveredPeripherals addObject:discPeripheral];
NSLog(@"Scan Ajout periph, total:%i",[discoveredPeripherals count]);
NSLog(@"Scan %i: Add %@",[discoveredPeripherals count]-1, discPeripheral.peripheral.name);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[discoveredPeripherals count]-1 inSection:1];
NSLog(@"Scan Nouveau periph, index:%i, section:%i",indexPath.row,indexPath.section);
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = discPeripheral.peripheral.name;
//[self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.myTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else
{
discPeripheral.peripheral = peripheral;
discPeripheral.advertisment = advertisementData;
discPeripheral.rssi = RSSI;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:1];
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
//ScanCell* cell = (ScanCell*)[self.tableView cellForRowAtIndexPath:indexPath];
//NSLog(@"%i: Update %@", row, discPeripheral.peripheral.name);
cell.textLabel.text = discPeripheral.peripheral.name;
//cell.labelInfo.text = [[NSString alloc] initWithFormat:@"RSSI: %@", discPeripheral.rssi];
}
}
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSInteger row = [self getRowForPeripheral:peripheral];
if(row != -1)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:1];
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath:indexPath];
//ScanCell* cell = (ScanCell*)[self.tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.text = [[NSString alloc] initWithFormat:@""];
cell.accessoryType = UITableViewCellAccessoryNone;
//[cell.activityView stopAnimating];
DiscoveredPeripheral* dp = [discoveredPeripherals objectAtIndex:row];
dp.state = DP_STATE_IDLE;
}
}
- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
//DiscoveredPeripheral* discPeripheral;
CBPeripheral* peripheral;
for(int i = 0; i < peripherals.count; i++)
{
peripheral = [peripherals objectAtIndex:i];
/*
discPeripheral = [[DiscoveredPeripheral alloc] initWithPeripheral:peripheral andAdvertisment:nil andRssi:nil];
if(peripheral.isConnected == TRUE)
{
discPeripheral.state = DP_STATE_CONNECTED;
}
[discoveredPeripherals addObject:discPeripheral];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[discoveredPeripherals count] - 1 inSection:1];
[self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
*/
//[cbCentralManager cancelPeripheralConnection:peripheral];
NSDictionary *dictionary;
dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
[cbCentralManager connectPeripheral:peripheral options:dictionary];
}
}
- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals
{
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
//NSLog(@"Central Manager State: %d", [central state]);
if(central.state == CBCentralManagerStatePoweredOn)
{
[cbCentralManager retrieveConnectedPeripherals];
}
}
@end
Edit: This view is called from a view controller. The view controller is receiving data from bluetooth, record and displays the data. What I would like to achieve is to have a separate view to connect to the BLE peripheral, go back to the main view, attach the serial port and run. In a second time, if the peripheral is lost, try to reconnect to it, else pop the connection view and alert the user.
Cheers.
Cedric
Upvotes: 1
Views: 3312
Reputation: 658
A very nice way to manage your entire bluetooth processes is the singleton model. You can move all your bluetooth code to a separate NSObject class and declare that file as a singleton. Create a function within your singleton class to initiate the connectPeripheral after you discover your device. Whenever you connect, you can save the peripheral you are connecting to as a variable or in an array if you have multiple devices. It's quite simple once you get the hang of it. Using this method of implementation you can refer to your connected Peripheral from anywhere within your app. Hope this helped!
Upvotes: 1
Reputation: 3084
You need to retain the peripheral. As soon as the peripheral object you are passed is released, iOS drops the connection. Save the CBPeripheral
object in a variable, and be sure to pass that back to your main view when the scan view closes.
Upvotes: 2