前言:
上一篇文章講了iOS藍(lán)牙開發(fā)的基礎(chǔ)與Demo的應(yīng)用http://m.itdecent.cn/p/6e079da2370c, 這一章講講藍(lán)牙在項目中實戰(zhàn)的.
藍(lán)牙Demo鏈接地址: https://github.com/iOSzhangkai/BLE4.0_iOS
工程環(huán)境
(1) xcode 8.2
(2)iOS 10.1
前期思路
在項目實施之前處于藍(lán)牙學(xué)習(xí)階段,對于藍(lán)牙的理解只處于可以使用,,知道這是藍(lán)牙...藍(lán)牙內(nèi)部方法,底層實現(xiàn)原理完全懵逼,大寫的懵逼,所以在demo中將藍(lán)牙方法整個粘入控制器中,經(jīng)過惡心而又漫長的原始階段終于將藍(lán)牙外設(shè)與手機連接給整出來了.....但是因為iOS提供藍(lán)牙外設(shè)連接的<CoreBluetooth/CoreBluetooth.h> 中的所以方法都是通過代理的實現(xiàn),導(dǎo)致項目的控制器頁面代碼行數(shù)多達(dá)兩千行,這對于一個程序猿是個恥辱啊!!![一個很小的功能],在項目上線后的幾周陸續(xù)看了許多大牛的藍(lán)牙封裝類,決定自己要改造這個控制器代碼,有寫的不到之處希望各位可以指出,共同進步.
*廢話不多說 直接上代碼 *
藍(lán)牙管理類的.h
#import <Foundation/Foundation.h>
//BLIE4.0 藍(lán)牙庫
#import <CoreBluetooth/CoreBluetooth.h>
//藍(lán)牙連接狀態(tài)的定義
#define kCONNECTED_UNKNOWN_STATE @"未知藍(lán)牙狀態(tài)"
#define kCONNECTED_RESET @"藍(lán)牙重置"
#define kCONNECTED_UNSUPPORTED @"該設(shè)備不支持藍(lán)牙"
#define kCONNECTED_UNAUTHORIZED @"未授權(quán)藍(lán)牙權(quán)限"
#define kCONNECTED_POWERED_OFF @"藍(lán)牙已關(guān)閉"
#define kCONNECTED_POWERD_ON @"藍(lán)牙已打開"
#define kCONNECTED_ERROR @"未知的藍(lán)牙錯誤"
/**
設(shè)備名稱
*/
#define PMServiceName @"Bozonn-Air01"
/**
設(shè)備UUID
*/
#define PMServiceUUID @"FFE0"
/**
設(shè)備配置UUID
*/
#define PMServiceUUID_Config @"00002902-0000-1000-8000-00805f9b34fb"
/**
設(shè)備讀取UUID
*/
#define PMServiceUUID_Receive @"0000ffe1-0000-1000-8000-00805f9b34fb"
/**
設(shè)備寫入UUID
*/
#define PMServiceUUID_Send @"0000ffe1-0000-1000-8000-00805f9b34fb"
/**
藍(lán)牙鏈接狀態(tài)
@param state 狀態(tài)
*/
typedef void (^BLELinkBlock)(NSString *state);
/**
藍(lán)牙返回數(shù)據(jù)
@param array 返回數(shù)據(jù)
*/
typedef void (^BLEDataBlock)(NSMutableArray *array);
typedef enum BLEState_NOW{
BLEState_Successful = 0,//連接成功
BLEState_Disconnect = 1, // 失敗
BLEState_Normal, // 未知
}BLEState_NOW;
/**
藍(lán)牙連接成功 或者斷開
*/
typedef void(^BLEStateBlcok)(int number);
@interface BLEModel : NSObject
@property (nonatomic,copy) NSString *connectState;//藍(lán)牙連接狀態(tài)
@property (nonatomic,copy) BLELinkBlock linkBlcok;
@property (nonatomic,copy) BLEDataBlock dataBlock;
@property (nonatomic,copy) BLEStateBlcok stateBlock;
/**
主動斷開鏈接
*/
-(void)cancelPeripheralConnection;
/**
發(fā)送命令
*/
- (void) sendData:(NSData *)data;
藍(lán)牙管理類.m
#import "BLEModel.h"
#import "BLEIToll.h"
@interface BLEModel ()<CBCentralManagerDelegate,CBPeripheralDelegate>
/**
* 藍(lán)牙連接必要對象
*/
@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- 掃描設(shè)備,連接
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"name:%@",peripheral);
/**
當(dāng)掃描到藍(lán)牙數(shù)據(jù)為空時,停止掃描
*/
if (!peripheral || !peripheral.name || ([peripheral.name isEqualToString:@""])) {
return;
}
/**
當(dāng)掃描到服務(wù)UUID與設(shè)備UUID相等時,進行藍(lán)牙與設(shè)備鏈接
*/
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 - 藍(lán)牙的狀態(tài)
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBManagerStateUnknown:
{
//NSLog(@"無法獲取設(shè)備的藍(lán)牙狀態(tài)");
self.connectState = kCONNECTED_UNKNOWN_STATE;
}
break;
case CBManagerStateResetting:
{
//NSLog(@"藍(lán)牙重置");
self.connectState = kCONNECTED_RESET;
}
break;
case CBManagerStateUnsupported:
{
//NSLog(@"該設(shè)備不支持藍(lán)牙");
self.connectState = kCONNECTED_UNSUPPORTED;
}
break;
case CBManagerStateUnauthorized:
{
//NSLog(@"未授權(quán)藍(lán)牙權(quán)限");
self.connectState = kCONNECTED_UNAUTHORIZED;
}
break;
case CBManagerStatePoweredOff:
{
//NSLog(@"藍(lán)牙已關(guān)閉");
self.connectState = kCONNECTED_POWERED_OFF;
}
break;
case CBManagerStatePoweredOn:
{
//NSLog(@"藍(lán)牙已打開");
self.connectState = kCONNECTED_POWERD_ON;
[_centralMgr scanForPeripheralsWithServices:nil options:nil];
}
break;
default:
{
//NSLog(@"未知的藍(lán)牙錯誤");
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(@"該設(shè)備的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 服務(wù)UUID:%@): %@ (data:%@)",service.UUID.description,c.UUID,c.UUID.data);
/**
>>> 特征UUID FOUND(in 服務(wù)UUID:FFE0): FFE1 (data:<ffe1>)
>>> 特征UUID FOUND(in 服務(wù)UUID:FFE0): FFE2 (data:<ffe2>)
*/
/*
根據(jù)特征不同屬性去讀取或者寫
if (c.properties==CBCharacteristicPropertyRead) {
}
if (c.properties==CBCharacteristicPropertyWrite) {
}
if (c.properties==CBCharacteristicPropertyNotify) {
}
*/
/*
設(shè)備讀取UUID
*/
if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Receive]]) {
self.writeCharacteristic = c;
[_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
}
/**
設(shè)備寫入UUID
*/
if ([c.UUID isEqual:[CBUUID UUIDWithString:PMServiceUUID_Send]]) {
self.writeCharacteristic = c;
[_discoveredPeripheral setNotifyValue:YES forCharacteristic:self.writeCharacteristic];
}
}
}
#pragma mark - 讀取數(shù)據(jù)
- (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- 外設(shè)斷開連接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
NSLog(@"外設(shè)斷開連接 %@: %@\n", [peripheral name], [error localizedDescription]);
self.stateBlock(BLEState_Disconnect);
//重連外設(shè)
if (!self.isInitiativeDisconnect) {
[self.centralMgr connectPeripheral:peripheral options:nil];
}
}
#pragma mark - 主動斷開連接
-(void)cancelPeripheralConnection{
self.isInitiativeDisconnect = YES;
if (self.discoveredPeripheral) {//已經(jīng)連接外設(shè),則斷開
[self.centralMgr cancelPeripheralConnection:self.discoveredPeripheral];
}else{//未連接,則停止搜索外設(shè)
[self.centralMgr stopScan];
}
}
/**
發(fā)送命令
*/
- (void) sendData:(NSData *)data{
/**
通過CBPeripheral 類 將數(shù)據(jù)寫入藍(lán)牙外設(shè)中,藍(lán)牙外設(shè)所識別的數(shù)據(jù)為十六進制數(shù)據(jù),在ios系統(tǒng)代理方法中將十六進制數(shù)據(jù)改為 NSData 類型 ,但是該數(shù)據(jù)形式必須為十六進制數(shù) 0*ff 0*ff格式 在iToll中有將 字符串轉(zhuǎn)化為 十六進制 再轉(zhuǎn)化為 NSData的方法
*/
[self.discoveredPeripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
//向peripheral中寫入數(shù)據(jù)后的回調(diào)函數(shù)
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//該方法可以監(jiān)聽到寫入外設(shè)數(shù)據(jù)后的狀態(tài)
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方法創(chuàng)建 ,在init方法中就已經(jīng)將藍(lán)牙操作對象進行初始化,遵循代理,在控制器中無需遵循任何的藍(lán)牙代理,引入藍(lán)牙庫
*/
_manager = [[BLEModel alloc]init];
/**
BLEModel 中的 BLELinkBlock 是將藍(lán)牙當(dāng)前狀態(tài)進行回調(diào),方便在項目中對藍(lán)牙處于什么狀態(tài)進行操作
*/
_manager.linkBlcok = ^(NSString *state){
NSLog(@"%@",state);
};
/**
BLEModel 中的BLEStateBlcok 是手機與外設(shè)鏈接狀態(tài)的回調(diào),
*/
_manager.stateBlock = ^(int number){
NSLog(@"%d",number);
};
/**
BLEModel 中的BLEDataBlock 是外設(shè)返回數(shù)據(jù)到手機
外設(shè)返回的數(shù)據(jù)一般為十六進制數(shù)據(jù),在BLEModel 內(nèi)部中已經(jīng)將十六進制數(shù)據(jù)進行一次處理,把拿到的數(shù)據(jù)已字符串的類型添加到array中,所以可以直接使用對array進行操作,無需再做處理
*/
_manager.dataBlock = ^(NSMutableArray *array){
NSLog(@"%@",array);
}
};
/*
設(shè)備給藍(lán)牙傳輸數(shù)據(jù) 必須以十六進制數(shù)據(jù)傳給藍(lán)牙 藍(lán)牙設(shè)備才會執(zhí)行
因為iOS 藍(lán)牙庫中方法 傳輸書記是以NSData形式
因此封裝 stringToByte 方法的作用字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)
發(fā)送一個 0xFA 0xE4 Ox33 的命令
*/
NSData *data = [[BLEIToll alloc] stringToByte:@"FAE433"];
NSLog(@"寫入數(shù)據(jù)%@",data);
[_manager sendData:data];
}
/**
* 設(shè)備給藍(lán)牙傳輸數(shù)據(jù) 必須以十六進制數(shù)據(jù)傳給藍(lán)牙 藍(lán)牙設(shè)備才會執(zhí)行
因為iOS 藍(lán)牙庫中方法 傳輸書記是以NSData形式 這個方法 字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)
*
* @param string 傳入字符串命令
*
* @return 將字符串 ---> 十六進制數(shù)據(jù) ---> NSData數(shù)據(jù)
*/
-(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進制數(shù)中的第一位(高位*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進制數(shù)中的第二位(低位)
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; ///將轉(zhuǎn)化后的數(shù)放入Byte數(shù)組里
[bytes appendBytes:tempbyt length:1];
}
return bytes;
}
小結(jié)
在開發(fā)中將BLEModel.h, BLEModel.m文件拖入工程 ,在控制器中實現(xiàn)BluetoothConnection方法就可以完成手機與外設(shè)連接,讀取外設(shè)數(shù)據(jù),對外設(shè)發(fā)送指令的需求.