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周邊。