热敏蓝牙打印机开发

最近在做小票打印这块,项目需求是IOS和安卓两种都要实现,开始做的时候也是一脸懵,然后网上找了不少资料,踩了一堆坑,看了好多文章,结果还好成了
蓝牙打印机一般分为两种打印模式,票据打印、标签打印

公司买的渣渣打印机连开发文档都没有,害我走了不少坑,让我开发买的时候也不咨询咨询我
目前微信小程序连接蓝牙打印机 wx.createBLEConnection 测试在IOS设备上没有问题,在部分安卓手机上会出现异常(表现为,连接是会弹出系统配对框,不管点取消还是输入配对码后点确定,都会立马断开连接。如果不输入也不取消则会在30秒以内自动断开蓝牙打印机)

现在采用的方式是各给安卓和IOS写一套蓝牙打印的命令
IOS
// ====================蓝牙操作==================  //初始化蓝牙模块
openBluetoothAdapter() {

    if (app.sysinfo.provider == 1) {
        // 开启蓝牙
        app.onBluetooth()
        setTimeout(() => {
            this.android_search()
        }, 2000)
        return false;
    }


    this.closeBluetoothAdapter()
    uni.openBluetoothAdapter({
        success: (res) => {
            console.log("初始化蓝牙模块: " + JSON.stringify(res));
            this.startBluetoothDevicesDiscovery()
        },
        fail: (res) => {
            if (res.errCode === 10001) {
                uni.onBluetoothAdapterStateChange((res) => {
                    console.log('监听蓝牙适配器状态变化事件', res)
                    if (res.available == false) {
                        app.global_printing = {}
                        this.connected = false
                        this.chs = []
                        this.canWrite = false
                    }
                    if (res.available) {
                        this.startBluetoothDevicesDiscovery()
                    }
                })
            }
            if (res.errCode) {
                app.alert('初始化蓝牙失败,错误码:' + res.errCode)
                return false;
            }
            app.alert(res.errMsg)
        }
    })
},

//获取本机蓝牙适配器状态
getBluetoothAdapterState() {
    uni.getBluetoothAdapterState({
        success: (res) => {
            console.log('获取本机蓝牙适配器状态。', JSON.stringify(res))
            if (res.discovering) {
                this.onBluetoothDeviceFound()
            } else if (res.available) {
                this.startBluetoothDevicesDiscovery()
            }
        },
        fail: (res) => {
            console.log('error:获取本机蓝牙适配器状态失败', JSON.stringify(res))
            setTimeout(() => {
                this.getBluetoothAdapterState()
            }, 500)
        }
    })
},

//开始搜寻附近的蓝牙外围设备
startBluetoothDevicesDiscovery() {
    console.log(this.discoveryStarted);
    if (this.discoveryStarted) {
        return
    }
    console.log('开始搜索蓝牙设备');
    this.discoveryStarted = true
    this.onBluetoothDeviceFound()
    setTimeout(() => {
        uni.startBluetoothDevicesDiscovery({
            allowDuplicatesKey: true,
            success: (res) => {
                console.log('startBluetoothDevicesDiscovery success', JSON.stringify(
                    res))
            },
            fail: (res) => {
                if (res.errCode == '10001') {
                    app.alert('当前蓝牙适配器不可用')
                } else {
                    app.alert('搜索蓝牙失败,状态码:' + res.errCode)
                }
            }
        })
    }, 500)

},

// 停止搜索
stopBluetoothDevicesDiscovery() {
    uni.stopBluetoothDevicesDiscovery()
    this.discoveryStarted = false
},

//寻找到新设备的事件的回调函数
onBluetoothDeviceFound() {
    console.log('寻找到新设备的事件的回调函数');
    uni.onBluetoothDeviceFound((res) => {
        console.log(res);
        res.devices.forEach(device => {
            if (!device.name && !device.localName) {
                return
            }
            const foundDevices = this.devices
            const idx = this.inArray(foundDevices, 'deviceId', device.deviceId)
            if (idx === -1) {
                this.devices.push(device)
            } else {
                this.devices[idx] = device
            }

        })
    })
},


//连接低功耗蓝牙设备
createBLEConnection(e) {
    uni.showLoading({
        title: '设备连接中',
        mask: true
    });

    const ds = e.currentTarget.dataset
    const deviceId = ds.deviceId
    const name = ds.name

    if (app.sysinfo.provider == 1) {
        if (ds.pair !== true) {
            this.android_search(deviceId)
        } else {
            console.log('已配对')
        }

        var device = null,
            BAdapter = null,
            BluetoothAdapter = null,
            uuid = null,
            main = null,
            bluetoothSocket = null;

        var mac_address = deviceId

        var main = plus.android.runtimeMainActivity();
        BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
        var UUID = plus.android.importClass("java.util.UUID");
        uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
        BAdapter = BluetoothAdapter.getDefaultAdapter();
        device = BAdapter.getRemoteDevice(mac_address);
        plus.android.importClass(device);
        bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
        plus.android.importClass(bluetoothSocket);

        if (!bluetoothSocket.isConnected()) {
            console.log('检测到设备未连接,尝试连接....');
            bluetoothSocket.connect();
        }

        this.connected = true
        this.name = name
        this.deviceId = deviceId
        this.canWrite = true
        app.global_printing = {
            name: name,
            deviceId: deviceId
        }

        app.saveData1('global_printing', app.global_printing)

        uni.hideLoading();
        return false;
    }

    uni.createBLEConnection({
        deviceId,
        success: (res) => {
            this.connected = true
            this.name = name
            this.deviceId = deviceId
            app.global_printing = {
                name: name,
                deviceId: deviceId
            }
            this.onBLEConnectionStateChange()
            // 防止获取失败
            setTimeout(() => {
                this.getBLEDeviceServices(deviceId)
            }, 1000)
        },
        fail: (res) => {
            uni.hideLoading();
            app.Toast('设备连接失败')
            console.log("蓝牙连接失败:", res);
        }
    })
    this.stopBluetoothDevicesDiscovery()
},

//获取蓝牙设备所有服务(service)
getBLEDeviceServices(deviceId) {
    uni.getBLEDeviceServices({
        deviceId,
        success: (res) => {
            console.log("获取蓝牙服务成功:" + JSON.stringify(res))
            if (res.services.length == 0) {
                uni.hideLoading();
                app.alert('没有获取到蓝牙服务,无法打印001')
                app.global_printing = {}
                return false
            }
            for (let i = 0; i < res.services.length; i++) {
                if (res.services[i].isPrimary) {
                    this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
                    return
                }
            }
        },
        fail: (res) => {
            setTimeout(() => {
                this.getBLEDeviceServices(deviceId)
            }, 500)
            console.log("获取蓝牙服务失败:" + JSON.stringify(res))
        }
    })
},


//获取蓝牙设备某个服务中所有特征值(characteristic)
getBLEDeviceCharacteristics(deviceId, serviceId) {
    console.log('获取蓝牙设备某个服务中所有特征值', deviceId, serviceId)
    uni.getBLEDeviceCharacteristics({
        deviceId,
        serviceId,
        success: (res) => {
            console.log('获取蓝牙设备某个服务中所有特征值 success', JSON.stringify(res))
            uni.hideLoading();
            if (res.characteristics.length == 0) {
                app.alert('没有获取到蓝牙服务,无法打印002')
                app.global_printing = {}
                return false
            }
            for (let i = 0; i < res.characteristics.length; i++) {
                let item = res.characteristics[i]
                if (item.properties.read) {
                    uni.readBLECharacteristicValue({
                        deviceId,
                        serviceId,
                        characteristicId: item.uuid,
                    })
                }
                if (item.properties.write) {
                    this.canWrite = true
                    app.global_printing._deviceId = deviceId
                    app.global_printing._serviceId = serviceId
                    app.global_printing._characteristicId = item.uuid

                    app.saveData1('global_printing', app.global_printing)

                    //this.writeBLECharacteristicValue()
                }
                if (item.properties.notify || item.properties.indicate) {
                    uni.notifyBLECharacteristicValueChange({
                        deviceId,
                        serviceId,
                        characteristicId: item.uuid,
                        state: true,
                    })
                }
            }
        },
        fail(res) {
            console.error('获取特征值失败:', res)
        }
    })

    // 操作之前先监听,保证第一时间获取数据
    uni.onBLECharacteristicValueChange((characteristic) => {
        console.log(this.data.chs);
        const idx = this.inArray(this.data.chs, 'uuid', characteristic.characteristicId)
        const data = {}
        if (idx === -1) {
            this.chs[this.data.chs.length] = {
                uuid: characteristic.characteristicId,
                value: ab2hex(characteristic.value)
            }
        } else {
            this.chs[idx] = {
                uuid: characteristic.characteristicId,
                value: ab2hex(characteristic.value)
            }
        }

    })
},

onBLEConnectionStateChange() {
    uni.onBLEConnectionStateChange((res) => {
        // 该方法回调中可以用于处理连接意外断开等异常情况
        console.log(`蓝牙连接状态改变device ${res.deviceId} state has changed, connected: ${res.connected}`)
        if (res.connected == false) {
            app.global_printing = {}
            this.connected = false
            this.chs = []
            this.canWrite = false
        }
    })
},


//断开与低功耗蓝牙设备的连接
closeBLEConnection() {
    app.global_printing = {}
    uni.closeBLEConnection({
        deviceId: this.deviceId
    })
    this.connected = false
    this.chs = []
    this.canWrite = false
},

//关闭蓝牙模块
closeBluetoothAdapter() {
    app.global_printing = {}
    uni.closeBluetoothAdapter()
    this.discoveryStarted = false
},

//发送数据
sendStr(bufferstr, success, fail) {
    var that = this;
    uni.writeBLECharacteristicValue({
        deviceId: app.global_printing._deviceId,
        serviceId: app.global_printing._serviceId,
        characteristicId: app.global_printing._characteristicId,
        value: bufferstr,
        success: function(res) {
            success(res);
            console.log('发送的数据:' + bufferstr)
            // console.log('message发送成功')
        },
        fail: function(res) {
            fail(res)
            console.log("数据发送失败:" + JSON.stringify(res))
        },
        complete: function(res) {
            // console.log("发送完成:" + JSON.stringify(res))
        }
    })
},


//遍历发送数据
printCode(arr) {
    var that = this;
    if (arr.length > 0) {
        this.sendStr(arr[0], function(success) {
            arr.shift();
            that.printCode(arr);
        }, function(error) {
            app.alert('打印失败,错误码:' + error.errCode)
            app.printing_status = false
            console.log(error);
        });
        return false;
    }

    setTimeout(function() {
        app.printing_status = false
        console.log('打印结束');
    }, 1000);
},
Android
就相对简单方便,采用Native.js直接调用Native Java接口通道,通过plus.android调用安卓原生系统API。
原生安卓文档 https://developer.android.google.cn/reference/android/bluetooth/BluetoothAdapter?hl=en
// ======================Android============
// 搜索蓝牙设备
android_search(address = '') {
    //搜索、配对
    var main = plus.android.runtimeMainActivity();
    var IntentFilter = plus.android.importClass('android.content.IntentFilter');
    var BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
    var BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice");
    var BAdapter = BluetoothAdapter.getDefaultAdapter();
    console.log("开始搜索设备");
    var filter = new IntentFilter();
    var bdevice = new BluetoothDevice();
    var on = null;
    var un = null;
    console.log('正在搜索请稍候');
    BAdapter.startDiscovery(); //开启搜索  
    var receiver;
    receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
        onReceive: (context, intent) => { //实现onReceiver回调函数  
            plus.android.importClass(intent); //通过intent实例引入intent类,方便以后的‘.’操作  
            // console.log(intent.getAction()); //获取action  
            if (intent.getAction() == "android.bluetooth.adapter.action.DISCOVERY_FINISHED") {
                main.unregisterReceiver(receiver); //取消监听  
                console.log("搜索结束")
            } else {
                var BleDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                //判断是否配对  
                if (BleDevice.getBondState() == bdevice.BOND_NONE) {
                    console.log("未配对蓝牙设备:" + BleDevice.getName() + '    ' + BleDevice.getAddress());
                    //参数如果跟取得的mac地址一样就配对  
                    if (address == BleDevice.getAddress()) {
                        if (BleDevice.createBond()) { //配对命令.createBond()  
                            if (BleDevice.getName() != null) {
                                console.log("配对成功蓝牙设备:" + BleDevice.getName() + '    ' +
                                    BleDevice.getAddress());
                                // app.Toast("配对成功蓝牙设备:" + BleDevice.getName())
                            }
                        } else {
                            console.log('配对失败')
                        }
                    } else {
                        if (BleDevice.getName() != on) { //判断防止重复添加  
                            on = BleDevice.getName();
                            if (BleDevice.getName() != null) {
                                this.devices.push({
                                    deviceId: BleDevice.getAddress(),
                                    name: BleDevice.getName()
                                })
                                console.log("搜索到蓝牙设备:" + BleDevice.getName() + '    ' +
                                    BleDevice.getAddress());
                            }
                        }
                    }
                } else {
                    if (BleDevice.getName() != un) { //判断防止重复添加  
                        un = BleDevice.getName();
                        if (BleDevice.getName() != null) {
                            this.devices.push({
                                deviceId: BleDevice.getAddress(),
                                name: BleDevice.getName() + ' (已配对)',
                                pair: true
                            })
                            console.log("已配对蓝牙设备:" + BleDevice.getName() + '    ' + BleDevice.getAddress());
                        }
                    }
                }
            }
        }
    });

    filter.addAction(bdevice.ACTION_FOUND);
    filter.addAction(BAdapter.ACTION_DISCOVERY_STARTED);
    filter.addAction(BAdapter.ACTION_DISCOVERY_FINISHED);
    filter.addAction(BAdapter.ACTION_STATE_CHANGED);
    main.registerReceiver(receiver, filter); //注册监听
},

// 打印
android_printCode(arr) {
    
    var that = this;

    // 打印
    var device = null,
        BAdapter = null,
        BluetoothAdapter = null,
        uuid = null,
        main = null,
        bluetoothSocket = null;


    var mac_address = app.global_printing.deviceId

    var main = plus.android.runtimeMainActivity();
    BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
    var UUID = plus.android.importClass("java.util.UUID");
    uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    BAdapter = BluetoothAdapter.getDefaultAdapter();

    try {
        device = BAdapter.getRemoteDevice(mac_address);
        plus.android.importClass(device);
        bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
        plus.android.importClass(bluetoothSocket);
    } catch (e) {
        console.log('asasssds-d=da=da-dsd');
        app.printing_status = false
        app.alert('打印失败')
        return false;
    }


    if (!bluetoothSocket.isConnected()) {
        console.log('检测到设备未连接,尝试连接....');
        bluetoothSocket.connect();
    }

    console.log('设备已连接');

    if (bluetoothSocket.isConnected()) {
        var outputStream = bluetoothSocket.getOutputStream();
        plus.android.importClass(outputStream);
        
        for (var i = 0; i < arr.length; i++) {
            outputStream.write(arr[i]);
        }

        outputStream.flush();
        device = null //这里关键  
        bluetoothSocket.close(); //必须关闭蓝牙连接否则意外断开的话打印错误  
    }

    setTimeout(function() {
        app.printing_status = false
        console.log('打印结束');
    }, 1000);
},
打印指令(更多打印指令参考 https://www.jianshu.com/p/dd6ca0054298)
/**
 * 复位打印机
 */
public static final byte[] RESET = {0x1b, 0x40};

/**
 * 左对齐
 */
public static final byte[] ALIGN_LEFT = {0x1b, 0x61, 0x00};

/**
 * 中间对齐
 */
public static final byte[] ALIGN_CENTER = {0x1b, 0x61, 0x01};

/**
 * 右对齐
 */
public static final byte[] ALIGN_RIGHT = {0x1b, 0x61, 0x02};

/**
 * 选择加粗模式
 */
public static final byte[] BOLD = {0x1b, 0x45, 0x01};

/**
 * 取消加粗模式
 */
public static final byte[] BOLD_CANCEL = {0x1b, 0x45, 0x00};

/**
 * 宽高加倍
 */
public static final byte[] DOUBLE_HEIGHT_WIDTH = {0x1d, 0x21, 0x11};

/**
 * 宽加倍
 */
public static final byte[] DOUBLE_WIDTH = {0x1d, 0x21, 0x10};

/**
 * 高加倍
 */
public static final byte[] DOUBLE_HEIGHT = {0x1d, 0x21, 0x01};

/**
 * 字体不放大
 */
public static final byte[] NORMAL = {0x1d, 0x21, 0x00};

/**
 * 设置默认行间距
 */
public static final byte[] LINE_SPACING_DEFAULT = {0x1b, 0x32};
关于二维码的打印
通过上面的文章我们可以知道

我们需要读取生成后的二维码的像素点的rgba,再将图片数据先4合1判断0还是1(0代表打印1代表不打印),紧接着八合1,因为一个字节有8位。最后使用打印机的位图指令逐行扫描打印

4合1
本想着二维码不是黑就是白,肯定不是255就是0,其实还是会有一小部分是其他数值的,这个要注意哦,每4位是一个像素点的rgba,然后黑白色的rgb就是(0,0,0)和(255,255,255),所以每四位只把第一位黑白化,然后将每四位的第一位取出来作为新的数组,当rule>200的时候,值取0,表示不打印,否则取1,表示打印;

8合1
假如我们取出来的8位数是[0,0,0,0,0,0,0,1],这个时候8合1,我们需要进行进制转换,从右往左是2的零次方,2的一次方,等等,依次上加,实际是 0 * 27 + 0 * 26 + 0 * 25 + 0 * 24 + 0 * 23 + 0 * 22 + 0 * 21 + 1 * 20,这个数就是我们要的最终数据的其中之一。

将数据转换成ArrayBuffer,其次打印必须要有指令!参考网址以及标准的ESC-POS指令集,下面代码中的数字都是指令,另外,由于我这边的打印机支持的是gb2312格式,所以在转成ArrayBuffer的同时,还需要把编码格式转成正确的格式。

不过有一点我是要说下的,要注意ios和安卓的不同,安卓一次只能写入不超过20字节(ios具体不清楚,目测120字节),建议是直接截取数据data.slice(20, byteLength),打印成功再次回调,循环打印。
// 二维码
qr(text,callback) {
    let that = this;
    const ctx = uni.createCanvasContext('myQrcode');
    ctx.clearRect(0, 0, 240, 240);
    drawQrcode({
        canvasId: 'myQrcode',
        text: String(text),
        width: 120,
        height: 120,
        callback(e) {
            // setTimeout(() => {
            // 获取图片数据
            uni.canvasGetImageData({
                canvasId: 'myQrcode',
                x: 0,
                y: 0,
                width: 240,
                height: 240,
                success(res) {
                    let arr = that.convert4to1(res.data);
                    let data = that.convert8to1(arr);
                    const cmds = [].concat([27, 97, 1], [29, 118, 48, 0, 30, 0,
                            240, 0
                        ],
                        data, [27, 74, 3], [27, 64]);
                    const buffer = toArrayBuffer(Buffer.from(cmds, 'gb2312'));
                    
                    // 二维码
                    for (let i = 0; i < buffer.byteLength; i = i + 120) {
                        that.arrPrint.push(buffer.slice(i, i + 120));
                    }
                    callback()
                }
            })
            // }, 3000);
        }
    });
},
1、toArrayBuffer ,是个组件,要安装的,https://www.npmjs.com/package/to-array-buffer 或者你用这种写法也可以const buffer = new Uint8Array(Buffer.from(cmds, 'gb2312')).buffer;

2、注意查看自己的数据是否正确,画图的数据有问题的话,也可能打印出黑块;

3、数据要算!!!要算!!要算!! ,比如我画图是160*160 ,然后我打印数据拼接的指令[29, 118, 48, 0, 20, 0, 160, 0]这个里面的20和160 这个就是算的,参考上方文章看下原因,大概就是1:8,然后画图和读图的数据一致

相关函数
(经过反复测试得出,打印纸一行最大字节数是32字节,这里指的是普通的票据打印机)
打印三列或者两列,是需要自己计算空格进行填充,没有现成的指令噢
总宽度 - 左侧文字长度 - 右侧文字长度 就是空格的长度。
/**
 * 打印两列
 *
 * @param leftText  左侧文字
 * @param rightText 右侧文字
 * @return
 */
printTwoData(leftText, rightText) {
    var sb = ''
    var leftTextLength = this.getBytesLength(leftText);
    var rightTextLength = this.getBytesLength(rightText);
    sb += leftText

    // 计算两侧文字中间的空格
    var marginBetweenMiddleAndRight = 32 - leftTextLength - rightTextLength;

    for (var i = 0; i < marginBetweenMiddleAndRight; i++) {
        sb += ' '
    }
    sb += rightText
    return sb.toString();
},
/**
 * 打印三列
 *
 * @param leftText   左侧文字
 * @param middleText 中间文字
 * @param rightText  右侧文字
 * @return
 */
printThreeData(leftText, middleText, rightText) {
    var sb = ''
    // 左边最多显示 8 个汉字 + 两个点
    if (leftText.length > 8) {
        leftText = leftText.substring(0, 8) + "..";
    }
    var leftTextLength = this.getBytesLength(leftText);
    var middleTextLength = this.getBytesLength(middleText);
    var rightTextLength = this.getBytesLength(rightText);

    sb += leftText
    // 计算左侧文字和中间文字的空格长度
    var marginBetweenLeftAndMiddle = 20 - leftTextLength - middleTextLength / 2;

    for (var i = 0; i < marginBetweenLeftAndMiddle; i++) {
        sb += ' '
    }
    sb += middleText

    // 计算右侧文字和中间文字的空格长度
    var marginBetweenMiddleAndRight = 12 - middleTextLength / 2 - rightTextLength;

    for (var i = 0; i < marginBetweenMiddleAndRight; i++) {
        sb += ' '
    }

    sb += rightText

    // 打印的时候发现,最右边的文字总是偏右一个字符,所以需要删除一个空格
    // sb.delete(sb.length() - 1, sb.length()).append(rightText);
    return sb.toString();
},

max(n1, n2) {
    return Math.max(n1, n2)
},
len(arr) {
    arr = arr || []
    return arr.length
},

//4合1 
convert4to1(res) {
    let arr = [];
    for (let i = 0; i < res.length; i++) {
        if (i % 4 == 0) {
            let rule = 0.29900 * res[i] + 0.58700 * res[i + 1] + 0.11400 * res[i + 2];
            if (rule > 200) {
                res[i] = 0;
            } else {
                res[i] = 1;
            }
            arr.push(res[i]);
        }
    }
    return arr;
},

//8合1
convert8to1(arr) {
    let data = [];
    for (let k = 0; k < arr.length; k += 8) {
        let temp = arr[k] * 128 + arr[k + 1] * 64 + arr[k + 2] * 32 + arr[k + 3] * 16 + arr[k + 4] * 8 +
            arr[k + 5] * 4 +
            arr[k + 6] * 2 + arr[k + 7] * 1
        data.push(temp);
    }
    return data;
},


inArray(arr, key, val) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i][key] === val) {
            return i;
        }
    }
    return -1;
},


// ArrayBuffer转16进度字符串示例
ab2hex(buffer) {
    var hexArr = Array.prototype.map.call(
        new Uint8Array(buffer),
        function(bit) {
            return ('00' + bit.toString(16)).slice(-2)
        }
    )
    return hexArr.join('');
},


// 计算文字占用长度
getBytesLength(str) {
    var num = str.length; //先用num保存一下字符串的长度(可以理解为:先假设每个字符都只占用一个字节)
    for (var i = 0; i < str.length; i++) { //遍历字符串
        if (str.charCodeAt(i) > 255) { //判断某个字符是否占用两个字节,如果是,num再+1
            num++;
        }
    }
    return num; //返回最终的num,既是字符串总的字节长度
}


代码大多是直接从项目中copy过来的,没有整理过,并不能直接运行,仅供参考
不是我那一卷打印失败的打印纸丢了,不然就让你们看看什么叫做 第一次做打印机开发的程序员
这次是真大干货,篇幅也很长,辛苦了我

本博客所有文章如无特别注明均为原创。作者:阿珏复制或转载请以超链接形式注明转自 阿珏博客
原文地址《热敏蓝牙打印机开发

相关推荐

发表评论

路人甲 OωO表情
Ctrl+Enter快速提交

网友评论(4)

很强!有饿了么订单条内味儿了
mengkun 1周前 (2019-11-05) 回复
@mengkun:我们是不饿了么
阿珏 1周前 (2019-11-05) 回复
非技术的路过。
repostone 1周前 (2019-11-05) 回复
@repostone:牛逼就完事了
阿珏 1周前 (2019-11-05) 回复