蓝牙通讯,今天我们将低功耗的BLE通讯,也就是使用特征值的通讯,该通讯的特点是低功耗,但是其实经常不小心就没电了。
主要方法如下
// 初始化CBCentralManagerDelegate
mCentralManager = CBCentralManager(delegate: self, queue: .main)
//扫描搜索蓝牙
mCentralManager?.scanForPeripherals(withServices: nil, options: nil)
// 停止扫描
mCentralManager?.stopScan()
当调用scan搜索蓝牙的时候,如果发现到设备,sdk会回调如下函数
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
}
// 这个是持续回调,并且会有重复蓝牙回调,所以在这里可以获取到蓝牙名字,但是却获取不到蓝牙的mac地址,需要进一步连接获取,正常情况下,这里需要做一个去重处理,避免重复连接,ios的蓝牙连接可以同时连接多个设备
连接设备,由上面方面或者到CBPeripheral中的设备,可以对这个设备进行连接,
self.mCentralManager?.connect(peripheral, options: nil)
紧接着就等CBCentralManagerDelegate的回调
public protocol CBCentralManagerDelegate : NSObjectProtocol {
/**
* @method centralManagerDidUpdateState:
*
* @param central The central manager whose state has changed.
*
* @discussion Invoked whenever the central manager's state has been updated. Commands should only be issued when the state is
* <code>CBCentralManagerStatePoweredOn</code>. A state below <code>CBCentralManagerStatePoweredOn</code>
* implies that scanning has stopped and any connected peripherals have been disconnected. If the state moves below
* <code>CBCentralManagerStatePoweredOff</code>, all <code>CBPeripheral</code> objects obtained from this central
* manager become invalid and must be retrieved or discovered again.
*
* @see state
*
*/
@available(iOS 5.0, *)
func centralManagerDidUpdateState(_ central: CBCentralManager)
/**
* @method centralManager:willRestoreState:
*
* @param central The central manager providing this information.
* @param dict A dictionary containing information about <i>central</i> that was preserved by the system at the time the app was terminated.
*
* @discussion For apps that opt-in to state preservation and restoration, this is the first method invoked when your app is relaunched into
* the background to complete some Bluetooth-related task. Use this method to synchronize your app's state with the state of the
* Bluetooth system.
*
* @seealso CBCentralManagerRestoredStatePeripheralsKey;
* @seealso CBCentralManagerRestoredStateScanServicesKey;
* @seealso CBCentralManagerRestoredStateScanOptionsKey;
*
*/
@available(iOS 5.0, *)
optional func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any])
/**
* @method centralManager:didDiscoverPeripheral:advertisementData:RSSI:
*
* @param central The central manager providing this update.
* @param peripheral A <code>CBPeripheral</code> object.
* @param advertisementData A dictionary containing any advertisement and scan response data.
* @param RSSI The current RSSI of <i>peripheral</i>, in dBm. A value of <code>127</code> is reserved and indicates the RSSI
* was not available.
*
* @discussion This method is invoked while scanning, upon the discovery of <i>peripheral</i> by <i>central</i>. A discovered peripheral must
* be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For
* a list of <i>advertisementData</i> keys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.
*
* @seealso CBAdvertisementData.h
*
*/
@available(iOS 5.0, *)
optional func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
/**
* @method centralManager:didConnectPeripheral:
*
* @param central The central manager providing this information.
* @param peripheral The <code>CBPeripheral</code> that has connected.
*
* @discussion This method is invoked when a connection initiated by {@link connectPeripheral:options:} has succeeded.
*
*/
@available(iOS 5.0, *)
optional func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
/**
* @method centralManager:didFailToConnectPeripheral:error:
*
* @param central The central manager providing this information.
* @param peripheral The <code>CBPeripheral</code> that has failed to connect.
* @param error The cause of the failure.
*
* @discussion This method is invoked when a connection initiated by {@link connectPeripheral:options:} has failed to complete. As connection attempts do not
* timeout, the failure of a connection is atypical and usually indicative of a transient issue.
*
*/
@available(iOS 5.0, *)
optional func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
/**
* @method centralManager:didDisconnectPeripheral:error:
*
* @param central The central manager providing this information.
* @param peripheral The <code>CBPeripheral</code> that has disconnected.
* @param error If an error occurred, the cause of the failure.
*
* @discussion This method is invoked upon the disconnection of a peripheral that was connected by {@link connectPeripheral:options:}. If the disconnection
* was not initiated by {@link cancelPeripheralConnection}, the cause will be detailed in the <i>error</i> parameter. Once this method has been
* called, no more methods will be invoked on <i>peripheral</i>'s <code>CBPeripheralDelegate</code>.
*
*/
@available(iOS 5.0, *)
optional func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
/**
* @method centralManager:connectionEventDidOccur:forPeripheral:
*
* @param central The central manager providing this information.
* @param event The <code>CBConnectionEvent</code> that has occurred.
* @param peripheral The <code>CBPeripheral</code> that caused the event.
*
* @discussion This method is invoked upon the connection or disconnection of a peripheral that matches any of the options provided in {@link registerForConnectionEventsWithOptions:}.
*
*/
@available(iOS 13.0, *)
optional func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral)
/**
* @method centralManager:didUpdateANCSAuthorizationForPeripheral:
*
* @param central The central manager providing this information.
* @param peripheral The <code>CBPeripheral</code> that caused the event.
*
* @discussion This method is invoked when the authorization status changes for a peripheral connected with {@link connectPeripheral:} option {@link CBConnectPeripheralOptionRequiresANCS}.
*
*/
@available(iOS 13.0, *)
optional func centralManager(_ central: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral)
}
如果连接成功,就会有成功的回调,到这里蓝牙连接获取成功的,紧接着开始搜索蓝牙的服务,通过调用
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self // 设置 CBPeripheralDelegate
peripheral.discoverServices(nil) // 搜索服务
}
发现服务,会有CBPeripheralDelegate中的回调,然后继续从服务中寻找特征
/** 发现服务 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service: CBService in peripheral.services! {
print(TAG + "外设中的服务有:\(service)")
}
// 根据UUID寻找服务中的特征
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
/** 发现特征 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic: CBCharacteristic in service.characteristics! {
print(TAG + "外设中的特征有:\(characteristic)")
print(TAG + "外设中的特征有UUID:\(characteristic.uuid.uuidString)")
if characteristic.uuid.uuidString == "2A25" { //获取mac addr 特征
peripheral.setNotifyValue(true, for: characteristic)
// 读取特征值,主要返回mac地址
peripheral.readValue(for: characteristic)
}
}
let characteristic = service.characteristics?.last
peripheral.setNotifyValue(true, for: characteristic!)
peripheral.readValue(for: characteristic!)
保留//匹配蓝牙对应特征
mCharacteristicDict[peripheral] = characteristic
}
/** 订阅状态 */
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print(TAG + "订阅失败: \(error)")
return
}
if characteristic.isNotifying {
print(TAG + "avc" + "订阅成功")
if mCurAddress != "" && mPeripheralDict[mCurAddress] == peripheral && characteristic == mCharacteristicDict[peripheral] {
connetSuc()
} else if mCurAddress == "" {
for (addr, per) in mPeripheralDict {
if per == peripheral {
mNotifyDict[addr] = true
break
}
}
}
} else {
print(TAG + "取消订阅")
}
}
/** 接收到数据 */
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if !(characteristic.value != nil) {
return
}
let data = characteristic.value!
//let message = CommonTool.dataToHexString(data)
let message = String(bytes: data, encoding: String.Encoding.utf8)!
// 接受到的数据,对数据进行处理
}
/** 写入数据 */
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print(TAG + "写入数据")
}
最后是建立成功发送数据
self.mCurPeripheral?.writeValue(data, for: self.mCurCharacteristic!, type: .withoutResponse)
这样基本就实现了具体特征值的一些基础通讯了,通讯过程的交互协议根据自己的业务需要去定义
后续我会把Android的蓝牙通讯也梳理一遍,敬请期待,Android相对比较复杂
网友评论