現(xiàn)在公司手頭的項(xiàng)目基本上是基于藍(lán)牙的開發(fā)益咬,所以對低功耗藍(lán)牙還是有一定的了解戳护,今天特意把我寫的藍(lán)牙模塊兒整理出來嚎货,供大家參考。
一 : 初識低功耗藍(lán)牙
1, 中心(central)和外設(shè)(peripheral)
剛接觸藍(lán)牙開發(fā)的時候虑凛,找了許多專業(yè)的資料碑宴,講到藍(lán)牙開發(fā)分為兩種模式,中心模式(central),和外設(shè)模式(peripheral)桑谍。云里霧里延柠,以至于搞了兩三天都不知道自己做的東西到底是做為中心還是做為外設(shè)。其實(shí)遠(yuǎn)遠(yuǎn)沒有那么復(fù)雜锣披,一般來講贞间,我們做的需要在軟件內(nèi)連接硬件,通過連接硬件給硬件發(fā)送指令以完成一些動作的藍(lán)牙開發(fā)都是基于中心模式(central)模式的開發(fā)雹仿,也就是說我們開發(fā)的app是中心增热,我們要連接的硬件是外設(shè)。外設(shè)有個特點(diǎn)是它會一直廣播自己胧辽,告訴中心我在這里峻仇,我在這里,快來連我---,這個時候呢邑商,我們的中心就可以通過掃描發(fā)現(xiàn)并且點(diǎn)擊連接了摄咆。
2凡蚜,服務(wù)(service)和特征(character)
我們通過藍(lán)牙連接了硬件,無非就是想通過連接硬件的讀取一些信息吭从,例如硬件通過溫度傳感器獲取的環(huán)境溫度朝蜘,或者濕度傳感器獲取的環(huán)境濕度,又或者是硬件自己的電量等等影锈。或者是給這個設(shè)備發(fā)送一些指令以達(dá)到控制硬件行為的目的蝉绷,例如我們的app連接了空調(diào)鸭廷,想發(fā)送指令過去改變空調(diào)的預(yù)設(shè)溫度,或者發(fā)送指令給共享單車熔吗,讓單車的鎖解開辆床。我們的app中心與設(shè)備之間的通信需要一個橋梁,這個橋梁就是服務(wù)與特征桅狠。一個設(shè)備中可能包含很多服務(wù)讼载,而一個服務(wù)可能又包含幾個特征。比方說中跌,標(biāo)準(zhǔn)藍(lán)牙模塊會有幾個內(nèi)置的服務(wù)與特征如溫度咨堤,心跳,等等漩符。
這些服務(wù)與特征都是硬件工程師可以直接拿來用的一喘。如果有其他特殊的需求,硬件工程師也可以再重新自己定義服務(wù)與特征嗜暴。
3凸克,讀(read) , 寫(write), 訂閱(notify)
我們的目的是讀取設(shè)備中的數(shù)據(jù)(read) , 或者給設(shè)備寫入一定的數(shù)據(jù)(write)。有時候我們還想設(shè)備的數(shù)據(jù)變化的時候不需要我們手動去讀取這個值闷沥,需要設(shè)備自動通知我們它的值變化了萎战,值是多少。把值告訴app舆逃,這個時候就需要訂閱這個特征了(notify)
二 : app做為中心(central)編碼
鑒于大部分需求都是以app做為中心蚂维,硬件設(shè)備做為外設(shè),所以這里只針對中心模式做詳細(xì)介紹路狮。
一般來講鸟雏,都要求我們的app時刻與硬件保持連接,當(dāng)然了览祖,這里的時刻保持連接指的是物理上的孝鹊,我們的app能看到的是當(dāng)前設(shè)備處于連接狀態(tài)。低功耗藍(lán)牙內(nèi)部的實(shí)現(xiàn)絕對不是時刻保持連接展蒂,否則也不能稱之為低功耗藍(lán)牙了又活。Corebluetooth框架內(nèi)部app做為中心的情況下藍(lán)牙管理有一個中心類苔咪,叫CBCentralManager,我們叫它中心管理類柳骄。我們手機(jī)的藍(lán)牙開啟狀態(tài)团赏,手機(jī)與藍(lán)牙設(shè)備等連接,斷開連接耐薯,連接失敗等等都由這個中心管理類來控制舔清。為了這個中心管理類在app的生命周期都存在。我們一般把它進(jìn)行封裝曲初,做成一個單例体谒。這樣app整個生命周期中這個單例時刻存在。下面是封裝的藍(lán)牙單例代碼臼婆。
import Foundation
import CoreBluetooth
//用于看發(fā)送數(shù)據(jù)是否成功!
class LLBlueTooth:NSObject {
//單例對象
internal static let instance = LLBlueTooth()
//中心對象
var central : CBCentralManager?
//中心掃描到的設(shè)備都可以保存起來抒痒,
//掃描到新設(shè)備后可以通過通知的方式發(fā)送出去,連接設(shè)備界面可以接收通知颁褂,實(shí)時刷新設(shè)備列表
var deviceList: NSMutableArray?
// 當(dāng)前連接的設(shè)備
var peripheral:CBPeripheral!
//發(fā)送數(shù)據(jù)特征(連接到設(shè)備之后可以把需要用到的特征保存起來故响,方便使用)
var sendCharacteristic:CBCharacteristic?
override init() {
super.init()
self.central = CBCentralManager.init(delegate:self, queue:nil, options:[CBCentralManagerOptionShowPowerAlertKey:false])
self.deviceList = NSMutableArray()
}
// MARK: 掃描設(shè)備的方法
func scanForPeripheralsWithServices(_ serviceUUIDS:[CBUUID]?, options:[String: AnyObject]?){
self.central?.scanForPeripherals(withServices: serviceUUIDS, options: options)
}
// MARK: 停止掃描
func stopScan() {
self.central?.stopScan()
}
// MARK: 寫數(shù)據(jù)
func writeToPeripheral(_ data: Data) {
peripheral.writeValue(data , for: sendCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
// MARK: 連接某個設(shè)備的方法
/*
* 設(shè)備有幾個狀態(tài)
@available(iOS 7.0, *)
public enum CBPeripheralState : Int {
case disconnected
case connecting
case connected
@available(iOS 9.0, *)
case disconnecting
}
*/
func requestConnectPeripheral(_ model:CBPeripheral) {
if (model.state != CBPeripheralState.connected) {
central?.connect(model , options: nil)
}
}
}
//MARK: -- 中心管理器的代理
extension LLBlueTooth : CBCentralManagerDelegate{
// MARK: 檢查運(yùn)行這個App的設(shè)備是不是支持BLE。
func centralManagerDidUpdateState(_ central: CBCentralManager){
if #available(iOS 10.0, *) {
switch central.state {
case CBManagerState.poweredOn:
print("藍(lán)牙打開")
case CBManagerState.unauthorized:
print("沒有藍(lán)牙功能")
case CBManagerState.poweredOff:
print("藍(lán)牙關(guān)閉")
default:
print("未知狀態(tài)")
}
}
// 手機(jī)藍(lán)牙狀態(tài)發(fā)生變化颁独,可以發(fā)送通知出去彩届。提示用戶
}
// 開始掃描之后會掃描到藍(lán)牙設(shè)備,掃描到之后走到這個代理方法
// MARK: 中心管理器掃描到了設(shè)備
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// 在這個地方可以判讀是不是自己本公司的設(shè)備,這個是根據(jù)設(shè)備的名稱過濾的
guard peripheral.name != nil , peripheral.name!.contains("*****") else {
return
}
// 這里判斷重復(fù)誓酒,加到devielist中惨缆。發(fā)出通知。
}
// MARK: 連接外設(shè)成功丰捷,開始發(fā)現(xiàn)服務(wù)
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){
// 設(shè)置代理
peripheral.delegate = self
// 開始發(fā)現(xiàn)服務(wù)
peripheral.discoverServices(nil)
// 保存當(dāng)前連接設(shè)備
self.peripheral = peripheral
// 這里可以發(fā)通知出去告訴設(shè)備連接界面連接成功
}
// MARK: 連接外設(shè)失敗
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
// 這里可以發(fā)通知出去告訴設(shè)備連接界面連接失敗
}
// MARK: 連接丟失
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
NotificationCenter.default.post(name: Notification.Name(rawValue: "DidDisConnectPeriphernalNotification"), object: nil, userInfo: ["deviceList": self.deviceList as AnyObject])
// 這里可以發(fā)通知出去告訴設(shè)備連接界面連接丟失
}
}
// 外設(shè)的代理
extension LLBlueTooth : CBPeripheralDelegate {
//MARK: - 匹配對應(yīng)服務(wù)UUID
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
if error != nil {
return
}
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service )
}
}
//MARK: - 服務(wù)下的特征
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
if (error != nil){
return
}
for characteristic in service.characteristics! {
switch characteristic.uuid.description {
case "A28DA977":
// 訂閱特征值坯墨,訂閱成功后后續(xù)所有的值變化都會自動通知
peripheral.setNotifyValue(true, for: characteristic)
case "******":
// 讀區(qū)特征值,只能讀到一次
peripheral.readValue(for:characteristic)
default:
print("掃描到其他特征")
}
}
}
//MARK: - 特征的訂閱狀體發(fā)生變化
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?){
guard error == nil else {
return
}
}
// MARK: - 獲取外設(shè)發(fā)來的數(shù)據(jù)
// 注意病往,所有的捣染,不管是 read , notify 的特征的值都是在這里讀取
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)-> (){
if(error != nil){
return
}
switch characteristic.uuid.uuidString {
case "***************":
print("接收到了設(shè)備的溫度特征的值的變化")
default:
print("收到了其他數(shù)據(jù)特征數(shù)據(jù): \(characteristic.uuid.uuidString)")
}
}
//MARK: - 檢測中心向外設(shè)寫數(shù)據(jù)是否成功
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if(error != nil){
print("發(fā)送數(shù)據(jù)失敗!error信息:\(String(describing: error))")
}
}
}
如果有沒有寫清楚的地方,歡迎大家提問和補(bǔ)充停巷。