- 前言
Info中添加 Privacy - Bluetooth Peripheral Usage Description
从刚接触到BLE到开发使用接近一个月了,从项目中学到不少新的东西,了解BLE之前推荐看下
蓝牙官方文档
或者参考
蓝牙官方文档翻译
因为我所要用的是 模式是手机端是中心设备,蓝牙设备是外围设备。所有协议部分中用到了 CBCentralManagerDelegate和CBPeripheralDelegate 。而且是全局 监控蓝牙状态 所以有必要把蓝牙管理类封装出来 ,并制定协议 ,以便把相关蓝牙的协议给抛给自己的代理。
先看BLECentralManager .h部分
extern BOOL IsBlueToothOpen;
/**
* 设备连接状态
*/
typedef NS_ENUM(NSInteger, BleManagerState) {
/**
* 未连接
*/
BleManagerStateDisconnect = 0,
/**
* 已连接
*/
BleManagerStateConnect,
};
@protocol CentralManagerDelegate;
@interface CentralManager : NSObject
{
CBPeripheral *_devicePeripheral; //设备
CBCharacteristic *_deviceCharacteristic; //设备服务特征 (用来发送指令)
CBCentralManager *_manager; //
NSMutableArray *_deviceArray;
}
+ (BOOL)isBlueOpen;
/**
* 设备管理 单利
*/
+ (CentralManager *)sharedManager;
/**
* 验证蓝牙是否可用
*/
- (BOOL)verifyCentralManagerState;
/**
* 连接状态
*/
@property (assign, nonatomic) BleManagerState deviceBleState;
@property (assign,nonatomic) CBPeripheral *deviceTied;
@property CTCallCenter *callCentter;
/**
* 添加监听
*/
- (void)addEventListener:(id <CentralManagerDelegate>)listener;
/**
* 删除监听
*/
- (void)removeEventListener:(id <CentralManagerDelegate>)listener;
/**
* 删除所有监听
*/
- (void)removeAllEventListener;
/**
* 取消所有蓝牙连接
*/
- (void)cancelPeripheralConnection;
/**
* 取消蓝牙搜索
*/
- (void)stopManagerScan;
//***************************
//****设备
//***************************
/**
* 搜索设备
*/
- (void)searchDeviceModule;
/**
* 连接设备
*
* @param peripheral 设备
*/
- (void)connectDevicePeripheralWithPeripheral:(CBPeripheral *)peripheral;
/**
* 开始扫描服务
*
* @param peripheral 设备
*/
- (void)startdiscoverSerVices:(CBPeripheral*)peripheral WithUUID:(NSArray<CBUUID*>*)aryUUID;
- (void)startdiscoverSerVices:(CBPeripheral*)peripheral;
@end
@protocol CentralManagerDelegate <NSObject>
@optional
/**
* 蓝牙未开启 或 不可用
*/
- (void)centralManagerStatePoweredOff;
//***************************
//****设备
//***************************
/**
* 发现设备
*/
- (void)didDiscoverDevicePeripheral:(CBPeripheral *)peripheral devices:(NSMutableArray *)deviceArray; //发现设备
/**
* 开始连接
*/
- (void)didStartConnectDevicePeripheral;
/**
* 断开连接
*/
- (void)didCancelDevicePeripheralConnection;
/**
* 发现的特征值
*/
- (void)didDiscoverDevicePeripheral:(CBPeripheral *)peripheral service:(CBService *)service;
/**
* 连接成功
*/
- (void)didConnectDevicePeripheral:(CBPeripheral *)peripheral;
/**
* 连接失败
*/
- (void)didFailToConnectDevicePeripheral:(CBPeripheral *)peripheral;
/**
* 断开连接
*/
- (void)didDisconnectDevicePeripheral:(CBPeripheral *)peripheral;
/**
* 连接超时
*/
- (void)didConnectionDeviceTimeOut;
- (void)didDiscoverCharacteristicsForperipheral:(CBPeripheral *)peripheral Service:(CBService *)service error:(NSError *)error;
/**
* 接收到数据
*/
- (void)didUpdateDeviceValueForCharacteristic:(CBCharacteristic *)characteristic ; // deviceData:(NSData *)deviceData;
/**
* 如果一个特征的值被更新,然后周边代理接收
*
* @param characteristic
*/
- (void)didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic;
.m 部分
BOOL IsBlueToothOpen = NO;
@interface CentralManager ()<CBCentralManagerDelegate, CBPeripheralDelegate,CentralManagerDelegate>{
// short ecgNum[500];
// unsigned char hr;
}
@end
@implementation CentralManager
{
NSMutableArray *_listener; //观察者
//设备数组
}
#pragma mark - 单例
+ (CentralManager *)sharedManager {
static CentralManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
- (instancetype)init {
if (self = [super init]) {
[self customInit];
}
return self;
}
- (void)customInit {
_listener = [NSMutableArray array];
_deviceArray = [NSMutableArray array];
_deviceBleState = BleManagerStateDisconnect;
//建立中心角色
_manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
//添加监听
- (void)addEventListener:(id <CentralManagerDelegate>)listener {
[_listener addObject:listener];
}
//删除监听
- (void)removeEventListener:(id <CentralManagerDelegate>)listener {
[_listener removeObject:listener];
}
/**
* 删除所有监听
*/
- (void)removeAllEventListener {
[_listener removeAllObjects];
}
/**
* 取消所有蓝牙连接
*/
- (void)cancelPeripheralConnection {
if (self.deviceTied) {
[_manager cancelPeripheralConnection:self.deviceTied];
self.deviceTied = nil;
}
_deviceBleState = BleManagerStateDisconnect;
[self stopManagerScan];
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didCancelDevicePeripheralConnection)]) {
[listener didCancelDevicePeripheralConnection];
}
}
}
/**
* 取消蓝牙搜索
*/
- (void)stopManagerScan {
if (_manager) {
[_manager stopScan];
}
}
+ (BOOL)isBlueOpen
{
return IsBlueToothOpen;
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
NSString *stateStr;
switch (central.state) {
case CBCentralManagerStateUnknown :
IsBlueToothOpen = NO;
stateStr = @"当前蓝牙状态未知,请重试";
break;
case CBCentralManagerStateUnsupported:
IsBlueToothOpen = NO;
stateStr = @"当前设备不支持蓝牙设备连接";
break;
case CBCentralManagerStateUnauthorized:
IsBlueToothOpen = NO;
stateStr = @"请前往设置开启蓝牙授权并重试";
break;
case CBCentralManagerStatePoweredOff:
IsBlueToothOpen = NO;
stateStr = @"蓝牙关闭,请开启";
break;
case CBCentralManagerStateResetting:
IsBlueToothOpen = NO;
break;
case CBCentralManagerStatePoweredOn:
{
IsBlueToothOpen = YES;
stateStr = @"正常";
//扫描外设(discover)
DDLog(@"扫描外设");
// NSDictionary *options = @{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:YES]};
//开始扫描设备
[self searchDeviceModule];
// [_manager scanForPeripheralsWithServices:nil options:nil];
}
break;
default:
stateStr = [NSString stringWithFormat:@"蓝牙异常 %d",(int)central.state];
break;
}
}
/**
* 验证蓝牙是否可用
*/
- (BOOL)verifyCentralManagerState {
if (_manager.state != CBCentralManagerStatePoweredOn) {
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(centralManagerStatePoweredOff)]) {
[listener centralManagerStatePoweredOff];
}
}
return NO;
}
return YES;
}
//***************************
//****设备
//***************************
//搜索设备
- (void)searchDeviceModule {
// if (![self verifyCentralManagerState]) {
// return;
// }
DDLog(@"搜索设备");
[_manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithBool:NO]}];
//
}
//连接蓝牙设备
- (void)connectDevicePeripheralWithPeripheral:(CBPeripheral *)peripheral {
if (![self verifyCentralManagerState]) {
return;
}
if (peripheral == nil) {
return;
}
self.deviceTied = peripheral;
[_manager connectPeripheral:peripheral options:@{ CBConnectPeripheralOptionNotifyOnConnectionKey : @YES}];
}
#pragma mark - *******************
/**
* 这里开始蓝牙的代理方法
*/
#pragma mark - >> 发现蓝牙设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
if (!peripheral.name) {
return;
}
NearbyPeripheralInfo *infoModel = [[NearbyPeripheralInfo alloc] init];
infoModel.peripheral = peripheral;
infoModel.advertisementData = advertisementData;
infoModel.RSSI = RSSI;
//设备
DDLog(@"蓝牙管理者 发现设备Device:%@, %.2f", peripheral.name, RSSI.floatValue);
if (_deviceArray.count == 0) {
[_deviceArray addObject:infoModel];
}else {
BOOL isExist = NO;
for (int i = 0; i < _deviceArray.count; i++) {
NearbyPeripheralInfo *model = _deviceArray[i];
if ([model.peripheral isEqual:peripheral]) {
isExist = YES;
_deviceArray[i] = infoModel;
}
}
if (!isExist) {
[_deviceArray addObject:infoModel];
}
}
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didDiscoverDevicePeripheral:devices:)]) {
[listener didDiscoverDevicePeripheral:peripheral devices:_deviceArray];
}
}
}
#pragma mark - >> 连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"连接成功:%@", peripheral.name);
AppDelegate *myDelegate = [[UIApplication sharedApplication]delegate];
myDelegate.devicesTied = peripheral;
[self stopManagerScan]; //停止扫描设备
_deviceBleState = BleManagerStateConnect;
self.deviceTied = peripheral;
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didConnectDevicePeripheral:)]) {
[listener didConnectDevicePeripheral:peripheral];
}
}
//因为在后面我们要从外设蓝牙那边再获取一些信息,并与之通讯,这些过程会有一些事件可能要处理,所以要给这个外设设置代理
peripheral.delegate = self;
//找到该设备上的指定服务 调用完该方法后会调用代理CBPeripheralDelegate(现在开始调用另一个代理的方法了)
// [peripheral discoverServices:nil];
}
- (void)startdiscoverSerVices:(CBPeripheral *)peripheral WithUUID:(NSArray<CBUUID *> *)aryUUID
{
[peripheral discoverServices:aryUUID];
}
- (void)startdiscoverSerVices:(CBPeripheral*)peripheral
{
// [peripheral discoverServices:nil];
}
#pragma mark - >> 连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
_deviceBleState = BleManagerStateDisconnect;
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didFailToConnectDevicePeripheral:)]) {
[listener didFailToConnectDevicePeripheral:peripheral];
}
}
NSLog(@"连接失败:%@", peripheral.name);
}
#pragma mark - >> 断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"断开连接:%@", peripheral.name);
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didDisconnectDevicePeripheral:)]) {
[listener didDisconnectDevicePeripheral:peripheral];
}
}
// 重连
// [self connectDevicePeripheralWithPeripheral:peripheral];
}
#pragma mark - >> CBPeripheralDelegate
#pragma mark - >> 发现服务
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error
{
if (error) {
DDLog(@" 发现服务时发生错误: %@",error);
return;
}
NSLog(@" 发现服务 ..");
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
}
//- (void)didDiscoverCharacteristicsForServiceperipheral:(CBPeripheral *)peripheral :(CBService *)service error:(NSError *)error;
#pragma mark - >> 发现特征值
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
DDLog(@" 蓝牙管理页面 》》》》》 发现服务 %@, 特性数: %ld", service.UUID, [service.characteristics count]);
// for (id listener in _listener) {
// if ([listener respondsToSelector:@selector(didDiscoverCharacteristicsForperipheral:Service:error:)]) {
// [listener didDiscoverCharacteristicsForperipheral:peripheral Service:service error:error];
// }
// }
for (CBCharacteristic *c in service.characteristics) {
// [peripheral readValueForCharacteristic:c]; 这句话千万不要写,会出错
//在这里 打开蓝牙外设的某些功能的notify通道
DDLog(@" 蓝牙管理页面特征UUID:%@", [c.UUID UUIDString]);
if([[c.UUID UUIDString] isEqualToString:HEART_READ_UUID])
{
[peripheral setNotifyValue:YES forCharacteristic:c];
}
NSLog(@"特性值: %@",c.UUID);
}
}
#pragma mark - >> 如果一个特征的值被更新,然后周边代理接收
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
DDLog(@"蓝牙管理页面 》》》》》 update Notification");
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didUpdateNotificationStateForCharacteristic:)]) {
[listener didUpdateNotificationStateForCharacteristic:characteristic];
}
}
}
#pragma mark - >> 读数据
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
for (id listener in _listener) {
if ([listener respondsToSelector:@selector(didUpdateDeviceValueForCharacteristic:)]) {
[listener didUpdateDeviceValueForCharacteristic:characteristic];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
DDLog(@"did write value For Characteristic");
DDLog(@"value:%@ error:%@", characteristic.value,error);
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error {
DDLog(@"did Write Value For Descriptor");
}
2、具体的步骤
1)、先连接外设
[self connectDevicePeripheralWithPeripheral:peripheral];
连接成功后会响应CBCentralManagerDelegate的方法
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
2)、扫描外设中的服务
[peripheral discoverServices:nil];
或者
[peripheral discoverServices:aryUUID];//扫描指定的服务
之后便会调用CBPeripheralDelegate 的代理
方法 :已经发现服务
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error;
在 这里调用
[peripheral discoverCharacteristics:nil forService:service];
发现服务中的特征值
找到特征值之后便会调用CBPeripheralDelegate 的代理的
方法:已经发现特征值
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
在这里 打开通道
[peripheral setNotifyValue:YES forCharacteristic:c];
这个时候就可以进行对外设的写入操作:
+ (void)writeToperipheral:(CBPeripheral *)peripheral Service:(NSString *)serviceUUID characteristic:(NSString *)characteristicUUID data:(NSData *)data
{
CBUUID *servUUID = [CBUUID UUIDWithString:serviceUUID];
CBUUID *charUUID = [CBUUID UUIDWithString:characteristicUUID];
CBService *service = nil;
CBCharacteristic *characteristic = nil;
for (CBService *ser in peripheral.services) {
if ([ser.UUID isEqual:servUUID]) {
service = ser;
break;
}
}
if (service) {
for (CBCharacteristic *charac in service.characteristics) {
if ([charac.UUID isEqual:charUUID]) {
characteristic = charac;
break;
}
}
}
if (characteristic) {
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
else{
NSLog(@"not found that characteristic");
}
}
当外设中的数据发生变化 也就是写入值以后会触发CBPeripheralDelegate 的代理的方法:
#pragma mark - >> 如果一个特征的值被更新,然后周边代理接收
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
若有数据从蓝牙中广播的时候
触发
#pragma mark - >> 读数据
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
3、这个是本人的一点心得体会,新手刚接触有关蓝牙4.0的开发,若有哪些言论有误,还请各路大神不吝赐教,谢谢。
By:帅哥