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 中

使用

import CoreBluetooth

这样CBCentralManager终于可以用了

正式开发

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

macos模拟器蓝牙central

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

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

帮助类的代码


//
//  BluetoothHelper.swift
//  SwiftBluetoothScanDemo1
//
//  Created by caijinglong on 2017/9/9.
//

import Foundation
import CoreBluetooth

protocol BluetoothHelperDelegate {
    
    func bluetoothHelperIndex()->Int
    
    func bluetoothHelperNotifyConnected(isConnected:Bool)
    
    func bluetoothHelperAutoStopScan()
}

class BluetoothHelper :NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{

    static let shared = BluetoothHelper()

    private var cm:CBCentralManager! = nil
    private var peripheral: CBPeripheral! = nil
    private var service:CBService! = nil

    private var characteristic:CBCharacteristic! = nil
    
    private var delegateDict = Dictionary<Int,BluetoothHelperDelegate>()
    
    public func registerDelegate(delegate:BluetoothHelperDelegate){
        delegateDict[delegate.bluetoothHelperIndex()] = delegate
    }
    
    public func unregisterDelegate(delegate:BluetoothHelperDelegate){
        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
    }
    
    private override init(){
        super.init()
        self.cm = CBCentralManager(delegate: self, queue: nil)
    }
    
    /// 被连接的打印机的名字
    var connectDeviceName:String? = ""
    
    /// 是否连接了打印机
    var isConnected:Bool = false{
        didSet{
            if(!isConnected){
                peripheral = nil
                service = nil
                characteristic = nil
                connectDeviceName = nil
                
                delegateDict.forEach({ (key,delegate) in
                    
                })
            }else{
                connectDeviceName = self.name
            }
        }
    }
    
    /// 蓝牙开关是否打开
    var btOpen = false
    private var name = "QSPrinter"

    // 后面是代理方法
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        NSLog("状态变化")
        if(central.state.rawValue == 5){
            btOpen = true
        }else{
            btOpen = false
            isConnected = false
        }
    }
    
    /// 开始扫描设备
    func startScan(name:String){
        self.name = name
        self.cm.stopScan()
        self.cm.scanForPeripherals(withServices: nil, options: nil)
        runDelay(5) { 
            self.delegateDict.forEach({ (_,delegate) in
                self.cm.stopScan()
                delegate.bluetoothHelperAutoStopScan()
            })
        }
    }
    
    /// 停止扫描设备
    func stopScan(){
        self.cm.stopScan()
    }
    
    /// 关闭连接设备
    func disconnect(){
        if(peripheral != nil){
            self.cm.cancelPeripheralConnection(peripheral)
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
        NSLog("\(String(describing: peripheral.name)) is discovered")
        if(peripheral.name?.uppercased() == name.uppercased()){
            self.peripheral = peripheral
            peripheral.delegate = self
            cm.connect(peripheral)
            cm.stopScan()
        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        NSLog("\(String(describing: peripheral.name)) 连接成功")
        let uuid = CBUUID(string: "18F0")
        peripheral.discoverServices([uuid])
    }

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        NSLog("\(String(describing: peripheral.name)) 连接断开")
        isConnected = false
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let service = peripheral.services?[0]{
            let uuid = CBUUID(string: "2AF1")
            peripheral.discoverCharacteristics([uuid], for: service)
            self.service = service
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristic = service.characteristics?[0]{
            NSLog("characteristic is prepared")
            isConnected = true
            self.characteristic = characteristic
        }
    }

    /// 输出字符串
    func writeText(text:String)throws{
        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))

        if let data = text.data(using: String.Encoding(rawValue: enc)){
            do {
               try self.writeData(data: data)
            } catch  {
                throw error
            }
        }
    }
    
    
    private var datas = [Data]()
    
    /// 写入数据
    func writeData(data:Data) throws {
        if(isConnected){
            datas.append(data)
        }else{
            throw BtError.NoConnectError
        }
    }
    
    /// 真实的打印方法
    func print(){
        for index in 0 ... datas.count - 1  {
            let item = datas[index]
            runDelay(0.02 * Double(index), {
                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
            })
        }
        self.datas.removeAll()
    }

}

enum BtError :Error{
    case NoConnectError
}

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

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

startScan其中是开始扫描的方法

    func startScan(name:String){
        self.name = name
        self.cm.stopScan()
        self.cm.scanForPeripherals(withServices: nil, options: nil)
        runDelay(5) { 
            self.delegateDict.forEach({ (_,delegate) in
                self.cm.stopScan()
                delegate.bluetoothHelperAutoStopScan()
            })
        }
    }

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

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
        NSLog("\(String(describing: peripheral.name)) is discovered")
        if(peripheral.name?.uppercased() == name.uppercased()){
            self.peripheral = peripheral
            peripheral.delegate = self
            cm.connect(peripheral)
            cm.stopScan()
        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        NSLog("\(String(describing: peripheral.name)) 连接成功")
        let uuid = CBUUID(string: "18F0")
        peripheral.discoverServices([uuid])
    }

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

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

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

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

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

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

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

详细分析写数据

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

   /// 输出字符串
    func writeText(text:String)throws{
        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))

        if let data = text.data(using: String.Encoding(rawValue: enc)){
            do {
               try self.writeData(data: data)
            } catch  {
                throw error
            }
        }
    }
    
    
    private var datas = [Data]()
    
    /// 写入数据
    func writeData(data:Data) throws {
        if(isConnected){
            datas.append(data)
        }else{
            throw BtError.NoConnectError
        }
    }
    
    /// 真实的打印方法
    func print(){
        for index in 0 ... datas.count - 1  {
            let item = datas[index]
            runDelay(0.02 * Double(index), {
                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
            })
        }
        self.datas.removeAll()
    }
func runDelay(_ delay:TimeInterval,_ block:@escaping () -> ()){
    let queue = DispatchQueue.main
    
    let delayTime = DispatchTime.now() + delay
    
    queue.asyncAfter(deadline: delayTime) {
        block()
    }
}

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

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

打印相关

编码问题

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

关于二维码

打印二维码使用的是ESC/POS的指令集,这个在指令集的说明文档中可以找到
这里的moduleSize是二维码的大小,具体参数可以参考说明文档,一般对接的打印机厂商都会提供

一般的打印机中文都是GBK编码的,而扫码一般是UTF8编码,这个编码转换很麻烦,所以尽量不要出现中文

打印机发送指令

//
//  PrinterHelper.swift
//  SwiftBluetoothScanDemo1
//
//  Created by Caijinglong on 2017/9/11.
//

import Foundation

/// printer helper
///
/// single instance
class PrinterHelper{
    
    static var shared:PrinterHelper = PrinterHelper()
    
    var helper : BluetoothHelper!
    
    //    var devices = [Printer]()
    
    private init(){
        helper = BluetoothHelper.shared
    }

    func registerDelegate(delegate: BluetoothHelperDelegate){
        helper.registerDelegate(delegate: delegate)
    }
    
    func unregisterDelegate(delegate: BluetoothHelperDelegate){
        helper.unregisterDelegate(delegate: delegate)
    }
    
    var index = 0
    
    let DIVIDER = "-----------------------------------------------"
    let ESC: Byte = 27//换码
    let FS: Byte = 28//文本分隔符
    let GS: Byte = 29//组分隔符
    let DLE: Byte = 16//数据连接换码
    let EOT: Byte = 4//传输结束
    let ENQ: Byte = 5//询问字符
    let SP: Byte = 32//空格
    let HT: Byte = 9//横向列表
    let LF: Byte = 10//打印并换行(水平定位)
    let CR: Byte = 13//归位键
    let FF: Byte = 12//走纸控制(打印并回到标准模式(在页模式下)
    let CAN: Byte = 24//作废(页模式下取消打印数据 )
    
    func conn(deviceName:String){
        helper.startScan(name: deviceName)
    }
    
    func disconnect(){
        helper.disconnect()
    }
    
    func sendMsg(msg:String) -> Self{
        try? helper.writeText(text: msg)
        return self
    }
    
    func sendBytes(bytes:[Byte]) -> Self{
        try? helper.writeData(data: Data.bytesArray(byteArray: bytes))
        return self
    }
    
    func sendHex(int:Int) -> Self {
        return self.sendHexs(hexInt: int)
    }
    
    func sendHexs(hexInt ints:Int...) -> Self{
        var data = Data()
        ints.forEach { (int) in
            data.append(UInt8(int))
        }
        try? helper.writeData(data: data)
        return self
    }
    
    func sendBytes(bytes:Byte...) -> Self{
        return sendBytes(bytes: bytes)
    }
    
    func alignLeft()-> Self{
        return sendBytes(bytes: ESC,97,0)
    }
    
    func alignCenter() -> Self {
        return sendBytes(bytes: ESC,97,1)
    }
    
    func alignRight() -> Self{
        return sendBytes(bytes: ESC,97,2)
    }
    
    func printDivider() -> Self {
        return sendMsg(msg: DIVIDER)
    }
    
    
    func startPrint(){
        helper.print()
    }
    
    func setFontSize(size:Int)  -> Self{
        var realSize: Byte = 0
        
        if(size <= 7){
            realSize = Byte(size * 17)
        }
        
        var result = [Byte]()
        result.append(0x1D)
        result.append(0x21)
        result.append(realSize)
        print("size = \(size)  realSize = \(realSize)")
        return sendBytes(bytes: result)
    }
    
    func newLine(lines:Int = 1) -> Self{
        for _ in 0...lines - 1{
            _ = sendHex(int: 0x0A)
        }
        return self
    }
    
    /**
     * 选择加粗模式
     
     * @return
     */
    func boldOn() -> Self {
        var result = [Byte]()
        result.append(ESC)
        result.append(69)
        result.append(0xF)
        return sendBytes(bytes: result)
    }
    
    
    /**
     * 取消加粗模式
     
     * @return
     */
    func boldOff() -> Self {
        var result = [Byte]()
        result.append(ESC)
        result.append(69)
        result.append(0)
        return sendBytes(bytes: result)
    }
    
    func subTitle(_ title:String) -> Self{
        return
            self.newLine()
                .setFontSize(size: 1)
                .boldOn()
                .alignCenter()
                .sendMsg(msg: title)
                .setFontSize(size: 0)
                .boldOff()
    }
    
    func sendQrcode(qrcode:String) -> Self{
        let moduleSize:Byte = 8
        var list = [Byte]()
        
        if let data = Data.gbkData(text: qrcode){
            
            //打印二维码矩阵
            list.append(0x1D)// init
            list.append(40) // adjust height of barcode
            list.append(107)// adjust height of barcode
            list.append(Byte(data.count + 3)) // pl
            list.append(0) // ph
            list.append(49) // cn
            list.append(80) // fn
            list.append(48) //

            data.forEach({ (char) in
                list.append(char)
            })
            
            list.append(0x1D)
            list.append(40)// list.append("(k")
            list.append(107)// list.append("(k")
            list.append(3)
            list.append(0)
            list.append(49)
            list.append(69)
            list.append(48)
            
            list.append(0x1D)
            list.append(40)// list.append("(k")
            list.append(107)// list.append("(k")
            list.append(3)
            list.append(0)
            list.append(49)
            list.append(67)
            list.append(moduleSize)
            
            list.append(0x1D)
            list.append(40)// list.append("(k")
            list.append(107)// list.append("(k")
            list.append(3) // pl
            list.append(0) // ph
            list.append(49) // cn
            list.append(81) // fn
            list.append(48) // m
        }
        
        return
            alignCenter()
            .sendBytes(bytes: list)
    }
}

蓝牙连接的类

//
//  BluetoothHelper.swift
//  SwiftBluetoothScanDemo1
//
//  Created by caijinglong on 2017/9/9.
//  Copyright © 2017 sxw. All rights reserved.
//

import Foundation
import CoreBluetooth

protocol BluetoothHelperDelegate:NSObjectProtocol {
    
    func bluetoothHelperIndex()->Int
    
    func bluetoothHelperNotifyConnected(isConnected:Bool)
    
    func bluetoothHelperAutoStopScan()
    
    func bluetoothHelperFindDevices(name:String)
}

extension BluetoothHelperDelegate{
    func bluetoothHelperFindDevices(name:String){
    }
}

protocol BluetoothHelperScanDeviceDelegate {
    func bluetoothScan(peripheral: CBPeripheral)
    
    func bluetoothHelperIndex()->Int
    
    func bluetoothConnected(name:String)
    
    func bluetoothDisconnect(name:String)
}

extension BluetoothHelperScanDeviceDelegate{
    func bluetoothConnected(name:String){}
    
    func bluetoothDisconnect(name:String){}
}

class BluetoothHelper :NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{

    static let shared = BluetoothHelper()

    private var cm:CBCentralManager! = nil
    private var peripheral: CBPeripheral! = nil
    private var service:CBService! = nil

    private var characteristic:CBCharacteristic! = nil
    
    private var delegateDict = Dictionary<Int,BluetoothHelperDelegate>()
    
    private var scanDelegateDict = Dictionary<Int,BluetoothHelperScanDeviceDelegate>()
    
    public func registerDelegate(delegate:BluetoothHelperDelegate){
        delegateDict[delegate.bluetoothHelperIndex()] = delegate
    }
    
    public func unregisterDelegate(delegate:BluetoothHelperDelegate){
        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
    }
    
    public func registerScanDelegate(delegate:BluetoothHelperScanDeviceDelegate){
        scanDelegateDict[delegate.bluetoothHelperIndex()] = delegate
    }
    
    public func unregisterScanDelegate(delegate:BluetoothHelperScanDeviceDelegate){
        delegateDict.removeValue(forKey: delegate.bluetoothHelperIndex())
    }

    private override init(){
        super.init()
        self.cm = CBCentralManager(delegate: self, queue: nil)
    }
    
    /// 被连接的打印机的名字
    var connectDeviceName:String? = ""
    
    /// 是否连接了打印机
    var isConnected:Bool = false{
        didSet{
            if(!isConnected){
                peripheral = nil
                service = nil
                characteristic = nil
                
                delegateDict.forEach({ (key,delegate) in
                    delegate.bluetoothHelperNotifyConnected(isConnected: false)
                })
                connectDeviceName = nil
            }else{
                connectDeviceName = self.name
                
                delegateDict.forEach({ (key,delegate) in
                    delegate.bluetoothHelperNotifyConnected(isConnected: true)
                })
            }
        }
    }
    
    /// 蓝牙开关是否打开
    var btOpen = false
    private var name = "QSPrinter"

    // 后面是代理方法
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        NSLog("状态变化: state = \(central.state.rawValue)")
        if(central.state.rawValue == 5){
            btOpen = true
        }else{
            btOpen = false
            isConnected = false
        }
    }
    
    /// 仅扫描
    func startScan(){
        self.name = ""
        self.cm.stopScan()
        self.cm.scanForPeripherals(withServices: nil, options: nil)
        runDelay(5) { 
            self.delegateDict.forEach({ (_,delegate) in
                delegate.bluetoothHelperAutoStopScan()
            })
        }
    }
    
    /// 开始扫描设备
    func startScan(name:String){
        self.name = name
        self.cm.stopScan()
        self.cm.scanForPeripherals(withServices: nil, options: nil)
        runDelay(5) { 
            self.delegateDict.forEach({ (_,delegate) in
                self.cm.stopScan()
                delegate.bluetoothHelperAutoStopScan()
            })
        }
    }
    
    /// 停止扫描设备
    func stopScan(){
        self.cm.stopScan()
    }
    
    /// 关闭连接设备
    func disconnect(){
        if(peripheral != nil){
            self.cm.cancelPeripheralConnection(peripheral)
        }
    }

    /// 扫描到设备
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
        NSLog("\(String(describing: peripheral.name)) is discovered")
        if(peripheral.name == nil){
            return
        }
        
        scanDelegateDict.forEach { (_,delegate) in
            delegate.bluetoothScan(peripheral: peripheral)
        }
        
        
        if(self.name.isEmpty){
            return
        }
        
        if(peripheral.name?.uppercased() == name.uppercased()){
            self.connect(peripheral: peripheral)
        }
    }

    /// 连接peripheral
    func connect(peripheral:CBPeripheral){
        self.peripheral = peripheral
        peripheral.delegate = self
        cm.connect(peripheral)
        cm.stopScan()
    }
    
    /// 连接成功后
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        NSLog("\(String(describing: peripheral.name)) 连接成功")
        let uuid = CBUUID(string: "18F0")
        peripheral.discoverServices([uuid])
        
        scanDelegateDict.forEach { (_,delegate) in
            delegate.bluetoothConnected(name: name)
        }
    }

    /// 断开连接后
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        NSLog("\(String(describing: peripheral.name)) 连接断开")
        scanDelegateDict.forEach { (_,delegate) in
            delegate.bluetoothDisconnect(name: name)
        }
        isConnected = false
    }

    /// 扫描设备的services
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let service = peripheral.services?[0]{
            let uuid = CBUUID(string: "2AF1")
            peripheral.discoverCharacteristics([uuid], for: service)
            self.service = service
        }
    }
    
    /// 扫描service的characteristics
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristic = service.characteristics?[0]{
            NSLog("characteristic is prepared")
            isConnected = true
            self.characteristic = characteristic
        }
    }

    /// 输出字符串
    func writeText(text:String)throws{
        let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))

        if let data = text.data(using: String.Encoding(rawValue: enc)){
            do {
               try self.writeData(data: data)
            } catch  {
                throw error
            }
        }
    }
    
    /// 输出二进制
    func writeBytes(){
        
    }
    
    private var lock = NSLock()
    private var isWritering = false
    
    private var tempData = Data()
    
    private var datas = [Data]()
    
    /// 写入数据
    func writeData(data:Data) throws {
        if(isConnected){
//            lock.lock(before: Date())
            tempData.append(data)
            datas.append(data)
        }else{
            throw BtError.NoConnectError
        }
    }
    
    /// 真实的打印方法
    func print(){
//        NSLog("printdata : \(tempData)")
        for index in 0 ... datas.count - 1  {
            let item = datas[index]
            runDelay(0.02 * Double(index), {
                self.peripheral.writeValue(item, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
            })
        }
        self.datas.removeAll()
    }
    
    private func _realWriterData(data:Data) {
        if(isConnected){
            NSLog("real write data : \(data)")
            self.peripheral.writeValue(data, for: self.characteristic, type: CBCharacteristicWriteType.withoutResponse)
        }else{
            
        }
    }
}

enum BtError :Error{
    case NoConnectError
}

后记

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容