不常用,倒是可以装逼iOS学习开发iOS Developer

IOS9 BLE 与外设交互及设备绑定

2016-06-04  本文已影响1201人  AppleLSY

最近在做一个项目,是关于运动手表的,APP获取手表传来的步数继而进行接下来的一些操作。也属于第一次接触蓝牙相关的项目,就开始猛啃蓝牙的相关信息。我没有去深入研究BabyTooth库,总感觉CoreBluetooth能让我更好的理解整个流程...当然大家可以把这认为是懒..,好了言归正传。

BLE属于低功耗蓝牙,但传输速率在我感觉有点慢。

外围设备和中央设备在CoreBluetooth中使用CBPeripheralManager和CBCentralManager表示。

CBPeripheralManager:外围设备通常用于发布服务、生成数据、保存数据。外围设备发布并广播服务,告诉周围的中央设备它的可用服务和特征。

CBCentralManager:中央设备使用外围设备的数据.中央设备扫描到外围设备后会就会试图建立连接,一旦连接成功就可以使用这些服务和特征。

外围设备和中央设备之间交互的桥梁是服务(CBService)和特征(CBCharacteristic),二者都有一个唯一的标识UUID(CBUUID类型)来唯一确定一个服务或者特征,每个服务可以拥有多个特征,UUID这个坑我已深陷多回..这后面再详说。下面是他们之间的关系:

一些UUID的宏设置

#define kSeverviceUUID @"3D9E5046-325A-A248-F1FE-15380D827D21"//获取到的设备的UUID,测试绑定的时候用的

#define kStepServiceUUID @"0C301900-BEB8-5C69-8714-099C77103418"//服务的UUID

#define kStepRecordsUUID @"0C302ABC-BEB8-5C69-8714-099C77103418"//特征的UUID

#define kStepControl @"0C302ABE-BEB8-5C69-8714-099C77103418"

1.创建中心设备

#pragma mark -扫描蓝牙外设

- (void)scanBtnClick

{

//创建中心设备管理器并设置当前控制器试图为代理

_centralManage= [[CBCentralManager alloc] initWithDelegate:self queue:nil];

}

接下来进入CBCentralManagerDelegate代理方法

2.中心服务器状态

#pragma mark - CBCentralManagerDelegate代理方法

//中心服务器状态更新后

- (void)centralManagerDidUpdateState:(CBCentralManager*)central

{

switch(central.state)

{

//蓝牙为开启状态,iOS应该是8以后吧,不加状态判断崩溃

case CBCentralManagerStatePoweredOn:

NSLog(@"BLE已打开");

{

_hud= [MBProgressHUD showHUDAddedTo:self.view.window animated:YES];

_hud.delegate=self;

_hud.labelText=@"扫描外设中..";

self.centralManage= central;

//这里填写nil,会扫描所有外设,但是当我写入一个指定的外设UUID的时候,却扫不到任何设备,目前不清楚是什么原因,希望大家告知。

[central scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];

}

break;

default:

NSLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为外围设备");

break;

}

}

#pragma mark - 发现外设

- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber*)RSSI

{

//根据外设名的前缀来查找相应的外设,本来在绑定的时候我打算通过判断外设的名字来绑定设备的,但是发现行不通,当时就这个绑定问题很是苦恼

if([peripheral.name hasPrefix:@"PD"])

{

_hud.labelText= [NSString stringWithFormat:@"扫描到外设%@,开始连接...",peripheral.name];

//坑来啦!就是这个坑啊,在这里可以获取到外设的 identifier,name 和 RSSI(信号强弱),我一直以为identifier就是外设的UUID,就一直用peripheral.identifier来做判断,一直就不走方法,打印出来的identifier你会发现是这样一串字符"<__NSConcreteUUID 0x15106e470> 3D9E5046-325A-A248-F1FE-15380D827D21",UUID前面还有一串字符,所以说identifier并不是真正的UUID。最后发现identifier还有个UUIDString的方法,接下来就可以通过判断UUIDString和当前设备的UUID是否一致来选择是否连接该设备来实现绑定。

[USER_D setObject:peripheral.identifier.UUIDString forKey:@"peripheralUUID"];

//停止扫描

[self.centralManage stopScan];

NSLog(@"获得的外设的UUID---%@",peripheral.identifier.UUIDString);

NSLog(@"advertisementData--%@",advertisementData);

NSLog(@"*****没有绑定*****");

self.peripheral= peripheral;

NSLog(@"开始连接外围设备");

//[self.centralManage connectPeripheral:peripheral options:nil];

[self.centralManage connectPeripheral:peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];

}

}

#pragma mark -连接到外围设备

- (void)centralManager:(CBCentralManager*)central didConnectPeripheral:(CBPeripheral*)peripheral

{

NSLog(@"连接外围设备成功");

_hud.labelText=@"连接外设成功";

//连接成功后建立一个通知,通知相机连接已完成(这个是写的自定义相机来实现蓝牙控制拍照)

[[NSNotificationCenter defaultCenter] postNotificationName:@"connected" object:nil];

//设置外围设备的代理为当前控制器

peripheral.delegate=self;

//外围设备开始寻找服务

[peripheral discoverServices:@[[CBUUID UUIDWithString:kStepServiceUUID]]];

}

#pragma mark - CBPeripheralDelegate代理方法

//外围设备寻找到服务器后

- (void)peripheral:(CBPeripheral*)peripheral didDiscoverServices:(nullableNSError*)error

{

NSLog(@"已发现可用服务...");

if(error)

{

NSLog(@"外围设备寻找服务过程中发生错误,错误信息:%@",error.localizedDescription);

}

//遍历查找到的服务

CBUUID*serviceUUID = [CBUUID UUIDWithString:kStepServiceUUID];

CBUUID*stepRecordsUUID = [CBUUID UUIDWithString:kStepRecordsUUID];

CBUUID*stepControl = [CBUUID UUIDWithString:kStepControl];

for(CBService*service in peripheral.services)

{

if([service.UUID isEqual:serviceUUID])

{

//外围设备查找指定服务中的特征

[peripheral discoverCharacteristics:@[stepRecordsUUID,stepControl] forService:service];

}

}

}

#pragma mark - 外围设备寻找到特征后

- (void)peripheral:(CBPeripheral*)peripheral didDiscoverCharacteristicsForService:(CBService*)service error:(nullableNSError*)error

{

     //在这里向外设写入内容,先遍历特征,遍历到可写入的特征

    //写入特征也比较坑,当时我拿到的硬件文档纯英文的不说,而且给的东西很少,很不清晰,你所写入的数据应该是硬件那边提供的,写入的分方法就是writeValue

//写入目标

[peripheral writeValue:[NSDatadataWithBytes:(uint8_t[]){43, goal, goal>>8,0,2} length:5] forCharacteristic:characterstic type:CBCharacteristicWriteWithResponse];//这是我写入目标的一种数据格式

}

#pragma mark - 特征值被更新后

- (void)peripheral:(CBPeripheral*)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic*)characteristic error:(nullableNSError*)error

{

NSLog(@"收到特征更新通知...");

if(error)

{

NSLog(@"更新通知状态时发生错误,错误信息:%@",error.localizedDescription);

}

//给特征值设置新的值

CBUUID *stepRecordsUUID = [CBUUID UUIDWithString:kStepRecordsUUID];

if([characteristic.UUID isEqual:stepRecordsUUID])

{

if(characteristic.isNotifying)

{

if(characteristic.properties == CBCharacteristicPropertyNotify)

{

NSLog(@"已订阅特征通知");

[peripheral readValueForCharacteristic:characteristic];

return;

}

elseif(characteristic.properties == CBCharacteristicPropertyRead)

{

//从外围设备读取新值,调用此方法会触发代理方法:- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error

[peripheral readValueForCharacteristic:characteristic];

}

else

{

NSLog(@"停止已停止");

//取消连接

[self.centralManage cancelPeripheralConnection:peripheral];

}

}

}

}

#pragma mark - 更新特征后(调用readValueForCharactrtistic:方法或者外围设备在订阅后更新特征值都会调用此方法)

- (void)peripheral:(CBPeripheral*)peripheral didUpdateValueForCharacteristic:(CBCharacteristic*)characteristic error:(nullableNSError*)error

{

//在这里读取外设传来的数据

//这只是一部分代码

NSData*data = characteristic.value;

uint32_t minuteTimestamp =0;

[data getBytes:&minuteTimestamp range:NSMakeRange(1,3)];

const uint8_t*bytes = data.bytes;

if(minuteTimestamp !=0)

{

NSDate*time = [NSDate dateWithTimeIntervalSince1970:SEC_FROM_1970_TO_2012 + minuteTimestamp *60];

NSTimeZone*zone = [NSTimeZone systemTimeZone];

NSInteger interval = [zone secondsFromGMTForDate:time];

NSDate*localDate = [time dateByAddingTimeInterval:interval];

[newText appendFormat:@"%@ %u, %u, %u, %u, %u\n",

localDate, bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]];

NSString*dateString = [NSString stringWithFormat:@"%@",localDate];

NSLog(@"======dateString====%@",dateString);

NSString*stepCount = [NSString stringWithFormat:@"%u",bytes[4]+bytes[5]+bytes[6]+bytes[7]+bytes[8]];

//把数据存入数据库中

[StepDAO insertData:dateString AndSteps:stepCount];

NSLog(@"读取到的特征时间和步数:%@,%@",newText,stepCount);

//其他的卡路里什么的计算就不写了,太复杂..算法是客户那边提供的..看着都恶心..

}

最后再说下,我发现其实BLE在读写数据的时候已经实现了绑定了,因为在写过断开重连后,重连的只会是之前连接的那个设备,但重新扫描就不一定扫描那个设备了,所以在链接外设的代理方法里判断identifier.UUIDString。

好啦,BLE的开发大概就是这样的一个流程了,其实并不是说很难,只要理清这个逻辑就行。第一次写,也懒得用MarkDown来排版,关于其他相关的我也会继续研究并更新,忘大家共同学习,共同进步。 

上一篇下一篇

猜你喜欢

热点阅读