iOS 通信协议—— 蓝牙通信
这几天小伙伴又给我提了一个新需求,要求给他们的项目提供一个iOS端的蓝牙通信App,虽然iOS也做过一些,Objective C和Swift多少也知道一些,不过并没有深入的研究过iOS的开发,为了节省工期囫囵吞枣的看了一些资料,像之前那篇文章一样,我这里就不舞大刀,我把我参考的资料就放在下面这里,你如果需要可以去看看。
《[iOS/swift]蓝牙连接》
Github代码仓库:MichaelLynx/BleDemo
《Swift CoreBluetooth 蓝牙库》
我参考这些资料和代码的基础上,重写了其中一部分代码,并且做了一定的封装,你可以直接复制黏贴,并且在自己的工程里使用。版本为Swift的,不使用Objective C是因为觉得写起来比较浪费时间,而且如果遇到了需要和底层数据打交到的地方,我依然可以用Objective C做封装就好了。
首先是这个BluetoothLowEnergy类的实现。
//
// BluetoothLowEnergyHandler.swift
// PetoiSerialSwift
//
// Created by Orlando Chen on 2021/3/23.
//
import Foundation
import CoreBluetooth
class BluetoothLowEnergy: NSObject {
// 一个蓝牙设备当中可能包含多个信道,一个UUID就是一个信道标记
var uuids: [CBCharacteristic] = []
// 中心对象
var central : CBCentralManager!
// 把中心设备扫描的外置设备保存起来
var deviceList: [CBPeripheral] = []
// 接收到的数据
var peripheralData: Data?
// MARK: 0. 初始化
override init() {
super.init()
// 初始化中心设备管理器
// 它的delegate函数为centralManagerDidUpdateState
// 其回调消息处理函数为:centralManager
self.central = CBCentralManager.init(delegate:self, queue:nil, options:[CBCentralManagerOptionShowPowerAlertKey:false])
// 初始化设备列表
self.deviceList = []
}
// MARK: 1. 扫描设备
func startScanPeripheral(serviceUUIDS: [CBUUID]?,
options: [String: AnyObject]?) {
// 清空列表
deviceList = [] // 清空设备列表
uuids = [] // 清空信道列表
// 开始进行扫描
self.central?.scanForPeripherals(withServices: serviceUUIDS, options: options)
}
// MARK: 2. 停止扫描
func stopScanPeripheral() {
self.central?.stopScan()
}
// MARK: 3.1. 获取搜索到的外接设备
func getPeripheralList()-> [CBPeripheral] {
return deviceList
}
// MARK: 3.2. 获取到当前蓝牙设备可用的消息信道
func getCharacteristic() -> [CBCharacteristic] {
return uuids
}
// MARK: 3.3. 指定监听信道
func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic) {
peripheral.setNotifyValue(true, for: notify)
}
// MARK: 4.1. 连结设备
// 连接设备之前要先设置代理,正常情况,当第一次获取外设peripheral的时候就会同时设置代理
func connect(peripheral: CBPeripheral) {
if (peripheral.state != CBPeripheralState.connected) {
central?.connect(peripheral , options: nil)
// 将外接设备的回掉函数连结到self
// 回掉消息处理函数为:peripheral
peripheral.delegate = self
}
}
// MARK: 4.2. 检测是否建立了连结
func isConnected(peripheral: CBPeripheral) -> Bool {
return peripheral.state == CBPeripheralState.connected
}
// MARK: 5.1. 发送数据
func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic,
type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse) {
let step = 20
for index in stride(from: 0, to: data.count, by: step) {
var len = data.count - index
if len > step {
len = step
}
let pData: Data = (data as NSData).subdata(with: NSRange(location: index, length: len))
peripheral.writeValue(pData, for: characteristic, type: type)
}
}
// MARK: 5.2. 接收数据
func recvData() -> Data {
return peripheralData ?? Data([0x00])
}
// MARK: 6. 断开连结
func disconnect(peripheral: CBPeripheral) {
central?.cancelPeripheralConnection(peripheral)
}
}
extension BluetoothLowEnergy: CBCentralManagerDelegate {
// MARK: 检查运行这个App的设备是不是支持BLE。
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
NSLog("BLE poweredOn")
case .poweredOff:
NSLog("BLE powered off")
case .unknown:
NSLog("BLE unknown")
case .resetting:
NSLog("BLE ressetting")
case .unsupported:
NSLog("BLE unsupported")
case .unauthorized:
NSLog("BLE unauthorized")
@unknown default:
NSLog("BLE default")
}
}
// MARK: 以ANCS协议请求的端,授权状态发生改变
func centralManager(_ central: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
// TODO
}
// MARK: 状态的保存或者恢复
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(dict)")
// TODO
}
// MARK:
func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
// TODO
}
// 开始扫描之后会扫描到蓝牙设备,扫描到之后走到这个代理方法
// MARK: 中心管理器扫描到了设备
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
guard !deviceList.contains(peripheral), let deviceName = peripheral.name, deviceName.count > 0 else {
return
}
// 把设备加入到列表中
deviceList.append(peripheral)
// TODO
}
// MARK: 连接外设成功,开始发现服务
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
// 设置代理
peripheral.delegate = self
// 开始发现服务
peripheral.discoverServices(nil)
}
// MARK: 连接外设失败
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral:
CBPeripheral, error: Error?) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
// TODO
}
// MARK: 连接丢失
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
// TODO
}
}
// MARK: 外置设备被绑定后的事件响应
extension BluetoothLowEnergy: CBPeripheralDelegate {
// MARK: 匹配对应服务UUID
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
if error != nil { // failed
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
return
}
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))")
for service in peripheral.services ?? [] {
peripheral.discoverCharacteristics(nil, for: service)
}
}
// MARK: 服务下的特征
func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service:
CBService, error: Error?) {
if error != nil { // failed
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))\n error:\(String(describing: error))")
return
}
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))")
for characteristic in service.characteristics ?? [] {
uuids.append(characteristic)
}
}
// MARK: 获取外设发来的数据
// 注意,所有的,不管是 read , notify 的特征的值都是在这里读取
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
return
}
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))")
if let data = characteristic.value {
self.peripheralData = data
}
}
//MARK: 检测中心向外设写数据是否成功
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
// NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
}
// TODO
}
}
所有的NSlog都是可以根据需要打开,方便调试使用的。TODO部分是你可以在上面进一步扩展的地方,当然修改原代码也是可以的。
至于如何使用?
首先,在iOS工程需要先在plist增加
Privacy - Bluetooth Always Usage Description
然后在某个类里实例化这个BLE类,如果是全局都需要进行蓝牙数据传输,那么就把这个类实例在AppDelegate文件里,并且设置为静态,这样就可以全局使用了。
用户需要具体调用的几个函数如下:
// MARK: 1. 扫描设备
func startScanPeripheral(serviceUUIDS: [CBUUID]?, options: [String: AnyObject]?)
// MARK: 2. 停止扫描
func stopScanPeripheral()
// MARK: 3. 获取搜索到的外接设备
func getPeripheralList()-> [CBPeripheral]
// MARK: 4.1. 连结设备
func connect(peripheral: CBPeripheral)
// MARK: 4.2. 检测是否建立了连结
func isConnected(peripheral: CBPeripheral) -> Bool
// MARK: 4.3. 获取到当前蓝牙设备可用的消息信道
func getCharacteristic() -> [CBCharacteristic]
// MARK: 4.4. 指定监听信道
func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic)
// MARK: 5.1. 发送数据
func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic, type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse)
// MARK: 5.2. 接收数据
func recvData() -> Data
// MARK: 6. 断开连结
func disconnect(peripheral: CBPeripheral)
由于蓝牙设备的通信过程是一个异步过程,等价于创建了一个后台线程对数据服务进行监听,所以如果对于音频流这一类实时会接受大量数据的应用,你可能需要增加一个标记或者其他方式以便处理,具体处理函数为
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
只不对对于文本数据,这个代码已经足够使用了,比较简单的一种处理方法,就是用个定时器,每隔几十毫秒查询一次数据;而比较实时的处理方法,就是做一个回调函数,不过这些都由你自己决定怎么做吧,好运!
还没有评论,来说两句吧...