iOS藍(lán)牙開發(fā)___項目實戰(zhàn)[內(nèi)附Demo地址]

前言:
上一篇文章講了iOS藍(lán)牙開發(fā)的基礎(chǔ)與Demo的應(yīng)用http://m.itdecent.cn/p/6e079da2370c, 這一章講講藍(lán)牙在項目中實戰(zhàn)的.

藍(lán)牙Demo鏈接地址: https://github.com/iOSzhangkai/BLE4.0_iOS

工程環(huán)境
(1) xcode 8.2
(2)iOS 10.1

前期思路
在項目實施之前處于藍(lán)牙學(xué)習(xí)階段,對于藍(lán)牙的理解只處于可以使用,,知道這是藍(lán)牙...藍(lán)牙內(nèi)部方法,底層實現(xiàn)原理完全懵逼,大寫的懵逼,所以在demo中將藍(lán)牙方法整個粘入控制器中,經(jīng)過惡心而又漫長的原始階段終于將藍(lán)牙外設(shè)與手機連接給整出來了.....但是因為iOS提供藍(lán)牙外設(shè)連接的<CoreBluetooth/CoreBluetooth.h> 中的所以方法都是通過代理的實現(xiàn),導(dǎo)致項目的控制器頁面代碼行數(shù)多達(dá)兩千行,這對于一個程序猿是個恥辱啊!!![一個很小的功能],在項目上線后的幾周陸續(xù)看了許多大牛的藍(lán)牙封裝類,決定自己要改造這個控制器代碼,有寫的不到之處希望各位可以指出,共同進步.

*廢話不多說 直接上代碼 *

藍(lán)牙管理類的.h

#import <Foundation/Foundation.h>
//BLIE4.0 藍(lán)牙庫
#import <CoreBluetooth/CoreBluetooth.h>
//藍(lán)牙連接狀態(tài)的定義
#define kCONNECTED_UNKNOWN_STATE @"未知藍(lán)牙狀態(tài)"
#define kCONNECTED_RESET         @"藍(lán)牙重置"
#define kCONNECTED_UNSUPPORTED   @"該設(shè)備不支持藍(lán)牙"
#define kCONNECTED_UNAUTHORIZED  @"未授權(quán)藍(lán)牙權(quán)限"
#define kCONNECTED_POWERED_OFF   @"藍(lán)牙已關(guān)閉"
#define kCONNECTED_POWERD_ON     @"藍(lán)牙已打開"
#define kCONNECTED_ERROR         @"未知的藍(lán)牙錯誤"

/**
 設(shè)備名稱
 */
#define PMServiceName @"Bozonn-Air01"
/**
 設(shè)備UUID
 */
#define PMServiceUUID @"FFE0"
/**
 設(shè)備配置UUID
 */
#define PMServiceUUID_Config @"00002902-0000-1000-8000-00805f9b34fb"
/**
 設(shè)備讀取UUID
 */
#define PMServiceUUID_Receive @"0000ffe1-0000-1000-8000-00805f9b34fb"
/**
 設(shè)備寫入UUID
 */
#define PMServiceUUID_Send @"0000ffe1-0000-1000-8000-00805f9b34fb"

/**
 藍(lán)牙鏈接狀態(tài)

 @param state 狀態(tài)
 */
typedef void (^BLELinkBlock)(NSString *state);

/**
 藍(lán)牙返回數(shù)據(jù)

 @param array 返回數(shù)據(jù)
 */
typedef void (^BLEDataBlock)(NSMutableArray *array);

typedef enum BLEState_NOW{
    
    BLEState_Successful = 0,//連接成功
    BLEState_Disconnect = 1, // 失敗
    BLEState_Normal,         // 未知
    
}BLEState_NOW;
/**
 藍(lán)牙連接成功 或者斷開

 */
typedef void(^BLEStateBlcok)(int number);
@interface BLEModel : NSObject

@property (nonatomic,copy) NSString *connectState;//藍(lán)牙連接狀態(tài)
@property (nonatomic,copy) BLELinkBlock linkBlcok;
@property (nonatomic,copy) BLEDataBlock dataBlock;
@property (nonatomic,copy) BLEStateBlcok stateBlock;

/**
 主動斷開鏈接
 */
-(void)cancelPeripheralConnection;

/**
 發(fā)送命令
 */
- (void) sendData:(NSData *)data;

藍(lán)牙管理類.m


#import "BLEModel.h"
#import "BLEIToll.h"

@interface BLEModel ()<CBCentralManagerDelegate,CBPeripheralDelegate>
/**
 *  藍(lán)牙連接必要對象
 */
@property (nonatomic, strong) CBCentralManager *centralMgr;
@property (nonatomic, strong) CBPeripheral *discoveredPeripheral;
@property (strong, nonatomic) CBCharacteristic* writeCharacteristic;

@property (nonatomic,assign) BOOL isInitiativeDisconnect;//主動斷開連接
@end
@implementation BLEModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        _centralMgr = [[CBCentralManager alloc]initWithDelegate:self queue:nil];

    }
    return self;
}

/**
 *  停止掃描
 */
-(void)stopScan
{
    [_centralMgr stopScan];  
}
#pragma mark -- CBCentralManagerDelegate
#pragma mark- 掃描設(shè)備,連接

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"name:%@",peripheral);
    /**
     當(dāng)掃描到藍(lán)牙數(shù)據(jù)為空時,停止掃描
     */
    if (!peripheral || !peripheral.name || ([peripheral.name isEqualToString:@""])) {
        return;
    }
    /**
     當(dāng)掃描到服務(wù)UUID與設(shè)備UUID相等時,進行藍(lán)牙與設(shè)備鏈接
     */
    
    if ((!self.discoveredPeripheral || (self.discoveredPeripheral.state == CBPeripheralStateDisconnected))&&([peripheral.name isEqualToString:PMServiceName])) {
        self.discoveredPeripheral = [peripheral copy];
        //self.peripheral.delegate = self;
        NSLog(@"connect peripheral:  %@",peripheral);
        [self.centralMgr connectPeripheral:peripheral options:nil];
    }

}


#pragma mark - 藍(lán)牙的狀態(tài)
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBManagerStateUnknown:
        {
            //NSLog(@"無法獲取設(shè)備的藍(lán)牙狀態(tài)");
            self.connectState = kCONNECTED_UNKNOWN_STATE;
        }
            break;
        case CBManagerStateResetting:
        {
            //NSLog(@"藍(lán)牙重置");
            self.connectState = kCONNECTED_RESET;
        }
            break;
        case CBManagerStateUnsupported:
        {
            //NSLog(@"該設(shè)備不支持藍(lán)牙");
            self.connectState = kCONNECTED_UNSUPPORTED;
        }
            break;
        case CBManagerStateUnauthorized:
        {
            //NSLog(@"未授權(quán)藍(lán)牙權(quán)限");
            self.connectState = kCONNECTED_UNAUTHORIZED;
        }
            break;
        case CBManagerStatePoweredOff:
        {
            //NSLog(@"藍(lán)牙已關(guān)閉");
            self.connectState = kCONNECTED_POWERED_OFF;
        }
            break;
        case CBManagerStatePoweredOn:
        {
            //NSLog(@"藍(lán)牙已打開");
            self.connectState = kCONNECTED_POWERD_ON;
            [_centralMgr scanForPeripheralsWithServices:nil options:nil];
        }
            break;
            
        default:
        {
            //NSLog(@"未知的藍(lán)牙錯誤");
            self.connectState = kCONNECTED_ERROR;
        }
            break;
    }
    self.linkBlcok(self.connectState);
    //[self getConnectState];
    
}
#pragma park- 連接成功,掃描services
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    if (!peripheral) {
        return;
    }
    [self.centralMgr stopScan];
    
    self.stateBlock(BLEState_Successful);
    
    NSLog(@"peripheral did connect:  %@",peripheral);
    [self.discoveredPeripheral setDelegate:self];
    [self.discoveredPeripheral discoverServices:nil];
     
}




#pragma mark - 掃描service
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSArray *services = nil;
    
    if (peripheral != self.discoveredPeripheral) {
        NSLog(@"Wrong Peripheral.\n");
        return ;
    }
    
    if (error != nil) {
        NSLog(@"Error %@\n", error);
        return ;
    }
    
    services = [peripheral services];
    NSLog(@"%@",services);
    if (!services || ![services count]) {
        NSLog(@"No Services");
        return ;
    }
    
    for (CBService *service in services) {
        NSLog(@"該設(shè)備的service:%@",service);
        /*
         >
         
         */
        if ([[service.UUID UUIDString] isEqualToString:PMServiceUUID]) {
            [peripheral discoverCharacteristics:nil forService:service];
            return ;
        }
    }
    
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error)
    {
        NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
        return;
    }
    
    for (CBCharacteristic *c in service.characteristics)
    {
        NSLog(@"\n>>>\t特征UUID FOUND(in 服務(wù)UUID:%@): %@ (data:%@)",service.UUID.description,c.UUID,c.UUID.data);
        /**
         >>>    特征UUID FOUND(in 服務(wù)UUID:FFE0): FFE1 (data:<ffe1>)
         
         >>>    特征UUID FOUND(in 服務(wù)UUID:FFE0): FFE2 (data:<ffe2>)
         
         */
        /*
         根據(jù)特征不同屬性去讀取或者寫
         if (c.properties==CBCharacteristicPropertyRead) {
         }
         if (c.properties==CBCharacteristicPropertyWrite) {
         }
         if (c.properties==CBCharacteristicPropertyNotify) {
         }
         */
        
        /*
         設(shè)備讀取UUID
        */
        if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Receive]]) {
            self.writeCharacteristic = c;
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
        }
        
        /**
         設(shè)備寫入UUID
         */
        if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Send]]) {
            self.writeCharacteristic = c;
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
        }  
    }
}
#pragma mark - 讀取數(shù)據(jù)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    
    if (error)
    {
        NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription);
        return;
    }
    NSData *data = characteristic.value;
    NSMutableArray *dataArr = [BLEIToll convertDataToHexStr:data];
    self.dataBlock(dataArr);
    
}


#pragma mark- 外設(shè)斷開連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    NSLog(@"外設(shè)斷開連接 %@: %@\n", [peripheral name], [error localizedDescription]);
    
    self.stateBlock(BLEState_Disconnect);
    //重連外設(shè)
    if (!self.isInitiativeDisconnect) {
        [self.centralMgr connectPeripheral:peripheral options:nil];
    }
    
}
#pragma mark - 主動斷開連接
-(void)cancelPeripheralConnection{
    
    self.isInitiativeDisconnect = YES;
    if (self.discoveredPeripheral) {//已經(jīng)連接外設(shè),則斷開
        [self.centralMgr cancelPeripheralConnection:self.discoveredPeripheral];
    }else{//未連接,則停止搜索外設(shè)
        [self.centralMgr stopScan];
    }
    
}

/**
 發(fā)送命令
 */
- (void) sendData:(NSData *)data{
    
     /**
      通過CBPeripheral 類 將數(shù)據(jù)寫入藍(lán)牙外設(shè)中,藍(lán)牙外設(shè)所識別的數(shù)據(jù)為十六進制數(shù)據(jù),在ios系統(tǒng)代理方法中將十六進制數(shù)據(jù)改為 NSData 類型 ,但是該數(shù)據(jù)形式必須為十六進制數(shù) 0*ff 0*ff格式 在iToll中有將 字符串轉(zhuǎn)化為 十六進制 再轉(zhuǎn)化為 NSData的方法
      
      */
    [self.discoveredPeripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
    
}
//向peripheral中寫入數(shù)據(jù)后的回調(diào)函數(shù)
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    //該方法可以監(jiān)聽到寫入外設(shè)數(shù)據(jù)后的狀態(tài)
    if (error) {
        NSLog(@"didWriteValueForCharacteristic error : %@", error.localizedDescription);
        return;  
    }   
    NSLog(@"write value success : %@", characteristic);
}

食用方法

#import "BLEModel.h"
@property (nonatomic,strong) BLEModel *manager;

- (void)BluetoothConnection{
 /**
  BLEModel 對象通過init方法創(chuàng)建 ,在init方法中就已經(jīng)將藍(lán)牙操作對象進行初始化,遵循代理,在控制器中無需遵循任何的藍(lán)牙代理,引入藍(lán)牙庫
*/  
    _manager = [[BLEModel alloc]init];
/**
BLEModel 中的 BLELinkBlock 是將藍(lán)牙當(dāng)前狀態(tài)進行回調(diào),方便在項目中對藍(lán)牙處于什么狀態(tài)進行操作
*/
    _manager.linkBlcok = ^(NSString *state){
        
        NSLog(@"%@",state);
    };
    
/**
BLEModel 中的BLEStateBlcok 是手機與外設(shè)鏈接狀態(tài)的回調(diào),
*/
    _manager.stateBlock = ^(int number){
        NSLog(@"%d",number);
    };

/**
BLEModel 中的BLEDataBlock 是外設(shè)返回數(shù)據(jù)到手機 
外設(shè)返回的數(shù)據(jù)一般為十六進制數(shù)據(jù),在BLEModel 內(nèi)部中已經(jīng)將十六進制數(shù)據(jù)進行一次處理,把拿到的數(shù)據(jù)已字符串的類型添加到array中,所以可以直接使用對array進行操作,無需再做處理
*/
    _manager.dataBlock = ^(NSMutableArray *array){
        
        NSLog(@"%@",array);

            
        }
    };
    
 
    /*
     設(shè)備給藍(lán)牙傳輸數(shù)據(jù) 必須以十六進制數(shù)據(jù)傳給藍(lán)牙 藍(lán)牙設(shè)備才會執(zhí)行
   因為iOS 藍(lán)牙庫中方法 傳輸書記是以NSData形式 
    因此封裝 stringToByte 方法的作用字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)

     發(fā)送一個  0xFA 0xE4 Ox33 的命令
     */
   
     NSData *data = [[BLEIToll alloc] stringToByte:@"FAE433"];
     NSLog(@"寫入數(shù)據(jù)%@",data);
     [_manager sendData:data];
   
}
/**
 *  設(shè)備給藍(lán)牙傳輸數(shù)據(jù) 必須以十六進制數(shù)據(jù)傳給藍(lán)牙 藍(lán)牙設(shè)備才會執(zhí)行
 因為iOS 藍(lán)牙庫中方法 傳輸書記是以NSData形式 這個方法 字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)
 *
 *  @param string 傳入字符串命令
 *
 *  @return 將字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)
 */

-(NSData*)stringToByte:(NSString*)string
{
    NSString *hexString=[[string uppercaseString] stringByReplacingOccurrencesOfString:@" " withString:@""];
    if ([hexString length]%2!=0) {
        return nil;
    }
    Byte tempbyt[1]={0};
    NSMutableData* bytes=[NSMutableData data];
    for(int i=0;i<[hexString length];i++)
    {
        unichar hex_char1 = [hexString characterAtIndex:i]; ////兩位16進制數(shù)中的第一位(高位*16)
        int int_ch1;
        if(hex_char1 >= '0' && hex_char1 <='9')
            int_ch1 = (hex_char1-48)*16;   //// 0 的Ascll - 48
        else if(hex_char1 >= 'A' && hex_char1 <='F')
            int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65
        else
            return nil;
        i++;
        
        unichar hex_char2 = [hexString characterAtIndex:i]; ///兩位16進制數(shù)中的第二位(低位)
        int int_ch2;
        if(hex_char2 >= '0' && hex_char2 <='9')
            int_ch2 = (hex_char2-48); //// 0 的Ascll - 48
        else if(hex_char2 >= 'A' && hex_char2 <='F')
            int_ch2 = hex_char2-55; //// A 的Ascll - 65
        else
            return nil;
        
        tempbyt[0] = int_ch1+int_ch2;  ///將轉(zhuǎn)化后的數(shù)放入Byte數(shù)組里
        [bytes appendBytes:tempbyt length:1];
    }
    return bytes;
}

小結(jié)
在開發(fā)中將BLEModel.h, BLEModel.m文件拖入工程 ,在控制器中實現(xiàn)BluetoothConnection方法就可以完成手機與外設(shè)連接,讀取外設(shè)數(shù)據(jù),對外設(shè)發(fā)送指令的需求.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容