开头
Bluetooth蓝牙
实际用处
- 与蓝牙设备进行通信
- 物联网设备的开发
- 最近正好在整蓝牙的项目,就顺便把iOS自带的框架学了下;项目中的框架用的是BabyBluetooth
关键词铺垫
- 中心设备,就是用来扫描周围蓝牙硬件的设备,比如手机的蓝牙来扫描并连接智能手环,这时候手机就是中心设备
- 外设,被扫描的设备,例如上面的智能手环
- 广播,外设不停地向外面发送蓝牙信号,让中心设备可以扫描到
- 服务,services,外设广播和运行的时候会有服务,可以理解成一个功能模块,中心设备可以读取服务。外设可以有多个服务
- 特征,在服务中的一个单位,一个服务可以有多个特征,特征会有一个value,一般读写的数据就是这个value
- UUID,区分不同的服务和特征,可以理解为服务和特征的身份证,用UUID来挑选需要的服务和特征。
- 订阅,类似监听
info.plist配置
<key>NSBluetoothAlwaysUsageDescription</key>
<string></string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>YES</string>
外设信息
<CBPeripheral: 0x281d34280, //对象值
identifier = E6F8E0AB-6B1E-200F-056C-05750B1A0D7A,
name = CH9140BLE2U,
state = connecting>
kCBAdvDataIsConnectable = 1
kCBAdvDataLocalName = CH9140BLE2U
kCBAdvDataManufacturerData = {length = 8, bytes = 0x0cc6209000000000}
kCBAdvDataTimestamp = "644931976.188664"
kCBAdvDataTxPowerLevel = 0
-56
<CBService: 0x2838409c0, isPrimary = YES, UUID = FFF0>
//第一个特征值
<CBCharacteristic: 0x28118c240, UUID = FFF1, properties = 0x12, value = (null), notifying = NO>
//第二个特征值
<CBCharacteristic: 0x28118c2a0, UUID = FFF2, properties = 0xC, value = (null), notifying = NO>
//第三个特征值
<CBCharacteristic: 0x28118c300, UUID = FFF3, properties = 0x1E, value = (null), notifying = NO>
配置中心设备
- 目的,让设备又能去扫描外面的蓝牙设备
- 对于中心设备来说,关键任务就是
扫描蓝牙、连接蓝牙、获取蓝牙的写、监听端口,而这个端口指的就是特征值,一般需要嵌入式去规定
- 而下面整套流程走下来,就是这个顺序,
扫描蓝牙->连接蓝牙->扫描蓝牙的服务->扫描蓝牙的特征值->处理特征值的需求->对蓝牙订阅或者是写操作,外加一些错误处理、数据处理,及蓝牙信息筛选等过滤操作,就可以连接蓝牙了
- 整体操作思路比较简单,顺一遍就有感觉了
- 头文件
#import <CoreBluetooth/CoreBluetooth.h>
初始
#define SERVICE_UUID @"FFF0"
#define CHARACTERISTIC_NOTIFY_UUID @"FFF1"
#define CHARACTERISTIC_WRITE_UUID @"FFF2"
#define CHARACTERISTIC_READ_UUID @"FFF3"
@interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic;
@property (nonatomic, strong) CBCharacteristic *notifyCharacteristic;
@end
- (void)viewDidLoad {
[super viewDidLoad]
//初始化后,自动扫描
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]
UIButton *postBtn = [UIButton buttonWithType:UIButtonTypeContactAdd]
postBtn.frame = CGRectMake(100, 100, 100, 100)
[postBtn addTarget:self action:@selector(didClickPost) forControlEvents:UIControlEventTouchUpInside]
[self.view addSubview:postBtn]
//主动读
UIButton *readBtn = [UIButton buttonWithType:UIButtonTypeContactAdd]
readBtn.frame = CGRectMake(200, 200, 100, 100)
readBtn.tintColor = [UIColor orangeColor]
[readBtn addTarget:self action:@selector(didClickGet) forControlEvents:UIControlEventTouchUpInside]
[self.view addSubview:readBtn]
}
扫描状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
if (central.state == CBManagerStatePoweredOn) {
NSLog(@"蓝牙可用 - %@", central);
[central scanForPeripheralsWithServices:@[] options:nil];
}
}
发现外设
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
self.peripheral = peripheral;
NSData *data = advertisementData[@"kCBAdvDataManufacturerData"];
if([data isEqualToData:[self convertHexStrToData:@"0cc6209000000000"]]) {
[central connectPeripheral:peripheral options:nil];
NSLog(@"%@", peripheral);
NSLog(@"%@", advertisementData);
NSLog(@"%@", RSSI);
}
}
连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
[central stopScan]
//设置代理
peripheral.delegate = self
//无差别寻找
[peripheral discoverServices:@[]]
//根据UUID寻找服务
// [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]]
NSLog(@"connecct success")
}
连接失败的回调
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"连接失败");
}
断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"断开连接");
[central connectPeripheral:peripheral options:nil];
}
发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"service : %@", service)
}
CBService *service = peripheral.services.lastObject
//寻找特征 无差别寻找
[peripheral discoverCharacteristics:@[] forService:service]
//寻找指定的特征 [CBUUID UUIDWithString:CHARACTERISTIC_NOFITY_UUID]
// [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service]
}
发现特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
// 遍历出所需要的特征
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"所有特征:%@", characteristic)
// 从外设开发人员那里拿到不同特征的UUID,不同特征做不同事情,比如有读取数据的特征,也有写入数据的特征
if ([characteristic.UUID.UUIDString isEqualToString:CHARACTERISTIC_WRITE_UUID]) {
self.writeCharacteristic = characteristic
}
else if([characteristic.UUID.UUIDString isEqualToString:CHARACTERISTIC_NOTIFY_UUID]) {
self.notifyCharacteristic = characteristic
// 订阅通知 - 监听这个端口
[peripheral setNotifyValue:YES forCharacteristic:self.notifyCharacteristic]
}
else if([characteristic.UUID.UUIDString isEqualToString:CHARACTERISTIC_READ_UUID]) {
self.readCharacteristic = characteristic
}
}
}
订阅状态的改变
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"订阅失败");
NSLog(@"%@",error);
}
if (characteristic.isNotifying) {
NSLog(@"订阅成功");
} else {
NSLog(@"取消订阅");
}
}
接收到数据回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSData *data = characteristic.value;
NSLog(@"%@", data);
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
写入数据回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
NSLog(@"写入成功");
}
向外设发送信息
- (void)didClickPost {
NSString *str = @"i am centralManager"
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]
[self.peripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse]
}
主动向外设读取数据
- (void)didClickGet {
[self.peripheral readValueForCharacteristic:self.readCharacteristic];
}
16进制转NSData
- (NSData *)convertHexStrToData:(NSString *)str
{
if (!str || [str length] == 0) {
return nil
}
NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:20]
NSRange range
if ([str length] % 2 == 0) {
range = NSMakeRange(0, 2)
} else {
range = NSMakeRange(0, 1)
}
for (NSInteger i = range.location
unsigned int anInt
NSString *hexCharStr = [str substringWithRange:range]
NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr]
[scanner scanHexInt:&anInt]
NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1]
[hexData appendData:entity]
range.location += range.length
range.length = 2
}
return hexData
}
配置蓝牙设备
- 目的,让中心设备能扫描到蓝牙设备
- 思路围绕着一个蓝牙设备需要什么,需要
服务、特征值、广播信息等,然后确定端口,按照步骤写下来就好
- 在写的时候,特征值要与中心设备相对应,要站在一个角度去写特征值,比如,中心设备的写也就是蓝牙中的被写
初始配置
#define SERVICE_UUID @"FFF0"
#define CHARACTERISTIC_NOTIFY_UUID @"FFF1"
#define CHARACTERISTIC_WRITE_UUID @"FFF2"
#define CHARACTERISTIC_READ_UUID @"FFF3"
@interface ViewController () <CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBMutableCharacteristic *notifyCharacteristic;
@property (nonatomic, strong) CBMutableCharacteristic *writeCharacteristic;
@property (nonatomic, strong) CBMutableCharacteristic *readCharacteristic;
@end
- (void)viewDidLoad {
[super viewDidLoad]
self.view.backgroundColor = [UIColor redColor]
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]
UIButton *postBtn = [UIButton buttonWithType:UIButtonTypeContactAdd]
postBtn.frame = CGRectMake(100, 100, 100, 100)
[postBtn addTarget:self action:@selector(didClickPost) forControlEvents:UIControlEventTouchUpInside]
[self.view addSubview:postBtn]
}
更新蓝牙状态
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBManagerStatePoweredOn) {
//创建 服务 和 特征
[self setupServiceAndCharacteristics];
//根据服务的UUID开始广播
// [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:SERVICE_UUID]]}];
}
}
设置服务和特征值
- (void)setupServiceAndCharacteristics {
//创建服务
CBUUID *serviceID = [CBUUID UUIDWithString:SERVICE_UUID]
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceID primary:YES]
//创建服务中的特征
CBUUID *characteristicNotifyID = [CBUUID UUIDWithString:CHARACTERISTIC_NOTIFY_UUID]
CBUUID *characteristicWriteID = [CBUUID UUIDWithString:CHARACTERISTIC_WRITE_UUID]
CBUUID *characteristicReadID = [CBUUID UUIDWithString:CHARACTERISTIC_READ_UUID]
//properties 服务中包括的功能,读、写、监听
CBMutableCharacteristic *notifyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicNotifyID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]
CBMutableCharacteristic *writeCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicWriteID properties:CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicReadID properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]
//把特征值添加到服务
service.characteristics = @[notifyCharacteristic, writeCharacteristic, readCharacteristic]
//把服务加入管理
[self.peripheralManager addService:service]
//手动给中心设备发送数据
self.notifyCharacteristic = notifyCharacteristic
self.writeCharacteristic = writeCharacteristic
self.readCharacteristic = readCharacteristic
}
添加服务后的回调函数
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]],CBAdvertisementDataLocalNameKey:@"宝找到你了"}];
}
发送广播的回调函数
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
NSLog(@"device is in advertising.");
}
外设被读取的时候的回调
- 当中心设备向蓝牙读数据时,这里会被事先准备好的数据发给中心设备
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
//请求中的数据,把内容发送给中心设备
NSString *str = @"i am periheral"
request.value = [str dataUsingEncoding:NSUTF8StringEncoding]
//成功响应
[peripheral respondToRequest:request withResult:CBATTErrorSuccess]
}
写入数据的回调
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
CBATTRequest *request = requests.lastObject;
NSLog(@"%@", [[NSString alloc] initWithData:request.value encoding:NSUTF8StringEncoding]);
}
主动给中心设备发送数据
- (void)didClickPost {
NSString *str = @"i am notifyCharacteristic";
bool sendSuccess = [self.peripheralManager updateValue:[str dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.notifyCharacteristic onSubscribedCentrals:nil];
if (sendSuccess) {
NSLog(@"数据发送成功");
}else {
NSLog(@"数据发送失败");
}
}
订阅成功的回调 - 订阅类比监听
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"%s",__FUNCTION__);
}
取消订阅的回调
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"%s",__FUNCTION__);
}
总结
- 这篇比较偏使用攻略,后面有时间,会把里面的关键函数的参数含义整理一下