一、关于蓝牙从连接到读发数据
在这篇文章中,我会按照上篇文章中介绍的流程,来代码实现:(由于自己也是第一次做蓝牙模块内容,有不对的地方还希望大神指正,小弟不胜感激......)
#import "BLEController.h"
//需要遵守蓝牙协议
#import <CoreBluetooth/CoreBluetooth.h>
#define kPeripheralName @"B10001"//硬件设备蓝牙名称
#define kServiceUUID @"0003CDD0-0000-1000-8000-00805F9B0131" //服务的UUID
#define kWriteCharacteristicUUID @"0003CDD2-0000-1000-8000-00805F9B0131" //特征的UUID
#define kNotifyCharacteristicUUID @"0003CDD1-0000-1000-8000-00805F9B0131" //特征的UUID
@interface BLEController ()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *manager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *character;
@end
1、创建蓝牙管理者对象
//第二个参数:nil默认为主线程
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
2、该方法当蓝牙状态改变(打开或者关闭)的时候就会调用
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBManagerStateUnknown:{
NSLog(@"未知蓝牙状态");
}
break;
case CBManagerStateResetting:{
NSLog(@"系统服务连接暂时丢失");
}
break;
case CBManagerStateUnsupported:{
NSLog(@"该设备不支持蓝牙");
}
break;
case CBManagerStateUnauthorized:{
NSLog(@"该设备蓝牙为被授权");
}
break;
case CBManagerStatePoweredOn:{
NSLog(@"该设备蓝牙已打开");
//打开后,开始扫描
[self scanBlueTooth];
}
break;
case CBManagerStatePoweredOff:{
NSLog(@"该设备蓝牙没有打开");
}
break;
}
}
3、发现外设后调用的方法
//查到外设后,连接设备,停止扫描
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
//可在该方法内部区分扫描到的蓝牙设备
NSLog(@"发现外设peripheral:%@ =====RSSI:%@ =====UUID:%@", peripheral, RSSI, peripheral.identifier);
//判断是否为你要连接的设备(我这里用设备名称判断的,扫描到我要的外设蓝牙名称后,停止扫描、连接外设)
if ([peripheral.name isEqualToString:kPeripheralName]) {
//扫描到设备之后停止扫描
[_manager stopScan];
//开始连接外设
[_manager connectPeripheral:peripheral options:nil];
_peripheral = peripheral;
}
}
3.1、连接成功会被调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"=====>%@连接成功 =====>UUID: %@",peripheral.name,peripheral.identifier);
//连接设备之后设置蓝牙对象的代理,扫描服务
[self.peripheral setDelegate:self];
// 外设发现服务,传nil代表不过滤
// 这里会触发外设的代理 didDiscoverServices 方法
[self.peripheral discoverServices:nil];
//这是我们业务需求:0.2s给我们的采集卡设备发一次命令来读数
[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(timered:) userInfo:nil repeats:YES];
}
//返回的蓝牙服务通知通过代理实现(已经发现服务)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSLog(@"=====>发现服务");
//一个设备可能为多个服务,所以要取你需要读写的那个服务(我们的外设就一个服务)
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
//根据你要的那个服务去发现特性
[self.peripheral discoverCharacteristics:nil forService:service];
}
}
}
3.2、连接失败会被调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"=====>连接失败");
}
3.3、断开连接会被调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"=====>断开连接");
//断开连接后,可能是因为信号不好,所以我们的需求是继续连接我们的设备
[self scanBlueTooth];
}
4、获得外围设备的服务、获得服务的特征
//已经发现特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
//首先读到外设特征服务,写到宏定义
NSLog(@"发现特征服务:%@",service.UUID);
for (CBCharacteristic *c in service.characteristics) {
//其次读到两个特征值,写到宏定义
NSLog(@"特征UUID: %@",c.UUID);
//哪个特征值是读的,哪个特征值是写的,一般硬件兄弟都会告诉你的
//(即使没有告诉你,也可以自己在AppStore下载LightBlue或UsrBleAssistent蓝牙调试工具看到你需要读写的特征值)
if ([c.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID]) {
//找到可写特征值D2
_character = c;
}else if ([c.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID]){
//设置通知
[_peripheral setNotifyValue:YES forCharacteristic:c];
}
}
}
5、从外围设备读数据
//获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (!error) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyCharacteristicUUID]]) {
// 此处的byte数组就是接收到的数据
NSString *str = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(@"原始数据:%@===>字符串:%@",characteristic.value,str);
}
}else{
NSLog(@"error:%@", error);
}
}
6、给蓝牙发送数据(及前边定时器的实现)
#pragma mark - 定时器时间事件
- (void)Timered:(NSTimer*)timer {
//读信号强度
[_peripheral readRSSI];
//实时读数
[self writeDataToBle:@"*APP.VALUE#$"];
}
//向蓝牙写入数
- (void)writeDataToBle:(NSString *)aStr{
if (_character.properties & CBCharacteristicPropertyWrite) {
//BLE设备接收的数据需要转为NSData类型
NSData *data = [aStr dataUsingEncoding:NSUTF8StringEncoding];
//第一个参数:你要写入的数据,为data类型。
//第二个参数:你要向哪个特征写入,一般就是属性为写入的那个特征。
//第三个参数:你写入的方式,这个参数有两个枚举分别是:
//CBCharacteristicWriteWithResponse//写入之后有回应
//CBCharacteristicWriteWithoutResponse//写入之后不需要回应
//使用WithResponse的成功后就会调用:-(void)peripheral:(CBPeripheral )peripheral didWriteValueForCharacteristic:(CBCharacteristic )characteristic error:(NSError )error;
[_peripheral writeValue:data forCharacteristic:_character type:CBCharacteristicWriteWithResponse];
}else{
NSLog(@"该字段不可写!");
}
}
到此BLE4.0从扫描到读数,已经实现
二、关于蓝牙你需要知道
1、一般蓝牙的读写通道(Characteristics)都是分开的,少数蓝牙会读写用同一个通道,在发现目标通道后,我们要利用写通道来向蓝牙写入数据,用读通道来读取蓝牙发来的包。
2、对于外设的BLE的唯一标识,亲测同一设备的 UUID 对于每台iOS设备都不一样,只能尽量保证设备的唯一性;特别是自动重连的过程,让用户没有感知
(在此工程中,我是根据外设的名字来判断链接的!因为我们的BLE外设名称都是以公司名称字母开头的!)
3、关于BLE的Mac:在开发过程中发现CBCentralManager 和 CBPeripheral 里边都找不到和Mac地址相关的字段,所以如果领导要外设的Mac,你大胆的告诉他:你无能为力!
(但有个解决办法:你可以与硬件工程师商量,能否将BLE的Mac地址写在服务里,这样可以为自动连接的唯一性做准备)
4、关于Read还是Notify的区别:
比如说我现在连接的是一个骑行设备上的蓝牙,此设备上有一个照明灯,现在APP可以控制这个灯,我发送开启的命令之后,如果开启成功那么设备会返回给我一个开启成功的信息,此时就是读取。
而通知呢就是:APP需要实时显示该设备的速度,我不需要向设备发送任何的指令,只要它的速度发生了变化,就会向APP发送此时的速度信息(关于区别这块内容是百度其他作者的,不知对不对)
当然关于BLE的实现也有好多优秀的三方库实现:比如 BabyBluetooth
BabyBluetooth是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X。
BabyBluetooth的优点:
1、基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮你更简单地使用CoreBluetooth API。
2、CoreBluetooth所有方法都是通过委托完成,代码冗余且顺序凌乱。BabyBluetooth使用block方法,可以重新按照功能和顺序组织代码,并提供许多方法减少蓝牙开发过程中的代码量。
3、链式方法体,代码更简洁、优雅。
4、通过channel切换区分委托调用,并方便切换
5、完善的文档,且项目处于活跃状态,不断的更新中
6、github上star最多的纯Bluetooch类库(非PhoneGap和SensorTag项目)