前言
其實(shí)最近一直在研究iOS藍(lán)牙開(kāi)發(fā)CoreBluetooth
牵囤,網(wǎng)上有關(guān)于iOS藍(lán)牙開(kāi)發(fā)一堆一堆的, 本人也是想寫(xiě)個(gè)學(xué)習(xí)筆記,基本闡述一些藍(lán)牙
的基本概念以及常規(guī)用法。
iOS藍(lán)牙框架介紹 (CoreBluetooth介紹)
在iOS開(kāi)發(fā)中朝捆,實(shí)現(xiàn)藍(lán)牙通信的方法有兩種。分別是GameKit.framework以及CoreBluetooth.framework懒豹,前者在iOS5后基本被淘汰芙盘。
在蘋(píng)果文檔中驯用,寫(xiě)了Communicate with Bluetooth 4.0 low-energy devices,也就是說(shuō)僅支持藍(lán)牙4.0低功耗協(xié)議(BLE)儒老。
對(duì)于iOS10以上的設(shè)備蝴乔,蘋(píng)果注明以下信息:
An iOS app linked on or after iOS 10.0 must include in its Info.plist file the usage description keys for the types of data it needs to access or it will crash. To access Bluetooth peripheral data specifically, it must include NSBluetoothPeripheralUsageDescription.
也就是說(shuō)需要聲明并注冊(cè)藍(lán)牙權(quán)限的使用。
CoreBluetooth協(xié)議
首先提及藍(lán)牙使用驮樊,在此引入兩個(gè)概念:中心設(shè)備和外圍設(shè)備薇正。
中心設(shè)備(客服端):作為中央管理器的設(shè)備,也就是本實(shí)例中的iOS設(shè)備囚衔。
外圍設(shè)備(服務(wù)器):也就是外部設(shè)備挖腰,扮演者產(chǎn)生數(shù)據(jù)的角色。許多傳感器佳魔、藍(lán)牙服務(wù)設(shè)備均是外圍設(shè)備曙聂。本實(shí)例中小米手環(huán)就是外圍設(shè)備晦炊。
外設(shè)鞠鲜、服務(wù)、特征間的關(guān)系
同時(shí)數(shù)據(jù)傳輸還涉及到以下幾個(gè)值:
- UUID:相當(dāng)與使用這個(gè)模塊對(duì)映的應(yīng)用的標(biāo)識(shí)。
- RSSI:信號(hào)強(qiáng)度稳衬,利用此信息可進(jìn)行藍(lán)牙測(cè)距霞捡,后面將進(jìn)行講解。
CoreBluetooth中涉及以下對(duì)象類(lèi):
- CBCentralManager:中心設(shè)備類(lèi)
- CBPeripheral:外圍設(shè)備類(lèi)
- CBCharacteristic:設(shè)備特征類(lèi)
「附上Demo地址」如果喜歡請(qǐng)賞賜一枚'Star'
正文
我們得明確一下很重要的幾個(gè)概念
1.當(dāng)前ios中開(kāi)發(fā)藍(lán)牙所運(yùn)用的系統(tǒng)庫(kù)是
<CoreBluetooth/CoreBluetooth.h>
2.藍(lán)牙外設(shè)必須為4.0及以上薄疚,否則無(wú)法開(kāi)發(fā)碧信,藍(lán)牙4.0設(shè)備因?yàn)榈秃碾姡砸步凶鯞LE街夭。
3.CoreBluetooth
框架的核心其實(shí)是兩個(gè)東西砰碴,peripheral
和central
, 可以理解成外設(shè)和中心,就是你的蘋(píng)果手機(jī)就是中心板丽,外部藍(lán)牙稱(chēng)為外設(shè)呈枉。
4.服務(wù)和特征(service and characteristic)
:簡(jiǎn)而言之,外部藍(lán)牙中它有若干個(gè)服務(wù)service
(服務(wù)你可以理解為藍(lán)牙所擁有的能力)埃碱,而每個(gè)服務(wù)service
下?lián)碛腥舾蓚€(gè)特征characteristic
(特征你可以理解為解釋這個(gè)服務(wù)的屬性)猖辫。
5.Descriptor
(描述)用來(lái)描述characteristic
變量的屬性。例如砚殿,一個(gè)descriptor
可以規(guī)定一個(gè)可讀的描述啃憎,或者一個(gè)characteristic
變量可接受的范圍,或者一個(gè)characteristic
變量特定的單位似炎。
6.跟硬件親測(cè)荧飞,iOS藍(lán)牙
每次最多接收155字節(jié)的數(shù)據(jù)凡人,安卓5.0以下最大接收20字節(jié),5.0以上可以更改最大接收量叹阔,能達(dá)到500多字節(jié)挠轴。
通過(guò)以上關(guān)鍵信息的解釋?zhuān)缓罂匆幌滤{(lán)牙的開(kāi)發(fā)流程:
建立中心管理者
- 掃描外設(shè)(discover)
- 連接外設(shè)(connect)
- 掃描外設(shè)中的服務(wù)和特征(discover)
- 4.1 獲取外設(shè)的services
- 4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值,
- 獲取Characteristics的Descriptor和Descriptor的值
- 訂閱Characteristic的通知
- 與外設(shè)做數(shù)據(jù)交互(explore and interact)
- 斷開(kāi)連接(disconnect)
具體實(shí)例:
1.創(chuàng)建一個(gè)中心管理者
/** 從這個(gè)代理方法中你可以看到所有的狀態(tài)耳幢,其實(shí)我們需要的只有on和off連個(gè)狀態(tài)*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStateUnknown:
NSLog(@"__CBManagerStateUnknown__");
break;
case CBManagerStateResetting:
NSLog(@"__CBManagerStateResetting__");
break;
case CBManagerStateUnsupported:
NSLog(@"__CBManagerStateUnsupported__");
break;
case CBManagerStateUnauthorized:
NSLog(@"__CBManagerStateUnauthorized__");
break;
case CBManagerStatePoweredOff:
NSLog(@"__CBManagerStatePoweredOff__");
break;
case CBManagerStatePoweredOn:
NSLog(@"__CBManagerStatePoweredOn__");
break;
default:
break;
}
}
當(dāng)發(fā)現(xiàn)藍(lán)牙狀態(tài)是開(kāi)啟狀態(tài)岸晦,你就可以利用中央設(shè)備進(jìn)行掃描外設(shè),如果為關(guān)閉狀態(tài)睛藻,系統(tǒng)會(huì)自動(dòng)彈出讓用戶(hù)去設(shè)置藍(lán)牙启上,這個(gè)不需要我們開(kāi)發(fā)者關(guān)心
2.利用中心去掃描外設(shè)
/** 兩個(gè)參數(shù)為nil, 默認(rèn)掃描所有的外設(shè),可以設(shè)置一些服務(wù)店印,進(jìn)行過(guò)濾搜索*/
[self.bluetoothManager scanForPeripheralsWithServices:nil
options:nil];
2.1當(dāng)掃描到外設(shè)冈在,觸發(fā)以下代理方法
在這里需要說(shuō)明的是,
一.當(dāng)掃描到外設(shè)按摘,我們可以讀到相應(yīng)外設(shè)廣播信息包券,RSSI信號(hào)強(qiáng)度(可以利用RSSI計(jì)算中心和外設(shè)的距離)。
二.我們可以根據(jù)一定的規(guī)則進(jìn)行連接炫贤,一般是默認(rèn)名字或者名字和信號(hào)強(qiáng)度的規(guī)則來(lái)連接溅固。
三.像我現(xiàn)在做的無(wú)鑰匙啟動(dòng)車(chē)輛鎖定車(chē)輛,就需要加密通訊兰珍,不能誰(shuí)來(lái)連接都可以操作車(chē)輛侍郭,需要和外設(shè)進(jìn)行加密通訊,但是一切的通過(guò)算法的校驗(yàn)都是在和外設(shè)連接上的基礎(chǔ)上進(jìn)行掠河,例如連接上了亮元,你發(fā)送一種和硬件約定好的算法數(shù)據(jù),硬件接收到校驗(yàn)通過(guò)了就正常操作唠摹,無(wú)法通過(guò)則由硬件(外設(shè))主動(dòng)斷開(kāi)爆捞。
/** 這里默認(rèn)掃到MI,主動(dòng)連接跃闹,當(dāng)然也可以手動(dòng)觸發(fā)連接*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"掃描連接外設(shè):%@ %@", peripheral.name, RSSI);
if ([peripheral.name hasSuffix:@"MI"]) {
/** 保存外設(shè)嵌削,并停止掃描,達(dá)到節(jié)電效果*/
self.pripheral = peripheral;
[central stopScan];
/** 進(jìn)行連接*/
[central connectPeripheral:peripheral options:nil];
}
}
3.當(dāng)連接到外設(shè)望艺,會(huì)調(diào)用以下代理方法
這里需要說(shuō)明的是
當(dāng)成功連接到外設(shè)苛秕,需要設(shè)置外設(shè)的代理,為了掃描服務(wù)調(diào)用相應(yīng)代理方法
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"連接外設(shè)成功找默!%@", peripheral.name);
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
/** 連接外設(shè)失敗*/
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"連接到外設(shè) 失斖Ы佟!名字:%@ 錯(cuò)誤信息:%@", [peripheral name], [error localizedDescription]);
}
4.掃描外設(shè)中的服務(wù)和特征
/** 掃描到服務(wù)*/
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error)
{
NSLog(@"掃描外設(shè)服務(wù)出錯(cuò):%@-> %@", peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"掃描到外設(shè)服務(wù):%@ -> %@",peripheral.name,peripheral.services);
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
NSLog(@"開(kāi)始掃描外設(shè)服務(wù)的特征 %@...",peripheral.name);
}
/** 掃描到特征*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSLog(@"掃描外設(shè)的特征失敵图ぁ店煞!%@->%@-> %@", peripheral.name, service.UUID, [error localizedDescription]);
return;
}
NSLog(@"掃描到外設(shè)服務(wù)特征有:%@->%@->%@", peripheral.name, service.UUID, service.characteristics);
//獲取Characteristic的值
for (CBCharacteristic *characteristic in service.characteristics){
//這里外設(shè)需要訂閱特征的通知蟹演,否則無(wú)法收到外設(shè)發(fā)送過(guò)來(lái)的數(shù)據(jù)
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
//需要說(shuō)明的是UUID是硬件定義好給你,如果硬件也是個(gè)新手顷蟀,那你可以先打印出所有的UUID, 找出有用的
//步數(shù)
if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"])
{
[peripheral readValueForCharacteristic:characteristic];
}
//電池電量
else if ([characteristic.UUID.UUIDString isEqualToString:@"FF0C"])
{
[peripheral readValueForCharacteristic:characteristic];
}
else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"])
{
//震動(dòng)
self.characteristic = characteristic;
}
}
}
/** 掃描到具體的值->通訊主要的獲取數(shù)據(jù)的方法*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
NSLog(@"掃描外設(shè)的特征失斁魄搿!%@-> %@", peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"%@ %@", characteristic.UUID.UUIDString, characteristic.value);
if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"]) {
Byte *steBytes = (Byte *)characteristic.value.bytes;
int steps = bytesValueToInt(steBytes);
NSLog(@"%d", steps);
} else if ([characteristic.UUID.UUIDString isEqualToString: @"FF0C"])
{
Byte *bufferBytes = (Byte *)characteristic.value.bytes;
int buterys = bytesValueToInt(bufferBytes)&0xff;
NSLog(@"電池:%d%%",buterys);
} else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"])
{
Byte *infoByts = (Byte *)characteristic.value.bytes;
NSLog(@"%s", infoByts);
//這里解析infoByts得到設(shè)備信息
}
}
5.與外設(shè)做數(shù)據(jù)交互
需要說(shuō)明的是蘋(píng)果官方提供發(fā)送數(shù)據(jù)的方法很簡(jiǎn)單,只需要調(diào)用下面的方法
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type
{
/**
我們只需要在搜索每個(gè)服務(wù)的特征鸣个,記錄這個(gè)特征羞反,然后向這個(gè)特征發(fā)送數(shù)據(jù)就可以了。
*/
}
6.斷開(kāi)連接
調(diào)用以下代碼,需要說(shuō)明的是中心斷開(kāi)與外設(shè)的連接囤萤。
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral
{
/*
以上呢是整個(gè)藍(lán)牙的開(kāi)發(fā)過(guò)程昼窗,系統(tǒng)提供的框架api就這么多;
*/
}
「附上Demo地址」如果喜歡請(qǐng)賞賜一枚'Star'
結(jié)語(yǔ): 目前先總結(jié)到這里涛舍。
擴(kuò)展: 正在研究iOS應(yīng)用與Siri交互
例如:對(duì)著Siri說(shuō) “支付寶付款碼” 會(huì)自動(dòng)訪(fǎng)問(wèn)支付寶并且彈出付款碼澄惊。 或者 對(duì)著Siri說(shuō)“給xxx發(fā)送一條微信” 會(huì)彈出自定義UI頁(yè)面 然后可以直接操作, 不需要在打開(kāi)微信 等相關(guān)功能
在之后會(huì)更新學(xué)習(xí)成果富雅。