背景:
最近公司项目需要接运动手环,获取运动步数,设备时间,重置设备时间等功能,需要进行蓝牙传输。
前言:
1.当前iOS中开发蓝牙所运用的系统库是<CoreBluetooth/CoreBluetooth.h>。
2.蓝牙外设必须为4.0及以上,否则无法开发,蓝牙4.0设备因为低耗电,所以也叫做BLE。
3.CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。
4.服务和特征(service and characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你可以理解为蓝牙所拥有的能力),而每个服务service下拥有若干个特征characteristic(特征你可以理解为解释这个服务的属性)。
5.Descriptor(描述)用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的单位。
图示
名词解释:
BLE:(Bluetooth low energy)蓝牙4.0设备因为低耗电;
Central:中心设备,相对比较强大,用来连接其他外围设备(手机);
Peripheral:外围设备,这一般就是非常小或者简单的低功耗设备,用来提供数据,并连接到一个更加相对强大的中心设备(小米手环等等等);
Services:服务,蓝牙外设对外广播的必定会有一个服务,可能也有多个,服务下面包含着一些特征,服务可以理解成一个模块的窗口;
Characteristic:特征,存在于服务下面的,一个服务下面也可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;
Description:描述,每个Characteristic可以对应一个或多个Description用于描述Characteristic的信息或属性(暂时不太了解)
UUID:可以理解成蓝牙上的唯一标识符(硬件上是Mac值,iOS应该是被系统拦截了,所以获取不到Mac值,相对于转化而来是的UUID),为了区分不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征。
流程:
1、建立一个Central Manager实例进行蓝牙管理CBCentralManagerDelegate
2、搜索外围设备(一直目标设备名称可指定搜索)
3、连接外围设备(目标设备)CBPeripheralDelegate
4、获得外围设备的服务
5、获得服务的特征(该步骤进行特征的读、写、通知等属性)
6、从外围设备读数据
7、给外围设备发送数据
1.属性
// 外围设备(一般是手机)设备
@property (nonatomic, strong) CBCentralManager *mCentral;
// 外设设备
@property (nonatomic, strong) CBPeripheral *mPeripheral;
// 特征值
@property (nonatomic, strong) CBCharacteristic *mCharacteristic;
// 服务
@property (nonatomic, strong) CBService *mService;
2.协议<CBCentralManagerDelegate, CBPeripheralDelegate>
2.1. CBCentralManagerDelegate----设备链接需要的协议
/**
* 首先设备管理的类初始化,一旦初始化完成之后,就会遵循CBCentralManagerDelegate协议
*/
_mCentral = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
@required
/**
* 蓝牙的所有状态,根据状态进行相关操作
* 开启--扫描
* 未开启/不支持蓝牙等--友好提示
typedef NS_ENUM(NSInteger, CBCentralManagerState) {
CBCentralManagerStateUnknown = CBManagerStateUnknown,
CBCentralManagerStateResetting = CBManagerStateResetting,
CBCentralManagerStateUnsupported = CBManagerStateUnsupported,
CBCentralManagerStateUnauthorized = CBManagerStateUnauthorized,
CBCentralManagerStatePoweredOff = CBManagerStatePoweredOff,
CBCentralManagerStatePoweredOn = CBManagerStatePoweredOn,
} NS_DEPRECATED(10_7, 10_13, 5_0, 10_0, "Use CBManagerState instead");
*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
@optional
/**
* 发现外设蓝牙设备的方法
* <这里进行会搜索到周边具有蓝牙功能的设备,可以通过对具体蓝牙名称或者已知蓝牙名称前缀进行筛选你要的蓝牙设备,并且目标设备在这里遵循CBPeripheralDelegate 协议,进行搜索设备的services>
* [self.mCentral stopScan]; // 停止扫描
self.mPeripheral = peripheral;
// CBPeripheralDelegate
self.mPeripheral.delegate = self; // 遵循协议
[self.mCentral connectPeripheral:peripheral options:nil]; // 连接外设-->扫描services
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI;
/**
* 中心管理者(连接外设)连接成功
*/
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
/**
* 中心管理者(连接外设)连接失败
*/
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral;
/**
* 中心管理者(外设丢失)连接丢失
*/
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
2.2 CBPeripheralDelegate
/**
* 扫描目标外设services回调
* [peripheral discoverCharacteristics:nil forService:service]; // 扫描service下面的特征值characteristics
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;
/**
* 获取特征值characteristics和描述
* 这里可以查询到具体characteristic属性properties(枚举类型)的读,写,通知,根据UUID(需要跟硬件工程师协商统一的)
* typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x200
};
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
/**
* 读取特征中的值和描述:更新特征值的时候调用,可以理解为获取蓝牙返回的数据(需要的所有数据均在这里进行处理)
*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
/**
* 状态改变和发现描述
*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
/**
* 发现外设的特征的描述数组
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error;
/**
* 写入数据成功与否的回调
*/
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
描述
我们是从手环中获取到运动步数,怎么获取,如何接收数据,怎么处理数据?这是开发前就需要了解的。通常硬件工程师会制定一个蓝牙数据处理协议,就称之为蓝牙协议吧,因为它关系着你能否获取到你想要的数据,导致能否按时完成项目。
-
我们的蓝牙协议是这样的
读懂蓝牙协议,进行指令发送(按照工程师给的蓝牙协议拼接指令数组)
// 获取总步数指令
Byte b[] = {0xC6};
// 获取每个小时步数指令
Byte b2[] = {0xC4, 0x03, 0x01, 0x8, 0x03};
NSData *data = [NSData dataWithBytes:&b length:sizeof(b)];
NSData *data2 = [NSData dataWithBytes:&b2 length:sizeof(b2)];
// NSLog(@"%@", data);
// [self.mPeripheral writeValue:data forCharacteristic:self.mCharacteristic type:CBCharacteristicWriteWithResponse];
[self.mPeripheral writeValue:data2 forCharacteristic:self.mCharacteristic type:CBCharacteristicWriteWithResponse];
- 数据的处理,这里根据不同的硬件工程师规定的蓝牙协议进行自行处理数据哈(根据工程师给的蓝牙协议按照对应字节进行解析16进制数据)
首先看看硬件工程师返回给我们的数据:
A*小于20字节的返回一次就够了
如:<29071306 1c090303 05130000 00000000 00000000>
B*大于20字节的分段接受,每次20字节
如:<24281306 18080034 00000000 02fb0000 0000006d>
<00000000 00000000 00000000 00000000 00000000>
<0000a5>
上面数据的解析思路(以我们的协议为参考,具体情况按照你们的来):
24281306 1808 解析:命令头24,数据长度0x28,然后是19年6月24日8点。。
数据是每6个byte为一组,前两个是步数,比如 0034 00000000表示 0x0034步,高两bit是用来表示步数或是睡眠,00表示步数,01表示睡眠,步数用14bit表示
图形化解析
开发这种东西首先要理解流程,其次才是代码,代码千千万,万变不离其宗,因为写这篇文章的时候还在开发公司的项目,而且离交付项目没多少时间了,所以代码写的只是为了调试,都有备注的,各位看官还请手下留情!!!
如果发现问题还请及时指出,与君共勉!!!
如果有需求,可以直接与本人联系,QQ:1749281699
最后再次发下Demo
附加:
可以使用以下测试软件进行测试:
LightBlue
nrf connect