最近在做一个项目,是关于运动手表的,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来排版,关于其他相关的我也会继续研究并更新,忘大家共同学习,共同进步。