//首先,定义一个蓝牙操作类
//
// LSBuzzBTmanager.h
// gene
//
// Created by lollipop on 2017/4/7.
// Copyright © 2017年 lesports. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
// 错误类型,后续根据厂商加上其他错误
typedef NS_ENUM(NSInteger,BuzzResult) {
BuzzBTSuccess = 0, // 成功
BuzzBTFail, // 失败
BuzzBTInvalid, // 目前蓝牙不可用
BuzzTimeout, // 此标志位只说明超时,并不说明没有搜到结果
};
typedef void (^LSBikeFailedBlock)(NSError *error);
typedef void (^LSBikeResultBlock)(BOOL result, NSError *error);
@protocol LSBuzzBTmanagerDelegate <NSObject>
/*
* @descripe 当前蓝牙是否可用
* @param available 当前蓝牙是否可用
*/
- (void)centralManagerDidUpdateState:(BOOL)available;
/*
* @descripe 搜到蓝牙设备的回调,每次搜到一个新设备都会回调
* @param isFinish 搜索是否结束
* @param list 搜索当前搜索结果
* @param error 参考BuzzError
*/
- (void)fetchDevice:(BOOL)isFinish device:(NSArray *)list apack:(NSArray *)adpacklist withResult:(BuzzResult)error;
/*
* @descripe 连接制定蓝牙设备结果的回调
* @param error 参考BuzzError
*/
- (void)connectResult:(BuzzResult)result withError:(NSError *)error;
/*
* @descripe 连接被断开,被动断开或者主动断开
* @param error 参考BuzzError
*/
- (void)disConnectResult:(BuzzResult)result withError:(NSError *)error;
/*
* @descripe 连接制定蓝牙设备结果的回调(成功)
* @param error 参考BuzzError
*/
- (void)hasconnectResult:(BuzzResult)result withError:(NSError *)error;
@end
@interface LSBuzzBTmanager : NSObject
@property (nonatomic, weak) id<LSBuzzBTmanagerDelegate> delegate; // 部分回调
@property (nonatomic,assign) NSInteger scanTimeInterval;// 扫描时间
@property (nonatomic,strong) NSMutableArray *rssiarray;// 信号强度
// 单例
+ (instancetype)sharedInstance;
/*
*
* 搜索并连接指定的蓝牙设备
*
*/
- (BOOL)FinddevFiter:(BOOL)pp;//查找设备,pp为YES表示开uuid过滤,NO表示关闭
- (void)stopSearchDevice;//停止扫描设备
- (void)connectBuzzard:(CBPeripheral *)peripheral;//连接某项蓝牙设备
- (void)disconnectBuzzard:(CBPeripheral *)peripheral;//与某项设备断开连接
- (void)writeValue:(NSString *)command option:(NSString *)option;
@end
//.m文件如下
//
// LSBuzzBTmanager.m
// gene
//
// Created by lollipop on 2017/4/7.
// Copyright © 2017年 lesports. All rights reserved.
//
#import "LSBuzzBTmanager.h"
#import "kbConsoleLog.h"
#define UUID_CONTROL_SERVICE @"FEE0"
#define UUID_LISTEN_CHARACTERISTICS @"FEE1"//写
@interface LSBuzzBTmanager() <CBCentralManagerDelegate,CBPeripheralDelegate> {
CBCentralManager *centerManager;
CBPeripheral *activePeripheral;
CBCharacteristic *readCharacteristic;
CBCharacteristic *controlCharacteristic;
}
@property (nonatomic,strong) NSMutableArray *devLists;
@property (nonatomic,strong) NSMutableArray *adpackLists;
@end
@implementation LSBuzzBTmanager
+ (instancetype)sharedInstance {
static LSBuzzBTmanager *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[LSBuzzBTmanager alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
if (self = [super init]) {
centerManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
}
return self;
}
#pragma mark 搜索蓝牙设备相关
- (BOOL)FinddevFiter:(BOOL)ppp {
// 蓝牙不可用
if (centerManager.state != CBManagerStatePoweredOn) {
if (_delegate && [_delegate respondsToSelector:@selector(fetchDevice:device:apack:withResult:)]) {
[_delegate fetchDevice:NO device:nil apack:nil withResult:BuzzBTSuccess];
}
return NO;
}
// 开始搜索
NSArray *serviceUUIDs = nil;
if (ppp) { // 过滤
// serviceUUIDs = @[[CBUUID UUIDWithString:@""]];
}
[centerManager scanForPeripheralsWithServices:serviceUUIDs options:nil];
if (_scanTimeInterval == 0) {
_scanTimeInterval = 20;
}
// 先取消之前的延时
[LSBuzzBTmanager cancelPreviousPerformRequestsWithTarget:self selector:@selector(scanTimeOut) object:self];
// 重新执行超时
[self performSelector:@selector(scanTimeOut) withObject:self afterDelay:_scanTimeInterval];
return YES;
}
- (void)stopSearchDevice {
[centerManager stopScan];
}
- (void)connectBuzzard:(CBPeripheral *)peripheral {
[centerManager connectPeripheral:peripheral options:nil];
}
- (void)disconnectBuzzard:(CBPeripheral *)peripheral {
[centerManager cancelPeripheralConnection:peripheral];
activePeripheral = nil;
// [HintView hintViewWithMessage:@"断开连接"];
}
- (void)scanTimeOut {
[centerManager stopScan];
if (_delegate && [_delegate respondsToSelector:@selector(fetchDevice:device:apack:withResult:)]) {
[_delegate fetchDevice:NO device:_devLists apack:self.adpackLists withResult:BuzzBTSuccess];
}
}
- (void)writeValue:(NSString *)command option:(NSString *)option{
if (activePeripheral.state == CBPeripheralStateConnected) {
}else if (activePeripheral.state == CBPeripheralStateConnecting){
[HintView hintViewWithMessage:@"正在连接"];
return;
}else if (activePeripheral.state == CBPeripheralStateDisconnected){
[HintView hintViewWithMessage:@"peripheral disconnected"];
return;
}else if (activePeripheral.state == CBPeripheralStateDisconnecting){
[HintView hintViewWithMessage:@"peripheral disconnecting"];
return;
}
NSData *data;
if (option.length > 0) {
data = [self command:command withOption:option];
} else {
data = [self dataFromHexString:command];
}
NSString *msg = [NSString stringWithFormat:@"Write command %@ on peripheral %@(%@)", data, activePeripheral.name, activePeripheral.identifier];
NSLogK(msg);
TDLog(@"controlCharacteristic is %@",controlCharacteristic);
if (controlCharacteristic==nil) {
[HintView hintViewWithMessage:@"未找到该设备"];
return;
}
[activePeripheral writeValue:data forCharacteristic:controlCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
#pragma mark - getter
- (NSMutableArray *)devLists{
if (!_devLists) {
_devLists = [[NSMutableArray alloc] init];
}
return _devLists;
}
- (NSMutableArray *)adpackLists{
if (!_adpackLists) {
_adpackLists = [[NSMutableArray alloc] init];
}
return _adpackLists;
}
- (NSMutableArray *)rssiarray{
if (!_rssiarray) {
_rssiarray = [[NSMutableArray alloc] init];
}
return _rssiarray;
}
#pragma mark CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
BOOL isAvaliable = NO;
if (central.state == CBManagerStatePoweredOn) {
isAvaliable = YES;
}
if (_delegate && [_delegate respondsToSelector:@selector(centralManagerDidUpdateState:)]) {
[_delegate centralManagerDidUpdateState:isAvaliable];
}
}
// 扫描到设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
if (!peripheral.name ) {
return;
}
if (peripheral.name == 0) {
return;
}
// 如果已经被搜索,就不新加入
if ([self.devLists containsObject:peripheral]) {
return;
}
// 1.信号过滤,信号太弱连不上
// 2.名称过滤
// 根据startScanBuzzard方法中扫描结果 再看是否需要增加过滤逻辑
[self.devLists addObject:peripheral];
NSData *data=advertisementData[@"kCBAdvDataManufacturerData"];
NSString *adpackstr= [self dataToHex:data];
[self.adpackLists addObject:adpackstr];
[self.rssiarray addObject:[NSString stringWithFormat:@"%@",RSSI]];
if (_delegate && [_delegate respondsToSelector:@selector(fetchDevice:device:apack:withResult:)]) {
[_delegate fetchDevice:NO device:_devLists apack:self.adpackLists withResult:BuzzBTSuccess];
}
}
// 连接设备
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
activePeripheral = peripheral;
if (_delegate && [_delegate respondsToSelector:@selector(hasconnectResult:withError:)]) {
[_delegate hasconnectResult:BuzzBTSuccess withError:nil];
}
NSArray *services = nil;// @[[CBUUID UUIDWithString:@""]];
activePeripheral.delegate = self;
[activePeripheral discoverServices:services];
}
// 连接设备失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSString *msg = [NSString stringWithFormat:@"-didFailToConnectPeripheral-%@",peripheral.name];
NSLogK(msg);
if (_delegate && [_delegate respondsToSelector:@selector(connectResult:withError:)]) {
[_delegate connectResult:BuzzBTFail withError:error];
}
}
// 丢失连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSString *msg = [NSString stringWithFormat:@"-didDisconnectPeripheral-%@",peripheral.name];
NSLogK(msg);
activePeripheral = nil;
if (_delegate && [_delegate respondsToSelector:@selector(disConnectResult:withError:)]) {
[_delegate disConnectResult:BuzzBTFail withError:error];
}
}
// 服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) {
NSString *msg = [NSString stringWithFormat:@"did discover peripheral:%@(%@) service error : %@", peripheral.name, peripheral.identifier, error.localizedDescription];
NSLogK(msg);
return;
}
NSString *msg = [NSString stringWithFormat:@"did discover peripheral:%@(%@) service %@", peripheral.name, peripheral.identifier, peripheral.services];
NSLogK(msg);
//,[CBUUID UUIDWithString:UUID_LISTEN_CHARACTERISTICS]
for (CBService *service in peripheral.services) {
if ([service.UUID.UUIDString isEqualToString:UUID_CONTROL_SERVICE]) {
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:UUID_LISTEN_CHARACTERISTICS]] forService:service];
break;
}
}
}
// periperal discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {
NSString *msg = [NSString stringWithFormat:@"peripheral %@(%@) did discover characteristic error %@", peripheral.name, peripheral.identifier, error.localizedDescription];
NSLogK(msg);
return;
}
NSString *msg = [NSString stringWithFormat:@"peripheral %@(%@) did discover characteristics %@", peripheral.name, peripheral.identifier, service.characteristics];
NSLogK(msg);
for (CBCharacteristic *chara in service.characteristics) {
if ([chara.UUID isEqual:[CBUUID UUIDWithString:UUID_LISTEN_CHARACTERISTICS]]) {
[peripheral setNotifyValue:YES forCharacteristic:chara];
controlCharacteristic = chara;
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSData *data = characteristic.value;
NSString *response = [self dataToHex:data];
NSString *msg = [NSString stringWithFormat:@"command did get response:%@", response];
NSLogK(msg);
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// NSString *msg = [NSString stringWithFormat:@"didWriteValueForCharacteristic:%@", error];
// TDLogK(msg);
TDLog(@"error is %@",error);
TDLog(@"characteristic is %@",characteristic);
}
#pragma mark 发送和接收数据解析相关,以下是针对我目前项目中蓝牙功能的封装,不一定适用其他项目
- (NSData *)command:(NSString *)hexCommand withOption:(NSString *)option{
NSData *optionData = [option dataUsingEncoding:NSUTF8StringEncoding];
NSInteger totalLength = optionData.length + 5;
NSString *hexOptionString = [self dataToHex:optionData];
NSString *lengthString = [NSString stringWithFormat:@"%X",(int)totalLength];
//最长只支持 255 长度的命令
if (lengthString.length==1) {
lengthString = [NSString stringWithFormat:@"0%@",lengthString];
}
NSString *hexCommandString = [NSString stringWithFormat:hexCommand,lengthString,hexOptionString];
NSData *commandData = [self dataFromHexString:hexCommandString];
return commandData;
}
- (NSData *)dataFromHexString:(NSString *)hexString {
const char *chars = [hexString UTF8String];
long i = 0, len = hexString.length;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:1];
}
return data;
}
- (NSString *)dataToHex:(NSData *)data {
NSUInteger i, len;
unsigned char *buf, *bytes;
len = data.length;
bytes = (unsigned char*)data.bytes;
buf = malloc(len*2);
for (i=0; i<len; i++) {
buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
buf[i*2+1] = itoh(bytes[i] & 0xF);
}
return [[NSString alloc] initWithBytesNoCopy:buf
length:len*2
encoding:NSUTF8StringEncoding
freeWhenDone:YES];
}
static inline char itoh(int i) {
if (i > 9)
return 'A' + (i - 10);
return '0' + i;
}
/*
* 把返回命令里的传递值拿出来
*/
- (NSString *)valueStringWithResponse:(NSData *)data {
//NSData *ad = [NSData dataWithBytes:0x00 length:2];
if (data.length <= 5) {
return nil;
}
NSData *tailData = [data subdataWithRange:NSMakeRange(data.length-1, 1)];
if ([tailData isEqualToData:[NSData dataWithBytes:"\0" length:1]]) {
if (data.length > 6) {
NSData *valueData = [data subdataWithRange:NSMakeRange(4, data.length-5)];
NSString *valueString = [[NSString alloc] initWithData:valueData encoding:NSUTF8StringEncoding];
return valueString;
}
} else {
if (data.length > 5) {
NSData *valueData = [data subdataWithRange:NSMakeRange(4, data.length-4)];
NSString *valueString = [[NSString alloc] initWithData:valueData encoding:NSUTF8StringEncoding];
return valueString;
}
}
return nil;
}
@end
//定义日志类
//
// kbConsoleLog.h
// TestDemoForKB
//
// Created by lollipop on 2017/4/1.
// Copyright © 2017年 lollipop. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface kbConsoleLog : NSObject
+ (instancetype)sharedInstance;
- (void)showLog:(NSString *)log;
@end
//.m文件
//
// kbConsoleLog.m
// TestDemoForKB
//
// Created by lollipop on 2017/4/1.
// Copyright © 2017年 lollipop. All rights reserved.
//
#import "kbConsoleLog.h"
@interface kbConsoleLog()
@property (nonatomic,copy) UIWindow *mainWindow;
@property (nonatomic,strong) UITextView *logView;
@end
@implementation kbConsoleLog
static kbConsoleLog *_consoleManager;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_consoleManager = [super allocWithZone:zone];
});
return _consoleManager;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return _consoleManager;
}
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_consoleManager = [[self alloc] init];
});
return _consoleManager;
}
- (void)showLog:(NSString *)log {
if ([NSThread isMainThread]) {
// [self show:log];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
// [self show:log];
});
}
}
- (void)show:(NSString *)log {
[self initMainWindow];
// 打印到控制台中
TDLog(@"%@", log);
NSMutableString *string = [_logView.text mutableCopy];
[string appendFormat:@"\n %@",log];
_logView.text = string;
NSRange bottom = NSMakeRange(string.length - 1, 1);
[_logView scrollRangeToVisible:bottom];
}
- (void)initMainWindow {
if (_mainWindow) {
return;
}
_mainWindow = [[UIWindow alloc] init];
_mainWindow.rootViewController = [UIViewController new];
_mainWindow.windowLevel = UIWindowLevelAlert;
_mainWindow.userInteractionEnabled = NO;
CGFloat rgbF = 30.0 / 255;
_mainWindow.backgroundColor = [UIColor colorWithRed:rgbF green:rgbF blue:rgbF alpha:0.1];
_mainWindow.frame = [UIScreen mainScreen].bounds;
_logView = [[UITextView alloc] init];
_logView.frame = _mainWindow.bounds;
_logView.textColor = [UIColor blackColor];
_logView.backgroundColor = [UIColor clearColor];
_logView.text = @"0-----";
[_mainWindow addSubview:_logView];
_mainWindow.hidden = NO;
[_mainWindow makeKeyWindow];
}
@end
//当前使用蓝牙
-(void)CONNECT{
[LSBuzzBTmanager sharedInstance].delegate = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[LSBuzzBTmanager sharedInstance] FinddevFiter:YES];
});
}
/*
* @descripe 当前蓝牙是否可用
* @param available 当前蓝牙是否可用
*/
- (void)centralManagerDidUpdateState:(BOOL)available {
TDLog(@"--centralManagerDidUpdateState--");
}
/*
* @descripe 搜到蓝牙设备的回调,每次搜到一个新设备都会回调
* @param isFinish 搜索是否结束
* @param list 搜索当前搜索结果
* @param error 参考BuzzError
*/
-(void)fetchDevice:(BOOL)isFinish device:(NSArray *)list apack:(NSArray *)adpacklist withResult:(BuzzResult)error{
self.devList = list;
self.adpackList=adpacklist;
TDLog(@"self.adpackList is %@",self.adpackList);
[self.tableView reloadData];
}
/*
* @descripe 连接制定蓝牙设备结果的回调
* @param error 参考BuzzError
*/
- (void)connectResult:(BuzzResult)result withError:(NSError *)error {
if (result == BuzzBTSuccess) {
[self performSegueWithIdentifier:@"pushToDetail" sender:nil];
}
TDLog(@"error is %@",error);
}
/*
* @descripe 连接被断开,被动断开或者主动断开
* @param error 参考BuzzError
*/
- (void)disConnectResult:(BuzzResult)result withError:(NSError *)error {
TDLog(@"error is %@",error);
[SVProgressHUD dismiss];
[HintView hintViewWithMessage:error.localizedDescription];
// [self initycdata:self.currentmodels];
//远程开门
}
-(void)hasconnectResult:(BuzzResult)result withError:(NSError *)error{
__block RemoteOpenDoorViewController *weakSelf = self;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
NSTimer *timer = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(timerAction) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[timer invalidate];
}
-(void)delayMethod{
[SVProgressHUD dismiss];
[[LSBuzzBTmanager sharedInstance] writeValue:self.currentmacs option:nil];
}
-(void)timerAction{
[[LSBuzzBTmanager sharedInstance] stopSearchDevice];
for (int i=0; i<self.devList.count; i++) {
CBPeripheral *per=self.devList[i];
[[LSBuzzBTmanager sharedInstance] disconnectBuzzard:per];
}
}
//开门
[[LSBuzzBTmanager sharedInstance] connectBuzzard:per];//后台直接操作