Edit: Now tested on my actual iphone, and I still get the same crash error
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString stringByAppendingString:]: nil argument'
When I ran the debugger, I ran through the debugger with breakpoints, but it did not reach any other function besides the tLog and the didLoad. The message "Bluetooth LE Device Scanner\r\n\r" went through the tLog a few times without before the app crashed because of the invalid argument
Original -
Info: Mac OSX Yosemite; Xcode 6.4; Using Objective C
Right now, I am following the video tutorial on setting up the bluetooth scanner to detect devices powered by bluetooth.
Video Links:
https://www.youtube.com/watch?v=zjr8qLE0sxk
https://www.youtube.com/watch?v=AjBpB4TcI-U (This part is where crash occurs)
I followed the code and understood the basics of core bluetooth framework. However, when running the test under both the iOS simulator and my iPhone, the application crashes at start-up.
The error I get is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString stringByAppendingString:]: nil argument' (iOS simulator)
I used the breakpoints that involve tlog and start-up, and that same error is still there because it is stuck printing the same thing.
Is there any fix I should do to get the basic bluetooth scanner running?
EDIT -
The code from the tutorial that I followed:
#import "ViewController.h"
#interface ViewController ()
// Now we need the properties to do various tasks for the device scanner
#property (weak, nonatomic) IBOutlet UITextView *outputTextView;
#property (weak, nonatomic) IBOutlet UISegmentedControl *verbositySelector;
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPeripheral;
#property BOOL bluetoothOn;
#property (weak, nonatomic) IBOutlet UILabel *valueLabel;
#end
#implementation ViewController
-(bool)verboseMode
{
return (self.verbositySelector.selectedSegmentIndex != 0);
}
-(void)tlog:(NSString*)msg
{
self.outputTextView.text = [#"\r\n\r\n" stringByAppendingString:self.outputTextView.text];
self.outputTextView.text = [msg stringByAppendingString:self.outputTextView.text];
}
-(IBAction)startScan:(id)sender
{
if (!self.bluetoothOn){
[self tlog:#"Bluetooth is OFF"];
return;
}
[self.centralManager scanForPeripheralsWithServices:nil
options:
#{CBCentralManagerScanOptionAllowDuplicatesKey: #NO}];
}
// Standard template items added in by default. DO NOT REMOVE!
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self tlog:#"Bluetooth LE Device Scanner\r\n\r"];
self.bluetoothOn = NO;
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// Core Bluetooth Tasks
-(void) centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
[self tlog:[NSString stringWithFormat:#"Discovered %#, RSSI: %#\n",
[advertisementData objectForKey:#"kCBAdvDataLocalName"], RSSI]];
self.discoveredPeripheral = peripheral;
if ([self verboseMode]){
[self.centralManager connectPeripheral:peripheral options:nil];
}
}
-(void) centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
error:(NSError *)error
{
}
-(void) centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error
{
}
-(void) centralManagerDidUpdateState:(CBCentralManager *)central
{
if (central.state != CBCentralManagerStatePoweredOn){
[self tlog:#"Bluetooth OFF"];
self.bluetoothOn = NO;
}
else {
[self tlog:#"Bluetooth ON"];
self.bluetoothOn = YES;
}
}
#end
Related
I've created a React Native project and I need to discover Bluetooth devices in the background. I'm trying to implement the entire background functionality on the native side using Objective-C.
As an initial step, I'm trying to use CBCentralManager to discover the device in the foreground first and then will move to the background case.
I'm able to call the init function of my custom class which handles BLE functionality but after that CBCentralManagerDelegate methods are not being called and I'm not able to proceed further.
// BLEScan.m
#import <Foundation/Foundation.h>
#import "BLEScan.h"
#interface BLEScan() <CBCentralManagerDelegate>
#property (strong, nonatomic) CBCentralManager *manager;
#property CBPeripheral *dot;
#end
#implementation BLEScan
- (instancetype) initModule {
NSLog(#"BLEScan: init");
if (self = [super init]) {
_manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
NSLog(#"BLEScan: state %li", (long)_manager.state);
}
return self;
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
// You should test all scenarios
NSLog(#"BLEScan: state %li", (long)central.state);
if (central.state != CBManagerStatePoweredOn) {
return;
}
if (central.state == CBManagerStatePoweredOn) {
// Scan for devices
[_manager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
NSLog(#"Scanning started");
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BLEScan *scanner = [[BLEScan alloc] initModule];
return YES
}
Link to the repo - https://github.com/inkloud-infiswift/CoreBluetooth
I am an Android developer, moving over to iOS, so please bear with me regarding the basics of iOS development.
I have the following code:
Here is the .m file:
#import "BlueToothLEManager.h"
#import "Constants.h"
#implementation BlueToothLEManager
#synthesize mBTCentralManager;
-(void)initializeCBCentralManager{
NSLog(#"initializing CBCentral Manager"); <--- This is being logged
mBTCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
#pragma mark - CBCentralManagerDelegate
// method called whenever you have successfully connected to the BLE peripheral
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
}
// CBCentralManagerDelegate - This is called with the CBPeripheral class as its main input parameter. This contains most of the information there is to know about a BLE peripheral.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
}
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSLog(#"Start scan"); <---- This is NOT being logged.
if(central.state != CBCentralManagerStatePoweredOn){
return;
}
if(central.state == CBCentralManagerStatePoweredOn){
NSLog(#"Scanning for BTLE device");
[mBTCentralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:DEVICE_NAME]] options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
}
}
#end
Here is the .h file:
#import <Foundation/Foundation.h>
#import CoreBluetooth;
#interface BlueToothLEManager : NSObject < CBCentralManagerDelegate, CBPeripheralDelegate>{
CBCentralManager *mBTCentralManager;
}
#property (strong, retain) CBCentralManager *mBTCentralManager;
-(void) initializeCBCentralManager;
#end
When I call initializeCBCentralManager everything appears to work, but for some reason the centralManagerDidUpdateState method isn't being called. Can someone tell me what I'm doing wrong?
You should clean up your property definition as your iVar is getting mixed up with your property. If you declare a property you don't need to declare an iVar. You also don't need #synthesize unless you want a specific name for the backing variable. If you use the self. notation then you can be sure that you are referring to the property and not an iVar.
Your .h file should be -
#import CoreBluetooth;
#interface BlueToothLEManager : NSObject < CBCentralManagerDelegate, CBPeripheralDelegate>
#property (strong, retain) CBCentralManager *mBTCentralManager;
-(void) initializeCBCentralManager;
#end
Then your .m file will be
#import "BlueToothLEManager.h"
#import "Constants.h"
#implementation BlueToothLEManager
-(void)initializeCBCentralManager{
NSLog(#"initializing CBCentral Manager");
self.mBTCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
#pragma mark - CBCentralManagerDelegate
// method called whenever you have successfully connected to the BLE peripheral
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
}
// CBCentralManagerDelegate - This is called with the CBPeripheral class as its main input parameter. This contains most of the information there is to know about a BLE peripheral.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
}
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSLog(#"Start scan");
if(central.state == CBCentralManagerStatePoweredOn){
NSLog(#"Scanning for BTLE device");
[central scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:DEVICE_NAME]] options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
}
}
#end
I have tested this and it works
I am new to this but I have a question about Xcode. I figured out how to get my view controller embedded in a navigation controller, but how do i get a segue from the first view to a second view to work? I have two views, and I need to segue between them, but it keeps giving me a thread1 SIGABRT message. I tried making the segue modal and this does not work. When I try to embed my second view controller in a navigation controller it creates a new navigation controller. Any advice?
#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPerepheral;
#property (strong, nonatomic) NSMutableData *data;
#property (strong, nonatomic) IBOutlet UITextView *textview;
#property (weak, nonatomic) IBOutlet UILabel *isConnected;
#property (weak, nonatomic) IBOutlet UILabel *myPeripherals;
#property (weak, nonatomic) IBOutlet UILabel *aLabel;
#end
#import "ViewController.h"
#implementation ViewController
- (IBAction)connect:(id)sender {
_centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:nil];
_data = [[NSMutableData alloc]init];
}
- (void)viewDidLoad {
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface BlueToothViewController : UIViewController
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPerepheral;
#property (strong, nonatomic) NSMutableData *data;
#property (strong, nonatomic) IBOutlet UITextView *textview;
#property (weak, nonatomic) IBOutlet UILabel *isConnected;
#property (weak, nonatomic) IBOutlet UILabel *myPeripherals;
#property (weak, nonatomic) IBOutlet UILabel *aLabel;
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
- (void)centralManger:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
-(void)cleanup;
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
#end
#import "BlueToothViewController.h"
#interface BlueToothViewController ()
#end
#implementation BlueToothViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_centralManager stopScan];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
//you should test all scenarios
if (central.state == CBCentralManagerStateUnknown) {
self.aLabel.text = #"I dont do anything because my state is unknown.";
return;
}
if (central.state == CBCentralManagerStatePoweredOn) {
//scan for devices
[_centralManager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
NSLog(#"Scanning Started");
}
if (central.state == CBCentralManagerStateResetting) {
self.aLabel.text = #"I dont do anything because my state is resetting.";
return;
}
if (central.state == CBCentralManagerStateUnsupported) {
self.aLabel.text = #"I dont do anything because my state is unsupported.";
return;
}
if (central.state == CBCentralManagerStateUnauthorized) {
self.aLabel.text = #"I dont do anything because my state is unauthorized.";
return;
}
if (central.state == CBCentralManagerStatePoweredOff) {
self.aLabel.text = #"I dont do anything because my state is powered off.";
return;
}
}
- (void)centralManger:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
self.myPeripherals.text = [NSString stringWithFormat:#"%#%#",peripheral.name, RSSI];
if (_discoveredPerepheral != peripheral) {
//save a copy of the peripheral
_discoveredPerepheral = peripheral;
//and connect
NSLog(#"Connecting to peripheral %#", peripheral);
[_centralManager connectPeripheral:peripheral options:nil];
}
}
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(#"Failed to connect");
[self cleanup];
}
-(void)cleanup {
//see if we are subscribed to a characteristic on the peripheral
if (_discoveredPerepheral.services != nil) {
for (CBService *service in _discoveredPerepheral.services) {
if (service.characteristics != nil) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"508EFF8E-F541-57EF-BD82-B0B4EC504CA9"]]) {
if (characteristic.isNotifying) {
[_discoveredPerepheral setNotifyValue:NO forCharacteristic:characteristic];
return;
}
}
}
}
}
}
[_centralManager cancelPeripheralConnection:_discoveredPerepheral];
}
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"Connected");
[_centralManager stopScan];
NSLog(#"Scanning stopped");
self.isConnected.text = [NSString stringWithFormat:#"Connected"];
[_data setLength:0];
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) { [self cleanup];
return;
}
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
//discover other characteristics
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if (error) { [self cleanup];
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:nil]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) { NSLog(#"Error");
return;
}
NSString *stringFromData = [[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
//Have we got everything we need?
if ([stringFromData isEqualToString:#"EOM"]) {
[_textview setText:[[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding]];
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
[_centralManager cancelPeripheralConnection:peripheral];
}
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if ([characteristic.UUID isEqual:nil]) {
return;
}
if (characteristic.isNotifying) {
NSLog(#"Notification began on %#", characteristic);
}
else {
[_centralManager cancelPeripheralConnection:peripheral];
}
}
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
_discoveredPerepheral = nil;
self.isConnected.text = [NSString stringWithFormat:#"Connecting..."];
[_centralManager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES}];
}
#end
2014-06-05 13:51:28.809 BlindPed[7044:60b] -[ViewController centralManagerDidUpdateState:]: unrecognized selector sent to instance 0x8cafb50
2014-06-05 13:51:28.812 BlindPed[7044:60b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController centralManagerDidUpdateState:]: unrecognized selector sent to instance 0x8cafb50'
* First throw call stack:
(
0 CoreFoundation 0x017f11e4 exceptionPreprocess + 180
1 libobjc.A.dylib 0x015708e5 objc_exception_throw + 44
2 CoreFoundation 0x0188e243 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x017e150b ___forwarding_ + 1019
4 CoreFoundation 0x017e10ee _CF_forwarding_prep_0 + 14
5 CoreBluetooth 0x01a46567 -[CBCentralManager xpcConnectionIsInvalid:] + 59
6 CoreBluetooth 0x01a4fd33 62-[CBXpcConnection initWithDelegate:queue:options:sessionType:]_block_invoke20 + 82
7 libdispatch.dylib 0x01c207b8 _dispatch_call_block_and_release + 15
8 libdispatch.dylib 0x01c354d0 _dispatch_client_callout + 14
9 libdispatch.dylib 0x01c23726 _dispatch_main_queue_callback_4CF + 340
10 CoreFoundation 0x0185643e __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 14
11 CoreFoundation 0x017975cb __CFRunLoopRun + 1963
12 CoreFoundation 0x017969d3 CFRunLoopRunSpecific + 467
13 CoreFoundation 0x017967eb CFRunLoopRunInMode + 123
14 GraphicsServices 0x038175ee GSEventRunModal + 192
15 GraphicsServices 0x0381742b GSEventRun + 104
16 UIKit 0x00230f9b UIApplicationMain + 1225
17 BlindPed 0x0000526d main + 141
18 libdyld.dylib 0x01e6a701 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Are you using storyboard? If you are, just drag a UINavigationController out (it'll come with a UITableViewController, but just click on that and delete it). Then drag out two view controllers, change their custom classes to your classes. Control-drag from the UINavigationController to your first view controller (set it as the root view controller).
You also need a button or something on the first view controller to trigger the segue. Once you add it, control-drag from the button to the second view controller and select the "push" segue. It's action will be connected to triggering the segue.
Drag the arrow with no ancestor (just a floating arrow) to your UINavigationController, and it should work.
My app won't detect peripherals. Im using light blue to simulate a bluetooth low energy peripheral and my app just won't sense it. I even installed light blue on two devices to make sure it was generating a peripheral signal properly and it is. Any suggestions? My labels are updating and the NSLog is showing that the scanning is starting.
Thanks in advance.
#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *navDestination;
#end
#import "ViewController.h"
#implementation ViewController
- (IBAction)connect:(id)sender {
}
- (IBAction)navDestination:(id)sender {
NSString *destinationText = self.navDestination.text;
}
- (void)viewDidLoad {
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface BlueToothViewController : UIViewController
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPerepheral;
#property (strong, nonatomic) NSMutableData *data;
#property (strong, nonatomic) IBOutlet UITextView *textview;
#property (weak, nonatomic) IBOutlet UILabel *charLabel;
#property (weak, nonatomic) IBOutlet UILabel *isConnected;
#property (weak, nonatomic) IBOutlet UILabel *myPeripherals;
#property (weak, nonatomic) IBOutlet UILabel *aLabel;
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
- (void)centralManger:(CBCentralManager *)central didDiscoverPeripheral: (CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
-(void)cleanup;
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
#end
#interface BlueToothViewController ()
#end
#implementation BlueToothViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
_centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:nil];
_data = [[NSMutableData alloc]init];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_centralManager stopScan];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
//you should test all scenarios
if (central.state == CBCentralManagerStateUnknown) {
self.aLabel.text = #"I dont do anything because my state is unknown.";
return;
}
if (central.state == CBCentralManagerStatePoweredOn) {
//scan for devices
[_centralManager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
NSLog(#"Scanning Started");
}
if (central.state == CBCentralManagerStateResetting) {
self.aLabel.text = #"I dont do anything because my state is resetting.";
return;
}
if (central.state == CBCentralManagerStateUnsupported) {
self.aLabel.text = #"I dont do anything because my state is unsupported.";
return;
}
if (central.state == CBCentralManagerStateUnauthorized) {
self.aLabel.text = #"I dont do anything because my state is unauthorized.";
return;
}
if (central.state == CBCentralManagerStatePoweredOff) {
self.aLabel.text = #"I dont do anything because my state is powered off.";
return;
}
}
- (void)centralManger:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
self.myPeripherals.text = [NSString stringWithFormat:#"%#%#",peripheral.name, RSSI];
if (_discoveredPerepheral != peripheral) {
//save a copy of the peripheral
_discoveredPerepheral = peripheral;
//and connect
NSLog(#"Connecting to peripheral %#", peripheral);
[_centralManager connectPeripheral:peripheral options:nil];
self.aLabel.text = [NSString stringWithFormat:#"%#", peripheral];
}
}
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(#"Failed to connect");
[self cleanup];
}
-(void)cleanup {
//see if we are subscribed to a characteristic on the peripheral
if (_discoveredPerepheral.services != nil) {
for (CBService *service in _discoveredPerepheral.services) {
if (service.characteristics != nil) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"508EFF8E-F541-57EF-BD82-B0B4EC504CA9"]]) {
if (characteristic.isNotifying) {
[_discoveredPerepheral setNotifyValue:NO forCharacteristic:characteristic];
return;
}
}
}
}
}
}
[_centralManager cancelPeripheralConnection:_discoveredPerepheral];
}
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"Connected");
[_centralManager stopScan];
NSLog(#"Scanning stopped");
self.isConnected.text = [NSString stringWithFormat:#"Connected"];
[_data setLength:0];
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) { [self cleanup];
return;
}
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
//discover other characteristics
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) { [self cleanup];
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) { NSLog(#"Error");
return;
}
NSString *stringFromData = [[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
self.charLabel.text = [NSString stringWithFormat:#"%#", stringFromData];
//Have we got everything we need?
if ([stringFromData isEqualToString:#"EOM"]) {
[_textview setText:[[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding]];
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
[_centralManager cancelPeripheralConnection:peripheral];
}
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if ([characteristic.UUID isEqual:nil]) {
return;
}
if (characteristic.isNotifying) {
NSLog(#"Notification began on %#", characteristic);
}
else {
[_centralManager cancelPeripheralConnection:peripheral];
}
}
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
_discoveredPerepheral = nil;
self.isConnected.text = [NSString stringWithFormat:#"Connecting..."];
[_centralManager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES}];
}
#end
Looks like you aren't declaring that you implement the CBCentralManagerDelegate protocol. Change your BlueToothViewController interface to add the CBCentralManagerDelegate.
#interface BlueToothViewController () <CBCentralManagerDelegate>
I have an iOS device (iPod Touch 5G) acting as a CBCentralManager, and Bluno acting as a CBPeripheralManager. When I attempt to connect to the peripheral with the central it appears to connect for a second or two then disconnects. I can see a LED on the Bluno light up for a second or two then dim down. I know there isn't a problem with the Bluno because I can use the LightBlue app on the iPod Touch to connect to the peripheral. So the problem probably lies somewhere in my code.
As of right now, I am getting the following message when I status syslog,
CoreBluetooth[WARNING] <CBCentralManager: 0x16e77470> is disabling duplicate filtering, but is using the default queue (main thread) for delegate events
Not sure if that is related to the connect / disconnect problem.
The code I have constructed is as follows,
Services.h
#define BLUNO_TRANSFER_SERVICE_UUID #"0xDFB0"
#define BLUNO_TRANSFER_CHARACTERISTIC_UUID #"0xDFB2"
ViewControllerDev2.h
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <QuartzCore/QuartzCore.h>
#import "SERVICES.h"
#interface ViewControllerDev2 : UIViewController <CBCentralManagerDelegate, CBPeripheralDelegate, UITextViewDelegate> {
}
#property (weak, nonatomic) IBOutlet UIBarButtonItem *btnDone;
// Core Bluetooth Peripheral stuff
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPeripheral;
#property (strong, nonatomic) NSMutableData *data;
#property (strong, nonatomic) IBOutlet UITextView *textView;
#property (weak, nonatomic) IBOutlet UIButton *btnSend;
- (IBAction)dismissScene:(id)sender;
- (IBAction)sendBTData:(id)sender;
#end
ViewControllerDev2.m
#import "ViewControllerDev2.h"
#implementation ViewControllerDev2
- (void)viewDidLoad
{
[super viewDidLoad];
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil]; // options:nil is an iOS 7 feature
_data = [[NSMutableData alloc] init];
}
- (void)viewDidUnload {
[self setBtnDone:nil];
[super viewDidUnload];
}
- (IBAction)sendBTData:(id)sender {
//[self sendData];
}
- (IBAction)dismissScene:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
[_centralManager stopScan];
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
// You should test all scenarios
if (central.state != CBCentralManagerStatePoweredOn) {
return;
}
if (central.state == CBCentralManagerStatePoweredOn) {
// Scan for devices
[_centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:BLUNO_TRANSFER_SERVICE_UUID]] options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
NSLog(#"Scanning started");
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
if (_discoveredPeripheral != peripheral) {
// Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
_discoveredPeripheral = peripheral;
// And connect
NSLog(#"Connecting to peripheral %#", peripheral);
[_centralManager connectPeripheral:peripheral options:nil];
// then stop scanning for peripherals
[_centralManager stopScan];
NSLog(#"Scanning stopped");
}
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(#"Failed to connect");
[self cleanup];
}
- (void)cleanup {
// See if we are subscribed to a characteristic on the peripheral
if (_discoveredPeripheral.services != nil) {
for (CBService *service in _discoveredPeripheral.services) {
if (service.characteristics != nil) {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BLUNO_TRANSFER_CHARACTERISTIC_UUID]]) {
if (characteristic.isNotifying) {
[_discoveredPeripheral setNotifyValue:NO forCharacteristic:characteristic];
return;
}
}
}
}
}
}
[_centralManager cancelPeripheralConnection:_discoveredPeripheral];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"Connected");
[_centralManager stopScan];
NSLog(#"Scanning stopped");
[_data setLength:0];
peripheral.delegate = self;
[peripheral discoverServices:#[[CBUUID UUIDWithString:BLUNO_TRANSFER_SERVICE_UUID]]];
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) {
[self cleanup];
return;
}
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:#[[CBUUID UUIDWithString:BLUNO_TRANSFER_CHARACTERISTIC_UUID]] forService:service];
}
// Discover other characteristics
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {
[self cleanup];
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BLUNO_TRANSFER_CHARACTERISTIC_UUID]]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(#"Error");
return;
}
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
// Have we got everything we need?
if ([stringFromData isEqualToString:#"EOM"]) {
//[_textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
[_centralManager cancelPeripheralConnection:peripheral];
}
[_data appendData:characteristic.value];
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:BLUNO_TRANSFER_CHARACTERISTIC_UUID]]) {
return;
}
if (characteristic.isNotifying) {
NSLog(#"Notification began on %#", characteristic);
} else {
// Notification has stopped
[_centralManager cancelPeripheralConnection:peripheral];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
_discoveredPeripheral = nil;
[_centralManager scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:BLUNO_TRANSFER_SERVICE_UUID]] options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
}
#end
Here's your problem:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(#"Discovered %# at %#", peripheral.name, RSSI);
if (_discoveredPeripheral != peripheral) {
// Save a local copy of the peripheral, so CoreBluetooth doesn't get rid of it
_discoveredPeripheral = peripheral;
// And connect
NSLog(#"Connecting to peripheral %#", peripheral);
[_centralManager connectPeripheral:peripheral options:nil]; // <-- this is the issue
// then stop scanning for peripherals
[_centralManager stopScan];
NSLog(#"Scanning stopped");
}
}
You're connecting to a weakly held object, you should connect to your _discoveredPeripheral object instead. Also, don't forget to update your property calls to use your BCPeripheral object, for example:
[_discoveredPeripheral discoverServices:#[[CBUUID UUIDWithString:BLUNO_TRANSFER_SERVICE_UUID]]];