iOS藍牙開發___項目實戰[內附Demo地址]

前言:
上一篇文章講了iOS藍牙開發的基礎與Demo的應用http://www.lxweimin.com/p/6e079da2370c, 這一章講講藍牙在項目中實戰的.

藍牙Demo鏈接地址: https://github.com/iOSzhangkai/BLE4.0_iOS

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

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

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

藍牙管理類的.h

#import <Foundation/Foundation.h>
//BLIE4.0 藍牙庫
#import <CoreBluetooth/CoreBluetooth.h>
//藍牙連接狀態的定義
#define kCONNECTED_UNKNOWN_STATE @"未知藍牙狀態"
#define kCONNECTED_RESET         @"藍牙重置"
#define kCONNECTED_UNSUPPORTED   @"該設備不支持藍牙"
#define kCONNECTED_UNAUTHORIZED  @"未授權藍牙權限"
#define kCONNECTED_POWERED_OFF   @"藍牙已關閉"
#define kCONNECTED_POWERD_ON     @"藍牙已打開"
#define kCONNECTED_ERROR         @"未知的藍牙錯誤"

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

/**
 藍牙鏈接狀態

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

/**
 藍牙返回數據

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

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

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

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

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

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

藍牙管理類.m


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

@interface BLEModel ()<CBCentralManagerDelegate,CBPeripheralDelegate>
/**
 *  藍牙連接必要對象
 */
@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- 掃描設備,連接

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"name:%@",peripheral);
    /**
     當掃描到藍牙數據為空時,停止掃描
     */
    if (!peripheral || !peripheral.name || ([peripheral.name isEqualToString:@""])) {
        return;
    }
    /**
     當掃描到服務UUID與設備UUID相等時,進行藍牙與設備鏈接
     */
    
    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 - 藍牙的狀態
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBManagerStateUnknown:
        {
            //NSLog(@"無法獲取設備的藍牙狀態");
            self.connectState = kCONNECTED_UNKNOWN_STATE;
        }
            break;
        case CBManagerStateResetting:
        {
            //NSLog(@"藍牙重置");
            self.connectState = kCONNECTED_RESET;
        }
            break;
        case CBManagerStateUnsupported:
        {
            //NSLog(@"該設備不支持藍牙");
            self.connectState = kCONNECTED_UNSUPPORTED;
        }
            break;
        case CBManagerStateUnauthorized:
        {
            //NSLog(@"未授權藍牙權限");
            self.connectState = kCONNECTED_UNAUTHORIZED;
        }
            break;
        case CBManagerStatePoweredOff:
        {
            //NSLog(@"藍牙已關閉");
            self.connectState = kCONNECTED_POWERED_OFF;
        }
            break;
        case CBManagerStatePoweredOn:
        {
            //NSLog(@"藍牙已打開");
            self.connectState = kCONNECTED_POWERD_ON;
            [_centralMgr scanForPeripheralsWithServices:nil options:nil];
        }
            break;
            
        default:
        {
            //NSLog(@"未知的藍牙錯誤");
            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(@"該設備的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 服務UUID:%@): %@ (data:%@)",service.UUID.description,c.UUID,c.UUID.data);
        /**
         >>>    特征UUID FOUND(in 服務UUID:FFE0): FFE1 (data:<ffe1>)
         
         >>>    特征UUID FOUND(in 服務UUID:FFE0): FFE2 (data:<ffe2>)
         
         */
        /*
         根據特征不同屬性去讀取或者寫
         if (c.properties==CBCharacteristicPropertyRead) {
         }
         if (c.properties==CBCharacteristicPropertyWrite) {
         }
         if (c.properties==CBCharacteristicPropertyNotify) {
         }
         */
        
        /*
         設備讀取UUID
        */
        if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Receive]]) {
            self.writeCharacteristic = c;
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
        }
        
        /**
         設備寫入UUID
         */
        if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Send]]) {
            self.writeCharacteristic = c;
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
        }  
    }
}
#pragma mark - 讀取數據
- (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- 外設斷開連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    NSLog(@"外設斷開連接 %@: %@\n", [peripheral name], [error localizedDescription]);
    
    self.stateBlock(BLEState_Disconnect);
    //重連外設
    if (!self.isInitiativeDisconnect) {
        [self.centralMgr connectPeripheral:peripheral options:nil];
    }
    
}
#pragma mark - 主動斷開連接
-(void)cancelPeripheralConnection{
    
    self.isInitiativeDisconnect = YES;
    if (self.discoveredPeripheral) {//已經連接外設,則斷開
        [self.centralMgr cancelPeripheralConnection:self.discoveredPeripheral];
    }else{//未連接,則停止搜索外設
        [self.centralMgr stopScan];
    }
    
}

/**
 發送命令
 */
- (void) sendData:(NSData *)data{
    
     /**
      通過CBPeripheral 類 將數據寫入藍牙外設中,藍牙外設所識別的數據為十六進制數據,在ios系統代理方法中將十六進制數據改為 NSData 類型 ,但是該數據形式必須為十六進制數 0*ff 0*ff格式 在iToll中有將 字符串轉化為 十六進制 再轉化為 NSData的方法
      
      */
    [self.discoveredPeripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
    
}
//向peripheral中寫入數據后的回調函數
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    //該方法可以監聽到寫入外設數據后的狀態
    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方法創建 ,在init方法中就已經將藍牙操作對象進行初始化,遵循代理,在控制器中無需遵循任何的藍牙代理,引入藍牙庫
*/  
    _manager = [[BLEModel alloc]init];
/**
BLEModel 中的 BLELinkBlock 是將藍牙當前狀態進行回調,方便在項目中對藍牙處于什么狀態進行操作
*/
    _manager.linkBlcok = ^(NSString *state){
        
        NSLog(@"%@",state);
    };
    
/**
BLEModel 中的BLEStateBlcok 是手機與外設鏈接狀態的回調,
*/
    _manager.stateBlock = ^(int number){
        NSLog(@"%d",number);
    };

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

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

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

-(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進制數中的第一位(高位*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進制數中的第二位(低位)
        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;  ///將轉化后的數放入Byte數組里
        [bytes appendBytes:tempbyt length:1];
    }
    return bytes;
}

小結
在開發中將BLEModel.h, BLEModel.m文件拖入工程 ,在控制器中實現BluetoothConnection方法就可以完成手機與外設連接,讀取外設數據,對外設發送指令的需求.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容