| 藍(lán)牙開發(fā) |
|---|
| 中心設(shè)備開發(fā) |
| 外設(shè)開發(fā) |
概念理解
中心設(shè)備: 主動(dòng)連接其他藍(lán)牙設(shè)備的一方,可以通過(guò)藍(lán)牙指令操作藍(lán)牙設(shè)備或讀取藍(lán)牙設(shè)備數(shù)據(jù)。如手機(jī)藍(lán)牙耳機(jī),手機(jī)就是中心設(shè)備。
外設(shè): 提供藍(lán)牙功能的設(shè)備,可以被中心設(shè)備連接。如手機(jī)藍(lán)牙耳機(jī),耳機(jī)就是外設(shè)。
服務(wù): 外設(shè)提供的功能模塊。一個(gè)外設(shè)可以有多個(gè)服務(wù),一個(gè)服務(wù)可以有一個(gè)或多個(gè)特征值。
特征值: 中心設(shè)備通過(guò)修改特征值或讀取特征值來(lái)使用外設(shè)的提供的服務(wù)。如手機(jī)可以通過(guò)修改音量對(duì)應(yīng)的特征值的大小來(lái)實(shí)現(xiàn)控制耳機(jī)的音量。
服務(wù)的UUID: 服務(wù)的UUID是服務(wù)的標(biāo)識(shí),外設(shè)上的每個(gè)服務(wù)都有一個(gè)UUID,但不同的外設(shè)可能會(huì)有相同uuid的服務(wù)
特征的UUID: 特征的UUID是特征的標(biāo)識(shí),外設(shè)上的各服務(wù)的每個(gè)特征都有一個(gè)UUID,但不同的外設(shè)可能會(huì)有相同uuid的特征
中心設(shè)備的開發(fā)流程

image
中心設(shè)備開發(fā) API
引入藍(lán)牙框架
#import <CoreBluetooth/CoreBluetooth.h>
創(chuàng)建中心設(shè)備
//創(chuàng)建中心設(shè)備管理者
self.centralManager = [[CBCentralManager alloc] initWithDelegate:_sharedInstance queue:nil options:nil];
///藍(lán)牙狀態(tài)變化回調(diào)。
///回調(diào)時(shí)機(jī):
///1、創(chuàng)建中心設(shè)備管理者后,系統(tǒng)會(huì)請(qǐng)求獲取藍(lán)牙權(quán)限,用戶同意或拒絕后,回調(diào)該方法
///2、用戶開啟或關(guān)閉藍(lán)牙后回調(diào)該方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStateUnknown:
case CBManagerStateUnsupported:
NSLog(@"模擬器不支持藍(lán)牙調(diào)試");
case CBManagerStateUnauthorized:
NSLog(@"未開啟藍(lán)牙權(quán)限");
break;
case CBManagerStatePoweredOff:
NSLog(@"藍(lán)牙處于關(guān)閉狀態(tài)");
break;
case CBManagerStateResetting:
NSLog(@"藍(lán)牙處于重置狀態(tài)");
break;
case CBManagerStatePoweredOn:
NSLog(@"藍(lán)牙已開啟");
//掃描外設(shè)
[self scanForPeripherals];
break;
}
//轉(zhuǎn)為了block回調(diào)
if (self.centralManagerStateDidUpdate) {
self.centralManagerStateDidUpdate(central.state);
}
}
掃描外設(shè)
外設(shè)的屬性:名字、信號(hào)強(qiáng)度、狀態(tài)、服務(wù)、標(biāo)志符。
//掃描外設(shè)
//services為nil,會(huì)掃描所有的外設(shè)
//serviceUUIDs 是[CBUUID UUIDWithString:@"uuid1"]的集合
//已連接狀態(tài)的外設(shè)是不會(huì)被掃描到的
//因?yàn)橛锌赡懿煌庠O(shè)的服務(wù)的UUID相同,所以是無(wú)法指定掃描某個(gè)外設(shè)的
[self.centralManager scanForPeripheralsWithServices:serviceUUIDs options:nil];
///掃描到外設(shè)回調(diào)
///外設(shè)減少不會(huì)回調(diào)該方法,所以掃描到的藍(lán)牙列表可能過(guò)段時(shí)間后有些外設(shè)是連不上的??梢哉{(diào)用掃描外設(shè)方法重新掃描。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
NSString *name = peripheral.name;
NSLog(@"發(fā)現(xiàn)了外設(shè):%@", name);
/** 添加外設(shè)到外設(shè)列表中,根據(jù)外設(shè)的identifier.UUIDString避免重復(fù)添加
為什么可以作為唯一標(biāo)識(shí)符:
1、不同的中心設(shè)備掃描同一臺(tái)外設(shè)identifier.UUIDString是不一樣的
2、同一臺(tái)中心設(shè)備每次掃描同一臺(tái)外設(shè),identifier.UUIDString是不變的 */
if (name.length > 0) {
BOOL exist = NO;
for (CBPeripheral *p in self.peripheralList) {
if ([p.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
exist = YES;
break;
}
}
if (!exist) {
[self.peripheralList addObject:peripheral];
}
}
//轉(zhuǎn)為了block回調(diào)
self.peripheralsDidUpdate(self.peripheralList);
}
連接外設(shè)
//peripheral是從peripheralList中找到的自己需要的一臺(tái)外設(shè)
[self.centralManager connectPeripheral:peripheral options:nil];
///外設(shè)連接失敗回調(diào)
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
//轉(zhuǎn)為了block回調(diào)
self.connectStateDidUpdate(peripheral.state);
NSLog(@"外設(shè)連接失敗");
}
//外設(shè)與中心設(shè)備連接成功回調(diào)
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
//保存當(dāng)前連接的外設(shè)
self.curPeripheral = peripheral;
//停止掃描
[self.centralManager stopScan];
//設(shè)置代理
peripheral.delegate = self;
//根據(jù)服務(wù)的UUID來(lái)尋找服務(wù), 傳nil則尋找該外設(shè)的所有服務(wù)
[peripheral discoverServices:serviceUUIDs];
//轉(zhuǎn)為了block回調(diào)
self.connectStateDidUpdate(peripheral.state);
NSLog(@"外設(shè)連接成功");
}
掃描服務(wù)
//根據(jù)服務(wù)的UUID來(lái)尋找服務(wù), 傳nil則尋找該外設(shè)的所有服務(wù)
[peripheral discoverServices:serviceUUIDs];
///外設(shè)發(fā)現(xiàn)服務(wù)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) {
//錯(cuò)誤處理
return;
}
for (CBService *service in peripheral.services) {
NSLog(@"發(fā)現(xiàn)的服務(wù):%@", service.UUID);
//尋找該服務(wù)的特征,characteristicUUIDs為nil,找的是該服務(wù)的所有的特征
[peripheral discoverCharacteristics:characteristicUUIDs forService:service];
}
}
掃描特征
//尋找該服務(wù)的特征,characteristicUUIDs為nil,找的是該服務(wù)的所有的特征
[peripheral discoverCharacteristics:characteristicUUIDs forService:service];
///外設(shè)發(fā)現(xiàn)特征回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"發(fā)現(xiàn)的特征:%@", characteristic.UUID.UUIDString);
/** 訂閱支持通知的特征 */
if (characteristic.properties & CBCharacteristicPropertyNotify) {
[self.curPeripheral setNotifyValue:YES forCharacteristic:characteristic];
}
//記錄下所有的特征
[self.characteristics addObject:characteristic];
}
}
///訂閱結(jié)果回調(diào)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"訂閱失敗");
}
if (characteristic.isNotifying) {
NSLog(@"訂閱成功");
} else {
NSLog(@"取消訂閱");
}
}
使用服務(wù)---修改特征值
//修改UUID為characteristicUUIDStr的特征的值,自定義方法
- (void)writeData:(NSData *)data characteristicUUIDStr:(NSString *)characteristicUUIDStr callback:(void (^)(BOOL))callback {
//記錄寫入回調(diào)block
self.writeCallback = callback;
///判斷該特征是否在掃描到的特征中存在
BOOL existUUID = NO;
for (CBCharacteristic *character in self.characteristics) {
/** 找到對(duì)應(yīng)UUID的特征進(jìn)行寫操作 */
if ([character.UUID.UUIDString isEqualToString:characteristicUUIDStr]) {
existUUID = YES;
/** 如果該特征有寫權(quán)限,進(jìn)行寫權(quán)限 */
if (character.properties & CBCharacteristicPropertyWrite) {
[self.curPeripheral writeValue:data forCharacteristic:character type:CBCharacteristicWriteWithResponse];
}else if (character.properties & CBCharacteristicPropertyWriteWithoutResponse) {
[self.curPeripheral writeValue:data forCharacteristic:character type:CBCharacteristicWriteWithoutResponse];
}
/** 如果該特征沒(méi)有寫權(quán)限,回調(diào)寫入失敗 */
else {
self.writeCallback(NO);
}
}
}
/** 如果沒(méi)有找到該UUID對(duì)應(yīng)的特征,回調(diào)寫入失敗 */
if (!existUUID) {
NSLog(@"未找到UUID為%@的特征", characteristicUUIDStr);
self.writeCallback(NO);
}
}
//寫入特征值結(jié)果回調(diào)。如果特征支持寫入回調(diào)才會(huì)調(diào)用
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
if (error) {
self.writeCallback(NO);
return;
}
NSLog(@"寫入成功");
//回調(diào)block
self.writeCallback(YES);
}
使用服務(wù)---讀取特征值&訂閱特征通知
///主動(dòng)讀取某特征的值,自定義方法
- (void)readValueForCharacteristicUUIDStr:(NSString *)UUIDStr {
///從特征列表中找到特征,并讀取
for (CBCharacteristic *character in self.characteristics) {
if ([character.UUID.UUIDString isEqualToString:UUIDStr]) {
//判斷該特征是否可讀
if (character.properties & CBCharacteristicPropertyRead) {
//讀取該特征值
[self.curPeripheral readValueForCharacteristic:character];
}
}
}
}
//訂閱某特征
[self.curPeripheral setNotifyValue:YES forCharacteristic:characteristic];
//1、讀取到特征值回調(diào)
//2、收到特征通知也會(huì)回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// 拿到外設(shè)發(fā)送過(guò)來(lái)的 NSData 數(shù)據(jù)
NSData *data = characteristic.value;
//轉(zhuǎn)為了block回調(diào)
if (self.valueForCharacteristicDidUpdate) {
self.valueForCharacteristicDidUpdate(characteristic.UUID.UUIDString, data);
}
NSLog(@"接收到外設(shè)數(shù)據(jù)回調(diào):%@", data);
}