CoreBluetooth框架使用

藍牙3.0協議由于高速度,通常用于音視頻播放
藍牙4.0協議以低功耗著稱,傳輸速度慢,一般用于控制藍牙設備
這里介紹iOS的CoreBluetooth框架,該框架支持藍牙4.0協議,目前使用的非常多

主要概念:

中心設備:對應的類是CBCentralManager,中心設備就是手機,用于掃描和連接藍牙
周邊設備:對應的類是CBPeripheral,周邊設備就是藍牙設備,用于數據的傳輸處理,當然了沒有絕對的中心設備或者周邊設備,他們是互通的,根據不同的需求,通常一個App中的中心設備和周邊設備的功能是整合在一起的
服務:對應類CBService,一個周邊設備可以包含若干個服務,通過廣播的形式供中心設備發現和使用。
特征:對應類CBCharacteristic,一個服務中可以包含若干個特征,特征是我們發送或接受數據的地方
UUID:對應類CBUUID,每個周邊設備、服務和特征都有一個UUID來唯一標識自己。UUID有16bit、32bit和128bit三種,進行UUID比較時要將低位的UUID轉換成高位的UUID再進行比較

下面是使用這些類的一般步驟:
1??創建中心設備管理者

中心設備管理者主要用于掃描和連接藍牙設備,
使用[[CBCentralManager alloc] initWithDelegate:self queue:nil]方法創建管理者,
遵守對應的協議CBCentralManagerDelegate

2??使用中心管理者掃描藍牙設備

使用如下方法掃描藍牙
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
參數為nil表示掃描所有藍牙,當掃描到藍牙設備時會調用如下代理方法
-(void)centralManager: didDiscoverPeripheral: advertisementData: RSSI:
需要注意的是每掃描到一個藍牙設備都會調用一次這個方法,RSSI是該藍牙的信號強度,一般來說距離越遠信號越弱,RSSI的值越接近0信號越強。通常會在這個方法中將信號較弱的藍牙設備屏蔽掉,也可以在這個方法中根據藍牙名稱peripheral.name來過濾不想要的藍牙設備
將掃描到的藍牙設備用數組保存起來,通過tableview展示出來

3??連接藍牙

點擊tableview的某一行,連接該行對應的藍牙設備,使用如下方法連接藍牙
[self.centralManager connectPeripheral:peripheral options:nil];
連接成功后會調用如下代理方法
-(void)centralManager: didConnectPeripheral:
在代理方法中設置已連接的藍牙設備的代理peripheral.delegate = self;
遵守相關協議CBPeripheralDelegate,該協議和數據傳輸有關。
在代理方法中還要做的一件事就是搜索服務,使用如下方法搜索服務
[peripheral discoverServices:nil];

4??搜索服務

在上一步我們已經搜索了服務,服務搜索到之后會調用如下代理方法
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:
需要注意的是這個方法只調用一次,并且所有的服務通過peripheral.services來獲取

5??搜索特征

我們需要在上一步的代理方法中逐個的搜索所有服務中的所有特征,通過for循環來遍歷吧
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
搜索到特征后會調用如下代理方法
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:
需要注意的是有多少個服務這個方法就會調用多少次,服務中的特征通過如下方法獲得
service.characteristics

6??處理特征
我們接收、讀取和監聽數據的所有操作都是在特征這一層來完成的,常用的特征有下面幾個,特征的功能是公司定的,一切以實際為準,下面三個是常用的功能。

1、用于讀取數據的特征:有些特征是負責讀取數據的,如果我們要主動讀取一些數據,需要通過這個特征來讀取數據,對應的讀取方法如下
[self.currentConnectPeripheral readValueForCharacteristic:ReadCharacteristic];
讀取到數據后會調用如下代理方法,
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:
通過characteristic.value獲取數據然后進行解析

2、用于寫入數據的特征:有些特征是負責寫入操作的,比如我們要同步時間,需要將手機的時間發送到藍牙設備,這個時候就需要用到這個特征,調用如下方法寫入數據
[self.currentConnectPeripheral writeValue:data forCharacteristic:WriteCharacteristic type:CBCharacteristicWriteWithResponse];
寫入成功后會調用如下代理方法
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
需要注意的是藍牙4.0協議,每次發送或接收的最大數據長度為20個字節,如果數據過長則需要分包發送,接收的時候也是一樣,如果命令過長則會分包接收命令,然后拼接命令

3、用于監聽數據的特征:有些特征是用于數據監聽的,比如藍牙設備會不定時的發送一些信息,如溫度信息,由于不定時,所以讀取數據的特征不太現實,只能用監聽數據的特征,只要該特征中的數值發生變化就會觸發如下代理方法
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:
不過這個特征比較特殊,需要多一步操作,設置監聽,使用如下方法設置監聽
[self.currentConnectPeripheral setNotifyValue:YES forCharacteristic:NotifyCharacter];

以下是完整的藍牙管理類代碼
.h

#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>

#define BLEMANAGER [BLEManager manager]
@protocol BLEManagerDelegate <NSObject>
@optional
//已經發現藍牙設備
-(void)BLE_didDiscoverPeripherals:(NSMutableArray<CBPeripheral *>*)peripherals;
//已經連接到藍牙設備
-(void)BLE_didConnectPeripheral:(CBPeripheral*)peripherial;
//連接錯誤
-(void)BLE_connectError;
//已經斷開藍牙外設
-(void)BLE_didDisconnectPeripheral:(CBPeripheral*)peripheral;
//已經掃描完畢
-(void)BLE_didEndScan;
@end

@interface BLEManager : NSObject
@property (nonatomic,weak) id<BLEManagerDelegate> delegate;
//掃描到的藍牙設備(通過了名字和信號強度rssi的篩選)
@property (nonatomic,strong) NSMutableArray<CBPeripheral*> *discoveredPeripherals;

//單粒方法
+ (instancetype)manager;
//初始化中心管理者
- (void)CL_initializeCentralManager;

#pragma mark - d
//藍牙是否正在連接
- (BOOL)CL_IsConnecttingPeripheral;
//開始掃描外圍設備 設置超時
- (void)CL_StartScanDeviceWithTimeout:(NSTimeInterval)timeout;
//開始連接指定的藍牙設備
- (void)CL_StartConnectPeripheral:(CBPeripheral *)peripheral;
//斷開當前連接的藍牙設備
- (void)CL_DisconnectPeripheral:(CBPeripheral*)peripheral;
//寫入數據
-(void)CL_writeValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID data:(NSData *)data

@end

.m

#import "BLEManager.h"
#import <CoreBluetooth/CoreBluetooth.h>
#define RSSI_blueTooth 80    //可接受的外圍藍牙信號強度最低值 rssi通常為負數 只要大于-50 藍牙信號強度就可接受
typedef struct _CHAR{
    char buff[1000];
}CHAR_STRUCT;

@interface BLEManager()<CBCentralManagerDelegate,CBPeripheralDelegate>
//藍牙中心管理者
@property (nonatomic,strong) CBCentralManager *centralManager;
//掃描狀態
@property (nonatomic,assign) BOOL isScan;
//掃描超時定時器
@property (nonatomic,strong) NSTimer *timeoutTimer;
//準備連接的藍牙外設
@property (nonatomic,strong) CBPeripheral *prepareConnectPeripheral;
//當前已經連接的藍牙外設
@property (nonatomic,strong) CBPeripheral *currentConnectPeripheral;
//藍牙的連接狀態 是否正在連接
@property (nonatomic,assign) BOOL isConnecttingBluetooth;

//特征值
@property (nonatomic,strong) CBCharacteristic *writeCharacteristic;  //寫入通道
@property (nonatomic,strong) CBCharacteristic *readCharacteristic;   //讀取通道
@property (nonatomic,strong) CBCharacteristic *notifyCharacteristic; //監聽通道

//藍牙返回數據 或命令
@property (nonatomic,strong) NSMutableArray *deviceCallBack;
@end

@implementation BLEManager
#pragma mark - 初始化管理者
//單粒方法
+ (instancetype)manager
{
    static BLEManager *sharedInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

//初始化中心管理者  只需在appdelegate中初始化一次即可
- (void)CL_initializeCentralManager
{
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    self.isScan = NO;
    self.isConnecttingBluetooth = NO;
    self.discoveredPeripherals = [NSMutableArray array];
    self.deviceCallBack = [NSMutableArray array];
}

#pragma mark - 藍牙的掃描和連接
//藍牙是否正在連接
- (BOOL)CL_IsConnecttingPeripheral{
    return self.isConnecttingBluetooth;
}

//開始掃描外圍設備 設置超時
- (void)CL_StartScanDeviceWithTimeout:(NSTimeInterval)timeout
{
    //設置默認超時時間
    if (timeout < 0 || timeout > 60) {
        timeout = 5;
    }
    if (self.centralManager.state != CBManagerStatePoweredOn) {
        NSLog(@"手機藍牙未開啟!");
        return;
    }
    //停止上一次的定時器
    [self destroyTimeoutTimer];
    //掃描前清空之前的設備記錄
    [self.discoveredPeripherals removeAllObjects];
    //再重新開啟一個定時器
    if (self.timeoutTimer == nil) {
        self.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:@selector(timeout) userInfo:nil repeats:NO];
    }
    [self.centralManager stopScan];
    //開始掃描之前一定要判斷centralManager的state是否為CBManagerStatePoweredOn 否則將不會掃描
    [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    self.isScan = YES;
}

//掃描超時回調
-(void)timeout{
    [self destroyTimeoutTimer];
    [self.centralManager stopScan];
    self.isScan = NO;
    if ([self.delegate respondsToSelector:@selector(BLE_didEndScan)])
        [self.delegate BLE_didEndScan];
}

//銷毀定時器
-(void)destroyTimeoutTimer
{
    [self.timeoutTimer invalidate];
    self.timeoutTimer = nil;
}

//開始連接指定的藍牙外設
-(void)CL_StartConnectPeripheral:(CBPeripheral *)peripheral
{
    if (self.centralManager.state != CBManagerStatePoweredOn) {
        NSLog(@"手機藍牙未開啟!");
        return;
    }
    peripheral.delegate = self;   //設置外設代理
    if (peripheral) {
        self.prepareConnectPeripheral = peripheral;
        peripheral.delegate = self;
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}

//斷開當前連接的藍牙
-(void)CL_DisconnectPeripheral:(CBPeripheral*)peripheral{
    [self.centralManager cancelPeripheralConnection:peripheral];
}

#pragma mark - 數據寫入和讀取
/**
 *  通用寫入數據,發送給外設
 */
-(void)CL_writeValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID data:(NSData *)data{
    
    CBUUID *su = [self UUIDWithNumber:serviceUUID];      //獲得serviceUUID         服務uuid
    CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //獲得characteristicUUID  特征uuid
    CBService *service = [self searchServiceWithUUID:su];        //搜索對應的服務
    if (service) {
        CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
        if (characteristic) {
            [self.currentConnectPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
        }
    }
}

// 通用從外設讀取數據
-(void)CL_readValue:(int)serviceUUID characteristicUUID:(int)characteristicUUID{
    
    CBUUID *su = [self UUIDWithNumber:serviceUUID];             //獲得serviceUUID         服務uuid
    CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //獲得characteristicUUID  特征uuid
    CBService *service = [self searchServiceWithUUID:su];        //搜索對應的服務
    if (service) {
        CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
        if (characteristic) {
            [self.currentConnectPeripheral readValueForCharacteristic:characteristic];    
        }
    }
}

// 通用該外設注冊通知狀態是否活躍   根據isActive值 開啟或者關閉對某個特征值的監聽
-(void)CL_notification:(int)serviceUUID characteristicUUID:(int)characteristicUUID isActive:(BOOL)isActive{
 
    CBUUID *su = [self UUIDWithNumber:serviceUUID];      //獲得serviceUUID         服務uuid
    CBUUID *cu = [self UUIDWithNumber:characteristicUUID];      //獲得characteristicUUID  特征uuid
    CBService *service = [self searchServiceWithUUID:su];
    if (service) {
        CBCharacteristic *characteristic = [self searchCharacteristicWithUUID:cu andService:service];
        if (characteristic) {
            //設置特征值變化的通知
            [self.currentConnectPeripheral setNotifyValue:isActive forCharacteristic:characteristic];
        }
    }
}

//在服務中搜索特征
-(CBCharacteristic *)searchCharacteristicWithUUID:(CBUUID *)cu andService:(CBService *)service
{
    CBCharacteristic *characteristic = nil;   //在服務中搜索特征
    for(int i=0; i < service.characteristics.count; i++) {
        CBCharacteristic *c = [service.characteristics objectAtIndex:i];
        if ([c.UUID isEqual:cu]) {
            characteristic = c;
            break;
        }
    }
    return characteristic;
}

//搜索服務
-(CBService *)searchServiceWithUUID:(CBUUID *)su
{
    CBService *service = nil;   //搜索對應的服務
    for(int i = 0; i < self.currentConnectPeripheral.services.count; i++) {
        CBService *s = [self.currentConnectPeripheral.services objectAtIndex:i];
        if ([s.UUID isEqual:su]) {
            service = s;
            break;
        }
    }
    return service;
}

-(CBUUID *)UUIDWithNumber:(int)number
{
    UInt16 s = [self _swap:number];
    NSData *sd = [[NSData alloc] initWithBytes:(char *)&s length:2];
    return [CBUUID UUIDWithData:sd];
}

- (UInt16)_swap:(UInt16) s{
    UInt16 temp = s << 8;
    temp |= (s >> 8);      //表示 temp = temp | (s >> 8)  按位或
    return temp;
}

//十六進制的字符串To String
- (NSString *)stringFromHexString:(NSString *)hexString {
    if (([hexString length] % 2) != 0)
        return nil;
    
    NSMutableString *string = [NSMutableString string];
    
    for (NSInteger i = 0; i < [hexString length]; i += 2) {
        
        NSString *hex = [hexString substringWithRange:NSMakeRange(i, 2)];
        NSInteger decimalValue = 0;
        sscanf([hex UTF8String], "%lx", &decimalValue);
        [string appendFormat:@"%ld", (long)decimalValue];
    }
    return string;
}

#pragma mark - CBCentralManagerDelegate 中心設備代理方法
/**
 *  中心管理者更新狀態
 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state == CBManagerStatePoweredOff) {  
        self.isConnecttingBluetooth = NO;
        self.currentConnectPeripheral = nil;   
        if ([self.delegate respondsToSelector:@selector(BLE_didDisconnectPeripheral:)])
            [self.delegate BLE_didDisconnectPeripheral:self.currentConnectPeripheral];
        NSLog(@"斷開連接");
    }
}

//中心設備發現外圍設備的回調
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    if (peripheral.name == nil) {
        return;
    }
    
    //按名字和信號強度篩選藍牙設備
if ([peripheral.name containsString:@"BLE"]) {
        if (RSSI.intValue > -RSSI_blueTooth) {
//            NSLog(@"掃描到的設備 = %@",peripheral);
            BOOL isRepetitive = NO;   //重復標記
            for (CBPeripheral *peripher in self.discoveredPeripherals){    //查重
                if ([peripheral.identifier.UUIDString isEqualToString:peripher.identifier.UUIDString])
                    isRepetitive = YES;   //有重復的
            }
            if (!isRepetitive)
                [self.discoveredPeripherals addObject:peripheral];
        }
}
    //通知代理
    if ([self.delegate respondsToSelector:@selector(BLE_didDiscoverPeripherals:)])
        [self.delegate BLE_didDiscoverPeripherals:self.discoveredPeripherals];
}

//中心設備成功連接外圍設備
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    self.isConnecttingBluetooth = YES;
    self.currentConnectPeripheral = peripheral;
    //搜索服務
    [self.currentConnectPeripheral discoverServices:nil];
    //通知代理
    if ([self.delegate respondsToSelector:@selector(BLE_didConnectPeripheral:)])
        [self.delegate BLE_didConnectPeripheral:peripheral];
    NSLog(@"已連接的設備%@",peripheral); 
}

//連接失敗
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    if ([self.delegate respondsToSelector:@selector(BLE_connectError)])
        [self.delegate BLE_connectError];
}

//設備斷開連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
    self.isConnecttingBluetooth = NO;
    self.currentConnectPeripheral = nil;
    //通知代理
    if ([self.delegate respondsToSelector:@selector(BLE_didDisconnectPeripheral:)])
        [self.delegate BLE_didDisconnectPeripheral:peripheral];
    NSLog(@"斷開的設備%@",peripheral);
}


#pragma mark - CBPeripheralDelegate 藍牙外設代理
//發現藍牙的服務
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
{
    if (error) {
        return;
    }
    NSLog(@"發現服務 = %@ count = %lu",peripheral.services,peripheral.services.count);
    //去發現所有服務中的所有特征
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:nil forService:service];
    }
}


//發現服務中的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
    if (error) {
        return;
    }
//公司不同 規定的特征的UUID可能也不同,
    for (CBCharacteristic *character in service.characteristics) {
        NSLog(@"發現服務中的特征%@",character);
        // 寫入數據的特征值 0xXXXX
        if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]]) {
            self.writeCharacteristic = character;
            continue;
        }
        // 讀取數據的特征值 0xXXXX
        if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]] ) {
            self.readCharacteristic = character;
            [self CL_readValue:0xXXXX characteristicUUID:0xXXXX];
            continue;
        }
        // 注冊監聽通道的特征值 0xXXXX
        if ([character.UUID isEqual:[CBUUID UUIDWithString:@"0000XXXX-0000-1000-8000-00805F9B34FB"]] ) {
            self.notifyCharacteristic = character;
            [self.currentConnectPeripheral setNotifyValue:YES forCharacteristic:character];    //監聽通道開啟監聽
            continue;
        }
    }
}

//特征值更新
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (error) {
        NSLog(@"特征值更新錯誤%@",error);
        return;
    }
    //處理特征值,解析數據,公司不同解析規則也不同
    NSData *receiveData = characteristic.value;
    Byte *byte = (Byte *)[receiveData bytes];         
    [self.deviceCallBack removeAllObjects];
    [self.deviceCallBack addObject:receiveData];
        
    if (self.delegate && [self.delegaterespondsToSelector:@selector(getValueForPeripheral)]) {
        [self.delegate getValueForPeripheral];
    }
}

//特征值寫入(修改)成功
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    if (error) {
        NSLog(@"寫入失敗%@",error);
    }else{
        NSLog(@"寫入成功");
    }
}

//特征值監聽狀態改變
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
    NSLog(@"特征監聽狀態改變 = %@",characteristic);
}

@end
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。