iOS藍牙開發 Bluetooth藍牙CoreBluetooth 藍牙中心設備的實現 藍牙外設的實現 有Demo

轉載請註明本文地址:www.jianshu.com/p/38a4c6451…git

目的

最近公司在作一個iOS藍牙項目,在開發的過程當中簡單整理了一些與之相關的基礎知識,在這裏分享一下。整理包括如下內容:github

一、iOS藍牙開發的關鍵詞 二、藍牙的簡單介紹 三、CoreBluetooth框架 四、實現iOS藍牙外設(Demo) 五、實現iOS藍牙中心設備(Demo框架

Demo的運行gif圖以下,中心設備能夠從外設讀取數據,也能夠向外設寫入數據。外設也能夠向中心設備發送數據。 PS:須要使用真機測試。學習

藍牙外設與中心設備之間的數據傳輸

iOS的藍牙開發是圍繞着CoreBluetooth框架來實現的。 下面先從iOS藍牙開發的基本概念提及。測試

1、iOS藍牙開發的關鍵詞

中心設備:就是用來掃描周圍藍牙硬件的設備,好比經過你手機的藍牙來掃描並鏈接智能手環,這時候你的手機就是中心設備。ui

外設:被掃描的設備。好比當你用手機的藍牙掃描鏈接智能手環的時候,智能手環就是外設。編碼

中心設備和外設

廣播:就是外設不停的散播藍牙信號,讓中心設備能夠掃描到。atom

外設廣播

服務(services):外設廣播和運行的時候會有服務,能夠理解成一個功能模塊,中心設備能夠讀取服務。外設能夠有多個服務。url

特徵(characteristic):在服務中的一個單位,一個服務能夠有多個特徵,特徵會有一個value,通常讀寫的數據就是這個value。spa

服務和特徵.png

UUID:區分不一樣的服務和特徵,能夠理解爲服務和特徵的身份證。咱們能夠用UUID來挑選須要的服務和特徵。

2、藍牙的簡單介紹

偷個懶:藍牙百科 藍牙( **Bluetooth® **):是一種短距離無線通訊技術 ,可實現固定設備、移動設備和樓宇我的域網之間的短距離數據交換(使用2.4—2.485GHz的ISM波段的UHF無線電波)。藍牙4.2發佈於2014年12月2日,本文發佈的時候,藍牙的最新版本爲4.2。

3、CoreBluetooth框架

藍牙開發層次圖

如上圖所示,iOS中的藍牙開發框架CoreBluetooth處在藍牙低功耗協議棧的上面,咱們開發的時候只是使用CoreBluetooth這個框架,經過CoreBluetooth能夠輕鬆實現外設或中心設備的開發。

CoreBluetooth能夠分爲兩大模塊,中心設備central,外設peripheral,它們倆各有本身的一套API供咱們使用。

中心設備和外設使用.png

上圖左邊的就是中心設備的開發類,咱們平時是使用CBCentralManager來進行相關操做。

  • CBCentralManager: 藍牙中心設備管理類,用來統一調度中心設備的開發
  • CBPeripheral :藍牙外設,例如藍牙手環、心率監測儀。
  • CBService :藍牙外設的服務,能夠有0個或者多個服務。
  • CBCharacteristic :服務中的特徵,每個藍牙服務中能夠有0個或多個特徵,特徵中包含數據信息。
  • CBUUID:能夠理解爲服務或特徵的身份證,能夠用來選擇須要的服務和特徵。

右邊是外設開發相關類,通常是圍繞着CBPeripheralManager來進行編碼。

  • CBPeripheralManager: 藍牙外設開發時使用,用來開發藍牙外設的中心管理類。
  • CBCentral:藍牙中心設備,例如用來鏈接藍牙手環的手機。
  • CBMutableService:外設開發的時候能夠添加多個服務,全部這裏用CBMutableService來建立添加服務。
  • CBMutableCharacteristic:每一個服務中能夠有多個特徵,外設開發給服務添加特徵的時候使用這個類。
  • CBATTRequest:讀或者寫請求。它的實例對象有一個value屬性,用來裝載外設進行藍牙讀取或寫入請求時的數據。通常在外設寫入或讀取的回調方法中有這一個參數。
    外設添加服務和特徵

4、實現iOS藍牙外設(Demo

外設管理器

一、首先導入CoreBluetooth框架,並遵照協議

#import <CoreBluetooth/CoreBluetooth.h>
// 遵照CBPeripheralManagerDelegate協議
@interface ViewController () <CBPeripheralManagerDelegate>
複製代碼

二、建立外設管理對象,用一個屬性來強引用這個對象。而且在建立的時候設置代理,聲明放到哪一個線程。

@property (nonatomic, strong) CBPeripheralManager *peripheralManager;

// 建立外設管理器,會回調peripheralManagerDidUpdateState方法
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
複製代碼

三、當建立CBPeripheralManager的時候,會回調判斷藍牙狀態的方法。當藍牙狀態沒問題的時候建立外設的Service(服務)和Characteristics(特徵)。

/* 設備的藍牙狀態 CBManagerStateUnknown = 0, 未知 CBManagerStateResetting, 重置中 CBManagerStateUnsupported, 不支持 CBManagerStateUnauthorized, 未驗證 CBManagerStatePoweredOff, 未啓動 CBManagerStatePoweredOn, 可用 */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    if (peripheral.state == CBManagerStatePoweredOn) {
        // 建立Service(服務)和Characteristics(特徵)
        [self setupServiceAndCharacteristics];
        // 根據服務的UUID開始廣播
        [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}];
    }
}
複製代碼

能夠先用宏來作兩個標識字符串,用來建立服務和特徵的UUID。 最終把建立好的特徵放進服務,把服務放入中心管理器。

#define SERVICE_UUID @"CDD1"
#define CHARACTERISTIC_UUID @"CDD2"

/** 建立服務和特徵 */
- (void)setupServiceAndCharacteristics {
    // 建立服務
    CBUUID *serviceID = [CBUUID UUIDWithString:SERVICE_UUID];
    CBMutableService *service = [[CBMutableService alloc] initWithType:serviceID primary:YES];
    // 建立服務中的特徵
    CBUUID *characteristicID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID];
    CBMutableCharacteristic *characteristic = [
                                               [CBMutableCharacteristic alloc]
                                               initWithType:characteristicID
                                               properties:
                                               CBCharacteristicPropertyRead |
                                               CBCharacteristicPropertyWrite |
                                               CBCharacteristicPropertyNotify
                                               value:nil
                                               permissions:CBAttributePermissionsReadable |
                                               CBAttributePermissionsWriteable
                                               ];
    // 特徵添加進服務
    service.characteristics = @[characteristic];
    // 服務加入管理
    [self.peripheralManager addService:service];
    
    // 爲了手動給中心設備發送數據
    self.characteristic = characteristic;
}
複製代碼

注意CBCharacteristicPropertyNotify這個參數,只有設置了這個參數,在中心設備中才能訂閱這個特徵。 通常開發中能夠設置兩個特徵,一個用來發送數據,一個用來接收中心設備寫過來的數據,咱們這裏爲了方便就只設置了一個特徵。 最後用一個屬性拿到這個特徵,是爲了後面單獨發送數據的時候使用,數據的寫入和讀取最終仍是要經過特徵來完成。

四、當中心設備讀取這個外設的數據的時候會回調這個方法。

/** 中心設備讀取數據的時候回調 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
    // 請求中的數據,這裏把文本框中的數據發給中心設備
    request.value = [self.textField.text dataUsingEncoding:NSUTF8StringEncoding];
    // 成功響應請求
    [peripheral respondToRequest:request withResult:CBATTErrorSuccess];
}
複製代碼

五、當中心設備寫入數據的時候,外設會調用下面這個方法。

/** 中心設備寫入數據的時候回調 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
    // 寫入數據的請求
    CBATTRequest *request = requests.lastObject;
    // 把寫入的數據顯示在文本框中
    self.textField.text = [[NSString alloc] initWithData:request.value encoding:NSUTF8StringEncoding];
}
複製代碼

六、還有一個主動給中心設備發送數據的方法。

/** 經過固定的特徵發送數據到中心設備 */
- (IBAction)didClickPost:(id)sender {
    BOOL sendSuccess = [self.peripheralManager updateValue:[self.textField.text dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristic onSubscribedCentrals:nil];
    if (sendSuccess) {
        NSLog(@"數據發送成功");
    }else {
        NSLog(@"數據發送失敗");
    }
}
複製代碼

七、中心設備訂閱成功的時候回調。

/** 訂閱成功回調 */
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"%s",__FUNCTION__);
}
複製代碼

八、中心設備取消訂閱的時候回調。

/** 取消訂閱回調 */
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"%s",__FUNCTION__);
}
複製代碼

以上就是iOS藍牙外設的基本實現流程,固然還有更多的地方能夠進一步處理,這就須要投入更多的時間來學習實驗了。

下面進入iOS藍牙開發的主要部分,中心設備的實現,這也是手機App一般擔任的角色。

5、實現iOS藍牙中心設備(Demo

中心設備管理器

一、同外設開發同樣,首先要導入CoreBluetooth框架。

#import <CoreBluetooth/CoreBluetooth.h>
複製代碼

二、遵照的協議與外設開發不一樣,中心設備的開發須要遵循以下兩個協議。

@interface ViewController () <CBCentralManagerDelegate,CBPeripheralDelegate>
複製代碼

三、建立中心管理器並用屬性強引用,建立的時候也會設置代理和選擇線程。

@property (nonatomic, strong) CBCentralManager *centralManager;

// 建立中心設備管理器,會回調centralManagerDidUpdateState
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
複製代碼

四、當建立中心管理對象的時候,會回調以下方法用來判斷中心設備的藍牙狀態。當藍牙狀態沒問題的時候,能夠根據外設服務的UUID來掃描須要的外設。因此天然而然的就想到了要定義與外設UUID相同的宏。

/** 判斷手機藍牙狀態 */
#define SERVICE_UUID @"CDD1"
#define CHARACTERISTIC_UUID @"CDD2"


- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    // 藍牙可用,開始掃描外設
    if (central.state == CBManagerStatePoweredOn) {
        NSLog(@"藍牙可用");
        // 根據SERVICE_UUID來掃描外設,若是不設置SERVICE_UUID,則掃描全部藍牙設備
        [central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]] options:nil];
    }
    if(central.state==CBCentralManagerStateUnsupported) {
        NSLog(@"該設備不支持藍牙");
    }
    if (central.state==CBCentralManagerStatePoweredOff) {
        NSLog(@"藍牙已關閉");
    }
}
複製代碼

五、當掃描到外設以後,就會回調下面這個方法,能夠在這個方法中繼續設置篩選條件,例如根據外設名字的前綴來選擇,若是符合條件就進行鏈接。

/** 發現符合要求的外設,回調 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    // 對外設對象進行強引用
    self.peripheral = peripheral;
    
// if ([peripheral.name hasPrefix:@"WH"]) {
// // 能夠根據外設名字來過濾外設
// [central connectPeripheral:peripheral options:nil];
// }
    
    // 鏈接外設
    [central connectPeripheral:peripheral options:nil];
}
複製代碼

七、當鏈接成功的時候,就會來到下面這個方法。爲了省電,當鏈接上外設以後,就讓中心設備中止掃描,而且別忘記設置鏈接上的外設的代理。在這個方法里根據UUID進行服務的查找。

/** 鏈接成功 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    // 能夠中止掃描
    [self.centralManager stopScan];
    // 設置代理
    peripheral.delegate = self;
    // 根據UUID來尋找服務
    [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
    NSLog(@"鏈接成功");
}
複製代碼

八、鏈接失敗和斷開鏈接也有各自的回調方法。在斷開鏈接的時候,咱們能夠設置自動重連,根據項目需求來自定義裏面的代碼。

/** 鏈接失敗的回調 */
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"鏈接失敗");
}

/** 斷開鏈接 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
    NSLog(@"斷開鏈接");
    // 斷開鏈接能夠設置從新鏈接
    [central connectPeripheral:peripheral options:nil];
}
複製代碼

九、下面開始處理代理方法。 最開始就是發現服務的方法。這個方法裏能夠遍歷服務,找到須要的服務。因爲上面作的外設只有一個服務,因此我這裏直接取服務中的最後一個lastObject就好了。 找到服務以後,連貫的動做繼續根據特徵的UUID尋找服務中的特徵。

/** 發現服務 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    
    // 遍歷出外設中全部的服務
    for (CBService *service in peripheral.services) {
        NSLog(@"全部的服務:%@",service);
    }
    
    // 這裏僅有一個服務,因此直接獲取
    CBService *service = peripheral.services.lastObject;
    // 根據UUID尋找服務中的特徵
    [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service];
}
複製代碼

十、下面這個方法裏作的事情很多。 當發現特徵以後,與服務同樣能夠遍歷特徵,根據外設開發人員給的文檔找出不一樣特徵,作出相應的操做。 個人外設只設置了一個特徵,因此也是直接經過lastObject拿到特徵。 再重複一遍,通常開發中能夠設置兩個特徵,一個用來發送數據,一個用來接收中心設備寫過來的數據。 這裏用一個屬性引用特徵,是爲了後面經過這個特徵向外設寫入數據或發送指令。 readValueForCharacteristic方法是直接讀一次這個特徵上的數據。

/** 發現特徵回調 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    
    // 遍歷出所須要的特徵
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"全部特徵:%@", characteristic);
        // 從外設開發人員那裏拿到不一樣特徵的UUID,不一樣特徵作不一樣事情,好比有讀取數據的特徵,也有寫入數據的特徵
    }
    
    // 這裏只獲取一個特徵,寫入數據的時候須要用到這個特徵
    self.characteristic = service.characteristics.lastObject;
    
    // 直接讀取這個特徵數據,會調用didUpdateValueForCharacteristic
    [peripheral readValueForCharacteristic:self.characteristic];
    
    // 訂閱通知
    [peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
}
複製代碼

setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic方法是對這個特徵進行訂閱,訂閱成功以後,就能夠監控外設中這個特徵值得變化了。

十一、當訂閱的狀態發生改變的時候,下面的方法就派上用場了。

/** 訂閱狀態的改變 */
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"訂閱失敗");
        NSLog(@"%@",error);
    }
    if (characteristic.isNotifying) {
        NSLog(@"訂閱成功");
    } else {
        NSLog(@"取消訂閱");
    }
}
複製代碼

十二、外設能夠發送數據給中心設備,中心設備也能夠從外設讀取數據,當發生這些事情的時候,就會回調這個方法。經過特種中的value屬性拿到原始數據,而後根據需求解析數據。

/** 接收到數據回調 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    // 拿到外設發送過來的數據
    NSData *data = characteristic.value;
    self.textField.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
複製代碼

1三、中心設備能夠向外設寫入數據,也能夠向外設發送請求或指令,當須要進行這些操做的時候該怎麼辦呢。

  • 首先把要寫入的數據轉化爲NSData格式,而後根據上面拿到的寫入數據的特徵,運用方法writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type來進行數據的寫入。

  • 當寫入數據的時候,系統也會回調這個方法peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error 。

/** 寫入數據 */
- (IBAction)didClickPost:(id)sender {
    // 用NSData類型來寫入
    NSData *data = [self.textField.text dataUsingEncoding:NSUTF8StringEncoding];
    // 根據上面的特徵self.characteristic來寫入數據
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}


/** 寫入數據回調 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
    NSLog(@"寫入成功");
}
複製代碼

1四、中心設備如何主動從外設讀取數據呢。

  • 用正在鏈接的外設對象來調用readValueForCharacteristic方法,而且把將要讀取數據的特徵做爲參數,這樣就能夠主動拿一次數據了。 去到第12步的回調方法中,在特徵的value屬性中拿到此次的數據。
/** 讀取數據 */
- (IBAction)didClickGet:(id)sender {
    [self.peripheral readValueForCharacteristic:self.characteristic];
}
複製代碼

後記

中心設備的開發是須要配合外設來進行的,通常會有硬件工程師或嵌入式工程師給出通訊協議,根據協議來對項目的各類需求進行操做。

本文所述的示例代碼在這裏:Demo

github地址:github.com/remember17

相關文章
相關標籤/搜索