iOS 蓝牙BLE开发
GAP(Generic Access Profile):它用来控制设备连接和广播,GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。
GATT(Generic Attribute Profile):BLE连接都是建立在GATT协议之上的。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。
BLE中主要有两个角色:外围设备(Peripheral)和中心设备(Central)。一个中心设备可以连接多个外围设备,一个外围设备包含一个或多个服务(services),一个服务包含一个或多个特征(characteristics)。
1、外围设备代码(CBPeripheralManager)
使用CoreBluetooth库,创建CBPeripheralManager,实现CBPeripheralManagerDelegate代理
//nil表示在主线程中执行。
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
创建完该对象,会回调peripheralManagerDidUpdateState:方法判断蓝牙状态,蓝牙可用,给外设配置服务和特征
CBMutableCharacteristic *writeReadCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"FF01"] properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsReadEncryptionRequired | CBAttributePermissionsWriteEncryptionRequired];
CBMutableService *service = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"FF66"] primary:YES];
[service setCharacteristics:@[writeReadCharacteristic]];
[self.peripheralManager addService:service];
注意CBAttributePermissions
typedef NS_OPTIONS(NSUInteger, CBAttributePermissions) {
CBAttributePermissionsReadable = 0x01, //可读
CBAttributePermissionsWriteable = 0x02, //可写
CBAttributePermissionsReadEncryptionRequired = 0x04, //可读,需要建立安全连接
CBAttributePermissionsWriteEncryptionRequired = 0x08 // //可写,需要建立安全连接
} NS_ENUM_AVAILABLE(10_9, 6_0);
当中心设备读写设置CBAttributePermissionsReadEncryptionRequired/CBAttributePermissionsWriteEncryptionRequired权限的Characteristic时,会弹出弹框,请求建立安全连接
image.png
给外设配置服务特征后,会调用peripheralManager:didAddService:error: 服务特征全部添加完后发起广播,如果在广播时设置CBAdvertisementDataServiceUUIDsKey,会把该service广播出去,中心设备在扫描时可根据该uuid找到该设备。外围设备靠不断发广播,使中心设备发现它。
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:@"0xFF66"]],CBAdvertisementDataLocalNameKey:@"ios设备"}];
当中央端连接上了此设备并订阅了特征时会回调 didSubscribeToCharacteristic:
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
[self.peripheralManager updateValue:[@"订阅特征" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic onSubscribedCentrals:nil];
}
当接收到中央端读的请求时会调用didReceiveReadRequest:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = [@"收到读的请求" dataUsingEncoding:NSUTF8StringEncoding];
[request setValue:data];
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
} else {
[self.peripheralManager respondToRequest:request withResult:CBATTErrorReadNotPermitted];
}
}
2、中心设备代码(CBCentralManager)
创建CBCentralManager对象,实现CBCentralManagerDelegate代理
self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
回调centralManagerDidUpdateState:代理方法,当central.state==CBManagerStatePoweredOn时,开启扫描,设置serviceUUIDs可扫描特定外设,CBCentralManagerScanOptionAllowDuplicatesKey设为NO不重复扫描已发现设备,YES是允许
[self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"0xFF66"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @NO}];
扫描到设备会回调centralManager:didDiscoverPeripheral:advertisementData:RSSI:,RSS绝对值越大,表示信号越差,设备离的越远
关闭扫描
[self.manager stopScan];
连接设备
[self.manager connectPeripheral:periphral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}];
// 连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
//发现服务,实现CBPeripheralDelegate代理
[peripheral discoverServices:nil];
}
// 连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {}
// 断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {}
发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
//发现特征
for (CBService *tempService in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:tempService];
}
}
发现特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
for (CBCharacteristic *characteristic in service.characteristics) {
//可读
if (characteristic.properties & CBCharacteristicPropertyRead) {
[peripheral readValueForCharacteristic:characteristic];
}
//可写
if (characteristic.properties & CBCharacteristicPropertyWrite) {
[peripheral writeValue:[@"xxxx" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
//Notify
if (characteristic.properties & CBCharacteristicPropertyNotify) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
//可写不需要回复
if (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
[permissStr appendString:@" WriteWithoutResponse"];
}
//Notify,需要配对
if (characteristic.properties & CBCharacteristicPropertyNotifyEncryptionRequired) {}
}
//读数据的回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
//是否写入成功的回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;