Swift连接BLE蓝牙打印机

文章目录

swift 蓝牙连接

项目简介

最近公司要用到便携式蓝牙打印机进行打印
打印机使用的 ECS/POS 指令集
ios 使用的 BLE 方案,安卓则是因为设备的原因只能选择普通蓝牙的连接方案
BLE 蓝牙相关的概念性东西我这里就不说了 大家可以自己去搜索下
有个 mac 的蓝牙开发工具 LightBlue,很好用,mac app store 自己下就好了,这个很方便你理解 BLE 的蓝牙相关 device(central/peripheral)->services->characteristics

屏幕快照 2017-09-08 下午3.39.04.png

这里可以看到,有一个 mobike,有兴趣的同学可以研究下 mobike 的蓝牙连接 ☺ 说笑一下,人家肯定有安全性校验的

语言选型

最近在学习 ios 开发,因为我是 android 出身,学习 ios 开发的时候 swift3 已经出现很久了,所以我这里使用的 swift 进行开发

坑 1

最初我使用了厂家提供的 ios sdk 进行开发,其中封装了很多常用的方法,让我自己以为很简单就能完成,但是事实上是我太天真了,首先厂家提供的是.a 的库,只有一个.h 文件暴露在外,我的项目是纯 swift 项目,这就不可避免的使用到了 swift 到 oc 的桥接

坑 2

满以为桥接完了调 SDK 方法就行,谁知道调用的时候根本就没反应,没办法,只能摸石头过河进行开发了,最初使用的是 oc 的 corebluetooth 方案,因为实在是没找到 swift 的相关说法,baidu 没搜到,没办法,oc 毕竟也算是入门了,直接开干了

坑 3

开发完 oc 的连接 demo,强迫症发作,决定一定要用纯 swift 开发,毕竟我们还是要跟随时代脚步的嘛

找文档

都说苹果的官方文档写的很好,那么我就上去看看吧,这里要吐槽一点,文档的方法,类描述确实很不错,看起来很清晰,但是但是..怎么没有告诉我 import 什么

UIKit 里不包含 Bluetooth 相关的类,而官方中将这个定义在System体系的 Core Bluetooth 中

使用

1import CoreBluetooth

这样CBCentralManager终于可以用了

正式开发

折腾了半天,终于可以开始开发了...

macos 模拟器蓝牙 central

这里要吐槽下公司,没有 ios 测试机,我自己又是安卓手机,没办法,这里有一招,建一个 macos 的项目,将UIKit换成Cocoa,或者Foundation,然后其他语法中 macos 和 ios 的蓝牙部分代码几乎一样,这样就能连接上打印机了,我这里又是模块开发,将数据部分的代码 copy 一份到 macos 的 demo 上,就能模拟真机的 mac 了
上面说几乎一样的原因是,cm.isScanning在 mac 开发中用不了,ios 中可以

当时记得苹果说 simulator 可以用 mac 的蓝牙开发,结果短短的一个版本以后就干掉了相关功能,真是狗

帮助类的代码

  1
  2//
  3//  BluetoothHelper.swift
  4//  SwiftBluetoothScanDemo1
  5//
  6//  Created by caijinglong on 2017/9/9.
  7//
  8
  9import Foundation
 10import CoreBluetooth
 11
 12protocol BluetoothHelperDelegate {
 13
 14    func bluetoothHelperIndex()->Int
 15
 16    func bluetoothHelperNotifyConnected(isConnected:Bool)
 17
 18    func bluetoothHelperAutoStopScan()
 19}
 20
 21class BluetoothHelper :NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{
 22
 23    static let shared = BluetoothHelper()
 24
 25    private var cm:CBCentralManager! = nil
 26    private var peripheral: CBPeripheral! = nil
 27    private var service:CBService! = nil
 28
 29    private var characteristic:CBCharacteristic! = nil
 30
 31    private var delegateDict = Dictionary<Int,BluetoothHelperDelegate>()
 32
 33    public func registerDelegate(delegate:BluetoothHelperDelegate){
 34        delegateDict[delegate.bluetoothHelperIndex()] = delegate
 35    }
 36
 37    public func unregisterDelegate(delegate:BluetoothHelperDelegate){
 38        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
 39    }
 40
 41    private override init(){
 42        super.init()
 43        self.cm = CBCentralManager(delegate: self, queue: nil)
 44    }
 45
 46    /// 被连接的打印机的名字
 47    var connectDeviceName:String? = ""
 48
 49    /// 是否连接了打印机
 50    var isConnected:Bool = false{
 51        didSet{
 52            if(!isConnected){
 53                peripheral = nil
 54                service = nil
 55                characteristic = nil
 56                connectDeviceName = nil
 57
 58                delegateDict.forEach({ (key,delegate) in
 59
 60                })
 61            }else{
 62                connectDeviceName = self.name
 63            }
 64        }
 65    }
 66
 67    /// 蓝牙开关是否打开
 68    var btOpen = false
 69    private var name = "QSPrinter"
 70
 71    // 后面是代理方法
 72    func centralManagerDidUpdateState(_ central: CBCentralManager) {
 73        NSLog("状态变化")
 74        if(central.state.rawValue == 5){
 75            btOpen = true
 76        }else{
 77            btOpen = false
 78            isConnected = false
 79        }
 80    }
 81
 82    /// 开始扫描设备
 83    func startScan(name:String){
 84        self.name = name
 85        self.cm.stopScan()
 86        self.cm.scanForPeripherals(withServices: nil, options: nil)
 87        runDelay(5) {
 88            self.delegateDict.forEach({ (_,delegate) in
 89                self.cm.stopScan()
 90                delegate.bluetoothHelperAutoStopScan()
 91            })
 92        }
 93    }
 94
 95    /// 停止扫描设备
 96    func stopScan(){
 97        self.cm.stopScan()
 98    }
 99
100    /// 关闭连接设备
101    func disconnect(){
102        if(peripheral != nil){
103            self.cm.cancelPeripheralConnection(peripheral)
104        }
105    }
106
107    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
108        NSLog("\(String(describing: peripheral.name)) is discovered")
109        if(peripheral.name?.uppercased() == name.uppercased()){
110            self.peripheral = peripheral
111            peripheral.delegate = self
112            cm.connect(peripheral)
113            cm.stopScan()
114        }
115    }
116
117    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
118        NSLog("\(String(describing: peripheral.name)) 连接成功")
119        let uuid = CBUUID(string: "18F0")
120        peripheral.discoverServices([uuid])
121    }
122
123    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
124        NSLog("\(String(describing: peripheral.name)) 连接断开")
125        isConnected = false
126    }
127
128    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
129        if let service = peripheral.services?[0]{
130            let uuid = CBUUID(string: "2AF1")
131            peripheral.discoverCharacteristics([uuid], for: service)
132            self.service = service
133        }
134    }
135
136    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
137        if let characteristic = service.characteristics?[0]{
138            NSLog("characteristic is prepared")
139            isConnected = true
140            self.characteristic = characteristic
141        }
142    }
143
144    /// 输出字符串
145    func writeText(text:String)throws{
146        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))
147
148        if let data = text.data(using: String.Encoding(rawValue: enc)){
149            do {
150               try self.writeData(data: data)
151            } catch  {
152                throw error
153            }
154        }
155    }
156
157
158    private var datas = [Data]()
159
160    /// 写入数据
161    func writeData(data:Data) throws {
162        if(isConnected){
163            datas.append(data)
164        }else{
165            throw BtError.NoConnectError
166        }
167    }
168
169    /// 真实的打印方法
170    func print(){
171        for index in 0 ... datas.count - 1  {
172            let item = datas[index]
173            runDelay(0.02 * Double(index), {
174                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
175            })
176        }
177        self.datas.removeAll()
178    }
179
180}
181
182enum BtError :Error{
183    case NoConnectError
184}

分析下代码 这里使用单例的方案管理连接,实际上 BLE 支持同时连接多个外设,我这里是因为目前没有这样的需求,所以考虑使用单例的模式,看官请根据自己的需求来

centralManagerDidUpdateState这个代理方法很重要,是唯一一个必须实现的方法,用于监听蓝牙的状态,是一个 Int 类型的枚举值,这里因为 ios10 有一个过期相关的提示,替换了 state 相关的类由CBCentralManagerState替换到CBManagerState,值没有变化,就是由 oc 的枚举方式替换到了 swift 的枚举,这里我直接使用 5 来进行判断,ios11 也没看见修改这个数值,短时间内直接用 5 就行,后续有 bug 再说 这里用一个 property 来储存蓝牙状态

startScan其中是开始扫描的方法

 1    func startScan(name:String){
 2        self.name = name
 3        self.cm.stopScan()
 4        self.cm.scanForPeripherals(withServices: nil, options: nil)
 5        runDelay(5) {
 6            self.delegateDict.forEach({ (_,delegate) in
 7                self.cm.stopScan()
 8                delegate.bluetoothHelperAutoStopScan()
 9            })
10        }
11    }

先停止扫描,然后开始扫描,记录一下 name,后续会用到,5 秒后停止扫描,并代理通知

 1func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
 2        NSLog("\(String(describing: peripheral.name)) is discovered")
 3        if(peripheral.name?.uppercased() == name.uppercased()){
 4            self.peripheral = peripheral
 5            peripheral.delegate = self
 6            cm.connect(peripheral)
 7            cm.stopScan()
 8        }
 9    }
10
11    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
12        NSLog("\(String(describing: peripheral.name)) 连接成功")
13        let uuid = CBUUID(string: "18F0")
14        peripheral.discoverServices([uuid])
15    }

这两个方法,第一个是扫描到了设备,这里我忽视大小写进行匹配,然后如果名字匹配则调用cm.connect(peripheral)进行连接,并且停止扫描
第二个方法是连接成功,这里 18F0 是 service 的名称,就是扫描 UUID 为 18F0 的 services

1    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
2        if let service = peripheral.services?[0]{
3            let uuid = CBUUID(string: "2AF1")
4            peripheral.discoverCharacteristics([uuid], for: service)
5            self.service = service
6        }
7    }

这里是在扫描到 services 后的代理,然后扫描 uuid 为2AF1的 Characteristics 这里 service 保持引用

1func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
2        if let characteristic = service.characteristics?[0]{
3            NSLog("characteristic is prepared")
4            isConnected = true
5            self.characteristic = characteristic
6        }
7    }

这里是扫描到 Characteristics 后的操作,这里我记录了一个连接状态 和 characteristic 的引用

1self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)//item是Data

这个是真实的输出方法,这个输出方法使用了了之前的peripheralcharacteristic 这里之所以要持有所有'中间'产物的引用是因为之前用 oc 写这个代码的时候因为没有持有 peripheral 的引用报错,导致代理获取不到数据

详细分析写数据

这里我使用了一个方案来写

 1   /// 输出字符串
 2    func writeText(text:String)throws{
 3        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))
 4
 5        if let data = text.data(using: String.Encoding(rawValue: enc)){
 6            do {
 7               try self.writeData(data: data)
 8            } catch  {
 9                throw error
10            }
11        }
12    }
13
14
15    private var datas = [Data]()
16
17    /// 写入数据
18    func writeData(data:Data) throws {
19        if(isConnected){
20            datas.append(data)
21        }else{
22            throw BtError.NoConnectError
23        }
24    }
25
26    /// 真实的打印方法
27    func print(){
28        for index in 0 ... datas.count - 1  {
29            let item = datas[index]
30            runDelay(0.02 * Double(index), {
31                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
32            })
33        }
34        self.datas.removeAll()
35    }
1func runDelay(_ delay:TimeInterval,_ block:@escaping () -> ()){
2    let queue = DispatchQueue.main
3
4    let delayTime = DispatchTime.now() + delay
5
6    queue.asyncAfter(deadline: delayTime) {
7        block()
8    }
9}

这里的思路大概是: 首先一个容器用于储存顺序存入的数据,然后在调用print的时候将所有数据进行输出,并且延迟一定的时间,每个输出时间间隔 0.02s
之所以这么做的原因是:我当时直接调用writeValue方法写数据,没有间隔时间,发现会出现样式错误,输出顺序错误的情况发生
这个时候我凭感觉认为是发生了writeValue和实际通讯的到达顺序不一致的问题,我查了下,BLE 主打的是低延迟,但是对应的数据通讯的数据量就有了限制,所以采用这个方案

当然我也试过使用一个 Data 储存所有字节数据的方案,发现打印机无法打印,具体原因没有深究

打印相关

编码问题

一般的蓝牙打印机中文使用的是 GBK 编码,ios 中是 GB_18030_2000,而 ios 默认是 utf8 编码,所以这里需要显示指定

关于二维码

打印二维码使用的是 ESC/POS 的指令集,这个在指令集的说明文档中可以找到
这里的 moduleSize 是二维码的大小,具体参数可以参考说明文档,一般对接的打印机厂商都会提供
一般的打印机中文都是 GBK 编码的,而扫码一般是 UTF8 编码,这个编码转换很麻烦,所以尽量不要出现中文

打印机发送指令

  1//
  2//  PrinterHelper.swift
  3//  SwiftBluetoothScanDemo1
  4//
  5//  Created by Caijinglong on 2017/9/11.
  6//
  7
  8import Foundation
  9
 10/// printer helper
 11///
 12/// single instance
 13class PrinterHelper{
 14
 15    static var shared:PrinterHelper = PrinterHelper()
 16
 17    var helper : BluetoothHelper!
 18
 19    //    var devices = [Printer]()
 20
 21    private init(){
 22        helper = BluetoothHelper.shared
 23    }
 24
 25    func registerDelegate(delegate: BluetoothHelperDelegate){
 26        helper.registerDelegate(delegate: delegate)
 27    }
 28
 29    func unregisterDelegate(delegate: BluetoothHelperDelegate){
 30        helper.unregisterDelegate(delegate: delegate)
 31    }
 32
 33    var index = 0
 34
 35    let DIVIDER = "-----------------------------------------------"
 36    let ESC: Byte = 27//换码
 37    let FS: Byte = 28//文本分隔符
 38    let GS: Byte = 29//组分隔符
 39    let DLE: Byte = 16//数据连接换码
 40    let EOT: Byte = 4//传输结束
 41    let ENQ: Byte = 5//询问字符
 42    let SP: Byte = 32//空格
 43    let HT: Byte = 9//横向列表
 44    let LF: Byte = 10//打印并换行(水平定位)
 45    let CR: Byte = 13//归位键
 46    let FF: Byte = 12//走纸控制(打印并回到标准模式(在页模式下)
 47    let CAN: Byte = 24//作废(页模式下取消打印数据 )
 48
 49    func conn(deviceName:String){
 50        helper.startScan(name: deviceName)
 51    }
 52
 53    func disconnect(){
 54        helper.disconnect()
 55    }
 56
 57    func sendMsg(msg:String) -> Self{
 58        try? helper.writeText(text: msg)
 59        return self
 60    }
 61
 62    func sendBytes(bytes:[Byte]) -> Self{
 63        try? helper.writeData(data: Data.bytesArray(byteArray: bytes))
 64        return self
 65    }
 66
 67    func sendHex(int:Int) -> Self {
 68        return self.sendHexs(hexInt: int)
 69    }
 70
 71    func sendHexs(hexInt ints:Int...) -> Self{
 72        var data = Data()
 73        ints.forEach { (int) in
 74            data.append(UInt8(int))
 75        }
 76        try? helper.writeData(data: data)
 77        return self
 78    }
 79
 80    func sendBytes(bytes:Byte...) -> Self{
 81        return sendBytes(bytes: bytes)
 82    }
 83
 84    func alignLeft()-> Self{
 85        return sendBytes(bytes: ESC,97,0)
 86    }
 87
 88    func alignCenter() -> Self {
 89        return sendBytes(bytes: ESC,97,1)
 90    }
 91
 92    func alignRight() -> Self{
 93        return sendBytes(bytes: ESC,97,2)
 94    }
 95
 96    func printDivider() -> Self {
 97        return sendMsg(msg: DIVIDER)
 98    }
 99
100
101    func startPrint(){
102        helper.print()
103    }
104
105    func setFontSize(size:Int)  -> Self{
106        var realSize: Byte = 0
107
108        if(size <= 7){
109            realSize = Byte(size * 17)
110        }
111
112        var result = [Byte]()
113        result.append(0x1D)
114        result.append(0x21)
115        result.append(realSize)
116        print("size = \(size)  realSize = \(realSize)")
117        return sendBytes(bytes: result)
118    }
119
120    func newLine(lines:Int = 1) -> Self{
121        for _ in 0...lines - 1{
122            _ = sendHex(int: 0x0A)
123        }
124        return self
125    }
126
127    /**
128     * 选择加粗模式
129
130     * @return
131     */
132    func boldOn() -> Self {
133        var result = [Byte]()
134        result.append(ESC)
135        result.append(69)
136        result.append(0xF)
137        return sendBytes(bytes: result)
138    }
139
140
141    /**
142     * 取消加粗模式
143
144     * @return
145     */
146    func boldOff() -> Self {
147        var result = [Byte]()
148        result.append(ESC)
149        result.append(69)
150        result.append(0)
151        return sendBytes(bytes: result)
152    }
153
154    func subTitle(_ title:String) -> Self{
155        return
156            self.newLine()
157                .setFontSize(size: 1)
158                .boldOn()
159                .alignCenter()
160                .sendMsg(msg: title)
161                .setFontSize(size: 0)
162                .boldOff()
163    }
164
165    func sendQrcode(qrcode:String) -> Self{
166        let moduleSize:Byte = 8
167        var list = [Byte]()
168
169        if let data = Data.gbkData(text: qrcode){
170
171            //打印二维码矩阵
172            list.append(0x1D)// init
173            list.append(40) // adjust height of barcode
174            list.append(107)// adjust height of barcode
175            list.append(Byte(data.count + 3)) // pl
176            list.append(0) // ph
177            list.append(49) // cn
178            list.append(80) // fn
179            list.append(48) //
180
181            data.forEach({ (char) in
182                list.append(char)
183            })
184
185            list.append(0x1D)
186            list.append(40)// list.append("(k")
187            list.append(107)// list.append("(k")
188            list.append(3)
189            list.append(0)
190            list.append(49)
191            list.append(69)
192            list.append(48)
193
194            list.append(0x1D)
195            list.append(40)// list.append("(k")
196            list.append(107)// list.append("(k")
197            list.append(3)
198            list.append(0)
199            list.append(49)
200            list.append(67)
201            list.append(moduleSize)
202
203            list.append(0x1D)
204            list.append(40)// list.append("(k")
205            list.append(107)// list.append("(k")
206            list.append(3) // pl
207            list.append(0) // ph
208            list.append(49) // cn
209            list.append(81) // fn
210            list.append(48) // m
211        }
212
213        return
214            alignCenter()
215            .sendBytes(bytes: list)
216    }
217}

蓝牙连接的类

  1//
  2//  BluetoothHelper.swift
  3//  SwiftBluetoothScanDemo1
  4//
  5//  Created by caijinglong on 2017/9/9.
  6//  Copyright © 2017 sxw. All rights reserved.
  7//
  8
  9import Foundation
 10import CoreBluetooth
 11
 12protocol BluetoothHelperDelegate:NSObjectProtocol {
 13
 14    func bluetoothHelperIndex()->Int
 15
 16    func bluetoothHelperNotifyConnected(isConnected:Bool)
 17
 18    func bluetoothHelperAutoStopScan()
 19
 20    func bluetoothHelperFindDevices(name:String)
 21}
 22
 23extension BluetoothHelperDelegate{
 24    func bluetoothHelperFindDevices(name:String){
 25    }
 26}
 27
 28protocol BluetoothHelperScanDeviceDelegate {
 29    func bluetoothScan(peripheral: CBPeripheral)
 30
 31    func bluetoothHelperIndex()->Int
 32
 33    func bluetoothConnected(name:String)
 34
 35    func bluetoothDisconnect(name:String)
 36}
 37
 38extension BluetoothHelperScanDeviceDelegate{
 39    func bluetoothConnected(name:String){}
 40
 41    func bluetoothDisconnect(name:String){}
 42}
 43
 44class BluetoothHelper :NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{
 45
 46    static let shared = BluetoothHelper()
 47
 48    private var cm:CBCentralManager! = nil
 49    private var peripheral: CBPeripheral! = nil
 50    private var service:CBService! = nil
 51
 52    private var characteristic:CBCharacteristic! = nil
 53
 54    private var delegateDict = Dictionary<Int,BluetoothHelperDelegate>()
 55
 56    private var scanDelegateDict = Dictionary<Int,BluetoothHelperScanDeviceDelegate>()
 57
 58    public func registerDelegate(delegate:BluetoothHelperDelegate){
 59        delegateDict[delegate.bluetoothHelperIndex()] = delegate
 60    }
 61
 62    public func unregisterDelegate(delegate:BluetoothHelperDelegate){
 63        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
 64    }
 65
 66    public func registerScanDelegate(delegate:BluetoothHelperScanDeviceDelegate){
 67        scanDelegateDict[delegate.bluetoothHelperIndex()] = delegate
 68    }
 69
 70    public func unregisterScanDelegate(delegate:BluetoothHelperScanDeviceDelegate){
 71        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
 72    }
 73
 74    private override init(){
 75        super.init()
 76        self.cm = CBCentralManager(delegate: self, queue: nil)
 77    }
 78
 79    /// 被连接的打印机的名字
 80    var connectDeviceName:String? = ""
 81
 82    /// 是否连接了打印机
 83    var isConnected:Bool = false{
 84        didSet{
 85            if(!isConnected){
 86                peripheral = nil
 87                service = nil
 88                characteristic = nil
 89
 90                delegateDict.forEach({ (key,delegate) in
 91                    delegate.bluetoothHelperNotifyConnected(isConnected: false)
 92                })
 93                connectDeviceName = nil
 94            }else{
 95                connectDeviceName = self.name
 96
 97                delegateDict.forEach({ (key,delegate) in
 98                    delegate.bluetoothHelperNotifyConnected(isConnected: true)
 99                })
100            }
101        }
102    }
103
104    /// 蓝牙开关是否打开
105    var btOpen = false
106    private var name = "QSPrinter"
107
108    // 后面是代理方法
109    func centralManagerDidUpdateState(_ central: CBCentralManager) {
110        NSLog("状态变化: state = \(central.state.rawValue)")
111        if(central.state.rawValue == 5){
112            btOpen = true
113        }else{
114            btOpen = false
115            isConnected = false
116        }
117    }
118
119    /// 仅扫描
120    func startScan(){
121        self.name = ""
122        self.cm.stopScan()
123        self.cm.scanForPeripherals(withServices: nil, options: nil)
124        runDelay(5) {
125            self.delegateDict.forEach({ (_,delegate) in
126                delegate.bluetoothHelperAutoStopScan()
127            })
128        }
129    }
130
131    /// 开始扫描设备
132    func startScan(name:String){
133        self.name = name
134        self.cm.stopScan()
135        self.cm.scanForPeripherals(withServices: nil, options: nil)
136        runDelay(5) {
137            self.delegateDict.forEach({ (_,delegate) in
138                self.cm.stopScan()
139                delegate.bluetoothHelperAutoStopScan()
140            })
141        }
142    }
143
144    /// 停止扫描设备
145    func stopScan(){
146        self.cm.stopScan()
147    }
148
149    /// 关闭连接设备
150    func disconnect(){
151        if(peripheral != nil){
152            self.cm.cancelPeripheralConnection(peripheral)
153        }
154    }
155
156    /// 扫描到设备
157    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
158        NSLog("\(String(describing: peripheral.name)) is discovered")
159        if(peripheral.name == nil){
160            return
161        }
162
163        scanDelegateDict.forEach { (_,delegate) in
164            delegate.bluetoothScan(peripheral: peripheral)
165        }
166
167
168        if(self.name.isEmpty){
169            return
170        }
171
172        if(peripheral.name?.uppercased() == name.uppercased()){
173            self.connect(peripheral: peripheral)
174        }
175    }
176
177    /// 连接peripheral
178    func connect(peripheral:CBPeripheral){
179        self.peripheral = peripheral
180        peripheral.delegate = self
181        cm.connect(peripheral)
182        cm.stopScan()
183    }
184
185    /// 连接成功后
186    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
187        NSLog("\(String(describing: peripheral.name)) 连接成功")
188        let uuid = CBUUID(string: "18F0")
189        peripheral.discoverServices([uuid])
190
191        scanDelegateDict.forEach { (_,delegate) in
192            delegate.bluetoothConnected(name: name)
193        }
194    }
195
196    /// 断开连接后
197    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
198        NSLog("\(String(describing: peripheral.name)) 连接断开")
199        scanDelegateDict.forEach { (_,delegate) in
200            delegate.bluetoothDisconnect(name: name)
201        }
202        isConnected = false
203    }
204
205    /// 扫描设备的services
206    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
207        if let service = peripheral.services?[0]{
208            let uuid = CBUUID(string: "2AF1")
209            peripheral.discoverCharacteristics([uuid], for: service)
210            self.service = service
211        }
212    }
213
214    /// 扫描service的characteristics
215    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
216        if let characteristic = service.characteristics?[0]{
217            NSLog("characteristic is prepared")
218            isConnected = true
219            self.characteristic = characteristic
220        }
221    }
222
223    /// 输出字符串
224    func writeText(text:String)throws{
225        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))
226
227        if let data = text.data(using: String.Encoding(rawValue: enc)){
228            do {
229               try self.writeData(data: data)
230            } catch  {
231                throw error
232            }
233        }
234    }
235
236    /// 输出二进制
237    func writeBytes(){
238
239    }
240
241    private var lock = NSLock()
242    private var isWritering = false
243
244    private var tempData = Data()
245
246    private var datas = [Data]()
247
248    /// 写入数据
249    func writeData(data:Data) throws {
250        if(isConnected){
251//            lock.lock(before: Date())
252            tempData.append(data)
253            datas.append(data)
254        }else{
255            throw BtError.NoConnectError
256        }
257    }
258
259    /// 真实的打印方法
260    func print(){
261//        NSLog("printdata : \(tempData)")
262        for index in 0 ... datas.count - 1  {
263            let item = datas[index]
264            runDelay(0.02 * Double(index), {
265                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
266            })
267        }
268        self.datas.removeAll()
269    }
270
271    private func _realWriterData(data:Data) {
272        if(isConnected){
273            NSLog("real write data : \(data)")
274            self.peripheral.writeValue(data, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
275        }else{
276
277        }
278    }
279}
280
281enum BtError :Error{
282    case NoConnectError
283}

## 后记 这里的代码是我测试项目中使用的,因为是我独立开发,所以有的代码比较乱,敬请见谅

蓝牙打印机连接本身不算什么高深的操作,只是其中的回调比较复杂,看起来麻烦,这里我也没讲什么概念性的东西,主要就是讲解下代码和实现步骤啥的 搞清楚了蓝牙外设提供的服务有什么,如何连接,另外需要注意的是 CBCharacteristicWriteType.withoutResponse,还有一个有回应的是,这里就看蓝牙设备本身如何设定的了
这里读取暂时没涉及到,有需要的同学自己研究下吧
最后祝大家都能顺利的完成自己的蓝牙连接!!