iOS 通信协议—— 蓝牙通信

深碍√TFBOYSˉ_ 2022-11-12 13:49 823阅读 0赞

这几天小伙伴又给我提了一个新需求,要求给他们的项目提供一个iOS端的蓝牙通信App,虽然iOS也做过一些,Objective C和Swift多少也知道一些,不过并没有深入的研究过iOS的开发,为了节省工期囫囵吞枣的看了一些资料,像之前那篇文章一样,我这里就不舞大刀,我把我参考的资料就放在下面这里,你如果需要可以去看看。

《[iOS/swift]蓝牙连接》

Github代码仓库:MichaelLynx/BleDemo

《Swift CoreBluetooth 蓝牙库》

我参考这些资料和代码的基础上,重写了其中一部分代码,并且做了一定的封装,你可以直接复制黏贴,并且在自己的工程里使用。版本为Swift的,不使用Objective C是因为觉得写起来比较浪费时间,而且如果遇到了需要和底层数据打交到的地方,我依然可以用Objective C做封装就好了。

首先是这个BluetoothLowEnergy类的实现。

  1. //
  2. // BluetoothLowEnergyHandler.swift
  3. // PetoiSerialSwift
  4. //
  5. // Created by Orlando Chen on 2021/3/23.
  6. //
  7. import Foundation
  8. import CoreBluetooth
  9. class BluetoothLowEnergy: NSObject {
  10. // 一个蓝牙设备当中可能包含多个信道,一个UUID就是一个信道标记
  11. var uuids: [CBCharacteristic] = []
  12. // 中心对象
  13. var central : CBCentralManager!
  14. // 把中心设备扫描的外置设备保存起来
  15. var deviceList: [CBPeripheral] = []
  16. // 接收到的数据
  17. var peripheralData: Data?
  18. // MARK: 0. 初始化
  19. override init() {
  20. super.init()
  21. // 初始化中心设备管理器
  22. // 它的delegate函数为centralManagerDidUpdateState
  23. // 其回调消息处理函数为:centralManager
  24. self.central = CBCentralManager.init(delegate:self, queue:nil, options:[CBCentralManagerOptionShowPowerAlertKey:false])
  25. // 初始化设备列表
  26. self.deviceList = []
  27. }
  28. // MARK: 1. 扫描设备
  29. func startScanPeripheral(serviceUUIDS: [CBUUID]?,
  30. options: [String: AnyObject]?) {
  31. // 清空列表
  32. deviceList = [] // 清空设备列表
  33. uuids = [] // 清空信道列表
  34. // 开始进行扫描
  35. self.central?.scanForPeripherals(withServices: serviceUUIDS, options: options)
  36. }
  37. // MARK: 2. 停止扫描
  38. func stopScanPeripheral() {
  39. self.central?.stopScan()
  40. }
  41. // MARK: 3.1. 获取搜索到的外接设备
  42. func getPeripheralList()-> [CBPeripheral] {
  43. return deviceList
  44. }
  45. // MARK: 3.2. 获取到当前蓝牙设备可用的消息信道
  46. func getCharacteristic() -> [CBCharacteristic] {
  47. return uuids
  48. }
  49. // MARK: 3.3. 指定监听信道
  50. func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic) {
  51. peripheral.setNotifyValue(true, for: notify)
  52. }
  53. // MARK: 4.1. 连结设备
  54. // 连接设备之前要先设置代理,正常情况,当第一次获取外设peripheral的时候就会同时设置代理
  55. func connect(peripheral: CBPeripheral) {
  56. if (peripheral.state != CBPeripheralState.connected) {
  57. central?.connect(peripheral , options: nil)
  58. // 将外接设备的回掉函数连结到self
  59. // 回掉消息处理函数为:peripheral
  60. peripheral.delegate = self
  61. }
  62. }
  63. // MARK: 4.2. 检测是否建立了连结
  64. func isConnected(peripheral: CBPeripheral) -> Bool {
  65. return peripheral.state == CBPeripheralState.connected
  66. }
  67. // MARK: 5.1. 发送数据
  68. func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic,
  69. type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse) {
  70. let step = 20
  71. for index in stride(from: 0, to: data.count, by: step) {
  72. var len = data.count - index
  73. if len > step {
  74. len = step
  75. }
  76. let pData: Data = (data as NSData).subdata(with: NSRange(location: index, length: len))
  77. peripheral.writeValue(pData, for: characteristic, type: type)
  78. }
  79. }
  80. // MARK: 5.2. 接收数据
  81. func recvData() -> Data {
  82. return peripheralData ?? Data([0x00])
  83. }
  84. // MARK: 6. 断开连结
  85. func disconnect(peripheral: CBPeripheral) {
  86. central?.cancelPeripheralConnection(peripheral)
  87. }
  88. }
  89. extension BluetoothLowEnergy: CBCentralManagerDelegate {
  90. // MARK: 检查运行这个App的设备是不是支持BLE。
  91. func centralManagerDidUpdateState(_ central: CBCentralManager) {
  92. switch central.state {
  93. case .poweredOn:
  94. NSLog("BLE poweredOn")
  95. case .poweredOff:
  96. NSLog("BLE powered off")
  97. case .unknown:
  98. NSLog("BLE unknown")
  99. case .resetting:
  100. NSLog("BLE ressetting")
  101. case .unsupported:
  102. NSLog("BLE unsupported")
  103. case .unauthorized:
  104. NSLog("BLE unauthorized")
  105. @unknown default:
  106. NSLog("BLE default")
  107. }
  108. }
  109. // MARK: 以ANCS协议请求的端,授权状态发生改变
  110. func centralManager(_ central: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
  111. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
  112. // TODO
  113. }
  114. // MARK: 状态的保存或者恢复
  115. func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
  116. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(dict)")
  117. // TODO
  118. }
  119. // MARK:
  120. func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
  121. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
  122. // TODO
  123. }
  124. // 开始扫描之后会扫描到蓝牙设备,扫描到之后走到这个代理方法
  125. // MARK: 中心管理器扫描到了设备
  126. func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
  127. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
  128. guard !deviceList.contains(peripheral), let deviceName = peripheral.name, deviceName.count > 0 else {
  129. return
  130. }
  131. // 把设备加入到列表中
  132. deviceList.append(peripheral)
  133. // TODO
  134. }
  135. // MARK: 连接外设成功,开始发现服务
  136. func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
  137. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
  138. // 设置代理
  139. peripheral.delegate = self
  140. // 开始发现服务
  141. peripheral.discoverServices(nil)
  142. }
  143. // MARK: 连接外设失败
  144. func centralManager(_ central: CBCentralManager, didFailToConnect peripheral:
  145. CBPeripheral, error: Error?) {
  146. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
  147. // TODO
  148. }
  149. // MARK: 连接丢失
  150. func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
  151. // NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
  152. // TODO
  153. }
  154. }
  155. // MARK: 外置设备被绑定后的事件响应
  156. extension BluetoothLowEnergy: CBPeripheralDelegate {
  157. // MARK: 匹配对应服务UUID
  158. func peripheral(_ peripheral: CBPeripheral,
  159. didDiscoverServices error: Error?) {
  160. if error != nil { // failed
  161. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
  162. return
  163. }
  164. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))")
  165. for service in peripheral.services ?? [] {
  166. peripheral.discoverCharacteristics(nil, for: service)
  167. }
  168. }
  169. // MARK: 服务下的特征
  170. func peripheral(_ peripheral: CBPeripheral,
  171. didDiscoverCharacteristicsFor service:
  172. CBService, error: Error?) {
  173. if error != nil { // failed
  174. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))\n error:\(String(describing: error))")
  175. return
  176. }
  177. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))")
  178. for characteristic in service.characteristics ?? [] {
  179. uuids.append(characteristic)
  180. }
  181. }
  182. // MARK: 获取外设发来的数据
  183. // 注意,所有的,不管是 read , notify 的特征的值都是在这里读取
  184. func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
  185. if error != nil {
  186. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
  187. return
  188. }
  189. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))")
  190. if let data = characteristic.value {
  191. self.peripheralData = data
  192. }
  193. }
  194. //MARK: 检测中心向外设写数据是否成功
  195. func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
  196. if let error = error {
  197. // NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
  198. }
  199. // TODO
  200. }
  201. }

所有的NSlog都是可以根据需要打开,方便调试使用的。TODO部分是你可以在上面进一步扩展的地方,当然修改原代码也是可以的。

至于如何使用?

首先,在iOS工程需要先在plist增加

  1. Privacy - Bluetooth Always Usage Description

然后在某个类里实例化这个BLE类,如果是全局都需要进行蓝牙数据传输,那么就把这个类实例在AppDelegate文件里,并且设置为静态,这样就可以全局使用了。

用户需要具体调用的几个函数如下:

  1. // MARK: 1. 扫描设备
  2. func startScanPeripheral(serviceUUIDS: [CBUUID]?, options: [String: AnyObject]?)
  3. // MARK: 2. 停止扫描
  4. func stopScanPeripheral()
  5. // MARK: 3. 获取搜索到的外接设备
  6. func getPeripheralList()-> [CBPeripheral]
  7. // MARK: 4.1. 连结设备
  8. func connect(peripheral: CBPeripheral)
  9. // MARK: 4.2. 检测是否建立了连结
  10. func isConnected(peripheral: CBPeripheral) -> Bool
  11. // MARK: 4.3. 获取到当前蓝牙设备可用的消息信道
  12. func getCharacteristic() -> [CBCharacteristic]
  13. // MARK: 4.4. 指定监听信道
  14. func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic)
  15. // MARK: 5.1. 发送数据
  16. func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic, type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse)
  17. // MARK: 5.2. 接收数据
  18. func recvData() -> Data
  19. // MARK: 6. 断开连结
  20. func disconnect(peripheral: CBPeripheral)

由于蓝牙设备的通信过程是一个异步过程,等价于创建了一个后台线程对数据服务进行监听,所以如果对于音频流这一类实时会接受大量数据的应用,你可能需要增加一个标记或者其他方式以便处理,具体处理函数为

  1. func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)

只不对对于文本数据,这个代码已经足够使用了,比较简单的一种处理方法,就是用个定时器,每隔几十毫秒查询一次数据;而比较实时的处理方法,就是做一个回调函数,不过这些都由你自己决定怎么做吧,好运!

发表评论

表情:
评论列表 (有 0 条评论,823人围观)

还没有评论,来说两句吧...

相关阅读

    相关 iOSBLE4.0通信功能

    概述 iOS蓝牙BLE4.0通信功能,最近刚学的苹果,为了实现蓝牙门锁的项目,找了一天学习了下蓝牙的原理,亲手测试了一次蓝牙的通信功能,结果成功了,那么就把我学习的东西分