iOS蓝牙开发___项目实战[内附Demo地址]

前言:
上一篇文章讲了iOS蓝牙开发的基础与Demo的应用http://www.jianshu.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阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容