最近由于項目需要, 一直在研究藍牙4.0,在這兒分享給大家, 望共同進步.
一、關(guān)于藍牙開發(fā)的一些重要的理論概念:
1.當前ios中開發(fā)藍牙所運用的系統(tǒng)庫是<CoreBluetooth/CoreBluetooth.h>。
2.藍牙外設(shè)必須為4.0及以上(2.0需要MFI認證),否則無法開發(fā),藍牙4.0設(shè)備因為低耗電,所以也叫做BLE。
3.CoreBluetooth框架的核心其實是兩個東西,peripheral和central, 可以理解成外設(shè)和中心,就是你的蘋果手機就是中心,外部藍牙稱為外設(shè)。
4.服務和特征(service and characteristic):簡而言之,外部藍牙中它有若干個服務service(服務你可以理解為藍牙所擁有的能力),而每個服務service下?lián)碛腥舾蓚€特征characteristic(特征你可以理解為解釋這個服務的屬性)。
5.Descriptor(描述)用來描述characteristic變量的屬性。例如,一個descriptor可以規(guī)定一個可讀的描述,或者一個characteristic變量可接受的范圍,或者一個characteristic變量特定的單位。
6.我們使用的藍牙模塊是在淘寶買的, 大概十多元一個, ios大概每次可以接受90個字節(jié), 安卓大概每次可以接收20個字節(jié), 具體數(shù)字可能會浮動, 應該是與藍牙模塊有關(guān)。
二、藍牙連接的主要步驟
1、創(chuàng)建一個CBCentralManager實例來進行藍牙管理;
2、搜索掃描外圍設(shè)備;
3、連接外圍設(shè)備;
4、獲得外圍設(shè)備的服務;
5、獲得服務的特征;
6、從外圍設(shè)備讀取數(shù)據(jù);
7、給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)。
三、代碼
// 加入權(quán)限訪問, 否則上傳AppStore會因為權(quán)限不足失敗
1. 初始化
#import <CoreBluetooth/CoreBluetooth.h>
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
2. 搜索掃描外圍設(shè)備
/**
* -- 初始化成功自動調(diào)用
* -- 必須實現(xiàn)的代理,用來返回創(chuàng)建的centralManager的狀態(tài)。
* -- 注意:必須確認當前是CBCentralManagerStatePoweredOn狀態(tài)才可以調(diào)用掃描外設(shè)的方法:
scanForPeripheralsWithServices
*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStateUnknown:
NSLog(@">>>CBCentralManagerStateUnknown");
break;
case CBCentralManagerStateResetting:
NSLog(@">>>CBCentralManagerStateResetting");
break;
case CBCentralManagerStateUnsupported:
NSLog(@">>>CBCentralManagerStateUnsupported");
break;
case CBCentralManagerStateUnauthorized:
NSLog(@">>>CBCentralManagerStateUnauthorized");
break;
case CBCentralManagerStatePoweredOff:
NSLog(@">>>CBCentralManagerStatePoweredOff");
break;
case CBCentralManagerStatePoweredOn:
{
NSLog(@">>>CBCentralManagerStatePoweredOn");
// 開始掃描周圍的外設(shè)。
/*
-- 兩個參數(shù)為Nil表示默認掃描所有可見藍牙設(shè)備。
-- 注意:第一個參數(shù)是用來掃描有指定服務的外設(shè)。然后有些外設(shè)的服務是相同的,比如都有FFF5服務,那么都會發(fā)現(xiàn);而有些外設(shè)的服務是不可見的,就會掃描不到設(shè)備。
-- 成功掃描到外設(shè)后調(diào)用didDiscoverPeripheral
*/
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
break;
default:
break;
}
}
#pragma mark 發(fā)現(xiàn)外設(shè)
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"Find device:%@", [peripheral name]);
if (![_deviceDic objectForKey:[peripheral name]]) {
NSLog(@"Find device:%@", [peripheral name]);
if (peripheral!=nil) {
if ([peripheral name]!=nil) {
if ([[peripheral name] hasPrefix:@"根據(jù)設(shè)備名過濾"]) {
[_deviceDic setObject:peripheral forKey:[peripheral name]];
// 停止掃描, 看需求決定要不要加
// [_centralManager stopScan];
// 將設(shè)備信息傳到外面的頁面(VC), 構(gòu)成掃描到的設(shè)備列表
if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) {
[self.delegate dataWithBluetoothDic:_deviceDic];
}
}
}
}
}
}
3.連接外圍設(shè)備
// 連接設(shè)備(.h中聲明出去的接口, 一般在點擊設(shè)備列表連接時調(diào)用)
- (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral
{
[self.centralManager connectPeripheral:peripheral options:nil];
}
#pragma mark 連接外設(shè)--成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
//連接成功后停止掃描,節(jié)省內(nèi)存
[central stopScan];
peripheral.delegate = self;
self.peripheral = peripheral;
//4.掃描外設(shè)的服務
/**
-- 外設(shè)的服務、特征、描述等方法是CBPeripheralDelegate的內(nèi)容,所以要先設(shè)置代理peripheral.delegate = self
-- 參數(shù)表示你關(guān)心的服務的UUID,比如我關(guān)心的是"FFE0",參數(shù)就可以為@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回調(diào)內(nèi)容就只有這兩個UUID的服務,不會有其他多余的內(nèi)容,提高效率。nil表示掃描所有服務
-- 成功發(fā)現(xiàn)服務,回調(diào)didDiscoverServices
*/
[peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服務UUID"]]];
if ([self.delegate respondsToSelector:@selector(didConnectBle)]) {
// 已經(jīng)連接
[self.delegate didConnectBle];
}
}
#pragma mark 連接外設(shè)——失敗
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"%@", error);
}
#pragma mark 取消與外設(shè)的連接回調(diào)
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"%@", peripheral);
}
4. 獲得外圍設(shè)備的服務
#pragma mark 發(fā)現(xiàn)服務回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
//NSLog(@"didDiscoverServices,Error:%@",error);
CBService * __nullable findService = nil;
// 遍歷服務
for (CBService *service in peripheral.services)
{
//NSLog(@"UUID:%@",service.UUID);
if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服務UUID"]])
{
findService = service;
}
}
NSLog(@"Find Service:%@",findService);
if (findService)
[peripheral discoverCharacteristics:NULL forService:findService];
}
5、獲得服務的特征;
#pragma mark 發(fā)現(xiàn)特征回調(diào)
/**
-- 發(fā)現(xiàn)特征后,可以根據(jù)特征的properties進行:讀readValueForCharacteristic、寫writeValue、訂閱通知setNotifyValue、掃描特征的描述discoverDescriptorsForCharacteristic。
**/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) {
/**
-- 讀取成功回調(diào)didUpdateValueForCharacteristic
*/
self.characteristic = characteristic;
// 接收一次(是讀一次信息還是數(shù)據(jù)經(jīng)常變實時接收視情況而定, 再決定使用哪個)
// [peripheral readValueForCharacteristic:characteristic];
// 訂閱, 實時接收
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
// 發(fā)送下行指令(發(fā)送一條)
NSData *data = [@"硬件工程師給我的指令, 發(fā)送給藍牙該指令, 藍牙會給我返回一條數(shù)據(jù)" dataUsingEncoding:NSUTF8StringEncoding];
// 將指令寫入藍牙
[self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
/**
-- 當發(fā)現(xiàn)characteristic有descriptor,回調(diào)didDiscoverDescriptorsForCharacteristic
*/
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}
6.從外圍設(shè)備讀取數(shù)據(jù)
#pragma mark - 獲取值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
// characteristic.value就是藍牙給我們的值(我這里是json格式字符串)
NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
// 將字典傳出去就可以使用了
}
#pragma mark - 中心讀取外設(shè)實時數(shù)據(jù)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (characteristic.isNotifying) {
[peripheral readValueForCharacteristic:characteristic];
} else {
NSLog(@"Notification stopped on %@. Disconnecting", characteristic);
NSLog(@"%@", characteristic);
[self.centralManager cancelPeripheralConnection:peripheral];
}
}
7. 給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)
// 上文中發(fā)現(xiàn)特征之后, 發(fā)送下行指令的時候其實就是向藍牙中寫入數(shù)據(jù)
// 例:
// 發(fā)送檢查藍牙命令
- (void)writeCheckBleWithBle
{
_style = 1;
// 發(fā)送下行指令(發(fā)送一條)
NSData *data = [@"硬件工程師提供給你的指令, 類似于5E16010203...這種很長一串" dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}
#pragma mark 數(shù)據(jù)寫入成功回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@"寫入成功");
if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) {
[self.delegate didWriteSucessWithStyle:_style];
}
}
8. 另外
- (void)scanDevice
{
if (_centralManager == nil) {
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[_deviceDic removeAllObjects];
}
}
#pragma mark 斷開連接
- (void)disConnectPeripheral{
/**
-- 斷開連接后回調(diào)didDisconnectPeripheral
-- 注意斷開后如果要重新掃描這個外設(shè),需要重新調(diào)用[self.centralManager scanForPeripheralsWithServices:nil options:nil];
*/
[self.centralManager cancelPeripheralConnection:self.peripheral];
}
#pragma mark 停止掃描外設(shè)
- (void)stopScanPeripheral{
[self.centralManager stopScan];
}
由于硬件方面剛開始用藍牙2.0跟我對接, 導致程序一直搜索不到設(shè)備.希望小伙伴們注意一下這個問題. 如果有不清楚的地方歡迎留言提問.
覺得有幫助的小伙伴, 可以動動勤勞的小手點個喜歡或者關(guān)注哦~
你們的肯定對我很重要!