CoreBluetooth For Central (4)

CoreBluetooth For Central (4)

Connect BLE Device

承前面Discover BLE Device所介绍内容,我们修改这个专案更名为BluetoothLE-Connect,这节要完成的项目是从发现装置之后,符合自已设定的周边名称后就自动连线至周边,不过,Xcode的操作细节就不像先前一样做介绍。

取得连线周边的名称

依照之前完成的程式码,当按下Scan时就可以取得周边讯息,范例如下:

2014-03-31 16:33:48.935 BluetoothLE-Connect[3454:60b] Scan And Connect
2014-03-31 16:33:49.032 BluetoothLE-Connect[3454:60b] peripheral
<CBPeripheral: 0x1753d3d0 identifier = 419D6B15-1F6C-EE7B-7751-2748ACA0D7C3, Name = "DannySerialApp", state = disconnected>
2014-03-31 16:33:49.034 BluetoothLE-Connect[3454:60b] advertisementData
{
    kCBAdvDataChannel = 39;
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = DannySimpleBLE;
    kCBAdvDataServiceUUIDs =     (
        FFF0
    );
    kCBAdvDataTxPowerLevel = 0;
}
2014-03-31 16:33:49.036 BluetoothLE-Connect[3454:60b] RSSI
-61
2014-03-31 16:33:49.037 BluetoothLE-Connect[3454:60b] localName:DannySimpleBLE

请将这息讯纪录下来,后面要利用这讯息判断周边是否有存在,当存在时就自动进行连线。

Scan And Connect 按键

将ButtonScan改成Scan And Connect,这是为了要提示使用者,但实际上按下后只有Scan的动作,Connect是在Delegate才会进行,程式码还是不变,只在最前面加上Stop的动作,避免使用者连续按下时会无法重复的Scan。

//-----------start-----------
- (IBAction)buttonScanAndConnect:(id)sender {
    [CM stopScan];
    [CM scanForPeripheralsWithServices:nil options:nil];
    NSLog(@"Scan And Connect");

}
//------------end------------

外观UI变更为:

Scan执行之后就会引发didDiscoverPeripheral,其中的连线是在这处理的,我们继续看下去。

Stop And Disconnect 按键

Button改成Stop and Disconnect,按下之后会停止 Scan动作以及将已连线动作断线,原先的程式:

//-----------start-----------
- (IBAction)buttonStop:(id)sender {

    [CM stopScan];
    NSLog(@"stopScan");
}
//------------end------------

我们连线后会将连线的CBPeripheral物件传至connectPeripheral,所以在按下时能够使用物件的方法去执行断线的动作,当未指定时connectPeripheral = NULL,不需要执行断线命令,将这段判断加入:

//-----------start-----------
    if (connectPeripheral == NULL){
        NSLog(@"connectPeripheral == NULL");
        return;
    }
//------------end------------

接下来再加入断线的动作,在动作之前先判断是否已经连线,但判断是否已连线的状态在iOS7之后有作变更:

  • iOS7
//-----------start-----------
    if (connectPeripheral.state == CBPeripheralStateConnected) {
        [CM cancelPeripheralConnection:connectPeripheral];
        NSLog(@"disconnect-1");

    }
//------------end------------

使用state方法取得状态,状态分为:

    CBPeripheralStateDisconnected
    CBPeripheralStateConnecting
    CBPeripheralStateConnected

  • iOS6
//-----------start-----------
    if ([connectPeripheral isConnected]) {
        [CM cancelPeripheralConnection:connectPeripheral];
        NSLog(@"disconnect-1");
    }
//------------end------------

外观UI变更为:

UI外观都已经完成,后面就会开始加入连线需要的程式码。

连线指定的周边

Scan蓝牙的周边时就会引发didDiscoverPeripheral的Delegate,发现多少周边就会不断的重复执行 didDiscoverPeripheral,我们要在这个情况下,对每个周边进行比对,如果比对是指定要连线的周边就会进行连线,否则程式就不理会。看一下原先的didDiscoverPeripheral程式码:

//-----------start-----------
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

    NSLog(@"peripheral\n%@\n",peripheral);
    NSLog(@"advertisementData\n%@\n",advertisementData);
    NSLog(@"RSSI\n%@\n",RSSI);

}
//------------end------------

我们利用advertisementData资料中的CBAdvertisementDataLocalNameKey取得的周边连线名称来判断周边是否存在,从刚的资料得到:

2014-03-31 16:33:49.034 BluetoothLE-Connect[3454:60b] advertisementData
{
    kCBAdvDataChannel = 39;
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = DannySimpleBLE;
    kCBAdvDataServiceUUIDs =     (
        FFF0
    );
    kCBAdvDataTxPowerLevel = 0;
}

CBAdvertisementDataLocalNameKey取得的就是列表中的kCBAdvDataLocalName内容,结果就是DannySimpleBLE

kCBAdvDataLocalName = DannySimpleBLE;

使用NSString的rangeOfString来比对字串内容是否相同,判断的程式为:

//-----------start-----------
if ([localName length] && [localName rangeOfString:@"DannySimpleBLE"].location != NSNotFound) {
:
:
}
//------------end------------

最后,我们加入一些判断需不需要连线至周边的程式码就会是下面的内容:

//-----------start-----------
    NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];

    NSLog(@"localName:%@",localName);

    if ([localName length] && [localName rangeOfString:@"DannySimpleBLE"].location != NSNotFound) {
        //抓到周边后就立即停子Scan
        [CM stopScan];
        NSLog(@"stopScan");
        connectPeripheral = peripheral;
        [CM connectPeripheral:peripheral options:nil];
        NSLog(@"connect to %@",peripheral.name);
    }
//------------end------------

其实确定连线时,会将目前连线的CBPeripheral指定至connectPeripheral,配合按下Stop and Disconnect,整个程式码修改成:

//-----------start-----------
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

    NSLog(@"peripheral\n%@\n",peripheral);
    NSLog(@"advertisementData\n%@\n",advertisementData);
    NSLog(@"RSSI\n%@\n",RSSI);

    NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];

    NSLog(@"localName:%@",localName);

    if ([localName length] && [localName rangeOfString:@"DannySimpleBLE"].location != NSNotFound) {
        //抓到周边后就立即停止Scan
        [CM stopScan];
        NSLog(@"stopScan");
        connectPeripheral = peripheral;
        [CM connectPeripheral:peripheral options:nil];
        NSLog(@"connect to %@",peripheral.name);
    }

}
//------------end------------

是否已连线至周边

已连线、已断线一旦发生时就会引发2个Delegate,直接看程式内容。

已连线

//-----------start-----------
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {

    NSLog(@"%@",@"connected");
}
//------------end------------

已断线

//-----------start-----------
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {

    NSLog(@"%@",@"disconnect-2");
}
//------------end------------

所以程式增加了已连线已断线 状态显示于Debug中,在这部分也都完成这次范例程式的解说。

执行范例程式

我们将范例程式执行之后,按下选单的Scan and Connect,接下来看一下Debug视窗会看到讯息。

下面为输出的讯息结果:

图片字太小的话,下面是文字讯息:

2014-03-31 17:25:59.000 BluetoothLE-Connect[3465:60b] UpdateState:PoweredOn
2014-03-31 17:26:02.021 BluetoothLE-Connect[3465:60b] Scan And Connect
2014-03-31 17:26:02.115 BluetoothLE-Connect[3465:60b] peripheral
<CBPeripheral: 0x15559e00 identifier = 419D6B15-1F6C-EE7B-7751-2748ACA0D7C3, Name = "DannySerialApp", state = disconnected>
2014-03-31 17:26:02.118 BluetoothLE-Connect[3465:60b] advertisementData
{
    kCBAdvDataChannel = 39;
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = DannySimpleBLE;
    kCBAdvDataServiceUUIDs =     (
        FFF0
    );
    kCBAdvDataTxPowerLevel = 0;
}
2014-03-31 17:26:02.120 BluetoothLE-Connect[3465:60b] RSSI
-61
2014-03-31 17:26:02.121 BluetoothLE-Connect[3465:60b] localName:DannySimpleBLE
2014-03-31 17:26:02.123 BluetoothLE-Connect[3465:60b] stopScan
2014-03-31 17:26:02.125 BluetoothLE-Connect[3465:60b] connect to DannySerialApp
2014-03-31 17:26:02.224 BluetoothLE-Connect[3465:60b] connected


结果中能看到在程式中的connected的Debug讯息,当我们按下按键上的Stop And Disconnect,就会立即断线

Debug讯息看到在Button中提示disconnect-1及在didDisconnectPeripheral中的disconnect-2提示出现,也就是当我们执行断线cancelPeripheralConnection时就会引发Delegate让开发者可以在断线时做一些程式的处理逻辑。

范例程式

额外说明

程式执行时,如果您需要再利用Deubg讯息观看Scan所有周边的结果,我们Scan and Connect程式内容修改一下:

//-----------start-----------
if ([localName length] && [localName rangeOfString:@"DannySimpleBLE"].location != NSNotFound) {
//------------end------------

修改成

//-----------start-----------
if ([localName length] && [localName rangeOfString:@""].location != NSNotFound) {
//------------end------------

将比对的名称改成空白,使比对条件不符合就能列出所有已Scan到的BLE周边。