iOS开发杂货铺ios实用开发技巧iOS实战

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

2017-05-12  本文已影响1105人  iOS_ZK03

前言:
上一篇文章讲了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方法就可以完成手机与外设连接,读取外设数据,对外设发送指令的需求.

这是个人开发的一个甲醛检测项目,他是通过一个甲醛检测器将数据传到手机中,完成对你身边甲醛,PM2.5.PM10,温度,湿度的测量,后续还将添加历史记录,使你对房间内的各种测量值有一个直观的展示,希望各位可以帮忙下载看看


APP传送码
上一篇下一篇

猜你喜欢

热点阅读